# Architectural Thesis & the Generation Pipeline

V8 (codename **Ariadne**) is the version that turns V5's _authored_ open-world
detective game into a _self-authoring_ one: a player or director asks for a
case, and the system mints a fair, solvable, fully-realized mystery — ground
truth, clue logic, prose, suspects, evidence, voice — with **no human authoring
per case**. The single architectural decision that makes that safe is a hard
separation between two layers, captured in one sentence the product repeats
everywhere: **the LLM proposes; a constraint solver disposes.** A mystery has a
_symbolic skeleton_ (who did it, the means/motive/opportunity, the clue logic,
and a machine-proved unique solution) and an _experienced surface_ (the prose,
dialogue, art, and voice an audience actually sees). V8 keeps them strictly
apart. The skeleton is authoritative and machine-checked; the surface is
generated from it, graded, and regenerated on failure. Nothing unsolvable,
unfair, unsafe, or canon-breaking ever reaches a player, because the part that
_could_ hallucinate is never the part that decides whether the case is solvable.

That choice is what separates V8 from the freestyle-LLM failure mode — the
rambling whodunit that contradicts itself, hides the murderer behind a fact it
never showed you, or "solves" by authorial fiat. V8 is a **small product** by
surface area, but the part that has to be _provably correct_ is real code, not a
prompt: a genuine finite-domain constraint solver proves uniqueness, the
seven-gate platform suite and eight-gate pipeline release decision block
failures in their respective tracks, and every generated asset is
content-credentialed. This page is the orientation companion to the V8
architecture set — the thesis, and the multi-stage pipeline that realizes it.
The section hub is [../V8_ARCHITECTURE.md](../V8_ARCHITECTURE.md).

## What ships, honestly

V8's `V8_TODOS.md` tracks the build at 87 of 90 tasks done (3 open, no
partials), and the code backs a substantial, _tested_ core. Because the monolith
architecture doc (`V8_ARCHITECTURE.md`) describes an ambitious eleven-stage,
nine-subsystem universe that reaches all the way into the running UE5 game, this
section separates what is real today from what is planned or provider-gated.
Honest "planned/gated" beats fake "shipped." There are, in fact, **two real code
tracks** in the repo, both expressing the solve-first thesis at different
scales.

**The focused symbolic core (real and tested today) — `libs/v8/*`.** Three
self-contained packages under `@oshun/v8-*` are the thesis in miniature.
`case-csp` (~1,100 lines incl. tests) is a genuine CSP solver — backtracking
search with node consistency, minimum-remaining-values ordering, and forward
checking (`solveCsp`, `libs/v8/case-csp/src/csp-solver.ts:212`) — that
_enumerates_ solutions rather than returning a boolean, and a
`proveUniqueSolution` (`csp-solver.ts:356`) that probes for a second model to
decide `unique | under-determined | unsatisfiable`. Its spec anchors correctness
against the canonical Zebra (Einstein) puzzle — "Norwegian drinks water, the
Japanese owns the zebra" (`csp-solver.spec.ts:168`) — so the engine is verified
against a known result, not just its own data. `case-bundle`'s `forgeCaseBundle`
(`libs/v8/case-bundle/src/case-bundle.ts:236`) proves solvability _first_,
generates prose and media through injected boundaries, and C2PA-stamps every
produced asset with the shared `@oshun/content-signing` Ed25519 signer.
`case-gates` registers a seven-gate suite over the shared
`@oshun/content-release-gates` service — composed, not forked
(`libs/v8/case-gates/src/index.ts:99`). This track is internally coherent
(bundle → csp + gates) and self-contained; notably, it is _not_ wired into the
runtime apps.

**The eleven-stage pipeline (real, and end-to-end tested offline) — `apps/v8/*`
over `libs/yemaya/case-*`.** The runtime apps wire a _separate_, larger set of
eleven subsystem libraries (~13k lines across `case-contracts`, `case-engine`,
`case-verifier`, `case-writers-room`, `case-assets`, `case-suspects`,
`case-eval`, `case-pipeline`, `case-director`, `case-compiler`,
`case-localization`). The Loom service's `runPipeline`
(`apps/v8/loom-service/src/pipeline.ts:127`) runs every stage from a single
`CaseSpec` — and the §12.4 acceptance test (`pipeline.test.ts:28`) **mints a
case and publishes it offline, deterministically, with zero provider credentials
and all eight `ReleaseDecision` gates green**, then runs the real
`validate-cold-cases.py` build gate. So "the pipeline" is not a slide: the
offline mint→publish path is genuinely executable and regression-tested.

