# Daedalus Compiler & Theseus Evaluation

V8 (Ariadne — the self-authoring detective universe) mints a mystery in two
halves: a _symbolic skeleton_ that
[Clew, Minos & Palimpsest](./clew-minos-palimpsest-symbolic-core.md) prove is
uniquely solvable, and an _experienced surface_ that
[Anansesem, Loom & the Ori suspects](./anansesem-loom-and-ori-suspects.md)
realize into prose, art, voice, and interrogation. **Daedalus** and **Theseus**
are the last mile: where those two halves become a thing the shipping game can
load, and where the pipeline earns the right to call a case shippable. Daedalus
is the **compiler** — it lowers a verified `MysterySession` plus its resolved
asset manifest into the _exact_ V5 structs the existing cold-cases plugin reads,
then runs the same Python schema gate the human Case Author tool runs. Theseus
is the **automated playtester and eval harness** — an autonomous solver that
plays the _compiled_ case against V5's real data model, an 8-dimension
LLM-as-judge quality panel, a difficulty calibrator, and a regression corpus,
composing into a single all-or-nothing release decision. Between them they
answer V8's version of V2's shipping question — _how do you know a generated
case is good enough to ship?_ — with proofs in game data rather than
attestations. This page is the deep companion to the section hub at
[../V8_ARCHITECTURE.md](../V8_ARCHITECTURE.md), whose §4.7–§4.8 introduce
Daedalus and Theseus.

## What ships, honestly

The compile mapping is **real and deterministic** — `Clue`s, clue logic, and
accusations lower into the `FV5*` structs (`libs/yemaya/case-compiler/src`) and
a v2 `cold_cases_manifest.json` that the **authoritative Python validator**
`V5/tools/cold-cases/validate-cold-cases.py --generated` must accept as a build
gate (`compile.ts:108`). Theseus is equally real: the solver reasons over the
compiled structs and is tested to reach the intended culprit decisively across
20 seeds (`eval.test.ts:56`); G4 in-game solvability is proven to **fail** when
a required edge is dropped (`eval.test.ts:67`); the PCA aggregation is a genuine
power-iteration eigenvector, not a mean (`pca.ts:40`); and a judge error
_propagates_ rather than being swallowed (`eval.test.ts:108`). The seven-gate
platform suite (`libs/v8/case-gates`) and the C2PA-stamping bundle forge
(`libs/v8/case-bundle`) are real and spec-anchored too.

What is a **seam, not a fabrication**: the LLM judge is an injected `JudgeFn`
that _refines_ the deterministic heuristic scores; absent, the heuristic still
grades from real case structure (`judge.ts:152`). The media providers are
injected `CaseMediaGenerator` boundaries — an absent provider yields a
`not-configured` slot, never a fabricated asset (`case-bundle.ts:262`).

Two **honestly labeled divergences from the architecture prose**, because the
doc and the code disagree. First, §4.7 names the app `apps/v8/daedalus`; the
real app is `apps/v8/daedalus-compiler`, a thin **CLI wrapper** — the compile
_core_ was lifted into a library, `@yemaya/case-compiler`, so other libs and
apps can import it without a lib↔app dependency
(`daedalus-compiler/src/index.ts:7`). The "runtime-relay" the same comment
mentions is aspirational; the actual on-demand relay is the separate
`apps/v8/loom-service` (it owns the `server.ts`). Second, §3.4 says deduction
edges set `Kind=Generated`; the **shipping** V5 enum is only
`'Authored' | 'Weak'` (`v5-target.generated.ts:15`), so the compiler maps
confidence to those two real values — its comment says so: "the shipping enum
has no `Generated` value" (`deductions.ts:9`). The code is honest to the engine;
the doc over-specified.

## Daedalus — compiling a verified skeleton into a V5 pack

Daedalus's contract is narrow and strict: turn a `MysterySession` Minos
_already_ proved (plus an optional `AssetManifest`) into a `CompiledCase` — the
`FV5*` structs, the v2 manifest, and a resolved asset bundle
(`compiled-case.ts:27`). `CompiledCase` lives in `@yemaya/case-contracts` so the
compiler (an app) and Theseus (a lib) agree on the shape without depending on
each other.

### The local pipeline: generate → verify → compile

