Sous

An executable recipe format.

Sous is an open, machine-readable format for recipes that a kitchen orchestration system can execute, not merely display. It is built on one idea that no existing recipe format has: an ingredient's state is separate from the procedure that produces it.

A recipe says it needs diced onion. Whether that comes from a bag of pre-diced onion or from a knife and a whole onion is a planning question, not a recipe fact.

"requires": [
  { "id": "onion",
    "substance": { "ref": "sous:onion_peeled", "label": "onion (peeled)" },
    "state":     { "form": "diced" },                            // WHAT is needed
    "quantity":  { "kind": "exact", "value": 500, "unit": "g" } }
]                                                                // not HOW to get it

Give that recipe a kitchen with whole onions and the planner emits peel then cut. Give it a kitchen with pre-diced onions and the plan is empty. Same recipe, both times. A raw onion is peelable and not cuttable, so the ordering is not a rule — it is the only reachable plan.

Worked plans shows this rather than asserting it: the same recipes scheduled against different kitchens, drawn as dependency graphs and Gantt charts, and then run — including a tempering step that is observed to be finished, breaches its ninety-second control point anyway, and is discarded.

Draft, unstable. The facet vocabulary is the highest-risk surface and needs validation against a much broader corpus before anything freezes. Read LIMITATIONS.md before building on this.

Schemas

These URIs are what $id and $schema fields point at. They are identifiers first and locations second — nothing in the toolchain dereferences them, since every cross-file $ref is relative and resolved against a local registry. They are served here for anyone who pastes one into a browser.

LayerNamespace URI
Recipe
Required states, a task network, a goal
https://sous.pras.ai/schema/v0/recipe.schema.json
Kitchen
Equipment and pantry — the planner's initial state
https://sous.pras.ai/schema/v0/kitchen.schema.json
Domain
Facets and operations — compiles to PDDL
https://sous.pras.ai/schema/v0/domain.schema.json
Schedule
One recipe, one kitchen, resolved — the document an executor consumes. Generated, never authored.
https://sous.pras.ai/schema/v0/schedule.schema.json
Run
What happened: observations, deviations, escalations
https://sous.pras.ai/schema/v0/run.schema.json
Common
Identifiers, quantities, state, conditions
https://sous.pras.ai/schema/v0/common/
Also served
https://sous.pras.ai/vocab/ The domain: facets, operations, units, equipment classes, 260 substances.
…/schema/v0/context.jsonld JSON-LD context. Maps foodon:, chebi: and the rest onto IRIs, and says plainly which of them dereference.

The namespace is scoped to the major version, so additive 0.N → 0.N+1 releases do not rewrite the $id of every schema or the $schema line of every document in the wild.

Getting started

git clone https://github.com/psigen/sous
cd sous && uv sync              # pins the interpreter and deps

uv run tests/run_tests.py        # the acceptance suite
uv run tools/sous_validate.py examples/*.sous.json

# what to do, then when to do it
uv run tools/sous_schedule.py \
    examples/dal-tadka.sous.json \
    examples/kitchens/home-kitchen.sous-kitchen.json --out plan.json

# then carry it out -- plan.json is all the executor reads
uv run tools/sous_run.py plan.json --format text

Reading

docs/spec/The normative specification, and the seven documents around it
SEMANTIC-RULES.mdConstraints JSON Schema cannot express
CONFORMANCE.mdWhat it means to implement Sous
EXECUTION.mdTurning a schedule into a run, and onto a workflow engine
SAFETY.mdAllergen derivation and critical control points
GOVERNANCE.mdVersioning and namespace policy
LIMITATIONS.mdKnown gaps and open problems
docs/arch/The decisions and, more usefully, why the obvious alternative was rejected