**Provider-gated injected seams (fail-loud, not faked).** The parts that need
real models or money are typed boundaries the pipeline _injects_ and runs
degraded without: the LLM proposer/flavor (`CreativeProposer`) and prose
enrichment (`CompletionFn`), live media providers
(Stability/Flux/Hunyuan3D/Meshy/Suno/ ElevenLabs via Isis), ACE/Inworld runtime
interrogation, and the `clingo` ASP binary. None of these fabricate when absent.
A missing prose writer fails loud (`CaseBundleNotConfiguredError`,
`case-bundle.ts:115`); an absent media provider yields an honest
`not-configured` asset slot, never a fake asset (`case-bundle.ts:260`); the
Minos sidecar reports whether `clingo` is present and falls back to the
in-process DPLL solver for the _same verdict_
(`apps/v8/minos-asp-sidecar/src/server.ts:26`); and the offline pipeline deps
run "the deterministic Clew core, the grounded surface, a text-only degraded
asset pack" with no keys at all (`apps/v8/loom-service/src/deps.ts:44`).

**Spec-described / external-gated.** The UE5 client plugin
(`V8/ue/Plugins/V8_Ariadne_CaseClient`), live in-engine runtime interrogation,
the on-demand runtime minting path, externally-authored case fixtures, and the
full live Palimpsest canon store (Postgres+pgvector+Neo4j) are described in the
monolith but are the remainder — they are not what the offline acceptance run
exercises.

**A real naming divergence worth knowing.** V8 has _two distinct gate
contracts_. The `libs/v8/case-gates` platform suite numbers seven checks **G1
fairness, G2 solvability, G3 clue-grounding, G4 voice, G5 misdirection, G6
prose, G7 safety** (`libs/v8/case-gates/src/index.ts:1`). The pipeline's
authoritative `ReleaseDecision` numbers eight checks **G1 formal uniqueness, G2
deductive completeness, G3 Knox/Van-Dine fair-play, G4 Theseus in-game solve, G5
human-calibrated judge evidence, G6 Sekhmet safety, G7 canon-consistency, G8
preregistered human-quality launch evidence**
(`libs/yemaya/case-eval/src/release.ts:1`). Both are real and fail closed, but
they are not interchangeable. When this page says "the gates," it means the
contract named by the surrounding code.

## The solve-first thesis

### Skeleton vs. surface

The skeleton is a `MysterySkeleton`
(`libs/v8/case-csp/src/mystery-skeleton.ts:69`): a set of solution _dimensions_
(culprit, weapon, location, motive, time…) each with a finite domain, an
intended `groundTruth` assignment, and a list of clues — each clue marked
`presented` or `withheld` and carrying a logical constraint over the dimensions.
The skeleton is **never shown to the player**; the surface (prose, interrogation
trees, art, VO) is realized from it afterward. This is what lets V8 gate a case
once on its truth and re-skin the prose freely over the same proven logic — the
same skeleton/surface split V9 encodes as `skeletonHash` vs. `surfaceHash`, here
applied to mysteries instead of lessons. The canonical data contracts that make
the split typed and parallelizable — `CaseSpec`, `CaseGroundTruth`, and the
`MysterySession` IR — have their own page:
[./canonical-data-contracts.md](./canonical-data-contracts.md).

### The solver that disposes

The "disposes" half is the real engine. A clue's logical content compiles to a
first-class CSP constraint — `is`/`is-not`/`one-of`/`if-then`/`same`, plus an
`allowed`-tuples escape hatch for arbitrary fair deductions
(`compileConstraint`, `mystery-skeleton.ts:85`). The solver's
`constraintViolated` uses **definite-violation semantics**
(`csp-solver.ts:120`): a constraint counts as violated only when no completion
of the unassigned variables can satisfy it, which is exactly what keeps
backtracking both sound and complete. Search seeds node consistency from the
unary constraints, picks the most-constrained variable next (MRV), and
forward-checks — pruning each unassigned neighbour's domain after every
assignment and undoing the pruning on backtrack (`forwardCheck`,
`csp-solver.ts:278`). The engine is domain-agnostic: the same code solves a
murder, a logic grid, or the Zebra puzzle. The spec proves the small cases that
matter to a mystery directly — four suspects `[ada, ben, cara, dan]`, three
eliminated, leaves exactly `cara` (`unique`); two eliminated leaves two
survivors (`under-determined`); a suspect both required and forbidden is
`unsatisfiable` (`csp-solver.spec.ts:89`).

