Sheet 03Proof ledger & evaluator registryScale: NTS

Proofs are the unit of account

Daedalus does not ask “is it done”. It asks which of seventeen named proofs a goal requires, which evaluators claim to establish each of them, and which proofs nobody in the registry can currently prove. Everything the system knows about quality is expressed in that vocabulary.

The seventeen proof kinds

Proof is a closed union at types.ts:12–29. It is not extensible at runtime; adding a proof means editing the type, which forces every switch and manifest to be re-checked by the compiler. The names fall into four rough groups, though the source itself imposes no grouping.

Universal gates

  • boots — the artifact starts
  • builds — the build command exits zero
  • tests_pass — the test command exits zero

Interface & appearance

  • user_interaction
  • visual_nontrivial — not a placeholder screen
  • responsive_layout

Cross-cutting

  • performance_budget
  • security_baseline

Gameplay

  • game_controls
  • game_collision
  • game_combat
  • game_win_loss

Domain contracts

  • api_contract
  • auth_behavior
  • cli_golden_output
  • data_invariants
  • citation_quality

“For build me Doom, the evaluator cannot be an LLM saying ‘looks good.’ It must run the artifact and prove behavior.”

RESEARCH.md, lines 213–215

Four of the gameplay proofs exist because of that sentence. The proof vocabulary is deliberately weighted toward things a program can observe from outside the artifact — pixels, exit codes, HTTP responses, frame timings — rather than toward things an agent can assert about its own work.

Fig. 3.1 Evaluator / proof coverage plan

Coverage plan mapping the seven registry evaluators onto the seventeen proof kinds Seven evaluator blocks on the left — shell-core, playwright-web, browser-game, api-contract, cli-golden, data-invariants and research-sources — each connected by lines to the proofs they declare in their proves array, listed down the right. shell-core proves boots, builds and tests_pass and applies to eight of the nine domains. playwright-web proves boots, user_interaction, visual_nontrivial and responsive_layout. browser-game proves the four gameplay proofs plus performance_budget. api-contract proves api_contract, auth_behavior and security_baseline. cli-golden proves cli_golden_output. data-invariants proves data_invariants and performance_budget. research-sources proves citation_quality. Every proof has at least one claiming evaluator, but security_baseline is claimed only by api-contract, which is restricted to the api domain. EVALUATOR MANIFESTS — evaluatorRegistry.ts:4–110 PROOF KINDS — types.ts:12–29 boots builds tests_pass user_interaction visual_nontrivial responsive_layout game_controls game_collision game_combat game_win_loss performance_budget api_contract auth_behavior cli_golden_output data_invariants citation_quality security_baseline shell-core 8 domains · weight 0.20 no command declared playwright-web web_app, browser_game · 0.25 2 hidden · 2 mutation tests browser-game browser_game only · 0.35 3 hidden · 3 mutation tests api-contract api only · 0.30 2 hidden · 2 mutation tests cli-golden cli only · 0.30 2 hidden · 1 mutation test data-invariants data_ml only · 0.30 1 hidden · 2 mutation tests research-sources research only · 0.30 1 hidden · 1 mutation test 1 security_baseline is proved only by api-contract, whose domains array is ["api"]. infra also requires it by default, so an infra goal always reports it uncovered. Each line is one entry in an evaluator's proves array. A proof is covered if any selected evaluator lists it.
Fig. 3.1 — Coverage is claim-based, not measured. An evaluator’s proves array is a declaration in a TypeScript literal; nothing verifies that a tool capable of establishing game_collision exists on the machine. Note also that only one of the seven manifests would ever overlap another on a real goal: performance_budget is claimed by both browser-game and data-invariants, which never co-occur because no domain selects both.

The registry in full

Each manifest is a plain object literal. Six fields are required by EvaluatorManifest (types.ts:55–70); the rest — publicTests, hiddenTests, mutationTests, artifacts, antiCheatChecks, scoreWeight, command — are optional annotations.

evaluatorRegistry — evaluatorRegistry.ts:4–110
id Domains Proves Runtime requirements scoreWeight
shell-core all except research boots, builds, tests_pass package scripts or explicit commands 0.20
playwright-web web_app, browser_game boots, user_interaction, visual_nontrivial, responsive_layout local web server, Playwright 0.25
browser-game browser_game game_controls, game_collision, game_combat, game_win_loss, performance_budget browser game, input driver, test telemetry or black-box probes 0.35
api-contract api api_contract, auth_behavior, security_baseline local API server, contract definition or generated schema 0.30
cli-golden cli cli_golden_output CLI command, fixture inputs 0.30
data-invariants data_ml data_invariants, performance_budget fixture dataset, metric command 0.30
research-sources research citation_quality source list, claim mapping 0.30

The scoreWeight column is inert. It appears seven times in evaluatorRegistry.ts and once as an optional field at types.ts:69, and is read nowhere. scoring.ts weights every required proof identically. Likewise command is declared optional on EvaluatorManifest and is not set on any of the seven manifests — there is no string in the registry that could be executed.

