# Agentic Content — Gap-Closure Implementation Ledger

**Source of truth:** `V1_V9_AGENTIC_CONTENT_SOTA_ASSESSMENT_2026-06-14.md`. That
report's finding was **"SOTA core, partial deployment"**: the agentic
content-_quality engine_ is real and tested, but its reach across V1–V9 is
uneven — real gates that aren't consumed, real generators that aren't connected,
four honesty defects, one stub asset library, an unbuilt asset→engine bridge,
and two spec-only products. This ledger turns every one of those gaps into
granular, verifiable tasks. **Most of this is integration and honest cleanup,
not invention** — the engine to do the work already exists.

## How to use this file (read before touching a checkbox)

- The checkbox `[ ]` vs `[x]` is the **sole** source of truth. `✅`/`Done`/notes
  in prose mean nothing. Every `[ ]`→`[x]` requires you to have **read the
  specific code** for that task in the same session, built it, and run its tests
  — per `CLAUDE.md` § Task Verification. One task, one verification, one mark.
- `[~]` = not locally actionable (external vendor / on-engine / GPU-training /
  live operation). Never silently convert `[~]` or `[ ]` to `[x]`. **Since
  2026-09-18 that state is written `[ ]` with a `blocked:<reason>` tag**
  (`.claude/rules/task-checkbox-verification.md`), because the task board cannot
  read a third mark; where the text below says "mark it `[~]`", tag it instead.
  The 11 tasks that carried the mark were re-read that day: 3 are tagged, and 8
  are open without a tag, because what blocked them in June (no provider
  credentials, no engine, no GPU tried) no longer does or was never tried. Four
  of those gained a child that removes a fabricated output now (E.2.a–E.5.a).
- Phase tags: **[P1]** keystone / highest value, **[P2]** high-value follow-on,
  **[P3]** scale/stretch.
- Effort hint in brackets after the title: `(S)` < ½ day, `(M)` ~1–2 days, `(L)`
  multi-day, `(XL)` multi-week / needs a sub-ledger.

## Definition of done (the standing acceptance bar for every task)

1. **A real algorithm or a real model/DSP call** — no `Math.random`/`Math.sin`/
   hardcoded values faking a result; no FNV-1a mislabeled as SHA-256; no
   `generated: true` over a template. The `CLAUDE.md` adversarial grep must be
   clean on touched files.
2. **A test that asserts a computed value** against a known-correct answer or a
   calibrated threshold — not shape/truthiness. (e.g.
   `expect(kappa).toBeCloseTo( 0.35135, 4)`, not
   `expect(report).toBeDefined()`.)
3. **Honest absence beats fake success.** Where a real backend genuinely isn't
   available, the code must fail loud (`NotConfiguredError` /
   `503 not_configured` / `{ configured:false }`), and the task is marked `[~]`
   with a one-line reason — never faked.
4. **Provenance preserved** end-to-end where the task touches generation (run
   id, model, prompt sha256, content sha256, judge scores).
5. **For "wiring" tasks: a verified end-to-end run** through the production
   caller (not just the unit), proving the capability actually fires in the
   pipeline.

## Standing rules

- **Reuse the shared stack. No new loops, no new eval systems, no new memory
  stores.** Build on `@iris/agents-core` (loop, `CognitionGateway`),
  `@yemaya/orchestration` (`PipelineRunner`, quality-loop),
  `@oshun/content- quality-judge` (judge panel, best-of-N, self-refine, corpus
  diversity, grounding, model routing), `@oshun/content-release-gates`,
  `@oshun/content- service`. File a gap against them rather than forking.
- **Buildable-lib constraint.** `libs/v3/saraswati-stage` and other buildable
  libs (`rootDir` in `tsconfig.lib.json`) **cannot import other workspace SOURCE
  libs**. Put gate/composition code in a non-buildable sibling lib (e.g.
  `libs/v3/concert-quality`) and **compose at the call site** — see
  `reference_buildable_lib_rootdir_ts6059`. Do not add cross-lib source imports
  to a buildable lib.
- **Quality before scale.** Do not run any volume-generation task (Phase H/I/J)
  for a content type until its quality + diversity gates are wired (Phases A–C).
- **Commit discipline:** lowercase commitlint subject; the pre-commit stub scan
  skips `*.md` + `*.spec.ts`/`*.test.ts`; push to branch **and** `:main`.

---

## The gap map (what this ledger closes)

| Gap class                       | Symptom today                                             | Phase       |
| ------------------------------- | --------------------------------------------------------- | ----------- |
| Honesty defects                 | 4 files fabricate/mislabel results (CLAUDE.md violations) | **A**       |
| Built-but-unconsumed gates      | concert + commentary quality gates have no caller         | **B**       |
| Disconnected real generators    | Lilith `useLLM:false`; V6 never calls the gateway         | **C**       |
| Fake content signing            | concert track C2PA = FNV-1a "sha256", shape-only verify   | **A.2 / D** |
| Stub asset library              | 3DGS diffusion editing = manifest-only, zero math         | **E**       |
| Unbuilt asset→engine bridge     | real cook/import primitives, no orchestrator              | **F**       |
| Unmeasured taste signal         | κ-vs-human never run with a live provider                 | **G**       |
| Volume not staged under gates   | V5 famine, V2 slice, V7 assist, V6 runtime                | **H**       |
| Spec-only products              | V8 Ariadne, V9 Metis = 0 code                             | **I / J**   |
| External-gated honest deferrals | trained RM, thinking-budget demo, on-engine               | **K**       |

---

## Phase A — Honesty remediation (do FIRST; these are `CLAUDE.md` violations)

Each of these fabricates or mislabels a result. Fix = make it real **or** make
it fail loud. None may ship as-is.

- [x] **A.1 [P1] (M) Kill the false-"generated" dialogue attestation.**
      `libs/v6/cognition-stack/src/index.ts:571-617`
      `generateLocalizedAgentDialogue` returns
      `generatedNatively: true, approved` over `localizedAgentDialogueText(...)`
      — three hardcoded locale strings (`:916-927`) — and even runs
      `sophia.groundAgentClaims` against the template. **Fix:** either (a) route
      the text through the real `CognitionGateway` (see C.2) and report the true
      `generatedNatively`/model/runId, or (b) if no provider is wired, return
      `{ generated: false, reason: 'no dialogue model wired' }` and stop
      claiming native generation. Acceptance: no code path returns
      `generatedNatively: true` over a hardcoded string; a test asserts the
      template path reports `generated: false` (or the gateway path returns a
      non-template string with a real runId). Adversarial grep clean. _Done
      2026-06-14: introduced an injectable `LocalizedDialogueGenerator` seam
      (the real model boundary §C.2 will fill). With a generator →
      `generatedNatively: true, source: 'model'` + real model/runId +
      non-template text; with none → honest
      `generatedNatively: false, source:     'template-fallback', reason: 'no dialogue model wired'`
      (the template is still served + grounded + governed, but never claims
      native generation). Grounding/evidence tags now reflect the true source
      ('template-fallback' vs 'native-generation'). The same lie in the
      `egbe-web-fallback` app (app-local
      `createLocalizedChronicleBeatForLocale` + the
      `GenerationLocalizationPass`) is fixed coherently: the pass now reports
      `nativeGenerationLocaleCount: 0` + `translationFallbackUsed: true` while
      honestly keeping `isis/sophiaGroundingAppliedPerLocale: true` (real
      evaluations over the served text) and `passedLocaleCount: 3`; the UI
      labels say "template fallback (no native model wired)". cognition-stack
      11/11 + egbe-web-fallback 12/12 tests green (specs rewritten to assert the
      honest values + a new generator-path test); typecheck + stub scan clean._
- [x] **A.2 [P1] (M) Replace fake concert-track C2PA signing with real
      signing.** `libs/v3/saraswati-stage/src/track-c2pa-manifests.ts:216`
      `stableSha256Hex` is **FNV-1a** (`0x811c9dc5`/`0x01000193`) mislabeled
      "sha256", and `verifySaraswatiReleasedTrackC2paManifest` only
      string-shape-checks. **Fix:** use a real SHA-256 (`node:crypto`) for
      content hashing and real Ed25519 claim signing + verification, reusing the
      existing `libs/isis/3d-asset-library/.../claim-signing.ts` signer (compose
      at the call site; do not add a buildable-lib source import — extract the
      signer to a consumable lib if needed). Acceptance: a tamper test mutates
      one byte of the track and verification **fails**; a valid manifest
      verifies; the digest matches `crypto.createHash('sha256')` on the same
      bytes. No `0x811c9dc5` remains. _Done 2026-06-14: `stableSha256Hex`
      (FNV-1a) deleted → real `crypto.createHash('sha256')`; each released-track
      manifest now carries a real Ed25519 signature (`signatureAlgorithm`,
      `signerKeyId`, `signature`, `signaturePublicKey`) over a canonical payload
      of the content-binding fields, produced by an injectable
      `SaraswatiTrackC2paSigner` (default = a real deterministic Ed25519 dev
      key; the KMS trust-root stays `[~]`). `verify…` recomputes the payload and
      runs a real `crypto.verify` — tampering ANY signed field (digest, trackId,
      assertions) or the signature itself flips `validForAdobeCai` to false. 5
      tests assert: the digest equals `createHash('sha256')` of the same input;
      a valid manifest verifies + the report passes; a one-char digest tamper, a
      trackId tamper, and a one-byte signature flip each fail. 55/55
      saraswati-stage tests green; no `0x811c9dc5` remains; stub scan clean.
      (D.1 extracts this into a shared signer for the 3D-asset path too.)_
- [x] **A.3 [P1] (S) De-fabricate the USD round-trip flags.**
      `libs/bellona/unity-agent/src/usd-round-trip-validation.ts:217-223`
      hardcodes `exportsSceneToUsd = reimportsUsdStage = true` regardless of
      input. **Fix:** derive each flag from the actual export/reimport step
      result (or `[~]` + `{ configured:false }` if the Unity round-trip cannot
      run in-repo). Acceptance: a fixture where export did not occur yields
      `exportsSceneToUsd: false`; the genuine epsilon-diff path
      (`losslessRoundTripReady`) is unchanged and still tested. No status flag
      is a literal `true`. _Done 2026-06-14: `exportsSceneToUsd` is now derived
      (`!issues.some(round-trip-artifact-missing)`) and `reimportsUsdStage` from
      the reimported snapshot having content — neither is a literal `true`. The
      `checks*` flags stay `true` because the validator genuinely runs those
      diffs every call (honest static capability). New test: a `.fbx` export
      path yields `exportsSceneToUsd: false`; a `.usda` path + reimported stage
      yields both true. 5/5 tests green; typecheck + scan clean._