### Proving a case fair _and_ solvable

The gate the whole thesis stands on is `proveCaseUniqueness`
(`mystery-skeleton.ts:239`). It compiles **only the player-visible (presented)
clues** into a CSP (`compileMysteryToCsp` defaults to `presented`,
`mystery-skeleton.ts:136`) and demands two things at once: the visible clues
must admit _exactly one_ solution, **and** that solution must equal the intended
ground truth. An under-determined case (multiple suspects survive), an
over-constrained one (no suspect survives), _or_ a case whose visible clues
prove a _different_ culprit than the author intended are all rejected — never
fabricated as solved. The spec exercises every one of those rejections,
including the subtle one: "a case solvable only with withheld clues is
under-determined for the player" (`mystery-skeleton.spec.ts:178`), which is
precisely the cheat fair-play exists to forbid. Malformed skeletons fail loud
before any "proof" (`assertGroundTruthInDomain`, `mystery-skeleton.ts:156`), and
a separate `groundTruthSatisfiesAllClues` (`:180`) surfaces a generator bug
where the case's own answer contradicts a clue it ships. The boolean
`uniqueSolutionProven` (`mystery-skeleton.ts:213`) is the single flag the
solvability gate consumes. The deeper symbolic-core walkthrough — Clew, Minos,
and the Palimpsest continuity graph — is its own page:
[./clew-minos-palimpsest-symbolic-core.md](./clew-minos-palimpsest-symbolic-core.md).

## The generation pipeline

The Loom service composes the stages end-to-end. The governing rule is
solve-first: the symbolic skeleton is built and _verified_ before any prose
exists, and the surface stages are constrained to realize that skeleton. The
stage names below mirror the Ariadne myth cycle (Clew the case engine, Minos the
verifier, Anansesɛm the writers' room, Loom the asset fabric, Theseus the
playtester, Daedalus the compiler); each maps to a `libs/yemaya/case-*` library
the apps wire.

```mermaid
flowchart TD
    SPEC[CaseSpec + canon snapshot] --> GT[Ground truth + clue derivation<br/>Clew · case-engine]
    GT --> VERIFY{Verify G1-G3<br/>Minos · case-verifier}
    VERIFY -- fail --> REPAIR[Repair: regenerate<br/>only offending clues]
    REPAIR --> VERIFY
    VERIFY -- pass --> NARR[Narrative realize<br/>Anansesem · writers-room]
    NARR --> ASSETS[Asset realize<br/>Loom · injected Isis seam]
    ASSETS --> CIR[Compile-IR → CompiledCase]
    CIR --> EVAL[Eval G4<br/>Theseus solve + diagnostic judge]
    EVAL --> CAL[G5 calibrated human-aligned<br/>judge evidence]
    CAL --> SAFE[Safety G6<br/>Sekhmet seam · fail-loud]
    SAFE --> CANON[Canon-consistency G7]
    CANON --> HUMAN[Human-quality launch evidence G8]
    HUMAN --> DECIDE{Release decision<br/>all eight gates green?}
    DECIDE -- no --> BLOCK[BLOCKED · not published,<br/>reasons journalled]
    DECIDE -- yes --> COMPILE[Compile-game → FV5* pack<br/>validate-cold-cases.py gate]
    COMPILE --> PROV[Provenance + telemetry<br/>seed · model versions · canon hash]
```

`runPipeline` (`pipeline.ts:127`) walks this graph for one `CaseSpec`, threading
a per-case token/asset budget, a `StageJournal` that records every stage in
monotonic order (for audit and replay), and a telemetry aggregator. Clew
generates the ground truth and clues; **Minos verifies G1–G3** before anything
downstream runs (`verifyCase`, `pipeline.ts:145`), running formal uniqueness via
the ASP/DPLL runner, deductive completeness, and the existing Knox/Van-Dine
fair-play checker. Anansesɛm realizes the briefing, scenes, and one
interrogation tree per suspect; Loom either calls the injected asset realizer or
emits a text-only degraded pack (`emptyAssetManifest`, `pipeline.ts:91`).
Theseus supplies the G4 in-game solvability verdict and a heuristic/PCA quality
diagnostic. G5 passes only with independently calibrated human-aligned judge
evidence bound to the exact artifact; the diagnostic cannot satisfy it.

### The eight gates and the release decision

