Disciplines · Audits

Oshun v1 — Audit pass 7: domain-correctness sweep

Passes 1-6 were structural gap-finders: "is there a guard / test / secret / RLS policy here?" — answerable by pattern-matching against known anti-patterns.

8sections7 minread

On this page

Date: 2026-05-28 Owner: platform team Previous passes: v2 · v3 · v4 · v5 · v6

Why this pass exists#

Passes 1-6 were structural gap-finders: "is there a guard / test / secret / RLS policy here?" — answerable by pattern-matching against known anti-patterns. The pass-6 doc claimed the remaining class (domain-business-logic correctness) "needs product-owner driven review." That conflated two things:

  • Spec-conformancedoes the code compute the right answer / enforce the right transitions? This is machine-checkable, by an agent that reconstructs each domain's invariants and checks the implementation against a ground truth (a textbook formula, a known-correct value, a structural invariant). That's this pass.
  • Spec-correctnessis the formula/policy itself right for our business/jurisdiction? This needs domain authority — the true residue.

Three read-only domain-correctness agents swept the highest-blast-radius surfaces (money/ledger, moderation/safety, RAG/retrieval), each reconstructing invariants and hunting for places the implementation produces a wrong answer or allows an illegal transition. 35 confirmed divergences, each with a concrete wrong value or illegal sequence.

In-session fixes (with oracle/property tests)#

Finding Severity Fix Test
Money-5 promotion free_period produced negative finalAmount (platform pays the customer) HIGH svc-payment-orchestrator/.../promotion-service.ts — clamp free_period discount to the charge; defense-in-depth clamp at applyPromotion so finalAmount ≥ 0 for all discount types promotion-service.test.ts: rewrote the test that asserted the buggy 20000, +2 clamp tests (81 pass)
Mod-1 escalated items were a permanent dead-end — escalation target could never resolve them HIGH aphrodite/admin/.../moderation/service.ts — add 'escalated' to the actionable-state guard service.test.ts: 3 tests (escalate→approve, escalate→ban, terminal still rejects)
Mod-7 chat link allowlist used substring includesevil-twitch.tv.phishing.com passed an allowlist of twitch.tv MEDIUM aphrodite/chat/.../content-filter.ts — parse host, exact-or-label-suffix match (same shape as Reg-1 OAuth fix) content-filter.test.ts: 5 tests incl. userinfo-spoof + look-alike (5 pass)

Verified NOT a bug (adversarial verification caught an over-claim)#

  • Money-7 (TWR denominator) — the agent flagged (V_t−V_{t−1}−CF)/(V_{t−1}+CF) as wrong vs …/V_{t−1}. But the code implements the beginning-of-period cash-flow convention (its comment says so), and the agent's "correct" value assumes end-of-period flow. Both are valid TWR conventions; which is right depends on when the flow landed. Changing the divisor would swap one valid convention for another and risk regressions. Left as-is; filed only as a doc/clarity item (the convention should be documented + caller-selectable).

Money / ledger findings (libs/lakshmi, lilith payment services)#

# Sev file:line Invariant violated
M-1 CRITICAL svc-creator-royalty/src/app.ts:167-190,560-566 platform fee + processing fee silently destroyed (recipients get 9701.25 of 10000); totalDistributed += sale.amount overstates payouts
M-2 CRITICAL svc-micro-transaction/src/micro.ts:711-719,599-609 off-chain channel transfer credited at transfer AND re-credited at settlement → receiver nets +2A
M-3 CRITICAL svc-micro-transaction/src/micro.ts:1053-1140 refund cap checks a single refund, not cumulative; refundedAmount overwrite not accumulate → double full-refund
M-4 HIGH lakshmi/retirement/src/rmd-calculator.ts:338-357 RMD-required at 70-71 but no factor <72 → falls back to age-120 factor 2.0 → distributes 50% of balance vs ~3.8%
M-5 HIGH svc-payment-orchestrator/.../promotion-service.ts:452-454 FIXED — free_period negative finalAmount
M-6 HIGH lakshmi/transactions/src/analysis/splits.ts:123-155 mixed fixed+pct+remainder: pct part scaled by sum-of-pct only → "50%" part gets 80% of base, remainder gets 0
M-7 lakshmi/investments/src/performance/returns.ts:119-132 NOT A BUG — valid begin-of-period TWR convention (see above)
M-8 MEDIUM svc-fiat-ramp/src/fiat-ramp.ts:1280-1309 off-ramp quote recomputes fee breakdown on NET payout instead of GROSS → displayed fee ≠ deducted fee, tier thresholds flip
M-9 MEDIUM svc-payment-orchestrator/src/stripe/refund-service.ts:288-322 createRefund skips eligibility/maxRefundAmount; !input.amount conflates amount=0 with omitted → 0-amount refund cancels subscription
M-10 MEDIUM svc-creator-royalty/src/app.ts:495-584 sale processing non-idempotent — duplicate saleId re-pays recipients

Cross-cutting: royalty + fiat-ramp + promotion operate on JS floats with .toFixed(6) rather than integer minor units — cent drift accumulates. Surfaces verified correct: lakshmi debt (avalanche/mortgage amortization), tax brackets, MACRS depreciation, investment rebalancing, settlement orchestration, aphrodite tip split.

Moderation / safety findings (aphrodite, kuanyin, themis)#

