Core concepts
The five load-bearing nouns — agent, world, suite, check, finding — and how they compose into a regression review.
Everything in these docs is built from five nouns: an agent you port, a world it runs in, a suite that binds the two to a set of scenarios and a gate, a check that runs one commit's worth of those scenarios, and a finding that narrates a single case whose behavior changed. The whole flow — from your first port to a green Dystopic check on your first PR — is just these five composing in order.
Read this page once. Every HOW page that follows leans on this vocabulary instead of re-teaching it.
The five nouns
Agent
Your ported runtime — the thing under test. The default and recommended shape is a sandbox (code) agent: the platform runs your source in a managed sandbox. Registered against your umbrella agent, it gets a numeric agent id.
World
The simulated environment the agent runs in: the tools it calls (through the Odyssey proxy), an optional multi-agent topology, and an optional ledger schema for simulated state.
Suite
Binds an agent to a set of scenarios and a gate. Authored on the platform — the web dashboard, the CLI/SDK, or the REST API — and attached to your umbrella agent, with its scenarios frozen and sourced server-side. This is what you run.
Check
One run of a suite's scenarios against one commit. A base-vs-head comparison is two checks — one per side.
Finding
One scenario whose behavior changed between base and head, narrated by the pairwise reviewer with a severity, a title, a rationale, and a deep link.
Two supporting nouns recur throughout: an umbrella agent (the platform agent a connected repo maps to — it supplies the CI execution recipe, gate, and suite binding) and an execution mode (per-tool: Simulated, Executed, or Live — where a tool's response comes from). Both are defined below.
Agent — the thing under test
An agent is your existing agent, ported so the platform can dispatch runs to it. It is a sandbox (code) agent — you upload Python source and the platform executes it in a managed sandbox. It ships as mode: "sandbox" on the wire.
The agent receives the run context and returns a response. The one required field of that response is final_response, a non-empty string — an empty string fails the run, because the judge has nothing to grade against. The agent's tools don't run real side effects during a check unless you say so: each tool call is a POST {proxy_url}/tools/{tool_name} through the Odyssey proxy, which is where the world lives.
Registering an agent — via the web dashboard or the SDK (create_code_agent(...) / POST /api/agents) — yields a numeric agent id in the create response and moves the agent from draft to active. When you connect a repo on the platform, one such agent becomes the repo's umbrella agent — the agent CI resolves its execution recipe, gate, and suite binding from.
Port an agent in Port your agent; register it in Register your agent. The full envelope/response/proxy contract is the wire contract.
World — the simulated environment
The world is what the agent's tool calls hit instead of production. It has up to three parts, and for a single-agent tool-using system you often need only the first:
-
Tools — each tool the agent calls is served by the Odyssey proxy. Every tool has an execution mode — the axis of where its response comes from — with three values:
- Simulated (default,
simulated()) — the world engine invents the response from world state and the tool's output schema. No real calls. - Executed (
executed()) — the tool's real code runs in the sandbox; its data operations execute against the simulated world. - Live (
live(*, tool_name, endpoint_id | endpoint_name)) — the call is forwarded to a bound live ToolEndpoint, with real side effects.
Tool schemas declare
nameandinput_schema; declaring anoutput_schemalets the simulator validate its generated response against a shape you control. - Simulated (default,
-
Topology (optional) — for multi-agent systems, a static declaration of
sub_agents(each with anactor_id, itstools, and who ittalks_to) plus atopology.entry. Single-agent systems declare no topology. -
Ledger schema (optional) — a closed-world ontology of
entitiesandflagsfor agents that operate on simulated state. Entity types are canonical singularsnake_case(regex^[a-z][a-z0-9_]*$). Omit it entirely (sendnull) for a fully-open ledger. Tools change that state either implicitly — the simulator infers the mutation — or through a ledger adapter, a per-tool binding that declares the change deterministically (add/update/remove/set_flag).
Both topology and ledger are optional. A single-agent system with no simulated state needs neither.
The three execution modes (their SDK helpers, legacy aliases, and public↔wire mapping) live in the execution-modes reference. Declare the world in Declare the world; the full topology + ledger schema is the declare-world reference.
Suite — the binding you run
A suite composes an agent and a world with a set of scenarios and a gate. It is platform-managed: you author it on the web dashboard, with the CLI/SDK (dystopic suites …), or via the REST API, and it is attached to your umbrella agent. Its scenarios are frozen and sourced server-side, so an in-app check and a CI check run byte-identical cases.
Three things make up a suite:
- Scenarios — the test cases the suite runs. Each scenario is a platform object (authored in the dashboard, with
dystopic scenarios create|import, or viaPOST /api/agents/{id}/scenarios); the suite binds an ordered set of them (dystopic suites bind/PUT /api/agents/{id}/suites/{sid}/scenarios). Editing a scenario applies to the next run of any binding suite, never retroactively — past checks froze their scenarios. - The agent the suite belongs to, and through it, the world.
- The gate — the tiers that decide whether a behavior change blocks a merge.
The gate is set on the platform — the umbrella agent's gate card in the dashboard, dystopic agents gate set, or PUT /api/agents/{id}/gate — and is platform-authoritative. Its tiers are:
constraint— block when a hard constraint regresses.reviewer_severity: <nit|warning|critical>— block when a finding lands at or above the chosen severity.min_pass_rate: 0..1— block when the head check's pass rate falls below the floor.scorer: <name>— block on named scorers.
An empty gate makes the check advisory — it reports but never blocks. Because the gate lives on the platform, it is RBAC'd and audited, and dropping config on a head commit cannot weaken or disarm it.
Author a suite in Create a test suite; the full scenario, suite, and gate grammar is the create-suite reference.
Check — one run of one commit
A check is one execution of a suite's scenarios against a single commit. It produces per-case verdicts for that commit and nothing more — a check knows only about itself.
A regression is a comparison, and a comparison needs two checks: one at the base commit and one at the head commit, run against the identical scenarios. odyssey ci review resolves both SHAs, runs a check on each side, and hands the pair to the reviewer.
odyssey ci review --platform-suite \
--base "$(git merge-base origin/main HEAD)" \
--head HEAD \
--repo owner/repo \
--pr-number 123You can export a single check's raw results — its per-case task list and totals — with runs export:
dystopic runs export <check_id> --json results.jsonThat is one check, not the comparison. Which brings us to the distinction the rest of these docs depend on.
Check vs. review — keep them distinct
A check is one run of one commit. A review is the pairwise base-vs-head object built from two checks. Casual prose conflates them; the docs do not.
| Check | Review | |
|---|---|---|
| What it is | One run of the suite's scenarios at one commit | The base-vs-head comparison of two checks |
| How many per PR | Two (base + head) | One |
| Fetch it with | runs export <check_id> → GET /api/checks/{check_id} | GET /api/ci/reviews/{review_id} |
| Carries | Per-case verdicts for that commit | gate_status, findings, diff, blocking_reasons |
The review is what carries the verdict. After assembly it has a gate_status — one of pass, blocked, neutral, or action_required — a diff (intersecting, regressions, improvements, constraint_regressions, …), a blocking_reasons list, and an array of findings.
When the base commit can't be resolved (a first PR, an absent base branch, or --base "origin/"), there is no pair to compare, so no review is created. odyssey ci review degrades to a floor-only run of the head check, gating only on min_pass_rate if configured.
Finding — a narrated changed case
A finding is one scenario whose behavior changed between the base check and the head check, analyzed by the pairwise reviewer (an LLM) and attached to the review. Each finding carries:
case_id— the scenario's name/id.severity—critical,warning, ornit, assigned by the reviewer (not the judge).title— a short summary of what changed.rationale— the longer explanation of what changed and why.deep_link— a platform trace URL for the head run of that scenario. The per-case base and head traces live on the platform review page at platform.pipelines.tech.
Findings feed the gate: a reviewer_severity: critical tier blocks the merge when a finding lands at or above that severity. If the suite structurally changed between base and head (its tools or ledger changed), the comparison isn't apples-to-apples, so the reviewer is skipped and the gate is downgraded to neutral.
Read a full review in Review the findings; the complete review schema is the review-findings reference.
How they compose
Read the nouns as a single sentence that runs the length of the flow:
You port an agent and register it, declare the world it runs in, author a suite that binds the agent to scenarios and a gate, and on each PR
odyssey ci reviewruns two checks (base and head) whose comparison becomes a review — an assembled verdict plus findings that a Dystopic GitHub check surfaces on the pull request.
Install & authenticate
Set up the CLI (https://api.pipelines.tech) so every step can call the platform.
Start the flow
Port your first agent and walk the seven steps to a green check.
Next: Install & authenticate →