Core concepts
The model at a glance
| Concept | Contract section | Inventory example |
|---|---|---|
| Adjustable assumptions | inputs |
Unit cost and weekly demand |
| Shared state | world |
A shared warehouse capacity |
| Kinds of participants or objects | types |
Retailer, supplier, product |
| Named objects | entities |
A particular shop or product |
| Generated people or organizations | population |
Customer segments drawn from rows |
| Decisions | actions |
Place a replenishment order |
| Sequence of decisions | stages |
Order, allocate, sell, settle |
| Autonomous changes | events, triggers |
Arrivals, demand, expiration |
| Information a participant receives | brief, views, records |
Own cash, visible prices, private bids |
| Measurements | metrics, outputs |
Stock over time and lost sales |
| Non-negotiable rules | invariants |
Inventory cannot be negative |
Types need not be agents. Products, orders, campaigns and contracts can be ordinary entities updated by actions and events.
A round
Start events run, then each stage runs in order, then end events and metrics. Ending conditions can stop the simulation after a stage; action-level ending conditions can stop it immediately after an action commits. A round can mean a day, week, campaign cycle or negotiation round.
Use sequential stages when later participants should see earlier decisions. Use simultaneous stages when decisions should be made from the same view, such as sealed bids. In simultaneous stages, choices are submitted before they are committed; do not assume a successful submission is already a settled outcome.
See stages, events and end conditions for exact fields.
Actions and atomicity
An action declares who may use it, typed arguments, eligibility requirements, effects and outcome text. The engine rejects invalid arguments and refuses actions that violate requirements. If an action effect fails, its changes are rolled back. Handle the returned ToolResult rather than assuming a tool call succeeded.
Atomic actions are useful for transferring money and goods together. Atomicity does not fix an incorrect business equation: an author must still test conservation, allocation order and units.
State, expressions and effects
Properties hold values. Expressions read those values; effects change them. For example, $actor.cash >= $params.qty * $inputs.unit_cost tests affordability. $actor.stock += $params.qty changes stock.
$actor is the acting entity, $params contains action arguments, and $it is the current item in a collection operation. Available roots depend on context. Expression reference lists the exact rules.
Information and privacy
A participant receives a static brief, an update and typed tools. Private properties and record visibility determine what is available to each viewer. Preview every role, and inspect recorded exposures when information boundaries matter. Keep secret values out of public announcement templates.
A host that loads the environment can inspect its full state. Participant visibility is not a security boundary against the host itself.
Determinism
Seeded engine randomness supports repeatable runs. Reproduction also requires the same contract, inputs, participant decisions and external responses. An LLM call may produce a different decision even with the same environment seed. Record exposures and replay calls when you need to reproduce those runs.
Author scenarios from a brief
The shortest reliable path is brief → contract → checks → known-answer tests → run → review. Start with the smallest model that preserves the decision the user wants to make.
1. Make the decision explicit
Write down the decision, horizon, actors, controllable inputs and outputs before writing rules. For example: “Should a distributor raise safety stock for seasonal products over the next 12 weeks, given supplier delays and a fixed cash budget?”
Distinguish supplied facts from assumptions. If a brief does not specify lead time, mark your chosen value as an assumption and expose it as an input. Do not bury it in an expression.
2. Map the causal structure
For each outcome, identify what changes it. Stock depends on opening inventory, deliveries, sales and returns. Sales depend on demand and available stock. Demand may depend on seasonality, price and promotion. Cash depends on receipts, payments and timing.
Represent objects with their own lifecycle as entities: an order that can arrive late or be canceled usually deserves an identity. Use maps and lists for simpler structured values; use records for history.
3. Choose the timing
Name each round and stage. State whether delivery happens before demand, whether participants observe competitors' decisions, and when payment clears. These choices can change the answer more than a sophisticated demand equation.
Give every delayed effect a clear starting point and due time. Include explicit tie-breaking when processing queued orders or allocating scarce capacity.
4. Build and inspect
fg-env new blank scenario.json
fg-env guide
fg-env guide actions
fg-env guide events
fg-env check scenario.json --rounds 5
fg-env preview scenario.json participant_id
Read only the reference sections needed for the current task. Expand mechanisms when you need to inspect their generated rules: fg-env expand scenario.json --mechanisms.
5. Test meaning, not just syntax
For every important requirement, write one observable check. Useful cases include zero demand, no budget, a full warehouse, a late delivery, a refund after settlement, overlapping audiences and tied queue priorities.
Calculate at least one small run by hand. Assert exact inventory, cash or capacity balances after each important transition. Also test changes: higher demand should not create inventory, and doubling a product table should not silently drop half the products.
6. Hand off a complete environment
Ship the contract and its data together, with:
- A short brief and list of assumptions, units and timing rules.
- Input descriptions, defaults and supported ranges.
- At least one baseline policy and a reproducible seed.
- Known-answer checks and boundary cases.
- Output definitions and limitations of the model.
- A run command and the SDK version used.
Instructions for an authoring agent
Use this as a starting instruction in your own authoring workflow:
Build a rounds-based environment with the environments-sdk. Read
fg-env guide, then the relevant sections. Map each requirement to contract rules and observable checks. Make uncertain assumptions explicit inputs. Use public SDK APIs and keep scenario logic in the contract. Check the contract, preview every role, run a deterministic baseline, and verify a small known-answer case. Report omissions and unsupported behavior. Deliver the contract, data, tests and run instructions.
The agent should repair errors using their paths and suggested fixes, then rerun the affected checks. A clean checker result does not prove that the brief was captured faithfully.
Model business behavior
Use ordinary contract sections for custom rules. Use a built-in mechanism when its semantics match your problem. Mechanisms expand into inspectable contract sections; they are optional building blocks, not fixed scenario templates.
Capability map
| Requirement | Building blocks | Important modeling choice |
|---|---|---|
| Multiple products and customer segments | types, population, table inputs |
Stable IDs, units, segment weights |
| Seasonal and price-sensitive demand | Patterns, demand | Lost demand versus observed sales; substitution |
| Inventory and reorder rules | Inventory, replenishment | Available versus reserved stock; lead times |
| Shared production capacity | Production, actions and stages | Allocation order, partial fulfillment, bottlenecks |
| Supplier networks | Supply chain, relations | Disruption correlation and payment terms |
| Money and settlement | Ledger, atomic effects | Cash versus revenue versus profit |
| Sales pipeline and approvals | Entity state, stages, delayed effects | Stage exit conditions, dependencies, churn |
| Bookings and subscriptions | Bookings, subscriptions | Cancellation, renewal and capacity release |
| Campaign exposure and diffusion | Channels, diffusion, relations | Audience overlap, repeated exposure, conversion lag |
| Queued work and scarce staff | Queues, actions, events | Priority, skills, abandonment and service duration |
| External judgments or data | Feeds, host mechanisms | Host response validation, availability and replay |
Nesting and reuse
Use relations for ownership and dependencies: a company has branches, a branch serves accounts, and an account owns orders. Iterate over actual entities or input rows rather than a fixed count. Use defs for repeated expressions, blocks for repeated effects, and imports for reusable contract fragments. Start with a single file and split only when it helps maintenance.
Three example briefs
Inventory under promotion. Two product tiers share warehouse space. A promotion brings demand forward, some customers substitute when stock runs out, and suppliers have correlated delays. Measure margin, stockouts, working capital and leftover stock. Include an arm without the promotion.
Enterprise sales and onboarding. Sales teams compete for accounts, discount requests need approval, implementations share specialist capacity, and late delivery increases churn. Track booked value separately from activated revenue and cash collection. Test whether selling faster overwhelms onboarding.
Local-service campaign. Several channels reach overlapping households; jobs require different technician skills, customers may cancel, and successful visits can generate referrals. Track unique reach, qualified demand, completed jobs and contribution after campaign spend. Do not count impressions as unique customers.
These are compositions of general rules, not special engines. Realism comes from the explicit causal model and supporting data.
Avoid false precision
More entities or equations do not automatically improve a forecast. Prefer a simple baseline first, add mechanisms that explain a material effect, and check the improvement on held-out cases. If conversion rates are assumed, label results conditional on those rates and show sensitivity.
Configurable objects and input controls
The agent declares the data users can change in inputs, then binds it into
entity properties, population rows or rules through $inputs. A map input
defines one object using nested fields. A table defines fields for each row;
a list can define typed items. Every field can include defaults, required
values, bounds, a label, a description and a unit.
{"shop": {"type": "map", "label": "Store settings", "display": "object", "default": {},
"fields": {
"price": {"type": "number", "default": 25, "min": 5, "max": 100, "step": 1, "display": "slider", "unit": "USD"},
"segment": {"type": "enum", "values": ["consumer", "business"], "default": "consumer", "display": "select"},
"description": {"type": "text", "default": "", "display": "textarea"}
}}}
This is an inputs section. The simulation reads $inputs.shop.price. Input
objects are configurable data; use types, entities and population to turn
that data into simulated actors and objects.
The optional display hint selects number, text, textarea, select,
toggle, date, slider, knob, table, object, list or json. Sliders
and knobs require numeric min and max; step sets the suggested increment.
Hosts choose how to render these controls. The SDK itself validates values and
applies nested defaults before execution, without requiring a user interface.
Presentation hints never change the simulation's equations.