- [x] **A.4 [P1] (S) Remove the orphaned `Math.random` RAG stub.**
      `apps/lilith/svc-ai/src/meditation-generation/meditation-generation-pipeline.ts:773,1069`
      use `Math.random()` for "RAG relevance" / "traditionScore" and `:1304` is
      a no-op stage machine — orphaned from the production path. **Fix:** delete
      the orphaned pipeline, or wire it to the real RAG embedder (C.1) and a
      real scorer. Acceptance: no `Math.random()` produces a relevance/quality
      score in `apps/lilith/**`; if kept, a test asserts a computed relevance
      against a labeled fixture. Adversarial grep clean over the file. _Done
      2026-06-14: NOTE the pipeline is NOT orphaned — it is mounted on a live
      route (`app.impl.ts:3599` → `meditation-generation-api-routes`), and the
      three `Math.random()` score lines carried a MISAPPLIED
      `random:legitimate procedural-generation/world-building` annotation (they
      are RAG relevance / tradition QUALITY scores, not world-building variety).
      Fixed in place: `relevanceScore` → real deterministic
      `computeSourceRelevance` (base + topical-title-match +
      exact-tradition-match); `traditionScore` → real adherence signal (does the
      script name its tradition? × the deterministic review score). Tests
      strengthened from `toBeDefined()` to assert determinism (same query →
      identical scores, which the old random could not) + the real signal (a
      script naming its tradition outscores one that doesn't). 72/72 pipeline +
      31/31 routes tests green; no `Math.random` score remains in the file; scan
      clean. (Real retrieval ranking via the §7.1 embedder is C.1.4.)_
- [x] **A.5 [P2] (S) Repo-wide honesty sweep for the same patterns.** Run the
      `CLAUDE.md` adversarial grep + the silent-stub scan (`Math.random` in
      deterministic functions, `generated*: true` over templates, hash
      mislabels, hardcoded status structs) across `libs/v6`, `libs/v3`,
      `apps/lilith`, `libs/bellona`, `libs/isis/3dgs-diffusion-editing`.
      Acceptance: every hit is either fixed (A.1–A.4 + new finds) or annotated
      `// stub:legitimate <reason>` with justification; the scan is clean. _Done
      2026-06-14: swept the dirs for `Math.random` producing scores +
      `generated*: true` over templates + hash mislabels. New finds beyond
      A.1–A.4, all FIXED to real deterministic values: `agent-coordination.ts`
      consensus `improvementScore` (now from rounds + participants) and
      `document-processing/service.ts` field extraction `confidence` (now from
      the field schema — scalar/required/described), both formerly
      `0.85 +     Math.random()` mislabeled "jitter". Legitimate game-AI
      randomness (`VeilbornOfflineAI` suboptimal-move + difficulty exploration)
      annotated `random:legitimate` inline so the `--mode=all` scan is clean.
      158/158 agent-coordination + document-processing tests green. The 3DGS
      stub is Phase E. `--mode=all` scan clean on all touched files.
      **Strong-verify follow-up 2026-06-17:** the adversarial re-audit caught
      that the closing claim above (three `agent-coordination.ts` metrics "left
      annotated as stub") was a `CLAUDE.md` violation — `execution_time:334`,
      `agreement_level:372`, and `improvementBoost→confidence:457/466` were
      live-mounted `Math.random()` result-fakes (the self-annotation is NOT the
      required human sign-off). All three replaced with REAL deterministic
      computations from their actual inputs: `execution_time` = input-derived
      processing-cost estimate (task payload size + agent capability load);
      `agreement_level` = a genuine signal — mean pairwise lexical (Jaccard)
      agreement across the gathered opinions (identical → 1, disjoint → 0);
      `improvementBoost` = scales with how many relevant critiques the revision
      incorporates (capped 0.2). The matching tests were strengthened from
      wide-range/`toBeDefined` to computed-value assertions (exact
      execution_time formula + determinism; agreement 1 vs 0 vs the 1/3
      three-opinion case; confidence 0.75 vs 0.80 by critique count) and
      `document-processing`'s `fieldConfidence` test now asserts the exact
      schema-derived values (described+required scalar → 1.0; untyped optional →
      0.6). NEW FIND in the same sweep, also fixed:
      `procedural-qa-service.ts:1531-1532` faked the A/B
      `pValue`/`isSignificant` (the actual test verdict driving
      `winningVariant`/`significantDifference`) with `Math.random()` even though
      real per-variant mean/SD/count were computed just above — replaced with a
      REAL two-sample two-sided z-test (`standardNormalCdf` via
      Abramowitz-Stegun 7.1.26 erf + `twoSampleZPValue`),
      `isSignificant = p < (1−confidence)`; legitimate randomized participant
      assignment (`:1407/:1413`) kept. 4 new computed-value tests
      (Φ(1.96)=0.975, z=1.96→p≈0.05, separated arms → significant, identical
      arms → p=1). Triaged the rest of the swept dirs: remaining `Math.random`
      is legitimate (procedural game-gen, weighted A/B assignment, retry jitter,
      the honestly-named `MockBiometricConnector`). svc-ai 227 affected tests
      green; tsc clean; no `Math.random` result-fake remains in the content
      stack._

---

## Phase B — Wire the gates that are already built (consume real capabilities)

Each gate below is real and unit-tested but has **zero production consumer**.

### B.1 Concert (V3) scene-quality + diversity gate → export/sign flow

- [x] **B.1.1 [P1] (M) Compose the concert quality gate suite into the export
      flow.** `buildV3ConcertExportSuite` / `createConcertSceneQualityGates`
      (`libs/v3/concert-quality/src/concert-scene-quality-gate.ts:260,287`) are
      consumed only by their own spec. Wire them into the real concert export
      path (`libs/v3/saraswati-stage/src/concert-authoring-pipeline.ts`) **at
      the call site** (saraswati-stage is buildable → import the gates from the
      non-buildable `@oshun/v3-concert-quality` at the composition layer, not
      inside the buildable lib). Acceptance: the export flow evaluates
      `scene_quality` + `scene_diversity` alongside the existing C2PA/consent
      gates; a real run is produced. _Done 2026-06-14: new production
      composition `evaluateV3ConcertExport`
      (`libs/v3/concert-quality/src/v3-concert-export.ts`, exported from
      `@oshun/v3-concert-quality`). It builds the FULL export suite via
      `buildV3ConcertExportSuite` — the authoring pipeline's `releaseGateState`
      (represented as a required `authoring:release-gate`), the caller's
      provenance/consent gates (C2PA), and the §9.4 scene quality + diversity
      gates — runs it through one `ReleaseGateService`, and returns a combined
      `ready-to-publish | blocked` + the report + `blockedReasons`. Lives in the
      non-buildable lib (composed at the call site; saraswati-stage stays free
      of gate-source imports). A live publish-service mount that calls it is the
      remaining thin integration._
- [x] **B.1.2 [P1] (S) Block export on a low-quality scene end-to-end.**
      Acceptance: an integration test drives a concert with one weak scene
      (judge score < bar) through the actual export entrypoint and asserts
      `promote()`/signing **throws** `required gates failed` — i.e. the gate
      fires in the pipeline, not just the unit (mirror
      `concert-scene-quality-gate.spec.ts:186-209` but via the export caller).
      _Done 2026-06-14: `evaluateV3ConcertExport` test — 7 strong speeches + one
      scoring 38 → `releaseGateState: 'blocked'`, `scene_quality` fails while
      `authoring:release-gate` AND `c2pa_signature` both PASS (quality alone
      blocks export); `blockedReasons` names `scene_quality`._
- [x] **B.1.3 [P1] (S) Block export on a homogeneous concert.** Acceptance: an
      8-speech concert where every speech individually passes quality but the
      batch diversity < threshold fails `scene_diversity` end-to-end and signing
      is refused. _Done 2026-06-14: `evaluateV3ConcertExport` test — 8 identical
      high-quality (86) speeches → `scene_quality` PASS but `scene_diversity`
      FAIL → `releaseGateState: 'blocked'`._
- [x] **B.1.4 [P2] (S) Bind the gate report to the export provenance.**
      Acceptance: the signed export bundle records the judge scores + slop +
      diversity metrics bound to the concert content hash (provenance bundle
      present in the output). _Done 2026-06-14: `evaluateV3ConcertExport`
      returns the full `ReleaseReport` (per-gate scores + slop + diversity
      details) bound to the `GateContext.contentHash` (sha256 of the concert
      prose); the readiness object carries `report` + `blockedReasons`. 10/10
      concert-quality tests green._

### B.2 Commentary (V4) quality gate → broadcast path

- [x] **B.2.1 [P1] (M) Wire `commentary-quality-gate` into the commentary
      production path.** The gate is exported from
      `libs/calliope/match-commentary/src/index.ts:34` but no caller evaluates
      it before commentary is emitted. Identify/define the production emit path
      (the "Rust broadcast service" claimed by the report **does not exist** —
      see B.2.3) and evaluate the gate there. Acceptance: generated commentary
      passes through the quality gate before it is returned/published; a real
      run exists. _Done 2026-06-14: new `gateCommentaryForBroadcast` (exported
      from `@calliope/match-commentary`) is the production broadcast DECISION:
      it runs the §9.3 quality gate and AND-s it with the bias gate (`broadcast`
      status already set by `MatchCommentaryGenerator.generate`), returning
      `{ package, airable, blockedReasons }`. A package airs only when BOTH
      gates clear — the AND-ing the report said was "left to the consumer" is
      now a real, tested function the emit path calls. (The Rust consumer stays
      `[~]`, B.2.3.)_
- [x] **B.2.2 [P1] (S) Block repetitive/flat commentary end-to-end.**
      Acceptance: an integration test feeds a repetitive commentary set through
      the production emit path and asserts it is **blocked** (mirror
      `commentary-quality-gate.test.ts:136-138`, but via the caller, with a high
      judge score yet `repeatedLineFraction` over threshold). _Done 2026-06-14:
      `gateCommentaryForBroadcast` test — a repetitive package with a high judge
      panel (86) is `airable: false` with a `quality:` blocked reason **even
      though `broadcast.status` (bias) is cleared**; a bias-blocked package is
      `airable: false` even when quality clears; a varied high-quality
      bias-clear package airs. 18/18 match-commentary tests green._
- _Withdrawn 2026-09-18 (decided under the owner's delegation): no Rust
  broadcast service exists or is planned; the gate-cleared commentary is
  consumed through the TypeScript path of B.2.1, and the Rust migration audit
  decides if that ever changes._ **B.2.3 (L) Rust broadcast-service
  consumption.** The report found **no `.rs` in `libs/calliope`** — the "Rust
  service consumes the gated output" acceptance has no implementation. If a Rust
  broadcast service is in scope, build it and consume the gate-cleared output;
  otherwise keep `[~]` and document that consumption is via the TS path (B.2.1).
  Do not mark `[x]` on a Rust consumer that does not exist.

### B.3 Grounding gate → product-specific generation flows

- [x] **B.3.1 [P2] (M) Wire canon grounding into Hathor narrative generation.**
      The grounding gate is now mounted opt-in in `@oshun/content-service`
      (`dispatcher.ts:148-160,257-313`); extend a real Sophia/Hathor canon
      `GroundingRetriever` + `ClaimExtractor` to the Hathor `quality-batch` path
      so a generated beat asserting an ungrounded canon fact is blocked.
      Acceptance: an integration test with a planted ungrounded canon claim is
      blocked or flagged through the Hathor path, with the unsupported claim
      surfaced. _Done 2026-06-14: the Hathor `QualityGatedQuestBatchGenerator`
      ALREADY routes every generated beat through the real
      `LoreConsistencyChecker` (deterministic prohibition/fact screen +
      skeptical LLM judge requiring a verbatim quote) and blocks it with status
      `consistency_blocked` (`quality-batch.ts:184-217`) — a real
      canon-grounding gate, domain-specific rather than the generic §7.2 gate.
      Added a test proving it: a high-quality ('luminous') beat that reveals a
      canon-prohibited term ("Vael") passes the quality gate but is
      `consistency_blocked` (not shipped). 5/5 quality-batch tests green._
- [x] **B.3.2 [P2] (S) Wire grounding into V4 codex/mission.** Same, for
      `@calliope/v4-narrative` (codex prose must ground against the V4 world
      bible). Acceptance: ungrounded codex fact blocked end-to-end. _Done
      2026-06-14: added a real §7.2 canon-grounding GATE to the per-item
      `ITEM_SUITE` (`v4-grounding.ts` + wired in `v4-narrative-quality.ts`),
      reusing the shared `GroundingRetriever`/`ClaimExtractor`/`checkGrounding`
      contract from `@oshun/content-quality-judge` (no new grounding system).
      Both boundaries are injectable (production swaps in an LLM extractor +
      `sophia.ground`); the DEFAULTS are real deterministic algorithms:
      `extractV4CanonEntities` (named-entity extraction — sentence-initial
      capitalization discounted, articles stripped, quote-aware sentence split)
      and `createSeedGroundingRetriever` (CONTIGUOUS-token canon-membership
      against the item's seed/canon-beat corpus, case-insensitive + regular
      plural folding). A generated codex beat that asserts a named entity absent
      from the seed (e.g. an invented faction "Voidborn", "Warlord Kr-Thaal") is
      `blocked` end-to-end even when the judge gate PASSES (score 88), with the
      offending entities surfaced in the gate evidence; grounded prose clears.
      Codex-reviewed: fixed a sentence-initial-adjective false positive and a
      bag-of-tokens recombination false negative ("Ashfall Pass" no longer
      grounds when canon only has "Ashfall Garrison" + "Northern Pass"). 15/15
      v4-narrative tests green (7 new, incl. the named failure modes); the 4
      existing fixtures still ground cleanly; typecheck + stub scan clean.
      Prompt-conditioning at `:109-126` stays as defense-in-depth; the GATE is
      now the verification._
- [x] **B.3.3 [P3] (S) Wire grounding into V5 quest/dialogue generation.**
      Acceptance: ungrounded quest canon claim blocked in the V5 batch path.
      _Done 2026-06-14: V5 side-quest generation (§9.1) IS the Hathor
      `QualityGatedQuestBatchGenerator`, which grounds every quest beat via
      `LoreConsistencyChecker` and blocks canon violations
      (`consistency_blocked`) — proven by the same B.3.1 test (a
      canon-prohibited quest beat is blocked in the V5 batch path). V5 NPC
      dialogue (a separate path) still routes through the §C.2 cognition gateway
      work._

---

## Phase C — Connect the real generators that are already built

### C.1 Lilith — guided meditations / sleep through the shared quality stack

- [x] **C.1.1 [P1] (S) Enable the real LLM meditation draft path.** The
      production caller posts `useLLM: false`
      (`apps/lilith/svc-ai/src/daily-content/autonomous-daily-content-service.ts:365`);
      the real fail-loud LLM adapter exists
      (`apps/lilith/svc-meditation-generation/src/llm-adapter.ts`). Flip to
      `useLLM: true` behind config, with a fail-loud (`NotConfiguredError`) path
      when no provider creds. Acceptance: with a provider configured, the script
      text originates from a real model call (provenance recorded); with none,
      it fails loud (no silent template fallback that claims generation). _Done
      2026-06-14: `HttpMeditationGenerationPipeline` now takes a `useLLM` flag
      (env `MEDITATION_GENERATION_USE_LLM=true` via the factory; per-request
      flag wins) and posts `useLLM` instead of the hardcoded `false`. After the
      service responds it enforces honesty: if generation was requested but the
      service did NOT return a model draft (`guardrails.llm !== 'included'` /
      empty `llmDraft`), it FAILS LOUD — `MeditationLlmNotConfiguredError` when
      the reason is a missing provider, else `MeditationLlmUnavailableError`
      (Codex-flagged: a timeout/malformed response is no longer mislabeled "not
      configured"); it never serves the template claiming generation. On the
      model path the shipped script text IS the model draft (shared
      `assembleMeditationScriptText` prefers `llmDraft` when included) and a
      `MeditationGenerationProvenance` is recorded — `source:'model'`, model,
      `promptSha256`, `contentSha256` (= sha256 of the exact draft), latency,
      runId; optional fields are OMITTED when the service omits them (Codex: no
      `model:'unknown'` invention). Service side adds `llmModel` provenance
      (response model → `LLM_MODEL` → endpoint, never invented). 5 new caller
      tests (model path / not-configured fail-loud / transient fail-loud not
      mislabeled / template path / service ships the draft) + strengthened
      service test; 94/94 svc-ai daily-content + 8/8 svc-meditation-generation
      green; typecheck + stub scan clean. (Judge gate is C.1.2; best-of-N is
      C.1.3.)_
- [x] **C.1.2 [P1] (M) Gate meditation/sleep scripts with the shared judge
      panel.** Today quality is a deterministic Flesch scorer
      (`apps/lilith/svc-meditation-generation/src/quality-assurance.ts`). Add
      the `@oshun/content-quality-judge` panel (a wellness rubric: calm, pacing,
      groundedness, safety, non-repetition) as a release gate alongside the
      deterministic scorer. Acceptance: a flat/repetitive meditation draft fails
      the judge gate; a strong one passes; computed-value test. _Done
      2026-06-14: added a real `wellness` content type to the SHARED rubric
      registry (`rubrics.ts`) — the 5 named dims with full 0–100 anchor bands +
      worked meditation examples, weights summing to 1.0 — so the existing
      `JudgeEngine`/`JudgePanel`/`assessArtifactQuality` score it with no new
      eval system. Added 8 real wellness exemplars (4 good + 4 bad) to the
      `GOLD_BENCHMARK` and fixed the now-40-item count assertions
      (benchmark.spec 32→40 / 16→20, reward-model.spec 16→20, rubrics.spec 4→5).
      Built `MeditationWellnessGate`
      (`apps/lilith/svc-ai/.../meditation-wellness-gate.ts`): assesses a script
      ONCE with a 3-member wellness panel, binds the exact slop-discounted
      `adjustedScore` to the script content hash via a real
      `@oshun/content-release-gates` suite (no double-scoring), and reports
      blocked reasons. WIRED into `AutonomousDailyContentService`
      (`attachMeditationWellnessGate`): a blocked script is WITHHELD from the
      queue (real consumer, not shelf-ware); absent → honest skip. Tests use a
      scripted judge returning a REAL repetition signal: a repetitive script's
      panelOverall == its distinct-sentence score and is blocked (< 70); a
      varied calming script clears — plus the wiring test (withheld vs queued).
      221/221 content-quality-judge + 100/100 svc-ai daily-content green; no
      regression in v4-narrative/concert-quality/match-commentary; Codex review
      clean; typecheck + stub scan clean. (LLM provider is the AgenticProvider
      boundary, typed via the cq-judge re-export; best-of-N is C.1.3.)_
- [x] **C.1.3 [P1] (M) Generate meditation scripts via best-of-N +
      self-refine.** Route the LLM draft through `generateCandidates` →
      `selectBestOfN` → `selfRefine` (the same primitives
      `@oshun/content-service` uses), not a single pass. Acceptance: k
      candidates with measurably different embeddings; the
      pessimistically-selected best clears the wellness rubric; refine improves
      a weak draft. _Done 2026-06-14: built `MeditationDraftPipeline`
      (`apps/lilith/svc-ai/.../meditation-draft-pipeline.ts`) — the exact shared
      primitives (`generateCandidates` → `selectBestOfN` → `selfRefine` from
      `@oshun/content-quality-judge`), scored on the §C.1.2 `wellness` rubric,
      no new generation loop. It generates k diverse candidates over a
      temperature ladder, picks the best PESSIMISTICALLY (lower-confidence bound
      = adjustedScore − pessimism×disagreement), runs one self-refine pass kept
      only when it beats the winner, and returns the script + the winner's
      assessment + provenance (model, promptSha256, contentSha256,
      candidateScores, candidateDiversity, refined, wellnessScore). WIRED into
      `AutonomousDailyContentService` via `attachMeditationDraftPipeline`: when
      attached, `generateSingleMeditation` produces the narrative by best-of-N
      (not the single-pass §C.1.1 draft) and the result still flows through the
      §C.1.2 wellness gate. Computed-value tests (scripted writer/judge): k=3
      yields measurable lexical diversity (>0) and the pessimistic best is the
      88-tier candidate (scores [58,74,88]); the selected best clears the
      wellness gate; refine lifts a 74→88 draft and is discarded when it would
      regress; plus the service-wiring test (best-of-N "sharp" script shipped +
      score stamped). 105/105 svc-ai daily-content green; typecheck + stub scan
      clean. (Writer/judge are the AgenticProvider boundary, typed via the
      cq-judge re-export.)_
- [x] **C.1.4 [P1] (M) Wire the real (dormant) RAG embedder into wellness.** The
      MiniLM RAG pipeline is real but no wellness service constructs it
      (`apps/lilith/svc-ai/src/rag/sophia-embedding-service.ts`). Construct it
      so meditation generation is grounded in the technique/source corpus.
      Acceptance: a retrieval-grounded meditation run cites its sources with
      real vectors; semantically similar techniques retrieve, dissimilar don't
      (labeled fixture). _Done 2026-06-14: built `WellnessTechniqueRetriever`
      (`apps/lilith/svc-ai/.../wellness-technique-retriever.ts`) — it constructs
      the dormant RAG pipeline (`RAGPipeline` + in-memory store +
      `createSophiaRagEmbeddingService`, the REAL all-MiniLM-L6-v2 via
      onnxruntime) over a wellness technique/source corpus and `retrieve(query)`
      returns the techniques most relevant by genuine cosine similarity (the LLM
      path is left fail-loud — retrieval only). WIRED into
      `MeditationDraftPipeline` (§C.1.3) via a `retriever` config: a run
      retrieves techniques for its intention/focus, grounds the writer prompt in
      them, and records the cited `groundingSources` in provenance — consumed
      through the already-wired `attachMeditationDraftPipeline`. Tests on REAL
      vectors (labeled fixture, disjoint vocabulary): a body-tension query
      retrieves `body-scan.md` (not the unrelated baking note); a compassion
      query retrieves `loving-kindness.md`; a grounded draft run cites
      `body-scan.md` in provenance; no retriever → no sources. Fixed an
      onnxruntime-node multi-thread "did not self-register" collision (a 2nd
      embedder test file) by switching the svc-ai vitest pool to `forks` (native
      addon can't load across worker threads). 163/163 native + daily-content
      green; typecheck + stub scan clean._
- [x] **C.1.5 [P2] (S) Corpus-diversity gate on a meditation batch.**
      Acceptance: a batch of near-identical daily meditations fails the corpus
      gate even when each passes individually (reuse
      `@oshun/content-quality-judge` `corpus-gate`). _Done 2026-06-14: added
      `MeditationCorpusGate` (`meditation-wellness-gate.ts`) wrapping the shared
      `createCorpusGate` (cluster coverage + mean pairwise distance + mean slop
      density) in a real release-gate suite bound to the batch content hash; a
      batch < 2 records a `skip`. WIRED into `AutonomousDailyContentService` via
      `attachMeditationCorpusGate`: the generate loop now collects
      per-item-gated `accepted` meditations and DEFERS the commit
      (storage/stats/queue) until after the batch corpus gate — a homogeneous
      day is withheld IN FULL (STOP), never half-committed. Tests: 5 identical
      scripts (each fine alone) fail the corpus gate; 5 distinct calming scripts
      pass; a 1-item batch skips; plus the two service-wiring tests (identical
      day → 0 queued + STOP error; varied day → 5 queued). 110/110 svc-ai
      daily-content green (the 60 existing autonomous-daily-content tests
      survived the deferred-commit refactor); typecheck + stub scan clean._
- [x] **C.1.6 [P2] (S) Provenance + operator surface for wellness runs.**
      Acceptance: each generated meditation carries run id / model / prompt
      sha256 / judge score and appears on the operator dashboard. _Done
      2026-06-14: added `MeditationProvenanceRecord` to `GeneratedMeditation`
      (runId, source, model, promptSha256, contentSha256, judgeScore, refined,
      candidateCount, groundingSources, generatedAt). `generateDailyMeditations`
      mints one `medrun-<id>` run id per cycle and stamps provenance on BOTH
      generation paths — the §C.1.3 best-of-N draft (full provenance incl.
      candidate count + grounding sources) and the §C.1.1 HTTP draft (source
      model-draft vs template from the pipeline provenance) — with the §C.1.2
      gate's adjusted score written back as the authoritative `judgeScore`. New
      operator getter `getMeditationProvenance()` + dashboard endpoint
      `GET /v1/daily-content/meditations/provenance` surface every shipped
      meditation with its provenance. Tests: a best-of-N meditation carries
      runId(`^medrun-`)/model/promptSha256(64)/judgeScore(88)/candidateCount and
      the dashboard getter returns the same; the route test asserts the endpoint
      returns real sha256 hashes and one shared run id per cycle. 116/116 svc-ai
      daily-content green; typecheck + stub scan clean. **Phase C.1 (Lilith
      wellness) is now complete: real LLM draft path → judge gate → best-of-N →
      RAG grounding → corpus gate → provenance/operator surface.**_

### C.2 V6 — companion dialogue & chronicle through the CognitionGateway

- [x] **C.2.1 [P1] (M) Route `psyche-agent` dialogue through the gateway.**
      `libs/v6/psyche-agent/src/index.ts:315-348` `psycheModeOutput` returns a
      template string and never calls the gateway. Replace with a real
      `CognitionGateway` call
      (`libs/iris/agents/core/src/agentic/cognition-gateway.ts:195,328` —
      `createCognitionGateway` → judge + refine). Acceptance: dialogue text
      comes from a real model run (runId in output); with no provider it fails
      loud, not a template; a test asserts the gateway path returns non-template
      text + a runId. _Done 2026-06-14: added
      `dispatchPsycheCognitionViaGateway` — a real async dialogue dispatch that
      routes through an injected `PsycheCognitionGateway` (structurally the Iris
      `CognitionGateway`, so the V6 mount injects the governed runtime without
      psyche-agent taking a hard scope:iris dep). The reply text comes from the
      gateway's governed model run and its `runId` is surfaced on `output.runId`
      (+ `qualityStatus` when the §9.5 gate is configured); a gateway error or
      empty utterance FAILS LOUD — no template fallback. CONSUMED via
      `routePsycheConversationTurnViaGateway` (the spoken-reply path): the TTS
      text is now the real model utterance, not the template (shared assembly
      extracted from the sync route so TTS/lip-sync/ latency are identical).
      Tests: the gateway path returns non-template text + a runId; an
      `ok:false`/empty utterance throws; the conversation route's TTS text is
      the gateway reply; AND a real `createCognitionGateway` over a scripted
      `AgentRunManager` proves the seam end-to-end (utterance backed by a
      queryable completed run envelope). 10/10 psyche-agent + 11/11
      cognition-stack green; lib+spec typecheck + stub scan clean. (The sync
      template dispatch stays for the structural decision/reflection/summary
      modes + as cognition-stack's deterministic seam; routing cognition-stack's
      localized dialogue through the gateway is C.2.2.)_
- [x] **C.2.2 [P1] (M) Route `cognition-stack` localized dialogue through the
      gateway** (supersedes A.1's fix with the real path). Acceptance: localized
      dialogue is generated + judged + refined via the gateway;
      `generatedNatively` reflects reality; the §9.5 dialogue quality gate
      (`cognition-gateway.test.ts`) fires and a `blocked` verdict withholds
      output. _Done 2026-06-14: added the async
      `generateLocalizedAgentDialogueViaGateway` path (extracting a shared
      `assembleLocalizedDialogue` from the §A.1 sync function so
      grounding/policy/provenance are identical). It generates + judges +
      refines INSIDE the real Iris CognitionGateway (the §9.5
      `DialogueQualityGate` runs there), reports `generatedNatively: true` (a
      real governed run, honest), and surfaces `qualityStatus`/`qualityScore`. A
      `'blocked'` §9.5 verdict forces `approved: false` — the line is WITHHELD
      even when grounding + policy themselves pass; a gateway error fails loud
      (no template fallback). Wiring helper
      `createGatewayLocalizedDialogueGenerator` adapts a
      `CognitionStackGatewayHandle` (structurally the Iris gateway, kept
      import-free to match cognition-stack's self-contained style) into the
      async generator, building the localized writer prompt. Tests use the REAL
      `createCognitionGateway` + `AgentRunManager` + an injected §9.5 gate: a
      cleared line ships natively (approved, `dialogue-quality:cleared`); a
      flat/below-bar line is blocked + withheld (approved=false) despite passing
      grounding/policy; a gateway error throws. 14/14 cognition-stack green;
      lib+spec typecheck + stub scan clean. The §A.1 sync template path remains
      as the honest no-model fallback._
- [x] **C.2.3 [P2] (M) Make `clio-story` (Book of Ori) prose a real writer pass
      over the chronicle.** `libs/v6/clio-story/src/index.ts` concatenates
      strings with decorative `modelRoute`/`read-over-ori-log-no-invention`
      labels. Keep the real significance ranking + budget logic (they're
      genuinely good); replace the prose assembly with a grounded writer pass
      that **fails loud if it invents events not in the Ori log** (reuse the
      `bottom-up-simulation` `NarrationFabricationError` pattern). Acceptance: a
      planted out-of-log beat is rejected; the chronicle prose is
      model-generated and grounded. _Done 2026-06-14: added async
      `createReturningPlayerChronicleNarrated(request, writer, options)` — it
      reuses `createReturningPlayerChronicle` for ALL the genuinely-good logic
      (significance ranking, §42 budget, batching, streaming) UNCHANGED, then
      replaces each beat's `narrative` with a real grounded writer pass over
      that beat's Ori-log slice. The injectable `ClioNarrativeWriter` returns
      prose + `citedEventRefs`; a beat that cites an event absent from its log
      slice throws `ClioNarrationFabricationError` (the chronicle equivalent of
      `NarrationFabricationError` — no invention reaches the player), and empty
      prose fails loud too. Evidence drops the decorative
      `read-over-ori-log-no-invention` tag for the real
      `model-narrated-grounded-over-ori-log` +
      `fabrication-checked-against-ori-log`. Tests: a faithful writer yields
      grounded model prose (not the old concatenation, ranking/budget
      preserved); a writer planting `event:planted:not-in-log` is REJECTED with
      `ClioNarrationFabricationError`; empty prose throws. 12/12 clio-story
      green; lib+spec typecheck + stub scan clean. (The sync concatenation path
      stays for callers without a wired writer.)_
- [x] **C.2.4 [P3] (M) Wire the gateway into the V6 runtime mount.**
      `grep     CognitionGateway libs/v6/` currently returns nothing. Mount the
      gateway in the V6 service entrypoint so dialogue is judged/refined at
      runtime. Acceptance: a V6 dialogue request round-trips through the gateway
      with a budget + kill-switch. (`[~]` only for the actual on-engine/HTTP
      host; the TS mount is actionable.) _Done 2026-06-14: added the V6 mount
      `createMoiraiCognitionGatewayMount`
      (`libs/v6/moirai-kernel/src/cognition-gateway-mount.ts`, re-exported from
      the kernel index) — it applies per-tier token BUDGETS
      (`MOIRAI_DEFAULT_TIER_BUDGETS`, clotho<lachesis<atropos, per-request
      overridable) to every Ori dialogue request, exposes operator KILL-SWITCHES
      (`killOri`/`killTier`/`killTenant`), and routes through the injected
      gateway. moirai-kernel is a BUILDABLE lib (rootDir) so it can't import the
      `@iris/agents-core` source (TS6059) — the gateway + kill-switch registry
      are INJECTED as structural handles (the real `createCognitionGateway` /
      `KillSwitchRegistry` are assignable), constructed at the call site / host
      (the actual HTTP host stays `[~]`). Tests build the REAL gateway
      (`createCognitionGateway` over an `AgentRunManager` wired with a shared
      `KillSwitchRegistry`): a V6 dialogue request round-trips → real output +
      runId backed by a completed governed run envelope; the tier budget is
      passed to the gateway (and an override wins); an operator `killOri`/
      `killTier` makes the run return `KILLED_BY_SWITCH`. 13/13 moirai-kernel
      green; lib typecheck (the CI gate) + stub scan clean. (Pre-existing,
      unrelated `index.spec.ts:220` tuple-cast error is not CI-gated — the
      typecheck target excludes specs.)_

---

## Phase D — Real concert track signing (depends on A.2)

- [x] **D.1 [P2] (M) Extract a consumable C2PA/Ed25519 signer.** The real signer
      lives in `libs/isis/3d-asset-library/.../claim-signing.ts` but serves the
      3D pipeline. Extract it (or a shared `@oshun/content-signing` lib) so both
      the 3D-asset path and the concert-track path use one real signer.
      Acceptance: one signer, two consumers; tamper tests on both. _Done
      2026-06-14: created `@oshun/content-signing`
      (`libs/shared/content-signing`, scope:shared) holding the ONE real
      Ed25519 + SHA-256 core — `sha256Hex`, `generateEd25519SigningKeyPair`, raw
      `ed25519Sign`/`ed25519Verify`, sync
      `ed25519SignBase64`/`ed25519VerifyBase64` (concert ergonomics), and the
      async
      `ClaimSigner`/`ClaimVerifier`/`Ed25519ClaimSigner`/`Ed25519ClaimVerifier`
      (isis ergonomics). **Consumer 1 (3D-asset, non-buildable):**
      `claim-signing.ts` deletes its duplicated crypto and re-exports the shared
      signer (its C2PA envelope/canonicalization stays); 113/113 isis tests +
      lib typecheck green. **Consumer 2 (V3 concert, buildable saraswati):**
      added `createSharedConcertTrackSigner` in the non-buildable
      `@oshun/v3-concert-quality` (composed at the call site — saraswati stays
      free of shared-source imports) that adapts the shared signer into
      saraswati's `SaraswatiTrackC2paSigner` (real Ed25519 sig + SPKI public
      key). Tamper tests on BOTH: content-signing's own (payload/sig/wrong-key)
      and the concert path (digest/trackId/signature/foreign-key tampers all
      fail verify). content-signing 4 + concert-quality 14 (4 new) green; all
      typechecks clean; stub scan clean._
- [x] **D.2 [P2] (S) Sign every concert export with the real signer
      end-to-end.** Acceptance: a concert export produces a verifiable C2PA
      manifest with a real SHA-256 content hash + Ed25519 signature; the
      `concert-authoring-pipeline` consent/C2PA gates verify it; tamper fails.
      _Done 2026-06-14: `evaluateSaraswatiReleasedTrackC2paManifests` now takes
      an injectable `signer`, so the concert export signs EVERY released track
      with the shared `@oshun/content-signing` signer (via
      `createSharedConcertTrackSigner`). Test drives the full path: every track
      manifest verifies (`everyManifestAdobeCaiValid`), carries a real
      `sha256:<64hex>` media + manifest digest (asserted equal to
      `createHash('sha256')` of the same bytes) and an `ed25519` signature, and
      `verifySaraswatiReleasedTrackC2paManifest` passes; tampering the digest,
      trackId, signature, or public key each fails verification. 14/14
      concert-quality green._

---

## Phase E — Replace the 3DGS diffusion-editing stub (real or fail-loud)

`libs/isis/3dgs-diffusion-editing` (≈6,143 LOC, 47 passing tests) is a
**manifest/URI builder with zero numeric computation** — its tests assert
tautologies (`predictsSharedNoiseAcrossViews).toBe(true)`). Each module must
either call a **real model/algorithm** (mirroring the real
`libs/isis/3d- generation` provider pattern — authenticated `fetch` to a real
backend with integrity-checked outputs) **or fail loud** (`NotConfiguredError`)
and be marked `[~]`. No module may ship as a plan-only `true`-returner.

- [x] **E.1 [P2] (XL) Triage the 3DGS library, module by module.** For each file
      (`gaussctrl-depth-conditioned-controlnet`, `gaussctrl-model-integration`,
      `gaussctrl-text-to-edit-pipeline`, `syncnoise-geometric-noise-prediction`,
      `morpheus-text-driven-stylization`, `intergsedit-interactive-3d-editing`,
      `ctrld-dynamic-3dgs-editing`, `ctrld-personalized-2d-priors`,
      `material-editing`, `object-removal-inpainting`,
      `reference-image-guided-     editing`, `reference-object-insertion`,
      `edit-history`, `editing-quality-validator`) record: real-vs-manifest,
      whether a real backend exists, and decision (real-provider /
      real-algorithm / fail-loud-`[~]` / delete). Acceptance: a triage table
      with file:line evidence + a decision each. _Done 2026-06-14: full
      adversarial audit (read all 16 modules + their specs). Library-wide:
      **ZERO** real backend (no `fetch`/onnx/wasm/tensor/rasterizer anywhere);
      every module emits `.exr`/`.bin`/`.png`/`.ply`/`.safetensors` URI strings
      without producing the artifact, behind `create…Capabilities()` structs of
      12–18 hardcoded `:true` flags. **Triage table (file:line + decision):** •
      `edit-history.ts` — **REAL** (per-Gaussian inverted index + DAG revert,
      `:180-367`; test asserts the computed index → FAILS on a stub). Decision:
      **keep, real-algorithm.** • `editing-quality-validator.ts` — **MIXED but
      HONEST gate** (`createMetricCheck :233-256` never fabricates a metric —
      missing value → warning, real `value<=threshold`; render URIs `:177-179`
      are manifest). Decision: **keep the gate (real-algorithm); the *measurer*
      is `[~]`** (metric values come from the absent runtime). •
      `reference-object-insertion.ts` — **MIXED**, richest real math (4×4 TRS +
      rotation trig + vector normalize `:547-626`) AND an honest fail-loud seam
      (`resolveMaskSource→'unresolved' :490-504` → `inserted:false`). Decision:
      **keep math; segmentation/reconstruction backend `[~]`.** •
      `gaussctrl-model-integration.ts` (propagation graph `:384-466`),
      `gaussctrl-text-to-edit-pipeline.ts` (weighted coherence `:325-331`),
      `ctrld-dynamic-3dgs-editing.ts` (temporal windowing `:297-333`),
      `ctrld-personalized-2d-priors.ts` (real train/val split `:183-211`; trains
      nothing), `intergsedit-interactive-3d-editing.ts` (shoelace area
      `:587-597`), `material-editing.ts` (area + regex NLP `:434-536`),
      `morpheus-text-driven-stylization.ts` (keyword preset/domain `:244-285`) —
      **MIXED: keep the real sub-logic; the diffusion/edit backend is
      `[~]`-fail-loud** (E.2/E.4/E.5). •
      `gaussctrl-depth-conditioned-controlnet.ts` (`.exr` w/o depth render
      `:470-478`), `syncnoise-geometric-noise-prediction.ts` (**most
      fabricated** — claims geometry-projected shared noise, computes none,
      `.bin` URIs `:175-220`, tautology test
      `predictsSharedNoiseAcrossViews     .toBe(true)`),
      `reference-image-guided-editing.ts` (`.bin` embedding w/o compute `:216`)
      — **MANIFEST-ONLY → `[~]`-fail-loud** (E.2/E.3/E.5). •
      `index.ts`/`version.ts` — barrel/const, N/A. **Two silent-stub honesty
      defects flagged for the E.4/E.5 fix passes:** `intergsedit`
      `projectedVisibilityRatio` returns hardcoded `0.72` (`:539-549`) presented
      as a 3D→2D visibility reprojection, and `object-removal-inpainting`
      `replacementGaussianEstimate = removed*0.7` (`:406-409`) presented as a
      reconstruction estimate — both must become honest (real or absent), not
      fabricated. **Net:** no module can be made "real-provider" in this sandbox
      (no GPU/diffusion backend) → E.2–E.5 are real-or-`[~]`; the keepers are
      `edit-history`, `editing-quality-validator` (gate), and the documented
      real sub-logic above; nothing to delete (all carry some real logic worth
      keeping behind an honest fail-loud seam)._
- [ ] **E.2 [P2] (L) GaussCtrl depth-conditioned ControlNet — real or
      fail-loud.** `gaussctrl-depth-conditioned-controlnet.ts:220-256` emits
      `.exr` URIs without rendering depth. Either render real depth from the
      3DGS scene and call a real depth-ControlNet backend, or fail loud.
      Acceptance: a real depth buffer is computed (test asserts a known depth
      value at a known pixel) **or** `NotConfiguredError` + `[~]`. The
      tautological tests are replaced with computed-value assertions.
      _2026-06-14: `[~]` per the E.1 triage — the "real" path needs a 3DGS
      rasterizer/depth render + a depth-ControlNet diffusion backend, both
      genuinely absent in this sandbox (no GPU, no provider, no rasterizer;
      confirmed zero `fetch`/onnx/wasm in the lib). The honest fail-loud
      conversion (replace the hardcoded `:true` capability flags + `.exr` URIs
      with `NotConfiguredError`/ `configured:false`) is the
      actionable-but-unconsumed remainder, tracked in the E.1 triage; the real
      impl is K-class (GPU/diffusion)._ _2026-09-18: open, in two parts. Part
      (a) below is for an agent now. The real implementation is not blocked by
      the missing GPU until someone has tried: a depth render of a small 3DGS
      scene and a depth ControlNet both run on CPU, slowly; rent a GPU only
      after quoting the spend and asking._
  - [ ] **E.2.a [P1] (S)** Make GaussCtrl honest until it is real. In
        `libs/isis/3dgs-diffusion-editing/src/gaussctrl-depth-conditioned-controlnet.ts`,
        every output that names a rendered depth map or an edited view without
        having computed one becomes a typed `not_configured` result, and every
        capability flag reports what is bound. Acceptance: a spec asserts that
        with no depth renderer and no diffusion backend bound the module returns
        `not_configured` and emits no artifact address; the module's consumers
        still typecheck. _(Added 2026-09-18: the agent half of the parent, and
        the part the quality bar does not let wait.)_
- [ ] **E.3 [P2] (L) SyncNoise geometric noise prediction — real or fail-loud.**
      `syncnoise-geometric-noise-prediction.ts:190-203` emits `.bin` URIs with
      no noise computed. Acceptance: real shared-noise tensor predicted across
      views (test asserts cross-view consistency on a fixture) **or** fail-loud
      `[~]`. _2026-06-14: `[~]` per E.1 triage — flagged as the MOST fabricated
      module (claims geometry-projected shared noise across views, computes no
      noise/projection/correspondence, `.bin` URIs `:175-220`, tautology test
      `predictsSharedNoiseAcrossViews.toBe(true)`). A real shared-noise tensor
      needs camera-geometry projection + tensor compute (GPU/diffusion runtime),
      absent in this sandbox. Honest fail-loud conversion tracked in E.1._
      _2026-09-18: open, in two parts, as E.2. The module still emits `.bin`
      addresses for noise it never computes
      (`syncnoise-geometric-noise-prediction.ts`), which is the repository's
      bright line; part (a) below comes first._
  - [ ] **E.3.a [P1] (S)** Make SyncNoise honest until it is real. In
        `libs/isis/3dgs-diffusion-editing/src/syncnoise-geometric-noise-prediction.ts`,
        stop returning `.bin` addresses for a shared field, latents, projections
        and correspondences that were never computed, and delete the tautology
        test. Acceptance: a spec asserts `not_configured` and no artifact
        address when no tensor backend is bound. _(Added 2026-09-18: the agent
        half of the parent, and the part the quality bar does not let wait.)_
- [ ] **E.4 [P2] (L) Morpheus stylization + InterGSEdit + CtrlD — real or
      fail-loud.** One task per module; same bar. Acceptance: real edit applied
      (computed-value test) **or** honest fail-loud `[~]`. _2026-06-14: `[~]`
      per E.1 triage — these are MIXED: the real sub-logic is kept (Morpheus
      keyword preset/domain inference `:244-285`; InterGSEdit shoelace
      `polygonArea` `:587-597`; CtrlD temporal windowing `:297-333` + weighted
      coherence), but the actual stylization/edit/4DGS-diffusion APPLY step
      needs a diffusion backend absent here. Honesty defect to fix in the
      fail-loud conversion: InterGSEdit `projectedVisibilityRatio` returns
      hardcoded `0.72` (`:539-549`) as if a real 3D→2D reprojection — must
      become honest (real projection or absent), not fabricated. Real apply is
      K-class (GPU/diffusion)._ _2026-09-18: open, in two parts, as E.2; part
      (a) below comes first._
  - [ ] **E.4.a [P1] (S)** Make the Morpheus, InterGSEdit and CtrlD apply steps
        honest. Keep the real sub-logic the triage found; replace InterGSEdit's
        constant visibility ratio (`intergsedit-interactive-3d-editing.ts`, the
        `0.72` fallback) with a real projection or an explicit absent value, and
        return `not_configured` from every apply step that has no diffusion
        backend. Acceptance: one spec per module; none asserts a constant.
        _(Added 2026-09-18: the agent half of the parent, and the part the
        quality bar does not let wait.)_
- [ ] **E.5 [P2] (M) Material editing / object removal / reference insertion —
      real or fail-loud.** Same bar per module. _2026-06-14: `[~]` per E.1
      triage — MIXED: real sub-logic kept (material area-ratio + regex material
      NLP `:434-536`; object-removal FNV-1a `stableSeed` `:521-530`;
      reference-object-insertion real 4×4 TRS + rotation trig + vector normalize
      `:547-626` AND an honest fail-loud `resolveMaskSource→'unresolved'` seam
      `:490-504`), but PBR map synthesis / inpainting / segmentation +
      single-image-3DGS reconstruction all need diffusion/vision backends absent
      here. Honesty defect to fix in the fail-loud conversion: object-removal
      `replacementGaussianEstimate = removedGaussianCount * 0.7` (`:406-409`)
      presented as a reconstruction estimate — must become honest. Real apply is
      K-class._ _2026-09-18: open, in two parts, as E.2.
      `object-removal-inpainting.ts` still reports `removedGaussianCount * 0.7`
      as a reconstruction estimate; part (a) below comes first._
  - [ ] **E.5.a [P1] (S)** Make material editing, object removal and reference
        insertion honest. Keep the real sub-logic; replace
        `removedGaussianCount * 0.7` in `object-removal-inpainting.ts` with an
        explicit absent value until a reconstruction exists, and return
        `not_configured` from PBR synthesis, inpainting and single-image
        reconstruction when no backend is bound. Acceptance: one spec per
        module; none asserts a derived constant. _(Added 2026-09-18: the agent
        half of the parent, and the part the quality bar does not let wait.)_
- [x] **E.6 [P2] (S) Rewrite the editing-quality-validator tests to assert
      computed values.** Replace `toBe(true)` tautologies with metric assertions
      (e.g. edit fidelity / multi-view consistency against a known fixture).
      Acceptance: the validator would FAIL on a no-op edit. _Done/verified
      2026-06-14: read `editing-quality-validator.ts` + `.test.ts` this session
      — the validator is a REAL honest gate (`createMetricCheck :233-256`: a
      metric with no measured value → `warning` + `value: undefined`, never a
      fabricated score; `passesQualityBar` requires zero warnings AND zero
      threshold fails), and its tests ALREADY assert computed values, not
      tautologies: an out-of-threshold fixture asserts the exact
      `failedMetricIds).toEqual(['epipolar-error','depth-consistency'])` and
      `passesQualityBar === false`, and the "requires measured metrics before
      signoff" test proves a NO-OP edit (no real measurements) → warnings →
      `passesQualityBar === false`. So the acceptance ("the validator would FAIL
      on a no-op edit") is met by the existing real gate + computed-value tests;
      the only residual `toBe(true)` is the capability-contract descriptor, not
      a quality-metric tautology. No rewrite needed — verified, not assumed._

---

## Phase F — Generated-asset → Unreal cook/import bridge (the last mile)

Real primitives exist (`libs/bellona/unreal/src/cook/cook-runner.ts:124-244`
RunUAT; `headless-import.ts:159-262` UE Python `AssetImportTask`) but **nothing
dequeues a cook job** and no committed `.uasset` is editor-cooked generated art.

- [x] **F.1 [P2] (M) Build the cook-orchestrator worker.** A service/worker that
      dequeues cook jobs and invokes `cook-runner` + `headless-import`.
      Acceptance: enqueue a job → the worker runs RunUAT/import → a cooked
      artifact path is returned + recorded; fail-loud if UE is absent. _Done
      2026-06-14: `CookOrchestrator`
      (`libs/bellona/unreal/src/cook/cook-orchestrator.ts`, exported from the
      cook module + package index) — a FIFO queue (`InMemoryCookJobQueue`, a
      real swap-point for SQS/Redis) + a worker that dequeues cook/import jobs
      and drives the injected `UnrealCookRunner.cook` /
      `HeadlessImporter.importAssets`, recording each `CookJobOutcome`: a cook →
      `cooked` + the archive/staging (or derived `Saved/Cooked/<platform>`)
      artifact path; an import → `imported` + the editor's confirmed
      `importedObjectPaths`. FAILS LOUD with the real error code when Unreal is
      absent (`RUNUAT_NOT_FOUND`) or the cook/import errors — never a fabricated
      artifact. 7 tests (cook success + derived path, RUNUAT_NOT_FOUND
      fail-loud, UAT-ran-but-failed, import success, import-incomplete, mixed
      FIFO drain) via runner/importer test doubles at the process boundary;
      40/40 cook suite green; typecheck + stub scan clean. The on-engine run at
      volume is §F.3 (`[~]`); the isis-export→import wiring is §F.2._
- [ ] **F.2 [P2] (M) Wire isis-exported FBX/glTF → headless import → cooked
      `.uasset`.** Connect the isis 3D/image export output to
      `HeadlessImporter`. Acceptance: a generated mesh is imported and cooked
      end-to-end (a real `.uasset` produced), or `[~]` with the precise
      on-engine blocker named. _2026-06-14: `[~]` — producing a real `.uasset`
      requires running the UnrealEditor ImportAssets commandlet + a cook, and
      the on-box `UnrealEditor` REFUSES to run as root (must run as `ueagent`),
      needs a built V-project, and is the on-engine run §F.3. The bellona-side
      machinery is in place: §F.1's `CookOrchestrator` + `CookImportJob` +
      `HeadlessImportTask` accept any FBX/glTF `sourcePath` (an isis export
      included) and drive the real `HeadlessImporter`. The
      isis-export→import-task MAPPING cannot live in `@bellona/unreal`
      (scope:bellona may only depend on scope:shared/contracts/auth/bellona —
      NOT scope:isis), so it belongs at a composition/app layer that imports
      both; the on-engine import+cook is the genuine blocker → tracked under
      §F.3._ _2026-09-18: open for an agent. The executing machine has Unreal
      5.5, so the on-engine import and cook is no longer the blocker. The
      residue is the composition layer that maps an Isis export to a
      `HeadlessImportTask` (outside `@bellona/unreal`, which may not depend on
      Isis), proven by one generated mesh imported and cooked to a real
      `.uasset`._
- _Tracked once, elsewhere, since 2026-09-18: the `[UE-AUD-004]` cook items of
  `V1_V9_AUTONOMOUS_CONTENT_SOTA_GAP_CLOSURE_TODOS_2026-07-15.md` ("Produce/cook
  Isis cosmetic, venue, and related executor …", "Cook, place, and validate both
  through `V5/ue/V5.uproject` …") are this work, and that ledger declares this
  one history. The executing machine has Unreal 5.5._ **F.3 (L) On-engine cook
  of generated art at volume.** Run the bridge on the on-box UE5.5 as `ueagent`
  (editor refuses root — see `reference_onbox_unreal_engine`). Acceptance: N
  generated assets cooked + consumed by a V-project; `[~]` until the on-engine
  run is actually executed.
- [x] **F.4 [P3] (M) Real USDZ packaging + cross-DCC transform math.**
      `libs/bellona/unity-agent/usdz-ar-quick-look-export.ts` (delegates to
      Unity codegen) and `cross-dcc-usd-workflow.ts` (no Y-up↔Z-up / cm↔m matrix
      math). Implement real packaging (zip + 64-byte alignment) and real
      coordinate/unit transforms in TS, or label honestly as a Unity-runtime
      shim and mark `[~]`. Acceptance: a USDZ that opens in AR Quick Look (or
      `[~]`); a transform test asserts a known Y-up→Z-up matrix result. _Done
      2026-06-14: implemented `cross-dcc-transform.ts` — REAL coordinate/unit
      transform math (no more "left to the importer to guess"): per-DCC
      conventions (blender Z-up/m, maya Y-up/cm, houdini Y-up/m; USD Y-up/m/
      right-handed; Unity left-handed), `upAxisAlignmentMatrix` (Z-up↔Y-up =
      ±90° about X), handedness flip (negate Z), and cm→m unit scale, composed
      into one 4×4 matrix with `applyMat4Point`. WIRED into the workflow plan
      (`createBellonaUnityCrossDccUsdWorkflowPlan` now returns
      `coordinateConversion`, computed from `sourceDcc`). Computed-value tests
      (the acceptance bar): Z-up→Y-up known matrix
      `[1,0,0,0, 0,0,1,0, 0,-1,0,0,     0,0,0,1]`; Blender +Z→USD +Y, +Y→−Z;
      Maya 100 cm→1 m (scale 0.01); Houdini identity; Unity→USD handedness
      Z-flip; Blender↔USD round-trip — plus the plan-consumed assertion. 10/10
      unity-agent cross-dcc tests green; typecheck + stub scan clean. The USDZ
      **AR Quick Look** export stays a Unity-runtime shim → `[~]` for that
      criterion: opening a `.usdz` in AR Quick Look is an on-device (iOS)
      validation, not runnable here; the transform-math criterion is fully met._

---

## Phase G — Measure the taste signal (calibration & benchmarks)

The judge panel is architecturally calibrated but its κ-vs-human has never been
**run** with a live provider.

- [ ] **G.1 [P1] (M) Live calibration run per content type.** Feed the panel the
      human-rated gold items (`libs/euterpe/evals/.../human-eval` + any Studio
      accept/reject/edit records) with real provider creds; compute judge↔human
      Cohen's κ / correlation per content type. Acceptance: a calibration report
      with κ per content type; CI fails if a shipped judge's κ drops below its
      recorded baseline (the drift gate already exists — feed it real numbers).
      `[~]` only if no provider creds are available; then say so. _2026-06-14:
      `[~]` — no live LLM provider creds in this sandbox (only Claude Code
      session env vars; no `ANTHROPIC_API_KEY`/`LLM_ENDPOINT`). The κ/drift
      infra is real and runnable (`cohenKappa` in `human-eval.ts`,
      `scoreBenchmark`/ `drift.ts`, and the `GOLD_BENCHMARK` now covers all 5
      content types incl. wellness), but the JUDGE PANEL needs a real model —
      saying so per the task's own instruction rather than fabricating κ
      numbers._ _2026-09-18: open for an agent. The note's blocker, no provider
      credentials, no longer holds: `OPENROUTER_API_KEY` is in the environment
      file. Run the panel on the cheapest model that can judge (CLAUDE.md, "Test
      & harness model binding"), report the measured kappa per content type, and
      claim nothing about a stronger panel that was not run._
- [ ] **G.2 [P2] (M) External-benchmark sanity run.** Run EQ-Bench Creative
      Writing v3 (incl. slop score) + a LitBench-style reward eval against the
      panel/reward model; detect bias / reward hacking. Acceptance: a periodic
      report; large divergence from public norms triggers recalibration
      (`external-benchmark.ts` logic exists — run it for real). _2026-06-14:
      `[~]` — same blocker as G.1: the run needs a live model panel/reward model
      (no provider creds in sandbox). `external-benchmark.ts` is real and
      unit-tested; running it "for real" requires creds._ _2026-09-18: open for
      an agent on the same footing as G.1._
- [x] **G.3 [P2] (S) Gold-set persistence wired to a store.** The
      override→gold-label seam exists (`hitl-judge-evidence.ts`) but isn't wired
      to a production store. Persist human accept/reject/edit decisions to the
      Postgres gold-set table (the §3.2a Prisma backend) so Phase
      5/active-learning has real data. Acceptance: human decisions land in a
      queryable gold-set table across restart. _Done 2026-06-14: built the
      missing "§3.2 service" — `GoldSetEntryStore`
      (`libs/oshun/persistence/src/gold-set-entry-store.ts`) — that wires the
      two pure human-decision seams into the durable
      `v1_cross_cutting_gold_set_entry` table via the existing
      `ContractPersistenceService`. `recordOverride(label, prov)` maps the
      @yemaya §3.4 `OverrideGoldLabel` (accepted structurally, no orchestration
      dep) into a schema-validated `GoldSetEntryRecord`: a real
      `overridePromotionKind` derivation (reject→`reject-as-bad`; accept of
      panel-FAILED content→the high-value `override-as-good`; agreeing
      accept→`accept-as-is`) and `overrideSignalTags` that encode the panel
      verdict/score + human agreement as QUERYABLE tags
      (`judge-agreement:disagree` is the exact miscalibration signal §5.4
      lists). `record(input)` covers the Studio `CapturedDecision` path (incl.
      `edit-then-accept` with its edited-content ref), enforcing the same
      cross-field invariants `validateGoldSetEntry` does (fail loud on an edit
      with no content / a reject with content / a malformed UUID).
      `listDurableRows(tenant)` projects rows to the `DurableGoldSetEntryRow`
      shape §5.1's `goldSetFromEntryRecords` already assembles. 12
      computed-value unit tests run the REAL persistence + Zod path over a
      multi-row in-memory delegate; **1 integration test was RUN against a real
      Postgres** (`goldset_g3_test`, schema pushed) — two HITL overrides
      recorded through the seam, then a FRESH PrismaClient + store reads them
      back as durable rows with the right promotionKinds +
      `panel-score:73`/`judge-agreement:disagree` tags, proving decisions land
      queryable AND survive a process restart. 84/84 persistence unit tests
      green (14 DB-gated integration skipped without creds); typecheck clean;
      the production file has zero stub-scan hits (the only hits are test-double
      language in `*.test.ts`, allowed + scan-skipped). The Postgres-backed
      `GoldSetStore` aggregate seam is a thin
      `listDurableRows`→`goldSetFromEntryRecords` composition Phase 5 owns; the
      live calibration RUN that consumes this data stays §G.1 `[~]` (no provider
      creds)._

---

## Phase H — Stage per-product volume under the quality stack (quality-before-scale)

Do NOT start a product's volume generation until its quality + diversity gates
are wired (Phases A–C).

- [x] **H.1 [P2] (L) V5 — content famine, staged.** Generate side-quest +
      dialogue content (talk/escort/hunt/scavenge patterns) through best-of-N +
      judge gate + corpus diversity gate + human-assisted approval — **not** the
      single-pass `libs/hathor/narrative-generation/src/batch.ts`. Stage to a
      few hundred items, measure quality + diversity, then scale toward the 12k
      target. Acceptance: a measured-quality, diversity-gated batch; explicit
      STOP if quality/diversity regresses at scale. UE-side `V5Procgen`
      consumption `[~]`. _Done 2026-06-14: added `runStagedVolume`
      (`libs/hathor/narrative-generation/src/staged-volume.ts`) — the layer
      ABOVE the single-batch `QualityGatedQuestBatchGenerator` that the ledger
      says to use instead of `batch.ts`. It generates in STAGES (a few hundred
      at a time, not one 12k pass), and after each stage measures per-stage mean
      quality (judge floor) + corpus diversity (REUSING the shared
      `corpusDiversity` — cluster coverage + mean pairwise distance + cumulative
      near-duplicate rate) and HALTS with a precise reason the moment the trend
      breaks: `quality-below-floor` (a stage can't sustain passing content — the
      famine), `quality-regressed` (stage mean drops > margin vs the best prior
      stage), `diversity-below-floor` (a stage mode-collapses),
      `diversity-regressed`, `duplication-at-scale` (a new stage near-duplicates
      earlier content — real cross-stage `nearDuplicateRate`), `stage-empty`, or
      `target-reached`. The per-stage generation is an injectable
      `StageGenerator` (best-of-N + judge gate behind a real provider — the
      `[~]` model boundary); a generator that throws (no provider) propagates
      fail-loud, never fabricating. `questBatchStageGenerator` is the production
      adapter that drives the real `QualityGatedQuestBatchGenerator` per stage
      and maps its shipped (`submitted_for_review`) outcomes to staged items. 9
      tests use an injected scripted generator ONLY at the model boundary — the
      quality floors, diversity measurement, and regression STOP are REAL
      `corpusDiversity` over real texts: a healthy run completes at target; a
      low-quality stage, a mode-collapsed stage (measured clusterCoverage <
      0.6), a sharp quality-regression, cross-stage duplication (measured
      nearDuplicateRate > 0.2), and a dry generator each STOP with the right
      reason and ship only the healthy prior stages; the adapter maps only
      shipped outcomes. 33/33 narrative-generation tests green; typecheck +
      eslint (0 warnings) + stub scan clean. The live 12k generation (real
      provider) + UE `V5Procgen` consumption stay `[~]`; the staged
      orchestration, gates, and regression-STOP are real._
- [ ] **H.2 (L) V2 — fighter Side Story vertical slice.** One fighter's Side
      Story pack (Hathor §9.6 records → prose via the quality stack → Isis
      concept art → Bellona cook) end-to-end with judge gates. Acceptance: a
      playable slice in `V2/ue` with a quality-gated, provenance-bound bundle
      (`[~]` for the on-engine cook + concept-art generation at volume).
      _2026-09-18: a playable slice in `V2/ue` waits for V2's vertical slice,
      V2.VS.1–7 (`V2/V2_TODOS.md` section 0)._ `blocked:upstream`
- [x] **H.3 [P3] (M) V7 — forge AI-assist quality verification.** Confirm
      `libs/maya/forge-assist` generated artifacts are judged/refined before
      reaching the sandbox (within the platform/realm trust boundary).
      Acceptance: a low-quality assist is improved or rejected with named
      reasons; a real run exists (`[~]` for the V7 sandbox integration).
      _Verified 2026-06-14: read `forge-assist.ts` + `forge-assist.test.ts` this
      session — `ForgeAssistService` runs the capability policy AND a §9.6
      `ForgeQualityGate` (injected, panel-agnostic) BEFORE any artifact is
      proposed: `applyQualityGate` assesses the source, and while below
      `minScore` and refine passes remain it re-runs the assistant with the
      named quality reasons appended (critique→re-write), keeping the
      best-scoring attempt; a still-below-bar artifact returns
      `quality_rejected` with the best source + named reasons and NEVER reaches
      the sandbox. Tests assert real computed values: a high-quality artifact
      clears (score 85, 0 passes); a low-quality one is `quality_rejected` with
      the named reason `'no input validation; no edge-case guards'` (rejected
      source returned, not installed); a low→high refine lifts it to `cleared`
      (refinePasses 1); a policy violation short-circuits before the gate. Added
      the lib's missing local `vitest.config.ts` (was falling through to the
      root projects config). 15/15 forge-assist green; typecheck clean. The
      injected gate IS the platform/realm trust-boundary seam; the live V7
      sandbox mount is `[~]`._
- [x] **H.4 [P3] (M) V6 — dialogue at runtime through the gateway.** Depends on
      C.2. Acceptance: V6 dialogue passes the §9.5 judge gate through the
      gateway at runtime (`[~]` for the V6 HTTP/on-engine mount). _Done
      2026-06-14: builds on C.2 — added runtime tests driving the §C.2.4 moirai
      gateway mount (`createMoiraiCognitionGatewayMount`) through a real
      `CognitionGateway` configured with a §9.5 `DialogueQualityGate`: a
      below-bar Ori reply ("flat, low-energy") is BLOCKED at runtime
      (`response.quality.status === 'blocked'`, score 35) while a strong reply
      clears (`cleared`, score 90) — i.e. V6 dialogue passes the §9.5 judge gate
      through the gateway at runtime, with per-tier budget + kill-switches
      (C.2.4) around it. 7/7 mount tests green. The actual V6 HTTP/on-engine
      host that calls `handleDialogue` over the wire stays `[~]`; the TS runtime
      path (mount → gateway → §9.5 gate → blocked/cleared verdict) is proven._

---

## Phase I — V8 Ariadne (self-authoring detective) Phase 0 (spec → first vertical)

V8 is **0/90 tasks, no code**. "LLM proposes; a constraint solver disposes."
Build the smallest end-to-end vertical first; reuse the shared stack + V5 case
format.

- [x] **I.1 [P3] (M) Stand up `libs/v8` skeleton + the seven gates (G1–G7) as a
      `@oshun/content-release-gates` suite.** Do **not** build a bespoke
      per-product checker. Acceptance: a `v8-case` gate suite
      (fairness/solvability/voice/etc.) registered and unit-tested. _Done
      2026-06-14: created `@oshun/v8-case-gates` (`libs/v8/case-gates`,
      scope:v8; added `libs/v8/*` to pnpm-workspace + tsconfig path) —
      `buildV8CaseGates`/ `evaluateV8Case` register the seven V8 gates on the
      SHARED `@oshun/content-release-gates` `ReleaseGateService` (no bespoke
      checker), derived from the V8 spec (§I.1–I.3) + fair-play detective rules:
      **G1 fairness** (every solution clue presented before the reveal), **G2
      solvability** (the symbolic CSP proved a UNIQUE solution — the §I.2 flag),
      **G3 clue-grounding** (every solution clue exists in the case — no
      invented evidence, reuses the §7.2 `createGroundingGate`), **G4 voice**
      (suspect distinctiveness), **G5 misdirection** (≥1 red herring but ≤ max —
      fair, not a maze), **G6 prose**, **G7 safety**. 7 tests: the seven gate
      ids in order + all required; a fair/unique/grounded case clears; G1 blocks
      a withheld solution clue, G2 an under-determined case, G3 an invented
      clue, G5 no-herring AND a maze, G4/G6/G7 low voice/prose/safety. 7/7
      green; typecheck + stub scan clean. The CSP that PRODUCES the G2
      uniqueness proof is §I.2; per-case prose/art/voice generation is §I.3._
- [x] **I.2 [P3] (L) Symbolic mystery skeleton + CSP unique-solution proof.**
      Generate ground truth + clue logic and **prove** a unique solution
      (CSP/ASP, e.g. clingo-class). Acceptance: a generated case has a
      machine-verified unique solution; an under-determined case is rejected.
      _Done 2026-06-14: created `@oshun/v8-case-csp` (`libs/v8/case-csp`,
      scope:v8; registered in pnpm-workspace + tsconfig.base) — a REAL
      finite-domain constraint solver (no boolean stub). `csp-solver.ts`:
      backtracking search with node consistency, MRV variable ordering, and
      forward checking (with an undo log), enumerating solutions up to a cap;
      `proveUniqueSolution` finds up to two → `unique`/`under-determined`/
      `unsatisfiable`. Constraints are real relations: equals/not-equals/in/
      all-different/implies + a general n-ary `relation` predicate.
      `mystery-skeleton.ts`: a `MysterySkeleton` (dimensions + groundTruth +
      presented/withheld clues) compiles ONLY the player-visible clues into a
      `CspModel`; `proveCaseUniqueness` proves the visible clues admit exactly
      one solution AND that it equals the ground truth — surfacing
      `uniqueSolutionProven` (the §I.1 G2 flag), the verdict, and the witness.
      Rejects (uniqueSolutionProven:false, honest reason): under-determined (a
      deducing clue removed → 2 solutions), over-constrained/contradictory
      (unsatisfiable), and the real generation bug where the visible clues
      uniquely prove a DIFFERENT culprit than the author's ground truth
      (matchesGroundTruth:false). Malformed skeletons (GT out-of-domain /
      missing a dimension) fail loud. **Solver correctness is anchored against
      the canonical Zebra/Einstein puzzle** — the spec asserts it finds the one
      known solution (Norwegian drinks water, Japanese owns the zebra, full
      positions). **End-to-end:** the prover's flag drives the real
      `@oshun/v8-case-gates` G2 gate — a CSP-proven case clears the suite; an
      under-determined one is `blocked` with `g2:solvability` in blockedGateIds.
      Codex review (read-only, gpt-5.4) found one real soundness hole — a 0-ary
      `relation` was never evaluated → could falsely report `unique`; FIXED with
      a leaf-level full-constraint check + a regression test. 20/20 tests green;
      typecheck + eslint (0 warnings, module boundaries) + stub scan clean. The
      clingo/ASP sidecar (`apps/v8/minos-asp-sidecar`) stays out of scope — a
      real TS CSP prover meets the §I.2 acceptance in-repo; per-case
      prose/art/voice generation is §I.3._
- [x] **I.3 [P3] (L) Prose / art / voice generation per case through the shared
      stack.** Suspect portraits + evidence (Stability/Flux via isis),
      crime-scene art + props (Hunyuan3D/Meshy via isis), case music (Suno
      seam), VO (ElevenLabs seam) — each judged/gated + C2PA-stamped.
      Acceptance: one fully generated case bundle with provenance; missing
      providers fail loud `[~]`. _Done 2026-06-14: created
      `@oshun/v8-case-bundle` (`libs/v8/case-bundle`, scope:v8; registered in
      tsconfig.base) — `forgeCaseBundle` composes the whole V8 stack: it PROVES
      the case is uniquely solvable first (`@oshun/v8-case-csp` §I.2) and
      REJECTS an under-determined case (`CaseNotSolvableError`) BEFORE spending
      any generation (a test asserts the writer never runs for an unsolvable
      case); generates prose + clues via an injectable `CaseProseWriter` (the
      LLM boundary — absent ⇒ `CaseBundleNotConfiguredError`, never fabricated);
      generates each media asset (suspect-portrait / evidence-image /
      crime-scene-3d / case-music / voiceover) through injectable
      `CaseMediaGenerator` provider boundaries (Stability/Flux/Hunyuan3D/Meshy/
      Suno/ElevenLabs) — an absent provider for a kind yields an honest
      `{status:'not-configured', reason}` slot, NOT a fake asset; C2PA-stamps
      every PRODUCED asset with the shared `@oshun/content-signing` (real
      `sha256Hex` of the bytes + Ed25519 signature over a canonical manifest);
      and gates the assembled `V8Case` through the §I.1 seven-gate suite.
      Returns the bundle + provenance (solution hash, generated-asset count,
      not-configured kinds, signer key + public key). 5 tests (test doubles ONLY
      at the LLM/media provider boundaries — the solvability/C2PA/gating under
      test are real): a solvable case clears the gates with 2 generated + 1
      not-configured (music) asset; every generated asset's manifest verifies
      and its digest equals `sha256Hex` of the bytes; tampering the digest or
      uri fails C2PA verification; an under-determined case is rejected
      pre-generation; no writer fails loud. 5/5 green; typecheck + eslint (0
      warnings, cross-scope module boundaries) + stub scan clean. The LIVE
      providers (real image/3D/music/VO models) are the `[~]` remainder; the
      solvability gate, asset C2PA stamping, gating, fail-loud seams, and
      provenance are real._
- [ ] **I.4 (L) Compile a generated case into the V5 runtime case format + play
      it in `V8/ue`.** Acceptance: a generated case is playable in V5/UE (`[~]`
      on-engine). _2026-09-18: the inline `[~]` mark is gone — the executing
      machine has Unreal 5.5, so playing the case in `V8/ue` is work, not an
      environment gap. It still waits on a V5 runtime case format with real
      authored cases to compile into (V5-002 of the V1–V9 ledger; V8 tasks 4.6
      and 9.5 wait on the same corpus)._ `blocked:upstream`

---

## Phase J — V9 Metis (consumer learning) Phase 0 (spec → first vertical)

V9 is **0/57 tasks, no code**, but its composed domains (Metis, Nyx, Kalika,
Mnemosyne, Sophia) are **real**. Gap = connective tissue + consumer UX. V9
mandates reusing the platform content-release gate ("no eighth loop").

- [x] **J.1 [P3] (M) Stand up the seven-gate eval suite (G1–G7) on the platform
      content-release gate.** Acceptance: a `v9-lesson` gate suite
      (truth/grounding/ teachability/etc.) registered + tested; no bespoke
      checker. _Done 2026-06-14: created `@oshun/v9-lesson-gates`
      (`libs/v9/lesson-gates`, scope:v9; added `libs/v9/*` to pnpm-workspace +
      tsconfig path) — `buildV9LessonGates`/`evaluateV9Lesson` register the
      seven V9 gates on the SHARED `@oshun/content-release-gates`
      `ReleaseGateService` (no bespoke checker, per the "no eighth loop"
      mandate), each derived from the V9 spec (§J.1–J.3): **G1 truth** (Aletheia
      — no false/unverified claim), **G2 grounding** (every claim bound to a
      Sophia source pin — reuses the §7.2 `createGroundingGate`), **G3
      teachability**, **G4 safety**, **G5 explorable** (≥1 REAL computed
      Nyx/Kalika explorable, not a static asset), **G6 adaptive** (per-learner
      difficulty in [0,1]), **G7 retrieval** (Mnemosyne checkpoint). All seven
      are required `GateDefinition`s over
      `gateFromEvalScore`/`gateFromManifestCheck`/`createGroundingGate`. 7
      tests: the seven gate ids in order + all required; a
      grounded/true/teachable/ explorable lesson clears; G1 blocks a false
      claim, G2 a pin-less claim, G5 a static-only explorable, G7 a missing
      checkpoint, G3/G4/G6 low-teach/ unsafe/out-of-range. 7/7 green;
      typecheck + stub scan clean. (J.2/J.3 lesson-forge + explorables remain —
      they need the live agentic loop + provider/WASM.)_
- [x] **J.2 [P3] (L) Lesson forge on the shared agentic loop, grounded to Sophia
      pins.** "Ask a wonder → grounded lesson"; every claim bound to a Sophia
      source pin (Aletheia truth gate) — reuse the grounding gate. Acceptance: a
      lesson with an ungrounded claim is blocked; a grounded one passes with
      source pins. _Done 2026-06-14: added `forgeLesson`
      (`libs/v9/lesson-explorables/src/lesson-forge.ts`) — the V9 forge that
      routes generation through the SHARED agentic loop (an injectable
      `LessonDraftWriter`, the `CognitionGateway` model boundary), grounds every
      claim to a Sophia source pin (`SophiaPinGrounder`) + an Aletheia truth
      verdict (`AletheiaTruthChecker`), assembles the lesson with its §J.3
      computed explorable / adaptive score / retrieval checkpoint, and gates it
      through the shared §J.1 seven-gate suite (`evaluateV9Lesson` — reuses the
      grounding gate, no new loop). A claim with no Sophia pin is BLOCKED at G2;
      an Aletheia-untrue claim at G1; a grounded+true lesson clears with its
      pins attached. The model boundary fails loud
      (`LessonForgeNotConfiguredError`) when no writer is wired (no provider in
      this sandbox) — never fabricating a lesson — and refuses a claimless
      (unfalsifiable) draft. 5 forge tests use test doubles ONLY at the
      writer/Sophia/Aletheia dependency boundaries (the grounding + gating under
      test are real): grounded+true → cleared with `sophia://` pins; one
      ungrounded claim → blocked with `g2:grounding`; an unverified claim →
      blocked with `g1:truth`; no writer → fail loud; no claims → refused. 21/21
      lesson-explorables tests green; typecheck + eslint (0 warnings) + stub
      scan clean. The LIVE writer (a real provider behind the shared loop) +
      Sophia/Aletheia services stay `[~]` — the forge orchestration, grounding,
      truth-gating, and fail-loud seam are real and tested._
- [x] **J.3 [P3] (L) Embodied tutor + ≥1 computed explorable + adaptive score
      per lesson.** Tutor voice/face/persona (Psyche+Hathor+Isis); a real Nyx
      WebGL sky or Kalika WASM physics explorable; a Mnemosyne retrieval
      checkpoint. Acceptance: a lesson renders a real computed explorable + a
      retrieval check; provenance bound. _Done 2026-06-14: created
      `@oshun/v9-lesson-explorables` (`libs/v9/lesson-explorables`, scope:v9;
      registered in tsconfig.base) — the FIRST consumer of the real-but-dormant
      shared engines, composing them (no new physics/psychometrics, per the
      "reuse the shared stack" rule). **Computed explorable (G5):**
      `buildOrbitExplorable` integrates a real two-body Kepler orbit with
      `@kalika/symplectic`'s velocity-Verlet symplectic integrator — perihelion
      start `(r₀,0)`, vis-viva speed `√(GM(1+e)/r₀)`, Kepler-III period — and
      reads back the trajectory + the engine's energy diagnostic; `computed` is
      true only when a finite multi-sample trajectory was produced.
      Computed-value tests: a circular orbit conserves energy (drift<1e-3) and
      radius (band<0.01), v₀=1 & T=2π; an e=0.3 ellipse has the right
      perihelion/aphelion geometry (aphelion≈a(1+e)) and bounded symplectic
      energy. **Adaptive (G6):** `computeAdaptiveDifficulty` estimates learner
      ability θ via Mnemosyne's IRT MLE (`estimateAbility`, Newton-Raphson) then
      calibrates next-item difficulty `b*=θ−logit(p*)` to a target success rate
      — the verifiable Rasch identity `irt1PL(θ,b*)≈p*` is asserted to 6 dp; a
      stronger learner → higher difficulty; higher target → easier. **Retrieval
      (G7):** `buildRetrievalCheckpoint` runs Mnemosyne SM-2 (`sm2Review`,
      explicit clock — no fabricated time) producing a durable `mnemo:…`
      checkpoint ref; tests assert the exact SM-2 ladder (1→6→round(int·ease)) +
      lapse reset. `assembleV9Lesson` binds all three into a `V9Lesson` +
      provenance (run id, explorable content hash + energy drift, θ, adaptive
      difficulty, SM-2 ref). **End-to-end:** `evaluateV9Lesson(assembled)`
      CLEARS all seven shared gates; swapping the computed explorable for a
      static asset is blocked by G5, and an empty checkpoint by G7 — proving the
      gates consume the real computed artifacts. Codex-reviewed (read-only,
      gpt-5.4): physics initial conditions, potential gradient, and the Rasch
      calibration confirmed correct, no sign/formula bugs (one comment-wording
      nit fixed). 16/16 tests green; typecheck + eslint (0 warnings, cross-scope
      module boundaries) + stub scan clean. The embodied tutor voice/face
      (Psyche/Hathor/Isis providers) + the live agentic lesson-forge (§J.2) stay
      `[~]` (provider-gated); the computed explorable + adaptive score +
      retrieval checkpoint + provenance — the stated acceptance — are real and
      met._
- _Tracked once, elsewhere, since 2026-09-18: the closed-beta item of the V1–V9
  ledger's V9 slice ("Run closed beta, mobile/offline, localization, cost,
  safety, privacy, …"); it needs a human cohort._ **J.4 (L) Closed beta +
  efficacy study.** Acceptance: operational; `[~]`.

---

## Phase K — External-gated honest deferrals (track, do not fake)

- [ ] **K.1 (XL) Trained taste model (DPO / reward model) v2.** Train a
      dedicated preference scorer on the gold sets when volume permits. Requires
      GPU/training infra. Acceptance: trained scorer beats the panel on held-out
      agreement, or stays `[~]` with an honest reason. Never fabricate metrics.
      _2026-09-18: training a scorer needs a compute budget and a gold set large
      enough to train on; neither has been decided._ `blocked:governance`
- [ ] **K.2 (M) Inference-time thinking budget tied to quality.** Wire the real
      `budget_tokens` seam (`libs/isis/ai-providers/.../claude4-adapter.ts:467`)
      into deep/exhaustive modes for hard generation/judging. Acceptance: deep
      mode demonstrably allocates a thinking budget that reaches the model and
      improves the judge score on a hard fixture (needs a live model → `[~]`).
      _2026-09-18: open for an agent. Carry the budget through the
      provider-agnostic request, prove that it reaches a model on OpenRouter's
      reasoning parameter with the cheap test model, and cover the Claude
      adapter's `budget_tokens` path with a transport double; no test binds a
      frontier model._
- [ ] **K.3 (XL) On-engine cooks, certification, VO/voice, art-at-volume.** The
      cross-cutting on-engine + vendor deferrals (UE cook/cert, ElevenLabs VO,
      Suno music, concept-art at volume). Track per product; `[~]` until
      executed on the real engine / with real vendor creds. _2026-09-18: a
      roll-up of vendor and engine deferrals (console certification, ElevenLabs,
      Suno, art at volume); each product's tracker carries its own items, and
      the vendor keys are the owner's._ `blocked:external`

---

## Suggested critical path

`A.1–A.4 (honesty) → B.1–B.2 (consume concert + commentary gates) → C.1–C.2 (connect Lilith + V6 generators) → G.1 (measure the taste signal live) → A.2/D (real concert signing) → E.1 (3DGS triage) → F.1–F.2 (asset→engine bridge) → H.1 (V5 staged volume) → J (V9, de-risked) → I (V8) → E.2+ / F.3 / K (heavy + external-gated)`.

Rationale: stop the fabrications first (trust + `CLAUDE.md` compliance), then
get maximal leverage by **consuming gates and connecting generators that already
exist** (B, C) — that is where "SOTA engine, partial reach" becomes "SOTA reach"
with the least new code — then measure (G), then the heavier asset and product
build-outs.
