greenroom
Reference

State contract

Field-by-field reference for .greenroom/state-contract.json.

The state contract declares your app's screens and how your router maps source files to them. It is what gives a run coverage authority: with it, Greenroom scopes your diff to exact screens and a clean run is a real pass; without it, clean runs cap at inconclusive. It lives at .greenroom/state-contract.json and, like the manifest, is always read from the PR's base revision.

Top-level fields

FieldTypeRequiredMeaning
schemaVersionstringyesContract schema version, e.g. "1.0".
routerKind"hash-spa" | "expo-router" | "react-navigation" | "swiftui"with graphPathsWhich deterministic extractor rebuilds your screen graph from source.
graphPathsstring[], 1 to 20with routerKindWhere the extractor reads. See the pairing rules below.
entryStatestringnoThe state a fresh launch lands on.
statesarray, 1 to 20yesThe screens you declare.
transitionsarray, up to 400noEdges between states.

Router kinds and graphPaths

routerKind and graphPaths must be declared together; one without the other is a malformed contract and fails validation loudly, rather than silently falling back to model-based mapping.

  • hash-spa: exactly one path, the single source file defining your views object. Diff scoping is line-level.
  • react-navigation: exactly one path, same single-file shape. Scoping for this kind is file-level, not line-level.
  • expo-router: one or more directories containing the routed screens (typically your app/ directory).
  • swiftui: one or more directories, typically the target's source root.

States

Each state entry:

FieldTypeRequiredMeaning
idstringyesStable identifier, referenced by transitions and reports.
routestringnoThe route or URL fragment that reaches it.
screenstringnoThe component or view name (TodayView, CheckoutPage).
variantstringnoDistinguishes variants of one screen (empty state, error state).
goalstringnoWhat a user accomplishes here, phrased as a goal. Drivers pursue this.
identityobjectnoKey facts that identify this state in an observation.
oraclestringnoA declared expectation checked against evidence. This is what powers tier 3 findings.
setupTransitionsstring[]noTransition ids that must run before this state is reachable.

Transitions

Each transition names from and to state ids, optionally with the action (or actions, up to 10) that performs it and preconditions that must hold. Transitions let the mapper reason about what else a changed screen can affect downstream.

Start small

A contract with five states and no oracles already upgrades coverage authority and makes scoping exact. Add oracles and variants where correctness matters most (checkout, data entry, anything with arithmetic), and grow from there. The schema caps states at 20 per contract deliberately: the contract describes your app's load-bearing surfaces, not every screen you have.

On this page