You will combine Starting State, Changes, a Constraint, and Generator logic in one inspection-depot project.
Create the Starting State
Create start.workspec.json:
{
"$schema": "https://universalautomation.wiki/workspec/v2.2.schema.json",
"simulation": {
"schema_version": "2.2",
"meta": {
"title": "Inspection depot",
"description": "Open a depot and inspect pumps that arrive before closing.",
"domain": "maintenance"
},
"config": {
"time_unit": "minutes",
"start_time": "08:00",
"end_time": "08:10"
},
"world": {
"layout": {
"locations": [
{
"id": "intake",
"name": "Intake",
"shape": { "type": "rect", "x": 20, "y": 20, "width": 200, "height": 140 }
},
{
"id": "workshop",
"name": "Workshop",
"shape": { "type": "rect", "x": 260, "y": 20, "width": 200, "height": 140 }
}
]
},
"objects": [
{
"id": "manager",
"type": "actor",
"name": "Depot manager",
"location": "workshop",
"properties": { "state": "available" }
},
{
"id": "inspector",
"type": "actor",
"name": "Inspector",
"location": "workshop",
"properties": { "state": "available" }
},
{
"id": "pump_a",
"type": "product",
"name": "Pump A",
"location": "intake",
"properties": { "quantity": 1, "state": "awaiting_inspection" }
}
]
},
"collections": {
"waiting_pumps": {
"from": "objects",
"as": "item",
"where": {
"all": [
{ "==": ["@item.type", "product"] },
{ "==": ["@item.state", "awaiting_inspection"] }
]
},
"open": true,
"closes_at": "08:10"
}
},
"process": {
"tasks": [
{
"id": "open_depot",
"actor_id": "manager",
"start": "08:00",
"duration": "1m",
"location": "workshop"
}
],
"work_definitions": [
{
"id": "inspect_arrival",
"instantiate": {
"for_each": "waiting_pumps",
"as": "pump",
"start": "on_appearance"
},
"task": {
"actor_id": "inspector",
"duration": "2m",
"location": "workshop"
}
}
]
}
}
}Create the Changes source
Create changes.workspec.js:
// @ts-check
/// <reference types="workspec/workspec-changes" />
WorkSpec.task("open_depot", (task) => {
task.onStart(() => {
set("manager", "state", "opening", { temporary: true });
});
task.onComplete(() => {
set("manager", "state", "available");
});
});Create the Generator source
Create generator.workspec.js:
// @ts-check
/// <reference types="workspec/workspec-generator" />
WorkSpec.onUpdate(({ time, create }) => {
if (time === 483) {
create({
id: "pump_b",
type: "product",
name: "Pump B",
location: "intake",
properties: { quantity: 1, state: "awaiting_inspection" }
});
}
});The Generator changes the world. The collection and work definition remain the only task-instantiation path.
Create the Constraint source
Create constraints.workspec.js:
// @ts-check
/// <reference types="workspec/workspec-constraints" />
WorkSpec.constraint("inspection.all_arrivals_complete", (context) => {
const instances = Object.values(context.state().task_instances);
const incomplete = instances.filter((instance) => instance.status !== "completed");
if (instances.length === 2 && incomplete.length === 0) {
return null;
}
return {
severity: "error",
observed: {
instance_count: instances.length,
incomplete: incomplete.map((instance) => instance.id)
},
expected: { instance_count: 2, incomplete: [] },
message: "Both pump inspection instances must complete."
};
});Validate the complete project
Run project validation with the Constraint:
npx workspec validate start.workspec.json \
--changes changes.workspec.js \
--generator generator.workspec.js \
--constraints constraints.workspec.js \
--time 08:05 \
--seed 7 \
--json \
--yesThe result has an empty problems array. Its run object records seed 7 and a resolved-through time of 485.
Inspect the resolved state
Run a snapshot with the same project inputs:
npx workspec snapshot start.workspec.json \
--changes changes.workspec.js \
--generator generator.workspec.js \
--time 08:05 \
--seed 7 \
--jsonCheck these results:
state.objectscontainspump_aandpump_b.state.task_statuses.open_depotiscompleted.state.task_instancescontains two completed inspection instances.run.resolved_throughis485.
Run the domain rule
Run the Constraint directly:
npx workspec constraints start.workspec.json \
--changes changes.workspec.js \
--generator generator.workspec.js \
--constraints constraints.workspec.js \
--time 08:05 \
--seed 7 \
--yesExpected output:
Runtime constraints at 485 (resolved through 485): 0 violations (0 errors), 0 validation problemsRender the result
Render the resolved world:
npx workspec render start.workspec.json \
--changes changes.workspec.js \
--generator generator.workspec.js \
--time 08:05 \
--seed 7 \
--out inspection-depot.svgThe SVG shows both pumps at intake. The snapshot remains the source of truth for their task-instance records.
Result
You now have a canonical WorkSpec 2.2 project that uses every authored source type.
Continue with the WorkSpec reference for exact contracts. Use Choose your path for task-focused sections.