# Setup and rule contracts

This page is derived from the [canonical developer-reference](/learn/developer-reference); its content has one source of truth.

## Setup schemas and normalization

Source of truth: `src/game/setup.ts`, `src/game/world.ts`.

`setupAnswersSchema` is a strict object with exactly `world`, `threat`, and `reward`; each is a trimmed, nonempty string of at most 140 characters. Unknown keys fail. `canonicalAnswers` trims and revalidates. `hashSetupRequest` hashes the canonical ordered answer tuple and prefixes it with `setup_`.

`LifeConfig` is strict at each schema level:

| Field | Accepted values / constraint |
| --- | --- |
| `environment.abundance` | `scarce`, `balanced`, `rich` |
| `environment.distribution` | `clustered`, `scattered`, `seasonal` |
| `environment.hazard` | `drought`, `toxin`, `heat`, `crowding` |
| `environment.volatility` | `stable`, `pulsing`, `chaotic` |
| `fitness` | exactly `survive`, `replicate`, `cooperate`, `explore`, `adapt`; each 0–1 and total within 1e-9 of 1 |
| `founders.balance` | `prey_heavy`, `balanced`, `predator_heavy` |
| `founders.diversity` | `focused`, `varied` |
| `founders.preyStrategy` | `efficient_grazing`, `early_brood`, `armored`, `swarming`, `dispersal` |
| `founders.predatorStrategy` | `ambush`, `pursuit`, `pack_hunting`, `efficient_kill`, `brood_hunting` |
| `rules` | optional `WorldRuleGraph`; if omitted the default graph is used by the engine |

`normalizeFitness` clamps negative/non-finite inputs to zero and normalizes; an all-zero input becomes equal weights. It is not a substitute for validating external config. `defaultConfig()` uses balanced/scattered/drought/stable, equal weights, balanced/varied founders, efficient grazing/pursuit, and `defaultRuleGraph()`.

Setup prose maps by fixed keyword rules in `deterministicSetup`; it does not generate executable logic. The route may request typed setup interpretation from Jev, validates the response, and returns deterministic setup as an explicitly labeled fallback when model access or validation fails.

### Fidelity correction and review

Live setup first establishes a model-authored organism-to-slot contract from finite spans of the original world and threat, then selects roles and directed relationships for those named organisms. The initial environment/fitness call no longer independently selects another species count, role set or feeding graph. A duplicate identity can receive one corrective mapping call: valid earlier assignments remain fixed, and the remaining palette excludes already assigned or overlapping earlier mentions. A still-inconsistent correction is rejected. This is mechanical source grounding and bounded model interpretation, not keyword-based semantic acceptance.

The browser shows the named intent, concrete contract conflicts and finite failure-stage/code metadata. A corrected graph must retain the established species, roles and directed links even if a fresh fidelity reviewer says `approve`. The original answers are preserved, and diagnostics contain no raw provider payload or credentials. Named-intent and failure metadata remain outside the deterministic replay contract. Setup costs six calls directly and at most thirteen with identity/food-web correction and the council protocol correction; all calls are reserved and their successful response usage is accounted for.

Council selection preserves the returned Choice protocol: the selected label must have maximal probability. A reproduced nonmaximal selection with otherwise valid data receives one budgeted corrective request; a second inconsistent response, malformed distribution, invalid metadata or denied reservation remains a failure. The original probability data are never changed or renormalized, and successful correction accounts for both requests. This does not add runtime-council retries or alter simulation/replay validation.

The fidelity request includes the complete interpreted configuration and a readable, engine-derived food web alongside the original answers and encoded graph. Pair directions are decoded using each pair's own endpoints; review checks supported food-web intent, not biological realism or guaranteed success of the player's objective. A `reselect` or `reject` fidelity verdict triggers at most one correction; the corrected proposal must independently receive `approve`, and persistent rejection stays blocked. Roles/count are selected first; the dependent pair request includes the fixed species/roles. The complete corrected graph is validated and reviewed against the unchanged original answers. An approved correction receives a council selected for that graph. Every provider call reserves the existing call budget, and successful response usage is accumulated. A persistent nonapproval gets a finite, model-selected review focus; that focus is not independent proof of a semantic mismatch. Failure inside correction preserves the nonapproval instead of silently returning a seedable fallback. Additional review metadata stays outside deterministic engine/replay contracts.