The pipeline's honesty is concentrated in two places. **G6 safety is fail-loud
by construction**: `requireSafetyClear` throws on flagged _or unscanned_
content, and the surrounding `try/catch` leaves `g6 = false` on any throw — so
unscanned content cannot accidentally publish (`pipeline.ts:199`). The **release
decision** then takes the conjunction of all eight gates (`decideRelease`,
`pipeline.ts:212`); only when it returns `publish: true` does the pipeline
re-compile the pack with every gate asserted green and run the non-skippable
Python build gate (`pipeline.ts:222`). A blocked case journals its blocking
reasons and produces no published pack. The §12.4 acceptance test asserts the
positive path end-to-end — all eight `decision.gates` true, one interrogation
tree per target suspect, and a pack whose manifest carries the eight green gates
plus generation provenance (`pipeline.test.ts:28`).

### The forge bundle and provenance

The `libs/v8` track expresses the same flow as a single function for the focused
core. `forgeCaseBundle` (`case-bundle.ts:236`) proves uniqueness first and
throws `CaseNotSolvableError` _before any generation_ if the case is
under-determined — the spec verifies it rejects an under-determined case with
zero generator calls (`case-bundle.spec.ts:169`). Each produced asset is hashed
and Ed25519-signed into a C2PA-style manifest (`stampAsset`,
`case-bundle.ts:184`); tampering any signed field — digest, URI, kind — flips
`verifyCaseAssetManifest` (`case-bundle.ts:215`) to false, which the spec proves
with a deliberately mutated manifest (`case-bundle.spec.ts:147`). The assembled
case is then gated through the same seven-gate suite via the shared
`ReleaseGateService` (`evaluateV8Case`, `case-gates/src/index.ts:183`), so a
quality or fairness miss leaves the bundle `blocked`. Provenance — seed, model
versions, canon-snapshot hash, signer key — is recorded on every run, which is
what makes a case reproducible from
`(seed, model-versions, canon-snapshot-hash)`.

### Minos, the ASP sidecar, and compiling to V5

Minos can offload the uniqueness proof to a `clingo`/ASP container via the
sidecar (`POST /solve`, `handleSolve`,
`apps/v8/minos-asp-sidecar/src/index.ts:29`), which emits the ASP program for
audit and returns whether the `clingo` backend is available — falling back to
the in-process DPLL solver for an identical verdict when no binary is present.
Daedalus closes the loop: its CLI runs generate→verify→compile and, on a green
Minos verdict, emits the case as V5's `FV5*` Mind-Palace structs plus a
`cold_cases_manifest.json` so a generated case drops into the _existing_
shipping game with no rewrite (`runCli`,
`apps/v8/daedalus-compiler/src/cli.ts:55`). A standalone compile is honestly
labelled a draft and skips the all-gates-green Python gate unless `--release` is
passed.

## Determinism, refusals, and where V8 sits

A run is parameterized by a `seed` and a canon snapshot, and the journal records
each stage so a case can be replayed and audited — determinism is both a
provenance lever and a cost lever (a popular case is minted and gated once). The
system is built to _refuse_ rather than fabricate, and the refusals are
specific: an under-determined or wrong-culprit case is rejected by
`proveCaseUniqueness`; an absent prose writer throws
`CaseBundleNotConfiguredError`; an absent media provider yields a
`not-configured` slot; unscanned content keeps G6 red; any gate failure blocks
publish and journals the reason. V8 is a domain product on the shared Oshun
platform — it reuses the platform's content-release-gate service,
content-signing, and quality-judge grounding gate rather than re-implementing
them, which is why its net-new footprint is small; the platform substrate it
builds on is described in
[../../platform/oshun-domain-libraries.html](../../platform/oshun-domain-libraries.html).

## Related

- The section hub: [../V8_ARCHITECTURE.md](../V8_ARCHITECTURE.md), and the
  product framing in [../README.md](../README.md)
- [./canonical-data-contracts.md](./canonical-data-contracts.md) — `CaseSpec`,
  `CaseGroundTruth`, and the `MysterySession` IR that let independent subsystems
  work in parallel
- [./clew-minos-palimpsest-symbolic-core.md](./clew-minos-palimpsest-symbolic-core.md)
  — the symbolic core in depth: generation, the uniqueness/fair-play verifier,
  and the continuity graph
- [../../platform/oshun-domain-libraries.html](../../platform/oshun-domain-libraries.html)
  — the shared platform libraries V8 composes (release gates, content signing,
  quality judge)
