Skip to main content

Variants

A variant document is a change to a model, written down. It is the input cwright propose takes beside a baseline: the proposal is applied to produce a candidate, and the two are then compared exactly as compare compares any pair.

It is a separate document with its own root, not a part of the model. That is what makes a proposal replayable — the same variant can be applied to a baseline that has since moved, and the run record carries the question that was asked rather than only the answer.

name: "a second scanner"
rationale: "litho is the constraint at 94% utilisation"
changes:
- { kind: set_servers, resource: scanner, servers: 2 }
fieldrequiredwhat it is
nameyesWhat this change is, in the proposer's words. Carried into the run record.
rationaleno, default ""Why it was proposed. Free text, never interpreted.
changesyesOne or more changes, applied in order.
An empty changes list is refused, and so is a variant that changes nothing

A proposal whose result is the baseline is not a proposal. apply refuses both — an empty list by name, and a document whose changes all leave the model as it was by comparing the resulting model_hash against the baseline's. ⭐ Otherwise a search loop spends a full paired comparison discovering that two identical models are identical, and reports NoMeaningfulDifference about a question nobody asked.

The changes​

⚠️ The set is closed on purpose. An open-ended "set this JSON path" would let a proposal reach anywhere in the document, including fields whose invariants the compiler relies on. Every change below names the thing it touches and nothing else.

⭐ Scale changes are factors, not absolutes. "10% faster" survives a change to the underlying model and "600 seconds" does not, so a search that mixes the two cannot be replayed against a new baseline.

set_servers​

How many servers a tool group has. The classic capital question.

kind: set_servers
resource: scanner
servers: 3

set_queue_capacity​

How many jobs may wait at a resource, excluding the one in service.

kind: set_queue_capacity
resource: scanner
queue_capacity: 3

⭐ The capacity question with an exact answer behind it: Naor's welfare optimum is a statement about exactly this bound. At ρ = 4, choosing the bound selfishly rather than for welfare throws away 95.73% of the attainable welfare.

⚠️ A bound of 0 needs when_full: block on the resource — otherwise it is a loss system with a different exact answer (Erlang B rather than Erlang C) rather than a smaller queue. apply validates the result, so that refusal comes from resources and is not restated here.

scale_process_time​

Multiply every process time on a resource, including a wafer cascade's piece_interval_seconds, so the whole cascade speeds up.

kind: scale_process_time
resource: bench
factor: 0.8

scale_release_interval​

Multiply a release stream's interarrival time. Above 1.0 is less load.

kind: scale_release_interval
product: job
factor: 1.25

scale_maintenance_interval​

Change how often a maintenance policy on a resource comes due.

kind: scale_maintenance_interval
resource: oven
factor: 2.0

set_batch​

Change a batching policy, in pieces. See batching.

kind: set_batch
resource: furnace
min_pieces: 75
max_pieces: 150

set_entity_rule​

Replace the rule that picks which waiting entity a free server takes. The rule is an ordinary dispatch rule.

kind: set_entity_rule
resource: scanner
rule:
ranks:
- - priority
- 1.0
- - age
- 1.0

What is refused, and where​

⭐ apply validates the result, not the change. A variant is legal when the model it produces is legal, so every refusal on the model pages applies unchanged and there is no second copy of any rule here. What the variant document adds on top:

refusalwhy
an unknown key anywhere in the document⚠️ the same rule as the model: a dropped key silently selects a default that looks plausible
changes emptya proposal with no changes is not a proposal
the result's model_hash equals the baseline'sthe proposal changes nothing, reported at the document rather than at one change
naming a resource, product or policy the model does not havereported at /changes/{i}, so the message names which change is wrong
the same setting changed twicereported at /changes/{i}: the second write would silently win

Where the vocabulary comes from​

This page is generated from nothing — but the set it describes is checked against crates/clockwright-ir/src/variant.rs by the gate, in three places: the schema an agent reads, the vocabulary the MCP server serves, and every fence above. A change added to the engine and not documented here fails the build.