# Living Scenes — Residual Audit (2026-06-11)

Scope: V1/features.md lines 3609–4523. Evidence: current code only (read
2026-06-11, branch `feat/hestia-epicure-embeddings`). The 2026-06-10
ground-truth audit's 57 tasks (B4 honest materialization, D4 real APNG
materialization + signed bytes + public media route) are confirmed landed and
are NOT re-reported; this is the residual.

## Pillar verdict (what exists / partial / absent)

| Spec pillar                                                                   | Verdict                                                                                                                                                                                |
| ----------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Keep / share / revoke / tombstone / delete loop                               | **EXISTS** (BFF, durable snapshot, tested) — but no product surface calls keep/share (finding 5)                                                                                       |
| Public viewer + password/unlock + embed + oEmbed + abuse report + transcripts | **EXISTS** (real, wired end-to-end, operator triage inbox real)                                                                                                                        |
| Takedown cascade (7 dispositions, SLA, banners)                               | **EXISTS** (route + lib executor + persisted overlay) — operator API only, no console UI; nightly SLA sweep orphaned                                                                   |
| C2PA signing of real media bytes                                              | **EXISTS** (Ed25519 over the APNG actually served; prod fail-closed without key — DEPLOY)                                                                                              |
| Materialization (D4 APNG)                                                     | **PARTIAL** — playable + signed, but score-independent (finding 3), non-durable, no re-render (finding 10)                                                                             |
| Scene Score schema + contracts                                                | **PARTIAL** — runtime Score is far thinner than the spec segment schema (no style anchor / narration pin / grounding pin / persona / transitions per segment); contracts mirror exists |
| Conductor runtime                                                             | **ABSENT at runtime** — pure-logic state machine exists in-lib, zero consumers; no session, no frame stream, no GPU dispatch, no segment adapters implemented (finding 4)              |
| Live Direction channel                                                        | **PARTIAL→ABSENT** — parse + verb policy real and wired (client-side), but cues apply to nothing; reconciliation + crisis classifier orphaned (findings 4, 21)                         |
| Blend kernel + technique catalog + compatibility scorer + per-technique evals | **ORPHANED** — `@yemaya/blend-kernel` (1,446 lines incl. the full 12-technique catalog) has zero app consumers (finding 19)                                                            |
| Customer composition surface + AI Compose Assist                              | **FIXTURE THEATER** — client-only fixtures, fabricated assist suggestion + continuity pre-score, nothing persisted (finding 2)                                                         |
| Domain templates (5)                                                          | **PARTIAL** — metadata + verb sets + tier gating exist; no template produces a scene; verb sets diverge from spec (finding 17)                                                         |
| AAA Scene Score Editor                                                        | **PARTIAL (demo)** — rich UI over one hardcoded fixture score; no persistence; ignores its own API (finding 20)                                                                        |
| Safety: PSE/seizure gate                                                      | **EXISTS** on the render path (WCAG luminance-swing gate enforced on real frames)                                                                                                      |
| Safety: crisis frame, cue pre-screen, tone bands                              | **PARTIAL→ABSENT** — pre-share check is a 6-phrase English substring list; crisis state is a client header (findings 7, 16)                                                            |
| Determinism / render envelope / re-render-from-score / engine-upgrade re-bake | **ORPHANED** — checkDeterminism, assertNodeParity, checkUpgradeAvailability, planRebake, rollback: zero consumers; no re-render endpoint (findings 3, 10, 23)                          |
| Provenance 4-channel redundancy                                               | **PARTIAL** — C2PA real; visible mark NOT in the media bytes; no audio (honestly n/a); attestation page real; channel-robustness eval orphaned (finding 12)                            |
| Shareability matrix                                                           | **TAUTOLOGY on live path** — grants fabricated to equal the requested reach (finding 7)                                                                                                |
| Cue privacy / intent redaction                                                | **UNENFORCED** — redaction engine orphaned; "redacted" layer is client-supplied (finding 8)                                                                                            |
| Tenant entitlements + share policy                                            | **CLIENT-SUPPLIED** — policy parsed from request headers; admin store never consulted (finding 6)                                                                                      |
| Reduced-motion + captions + AD + transcript                                   | **PARTIAL** — transcripts real; captions/AD/stills are dead `oshun://` metadata; viewer keeps animating under reduced-motion (findings 14, 15)                                         |
| Latency/quality/backpressure budgets, dashboards                              | **ABSENT** — no measurement anywhere; conductor backpressure logic orphaned (finding 24)                                                                                               |
| Eval sets / release gates / rollback                                          | **ORPHANED ENGINES** — no fixture corpora, no callers (finding 23)                                                                                                                     |