What the manifests do carry, and carry seriously, is anti-cheat intent. Every non-trivial evaluator declares mutation tests that must fail on a deliberately broken artifact. The browser-game manifest is the most explicit:

packages/core/src/evaluatorRegistry.ts · lines 41–52verbatim
41    mutationTests: [
42      "disabled movement must fail",
43      "disabled collision must fail",
44      "fake telemetry without pixel evidence must fail"
45    ],
46    artifacts: ["screenshots", "video", "frame stats", "gameplay transcript"],
47    antiCheatChecks: [
48      "cross-check telemetry against canvas pixels",
49      "store random seed",
50      "reject animated noise as gameplay"
51    ],
52    scoreWeight: 0.35

“Reject animated noise as gameplay” is the kind of rule that only gets written by someone who has watched an agent produce a canvas full of moving pixels and call it Doom. These strings are instructions to a human or agent writing the evaluator, not assertions the registry can enforce.

Selection is two array filters

packages/core/src/evaluatorRegistry.ts · lines 112–124verbatim, wrapped
112export function selectEvaluators(domain: Domain, requiredProofs: Proof[]): EvalSelection {
113  const candidates = evaluatorRegistry.filter((e) =>
113      e.domains.includes(domain) || e.domains.includes("unknown"));
114  const selected = candidates.filter((e) =>
114      e.proves.some((proof) => requiredProofs.includes(proof)));
115  const covered = new Set(selected.flatMap((e) => e.proves));
116  const uncoveredProofs = requiredProofs.filter((proof) => !covered.has(proof));
117
118  return { domain, requiredProofs, evaluators: selected, uncoveredProofs };
124}

Line 113 treats "unknown" in an evaluator’s domains array as a wildcard, not as the literal unknown domain. That is why shell-core — which lists eight domains including "unknown" — is a candidate for every goal. It is a useful behaviour, but the type does not distinguish “applies to unclassified goals” from “applies everywhere”, so an evaluator author cannot express one without the other.

Line 115 also has a quiet consequence. covered is built from the full proves array of every selected evaluator, not from the intersection with requiredProofs. Selecting api-contract for its api_contract proof therefore also marks security_baseline covered, whether or not the goal asked for it. Coverage is transitive through selection.

Selection outcome for every domain’s default proof set

selectEvaluators(domain, defaultProofsForDomain(domain))
DomainSelected evaluatorsUncovered proofs
browser_gameshell-core, playwright-web, browser-gamenone
web_appshell-core, playwright-webnone
apishell-core, api-contractnone
clishell-core, cli-goldennone
data_mlshell-core, data-invariantsnone
researchresearch-sourcesnone
libraryshell-corenone
unknownshell-corenone
infrashell-coresecurity_baseline

Eight of nine domains are fully covered by construction, which is unsurprising: the default proof lists and the evaluator manifests were written together. infra is the exception and the only place the missing-evaluator path fires without a hand-edited contract.

Fig. 3.2 Missing-evaluator path — elevation

The path from an uncovered proof to a frozen evaluator, with implemented and unimplemented stages distinguished An uncovered proof enters proposeEvaluator, which branches three ways on domain: a browser_game branch suggesting Playwright, gameplay bot and canvas check files; an api branch suggesting contract test and fixture files; and a generic fallback branch. Each branch emits an EvaluatorProposal carrying a purpose, suggested files, a validation plan and anti-cheat checks, with status fixed to the literal string proposal. Downstream of the proposal the diagram shows four stages drawn as ghosted outlines — eval-writer implementation, mutation and breakage validation, eval-critic approval recorded as an EvalValidationRecord, and promotion into the registry — none of which have code in the repository. Only the final freeze step is implemented, and it snapshots the evaluators already selected rather than any newly written one. uncoveredProofs[] e.g. "security_baseline" from EvalSelection proposeEvaluator (domain, proof) registry.ts:126–192 3 branches on domain domain === "browser_game" 3 suggested files · 4 anti-cheat checks domain === "api" 2 suggested files · 3 anti-cheat checks fallback (7 other domains) 2 suggested files · 3 anti-cheat checks EvaluatorProposal id eval_proposal_<8> proof · domain · purpose suggestedFiles[] validationPlan[] antiCheatChecks[] IMPLEMENTED — pure function, returns an object status is the literal "proposal" and never changes DOWNSTREAM OF THE PROPOSAL — docs/ARCHITECTURE.md:88–96 eval-writer implements the files named in the proposal mutation check break behaviour, confirm the eval fails eval-critic approval EvalValidationRecord types.ts:149–159 registry entry new EvaluatorManifest joins the array freezeEvals daedalus.ts:67 IMPLEMENTED 1 1 · EvalValidationRecord has five boolean gates — mutationFailurePassed, hiddenSeedSplit, antiHardcodeCheck, artifactEvidenceCaptured, approved — and a notes array. RunState.evalValidationRecords is initialised to [] at daedalus.ts:52 and no code path ever pushes to it. There is no MCP tool and no CLI command that writes one. 2 2 · freezeEvals() snapshots run.evalSelection.evaluators — the evaluators chosen at createRun time. A newly written evaluator cannot enter that set, because the set is derived from the static registry array and is never recomputed after the run is created. SOLID = code exists · HATCHED = described in docs and types, no implementation
Fig. 3.2 — The proposal generator is real and produces a usable work order. Everything between the proposal and a trustworthy frozen evaluator is currently a human or agent process described in docs/ARCHITECTURE.md and enforced only by convention.