The CLI is the offline pipeline in one command
(`apps/v8/daedalus-compiler/src/cli.ts:55`):

```
daedalus --seed 42 --cell Period --difficulty standard --suspects 4 --out ./out
```

`runCli` builds a `CaseSpec` from the flags (difficulty drives the suspect/clue
bands — `standard` ⇒ 4 suspects, 11 clues, `cli.ts:38`), calls
`generateCaseSync` (Clew), then `verifyCase` (Minos). It prints the Minos
verdict — `g1` uniqueness, `g2` completeness, `g3` fair-play, plus balance — and
**refuses to compile a case that failed verification**, returning exit code 1
(`cli.ts:85`). Only on a green verdict does it compile, writing
`<caseId>.compiled.json` and `.manifest.json` (`cli.ts:108`).

The draft-vs-release distinction is load-bearing. A standalone compile is a
**draft**: Minos's G1–G3 are proven here, but G4–G8 (Theseus, calibrated judge
evidence, safety, canon, human-quality launch evidence) are downstream, so a
bare invocation labels it a draft and **skips the Python build gate** unless
`--release` is passed with every gate asserted green (`cli.ts`). A draft
produces the structs Theseus needs to play _without_ pretending the case cleared
gates it has not been run through. In release mode the CLI runs G4, the
diagnostic judge, G6, and G7, loads exact-artifact calibrated G5 and
human-quality G8 evidence, calls `decideRelease`, and only then re-compiles
through the Python gate. Missing evidence blocks; no downstream gate is inferred
from `--release`.

### The FV5 mapping

`compileCase` (`compile.ts:59`) builds a shared context, then runs four
deterministic mappers:

- **Evidence** (`evidence.ts`): one `FV5MindPalaceEvidenceNode` per clue. The
  fiction timestamp is `t+NN%` from the clue's `introducedAt.percentage` (what
  Theseus later reads to score pacing), `bFalseLead` mirrors `isRedHerring`, and
  `Image` binds to the generated asset when the manifest has one.
- **Deductions** (`deductions.ts`): two pairing kinds form the Mind Palace
  graph. The **culprit chain** pairs the incrimination clues in the order they
  surface (means → opportunity → motive → broken alibi), with confidence
  climbing `0.7 → 0.9` as it converges on the alibi flaw — the `0.95` in the
  `Math.min(0.95, …)` clamp is a never-binding cap, since the formula's realized
  top is `0.7 + 0.2·1 = 0.9` (`deductions.ts:69`); each **herring↔clearance**
  pair links a refuted red herring to its suspect's clearance, at `0.55`. `Kind`
  is `deductionKindFromConfidence` — `≥0.7 ⇒ Authored`, else `Weak`
  (`v5-target.ts:31`) — and a cross-cell pairing sets `bCrossEra`
  (`deductions.ts:47`).
