Date: 2026-06-11 · Auditor slice 03 · Static read-only pass (no builds/tests
run). Baseline: all 57 tasks of V1_SPEC_GROUND_TRUTH_AUDIT_2026-06-10.md
landed; this report covers only what is residually broken/dishonest in current
code.
Prior strengths confirmed and NOT re-litigated: the Nyx ephemeris tier is real
(/v1/nyx/tonight, /v1/nyx/sky-almanac, /v1/nyx/events.ics,
/v1/events/upcoming all compute from @oshun/domain-nyx,
apps/oshun/bff/src/routes/domain-stubs.ts:1567–1670); the /veritas hub is
honest (real briefing or honest-unavailable,
apps/oshun/web/src/lib/lilith-data/veritas.ts:29–37); guardedFixtureRoute
fails closed in production (domain-stubs.ts:840–856); the Veritas
retraction-cascade engine is wired end-to-end on the write side
(apps/oshun/bff/src/veritas/retraction-cascade-runtime.ts:48–113, started from
server.ts:520–536). Adversarial grep over the BFF veritas/nyx
route/adapter/store files returned zero stub-comment or Math.random hits.
1. The /v1/oshun/* facade does not exist for Veritas or Nyx — 35 BFF adapter operations are 502-by-construction#
Severity: P0-STRUCT
Evidence:
- BFF expectations —
apps/oshun/bff/src/adapters/domain-service-adapters.ts:1090–1305(veritas) and:1306–1552(nyx). Every method targets/v1/oshun/...onOSHUN_VERITAS_API_BASE_URL/OSHUN_NYX_API_BASE_URL(config/domain-service-discovery.ts:42–56). - Downstream reality —
apps/veritas/apimounts only/api/v1/*(articles/claims/sources/stories/taxonomy/preferences/payments…,src/interfaces/http/routes/v1/index.ts; Postgres-backed, 503 fail-closed, e.g.articles.ts:95–98, 276–279).apps/nyx/apimounts only/api/v1/{objects,ephemeris,events,satellites}(src/app.ts:111–114).grep -rn "oshun" apps/{veritas,nyx}/api/srcfinds only@oshun/databaseimports — no/v1/oshunroute exists in either service. Compare:apps/arete/api/src/routes/oshun-facade.tsandapps/tara/api/src/oshun-facade/exist (C1). - Default adapters are the real HTTP ones
(
apps/oshun/bff/src/app.ts:1136–1149); every non-existent path → 404 →DomainServiceAdapterError→ the routeguarded()wrappers reply 502 (routes/veritas.ts:227–239,routes/nyx.ts:263–275).
Spec promise: features.md 659–684 (Veritas hub/briefing, save/follow, evidence inspection), 985–1007 + 1011–1032 (Nyx event model, save/follow/remind, highlights).
What the code actually does (blast radius, all 502/empty in any real deployment):
/v1/veritas/adapter/{availability,home-cards,continue-items,search,launch,saved-articles,trending-topics,bridge-contexts}(routes/veritas.ts:302–501) and/v1/veritas/briefing/home(:527–530) → the/veritashub renders permanent honest-unavailable./v1/nyx/adapter/{availability,home-cards,continue-items,search,launch,nightly-highlights,object-search,saved-objects,event-reminders,observation-logs,bridge-moments}(routes/nyx.ts:314–655) and/v1/nyx/perspective/home(:782–818, 503 every time).- Assistant tool surface: 23 veritas/nyx ops (
routes/assistant.ts:70–108); wearable (7), desktop (7); achievements — every Veritas and Nyx criterion metric degrades to 0 forever (routes/achievements.ts:163–199, honest-degrade but permanently zero); workspace evidence panel (apps/oshun/web/src/components/workspace/data/useWorkspaceLiveData.ts→/v1/veritas/adapter/saved-articles) shows "unavailable". - Misleading availability:
probeDomainHealthprobes/readyz(veritas) //health(nyx) (health/domain-service-health.ts:115–133) — both EXIST downstream and return ok, so/v1/{veritas,nyx}/adapter/availabilityreports the domain available (libs/oshun/domain-veritas/src/canonical-adapter.ts:75–77maps availability fromgetHealth) while every data read 404s.
What a bridge router needs (per the C1 tara/arete pattern):
- Veritas (17 endpoints):
GET briefs/trending,GET articles/trending,GET articles/:id,GET articles/continue,GET articles/unread-count,GET articles/reading-stats,GET articles/engagement-stats,GET claims/top,GET claims/:id,GET topics/trending,GET topics/followed,POST+DELETE topics/:id/follow,GET categories,GET saved,POST+DELETE saved/:articleId. Catalog legs can map straight onto the existing Postgres routes (/api/v1/articles/trending,/api/v1/claims,/api/v1/categories,/api/v1/topics,/api/v1/sources/:idfor credibility fields). Per-user legs (saved/followed/continue/unread/reading/engagement) have NO downstream store — needs aVeritasOshunStore(Drizzle/Postgres write-through like Tara's shadow-user bridge or Arete's redis facade store). - Nyx (18 endpoints):
GET events/nightly,GET events/:id,GET events/continue,GET events/upcoming,GET objects/search,GET objects/:id,GET objects/visible,GET+POST observations,GET observations/stats,GET observations/search-activity,GET saved,POST+DELETE saved/:objectId,GET+POST reminders,POST reminders/:reminderId. Objects/ephemeris legs can use the real@nyx/messier+@nyx/ephemerisservices already inapps/nyx/api. Events must NOT bridge toapps/nyx/api's events-service (see finding 11 — hardcoded 2024 samples); compute from@oshun/domain-nyxlike/v1/nyx/tonightdoes. All per-user legs (observations/saved/reminders/continue/search-activity) have no downstream store — new facade store required. - Both must fail closed in production without
{VERITAS,NYX}_OSHUN_FACADE_TOKENmatchingx-oshun-service-token(BFF already sendsOSHUN_DOMAIN_SERVICE_TOKEN,app.ts:1144–1148), plus contract round-trip tests booting the real routers throughcreateDomainServiceAdapters(the C1 tests caught real drift day one).
Fix sketch: replicate the C1 bridge-router pattern: apps/veritas/api
facade over the existing Postgres catalog + new per-user store; apps/nyx/api
facade over Messier/ephemeris + @oshun/domain-nyx event solver + new per-user
store; token fail-closed; contract tests.
2. /veritas/{claim,counterclaims,mobile,provenance,retraction,source,story,topic} serve fabricated journalism with zero register label — and the REAL hub deep-links into them#
Severity: P0-HONESTY
Evidence:
apps/oshun/web/src/lib/lilith-data/veritas-depth.ts:1–14("fixture today, BFF tomorrow") and e.g.:33–135— invented bylines ("By M. Halvorsen"), invented sources ("Statnett · operator data", "NVE · regulator note"), invented confidence values (0.92), an entire fake Norway-grid investigation. All eight pages import it directly (src/app/veritas/{claim,counterclaims,mobile,provenance,retraction,source,story,topic}/page.tsx).- No label anywhere:
grep -rn -i "preview|illustrative|example" src/app/veritas src/components/lilith/veritas.tsx→ zero hits in 2,126 lines of broadsheet components; nolayout.tsxundersrc/app/veritas/. DomainPreviewBannermounts only inDomainRouteExperience(src/components/DomainRouteExperience.tsx:579), which renders only for/domains/[domainId]— never for top-level/veritas/*.- Live nav into the fabrication: the honest
/veritashub's subnav links "Topic hubs" →/veritas/topic, "Sources" →/veritas/source, "Story updates" →/veritas/story(src/components/lilith/rooms.tsx:578–580), and the real lead's "Read sources" button defaults to/veritas/claim(src/components/lilith/VeritasArticleActions.tsx:28, rendered atrooms.tsx:681). A member reading a REAL grounded article clicks "Read sources" and lands on a FABRICATED claim page about a different, invented story — presented with full editorial authority.
Spec promise: features.md 659–666 (these surfaces are "presentational depth" of the Veritas domain), 678–681 (inspect-source interactions with real evidence bundles), 702–704 (trust display "never numerical scores presented without context" — these pages show invented confidence as if computed).
What the code actually does: the 06-10 headline "Veritas depth fabricates
journalism" is fixed for /domains/* (D1 banner) and for the hub (honest
briefing), but the eight top-level /veritas/* broadsheet pages — the exact
pages that fabricate journalism — are outside the D1 register and reachable in
two clicks from real content.
Fix sketch: either route these pages through the preview register (a
/veritas/* layout mounting DomainPreviewBanner domainId="veritas"), or point
the hub subnav + readSourcesHref at real surfaces (/domains/veritas?path=...
claims workspace) until the depth pages are wired; minimally change
readSourcesHref so a real article never links to invented sources.
3. D1 preview-banner coverage gap: the 30 static /domains/{veritas,nyx}/* tool pages never render the banner#
Severity: P0-STRUCT
Evidence: banner mounts once in DomainRouteExperience.tsx:579, used only
by src/app/domains/[domainId]/page.tsx. The deep tools are separate static
Next routes — 11 veritas
(src/app/domains/veritas/{articles,bias,claims,fact-check,headlines,knowledge-graph,newsletter,nlp,research,story,topics}/page.tsx) +
19 nyx (src/app/domains/nyx/...) —
grep -rln DomainPreviewBanner src/app/domains/ → zero hits; no shared
layout.tsx exists under src/app/domains/{veritas,nyx}/. These pages render
exactly the simulation-data components the registry exists for
(VeritasArticleAnalyzer.tsx, NyxMoonTracker.tsx, NyxDashboardWidgets.tsx,
… — 31 files import *-simulation-data). They are live-linked: NyxRoom subnav
→ /domains/nyx/satellites, /domains/nyx/events?filter=meteor,
/domains/nyx/observation-log (rooms.tsx:815–817) and event rows →
/domains/nyx/events/${id} (rooms.tsx:1030).
Spec promise: features.md 663–666 + 989–992 list these as the power-user
surfaces; the D1 decision (audit doc line 507) promises "every /domains/* deep
surface now carries an explicit Preview register" — the completion note's claim
is false for the static subroutes, which is where the simulation data actually
lives (DOMAIN_PREVIEW_SURFACES itself is correct; the mounting is what
misses).
What the code actually does: /domains/veritas (shell) shows the banner;
/domains/veritas/articles (the actual simulation UI) does not. Same for all
nyx tools.
Fix sketch: add src/app/domains/veritas/layout.tsx and
src/app/domains/nyx/layout.tsx (and tara/arete equivalents, out of slice)
rendering <DomainPreviewBanner domainId=.../> above children — one file per
domain, registry-driven exactly as designed.
4. GET /v1/nyx/observations is unauthenticated and the store is not user-scoped — every member's "private" field log is one global list#
Severity: P0-SEC
Evidence:
- Route:
apps/oshun/bff/src/routes/domain-stubs.ts:1628–1634—app.get('/v1/nyx/observations', ...)with no preHandler at all returnsdomainStubsStore.observations.list(). The POST (:1600–1627) has onlyoriginGuard+csrfGuard— no auth, and never touchesauthContext. - Store:
apps/oshun/bff/src/routes/domain-stubs-store.ts:119–162—NyxObservationStorerecords{observationId, eventId, targetLabel, observedAtIso, conditions, note, ...}with nouserIdfield;list()returns everything (Postgres or in-memory tier alike). Contrast: tara sittings/analytics in the same file readrequest.authContext?.userIdandcollectionsAuthGuard(domain-stubs.ts:1166–1181). - The page feeding it explicitly promises privacy:
src/app/nyx/observation/page.tsx:44rendersright="private"and the docblock says "Nyx anchors it to your observation lifetime".
Spec promise: features.md 1030–1032 (save/remind is per-user) and the Nyx
model generally treats observation logs as personal data
(getObservationLogs({userId}) everywhere else, e.g.
domain-service-adapters.ts:1381–1394).
What the code actually does: any session (or any direct BFF caller — no auth
check) can read every user's observation notes; every write is anonymous and
co-mingled. This is both a privacy leak and a third source of truth for
observations (vs. the downstream /v1/oshun/observations per-user store of
finding 1 and the web's localStorage useNyxStore.observations).
Fix sketch: require auth on GET+POST, add userId to StoredNyxObservation
(+ Postgres column), scope list() per user; when the Nyx facade (finding 1)
lands, fold this store into it so there is one observation log.
5. Nyx save/follow/remind is double-broken: the store users write is in-memory and never delivered; the delivery worker reads a store that doesn't exist#
Severity: P1
Evidence:
- Write path:
PUT /v1/nyx/event-actions/:eventId→nyxEventActionStateStore(routes/nyx.ts:683–756), which is a module-levelMap(nyx/event-action-state-store.ts:83–95) — despite theNyxPersistedEventActionStatenaming, there is no durable-snapshot wiring inserver.ts(grep nyxEventActionState server.ts→ nothing) and no Postgres tier. Every saved event, followed event class, and reminder route (cadence/channels/reminderTime/offsetMinutesBeforePeak) evaporates on restart. - Delivery path: the reminder worker's Nyx producer (
server.ts:650–653) callsapp.domainAdapters.nyx.getEventReminders— the nonexistent downstream/v1/oshun/reminders(finding 1) — andproduceNyxEventRemindersswallows the throw per user ("fail-soft",reminders/nyx-reminder-bridge.ts:39–44). Net effect: no Nyx event reminder can ever be delivered, while the UI happily stores reminder config in the in-memory store the worker never reads. - Two sources of truth on the read side too:
/v1/nyx/adapter/event-reminders(routes/nyx.ts:570–587) serves the downstream (502) store;/v1/nyx/event-actionsserves the local one.
Spec promise: features.md 1030–1032 ("save event, follow event class, set reminder cadence … per-notification-channel routing") and 1027–1029 (calendar/reminder cadence per user preference).
What the code actually does: stores rich reminder config that nothing enforces; delivers from a store that 404s. Classic "stored config never enforced".
Fix sketch: wire nyxEventActionStateStore into the durable snapshot (like
the C16 handoff store), and point produceNyxEventReminders at it (it has
everything: reminderTime, routes, enabled) until the facade lands; when the
facade lands, make the event-actions route write through to it.
6. The real retraction cascade has no customer-facing read path — /v1/veritas/{drift,retractions} are prod-503 fixtures and no surface shows cascade banners#
Severity: P1
Evidence:
- Write side is real:
POST /api/oshun/domains/veritas/sources/:id/retractpersists via contract-persistence and publishes on Redis; the worker re-grounds Sophia answers and re-validates Metis lessons (veritas/retraction-cascade-runtime.ts:69–113). - Read side is a fixture:
/v1/veritas/driftand/v1/veritas/retractionsareguardedFixtureRoutes (domain-stubs.ts:1822–1827) — 503 in production — and they are the ONLY data source of the customer evidence page (src/app/veritas/evidence/page.tsx:66–70). So in production the page always renders "0 drift · 0 retracted" even after a real cascade fires. - No story-page/saved-claim/notebook banner exists:
grep -rn "retraction" apps/oshun/web/src/componentsshows only the depth-fixtureVeritasRetractionletter; nothing reads cascade records.
Spec promise: features.md 889–913 (retraction-cascade UX: story-page banner, per-saved-claim banner, notebook notice, severity gating, "appeal / correct us"), 732–736 (user-visible correction notes + story-update notifications).
What the code actually does: two sources of truth — real cascades in contract persistence, fabricated drift/retraction lists on the (dev-only) customer route; the spec's customer experience is unimplemented.
Fix sketch: replace the two fixture routes with reads over the cascade
persistence (cascade id, trigger source, notice text, scope, timestamps — all
persisted by createContractPersistenceRetractionPort), and surface a banner on
/veritas briefing + saved items when a cascade touches them.
7. The Veritas editorial engines remain runtime-orphaned: source-quality composite, attestor lifecycle, counterclaim balance, topic-hub composer, editorial state machine, contradiction execution#
Severity: P1
Evidence:
libs/oshun/domain-veritas/src/{source-quality/{composite,banding,domain-weights,rescoring,tenant-floor,vector-store},attestor/{credential-verification,conflict,probation,revocation,expiry,disagreement,audit},counterclaim/{surface,reader-preferences,audit},topic-hub/{composer,editorial-gating,refresh,locale-variant},editorial/state-machine,contradiction/{probe,execution,reviewer-routing,tenant-timebox,monitoring}}
— full implementations with domain tests. Runtime consumers of
@oshun/domain-veritas in the BFF: only domain-service-adapters.ts:51
(types), retraction-cascade-runtime.ts:18,
cross-domain-recommendations.ts:14.
grep -rln "computeSourceQualityComposite|bandFromComposite|EditorialStateMachine|composeTopicHub" apps libs
(excluding the lib itself and tests) → only
libs/sophia/verification/src/contradiction/contradiction-loop.ts (the Sophia
half of the loop — itself unconsumed by any app route). No route, worker, or
store operates source scoring, attestation, counterclaim surfacing, topic-hub
gating, or the state machine. The credibilityTier/credibilityScore shown on
the home briefing comes from the (nonexistent) downstream feed
(routes/veritas.ts:588–613), not from the composite.
Spec promise: features.md 749–961 — the entire "Editorial Workflow, Source Verification, and Contradiction Detection" section, whose opening line says the schemas "are inert without the editorial loop that operates them" (751–753).
What the code actually does: the loop is still inert. This is the recorded systemic failure mode (audit doc lines 49–58); for Veritas, only the cascade slice got wired since.
Fix sketch: when the Veritas facade store lands (finding 1), compute
credibilityTier from computeSourceQualityComposite+bandFromComposite over
real source records instead of passing through a downstream field; add an admin
lane route pair (catalog + verb) driving the editorial state machine +
attestation records, following the studio lane-console pattern.
8. USE_MOCK_ADAPTERS=true serves fabricated journalism and sky data as real, with no production guard#
Severity: P1
Evidence: apps/oshun/bff/src/app.ts:1136–1139 —
if (process.env.USE_MOCK_ADAPTERS === 'true') return createMockDomainServiceAdapters();
— no environment check. The mocks fabricate news ("AI breakthroughs in Q3", "New
CRISPR therapy shows 94% efficacy…",
adapters/mock-domain-service-adapters.ts:208–296) and Nyx events/logs, all
flowing through the same routes that label themselves real. Contrast the repo's
own posture: guardedFixtureRoute 503s in production
(domain-stubs.ts:847–856).
Spec promise: features.md 686–707 (trust/confidence display is the product; fabricated verdicts under real chrome is the worst-case failure).
What the code actually does: one env var in a production deployment turns every Veritas/Nyx/tara/arete surface into convincing fabrications with no banner.
Fix sketch: in createDefaultDomainAdapters, refuse mocks when
resolveOshunRuntimeEnvironment() is production/staging (throw at boot),
mirroring the fixture guard.
9. Dead hydration path: hydrateDomainStoresFromBff calls four /v1/domains/*/state endpoints that don't exist, and nothing calls it#
Severity: P2
Evidence: apps/oshun/web/src/lib/stores/domain-stores.ts:606–636 fetches
/v1/domains/{tara,arete,veritas,nyx}/state; no BFF route matches
(grep -rn "domains/veritas/state" apps/oshun/bff/src → nothing; only
/v1/domains/:domainId/bootstrap exists in routes/entitlements.ts:47). The
function itself has zero callers (grep -rn hydrateDomainStoresFromBff src →
definition only). The zustand stores are honest-empty localStorage, so no
fabrication — but lastSyncedAt/syncFromBff imply a sync that can never
happen.
Spec promise: features.md 677 / 1030 (per-user save/follow state — implies server truth, not per-browser).
What the code actually does: dead code advertising a BFF sync;
useVeritasStore.savedClaims/followedTopics and useNyxStore.observations live
only in the browser.
Fix sketch: delete hydrateDomainStoresFromBff (or re-point it at the E6
/v1/client-state/:stateKey channel with new slots), and route store mutations
through the facade once finding 1 lands.
10. Veritas save/follow is three disconnected stores, and the web has no server mutation path at all#
Severity: P2
Evidence: (a) "Save to notebook" on the real lead writes localStorage
webLibraryStore (VeritasArticleActions.tsx:37–55); (b) the workspace
evidence panel reads server-side saved articles via
/v1/veritas/adapter/saved-articles (useWorkspaceLiveData.ts) — a different
store (downstream, currently 502); (c)
useVeritasStore.savedClaims/followedTopics is a third (localStorage zustand).
The BFF exposes no POST route for
saveArticle/unsaveArticle/followTopic/unfollowTopic — routes/veritas.ts is
GET-only; the adapter mutations (domain-service-adapters.ts:1198–1244) are
reachable only through assistant tooling (routes/assistant.ts:82–85).
Spec promise: features.md 677 ("Save/follow controls for stories, claims, and topics"), 734–736 (story-update notifications "per-followed-topic" — impossible when follows never reach the server).
What the code actually does: a member's saves never appear in the workspace
evidence panel or count toward veritas_articles_saved achievements; follows
can never drive notifications.
Fix sketch: add
POST/DELETE /v1/veritas/adapter/saved-articles/:articleId +
topics/:topicId/follow BFF routes over the adapter mutations; make
VeritasArticleActions write through (localStorage as offline cache),
contingent on finding 1.
11. apps/nyx/api events + satellites services are hardcoded 2024 sample data — empty/garbage in 2026#
Severity: P2
Evidence: apps/nyx/api/src/services/events-service.ts:11–13 —
// Sample Events Data — a fixed ASTRONOMICAL_EVENTS array of 2024 dates;
getUpcomingEvents filters eventStart >= now (:262–276), so every query
from 2026-06-11 returns [] forever (honest-empty, but the endpoint can never
serve a real event). satellites-service.ts:11–25 — sample satellites with TLE
epoch 24015.5 (Jan 2024); SGP4 propagation from a 2.5-year-stale ISS TLE
produces meaningless pass predictions through the otherwise-real
@nyx/realtime-satellites tracker. By contrast ephemeris-service.ts (real
@nyx/ephemeris Meeus engine) and objects-service.ts (real @nyx/messier
catalog) are genuine.
Spec promise: features.md 1024–1026 — prediction sources "each with version, license, attribution, freshness"; 1011–1016 event taxonomy.
What the code actually does: the only currently-running Nyx event source
that is real is the BFF's own @oshun/domain-nyx solver. Any facade (finding 1)
bridged onto apps/nyx/api's events-service would serve nothing.
Fix sketch: back /api/v1/events with the @oshun/domain-nyx
lunar-phase/season solver + the source-backed featured meteor/eclipse table the
BFF already uses; for satellites either ingest a live TLE feed
(DEPLOY-credentialed) or fail closed with not_configured instead of
stale-epoch predictions.
12. /v1/veritas/briefing/home and /v1/nyx/adapter/* error envelopes are inconsistent: the briefing handler 500s instead of its own 502/503 contract#
Severity: P2
Evidence: routes/veritas.ts:527–530 —
Promise.all([getTrendingArticles, getTopClaims]) with no try/catch (unlike
every /adapter/* route's guarded()); a downstream
DomainServiceAdapterError propagates to Fastify's generic 500. The web treats
any non-OK as unavailable so no fabrication results, but ops dashboards see 500s
for a routine downstream outage and the route's own 503 leg (:533–539) is
unreachable for the throw case.
Spec promise: n/a (robustness); features.md 668–675 implies a graceful briefing surface.
Fix sketch: wrap the two adapter calls in the same try → 502
domain_unavailable envelope used at routes/veritas.ts:227–239.
13. NyxRoom navigation sends members from the real sky into unlabeled simulations, with one mislabeled target#
Severity: UX
Evidence: rooms.tsx:815–817 — the real ephemeris hub's subnav: "Planets" →
/domains/nyx/satellites (a satellite tracker, not planets —
NyxSatelliteTracker.tsx, simulation-backed); "Meteors" →
/domains/nyx/events?filter=meteor (simulation events); "Observations" →
/domains/nyx/observation-log (simulation log) even though the member's REAL
logging flow lives at /nyx/observation (and their real entries at
GET /v1/nyx/observations). Combined with finding 3 (no banner on those pages),
the register seam is invisible: real tonight-card → one click → fabricated
observation history.
Spec promise: features.md 985–1007 (the hub and deep tools are one coherent domain surface).
Fix sketch: point "Observations" at /nyx/observation; rename "Planets" or
point it at the solar-system/ephemeris view; banner-cover the rest (finding 3).
Severity counts#
| Severity | Count |
|---|---|
| P0-SEC | 1 |
| P0-HONESTY | 1 |
| P0-STRUCT | 2 |
| P1 | 4 |
| P2 | 4 |
| UX | 1 |
| DEPLOY | 0 |
| Total | 13 |
(DEPLOY: nothing new — once finding 1's facades land,
OSHUN_{VERITAS,NYX}_API_BASE_URL + OSHUN_DOMAIN_SERVICE_TOKEN + facade
tokens are the existing deploy-tier requirement; live TLE feed in finding 11
would add one credential.)