Asking for the gap directly

The missing-evals command compiles a goal and returns a proposal for each proof no selected evaluator claims. Because infra is the one domain with a real gap, it is the natural demonstration.

apps/cli/src/index.ts:82–89 — missing-evals <goal...>
$ daedalus missing-evals "deploy a kubernetes cluster with terraform"
# classifyDomain -> "infra"   (keywords: kubernetes, terraform)
# defaultProofsForDomain("infra") -> builds, tests_pass, security_baseline
# selectEvaluators -> [shell-core]   uncoveredProofs -> [security_baseline]

[
  {
    "id": "eval_proposal_IkuDsIk1",
    "proof": "security_baseline",
    "domain": "infra",
    "status": "proposal",
    "purpose": "Create a reproducible evaluator for security_baseline.",
    "suggestedFiles": [
      "evals/custom/evaluator.test.ts",
      "evals/custom/fixtures/"
    ],
    "validationPlan": [
      "define observable evidence for the proof",
      "write a passing check for a correct artifact",
      "mutate or mock a broken artifact",
      "confirm the evaluator fails on the broken artifact"
    ],
    "antiCheatChecks": [
      "avoid checking only static text",
      "prefer artifact behavior over agent trace claims",
      "add hidden randomized cases where possible"
    ]
  }
]

Field values transcribed from the fallback branch at evaluatorRegistry.ts:176–191; the identifier suffix is nanoid(8) and varies per invocation.

Note what the fallback branch does not know. It has no idea that a security baseline for Terraform means Checkov or tfsec, or that RESEARCH.md:241 already lists Semgrep, gitleaks, OSV, Trivy, npm audit and pip-audit as the intended security tooling. The proposal is domain-blind boilerplate outside the two hand-written branches.

Evaluator packs

Six JSON documents sit under evaluator-packs/, one per pack. They restate the domain-to-evaluator mapping and add a list of recommended file paths. No TypeScript source imports them, and package.json has no YAML or glob dependency, so they function as documentation for whoever writes the evaluators by hand.

evaluator-packs/*/pack.json
PackDomainsEvaluatorsRecommended files
gamebrowser_gameshell-core, playwright-web, browser-gameevals/playwright/game.spec.ts
evals/gameplay/bot.ts
evals/visual/canvas-check.ts
evals/perf/frame-budget.ts
webweb_app, browser_gameshell-core, playwright-webevals/playwright/app.spec.ts
evals/visual/responsive.spec.ts
apiapishell-core, api-contractevals/api/contract.test.ts
evals/api/auth.test.ts
evals/api/invalid-inputs.test.ts
cliclishell-core, cli-goldenevals/cli/golden.test.ts
evals/cli/fixtures/
datadata_mlshell-core, data-invariantsevals/data/invariants.test.ts
evals/data/fixtures/
researchresearchresearch-sourcesevals/research/source-audit.json
evals/research/claim-map.json

The game pack carries one field the others do not: a test_api_guidance object naming window.__DAEDALUS_GAME_TEST__ as the sanctioned telemetry hook, with the rule “Test telemetry is allowed, but it must be cross-checked against screenshots/canvas/video evidence.” That is the same distrust of self-reported state that runs through the whole design — a game can be instrumented to say it is working, so the instrument is only admissible alongside pixels.

There is no infra pack and no security pack, which matches the one red cell in Fig. 2.2 and the outstanding item at RESEARCH.md:319.

Sheet notes

  1. The registry array is exported as evaluatorRegistry and is mutable at runtime by any importer. Nothing freezes it, and no code re-reads it after createRun() has captured a selection into run state.
  2. EvalSelection stores whole EvaluatorManifest objects, not identifiers, so run.json embeds a full copy of each selected manifest. A run’s record of what was selected therefore survives edits to the registry — a genuine and deliberate property.
  3. templates/eval-plan.yaml sketches a three-part structure — public_evals, hidden_evals, anti_cheat — that has no counterpart in the type system. FrozenEvalSnapshot is the closest thing, and it holds only identifiers and hashes.
  4. Nothing in the codebase distinguishes a public test from a hidden one at runtime. The publicTests and hiddenTests arrays are adjacent string lists in the same object, readable by anyone who can read the module.