Realistic V1 polish vs V1.x: wiring the existing keep/share loop to the card
(5), the header→store tenant-policy fix (6), score-derived rendering (3, the
compositor already takes tone/dimensions/frames), server-side redaction (8),
first-frame route (9), media durability/re-render (10), honest download (11),
embedded visible mark (12), and reduced-motion honesty (14) are all V1-shaped.
Conductor/streaming, blend kernel execution, real Compose Assist (AgentRun), GPU
segment generation, eval corpora, latency SLOs are V1.x.

---

### 1. Customer Living Scene card fakes Keep / Share / generation success

Severity: P0-HONESTY Evidence:
`apps/oshun/web/src/components/studio/generation/LivingSceneCard.tsx:143-190`
(`startScene` → notice "Scene queued with gallery delivery and provenance
attached"; `keepCurrentOffering` → "Living Offering kept for this account with
lifetime retention"; `shareCurrentOffering` → "Share queued … after Lilith
re-check"); `apps/oshun/web/src/app/studio/generation/living-scene/page.tsx:44`
renders the card with NO `onKeep`/`onShare`/`onSendToGallery` callbacks and the
component makes zero network calls. Spec promise: features.md 3634-3637 (keep,
return, share), 4228-4235 (Keep default action, Share explicit second action).
What the code actually does: button handlers set a `notice` string claiming a
queued generation, a kept lifetime artifact, and a Lilith-checked share — while
no session, no artifact, and no share exist. The real BFF keep/share routes are
never called from any product surface. Fix sketch: wire the card's Keep/Share to
`POST /v1/living-scenes/artifacts/keep` and `/artifacts/:id/share` (the e2e
support file `apps/oshun/web/e2e/support/living-scene.ts` already shows the
exact payloads); until then the notices must say "not available".

### 2. /studio/compose is fixture theater: fabricated Compose Assist + fabricated continuity score

Severity: P0-HONESTY Evidence:
`apps/oshun/web/src/app/studio/compose/ComposeClient.tsx:31-93` (hardcoded
segment library `kept-lotus-001`, `nyx-anchor-aurora`, `metis-step-004`;
`continuityPreScore: 0.92` hardcoded), `:428-439` (assist panel shows the
constant string "Suggested Segment: Metis lesson-step bridge · confidence
0.74-0.88"; `acceptSuggestion` inserts a hardcoded fixture segment), `:449-450`
(the fabricated 0.92 displayed as "Continuity pre-score" under "Promotion
gates"). No fetch in the file. Same pattern on mobile
(`apps/oshun/mobile/app/compose.tsx`). Spec promise: 3777-3790 (sources = user's
kept scenes + template libraries + tenant imports), 3916-3943 (assist runs as
Isis AgentRun with budgets, audit, gold-set), 3881-3914 (compatibility scorer
produces the scores). What the code actually does: the only consumed parts of
the runtime lib
(`validateCompositionDraft`/`promoteCompositionDraft`/`filterComposeAssistTechniqueSuggestions`)
run client-side over fixtures; the "AI suggestion", its confidence band, and the
continuity pre-score are constants; "promotion" produces a local object that is
never persisted, kept, or rendered. The user cannot tell any of this is canned.
Fix sketch: load segments from `GET /v1/living-scenes/artifacts`; compute the
pre-score with `@yemaya/blend-kernel/compatibility/scorer` (orphaned, ready);
either wire a real assist AgentRun or remove the assist panel; label clearly
until then.

### 3. Materialized share media is score-independent — every scene renders the identical gradient

Severity: P0-STRUCT Evidence:
`apps/oshun/bff/src/routes/living-scenes.ts:671-689` —
`renderLivingSceneSegmentToPng({ tone: 'contemplative', width: 480, height: 270, frameCount: 24 })`
takes nothing from `artifact.score` (no segments, tones, durations, seeds,
narration, template) and hardcodes `'contemplative'` for all five templates;
`libs/yemaya/rendering-pipelines/src/raster-kernel/living-scene-render.ts:118-148`
is fully deterministic in those four inputs ⇒ byte-identical media for every
artifact on the platform. Spec promise: 4183-4187 ("Score storage is the source
of truth … re-render on demand reproduces the same artifact"), 3650-3660 (Render
Envelope = reproducibility inputs: model hashes, seeds, per-segment seeds),
4032-4114 (per-template visual behavior). What the code actually does: the kept
Score and Render Envelope (incl. per-segment seeds the keep route carefully
Map-normalizes and persists) are dead weight at render time; a Nyx sky briefing,
a Metis lesson, and a Tara arc all materialize to the same blue gradient. C2PA
then signs media that has no derivation from the score it attests. Fix sketch:
derive render inputs from the score — per-segment `tone` (the renderer already
supports all 6 tones), per-segment frame counts from `durationSeconds`, phase
offsets from `perSegmentSeedsHex` — and concatenate per-segment frame runs into
the APNG. Pure plumbing; no new engine needed.

### 4. Conductor runtime + live direction execution are absent: a shelf of orphaned engines

Severity: P0-STRUCT Evidence: zero non-test consumers (workspace-wide grep) for
`initialiseConductor`/`planPreWarm`/`advancePlayhead`/`setBackpressure`/`planReconnect`
(`libs/yemaya/living-scenes-runtime/src/conductor/conductor.ts`),
`reconcileCues` (`cues/reconciliation.ts`),
`decideCrisisAction`/`checkLocaleParity` (`cues/crisis-classifier.ts`);
`segment-adapter.ts` defines `SegmentAdapter`
('hunyuan'|'wan'|'cogvideo'|'custom-on-comfy') with **no concrete adapter
implementation anywhere in the repo**; the cue acceptance verdict claims
application — `customer-card.ts:407` returns notice
"`${verb} cue applied at the next scene boundary`" with no scene, queue, or
boundary in existence. Spec promise: 3704-3730 (Conductor streams a Score,
pre-warm ≥2, carry-state, backpressure, reconnect, provider failover), 3665-3702
(cue queue, reconciliation precedence, sensitive-intention classifier on every
voice/text cue, latency budgets). What the code actually does: cue handling ends
at parse + verb-allowlist policy; the crisis classifier is never invoked on any
live cue path (BFF `/card/cues` → `evaluateLivingSceneCue` →
`parseCue`+`assertVerbAllowed` only); accepted cues are discarded after the
response. The conductor is well-tested pure logic with no caller. Fix sketch:
V1.x for true streaming. For V1 honesty: change the accepted-cue notice to "cue
accepted" (not "applied"), and run `decideCrisisAction` on every voice/text cue
in `evaluateLivingSceneCue` (it's sitting right next to the parser).

### 5. The excellent keep/share/takedown loop has no product caller — API-only feature

Severity: P1 Evidence: workspace grep — the only callers of
`POST /v1/living-scenes/artifacts/keep`,
`POST /v1/living-scenes/artifacts/:id/share`,
`POST /v1/living-scenes/shares/:id/revoke`, `GET /v1/living-scenes/artifacts`
are e2e fixtures (`apps/oshun/web/e2e/support/living-scene.ts:114,128`). No
web/mobile surface lists kept offerings, creates shares, or revokes them. The
operator takedown route (`/v1/living-scenes/admin/artifacts/:id/takedown`)
likewise has no console UI. Spec promise: 4228-4252 (user keeps, returns to,
shares, revokes from product surfaces). What the code actually does: a user has
no way to keep a scene, see their kept offerings, mint a share link, or revoke
one. Every public share in existence was created by a test. Fix sketch: a "My
offerings" list view (GET artifacts → share/revoke buttons) closes the loop with
routes that already exist and are tested; add a takedown form to the tenant
console.

### 6. Tenant share policy is client-supplied request headers; the admin store is never enforced

Severity: P0-SEC Evidence:
`apps/oshun/bff/src/routes/living-scenes.ts:1624-1651` (`parseTenantSharePolicy`
reads `x-oshun-living-scenes-share-allowed`, `-public-share-allowed`,
`-policy-state` etc. from the request), `:1300-1316` (card tenant policy from
headers), `:1565-1577` (retention bounds from headers), `:644-645` (mass-share
window/threshold from `x-oshun-share-window-seconds`/`x-oshun-share-burst-max` —
the sharer can lift their own spam gate), `:707-708` (crisis label from
`x-oshun-living-scene-crisis-label`), `:1543-1549` (`x-oshun-now` time
override). Meanwhile
`apps/oshun/bff/src/tenant-console/tenant-living-scenes-store.ts` holds the "BFF
source of truth" policy record (shareAllowed/shareScope/retention/branding)
whose only consumer is the read-only admin console route
(`routes/admin-tenant-console.ts:57`). Spec promise: 4358-4374 ("fail-closed
enforcement: the share flow consults tenant policy at every step; ambiguous
policy resolves to deny"), FERPA-class per-tenant share denial. What the code
actually does: an authenticated member of a share-banned tenant simply omits the
headers (defaults: shareAllowed=true, publicSharingAllowed=true) and shares
publicly; the fail-closed branches only fire if the client volunteers the
denying header. Two sources of truth: the stored policy the operator edits is
decorative. Fix sketch: resolve policy server-side from
`tenantLivingScenesStore` keyed by `authContext.tenantId` (headers at most as
test overrides in non-prod); same for the mass-share thresholds and
`x-oshun-now`.

### 7. Shareability matrix is a self-fulfilling tautology; crisis state rides a header

Severity: P1 Evidence: `apps/oshun/bff/src/routes/living-scenes.ts:706` passes
`defaultShareabilityGrants(reachForPrivacy(effectivePrivacyLevel))`;
`libs/yemaya/living-scenes-runtime/src/personal-artifacts/personal-artifacts.ts:934-946`
fabricates all nine component grants at exactly the requested reach with
invented rationales ("music bed licensed for requested reach", "voice consent
grants requested reach") — no consent record, rights metadata, or license is
ever consulted. Crisis gating depends entirely on the optional
`x-oshun-living-scene-crisis-label` header. Spec promise: 4299-4321
(per-component grants from the component's own consent record; resolution = min
across components; crisis-touched ⇒ private-only regardless). What the code
actually does: `resolveShareability` (real machinery, real crisis private-only
rule) is fed inputs constructed so it can never restrict anything on the live
path. The matrix "passes" for every share by construction. Fix sketch: derive
grants from real sources where they exist (tenant policy → the `tenant-policy`
component; score.intent presence → `intent`; Lilith check → `lilith-policy`;
crisis from a server-side record not a header) and mark the rest honestly
`'public'`-by-absence with rationale "no restricting record".

### 8. Intent redaction is unenforced — the "public-redacted" layer is whatever the client sent

Severity: P1 Evidence: `redactIntent`/`buildIntentLayers`
(`libs/yemaya/living-scenes-runtime/src/cue-privacy/intent-redaction.ts:87,133`)
have zero non-test consumers; the keep route accepts
`score.intent.publicRedactedText` verbatim
(`routes/living-scenes.ts:1375-1390`); `score-schema.ts:286-323` validates only
shape and that `privateIntentHash` is 64 hex chars — it never recomputes the
hash over `privateText` nor checks the public layer was actually redacted. The
public viewer then publishes `publicRedactedText` as the OG description
(`personal-artifacts.ts:1104-1110`). Spec promise: 4279-4290 (a Lilith-governed
redaction classifier writes the public layer; default-deny on
share-with-private). What the code actually does: a buggy or malicious client
can place the private intent (named persons, disclosures) into
`publicRedactedText` and it goes straight to the public page and OG preview. The
204-line redaction engine with the category taxonomy sits unused. Fix sketch: in
the keep route, recompute `privateIntentHash` (`stableHash` already there) and
run `redactIntent(privateText)` server-side, overwriting the client's public
layer.

### 9. OG/Twitter preview image and viewer stills point at a route that does not exist

Severity: P1 Evidence: `routes/living-scenes.ts:1007-1009,1121-1123` builds
`firstFrameUrl = ${origin}/api/living-scenes/<shortCode>/first-frame`; repo-wide
search: no `first-frame` handler in `apps/oshun/web/src/app/api/**` and no such
BFF route. The viewer uses it for the OG image, Twitter card, motion-layer
background, and the reduced-motion still (`scene/[id]/page.tsx:43,49,239,251`).
Spec promise: 4259-4261 (OG/Twitter previews generated from the Score's first
frame). What the code actually does: every share's social preview image and the
viewer's background/reduced-motion still are 404s. Fix sketch: add
`GET /v1/living-scenes/public/:shortCode/first-frame` that returns the first
frame PNG of the stored APNG (one chunk-slice away), and point `firstFrameUrl`
at it.

### 10. Share media is RAM-only and the spec's materialization lifecycle is orphaned

Severity: P1 Evidence: `routes/living-scenes.ts:174`
`shareMediaById = new Map<string, Buffer>()` is excluded from
`captureLivingSceneShareSnapshot()` (`:228-237`) — after a BFF restart the media
route 404s `media_not_materialized` (`:1084-1091`) for every pre-restart share
while the viewer still renders the share's C2PA manifest/media hash as playable.
No re-render path exists despite the renderer being deterministic. The whole B4
lifecycle —
`newMaterialization`/`completeMaterialization`/`shouldEvict`/`evict`/`resolveShareRequest`
(`envelope/materialize-on-share.ts`) with its 30-day post-share TTL — has zero
consumers; `share.materialization` is created once at `state: 'pending'` and
never completed even though real bytes now exist at exactly that moment. Spec
promise: 4184-4187 (MP4 cached 30 days post-share then evicted; re-render on
demand reproduces bit-equivalently). What the code actually does: media lives
forever in process memory, dies on restart, and the share's own
`materialization.state` stays `'pending'` (with the `materialization-pending`
advisory note) even while the real APNG is being served. Fix sketch: persist
media (or re-render on miss — it's deterministic given finding 3's fix) and call
`completeMaterialization` with the APNG evidence at share-create time.

### 11. Download grant promises an MP4 that doesn't exist, with no download endpoint

Severity: P1 Evidence: `personal-artifacts.ts:640-668` — `createDownloadGrant`
returns `format: 'mp4'` (hardcoded) for media that is APNG, plus
`c2paManifestId` and `visibleMarkShortCode` implying intact embedded provenance;
no route exchanges `grantId` for bytes. The viewer's "Download grant" button
(`scene/[id]/page.tsx:363-367`) form-POSTs to the grants route and the browser
lands on raw JSON. Spec promise: 4250-4252 (download grant produces an MP4
download with C2PA + visible mark

- audio watermark intact). What the code actually does: mints grant metadata for
  a nonexistent file format with no redemption path — a result-shaped record
  with no result behind it. Fix sketch: `format: 'apng'`, add
  `GET .../download?grant=<id>` serving the stored bytes with
  `content-disposition: attachment` (validating grant expiry), and handle the
  response client-side.

### 12. Visible provenance mark is never embedded in the media bytes

Severity: P1 Evidence: the renderer supports burn-in
(`living-scene-render.ts:159,193-195` `watermark?: { mark, placement }` →
`embedWatermarkMark`), but neither the share path
(`routes/living-scenes.ts:672-677`) nor the standalone render route
(`living-scenes/render-route.ts:62`) passes one — despite `server.ts:601-603`
logging "watermarked … PNG frames" and lib comments claiming "the deterministic
PNG render still carries a visible watermark" (`personal-artifacts.ts:497-500`).
The shortCode badge is a DOM overlay only (`scene/[id]/page.tsx:273-275`). The
channel-robustness eval (`provenance/channel-robustness/channel-eval.ts`
`evaluateAllChannels`) has zero consumers. Spec promise: 4207-4210 (visible mark
in a corner of the media, survives crops and recompression — one of the four
redundant channels), 4219-4224 (per-channel survival measured, below-100% blocks
release). What the code actually does: saving/re-sharing the APNG strips every
embedded provenance channel (no visible mark in pixels, no audio to watermark);
only the C2PA sidecar in the API response and the attestation page survive — and
the survival eval that would have caught this never runs. Fix sketch: render the
`visibleMark.shortCode` glyph via `embedWatermarkMark` into the frames before
APNG assembly (re-render after `createLivingSceneShare` returns the mark, or
precompute the mark from the share seed which is available before render).

### 13. Unauthenticated, un-rate-limited CPU-bound render endpoint

Severity: P1 Evidence: `living-scenes/render-route.ts:42` —
`POST /v1/living-scenes/render` has no `preHandler` (contrast every other
living-scenes route: `authProtection` or `publicAbuseProtection`); registered
live in `server.ts:603`. Accepts up to 1920×1920×120 frames per request: ~440M
pixels through per-pixel gradient + gaussian blur

- PNG encode + base64 into a single JSON response. No global rate-limit plugin
  found in `server.ts`/`app.ts`. No product consumer calls this route (dead +
  exposed). Spec promise: none for an anonymous bulk-render API; 4268-4270
  requires rate limiting on public surfaces. What the code actually does: lets
  any anonymous caller pin a CPU core for seconds and extract multi-hundred-MB
  responses in a loop. Fix sketch: add `publicAbuseProtection` (and/or auth),
  cap dimensions to the 480×270 profile actually used, or remove the route until
  something consumes it.

### 14. Reduced-motion is claimed but the animation keeps playing

Severity: P1 Evidence: `scene/[id]/page.tsx:196-200` — `prefers-reduced-motion`
CSS hides `[data-living-scene-motion-layer]` (a static background div!) and
shows the carousel div, but the animated APNG `<img>` (`:263-272`) sits in a
third, always-visible container and keeps animating; the panel hardcodes
`data-honors-reduced-motion` from
`viewerChrome.honorsReducedMotionPreference: true`
(`personal-artifacts.ts:1056-1057`, literally hardcoded `true`). Shares with
`rendition: 'reduced-motion'` receive the identical animated media
(`routes/living-scenes.ts:672-689` ignores the rendition). Spec promise:
4430-4436 (reduced-motion render = still-image carousel, independent shareable
artifact), 4449-4450 (accessibility release gate). What the code actually does:
vestibular-safety users get the full animation plus a label saying
reduced-motion is honored. Fix sketch: wrap the APNG `<img>` in the motion layer
(so the CSS hides it) and show the first frame still in the reduced-motion
branch; serve a 1-frame APNG for `rendition: 'reduced-motion'` shares.

### 15. Captions / audio descriptions / reduced-motion stills are dead `oshun://` metadata

Severity: P2 Evidence: `personal-artifacts.ts:1010,1025,1036,1050` —
`stillFrameUrl`, caption `sidecarUrl`, AD `sidecarUrl`, transcript `downloadUrl`
all use the unresolvable `oshun://living-scenes/...` scheme;
`burnInAvailable: true` hardcoded with no burn-in pipeline; no caption/AD media
exists (the render is silent and visual-only). The viewer displays "Captions:
en-US vtt · burn-in available" (`scene/[id]/page.tsx:323-326`). (Transcripts are
the honest exception: real text served by the BFF transcript route.) Spec
promise: 4437-4443 (sidecar caption track, per-segment AD, both ship in
lockstep). What the code actually does: claims caption/AD coverage in
user-visible UI while the URLs cannot resolve anywhere — the same `oshun://`
fabrication pattern B4 removed from materialization survives in the
accessibility envelope. Fix sketch: generate a real VTT from the transcript
lines (text exists), serve it beside the transcript route; report AD as
unavailable until an AD track exists.

### 16. "Lilith pre-share re-check" is a six-phrase English substring blocklist

Severity: P2 Evidence: `personal-artifacts.ts:1088-1098` —
`containsBlockedSafetyFrame` checks
`['suicide','self-harm','kill myself','medical diagnosis','abuse disclosure','location of safety']`
against segment display-names/parameters and cue args; it never inspects the
rendered media, narration, or non-English text; no integration with the actual
Lilith policy substrate or policy versioning beyond echoing a header string
(`routes/living-scenes.ts:709-710`). Spec promise: 4271-4274 (fresh Lilith
re-check on the materialized artifact at every outbound share), 3696-3699
(locale parity). What the code actually does: a keyword screen on metadata text,
labeled as the Lilith re-check in the share record (`lilithPreShareCheck`). Fix
sketch: route the score text through the BFF's real Lilith moderation seam
(svc-moderation exists) and record its verdict + true policy version; keep the
keyword screen as a fast pre-filter only.

### 17. Per-template cue verb sets diverge from the spec

Severity: P2 Evidence: `customer-card.ts:109-147` — Veritas omits `shape-by`
(spec 4068-4070 grants `shape-by(narrator-pace)`); Metis omits `morph` and
`shift-style` (spec 4092-4095 grants both — "the most expressive of the five");
Nyx omits `recall` (spec 4058 grants it) while gaining `morph`/`shift-style` the
spec never lists for Nyx. Spec promise: 4058-4059, 4068-4070, 4092-4095. What
the code actually does: enforces a different verb policy per template than the
one specified — users of Metis lose exactly the pedagogical verbs the spec calls
essential. Fix sketch: align `TEMPLATE_ALLOWED_VERBS` to the spec lists (3-line
constant edit) and add a test asserting each template's set against the spec.

### 18. Customer card cost/latency "estimate" is string-length numerology

Severity: P2 Evidence: `customer-card.ts:304-309` —
`domainOffset = template.domainId.length % 3`;
`costUsd = 0.42 + domainOffset * 0.08 + proClass`;
`latencySeconds = 42 + domainOffset * 11 …`. Rendered to users via
`estimateLivingSceneTemplate` on the card. Spec promise: none specifically — but
presenting a computed-looking `costUsd` derived from the length of the word
"veritas" is a fabricated result on a live surface. What the code actually does:
invents stable but meaningless cost/latency numbers. Fix sketch: replace with
honest static per-template figures (or drop the fields) until a real cost model
exists.

### 19. Technique catalog exists three times; the real one (blend-kernel) is fully orphaned

Severity: P2 Evidence:
`libs/yemaya/blend-kernel/src/catalog/cinematographic-catalog.ts` (422 lines,
full `CinematographicTechnique` entries with kernel compositions, eval
thresholds, allowlists) + `catalog/tone-gating.ts` + `compatibility/scorer.ts` +
`continuity-evals/per-technique.ts`: **zero consumers in any app** (repo-wide
grep for `@yemaya/blend-kernel` over `apps/` returns nothing). The compose
surface instead uses a second, independent allowlist embedded in
`living-scenes-runtime/src/compose-assist/compose-assist.ts:208-236`
(`TEMPLATE_TECHNIQUE_ALLOWLISTS`), and
`libs/contracts/src/living-scene/technique.ts` is a third (schema) copy. Spec
promise: 3811-3871 (one versioned, contract-backed catalog feeding picker,
assist, and promotion gates). What the code actually does: the catalog that
satisfies the spec is dead code; the copy that runs is a reduced allowlist that
can silently drift from it. Fix sketch: make compose-assist consume the
blend-kernel catalog (delete the embedded allowlists), and validate the catalog
against the contracts schema in a test.

### 20. AAA Scene Score Editor is a single-fixture demo that ignores its own API

Severity: P2 Evidence:
`apps/yemaya/studio-web/src/score-editor/ScoreEditorPage.tsx` (no fetch; all
state from `SCENE_SCORE_FIXTURE`), `core.ts:153-300` — one hardcoded score
("Evening Sea Walk") with hand-written transition eval numbers (fvd 48/61,
flicker 0.06…) and carry-state scores; promotion (`createPromotedVersion`)
mutates local state only. The entitlement-gated `/v1/score-editor` API
(`apps/yemaya/api/src/routes/score-editor.ts`) serves only a static session
descriptor and is not called by the editor. Spec promise: 4116-4146 (score
versioning on every save, promotion through Isis environments, fixture-rehearsal
against the workflow class's fixture set, replay of previously rendered
sessions). What the code actually does: the timeline/inspector/scorecard/replay
UI affordances exist and the gate logic (`isTransitionGatePassed`,
`canPromoteScore`) is real, but there is exactly one score in the world, its
eval numbers are authored by hand, and nothing persists. Fix sketch: V1:
load/save scores via the yemaya API (a real store behind `/v1/score-editor`);
keep the fixture as the seeded example. Real eval scoring is V1.x.

### 21. Dead BFF card/cue routes + duplicated entitlement logic client-side

Severity: P2 Evidence: `GET /v1/living-scenes/card` and
`POST /v1/living-scenes/card/cues` (`routes/living-scenes.ts:492-540`) have no
callers anywhere (web/mobile/tests-of-product); the web card imports
`resolveLivingSceneCard`/`evaluateLivingSceneCue` directly from the lib and
evaluates tier/tenant/cue policy in the browser
(`LivingSceneCard.tsx:73,131-138`), fed by headers parsed in the page
(`living-scene/page.tsx:55-70`). Spec promise: 4028-4030 (card resolves
templates from tier and tenant entitlements — an enforcement decision). What the
code actually does: two parallel implementations of the same gate; the enforced
one (server) is unreachable, the reachable one (client) is advisory by nature.
Drift between them would be invisible. Fix sketch: have the card fetch
`/v1/living-scenes/card` + post cues to `/card/cues` (routes already tested),
keeping the lib calls as optimistic UI only — or delete the routes.

### 22. Customer-tier composition caps unenforced server-side

Severity: P2 Evidence: free ≤6 segments/≤90s and paid ≤24/≤8min live only in
client-side `validateCompositionDraft` (`compose-assist.ts:454`); the BFF keep
route accepts any score of any size for any tier
(`routes/living-scenes.ts:554-590`; `score-schema.ts:143-176` checks structure
only — no segment-count/duration/tier rule). Spec promise: 3791-3796 (tier caps;
crisis frame disables free-tier composition). What the code actually does: caps
are a UI suggestion; the persistence layer takes anything. Fix sketch: enforce
the caps in the keep route from `authContext` tier + segment count/total
duration.

### 23. Determinism / parity / engine-upgrade / rollback / eval-gate engines: zero callers, zero fixtures

Severity: P2 Evidence: repo-wide grep — `checkDeterminism`
(determinism-harness), `assertNodeParity` (n-node-parity),
`checkUpgradeAvailability`/`planRebake` (engine-upgrade — the spec's "v2
available" one-tap re-bake has no surface),
`planRollback`/`isArtifactPlayableAfterRollback` (release-gates/rollback),
living-scenes `evaluateReleaseGate` (eval-gates): all NONE outside the lib's own
tests. No per-template fixture corpus (spec ≥30/template) exists anywhere. Spec
promise: 4179-4200 (determinism as release gate, upgrade migration), 4452-4478
(eval sets, gates, rollback). What the code actually does: the
gate/parity/upgrade logic is real, typed, tested — and disconnected from any
pipeline; nothing can pass or fail these gates in production. What's V1 vs V1.x:
with finding 3 fixed, `checkDeterminism` over the compositor is a cheap real CI
gate (V1); N-node parity, FVD-class continuity evals and 30-fixture corpora are
V1.x. Fix sketch: wire `checkDeterminism` into a living-scenes CI test over the
real render path; leave the rest explicitly recorded as V1.x.

### 24. Latency/quality budgets and backpressure: nothing measures anything

Severity: P2 Evidence: no timing capture on any living-scenes route; conductor
backpressure (`conductor.ts:171`) orphaned (finding 4); no P50/P95 dashboards,
no SLO alerts, no per-provider metrics (there are no providers). Spec promise:
4397-4413 (first-frame P50 ≤400ms, segment latency, cue latency budgets, quality
dashboards, SLO breach alerts). What the code actually does: budgets exist only
as prose. With no streaming runtime this pillar is structurally V1.x; flagged so
it is consciously deferred rather than assumed. Fix sketch: record per-route
timings into the existing BFF observability when the runtime lands (V1.x).

### 25. Public viewer keyboard cue buttons are inert

Severity: UX Evidence: `scene/[id]/page.tsx:342-351` — cue verb
`<button type="button">` elements in a server component with no handlers, no
form, no navigation; presented under "Accessibility" alongside
`data-assistive-tech-navigable="true"`. Spec promise: 4446-4448 (cue verbs
available via keyboard). What the code actually does: renders verbs from the Cue
Plan as buttons that do nothing on a static replay page (where cues have no
meaning anyway). Fix sketch: render them as a read-only list ("cues used in this
offering") until replay exists; don't ship buttons that no-op.

### 26. Creation→viewing→sharing journey is four disconnected islands

Severity: UX Evidence: synthesis of findings 1, 2, 5: (a) the card claims to
generate/keep/share but calls nothing; (b) compose promotes a
`PromotedCompositionScore` type that cannot be kept (keep wants the unrelated
`score-schema.Score`) — its output evaporates; (c) keep/share is reachable only
by API; (d) the public viewer plays media unrelated to any score. No navigation
links compose → card → offerings → share → viewer; `/studio/compose` and
`/studio/generation/living-scene` are anon PUBLIC_PATHS (proxy.ts:42-43)
detached from the authed product shell. Spec promise: 3611-3639 (one continuous
customer promise from "I asked for an evening walk by the sea" to a kept,
shareable offering). What the code actually does: each surface demos its own
slice; no user can traverse the promised journey end to end. Fix sketch: one
artifact spine — compose promotion emits a `score-schema.Score`, card Keep posts
it, an offerings list links to share creation, share links to the viewer.

### 27. Provenance signing & unlock secrets — correctly deploy-bound (for the record)

Severity: DEPLOY Evidence: `routes/living-scenes.ts:1472-1500`
(`OSHUN_LIVING_SCENES_C2PA_SIGNING_KEY` absent in prod ⇒ share route 503
fail-closed; deterministic dev key otherwise);
`living-scenes/scene-unlock-token.ts:104` (`OSHUN_SCENE_UNLOCK_TOKEN_SECRET`
required in prod). The fal LTX enhancer remains release-gate-bound per D4 — not
a finding. What deployment needs: set both env vars (32-byte hex signing key +
unlock secret) and publish the signer public key for verification.

---

## Severity counts

| Severity   | Count  | Findings                               |
| ---------- | ------ | -------------------------------------- |
| P0-SEC     | 1      | 6                                      |
| P0-HONESTY | 2      | 1, 2                                   |
| P0-STRUCT  | 2      | 3, 4                                   |
| P1         | 9      | 5, 7, 8, 9, 10, 11, 12, 13, 14         |
| P2         | 10     | 15, 16, 17, 18, 19, 20, 21, 22, 23, 24 |
| UX         | 2      | 25, 26                                 |
| DEPLOY     | 1      | 27                                     |
| **Total**  | **27** |                                        |
