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', addGET .../download?grant=<id>serving the stored bytes withcontent-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: addpublicAbuseProtection(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 |