Date: 2026-06-11. Read-only static audit of the residual after the 06-10 ground-truth audit's 57 tasks landed. Evidence = current source only.
Headline verification#
The 06-10 headline — "No persisted authored artifact; lanes are stateless analyzers" — is STILL TRUE for editorial content, with three narrow nuances:
- The
/v1/admin/studio/*lane "stores" remain pure analyzers over caller-supplied JSON (verified:approval-workflow-store.ts,comment-thread-store.ts,asset-rendition-store.ts,file-ingestion-store.ts— all documented and implemented as "pure + deterministic; 0 Date.now/random" evaluators with no record persistence). No comment, approval, asset, or i18n state survives a request. - What DOES persist now: editorial release-streams (durable store + admin
panel, see finding on enforcement), the C8 communications loops
(banners/help/templates/ status), opaque per-user workspace preferences
(
studio/workspace-state-store.ts— explicitly "density, viewport mode, saved filters", not artifacts), and the game-domain authoring records (Hathor story-graph/quest, Bellona — out of this slice's editorial-content scope but real persistence). @oshun/studio-authoringstill has exactly 1 consumer (StudioAuthoringWorkspace.tsx, importing 7 functions fromauthoring-blocks+evaluatePublishReadiness);@oshun/customer-curationgained exactly 1 consumer (the sharing engine vialibrary/share-policy-store.ts). Everything else in both libs remains orphaned.
Nisaba seeds fabricated notebooks and annotations as the member's own, ungated in production#
Severity: P0-HONESTY
Evidence:
apps/oshun/bff/src/nisaba/state.ts:847-856—getInternalRecordlazily callsbuildSeedRecord(userId)for EVERY user; noallowFixtures()/production gate anywhere in the file.apps/oshun/bff/src/nisaba/state.ts:1563-1581— seed annotationnisaba-annotation-compassion-1is attributedauthor: { userId, displayName: deriveDisplayName(userId) }with note text the member never wrote ("The cessation is framed as a rule, not a feeling.") and fixed timestamps (2026-03-22/23).apps/oshun/bff/src/nisaba/state.ts:1586-1625— two seed notebooks (nisaba-notebook-attention,nisaba-notebook-compare) populated with items.apps/oshun/bff/src/search/user-object-candidates.ts:75-79— C7's universal search ranksnisabaConsumerStateStore.listNotebooks(userId)as the member's "REAL objects", so a brand-new member searches and finds two notebooks "they" curated.- No
seedDataNotice(the E1 labeling pattern) on any/v1/nisaba/notebooksresponse (grep -i seed routes/nisaba.ts→ 0 hits).
Spec promise: features.md 1768 ("Customer-controlled notebooks…"), 1786-1788 (annotation durability tests). D6 fixed PASSAGE reading state to honest-zero (state.ts:876 comment) and C4 killed the identical Iris seed pattern — this same class survived in the notebook/annotation seed.
What the code actually does: serves every member identical fabricated notebooks and self-attributed annotations as durable-looking personal data, and surfaces them in search results among real saves.
Fix sketch: start buildSeedRecord with empty notebooks/annotations maps
(keep corpus passages, evidence views), or gate the personal seeds behind
allowFixtures() + an explicit seed label, mirroring C4/E1.
Collections exist as two disconnected systems (server store vs localStorage fixtures)#
Severity: P0-STRUCT
Evidence:
- Server system:
apps/oshun/bff/src/routes/domain-stubs.ts:2482-2540—GET/POST /v1/library/collections, auth-scoped, persisted (domain-stubs-store.ts:164-199, snapshot/Postgres-backed); written byapps/oshun/web/src/app/library/collections/new/LibraryNewCollectionForm.tsx:51, read byapp/library/collections/page.tsx:45and by universal search (search/user-object-candidates.ts:57-64). - Client system:
apps/oshun/web/src/components/library/LibraryDashboard.tsx:106-162ships 4 hardcoded fixture collections (evening-integration,research-to-practice,sky-and-scripture,learning-reentry) +LIBRARY_COLLECTION_ITEM_IDS; all collection CRUD (handleCreateCollectionFromFilters:830,handleSaveCollectionDetails:880,handleToggleCollectionItem:893,handleArchiveCollection:905) mutates React state +localStorage(oshun.library.collections.v1, :642-650). The dashboard NEVER fetches or posts/v1/library/collections(zero network calls for collections in the file). LibraryDashboardis the component behind both/libraryand/library/collection/[collectionId].
Spec promise: features.md 1768-1772 (customer-controlled collections, ordering, import); 1788 ("progress sync across devices").
What the code actually does: a collection created on /library/collections/new
is durable and searchable but never appears in the dashboard's collection
manager; a collection created/edited/archived in the dashboard is device-local,
invisible to the index page, to search, and to other devices. E2 fixed exactly
this split for saved ITEMS but left collections behind. The fixture collections
are also unlabeled and prod-visible (the BFF-side fixture merge at
domain-stubs.ts:2500 is allowFixtures()- gated; the client-side ones are not).
Fix sketch: back the dashboard's collection state with /v1/library/collections
(reuse the saved-items LWW sync pattern from webLibraryStore.ts); drop or
guest-gate the client fixture collections.
/studio/authoring: unlabeled fixture document, fabricated governance bindings, and a "saved" claim with zero persistence#
Severity: P0-HONESTY
Evidence:
apps/oshun/web/src/components/studio/StudioAuthoringWorkspace.tsx:23-64—SEED_DOCUMENT(doc-demo, "Morning Tara Practice"); :101-108DEMO_BINDINGSwith fabricated governance ids (sophiaEvidencePackId: 'ep-tara-baseline',lilithToneReviewId: 'tr-2026-05-12-tara') never read from Sophia/Lilith/Isis.- :415-438 — edits run the lib's
acceptAutosaveand setautosaveStatus = 'saved @ lamport=N'; nothing is written anywhere (the only network calls in the file are the readability lane, :170/:220). Reload discards the document. The component doesn't even use the available/v1/studio/workspaces/:id/statepreference store. - :740-741 — publish readiness renders "✓ All bindings satisfied — eligible for
normal pipeline" computed from the fabricated
DEMO_BINDINGS; :787 — AI assist renders "request is eligible for upstream agent" — no upstream agent exists or is called. - No demo/preview labeling anywhere in the render;
DomainPreviewBannercovers/domains/*only (components/domains/DomainPreviewBanner.tsx:1-13) — this surface is uncovered.
Spec promise: features.md 1638-1640 ("Save, autosave, draft locking, … recovery, named checkpoints"), 1620-1624 (authoring "governed by Sophia grounding, Isis … policy, and Lilith tone policy").
What the code actually does: a demo harness over real lib evaluators, presented with the same visual authority as a live workspace, telling the operator their work is "saved" and governance-bound when neither is true.
Fix sketch: label the surface as a fixture rehearsal until persistence lands, change "saved" to an honest "in-memory only" status, and source bindings from real reads or render them as explicitly unbound.
Atelier/Lilith-studio scenes: global unscoped store with unauthenticated read and write#
Severity: P0-SEC
Evidence:
apps/oshun/bff/src/routes/domain-stubs.ts:2543—GET /v1/atelier/sceneshas NO preHandler (no auth); it listsdomainStubsStore.scenes.list().apps/oshun/bff/src/routes/domain-stubs-store.ts:215-246— the scenes store is a globalMapwith noownerUserId;list()returns every row to every caller.domain-stubs.ts:2565(POST /v1/lilith-studio/scenes) and :2597 (POST /v1/atelier/scenes) take onlyoriginGuard, csrfGuard— no auth guard — and persist into the same global store.
Spec promise: features.md 1786 ("sharing-permission isolation"); cf. A7, which fixed exactly this for library collections.
What the code actually does: any unauthenticated caller can write scene records, and every user's authored scene titles (e.g. practice/ritual titles, which are personal) are served to every other user and to anonymous callers.
Fix sketch: add the auth preHandler to all three routes, add ownerUserId to
StoredAtelierScene/StoredStudioScene, and filter list() by owner exactly
as LibraryCollectionStore.list does. (Cross-slice note: atelier may belong to
the generation slice; flagged here because it shares the curation route
file/store.)
Share-link journey dead-ends: minted URLs route nowhere and the grant carries no content#
Severity: P1
Evidence:
apps/oshun/bff/src/library/share-policy-store.ts:318-321— created links point athttps://oshun.app/share/<token>/oshun://share/<token>.- No
app/shareroute exists in the web app (find apps/oshun/web/src/app -d -name "share*"→ only a Bellona studio dir); onlylib/deep-link-associations.ts:12-13declares the path for OS association files. apps/oshun/bff/src/routes/library-sharing.ts:213-244— the recipient resolution endpointGET /v1/library/shared/:collectionIdreturns ONLY{ collectionId, granted, tier, via }— never the collection's label or items — and a repo-wide grep finds zero callers (web, mobile, miniapp).- Mobile's
deepLinkHydration.ts:103-124parses/share/<token>into a share route, butshare/mobileShareIngest.tsis the inbound share-INTO-app system; nothing resolves a collection share token to content.
Spec promise: features.md 1773-1775 (shared via link with view/comment/copy permissions).
What the code actually does: an owner can mint a cryptographically sound link whose recipient lands on a 404 page, and even calling the API directly yields a permission verdict with nothing to view. The engine is real; the journey delivers nothing.
Fix sketch: add a /share/[token] web page that calls the resolve endpoint, and
extend the resolve response (on grant) with the collection label + items read
from domainStubsStore.collections.
Share-policy ownership is first-writer-wins and collides on fixture collection ids#
Severity: P1
Evidence:
apps/oshun/bff/src/library/share-policy-store.ts:140-158(setVisibility) and :186-194 (createShareLink) — when no policy exists for acollectionId, the caller becomes its owner; ownership is never checked against the actual collections store (domainStubsStore.collectionshasownerUserId, never consulted).apps/oshun/web/src/components/library/LibraryDashboard.tsx:1051— share controls operate onselectedCollection.id, which for every member includes the SAME four fixture ids (evening-integration…, :106-162).- Collection ids are guessable (
col-<Date.now()>-<seq>,domain-stubs-store.ts:174).
Spec promise: features.md 1786 ("sharing-permission isolation").
What the code actually does: the first member to touch sharing on
evening-integration owns it tenant-wide — every other member then gets 403
"Only the collection owner can change its sharing" on a collection their own UI
presents as theirs. A malicious member can likewise pre-claim the share policy
of another member's real collection id and lock them out of sharing it.
Fix sketch: on first policy creation, verify the caller owns collectionId in
domainStubsStore.collections (404/403 otherwise); becomes automatic once
finding #2 unifies the two collection systems.
Share policies/links are not durable — every minted link dies on deploy#
Severity: P1
Evidence: librarySharePolicyStore is a plain in-memory Map
(share-policy-store.ts:101) with a reset() but no snapshot wiring; it is
absent from the wireDurable* roster in apps/oshun/bff/src/server.ts (which
durably wires ~40 sibling stores, including living-scene shares at :106).
Spec promise: features.md 1773-1775; links are offered with 30-day and "No
expiry" presets (CollectionShareControls.tsx:79-83).
What the code actually does: hands the member a long-lived URL whose server-side
grant evaporates on the next restart (library_collection_not_found 404).
Fix sketch: add toSnapshot()/hydrate() + wireDurableLibrarySharePolicies in
server.ts, same pattern as wireDurableLivingSceneShares.
User-created Nisaba notebooks/annotations are in-memory only#
Severity: P1
Evidence: nisabaConsumerStateStore (apps/oshun/bff/src/nisaba/state.ts:859)
is a plain class instance; no snapshot serialization exists in the file and
server.ts has no nisaba wiring (grep nisaba server.ts → 0). Meanwhile
createNotebook (:704), addNotebookItem (:764), annotation CRUD (:552-633)
are real per-user mutations exposed at POST /v1/nisaba/notebooks (+items) and
used by the live web forms
(app/nisaba/notebook/new/NisabaNewNotebookForm.tsx:43).
Spec promise: features.md 1786-1788 ("annotation durability and progress sync across devices").
What the code actually does: a member's authored notebooks and annotations — the one real authored-artifact write path in this slice — are lost on every deploy, while the fabricated seeds (finding #1) come back.
Fix sketch: add snapshot persistence (wireDurableNisabaConsumerState)
following the Telegram-user-state / iris-memory pattern (explicit Map/Date
serialization).
/studio/authoring is an orphan route — nothing links to it#
Severity: P1
Evidence: repo-wide grep for studio/authoring in apps/oshun/web/src matches
only the page itself (app/studio/authoring/page.tsx); the studio hub
(app/studio/page.tsx) links sibling lanes (e.g. review-approval-workflows is
linked from the hub and four other pages) but never authoring. Only e2e specs
visit it by URL.
Spec promise: features.md 1592-1596 (authoring "exposed through Oshun Studio rather than buried", "first-class product surfaces").
What the code actually does: the slice's flagship §16.2 surface is unreachable by any in-app navigation.
Fix sketch: add the authoring card/link to the studio hub catalog (and command palette), or fold the workspace into a linked surface.
Editorial lifecycle state machines (×2) have zero runtime consumers; live content uses ad-hoc states#
Severity: P1
Evidence:
libs/oshun/studio-authoring/src/editorial-lifecycle/lifecycle.ts:15-44— the full 12-state canonical machine + gate-checkedtryEditorialAdvance; zero imports outside the lib (repo grep).libs/oshun/domain-veritas/src/editorial/state-machine.ts:1-29— a SECOND 12-state editorial machine (Veritas variant, exported asEditorialStateMachine); zero imports in any app.- The content that actually publishes (admin communications help
articles/banners, C8) and the release-streams store each use their own small
status enums; persona lifecycle (
admin/persona-lifecycle-store.ts) reuses 'in-review' textually but not the machine.
Spec promise: features.md 1651-1657 ("Canonical content lifecycle states reused across all domains" + the ARCHITECTURE diagram cross-ref).
What the code actually does: two orphaned canonical machines and N ad-hoc per-store status enums — the canonical-reuse promise is inverted.
Fix sketch: when any artifact persistence lands (notebooks aside, the
help-article store is the nearest fit), drive its status transitions through
tryEditorialAdvance; retire or delegate the Veritas duplicate.
Editorial release streams: embargo/blocked states stored but never enforced#
Severity: P1
Evidence: apps/oshun/bff/src/admin/admin-editorial-release-streams-store.ts is
a real durable store with kinds incl. embargo (requires embargoLiftsAt,
:153) and a validated status machine (:209-224); its ONLY consumers are its own
routes (routes/admin-editorial-release-streams.ts, mounted at
/v1/admin/editorial/release-streams) and the admin panel
(apps/oshun/admin/src/components/EditorialReleaseStreamPanel.tsx). No publish,
generation, communications, or delivery path reads stream status or
embargoLiftsAt (repo grep for the store outside its own route → 0).
Spec promise: features.md 1664-1666 (embargo/takedown/hotfix flows that bind editorial decisions), 1671-1673 ("embargo respect" tests).
What the code actually does: an operator can mark a subject "blocked" or embargoed until a date, and nothing anywhere prevents or delays anything.
Fix sketch: at minimum, have the C8 communications publish path and the generation release leg consult open release streams for their subject ids before serving; or document the store as advisory-only in the route header.
Orphaned engine census: 7 of 9 studio-authoring module families and 5 customer-curation modules still have zero consumers#
Severity: P2
Evidence (repo-wide import grep, non-test):
@oshun/studio-authoringconsumed:authoring-blocks(5 files) +editorial-lifecycle/pipeline.evaluatePublishReadiness— all via the single demo workspace. Zero consumers:creator-roles/(roles, onboarding, scorecards),asset-metadata/(metadata, approval-queue, bulk-upload, EXIF/presets, analytics),templates/(+rehearsal),localization-workflow/(translation, glossary, locale-formats),versioning/(+branching),taxonomy-curation/(+stewardship),collaboration/(+routing),editorial-lifecycle/lifecycle+recurrence.@oshun/customer-curationconsumed:sharing/sharing(share-policy-store). Zero consumers:collections/(ordering, smart-collections, templates, import-from-another),annotations/,share-cards/,version-awareness/. (bookmarks/non-use is a documented deliberate deviation inlibrary/saved-items-store.ts:9-16— NOT a finding.)
Spec promises with no runtime today as a result: smart collections (1771), drag-and-drop ordering/nesting/import (1771-1772), customer version awareness "show diff" (1784-1785), embeddable share cards with provenance (1776-1778), per-artifact versioning/diff/rollback (1734-1746), translator workspace (1715-1733), taxonomy curation (1694-1714), template gallery (1793-1806), creator roles/queues (1601-1615).
Fix sketch: this is the wiring backlog the 06-10 audit named ("orphaned libraries are mostly excellent code — the work is wiring"); none of the 57 tasks touched it, and no recorded V1.x deferral covers these sections. Needs an explicit build-or-defer decision per family.
Reading progress is a write-less field#
Severity: P2
Evidence: nisaba/state.ts:891 serves progressPercent: 0 (honest-zero per D6)
and lastReadAt: ''; no route or store method ever updates them
(grep -i progress routes/nisaba.ts → serialization only, :2131).
Spec promise: features.md 1782-1783 (reading progress, time-to-finish, resume across devices).
What the code actually does: honest, but the field can never change — progress UI downstream will show 0% forever.
Fix sketch: add a POST /v1/nisaba/passages/:id/progress (or fold into the
client-state E6 sync) updating the per-user record.
Collections page copy promises interactions that don't exist#
Severity: UX
Evidence: apps/oshun/web/src/app/library/collections/page.tsx:79-81 — "Drag
and drop, reorder, share or keep private". No drag-and-drop or manual reorder
exists anywhere (the customer-curation/collections/ordering.ts engine is
orphaned; the dashboard only toggles membership,
LibraryDashboard.tsx:893-903); "share or keep private" routes through the
broken journeys in findings #5/#6.
Spec promise: features.md 1771.
Fix sketch: soften the copy until ordering ships, or wire the orphaned ordering engine into the dashboard's collection item list.
Collections deep-links drop the selected collection#
Severity: UX
Evidence: app/library/collections/page.tsx:101 and
app/library/collections/new/LibraryNewCollectionForm.tsx:112 both link to
/library?collectionId=…, but /library's page reads only domain/kind
(app/library/page.tsx:37-44) and passes no initialCollectionId; the working
route is /library/collection/[collectionId]
(app/library/collection/[collectionId]/page.tsx:35). Compounded by finding #2:
even the correct route can't resolve a BFF collection id, because the
dashboard's collection list comes from localStorage/fixtures
(resolveInitialCollectionId over defaultCollectionSnapshot.collections,
LibraryDashboard.tsx:588-590).
Spec promise: features.md 1768-1772.
Fix sketch: point both links at /library/collection/<id>; real resolution
requires finding #2's unification.
Telegram /save captures are write-only — no surface ever shows them#
Severity: UX
Evidence: B12 wired real capture writes (telegram/user-state-store.ts) and a
self-scoped GET /telegram/captures, but a repo-wide grep finds no
web/mobile/miniapp reader of that route; captures never appear in the library,
notebooks, or search (search/user-object-candidates.ts reads saved
items/collections/notebooks/habits only).
Spec promise: features.md 1768 (notebooks/collections "across all domains") + the messaging capture journey.
Fix sketch: either surface captures in the library (a telegram source lane) or
feed them into the saved-items store instead of a parallel silo.
Annotation system exists only in Nisaba#
Severity: UX
Evidence: per-user annotations live solely in nisaba/state.ts and the
/v1/nisaba/annotations routes; Veritas stories, Metis lessons, and Tara
transcripts have no annotation write path (no annotation routes outside
nisaba.ts; the customer-curation/annotations cross-artifact engine is
orphaned).
Spec promise: features.md 1779-1781 ("Annotation system across reading surfaces (Nisaba editions, Veritas stories, Metis lessons, Tara transcripts) … cross-artifact backlinks, and export").
Fix sketch: lift the nisaba annotation store to a shared per-user store keyed by
(surface, artifactId) (the orphaned lib engine already models this), then
mount on Veritas/Metis/Tara readers.
Studio editorial lanes share no artifact identity — authoring can't flow into review, comments, or localization#
Severity: UX
Evidence: /studio/authoring links to review-approval-workflows /
commenting-annotation-system / real-time-collaboration-substrate / asset-preview
/ i18n (app/studio/authoring/page.tsx:20-35), but each console evaluates
caller-pasted JSON against its stateless analyzer (e.g.
StudioReviewApprovalWorkflowsWorkspace.tsx:116 POSTs a hand-typed stages
payload to /v1/admin/studio/approval-workflow); none accepts a
document/artifact id, so the authored document (itself unpersisted) cannot enter
any of the linked workflows.
Spec promise: features.md 1601-1693 model one pipeline over shared artifacts (author → review → comments → publish → localize).
Fix sketch: once an authored artifact persists (finding #3's fix), thread its id through the lane consoles as the default payload source; until then the cross-links oversell continuity.
Severity counts#
| Severity | Count |
|---|---|
| P0-SEC | 1 |
| P0-HONESTY | 2 |
| P0-STRUCT | 1 |
| P1 | 7 |
| P2 | 2 |
| UX | 5 |
| DEPLOY | 0 |
| Total | 18 |
Adversarial scan notes: the mandatory stub-grep over
libs/oshun/studio-authoring, libs/oshun/customer-curation,
bff/src/library, bff/src/nisaba returned zero actionable hits (both libs are
clean, real domain code — the problem is wiring, not fabrication, except where
listed above). Math.random() scan clean. Known lane-console architecture and
recorded deviations (bookmarks engine non-reuse; BFF fixture merges behind
allowFixtures()) were excluded as designed/documented behavior.