- **Accusations** (`accusations.ts`): the culprit's outcome is
  `Rating: 'Brilliant'`, `OutcomeBranch: 'TruthRevealed'`, carrying the culprit
  chain's edge ids as `RequiredEdgeIds` (`accusations.ts:30`) — the spine
  Theseus G4 walks. Each innocent gets a plausible-wrong outcome: one who _kept_
  a motive is `Doubtful`/`PlausibleConviction` ("a wrongful conviction waiting
  to unravel," the hook Palimpsest's `freed-vengeful` write-back seizes), one
  cleared by alibi is `Wrong`/`Unproven` (`accusations.ts:53`).

### The manifest and the Python build gate

`compilePack` (`pack.ts`) emits the `FV5ColdCasePackDefinition` header and the
v2 `GeneratedColdCaseManifest`. VO counts come from the asset manifest's real
voice-over summary (`summariseVoiceOver`), defaulting to zero when no audio was
generated rather than inventing line counts (`pack.ts:43`). The pack records a
full **generation provenance** block — `seed`, the C2PA root, the asset-manifest
id, the Minos verifier-report id, and the `(seed, model-versions, canon-hash)`
repro key (`pack.ts:89`) — alongside the `releaseGates` map (`pack.ts:110`).

Two gates then bite, in order. The in-process `validateGeneratedManifest`
(`manifest.ts:72`) mirrors the Python validator: ≥3 evidence nodes and ≥2
deduction pairs, no duplicate ids, `sourceCells` containing the lead cell and
`MindPalace`, a non-empty title/brief, a provenance block whose seed matches and
that cites a verifier report and C2PA root — **and that every release gate is
green** (`manifest.ts:99`). A draft tolerates not-yet-green release-gate
problems by filtering them out (`compile.ts:100`); a publish does not. Then, on
a publish only, `runPythonBuildGate` writes the manifest to a temp file and runs
`validate-cold-cases.py --generated`, throwing `CompileError` on a non-zero exit
(`compile.ts:125`). The CLI test proves the path end to end: a `--release` run
of seed 5 exits 0 and `validateGeneratedManifest` of the emitted manifest
returns `[]` (`cli.test.ts:9`). The schema is **v2, backward-compatible** — the
25 authored launch packs (v1) still validate; v2 only _adds_ generation
provenance (`manifest.ts:1`). One more compile-time guard: `resolveBundle`
**throws** if any `FSoftObjectPath` the structs reference is unresolved
(`compile.ts:91`) — a case cannot compile pointing at art that does not exist.

## Theseus — playtesting the compiled case

Theseus (`libs/yemaya/case-eval`, `@yemaya/case-eval`) is the architecture's
named eval lib (§4.8 — here the doc and code agree). Its defining choice: it
reasons over the **compiled** `FV5*` data, _not_ the IR. Minos proved the IR
admits a unique solution; Theseus proves that property _survived the compile_ —
catching a dropped edge, a mis-bound node, or a herring that lost its clearance,
losses an IR-level proof structurally cannot see (`solver.ts:1`).

### The autonomous solver

`solveCompiledCase(compiled, skill)` (`solver.ts:30`) is a deterministic graph
solver. It scores each suspect by the confidence of the deduction edges
converging on them; a herring↔clearance edge **refutes** (subtracts), modeling
"looked guilty, then cleared" (`solver.ts:63`). The `skill ∈ [0,1]` parameter is
a confidence floor — `(1−skill)·0.6` — so a low-skill solver ignores buried
chains and may miss the answer (`solver.ts:39`). It accuses the highest
positive-net suspect and reports `decisive` only when the margin over the
runner-up is ≥ 0.5 (`solver.ts:88`). The full-skill solver is authoritative; an
LLM "detective" is an optional realism layer, not required to prove solvability.

### G4 — in-game solvability

`checkInGameSolvable(compiled)` (`g4.ts:24`) is the end-to-end proof in game
data. It finds the `Brilliant` outcome, then demands four things: the solver
accuses _that_ culprit, the accusation is decisive, **every** `RequiredEdgeIds`
edge exists and is _walkable_ (both endpoints resolve to real evidence nodes,
`g4.ts:54`), and the cited `EvidenceChain` nodes resolve. Any gap fails the gate
with a reason (`g4.ts:75`). The teeth: a test removes one required edge from a
verified case and asserts G4 flips to `false` (`eval.test.ts:67`) — the exact
compile-loss class this gate catches.

### The judge-panel diagnostic and G5 calibration

`scoreHeuristic` (`judge.ts:81`) grades eight rubric dimensions — coherence,
surprise, fairness-feel, pacing, character, prose, voice-fit,
difficulty-accuracy — each from **real structure**, never a random number.
Coherence runs the solver (culprit + decisive ⇒ 0.9); surprise peaks at a ~⅓
red-herring density; pacing is `1 − normalized std-dev` of the evidence staging
gaps; difficulty-accuracy measures how closely the a-priori grade, the empirical
`1 − solveRate`, and the target align (`judge.ts:116`). An injected `JudgeFn`
may then refine any subset of scores, and a judge that throws **propagates**
(fail-loud, `eval.test.ts:108`). `judgeCase` aggregates equal-weight;
`judgePanel` learns weights across a batch via real PCA: `pca1Weights`
mean-centers the score matrix, builds the covariance, and takes its top
eigenvector by power iteration (`pca.ts:40`), sign- and L1-normalized to
non-negative weights summing to 1 (`pca.ts:58`). The spec checks the eigenvector
of a diagonal `[[5,0],[0,1]]` favors axis 0 (`eval.test.ts:123`) — a stub mean
would fail it. The default pass threshold is 0.6 (`judge.ts:164`).

Those heuristic/PCA outputs are diagnostics, not release authority. G5 requires
independently calibrated human-aligned judge evidence bound to the exact
artifact, licensed gold, current approval, bias probes, held-out
champion/challenger evidence, and a clean contamination analysis
(`calibratedG5EvidenceProblems`, `release.ts`). Missing or invalid evidence
fails G5 even when the heuristic score passes.

### Calibration and the regression corpus

`calibrateDifficulty` (`calibration.ts:24`) runs Theseus at five skill levels
(`0.2 … 1.0`) to estimate a solve-rate, then reports the empirical difficulty
`1 − solveRate` and the `drift` from Minos's a-priori grade — a case the
strongest solver cannot crack is mis-graded, a case every weak solver cracks is
easier than its grade claims, and that signal feeds back to the Oracle's player
model. `runRegressionCorpus` (`corpus.ts:26`) anchors the suite: every corpus
case must be G4-solved by Theseus **and** judged above threshold (V5's 25
authored cases are the intended golden fixtures once cooked). Its test forges
six generated cases and asserts all six solve and pass (`eval.test.ts:166`).

## The two gate contracts

One thing to internalize before reading any gate code: **V8 has a seven-gate
platform suite and a distinct eight-gate pipeline `ReleaseDecision`, both
real**. Confusing them makes the code look contradictory when it is not.

| #   | Theseus `ReleaseDecision` (`case-eval/src/release.ts`) | Platform suite (`@oshun/v8-case-gates`, `case-gates/src/index.ts`) |
| --- | ------------------------------------------------------ | ------------------------------------------------------------------ |
| G1  | Minos formal uniqueness                                | fair-play (every solution clue presented before the reveal)        |
| G2  | deductive completeness                                 | solvability (the CSP proved one solution)                          |
| G3  | fair-play + mechanic balance                           | clue-grounding (every cited clue exists)                           |
| G4  | **Theseus** in-game solvable                           | suspect voice-distinctiveness                                      |
| G5  | calibrated human-aligned judge evidence                | misdirection (≥1 herring, ≤ max — fair, not a maze)                |
| G6  | Sekhmet safety (§11.4)                                 | prose quality                                                      |
| G7  | canon consistency (§1.4)                               | content safety                                                     |
| G8  | preregistered human-quality launch evidence            | —                                                                  |

The **`ReleaseDecision`** framing (`release.ts:40`) is ordered by the _pipeline
stage_ that produces each verdict — Minos owns G1–G3, Theseus and calibrated
judge evidence G4–G5, the safety and canon stages G6–G7, and preregistered
human-quality launch evidence G8 — and `decideRelease` publishes only when all
eight pass, attaching a specific blocking reason per failure; its test confirms
a single failing gate (G6) blocks publication (`eval.test.ts:179`). The
**platform-suite** framing (`case-gates/src/index.ts:99`) is ordered by _kind of
check_ and registers seven `GateDefinition`s on the shared
`@oshun/content-release-gates` `ReleaseGateService` via the platform's
`gateFromManifestCheck` / `gateFromEvalScore` / `createGroundingGate` builders —
**composed, not forked**. `evaluateV8Case` returns `cleared` only when all seven
pass, and its spec asserts the exact gate-id list and that each fault blocks its
own gate (`case-gates.spec.ts:30`); like V9's lesson-gates suite it reads a
_pre-stamped_ `uniqueSolutionProven` flag for G2 (`index.ts:60`) rather than
re-solving. There is even a _third_ naming — Minos's internal
`g1Uniqueness/g2Completeness/g3FairPlay` the CLI prints (`cli.ts:82`), which
supplies the `ReleaseDecision`'s G1–G3. The common thread: one false gate
blocks, and the manifest validator enforces the same rule in process — every
release gate green or no compile (`manifest.ts:99`).

The `libs/v8/case-bundle` forge is where the platform suite bites in the
`libs/v8` vertical: `forgeCaseBundle` proves uniqueness _before any generation_
(throwing `CaseNotSolvableError`, `case-bundle.ts:237`), fails loud with no
prose writer, C2PA-stamps each produced asset with a verifiable Ed25519 manifest
(`stampAsset`, `:184`), and runs the assembled case through `evaluateV8Case` — a
quality or fairness failure leaves `blocked: true`. Its spec proves no
prose/media is generated for an unsolvable skeleton and that tampering any
signed field flips verification to false (`case-bundle.spec.ts:147`,`:169`).

```mermaid
flowchart TD
  S[CaseSpec + seed] --> GEN[Clew: generateCaseSync]
  GEN --> MIN{Minos: verifyCase<br/>G1 unique · G2 complete · G3 fair-play}
  MIN -- fail --> X1[exit 1: not compiling]
  MIN -- pass --> DRAFT[Daedalus draft compile<br/>FV5 structs, skipPythonGate]
  DRAFT --> THE[Theseus eval on compiled data]
  THE --> G4{G4 in-game solvable?<br/>solver hits culprit, edges walkable}
  G4 -- no: dropped edge / mis-bound node --> BLK[blocked: no publish]
  G4 -- yes --> G5{G5 calibrated human-aligned<br/>judge evidence valid?}
  G5 -- no --> BLK
  G5 -- yes --> ALL{all eight gates green?<br/>+ G6 safety, G7 canon, G8 human quality}
  ALL -- no --> BLK
  ALL -- yes --> PUB[Daedalus publish compile<br/>validateGeneratedManifest + validate-cold-cases.py]
  PUB -- python gate rejects --> X2[CompileError]
  PUB -- accepted --> PACK[v2 cold_cases_manifest pack<br/>+ provenance + C2PA → cold-cases plugin]
```

## How a case fails eval — edge cases

- **In-game solvability lost.** A compile that drops a required edge, mis-binds
  a node, or strips a herring's clearance makes the solver miss or under-decide:
  G4 returns `pass: false` with the reason and `decideRelease` refuses to
  publish (`g4.ts:75`, `eval.test.ts:67`).
- **G5 calibration absent or invalid.** A passing heuristic/PCA aggregate cannot
  publish without exact-artifact, current, independently calibrated
  human-aligned judge evidence (`calibratedG5EvidenceProblems`, `release.ts`);
  the platform-suite parallels are low voice, low prose, or unsafe content
  (`case-gates.spec.ts:106`).
- **Under-determined skeleton.** Caught _before_ any generation in the bundle
  forge (`CaseNotSolvableError`); in the suite it fails G2 solvability
  (`case-gates.spec.ts:65`).
- **A solution clue withheld, invented, or mis-balanced misdirection.** Fail
  platform-suite G1 fairness (`case-gates.spec.ts:50`), G3 clue-grounding
  (`:71`), or G5 misdirection — zero herrings or past `maxRedHerrings` (`:79`).
- **A non-green release gate at compile.** A draft tolerates it; a publish does
  not — `validateGeneratedManifest` then the Python gate reject the pack
  (`manifest.ts:99`, `compile.ts:108`).
- **Honest absences.** No `python3` ⇒ `skipPythonGate` keeps a draft buildable
  while labeling it a draft; an absent media provider ⇒ a `not-configured` slot,
  not a faked asset; a wired LLM judge that errors ⇒ propagated, not swallowed.

On any of these the case does not ship — nothing is published or cached — and
the failing gate's reason is the signal a repair pass (Clew) or a human reviewer
acts on.

## Related

- [Clew, Minos & Palimpsest — the symbolic core](./clew-minos-palimpsest-symbolic-core.md)
  — produces and proves the verified skeleton Daedalus compiles; Minos's
  IR-level uniqueness proof is what Theseus G4 re-confirms survived the compile.
- [Anansesem, Loom & the Ori suspects](./anansesem-loom-and-ori-suspects.md) —
  the writers' room and asset fabric whose trees and manifest Daedalus folds
  into the V5 pack; the Loom service is the runtime relay that mints and fetches
  cases.
- [Oracle, cross-cutting concerns & deployment](./oracle-director-cross-cutting-and-deployment.md)
  — the case director that consumes Theseus's calibration to grade and surface
  cases, plus the loom-service relay, provenance, budget, and HITL plumbing.
- Hub: [../V8_ARCHITECTURE.md](../V8_ARCHITECTURE.md).
