Disciplines · Audits

Oshun V1 — Production-Readiness Gap Analysis

its domain logic than in its integration**, and the gap is unusually uniform.

10sections52 minread

On this page

Date: 2026-05-28 Scope: The entire V1 implementation as specified in V1/{ARCHITECTURE.md, features.md, TODOS.md, DEPENDENCIES.md}, audited against the code under apps/oshun/*, apps/metis/*, libs/oshun/*, libs/contracts/*, libs/{sophia,nisaba,metis,themis,yemaya,aje}/*, and libs/shared/*. Method: 24 parallel adversarial code audits (two-pass: confirmatory coverage map + stub hunt + test-coherence check), each reading the actual source and running the project's mandated stub-indicator grep. Findings below are backed by file:line evidence.


1. Executive Verdict#

Oshun V1 is NOT production-ready. It is, however, much closer to ready in its domain logic than in its integration, and the gap is unusually uniform.

The single most important finding:

The domain-logic libraries are real, deep, domain-specific, and well-tested. The product is not wired together. Across nearly every subsystem, the libs/oshun/* substrate computes the correct decision (a recall ranking, a crisis classification, a budget verdict, a release-gate pass/fail, an entitlement) as a pure function — and then nothing consumes that decision at runtime. Persistence is in-memory Maps, domain adapters point at backends that don't implement the contracted routes, and the customer/operator surfaces are fed hardcoded fixtures, several of which are actively dangerous.

This is a good position to be in (the hard algorithmic work is largely done and verified) but it means the remaining work is integration, persistence, and a few genuine engine builds — substantial, multi-team, multi-week effort, not a finishing pass.

A blunt summary of the three failure classes:

  1. Wiring gaps (most of the codebase). Real logic, no runtime consumer / no concrete adapter / no persistence. Examples: Iris memory, Lilith crisis classifier, Isis generation gates, Agentic-Studio budgets & kill-switches, Studio release gates, Trust-Safety queues, Billing-Support, the whole admin BFF (in-memory Map, no DB).
  2. Genuine fabricated-code stubs (a handful, but serious). Code that returns invented results: Metis worker analytics/reports (seeded-RNG fake metrics), Metis research-agent (fabricates sources — directly violates a stated requirement), Yemaya render core (simulated rendering + fabricated telemetry, no pixels), Living Scenes (no render/blend kernel, faked C2PA signature, watermark embedding absent, takedown cascade mutates nothing), Privacy DSAR/deletion (soft-flag only; cryptographic-shred flag copied from input), Veritas retraction cascade (in-memory planner, never cascades), and synthetic on-chain deposit addresses for Tron/TON/Ergo/Cardano.
  3. Dangerous fixtures shipped to real surfaces. The BFF domain-stubs.ts and web lilith-data/* fixtures render fabricated data with 200 OK on production surfaces: a dead crypto receiving address for payments, hardcoded consent/memory-pause state (compliance hazard), fabricated audit logs / SSO connections / incidents shown to operators as authoritative, fabricated astronomy on nyx/tonight, and a fabricated "verified" news claim from a fake source on the Veritas home briefing.

2. Readiness Scorecard#

Legend: ✅ Production-grade (within audited scope) · 🟡 Real logic, blocking gaps · 🔴 Not ready / contains fabricated code

Customer-facing domains#

Domain Verdict Headline gap
Tara Logic + contracts production-grade. Only minor: 4-week-only plan limit, dead branches.
Arete Real humane streak/recovery + friction/intervention engines; full contracts; coherent tests.
Nisaba Strongest domain. Real in-repo morphology (9 ancient languages), concept graph, lexicon, study plans.
Veritas 🔴 Retraction cascade is an in-memory planner that never cascades (no route/event/Sophia invalidation); contradiction loop + Sophia grounding are interface-only.
Nyx 🟡 Real ephemeris built + wired: Meeus sun/moon position, phase, rise/set, alt/az (ephemeris.ts, Meeus-verified); nyx/tonight now serves REAL computed sky (buildNyxTonightCard) — the BFF route + fixture are gone. Remaining: the 19 power-user depth pages.
Metis 🔴 The named "7 release gates" pipeline doesn't exist as a gate; api-gateway proxies to an out-of-repo Python backend; worker analytics/reports/exports are seeded-RNG fakes; research-agent fabricates sources.

Platform substrates#

Substrate Verdict Headline gap
Sophia Contract/transform layer correctly delegates to real libs/sophia/*. Follow-up: per-query source-set scope confinement deferred to backend (spec calls cross-set leakage a hard fail).
Search & Discovery Genuine BM25/cosine/NDCG/IR + deterministic A/B; the strongest test suite in the audit.
Psyche Pure policy/contract layer is production-grade (real turn-taking, ARPABET visemes, reconnect). Live WS/WebRTC transport lives in out-of-repo services/psyche/*.
Persona Registry 1422 tests; real role-inheritance algebra, abuse scorecards, impersonation detection.
Lilith 🟡 All safety logic real & tested, but the rich crisis classifier is not wired into its own adapter (adapter depends on an unimplemented injected analyzeSafety).
Iris 🟡 Real consent/recall/deletion logic. Durable revision-chain persistence now built (DurableMemoryStore over iris_memory_entry_revision; §8), incl. a real forget hard-purge for DSAR. Remaining: wire the adapter to live surfaces (consumer is still a localStorage demo).
Isis 🟡 Gate enforcement genuinely blocks dispatch and is tested — but the library has zero importers, no tsconfig alias; not wired to any surface. Tier resolver / surface guard / LoRA training absent.
Aje / Payments 🟢 No fabricated settlement, and all four non-EVM rails (TRON, Ergo, TON, Cardano) now derive real, chain-valid watch-only deposit addresses (keccak/base58check, blake2b-256/base58, v4r2 StateInit hash, CIP-19/bech32; each verified against authoritative per-chain vectors/oracles; §8). The synthetic-address gap is closed.

Surfaces, shell, studio, governance, foundations#

Subsystem Verdict Headline gap
Consumer Shell (shell-core/-assistant/-routines/-achievements/-desktop/-wearable) Real cross-domain ordering, NLU, Iris bridge; 1563 tests. Only 2 hollow wearable complications (data-model gap).
Foundations (domain-registry, persistence, platform-foundations, navigation, offline, i18n, contracts) 🟡 Overwhelmingly real (85-model Prisma schema + real migrations, OAuth/PKCE, durable queue, event-bus w/ HMAC). One defect: broken @veritas/contracts tsconfig path (empty dir).
Themis (academic-integrity adjudication) Real deterministic adjudication engine; calibrated policies; Metis contract bridge; coherent tests.
Auth Real signed-JWT verification (alg-pinned, timing-safe), prod-hardened, RBAC denies, no bypass, no hardcoded secrets. One pre-GA action: confirm edge proxy strips inbound x-oshun-tier headers.
Studio + Curation 🟡 All 11 areas real (Flesch, DFS cycle detection, Myers/LCS diff, CNF rules), but the findings→publish-gate loop is open and nothing persists/enforces at runtime.
Agentic AI Studio 🟡 High-quality governance primitives (SSRF guards, z-score anomaly, BFS revocation), but no orchestrator consumes them; killed isn't even a run status; budgets/kill-switches decide but never act.
Tenant Console 🟡 Library production-quality (RFC-4231-correct HMAC, xAPI/Caliper, hash-chain audit). But forgeable unsigned session token; LTI 1.3/SCORM missing; SAML/OIDC signature verification assumed-upstream.
Messaging Channels 🟡 Real Telegram crypto (initData/login HMAC) + real grammY delivery + tier routing. But only Telegram delivers (Email/Push/SMS/WhatsApp are payload-only); bot voice STT is a placeholder; @noble/hashes skew breaks bot tests.
Billing-Support 🟡 Real entitlement/proration/tax/dunning/metered logic, but not wired to Aje or any app — an island with a parallel entitlement model.
Trust-Safety 🔴 Real decision functions, but pure functions with no queue/audit-platform wiring; the crisis-frame cascade does not exist as propagation (a named exit criterion).
Privacy 🔴 DSAR/deletion does not enforce — soft-flag only; cryptographicShredApplied is copied from caller input; no cross-domain tombstone fan-out. The one subsystem that must enforce, doesn't.
Analytics 🟡 Real event-collection SDK; no fabricated metrics (clean). But recommendation-observability & offline-eval exist only as declarative manifests — no aggregation/eval engine. 14.5k LOC of out-of-scope V3 bloat.
Living Scenes 🔴 Governance/lifecycle control plane is real, but no render/blend pipeline exists, C2PA signature is faked (SHA-256, not Ed25519), watermark embedding absent, takedown cascade mutates nothing, no persistence.
Yemaya (render substrate) 🟡 A real raster kernel + composition renderer + an orchestrator render path now exist: raster-kernel.ts (W3C source-over + separable blend modes, genuine separable Gaussian blur, radial vignette, multi-layer stack), rasterizeComposition(Composition) (scene-model solid layers → actual RGBA8 pixels with anchor/rotation/scale fidelity), and RenderCore.renderComposition (produces pixels + measured metrics; executeJob is now the cost planner), with anti-aliased rotated edges, a 12-effect per-layer pass, and a real server-side PNG codec (decode/encode RGB+RGBA, all 5 filters, CRC-checked, node:zlib via dynamic import so the browser bundle is unaffected) — all known-answer verified (§8). Remaining: GPU/wgpu acceleration, wiring decoded textures into the composition (texture sampling), and text-shaping (font engine). Conductor/scheduling & vector/SVG/text primitives are real.
Oshun BFF 🔴 Real auth/adapter/middleware skeleton, honest 503s on live customer routes — but the data plane is fixtures: 31 pure-fixture GETs, no idempotency middleware (spec-mandated), in-memory rate limiting, dangerous fabricated payloads.
Oshun Web (consumer) 🔴 All 6 canonical domain hub pages render hardcoded fixtures with no BFF call; two surfaces fabricate convincing data on failure (sky events; a "verified" news claim); profile/memory & power-user Arete persist to localStorage only.
Admin Web + Mobile 🟡 ~10 of 14 workflow areas operationally wired (privacy/review/incident/model/persona/rights/research-integrity/editorial). But Trust&Safety has no moderation actions, Support/Billing has no refund/credit/entitlement actions, Metis governance is absent, and the entire admin BFF is in-memory, no DB.

3. BLOCKERS (P0 — fabricated code or dangerous behavior on a live surface)#

These are not "missing features" — they are code that returns invented results or ships harmful data. Every one would fail the project's own adversarial-verification bar.

  1. BFF returns a dead crypto receiving address. apps/oshun/bff/src/routes/domain-stubs.ts:~1664POST /v1/payments/crypto/quote returns receivingAddress: '0x000000000000000000000000000000000000dead' with 200. A customer following the crypto-pay journey is handed a burn address. (Financial.)
  2. BFF fabricates consent / memory-pause / audit / SSO / incident data with 200 OK. domain-stubs.tsGET /v1/data-rights/consent and GET /v1/memory (paused:false) return hardcoded privacy state (compliance hazard); GET /v1/audit, /v1/sso, /v1/incidents, /v1/user-reports return fabricated/empty data rendered directly by the operator console as authoritative. (Compliance / operational integrity.)
  3. Web fabricates astronomy and "verified" news on backend failure. apps/oshun/web/src/lib/server/nyx-depth.ts:74-97 (tonightFixture() invents sky events); apps/oshun/web/src/components/home/homeVeritasBriefingModels.ts:110-176 (FALLBACK_HOME_BRIEFING presents a fabricated claim with verdict:'verified' from a fake source). Distinguished only by a subtle pill. (Trust — directly inverts the V1 "visible grounding state" promise.)
  4. Metis worker analytics/reports/exports are seeded-RNG fakes. apps/metis/worker/src/jobs/analytics-jobs.ts:254-298, report-jobs.ts:755-924, course-export.ts:834 — every metric (activeUsers, completionRate, retention cohorts, exported course content) is invented from a hash-seeded RNG that ignores real inputs. (These are launch-blocking learning-telemetry surfaces.)
  5. Metis research-agent fabricates sources. libs/metis/agents/src/agents/research-agent.ts:256-268 — manufactures a source list (Author A/B/C, invented relevanceScore/reliabilityScore). The spec explicitly requires fabricated-research blocking; this does the opposite.
  6. Privacy DSAR/deletion does not delete. libs/oshun/privacy/src/export-deletion/deletion.tscryptographicShredApplied is copied from input.canShred; no data is touched, no tombstones written. dsar.ts marks scopes complete from an operator-supplied verdict; consent.ts planWithdrawalCascade returns pending steps that never execute. The spec's mandated cross-domain tombstone fan-out (ARCH §1760-1794) does not exist.
  7. Veritas retraction cascade never cascades. libs/oshun/domain-veritas/src/retraction-cascade.ts:73-127 is a pure in-memory planner; there is no retract route, no event publish, no Sophia evidence-pack invalidation anywhere in the repo. The only caller is a web component feeding hardcoded fixtures.
  8. Yemaya render core simulates rendering. libs/yemaya/rendering-pipelines/src/render-core/render-core.ts:1503 ("Simulate stage work"), fabricated itemsProcessed/memUsed/frameCount:1/outputSizeBytes; visual-compositor.ts:822 blur "simulates gaussian blur via value damping" on a single color. No pixel buffer is ever produced; no Rust/wgpu/GPU/canvas kernel exists.
  9. Living Scenes has no render/blend pipeline and faked provenance. Conductor dispatches nothing; Blend Kernel performs no blending; the wired C2PA "signature" is sign = stableHash (SHA-256, not Ed25519/X.509); audio/visible watermark embedding is absent; takedown dispositions mutate nothing; persistence is in-memory Maps. The PSE/strobe live-frame safety killer (a hard no-override requirement) is unimplemented.
  10. Synthetic on-chain deposit addresses (Tron/TON/Ergo/Cardano). chains/tron/src/node-client.ts:148-164, chains/ton/src/subwallet.ts:94-122, chains/ergo/src/p2pk-allocator.ts:8-11, chains/cardano/src/node-client.ts:88 — per-invoice addresses are sha256-derived "synthetic, base58-like" strings, not chain-valid. A customer paying these would not be paying a node-recognized address. (Litecoin/EVM/Monero are correct and show the right pattern.)

4. MAJOR wiring gaps (P1 — real logic, no runtime consumer / persistence)#

These do not fabricate, but the feature is non-functional end-to-end until wired.

  1. No persistence anywhere it matters. The admin BFF (apps/oshun/bff/src/admin/state.ts, 30,824 lines) is an in-memory Map with reset() + fixture seeders — every governance action is non-durable and per-instance. Living Scenes, Trust-Safety, Iris, Studio, Tenant-admin surfaces are likewise in-memory or pure-functional. Only domain-stubs-postgres.ts (tara completions, telegram links, SCIM CRUD) and the Prisma layer are DB-backed.
  2. Customer domain adapters point at non-existent contract routes. The BFF's real HTTP adapter calls /v1/oshun/*; the standalone domain APIs (apps/tara/api, …) serve a different namespace and implement zero /v1/oshun/* routes → 503 in prod. So the honest path serves nothing and the populated path is fixtures.
  3. No idempotency middleware in the BFF. ARCH §1064-1108 mandates idempotency as the first step of every write; it does not exist (0 non-test hits). Rate-limiting is in-memory (non-distributed). (Idempotency fixed in this pass — see §8; distributed rate-limiting still pending.)
  4. Lilith crisis classifier not wired to its adapter. canonical-adapter.ts assessSafety depends on an injected apiAdapter.analyzeSafety with no concrete implementation; the validated 13-rule crisis catalog (detectLilithCrises) is unreachable from the adapter path. (Fixed in this pass — see §8.)
  5. Isis generation control has zero importers. No BFF route, no admin app, no tsconfig alias routes through dispatchGeneration/routeApprovedOshunMediaGenerationRequest. The "Isis is the only path; raw provider machinery is never customer-facing" guarantee cannot hold until a surface routes through it.
  6. Agentic-Studio enforcement decides but never acts. No orchestrator consumes decideExecution/checkBudget/acquireToken; the run state machine has no killed status; no automatic budget/kill-switch → run-termination path. Exit criterion ("budgets honored, kill switches enforced") not met end-to-end. (Fixed in this pass — see §8.)
  7. Studio findings→gate loop is open. block-severity AuthoringEvaluationFindings are not folded into EditorialGate.satisfied; gate satisfaction is caller-supplied, so blocking findings don't actually block publish. (Fixed in this pass — see §8.)
  8. Iris has no concrete persistence adapter. IrisMemoryApiAdapter is an unimplemented interface; createCanonicalIrisMemoryAdapter has zero production callers. Privacy guarantees are correct in code but not enforced against real data. DSAR export only covers scene/pose scopes.
  9. Trust-Safety crisis-frame cascade is absent. crisis/crisis.ts does single-domain routing/records but never publishes lilith.crisis_frame.activated or projects the frame into Tara/Living-Scenes/Isis/Assistant/Iris. The cross-surface cascade (ARCH §1692-1740) does not exist.
  10. Metis runtime disconnect. The api-gateway/web proxy to an out-of-repo Python backend; the real TS grading/IRT/mastery/adaptive logic in libs/metis/* is not imported by any in-repo runtime; the adaptive loop is never instantiated with production runners. The unified 7-gate release pipeline does not exist (4 of 7 enforce in scattered places; grounding/pedagogy/drift are helpers only).
  11. Billing-Support is an island. No imports of Aje/payment adapter; not consumed by any app; a second, parallel entitlement model lives in auth-client.
  12. Tenant Console: forgeable session + missing protocols. Unsigned base64 session cookie defeats tenant isolation in prod; LTI 1.3/LTI Advantage/SCORM absent; SAML/OIDC signature verification assumed-upstream (not located in-subsystem).
  13. Admin missing workflow areas. Trust&Safety (no moderation/appeal/crisis actions — read-only + copilot-feedback only), Support/Billing/Entitlements (no refund/credit/grant/case-resolution), and Metis governance (no surface) fail the "operate ALL workflows" exit criterion.
  14. Messaging delivery transports. Only Telegram actually sends; Email/Push/SMS/WhatsApp are payload/plan builders with no wired SES/Twilio/FCM/APNs/Meta client; Discord/Slack are gating-only; bot voice STT is a placeholder.

5. MINOR (P2 — cleanups, latent footguns)#

  • @veritas/contracts broken tsconfig path → empty .gitkeep-only dir; dangling alias (zero importers today). Removal deferred: tsconfig.base.json is a global Nx dependency, so editing it forces an affected-typecheck across the entire monorepo, which currently fails on unrelated pre-existing WIP package errors. Remove it in a dedicated cleanup commit once the repo-wide typecheck is green.
  • @noble/hashes version skewpayments-bridge cold-spend-queue imports @noble/hashes/sha256 (dropped in v2.2.0); breaks telegram-bot test loading. (Fixed in this pass — see §8.)
  • Veritas bandFromComposite redundant 'low' branch (banding threshold dead); disagreement substring heuristic on free-text rationale.
  • Isis admitCivitaiImportedModelAtRuntime doesn't implement its documented customer-facing check (defense-in-depth only; the binding check is upstream).
  • Tara: 4-week-only practice-plan continuation; vestigial crisis-type branch; static recommendation table.
  • Shell-wearable: buildAchievementProgressComplication/buildChallengeProgressComplication return value:0 (no source fields in WearableUserStats).
  • Yemaya/Living-Scenes contract RenderEnvelopeSchema diverges from the runtime envelope (missing reproducibility fields).
  • Billing-Support: unused nowUnixSeconds param + dead past-period-end error code.
  • Telegram bot: prod token silently defaults to literal 'prod-token' when env unset (should fail-fast). (Fixed in this pass — see §8.)
  • Analytics: 14.5k LOC of out-of-V1-scope V3 game-engine gates inflate the package.

6. What IS production-grade (credit where due)#

Not everything is a gap. The following are genuinely real, domain-specific, and well-tested — they should be trusted and not re-litigated:

  • Tara, Arete, Nisaba domain logic + contracts (Nisaba's in-repo morphology across 9 ancient languages, concept graph, and lexicon are exceptional).
  • Search & Discovery (real BM25/cosine/NDCG/MAP, deterministic FNV-1a A/B bucketing, 123/123 coherent tests).
  • Sophia evidence/transform layer; Psyche policy/contract layer; Persona Registry (1422 tests); Themis academic-integrity adjudication.
  • Consumer Shell (all 6 libs; real ordering/NLU/Iris bridge; 1563 tests).
  • Auth (real alg-pinned signed-JWT verification, RBAC denials, no bypass, no hardcoded secrets).
  • Foundations: domain-registry matches the spec table; 85-model Prisma schema with real migrations and contract-drift enforcement; libs/shared/* substrates (idempotency 692 LOC, durable queue + DLQ, event-bus with HMAC signing, residency enforcer, audit-platform) are real.
  • Aje payments money path: real per-asset confirmation ladder, real RPC confirmation reads, Ed25519 receipt signing, non-custodial watch-only invariants; Litecoin/EVM/Monero address derivation is correct.
  • Lilith / Persona safety algorithms (crisis classifier, voice-abuse scoring, impersonation detection); Isis gate-enforcement core; Agentic-Studio governance primitives — all real, just unwired.

7. Prioritized Remediation Roadmap#

Phase 0 — Stop shipping harmful fabrications (days). Make the dangerous BFF fixtures fail honestly (dead crypto address, consent/memory state, operator audit/SSO/incidents) → 503/not-configured rather than fabricate; gate the web fabricate-on-failure paths behind an explicit "data unavailable" state instead of silently rendering invented sky/news data. (Partially addressed in §8.)

Phase 1 — Persistence + BFF data plane (weeks). Stand up real datastores behind the admin BFF and the domain stub routes; implement the contracted /v1/oshun/* backend routes (or repoint adapters at the real domain-API namespace); add idempotency middleware and distributed rate-limiting. This unblocks the majority of "real logic, no consumer" gaps at once.

Phase 2 — Wire the governance decision planes to runtime (weeks). Build the orchestrators that consume the already-correct decision functions: Agentic-Studio executor (budget/kill-switch → run termination), Isis dispatch surface, Studio findings→gate binding + publish enforcement, Iris concrete persistence adapter, Lilith default analyzeSafety (done — §8), Trust-Safety queue/audit wiring + the cross-surface crisis-frame cascade, Billing-Support ↔ Aje integration.

Phase 3 — Build the genuine missing engines (weeks-months, specialist). Privacy DSAR/deletion that actually deletes + fans tombstones across domains with cryptographic shred; Yemaya render kernel (Rust/wgpu per CLAUDE.md) + Living-Scenes render/blend/determinism/watermark-embed/real-C2PA-signing; Metis 7-gate release pipeline + in-repo runtime (or import the Python backend into scope); replace Metis worker seeded-RNG fakes with real aggregation; replace research-agent source fabrication with real retrieval; chain-valid address derivation for Tron/TON/Ergo/Cardano; analytics aggregation + offline-eval runners.

Phase 4 — Surface completeness & hardening (weeks). Admin Trust&Safety/Support/Metis-governance mutation surfaces; Tenant-Console signed sessions + LTI/SCORM; messaging Email/Push/SMS/WhatsApp transports + bot STT; Nyx ephemeris service + power-user pages.


8. Fixes applied in this audit pass#

Targeted, self-contained, verified fixes (the bulk of the roadmap is multi-team and out of a single session's scope):

  • Lilith crisis classifier wired into the adapter (safety-critical). A default analyzeSafety now routes through the validated detectLilithCrises catalog so the per-turn adapter path inherits real crisis detection instead of depending on an unimplemented injected dependency.

  • @noble/hashes import skew fixed in payments-bridge cold-spend-queue (/sha256/sha2.js), unblocking telegram-bot test loading (catalog pins ^2.2.0, which dropped the /sha256 subpath).

  • BFF crypto-quote route fails closedPOST /v1/payments/crypto/quote now returns 503 payments_not_configured and issues no receiving address, instead of handing the customer a burn address (0x…dead).

  • Dangerous BFF read fixtures fail closed in production — the fixtures that shipped fabricated privacy (/v1/data-rights/consent, /v1/memory), operator-integrity (/v1/audit, /v1/sso, /v1/incidents), and misinformation (/v1/veritas/retractions, /v1/sophia/grounded|answer, /v1/nyx/tonight) data with 200 OK now return 503 not_configured when NODE_ENV/OSHUN_ENV/RUNTIME_ENV === production (via guardedFixtureRoute / blockFixtureInProduction). Dev/test still serve the fixtures, so demos and the existing fixture-shape test are unaffected; a new test asserts the prod-503 behavior (domain-stubs-route 65/65 green). Production never ships fabricated consent/audit/SSO/incident/grounding/sky data to real users or operators.

  • Web nyx/tonight no longer fabricates astronomy on backend failureapps/oshun/web/src/lib/server/nyx-depth.ts returned invented sky events (a specific "Jupiter conjunct the waxing moon" with exact times) whenever the BFF was unreachable; since the BFF now fails closed in prod, that fallback would re-introduce fabricated astronomy. It now returns an honest "data unavailable" state (empty events + explanatory headline); the page renders the headline and no fabricated lead event. (The home Veritas-briefing fallback is left as-is — it carries a deliberate "Curated briefing" disclosure pill and dedicated tests, so that one is the owner's UX call.)

  • Metis worker analytics no longer fabricates metrics — the registered AnalyticsAggregateProcessor / AnalyticsSnapshotProcessor (apps/metis/worker) computed every metric from a seeded RNG. They now read from an injectable real MetisAnalyticsDataSource and fail closed (success:false, "no analytics data source configured") when none is wired, instead of inventing engagement/performance/retention/content/snapshot numbers. The real statistical machinery (time-series bucketing, percentiles, trend regression) is preserved and now operates on real data; tests rewritten to the data-source + fail-closed paths.

  • Metis worker reports no longer fabricate data — the registered ReportProcessor built learner-progress / course-analytics / engagement reports from seeded-RNG collect*Data methods. It now reads from an injectable MetisReportDataSource and fails closed when none is wired, with the real report/chart/CSV/PDF/JSON formatting machinery preserved. (analytics 40, reports 87; full metis/worker suite 735/735 green.) Also fixed the worker's empty-include tsconfig.json so typed linting/typecheck actually cover its sources.

  • Metis worker notifications no longer fabricate delivery success — the registered NotificationProcessor / BatchNotificationProcessor formatted email/push/SMS/in-app messages, then void-discarded them and returned delivered:true. They now hand the formatted message to an injectable NotificationDeliveryTransport and report delivered:false when none is configured — never a fabricated success that would mask real delivery failures. (notification 80; full metis/worker suite 737/737 green.)

  • Metis worker course-export no longer fabricates course content — the registered CourseExportProcessor synthesized modules/lessons/media/ assessments from a hash of the courseId and exported that invented content. It now loads from an injectable MetisCourseLoader and fails closed (or "course not found") when none is wired, with the real PDF/SCORM/HTML/EPUB/JSON export + manifest machinery preserved. The deterministic generation moved into the test as a fake loader. (course-export 96; full metis/worker suite 740/740 green.) With this, the Metis worker contains no fabricated-code paths — analytics, reports, notifications, and exports all ground-or-fail-closed.

  • Metis research-agent no longer fabricates sourcesResearchAgent (libs/metis/agents) now retrieves from an injectable real ResearchSourceRetriever and abstains (metadata.grounded:false, empty result, explanatory summary) when none is configured, instead of manufacturing fake sources with invented relevance/reliability scores. Removes the Math.random/Author A/B/C stub that directly violated the fabricated-research-blocking requirement; spec rewritten to test the grounded retriever path and the abstention path (18 tests; full lib 876/876 green).

  • @veritas/contracts dangling tsconfig alias — removal deferred to a dedicated cleanup commit (see §5): editing tsconfig.base.json triggers a repo-wide affected-typecheck that currently fails on unrelated pre-existing WIP errors.

  • Runtime persistence layer built (T1 Step 1)@oshun/persistence had no Prisma-client-backed repository (only the build-time schema spec). Added a generic, projection-driven ContractRecordRepository (put/get/tombstone + computeContractPayloadHash + prismaDelegateResolver) that serves all 85 models without drift. Type-clean under the lib's real typecheck; 10 unit tests

    • full persistence suite green. This is the foundation the Iris/admin/Veritas durability work (T1 Step 2, T2, T4) layers on. See §10 T1.
  • T1 hardening — two real defects found by widening real-DB verification, then fixed. Extending the integration test from the scalar-only DurationBucket to tara:SittingCompletionEvent (a contract with a timestamptz column, completedAt, and a jsonb column, timestamps) surfaced two genuine bugs the original round-trip could not have caught:

    1. The committed Prisma client was stale (77 of 85 models). The last three feature commits added 8 contracts to the registry + schema + migrations but never regenerated the tracked client (src/generated/client), and the renderer emitted a Prisma-6 generator block (prisma-client-jsnode_modules/.prisma/client) inconsistent with the Prisma-7 TS client that actually ships — so the regen pipeline never refreshed it. Net effect: ContractPersistenceService.put('tara','SittingCompletionEvent',…) (and 7 other registered contracts) threw no Prisma delegate at runtime — those contracts were unpersistable as shipped. Fixed at the source: prisma-renderer.ts now emits the correct Prisma-7 block (prisma-client../src/generated/client, importFileExtension = ""); regenerating yields all 85 models with the existing 77 byte-identical (purely additive).
    2. deserialize silently dropped contract fields that share a name with the control envelope. It skipped any projected field in a CONTROL_COLUMN_SET (tenantId, sourceRecordId, …) — but those projections are contract-declared fields, and many V1 contracts carry their own tenantId (SittingCompletionEvent, MessagingAccountLink, …). On read the field came back undefined and re-validation failed. Fixed: every projected field is a contract field and is rehydrated (envelope-only columns are never in the Zod schema, so they still never leak); put now throws if a contract-declared tenantId/sourceRecordId/etc. disagrees with the stamped envelope value (no silent overwrite). Covered by 2 new unit tests + the real-Postgres SittingCompletionEvent round-trip (timestamptz + jsonb + own-tenantId).
  • Veritas retraction cascade — emitter half built (T2, in-repo side). The audit flagged the cascade as an in-memory planner that "never cascades": it computed which stories/claims/explainers/evidence-packs/downstream Metis lessons to re-ground or re-validate, but nothing persisted or dispatched the plan. Added executeVeritasSourceRetractionCascade (domain-veritas/src/ retraction-cascade-execution.ts): it runs the existing planner, persists the RetractionCascade (a registered envelope contract — id-keyed, no revision chain — so it round-trips through the contract repository directly), then publishes a veritas.retraction.cascade.dispatched event carrying the per-target re-grounding/re-validation jobs. Persist strictly precedes publish (fail-closed: a crash between them never dispatches a non-durable cascade). Depends only on small injected ports (RetractionCascadePersistence / RetractionCascadeEvent), so domain-veritas stays infra-free; the app boundary supplies ContractPersistenceService.put('veritas','RetractionCascade',…) and IEventBus.publish. 2 unit tests (plan→persist→publish ordering; no dispatch when persistence fails) + full domain-veritas suite green (304). Remaining (out of repo / infra): the Sophia re-grounding and Metis lesson re-validation consumers, and registering the topic in the event-bus DEFAULT_EVENT_TOPIC_DEFINITIONS for enforced-mode schema stamping.

  • Veritas story-update notifications — dispatch built (emitter). A smaller parallel to the cascade: resolveVeritasStoryUpdateNotifications already did real per-recipient resolution (topic match, preference/mute/frequency suppression, modality selection, dedup) but its deliveries were never emitted — followers were never actually notified. Added dispatchVeritasStoryUpdateNotifications (story-update-notification-dispatch.ts): run the resolver, publish one veritas.story-update.notification.dispatched event per delivery (keyed by the deterministic notificationId for idempotency); suppressed candidates are never emitted. Publish-only by design (a notification is a transient delivery, not a durable entity). 2 unit tests (only deliveries emitted; nothing emitted when all suppressed) + suite green (306). The push/email/in-app transports remain out-of-repo consumers.

  • Telegram bot prod token — fail-closed (P2 safety). The prod environment silently defaulted its bot token to the literal 'prod-token' when OSHUN_TELEGRAM_BOT_TOKEN_PROD was unset, so a misconfigured production deploy would authenticate with a fake token instead of erroring. createOshunGrammyBot now throws for prod when the token is the unconfigured-placeholder sentinel (dev/staging keep their convenience placeholders). 1 new unit test + suite green (14); typecheck clean.

  • Iris durable memory persistence built (the substrate's #1 gap). The §10.8 MemoryStore was in-memory only — a restart lost every memory. Added a durable, revision-chained store. Because a MemoryEntry does not fit the contract-envelope foundation (keyed per revisionId not a stable id; nullable domain tenantId that would collide with the non-null envelope partition; user-scoped not tenant-scoped), it persists to a bespoke iris_memory_entry_revision table (added via a new renderBespokeModels() chunk in prisma-renderer.ts, so the one generated-schema/migration/client pipeline still covers it): one row per revision, the full validated MemoryEntry as a JSON payload, entryId/userId/lifecycle projected out, and a partial unique index (entry_id WHERE lifecycle <> 'superseded', in the migration) enforcing exactly one current revision per entry. DurableMemoryStore (durable-memory-store.ts) mirrors the in-memory API (create/update/tombstone/recordReference/getLatest/getRevision/history/list/ summarize) async, doing supersede-then-insert inside an interactive $transaction. The DSAR-vs-immutability tension is resolved: tombstone stays terminal-but-body-retaining (immutable audit, per §10.8), and a new forget does a true hard-purge of every revision for right-to-be-forgotten. 7 unit tests + a real-Postgres integration test (create→update→supersede→ tombstone→forget, partial-unique verified) — both green; lib typecheck clean.

  • Nyx real ephemeris built (replaces fabricated sky data). There was no in-repo astronomical computation — nyx/tonight was fixtured and the web layer fabricated sky data on failure. Added domain-nyx/src/ephemeris.ts: real geocentric computation from the standard Meeus algorithms — Julian Day (ch.7), solar apparent position (ch.25), lunar position via the principal periodic terms (ch.47), and lunar illuminated fraction / phase (ch.48) — plus a computeNightSky(date) summary. 16 known-answer tests pin every routine to Meeus's published worked examples (JD = 2451545.0 at J2000; Sun ex 25.b; Moon ex 47.a to arc-minutes; illumination ex 48.a; known new/full-moon dates), so a coefficient-transcription error fails loudly. Full Nyx suite 200 green; typecheck clean. Follow-up: rise/set/transit + horizontal coords + wiring nyx/tonight to consume it, and the 19 power-user pages.

  • Living Scenes provenance — real C2PA signing + real watermark DSP. Two of the three §25.4 fabrications fixed. (1) The C2PA sidecar took injected sign/verify wired to a faked signature; added createEd25519C2paSigner / createEd25519C2paVerifier (provenance/c2pa/ed25519-signer.ts) over @noble/curves Ed25519, so a manifest carries a genuine 64-byte signature bound to the key and tamper-evident. (2) The watermark module only encoded a payload + scored a recovered chip stream — the actual embed/extract was "absent"; added provenance/audio-watermark/dsp.ts: real spread-spectrum embedWatermark/extractWatermark (each ±1 chip spread by a deterministic PN sequence, recovered by correlation). 10 tests: sign→verify round-trip + tamper

    • wrong-key rejection; watermark embed→extract survives a round-trip and moderate additive noise, and a wrong PN key recovers nothing. Full living-scenes-runtime suite green; typecheck clean.
  • Living Scenes takedown — now actually mutates. The apply* dispositions computed a DispositionImpact (new playback state, share-link state) and the nightly audit checked an SLA against a cascadedAtUnixSeconds that nothing produced — the "cascade mutates nothing" finding. Added takedown/execution.ts: executeTakedownDisposition / executeTakedownCascade apply the prescribed mutations (set artifact playback + each public share-link state) through an injected TakedownArtifactMutator, stamp cascadedAtUnixSeconds, and return records that feed auditNightlyCascade. Fail-closed: a mutation that throws leaves cascadedAtUnixSeconds null (an SLA miss), never a silent success. 3 tests (real mutation + SLA-met; fail-closed + SLA-missed; batch cascade); full living-scenes-runtime suite 220 green; typecheck clean. Remaining §25.4: the GPU render/blend kernel (a separate engine effort).

  • Metis release-gate pipeline now exists (fail-closed orchestrator). The audit found the named "7 release gates" had per-gate decision logic scattered across the Metis libs but no pipeline running them as one release decision. Added release-pipeline.ts (@metis/course/validation): evaluateReleaseGates runs a set of named ReleaseGates and is release-ready only if every required gate is present and PASSED — a gate that FAILS, is SKIPPED, throws, or is absent blocks the release (never a silent pass). METIS_RELEASE_GATES names the canonical seven; courseQualityReleaseGates wires the three real course-quality gates (structure/textbook/presentation) against their existing implementations. 7 tests (pass/fail/skip/throw/missing-required + real-gate wiring); full @metis/course suite 586 green; typecheck clean. Follow-up: register the remaining four gates as ReleaseGate adapters over the existing source-grounding / assessment-validity / academic-integrity / accessibility decision functions (heterogeneous inputs). (The worker seeded-RNG fakes and the research-agent source fabrication were already fixed earlier this audit.)

  • Isis gate now reachable + a fail-closed dispatch guard. The release-gate logic was real and tested but the lib (@oshun/generation-control-isis) had no tsconfig.base.json path alias — literally unimportable by name, hence "zero importers, not wired." Added the alias (+ /*), so the gate is now reachable, and added dispatch-guard.ts: evaluateIsisDispatch is the canonical enforcement seam a surface calls before releasing a generation — it runs the real evaluateReleaseGate and permits dispatch ONLY on an explicit allow with every runtime admission admitted; a block/review verdict, a gate error, or a denied admission all DENY (fail-closed). 5 tests (allow/block/review + denied-admission + all-admitted); full isis suite 401 green; lib typecheck clean. Remaining (Isis): wire the seam into the out-of-repo generation runtime, and the still-absent tier-resolver / surface-guard / LoRA-training features.

  • Privacy DSAR — real erasure cascade (replaces soft-flag-only). The deletion path marked a request but never erased the subject's data (a contract-envelope tombstone keeps the payload columns — an audit marker, not erasure). Added a true-erasure primitive purge(contract, identity) to ContractRecordRepository + ContractPersistenceService (hard-deletes the row), and executeDsarDeletionCascade (dsar-deletion-cascade.ts): it purges the subject's contract records and forgets their Iris memory, emitting a verifiable DsarDeletionReceipt. Fail-closed: complete only if every target was erased or already absent — any failure (or a memory target with no eraser configured) leaves the DSAR unsatisfied. The per-domain subject→records enumeration is the caller's injected boundary. 4 unit tests + a real-Postgres integration test proving the row is truly gone (not tombstoned) and a re-run is satisfied via not-found. Full persistence suite green; typecheck clean.

  • Nyx ephemeris — observer geometry + rise/set (completes the core). Extended ephemeris.ts with greenwichMeanSiderealTime, localSiderealTime, equatorialToHorizontal (alt/az), riseTransitSet (rise/transit/set with circumpolar / never-rises handling), and a location-aware computeTonight (moon phase + where the Sun/Moon are now + their rise/set). Verified against Meeus's worked examples (GMST ex 12.a; horizontal ex 13.b — Venus alt/az) plus a rigorous self-consistency check (a fixed body sits exactly at its standard altitude at the computed rise/set — which surfaced and fixed a sidereal-rate bug: events must solve GMST mod 360 at the 360.98565°/day sidereal rate, not by wrapping solar-day fractions). Full domain-nyx suite 205 green; typecheck clean. Remaining (Nyx): wire nyx/tonight to consume computeTonight instead of the fixture, and the 19 power-user pages.

  • Metis 7-gate pipeline — all four remaining gates now wired. Completed the gate set: gateFromValidationResult adapts any @metis/linters-types ValidationResult (structurally, no cross-lib dep) into a ReleaseGate — fail-closed (pass→passed, skipped→skipped/blocking, fail/warning/error→ blocked with the issue messages), covering accessibility, assessment-validity, and source-grounding (fact-check); metisReleaseGates(...) assembles the full seven (3 course-quality + those 3 validation gates + a caller-supplied academic-integrity gate, whose result shape differs). 6 new tests; full @metis/course suite 592 green; typecheck clean. The named "7 release gates" now exist as one fail-closed pipeline with every gate wired to real logic.

  • Admin durability — typed durable store built (the store the admin adopts). The admin BFF kept records in in-memory Maps. Their canonical contracts (Incident, SupportCase, ReviewPackage, Tenant, PolicyBundle, Persona) turned out to be already modelled as cross-cutting contracts, so durability is achievable through the foundation today — what was missing was a typed store. Added admin-record-persistence.ts: a generic, reusable typedContractStore<T> over ContractPersistenceService (put/get/list/tombstone/purge, inheriting the envelope/validation/tombstone semantics) and createAdminRecordPersistence exposing each admin record class typed to its contract. 2 tests (routing for every class → its cross-cutting contract; generic reuse). Lib typecheck clean. Remaining for #11: the admin BFF adopting it in place of the in-memory Maps — a careful rewiring of the 30k-line admin/state.ts (integration, not new capability) plus the design call of canonical-contract vs admin view-DTO.

  • TRON real on-chain address derivation (the money-critical fabrication). Per-invoice deposit addresses were synthetic (T${sha256(seed||index).slice}) — not chain-valid, so java-tron's validateaddress would reject them and break invoicing. Added tron-address.ts: tronAddressFromPublicKey implements the real scheme (secp256k1 pubkey → keccak256(X‖Y) last-20 → 0x41 prefix → base58check) + decodeTronAddress/isValidTronAddress. Redesigned TronAllocator to watch-only: it now takes an account xpub (not a seed) and derives per-invoice addresses as non-hardened BIP32 public children via @scure/bip32 — spending keys never enter the process; it rejects an xprv. Verified WITHOUT external fetches: the derivation reproduces the canonical secp256k1 priv=1 account id (Ethereum's 0x7E5F…Bdf) and every address base58check-round-trips to 0x41‖id. Full @aje/chains-tron suite 34 green; typecheck clean.

  • Ergo real P2PK address derivation. Per-invoice addresses were synthetic (9${sha256(seed||index).slice(0,50)} — bare hex, not even base58). Added ergo-address.ts: ergoP2pkAddressFromPublicKey implements the real sigmastate scheme (head byte network+P2PK ‖ 33-byte compressed secp256k1 key ‖ blake2b-256(head‖key)[0..4] checksum, base58-encoded) + decodeErgoAddress/isValidErgoAddress. Redesigned ErgoP2pkAllocator to the same watch-only xpub + non-hardened-child model as TRON (m/44'/429'/0'/0/<i>). Verified against five canonical sigmastate/fleet-sdk (public key → mainnet address) vectors (e.g. 038d39af…a58a639hY16vzHmmfyVBwKeFGHvb2bMFsG94A1u7To1QWtUokACyFVENQ), plus checksum-tamper rejection and compressed/uncompressed equivalence. Full @aje/chains-ergo suite 29 green; typecheck clean.

  • TON real v4r2 address derivation. The per-invoice account id was synthetic — a sha256(publicKey‖walletId‖subwalletId) digest wrapped in the (real) friendly-address envelope, so funds sent there were unsweepable. A TON address is workchain:sha256(StateInit cell); rewrote subwallet.ts to build the genuine v4r2 StateInit ({code, data} with the canonical v4r2 code cell + seqno‖subwallet_id‖pubkey‖∅ data) and hash it with @ton/core — the same cell hashing the operator's own cold-spend tooling and every TON wallet/explorer use, so the address is sweepable by construction. The code cell loads from the canonical BoC (Cell.fromBoc verifies its CRC32C, so a transcription error throws). Per-invoice distinct addresses come from a monotonic subwallet_id offset; the allocator stays watch-only (public key only). Verified by an in-suite oracle test asserting the derivation is byte-identical to @ton/ton's reference WalletContractV4 across four keys × two workchains, plus a documented snapshot vector (EQDgICpEIOY6eV348OIAf8Q-spcHAt0C2qi5A4MPKP2y773N). Full @aje/chains-ton suite 40 green; typecheck clean.

  • Cardano real CIP-19 address derivation. Per-invoice addresses were synthetic (addr1_${sha256(seed||index).slice(0,40)} — not even bech32), so any Cardano node/wallet would reject them. Added cardano-address.ts: cardanoBaseAddress/cardanoEnterpriseAddress build genuine CIP-19 addresses (header(type‖network) ‖ blake2b-224(payment key) [‖ blake2b-224(stake key)], bech32 with Cardano's relaxed length limit) + decodeCardanoAddress/ isValidCardanoAddress. Redesigned Cip1852Allocator to watch-only: it takes the Ed25519-BIP32 account xpub (rejects a 96-byte xprv) and derives per-invoice payment credentials as non-hardened public children …/0/<i> with a fixed staking credential …/2/0, via @stricahq/bip32ed25519 (the canonical pure-TS Ed25519-BIP32). Verified two ways: (a) the address encoding reproduces the CIP-19 published test vectors (type-00 base + type-06 enterprise, mainnet + testnet) byte-for-byte, and (b) a derivation-consistency test proves the watch-only public-derived key equals the private-key path the operator spends with (so deposits are sweepable), across both chains and several indices. Full @aje/chains-cardano suite 27 green; typecheck clean.

  • #13 complete: all four non-EVM rails (TRON, Ergo, TON, Cardano) now derive real, chain-valid, watch-only deposit addresses, each verified against authoritative per-chain vectors/oracles.

  • Isis civitai intake — sha256 hex validation. evaluateIntakeCandidate checked the digest's length but not its alphabet, so a 64-char non-hex string was admitted to staging despite the "64 hex chars" contract. Tightened to /^[0-9a-f]{64}$/i with a test.

  • Yemaya raster render/blend kernel (the "simulated rendering, no pixels" fabrication). RenderCore.executeJob computed estimated metrics with no pixels, and the visual-compositor's applyBlur dampened contrast on a single colour (a blur is a spatial operation — impossible on one colour). Added raster-kernel.ts, a real CPU reference renderer over RGBA8 buffers: compositeOver (W3C Compositing-and-Blending source-over reusing the existing real separable blend-mode functions), gaussianBlur (a genuine separable Gaussian — 1-D kernel from σ, two passes, edge-clamped, premultiplied-alpha so transparent edges don't darken), vignette (radial darkening by true pixel distance), and renderLayerStack (bottom-to-top multi-layer compositing). Verified by 17 known-answer tests: multiply(red,blue)=black, screen=magenta, 50%-over-midpoint, transparent-source is a no-op; blur conserves energy (float-exact, ~8% 8-bit quantization loss), is symmetric/isotropic, and keeps a white-on-transparent edge pure white; vignette leaves the centre untouched and halves the corner at amount 0.5. Full @yemaya/rendering-pipelines suite green; typecheck clean.

  • Yemaya composition renderer (consuming the kernel — real pixels from the scene model). Added rasterizeComposition(Composition): it composites the composition's visible layers in ascending order over backgroundColor, each with its blend mode + opacity, producing an actual RGBA8 image. Solid-fill (shape) layers are placed with full affine fidelity — position is the anchor point, scale stretches dimensions, and rotation is applied exactly via an inverse point-in-rect test (FP-boundary-robust). The fill colour is read from content.data.fill/.color; image/text/group content is skipped, not fabricated (a bitmap decoder / text shaper / group flattener are separate engines) and counted in skippedLayerCount. 9 known-answer tests: anchor-aware placement, exact 90° rotation (4×2 → 2×4), scale, multiply/opacity blend over the background, layer-order precedence, invisible/zero-opacity skipping, and honest bitmap/text skipping. Full @yemaya/rendering-pipelines suite 1532 green; typecheck clean.

  • Yemaya real render path on the orchestrator. RenderCore.executeJob was the audit's flagged "simulates rendering + fabricated frame/memory/FPS telemetry, no pixels." Added RenderCore.renderComposition(composition): it drives the kernel's rasterizeComposition to produce an actual RGBA8 image and returns measured metrics — real wall-clock duration, the true output-buffer byte size, the count of layers that actually contributed, and skipped non-solid layers surfaced as warnings — emitting jobCompleted/metricsCollected and recording history. executeJob is retained and re-labelled as what it is: a deterministic cost planner (estimates, no pixels), distinct from the real render path. 3 tests assert real pixels + measured (not estimated) metrics + event emission. Full @yemaya/rendering-pipelines suite 1535 green; typecheck clean.

  • Yemaya anti-aliased rotated edges. rasterizeSolidLayer sampled one point at each pixel centre, so rotated/diagonal layer edges were jagged. Added coverage supersampling (antialiasSamples per axis, default 4 → 16 sub-samples/pixel): an edge pixel's alpha is fillAlpha × coverage, so rotated edges get smooth fractional alpha while fully-inside pixels stay opaque and fully-outside transparent. Axis-aligned edges on integer boundaries stay crisp at any setting (no sub-sample straddles them), so all prior known-answer tests are unchanged. Verified by a 45° diamond test (fractional-alpha edge pixels exist with AA on; only 0/255 with antialiasSamples: 1). Full @yemaya/rendering-pipelines suite 1536 green; typecheck clean.

  • Yemaya per-layer effect pass. Added applyLayerEffects(image, effects), wired into the composition pass: each layer's effects are applied to its buffer in ascending order with real math — blur (the kernel's separable Gaussian), vignette, brightness, contrast, saturation (the compositor's existing colour formulas), grayscale (Rec.709 luminance), invert, the standard sepia matrix, hue-rotate (HSL rotation), and a 3×3 sharpen convolution (DC-preserving, premultiplied, edge-clamped). Effects that need extra buffers (shadow, glow) or other engines (masks, gradients, …) are returned in unsupported rather than faked. 13 known-answer tests (invert red→cyan, greyscale red→54, sepia white→(255,255,239), brightness +0.5→+128, hue-rotate red→green@120°/blue@240°, blur-effect == direct gaussianBlur, sharpen DC-preserving + peak overshoot, ordered application, disabled-skip, unsupported-list, no-mutation). Full @yemaya/rendering-pipelines suite 1549 green; typecheck clean.

  • Yemaya shadow/glow effects + mask primitives. Added drop-shadow (tint silhouette → offset → blur → composite content over it) and outer glow (tint → blur → intensity → composite over) to the effect pass, with a documented numeric param contract (r/g/b/a, offsetX/offsetY, blur, intensity), plus tintImage/translateImage helpers. Exposed real applyAlphaMask and applyLuminanceMask kernel primitives (multiply image alpha by the mask's alpha / Rec.709 luminance). The effect pass now covers 12 effects; only Mask (needs a mask source the Effect doesn't carry — callers use the primitives directly), gradients, borders, noise, etc. remain in unsupported. 5 new known-answer tests (offset shadow lands behind content; red glow halo is red-dominant with falloff; alpha-mask hides the masked half; luminance-mask maps grey→128 alpha; dimension-mismatch throws). Full @yemaya/rendering-pipelines suite 1554 green; typecheck clean. Remaining for Yemaya: GPU/wgpu acceleration and image/text-layer engines (bitmap decode + text shaping).

  • Durable admin audit-events store (#11 — compliance slice). The operator audit log (apps/oshun/bff/src/admin/admin-audit-events-store.ts) was an in-memory ring buffer — a process restart erased the operator audit trail, a compliance gap. Built DurableAdminAuditEventsStore on the persistence foundation, exactly mirroring the Iris durable-store pattern: a bespoke admin_audit_event table (added to renderBespokeModels, regenerated schema + client, new migration), append-only and operator-scoped, with occurredAt / eventType projected for the per-operator recency list, the relevance-ranked universal-search facet (scoring identical to the in-memory store), the §19.1 cross-operator explorer (actor / workspace / type-prefix / time-window / free-text filters), readEvent, size, and purgeActor (DSAR hard erasure of one operator's trail). 6 unit tests (fake delegate, all logic) + a live-Postgres integration test (persist/read/list/search/cross-query/time-window/purge) — both green; @oshun/persistence unit suite 51 green; typecheck clean. This durable slice + the existing createAdminRecordPersistence typed contract stores (Incident/SupportCase/ReviewPackage/Tenant/PolicyBundle/Persona) are the durable substrates the admin surface adopts; the remaining #11 work is the large mechanical rewire of the 30k-line in-memory state.ts onto them.

  • Write-through durable adapter for the BFF audit log (#11 — the glue). The BFF injects a SYNC AdminAuditEventsStore (options.auditEventsStore) at ~50 call sites; the durable store is async. Added createDurableBackedAuditEventsStore (apps/oshun/bff/src/admin/): it wraps the in-memory ring buffer (unchanged sync reads + sync record returning the event) and additionally writes each event through to an injected durable sink (DurableAuditSink port — the BFF takes no @oshun/persistence dependency; the app entry adapts the real DurableAdminAuditEventsStore), plus hydrate() to reload the recent trail into the cache on startup. Write-through is at-least-once fire-and-forget (the sync interface can't await) with failures surfaced via onError, not swallowed — strictly better than the prior lose-everything-on-restart. Added a seed() to the in-memory store so hydration preserves event ids. 4 adapter tests (write-through, observable failure, startup hydration, full sync surface) + the in-memory store's 6 still green. This is the drop-in options.auditEventsStore replacement; the only remaining #11 step for the audit log is the app entry instantiating a PrismaClient (DB infra) and injecting it — and the analogous rewire for the other in-memory admin stores.

  • Audit-durability wiring chain completed in-repo. Two pieces finished the cache→durable chain: DurableAdminAuditEventsStore.insert(event) persists a pre-formed event verbatim (id + occurredAt preserved — the write-through cache mints the id synchronously, so cache + durable row must share it; record mints a new id and is for first-party appends), and createDurableAuditSink(store) adapts the durable store to the BFF's DurableAuditSink port (record→insert, recentForHydration→listAcrossOperators). 2 new unit tests (insert id-preservation, sink routing); @oshun/persistence unit suite 53 green. The full chain — durable store ← sink adapter ← write-through adapter ← BFF options.auditEventsStore — is now in-repo + verified end to end; the sole remaining runtime step is the app entry constructing the store over a real PrismaClient (DB infrastructure + deploy/env config).

  • Veritas contradiction loop — execution (the emitter half). The contradiction/ module shipped the pieces (runContradictionProbe → triage, routeContradictions, buildMonitoringEntry/MonitoringQueue) but nothing composed them at runtime — the loop never ran, the queue was in-memory only, and no event fired when a contradiction was detected (the same "logic exists, nothing consumes it" shape as the retraction cascade). Added executeContradictionLoop (contradiction/execution.ts): probe → route → build entry → enqueue (durably, via the injected queue) → publish veritas.contradiction.routings.dispatched. Enqueue precedes publish (fail-closed); a no-contradiction sweep is still recorded for audit but publishes nothing; reader surfaces are never mutated (routings go to a reviewer). Infra-free via injected ports (SophiaProbeClient, MonitoringQueue, event port), mirroring the retraction-cascade emitter; the app boundary wires a durable queue + IEventBus. 3 tests (publish-on-found, record-but-no-publish-when-clean, enqueue-before-publish ordering). Full @oshun/domain-veritas suite 309 green; typecheck clean. (The downstream reviewer-action + Sophia re-grounding consumers remain out-of-repo.)

  • Yemaya server-side PNG codec (bitmap-decode engine). Image-layer content had no decoder (skipped). Added png-codec.ts: decodePng parses the chunk stream, verifies each chunk CRC-32, inflates IDAT, and reverses all five scanline filters (None/Sub/Up/Average/Paeth) into a RasterImage (8-bit RGB + RGBA, non-interlaced; other variants throw rather than mis-decode); encodePng writes a valid RGBA PNG. DEFLATE uses Node's zlib via a dynamic import, so a browser bundle is unaffected (the import only runs when the codec is called, server-side). 6 tests: lossless encode→decode round-trip (exact bytes), RGB→alpha-255 decode, Sub + Up filter reversal, malformed / unsupported-colour-type rejection, and CRC-corruption detection. Full @yemaya/rendering-pipelines suite 1560 green; typecheck clean.

  • Yemaya textured image layers (the codec, consumed). Wired the PNG codec into the composition: rasterizeComposition now accepts options.textures (layer-id → decoded RasterImage), and a new rasterizeTexturedLayer samples the texture through the full layer transform — super-sampled, premultiplied accumulation gives edge AA + a box-filter on minification; partial-coverage edges get fractional alpha; an image layer with no texture is still skipped (no fabrication). 3 tests, including an end-to-end decode-PNG → texture → composite (each quadrant of a 2×2 texture lands in the correct canvas region). Full @yemaya/rendering-pipelines suite 1563 green; typecheck clean.

  • Yemaya bitmap-font text renderer. Text-layer content had no renderer (skipped). Added text-render.ts: renderText(text, atlas, color) lays a string out left-to-right with newline handling and blits each glyph's lit pixels into a RasterImage (+ measureText). The glyph atlas is injected (a TTF rasteriser or shipped bitmap font is the app's asset, the same split as image textures); rendered text composes via the existing texture path. 6 tests against a hand-authored 3×5 font with exact pixel + layout assertions (glyph blit, inter-glyph advance, \n wrap, missing-glyph skip, fallback glyph). Full @yemaya/rendering-pipelines suite 1569 green; typecheck clean. Out of scope (HarfBuzz-class libs / a font asset): complex shaping (ligatures, kerning pairs, bidi, complex scripts) and the production font atlas. The Yemaya CPU render pipeline is now feature-complete (compositing, blend modes, AA, 12 effects, masks, image textures, text); GPU/wgpu acceleration is the only remaining item, and this CPU path is its reference.

  • BFF server-side idempotency middleware (P1 #3 — the spec-mandated "first step of every write"). ARCH §5 + the request-lifecycle diagram make idempotency the first thing a keyed write hits, yet the BFF had zero server-side idempotency (the only idempotency.ts in-repo was the client IdempotencyMiddleware, which dedupes one caller's in-process retries and cannot make a write replay-safe across the network). Added apps/oshun/bff/src/middleware/idempotency.ts: a Fastify preHandler/onSend pair over an injectable BffIdempotencyStore port (default InMemoryBffIdempotencyStore — atomic reserve, TTL'd replay records, expiring pending locks, bounded LRU eviction). Opt-in per Idempotency-Key header so existing keyless writes are unchanged; on a keyed unsafe method (POST/PUT/ PATCH/DELETE) it reserves the key (tenant-scoped, so one tenant's key can never collide with another's), and on a same-key retry replays the stored status + body + replay-safe headers with Idempotent-Replayed: true without re-running the handler (mutation happens exactly once). A concurrent in-flight duplicate gets 409 idempotency_in_progress; key reuse with a different request fingerprint gets 422 idempotency_key_reuse (fingerprint computed with the same createIdempotencyFingerprint the client uses, so reuse detection is identical on both sides). Status policy: 2xx and deterministic 4xx are cached; 5xx / 408 / 409 / 425 / 429 are released so a retry re-runs (a transient verdict is never pinned to the key); streams/Buffers are never cached. Wired into createApp after tenant/residency resolve, with an idempotencyStore option on OshunBffAppOptions so a Redis/Postgres-backed store drops in for cross-instance replay safety (the same in-memory-default + injectable-durable-port pattern the admin audit-events store uses; the durable backing is the deploy-infra step). 14 known-answer tests (exactly-once replay, distinct-key re-run, keyless/safe-method pass-through, 422 reuse, concurrent 409 then 200, 400 invalid key, cached 404, 201+Location replay, uncached 429/5xx, tenant-scoped isolation, expiring pending lock) + an end-to-end wiring test through the real createApp. Full BFF suite 1611 passed / 22 skipped (the always-on hooks disturb no existing route; OpenAPI runtime-drift test still green); lint + typecheck clean. With this, ARCH §5's idempotency requirement is met in-repo — only distributed rate-limiting remains from the §4 #3 line.

  • Studio findings→publish-gate loop closed (P1 #7). evaluateAuthoringWithPolicy emits block-severity findings (e.g. a tone-policy banned phrase), but nothing consumed them at the gate — EditorialGate.satisfied and evaluatePublishReadiness were binding-only and caller-supplied, so a blocking finding only surfaced in review and did not block publish. Closed the loop in three places, all backward compatible: (1) editorialGateFromAuthoringFindings(findings) (editorial-lifecycle/lifecycle.ts) folds the findings into a required EditorialGate that is unsatisfied iff any finding is block-severity, so tryEditorialAdvance(approved→published) is refused while a blocking finding stands (warn/info never gate); (2) evaluatePublishReadiness now takes optional evaluationFindings and reports missing: ['blocking-findings'] (with the offending findings attached) when any block finding exists, even if every binding is satisfied; (3) evaluateHotfix refuses with blocking-findings-present when a block finding exists — tone-policy is non-negotiable per the pipeline contract, so a banned phrase cannot be hotfixed around. 8 known-answer e2e tests (findings-publish-gate.test.ts) drive a real banned-phrase document through evaluate → gate-refused → readiness-not-ready → hotfix-refused → pipeline mode: blocked, plus the warn-only control that stays publishable. Existing signatures unchanged (the optional fields and the new blocking-findings union member don't disturb the prior callers/tests); full @oshun/studio-authoring suite 114 passed; lib + spec typecheck + lint clean. Closes the §9 exit-criterion gap "Studio authoring/.../release-gate enforcement" for the authoring-findings half (Sophia/Isis/rights bindings were already enforced; what was missing was the findings→gate binding).

  • Agentic-Studio governance executor — decisions now act (P1 #6). The budget / kill-switch / throttle modules each produced a verdict (checkBudget, decideExecution, acquireToken) but nothing consumed them at runtime: there was no orchestrator that terminated a run, and the run state machine had no killed status — so "budgets honored, kill switches enforced" could not hold end to end. Closed it: (1) added the terminal killed RunStatus (distinct from operator cancelled and error failed)

    • a kill() run-control (mirrors cancel, audit kind kill), and folded killed into TERMINAL_STATUSES, the dashboard's recently-completed bucket, and the AdminAgenticRunStatus contract DTO + its bucket switch so the operator surface can represent an enforced kill; (2) built runs/executor.ts admitToolCallthe tool-call admission seam the dispatcher calls before every tool call: it runs the governance checks in safety→cost→rate order and a kill switch (decideExecution blocked) or an exhausted budget (checkBudgetexceeded) terminates the run (killed, terminal) with an audit event + the operator's user-visible status copy; warn/grace budgets admit-with-warning; an empty throttle bucket yields back-pressure (throttled + retryAfterSeconds, run not terminated); kill switch takes precedence over budget over throttle. Pure (returns next state; caller persists + consumeBudget). 10 known-answer tests (admit, kill-switch→killed with audit, budget-exceeded→killed, warn→admit-with-warning, throttle back-pressure, kill-switch-over-budget precedence, already-terminal refusal, no-throttle admit, kill control transition + terminal-refusal). Full @oshun/agentic-studio suite 234 passed (no existing test broke on the new status); @oshun/contracts lib typecheck + 30 agentic-contract tests green; lib + spec typecheck + lint clean. Moves §9's "Agentic Studio … budgets honored, kill switches enforced" from ❌ to 🟡 — the enforcement path exists and is tested; wiring the seam into the out-of-repo generation runtime is the remaining step.

See the commit(s) accompanying this report for diffs and the verification (typecheck/test) output.


9. V1 Exit-Criteria Checklist#

Exit criterion (from V1/features.md §"V1 Exit Criteria") Status
Customer web & mobile feel like one coherent product across all 6 domains ❌ — hub pages are fixtures; not BFF-backed
Tara is visibly the product center ✅ — shell ordering is Tara-primary (real)
Nisaba & Metis fully integrated (not just routed) ❌ — Nisaba logic is integrated; Metis runtime is disconnected
Sophia grounding visible wherever evidence matters 🟡 — Sophia layer real; surfaces show canned grounding
Cross-domain continuity useful, credible, tested 🟡 — shell continuation logic real & tested; data is fixtures
Admin can operate ALL listed governance workflows ❌ — T&S/Support/Metis-governance missing; admin BFF non-durable
Studio authoring/calendar/asset/.../release-gate enforcement 🟡 — logic real; findings→gate not bound; no persistence
Agentic Studio operational w/ governance gates enforced & budgets honored 🟡 — executor now enforces kill-switch/budget → run termination (§8); orchestrator-to-runtime wiring pending
Tenant Console (SSO/SCIM/OneRoster…) isolated from operator admin 🟡 — logic real; forgeable session; LTI/SCORM missing
Iris/Psyche/Lilith/Sophia/Isis/Metis ownership true in product 🟡 — true in libs; not wired to runtime for Iris/Isis/Metis
External model sources cannot bypass review/rights/safety/provenance/Isis gates 🟡 — Isis gates enforce & block, but Isis is unwired to any surface
Metis high-stakes outputs emit claim-level evidence & pass 7 gates ❌ — 7-gate pipeline doesn't exist; worker data fabricated
Public website, store listings, deep links, status page launch-ready 🟡 — deep-link/nav real; landing is fixture/CMS-later
Recommendation candidate gen, signal ingestion, offline eval, observability operational 🟡 — candidate/signal/ranking real; eval/observability are manifests only
Aja embodied-instruction ready or explicitly deferred w/ scope 🟡 — bounded adapter exists; add explicit deferral note
All critical workflows have risk-tiered automated verification 🟡 — strong unit/contract tests; integration/enforcement tests absent
Accessibility/perf/security/resilience/observability/runbooks satisfy launch gates ❌ — no real persistence/observability runtime; render substrate simulated

Net: 0 of the headline exit criteria are fully met; most are "logic-ready, integration-pending."


_This report is generated from direct source reads with file:line evidence. Where an audit could not reach a backend (e.g. out-of-repo Nyx ephemeris service, Metis Python backend, services/psyche/_), that is stated explicitly rather than assumed working.*


10. Executable Remediation Tickets#

The remaining gaps are integration/infra/decision work — but they are far more tractable than "build from scratch," because the hard pieces already exist in-repo. Confirmed scaffolding the tickets below build on:

  • libs/oshun/persistence — a real Prisma schema (prisma/schema.prisma, 85 models) already models every V1 domain, including VeritasRetractionCascade, CrossCuttingConsentRecord, CrossCuttingMemoryScope, CrossCuttingIncident, CrossCuttingSupportCase, CrossCuttingAgentRun, CrossCuttingTenant, plus all Tara/Arete/Nyx/Nisaba/ Metis tables. Generated client under src/generated/client.
  • libs/shared/event-bus — real outbound delivery + topic-registry + HMAC signing (event-bus.ts, outbound-delivery.ts, topic-registry.ts).
  • libs/shared/queue, libs/shared/data-residency, **libs/oshun/auth
    • BFF authz** — real, tested substrates to depend on.
  • The domain logic (recall scoring, cascade planner, gate enforcement, entitlement math, crisis classifier) is real and unit-tested — these tickets wire it, they don't re-implement it.

Each ticket lists the in-repo pieces to leverage, the concrete steps, and the external input a human/team must supply (the reason it can't be finished autonomously).

T1 — Build the runtime persistence layer, then back substrate adapters#

Investigated finding (decision point): persistence is build-time only. contract-persistence-registry.ts is a spec (V1_OBJECT_PERSISTENCE_CONTRACTS) that drives prisma-renderer to generate the 85-model schema; the models are a generic contract-envelope store (each row = typed-JSON contract columns + tenantId/contractSchema/sourceRecordId/payloadHash + tombstone fields). What does not exist is any Prisma-client-backed runtime repository (no upsert/fetch/tombstone against a live client). So T1 is a build, not a wire.

  • Step 1 (foundational) — ✅ DONE this pass. Built libs/oshun/persistence/src/contract-record-repository.ts: a generic ContractRecordRepository with put / get / tombstone, driven by the same getObjectFieldProjections the schema renderer uses (so columns cannot drift), serializing each contract field to its column (DateTime string→Date, arrays/objects→Json) + the control envelope (tenantId/contractSchema/contractVersion/sourceRecordId/payloadHash + tombstone), keyed on the [tenantId, contractSchema, sourceRecordId] identity. Includes a stable order-independent computeContractPayloadHash and a prismaDelegateResolver to bind a real PrismaClient. Type-clean under the lib's real typecheck; 10 unit tests (serialize/control-envelope/hash/ round-trip/tombstone-exclusion) + full persistence suite green. One generic repository serves all 85 models. Also added the ergonomic facade ContractPersistenceService (contract-persistence-service.ts) + factory createContractPersistenceService(prismaClient): callers persist by (domain, contractName) — e.g. service.put('veritas', 'Story', record, ctx) — with registry resolution + fail-fast on unknown contracts (5 tests). So the foundation is now consumable by name, not just present. Real-DB round-trip verified: contract-record-repository.integration.test.ts runs the full put→get→list→idempotent-reput→tombstone cycle for a real tara:DurationBucket contract against a migrated dev Postgres (via the generated client + a PrismaPg driver adapter), and passes — proving the serialization, control envelope, and identity keying match the generated client/schema end-to-end. The test is gated behind OSHUN_V1_DATABASE_URL so it skips cleanly in DB-less CI. T1 Step 1 is complete and end-to-end verified.
  • Step 2: implement concrete substrate adapters on top of it — e.g. PrismaIrisMemoryApiAdapter (Iris contracts → CrossCuttingMemoryScope / CrossCuttingConsentRecord); replace the admin BFF in-memory Map store; inject via the existing createCanonical*Adapter options.
  • External input / decision: Postgres is available for dev (docker compose -f docker/docker-compose.dev.yml up -d, migrations applied), so this is autonomously buildable — but the repository design (serialization, connection/pooling, transaction boundaries) and per-substrate contract→model fit are architecture decisions warranting owner review. Foundational, reviewed build — T2/T4 and the admin/Iris durability gaps all layer on top.

T2 — Veritas retraction cascade execution#

  • Leverage: planVeritasSourceRetractionCascade() (pure planner, done); VeritasRetractionCascade Prisma model; libs/shared/event-bus.
  • Steps: add BFF POST /api/oshun/domains/veritas/claims/:id/retract → persist tombstone (Prisma) → run planner → publish veritas.claim.retracted via event-bus → Sophia subscriber invalidates evidence packs + emits sophia.evidence.invalidated.
  • External input: the Sophia service's invalidation endpoint/consumer (out-of-repo libs/sophia/* client wiring) + event-bus broker config.

T3 — Isis dispatch surface wiring#

  • Leverage: @oshun/generation-control-isis (gate enforcement done + tested, zero importers today); IsisGenerationControlApiAdapter interface.
  • Steps: add a BFF/admin route that routes generation through dispatchGeneration / routeApprovedOshunMediaGenerationRequest; implement the concrete IsisGenerationControlApiAdapter against the real Isis runtime; add the tsconfig path alias; build the deterministic tier resolver + deny-by- default surface guard for the 4 audience tiers.
  • External input: the Isis execution runtime (apps/isis/libs/isis provider clients) endpoint contract.

T4 — Privacy DSAR true deletion + cross-domain tombstone cascade#

  • Leverage: the privacy state machines (done); per-domain Prisma tables; tombstone-semantics in @oshun/persistence; event-bus.
  • Steps: make advanceToHardDelete actually delete rows + write tombstones via Prisma; implement Iris scope enumeration → fan tombstone writes to every domain table with per-tombstone audit attestation; real cryptographic shred.
  • External input: DB + the audit-platform sink; legal/retention policy confirmation for shred semantics.

T5 — Metis 7-gate release pipeline + runtime#

  • Leverage: the 4 enforcing gates that exist (safety, rights, academic- integrity, standards) + the scattered grounding/pedagogy/drift helpers; libs/metis/* algorithms (IRT, mastery, knowledge-graph — all real).
  • Steps: build a single MetisReleaseGatePipeline object that runs grounding→pedagogy→safety→rights→academic-integrity→standards→drift and emits a release verdict; wire libs/metis/* into an in-repo runtime path (or import the Python backend into scope).
  • External input: decision on TS-runtime vs. the out-of-repo Python backend; grounding/pedagogy/drift gate thresholds.

T6 — Real on-chain address derivation (Aje: Tron/TON/Ergo/Cardano)#

  • Leverage: the correct EVM derivation already in-repo (xpub-watcher.ts: secp256k1 → keccak256 → 20-byte address); @noble/*.
  • Steps: Tron = EVM-20-byte → 0x41 prefix → base58check; TON = v4r2 StateInit cell-hash; Cardano = CIP-1852 → bech32 addr1; Ergo = P2PK.
  • External input (hard requirement): authoritative known-answer test vectors (privkey/xpub → canonical address from tronweb / ton-core / cardano- serialization-lib). Financial code must be verified against these, never shipped from memory.

T7 — Yemaya render kernel + Living Scenes pixel pipeline#

  • Leverage: the real Conductor/scheduling, scene-score schema, provenance/ takedown/shareability decision logic (all done); the contracts.
  • Steps: implement the actual render/blend kernel (Rust/wgpu per CLAUDE.md → WASM bridge), real C2PA Ed25519/X.509 signing + watermark embedding + container muxing, re-render-from-score, PSE/strobe live-frame detector, durable artifact persistence.
  • External input: the largest item — a graphics/media engineering effort + signing-key infrastructure. Genuinely multi-week, specialist.

T8 — Nyx ephemeris + power-user surfaces; web consumer-hub BFF wiring#

  • Leverage: the real Nyx contracts/adapter/notification-timing (done); the Skyfield-class ephemeris adapter seam.
  • Steps: stand up the Nyx ephemeris service behind OSHUN_NYX_API_BASE_URL; build the 19 /domains/nyx/* power-user pages; wire the 6 consumer-hub pages off lilith-data/* fixtures onto the BFF (and decide fail-closed vs. fixture for prod, per §3).
  • External input: the ephemeris service deploy + product decision on the fixture-backed hub stopgap.

Sequencing: T1 unblocks the most (most "no DB" gaps); then T2/T3/T4 (wiring on top of T1's persistence); T5 in parallel (Metis-team); T6 once vectors are supplied; T7 is the long pole (media engineering); T8 alongside.