# Sev file:line Invariant violated
MD-1 HIGH aphrodite/admin/.../moderation/service.ts:340-346 FIXED — escalated dead-end
MD-2 HIGH aphrodite/admin/.../moderation/service.ts:329-388 no separation-of-duty — same reviewer who flagged/escalated can self-approve; dismiss→approved clears the flag
MD-3 MEDIUM aphrodite/admin/.../moderation/service.ts:359-384 request_edit (stays pending) still writes reviewedAt/reviewedBy → skews review-time metrics
MD-4 LOW aphrodite/admin/.../moderation/service.ts:383 escalateTo reassigns item on non-escalate actions
MD-5 HIGH kuanyin/performer-protection/src/boundary-enforcement.ts:3560-3579 progressive enforcement recomputed from ratio each call → non-monotonic; a critical offender gets downgraded by a later mild violation
MD-6 LOW kuanyin/performer-protection/src/boundary-enforcement.ts:3598-3599 progressive recordId reconstruction can collide across viewer/performer pairs; re-scan double-counts violations
MD-7 MEDIUM aphrodite/chat/.../content-filter.ts:179-188 FIXED — substring allowlist bypass
MD-8 LOW aphrodite/chat/.../content-filter.ts:253-267 duplicate-message check off-by-one vs configured max (debatable semantics)

Surfaces verified correct: themis academic-integrity appeals (SoD properly enforced), classifier policy thresholds, originality-shield fail-closed cascade, kuanyin samma-vaca sequential gates.

RAG / retrieval findings (iris/knowledge, sophia, conversation-rag, svc-ai)#

# Sev file:line Correctness property violated
R-1 HIGH iris/knowledge/rag/src/response-generator.ts:469-584 grounding uses word-overlap (Jaccard) as entailment proxy → negated/contradicted claims marked "grounded"
R-2 HIGH iris/knowledge/rag/src/response-generator.ts:469 + grounding/.../hallucination-detector.ts:813-819 Jaccard penalizes long chunks → genuinely supported short claims marked ungrounded
R-3 CRITICAL iris/conversation-rag/src/source-tracker.ts:414-516 (real path) citations attached by source-TITLE keyword, ignoring content → [n] points to wrong source
R-4 HIGH iris/knowledge/grounding/src/source-citation.ts:386-415 cosine ≥0.9 ⇒ direct support; substring ⇒ strength 1.0 — paraphrase/contradiction labeled direct
R-6 HIGH iris/knowledge/rag/src/reranker.ts:147-151 assessQuality re-sorts cross-encoder results by bag-of-words overlap, destroying the rerank
R-7 MEDIUM iris/conversation-rag/src/relevance-scorer.ts:86-89 (real) boosts applied AFTER sort, array never re-sorted
R-8 HIGH iris/conversation-rag/src/relevance-scorer.ts:80-83 (real) softmax/minmax normalize THEN minScore filter → all chunks filtered (zero context)
R-9 CRITICAL iris/knowledge/retrieval/src/hybrid-retriever.ts:611-628 cross-encoder scores[i] indexed by unfiltered result index when a doc is missing → scores misaligned to wrong docs
R-10 MEDIUM iris/knowledge/rag/src/retriever.ts:351-518 RRF dedups per-method not per-source-list → multi-query hits double-counted
R-11 CRITICAL iris/conversation-rag/src/rag-pipeline.ts:964-1018 (real default) buildContextFromChunks enforces NO token budget → context overflows model silently
R-12 HIGH svc-ai/src/rag/pipeline.ts:377-408 (real) citations built from ALL docs but context truncated → cites unseen sources; documentsUsed overcounts
R-13 MEDIUM iris/knowledge/rag/src/context-builder.ts:417-440 strategic_ordering + budget truncation drops 2nd-best before worst; position labels use pre-truncation length
R-14 HIGH iris/knowledge/chunking/src/semantic-chunker.ts:544-551 overlap copies chunk PREFIX not boundary suffix → no real overlap + corrupt offsets
R-15 MEDIUM svc-ai/src/rag/pipeline.ts:610-618 paragraph chunking no maxChunkSize → chunk can exceed embedding model max seq length
R-16 MEDIUM iris/knowledge/retrieval/src/dense-retriever.ts:476 HNSW early-stop compares against unsorted candidates tail → prunes search early, lowers recall
R-17 LOW svc-ai/src/rag/pipeline.ts:638-652 empty/whitespace chunks not filtered before indexing in the svc path

Surfaces verified correct: temporal-relevance-scorer, freshness-tracker staleness, retrieval-lib hybrid fusion (RRF/CombSUM/CombMNZ/Borda dedup by documentId), BM25 IDF/TF.

Tracking#

CRITICALs filed as discrete tasks (#300+). HIGH/MEDIUM/LOW are tracked in the tables above with exact file:line + the proposed oracle test from each agent; they feed the per-domain owner backlog. The three in-session fixes ship with this commit.

Is the residue now truly human-only?#

Yes. After this pass, what remains is genuine spec-correctness: is the RMD table the right one for the current tax year, is the moderation escalation policy the one legal wants, is the grounding entailment threshold calibrated for our content. Those are product/legal/domain-owner sign-offs, plus curating the labeled eval sets that turn R-1/R-2/R-3 grounding fixes into regression fixtures. The machine-findable conformance bugs — wrong formulas, illegal transitions, misaligned scores, missing budgets — are now enumerated.