Cookwala / SDK and scenarios / 014

A V0 imported recipe is refused on a robot and assigned to a person when one is present

The fifi.cooking collection holds 2,042 described recipes (V0, RFC-0009). Every step is a legacy step that no device can execute. The dry run refuses them on any robot alone and hands every step to a person when one is present.

Audience: recipe publisher · Goal: Help end hunger · Level: beginner

Read first: /docs/RECIPE-FORMAT/ /docs/CORE/

Steps

  1. A V0 document passes the recipe schema: described is a valid level. validate

  2. Fattoush salad (archive, CC-BY-4.0) on the kitchen robot, nobody present: refused at the first step. dryRun

  3. With a person present: accepted, and all three steps are the person's. dryRun

  4. The vocabulary entry says why: legacy_step belongs to the legacy family, declares no sensors, and its definition says no device declares it as a capability. getOps

Outcome

You know that a described recipe is findable and attributable today, and that no device will act on it until a reviewed process graph makes it V1. Next: scenario 016 reads a V1 recipe as a document.

Command line

cookwala dryrun recipes/archive/salad-08.cookwala.json --device examples/capabilities/demo-hob-robot.json
cookwala dryrun recipes/archive/salad-08.cookwala.json --device examples/capabilities/demo-hob-robot.json --human-present

Recorded output

Status: ok

[1] validate: {"ok": true, "errors": []}
[2] dryRun: {"state": "refused", "refusal": {"reason": "missing_capability", "node": "n1", "detail": "device cannot perform cw.op.legacy_step and no person is present to do it"}, "plan": []}
[3] dryRun: {"state": "accepted", "plan": [{"node": "n1", "op": "cw.op.legacy_step", "by": "human", "verifiedBy": "human"}, {"node": "n2", "op": "cw.op.legacy_step", "by": "human", "verifiedBy": "human"}, {"node": "n3", "op": "cw.op.legacy_step", "by": "human", "verifiedBy": "human"}]}
[4] getOps: {"name": "ops", "version": "0.2.0", "entries": [{"id": "cw.op.dispense", "label": {"en": "Dispense / measure", "ar": "قياس وإضافة"}, "definition": "Measure an ingredient by mass, volume or count and add it to a target.", "classes": ["cw.opfamily.handle"], "paramsSchema": {"type": "object", "properties": {"precision": {"type": "number"}}}, "sensors": ["cw.sense.mass"], "links": {"ieee1872": "IEEE 1872.1-2024 Task (to be mapped)"}}, {"id": "cw.op.transfer", "label": {"en": "Transfer", "ar": "نقل"}, "definition": "Move food or a vessel's contents from one place to another.", "classes": ["cw.opfam…

Code

Run: python scenarios/out/014-v0-recipe-refused-on-a-robot-assigned-to-a-person/python.py

# Scenario 014: A V0 imported recipe is refused on a robot and assigned to a person when one is present
# The fifi.cooking collection holds 2,042 described recipes (V0, RFC-0009). Every step is a legacy step that no device can execute. The dry run refuses them on any robot alone and hands every step to a person when one is present.
# Run a hub first: python hub/cookwala_hub.py --recipes examples
import json, os
from cookwala.client import CookwalaClient, CookwalaProblem

def load(path):
    with open(path, encoding="utf-8") as f:
        return json.load(f)

def pick(obj, path):
    for part in path.replace("]", "").replace("[", ".").split("."):
        obj = obj[int(part)] if part.isdigit() else (len(obj) if part == "length" else obj.get(part))
    return obj

c = CookwalaClient(os.environ.get("COOKWALA_HUB", "http://localhost:7878"))


# Step 1: A V0 document passes the recipe schema: described is a valid level.
valid = c.validate("recipe", load("recipes/archive/salad-08.cookwala.json"))
assert pick(valid, "ok") == True, pick(valid, "ok")
print("ok", "ok", "=", True)

# Step 2: Fattoush salad (archive, CC-BY-4.0) on the kitchen robot, nobody present: refused at the first step.
alone = c.dry_run(recipe=load("recipes/archive/salad-08.cookwala.json"), device_id="demo-hob-robot", human_present=False)
assert pick(alone, "state") == "refused", pick(alone, "state")
print("ok", "state", "=", "refused")
assert pick(alone, "refusal.reason") == "missing_capability", pick(alone, "refusal.reason")
print("ok", "refusal.reason", "=", "missing_capability")
assert pick(alone, "refusal.node") == "n1", pick(alone, "refusal.node")
print("ok", "refusal.node", "=", "n1")
assert pick(alone, "refusal.detail") == "device cannot perform cw.op.legacy_step and no person is present to do it", pick(alone, "refusal.detail")
print("ok", "refusal.detail", "=", "device cannot perform cw.op.legacy_step and no person is present to do it")

# Step 3: With a person present: accepted, and all three steps are the person's.
withPerson = c.dry_run(recipe=load("recipes/archive/salad-08.cookwala.json"), device_id="demo-hob-robot", human_present=True)
assert pick(withPerson, "state") == "accepted", pick(withPerson, "state")
print("ok", "state", "=", "accepted")
assert pick(withPerson, "plan.length") == 3, pick(withPerson, "plan.length")
print("ok", "plan.length", "=", 3)
assert pick(withPerson, "plan[0].op") == "cw.op.legacy_step", pick(withPerson, "plan[0].op")
print("ok", "plan[0].op", "=", "cw.op.legacy_step")
assert pick(withPerson, "plan[0].by") == "human", pick(withPerson, "plan[0].by")
print("ok", "plan[0].by", "=", "human")
assert pick(withPerson, "plan[2].verifiedBy") == "human", pick(withPerson, "plan[2].verifiedBy")
print("ok", "plan[2].verifiedBy", "=", "human")

# Step 4: The vocabulary entry says why: legacy_step belongs to the legacy family, declares no sensors, and its definition says no device declares it as a capability.
ops = c.get_ops()
assert pick(ops, "entries[56].id") == "cw.op.legacy_step", pick(ops, "entries[56].id")
print("ok", "entries[56].id", "=", "cw.op.legacy_step")
assert pick(ops, "entries[56].classes[1]") == "cw.opfamily.legacy", pick(ops, "entries[56].classes[1]")
print("ok", "entries[56].classes[1]", "=", "cw.opfamily.legacy")
assert pick(ops, "entries[56].sensors.length") == 0, pick(ops, "entries[56].sensors.length")
print("ok", "entries[56].sensors.length", "=", 0)

print("scenario complete")

scenarios/014-v0-recipe-refused-on-a-robot-assigned-to-a-person.json

Previous: 013 Next: 015