Run `LIFEPOT_LIVE_SETUP_TEST=1 npm test -- src/app/api/judge/setup-live.test.ts` with a server-side `TYPESAFE_API_KEY` already configured to check real Jev acceptance of ordinary inputs. This explicitly opted-in test makes paid inference requests; without both settings it is skipped. Mocked approval tests prove the correction/control flow, not model fidelity. For an approved Cloudflare preview that already holds the server-side key, run `PLAYWRIGHT_BASE_URL=http://localhost:9239 LIFEPOT_LIVE_GAMEPLAY_BASE_URL=http://localhost:9239 npm run test:e2e -- e2e/live-jev-gameplay.spec.ts --workers=1 --retries=0`. This separate opt-in browser test exercises ordinary setup, adaptive play, reflection, a mobile follow-up and inference-free replay; it is skipped during normal fixture runs. Do not point it at production by default.

## Rule graph reference

Source: `src/game/rules.ts`; tests: `src/game/rules.test.ts`.

A graph has integer `version >= 1`, 2–4 species in canonical prefix order (`A`, `B`, `C`, `D`), one interaction for every canonical species pair exactly once and in order, an environment object, and optional validated council metadata. Species roles: `producer`, `grazer`, `hunter`, `scavenger`, `omnivore`. Self-interactions: `cooperative`, `territorial`, `cannibalistic`, `neutral`. Pair modes: `a_consumes_b`, `b_consumes_a`, `competition`, `mutualism`, `avoidance`, `neutral`.

The graph must have a viable basal-feeding role (`producer`, `grazer`, or `omnivore`). Roles alone do not authorize predation: directional pair edges control inter-species consumption. A strict invalid shape throws `Invalid ecosystem rule graph`; canonical-order, complete-pair, and basal-path invariant violations throw descriptive errors. Validation returns a cloned graph.

Environment vocabularies are regeneration (`steady`, `pulsed`, `depletion_feedback`), pressure (`stability`, `drought`, `toxin_wave`, `heat_wave`, `fragmentation`, `nutrient_bloom`), volatility (`stable`, `pulsing`, `chaotic`), intensity (`low`, `medium`, `high`), and duration (`short`, `medium`, `long`, `persistent`). Scheduled activations are `immediate`, `after_6`, `after_12`, `after_24`, `resources_low`, `population_boom`, `population_crash`, `species_emergence`, `species_extinction`, `predation_high`; transitions are `abrupt`, `ramp`, `pulse`.

A `RulePatch` is exactly one of: `{kind:"pair", pair, mode}`, `{kind:"self", species, value}`, or `{kind:"environment", field, value}`. It is strict; unknown keys/enum values reject. Pair and species targets must exist. A changed patch increments graph version once; an unchanged patch does not. Scheduled changes bind a generation 0–180, activation, duration, transition, and patch. They only take effect when their trigger is due and the accepted decision's graph-version evidence is valid. Temporary changes revert according to duration; persistent changes do not automatically revert.

### Validation example (actual functions)

Run the focused tests above to execute these cases using the repository's installed Vitest. This valid default graph is the actual `defaultRuleGraph()` contract; the second input is deliberately invalid because it adds an undeclared graph property and strict validation rejects it.

```ts
import { defaultRuleGraph, validateRuleGraph } from "./src/game/rules";

const graph = validateRuleGraph(defaultRuleGraph());
console.log(graph.species.length, graph.interactions[0].mode); // 2, "b_consumes_a"
try {
  validateRuleGraph({ ...graph, surprise: true });
} catch (error) {
  console.log((error as Error).message); // "Invalid ecosystem rule graph"
}
```

This snippet assumes the project TypeScript/Vitest environment and repository-root import path. It is a source-level example, not a standalone JavaScript script.
