Disciplines · Audits

Agentic Content — Gap-Closure Implementation Ledger

in prose mean nothing.

16sections59 minread

On this page

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.

  • 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.
  • 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.)
  • 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.
  • 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:3599meditation-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.)
  • 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#

  • 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.
  • 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.
  • 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'.
  • 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#

  • 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.)
  • 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#

  • 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.
  • 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.
  • 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#

  • 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.)
  • 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.)
  • C.1.3 [P1] (M) Generate meditation scripts via best-of-N + self-refine. Route the LLM draft through generateCandidatesselectBestOfNselfRefine (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 (generateCandidatesselectBestOfNselfRefine 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.)
  • 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.
  • 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.
  • 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#

  • 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,328createCognitionGateway → 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.)
  • 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.
  • 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.)
  • 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)#

  • 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.
  • 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.

  • 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.tsREAL (per-Gaussian inverted index + DAG revert, :180-367; test asserts the computed index → FAILS on a stub). Decision: keep, real-algorithm.editing-quality-validator.tsMIXED 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.tsMIXED, richest real math (4×4 TRS + rotation trig + vector normalize :547-626) AND an honest fail-loud seam (resolveMaskSource→'unresolved' :490-504inserted: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.)
  • 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.

  • 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.
  • 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.
  • 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 listDurableRowsgoldSetFromEntryRecords 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).

  • 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
  • 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 [~].
  • 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.

  • 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.
  • 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.
  • 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").

  • 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 GateDefinitions 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.)
  • 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.
  • 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.