Cette page a été traduite par une machine et n'a pas encore été révisée par une personne. L'anglais est la référence ; les corrections sont les bienvenues sur GitHub. GitHub

Cookwala / SDK et scénarios / 018

An execution request with the wrong recipe hash is refused; the right hash still needs a person

An agent sends an ExecuteRequest naming a recipe and its hash. The executor compares the hash with the revision it holds and refuses a mismatch; with the right hash, the device's own dry run still applies. Nothing heats up in this scenario.

Audience: agent builder · Objectif: Des robots pour les gens · Niveau: intermediate

Lire d'abord: /docs/CORE/ /docs/API/

Étapes

  1. Fetch the recipe the agent wants cooked. getRecipe

  2. Compute the hash the request must carry. hash

  3. Send a request with a zero hash: HTTP 202 with state refused, reason recipe_hash_mismatch. A refusal is a result, not an error. startExecution

  4. Send the right hash with nobody present: the hub's device is the example arm, and cutting needs a person. Refused before any motion. startExecution

  5. The hub told the agent which hash it holds.

Résultat

You have seen two refusals an agent must handle before cooking starts, and that both arrive as a status, not a crash. Scenarios 040 onwards start, watch, stop and log an accepted execution.

Ligne de commande

cookwala hash examples/shakshuka.cookwala.json
curl -s -X POST http://localhost:7878/v1/executions -H 'Content-Type: application/json' -H 'Idempotency-Key: sc018-wrong-hash' -d '{"core": "0.2.0", "kind": "ExecuteRequest", "id": "sc018-wrong-hash", "recipe": "cw:cookwala.ai:example-shakshuka", "recipeHash": "sha256:0000000000000000000000000000000000000000000000000000000000000000", "requestedBy": "agent:scenario-018", "idempotencyKey": "sc018-wrong-hash", "x-hub-human-present": true}'

Sortie enregistrée

Statut: ok

[1] getRecipe: {"$schema": "https://cookwala.ai/v1/schemas/recipe.schema.json", "cookwala": "0.1.0", "id": "example-shakshuka", "revision": 2, "updated": "2026-10-03", "hash": "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841", "verification": {"level": "V2", "evidence": {"simulatorRuns": 1, "humanReviews": 1, "fieldReports": 0}, "reviewedBy": "fifi.cooking editorial", "notes": "Illustrative example for the spec; values are realistic but not field-verified."}, "dish": {"names": {"en": "Shakshuka", "ar": "شكشوكة", "fr": "Chakchouka"}, "cuisine": ["TN", "MA", "DZ", "LY"], "course": "main…
[2] hash: {"hash": "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841"}
[3] startExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "sc018-wrong-hash", "seq": 0, "state": "refused", "request": "sc018-wrong-hash", "updatedAt": "2026-10-07T00:54:07Z", "refusal": {"reason": "recipe_hash_mismatch", "detail": "this hub holds sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841"}}
[4] startExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "sc018-right-hash", "seq": 0, "state": "refused", "request": "sc018-right-hash", "updatedAt": "2026-10-07T00:54:07Z", "refusal": {"reason": "needs_human_present", "detail": "cw.op.cut may not run unattended", "node": "n1"}}

Code

Exécuter: python scenarios/out/018-wrong-hash-refuses-an-execution/python.py

# Scenario 018: An execution request with the wrong recipe hash is refused; the right hash still needs a person
# An agent sends an ExecuteRequest naming a recipe and its hash. The executor compares the hash with the revision it holds and refuses a mismatch; with the right hash, the device's own dry run still applies. Nothing heats up in this scenario.
# 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: Fetch the recipe the agent wants cooked.
recipe = c.get_recipe("shakshuka")
assert pick(recipe, "id") == "example-shakshuka", pick(recipe, "id")
print("ok", "id", "=", "example-shakshuka")

# Step 2: Compute the hash the request must carry.
h = c.hash(recipe)
assert pick(h, "hash") == "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841", pick(h, "hash")
print("ok", "hash", "=", "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841")

# Step 3: Send a request with a zero hash: HTTP 202 with state refused, reason recipe_hash_mismatch. A refusal is a result, not an error.
try:
    wrong = c.start_execution({"core": "0.2.0", "kind": "ExecuteRequest", "id": "sc018-wrong-hash", "recipe": "cw:cookwala.ai:example-shakshuka", "recipeHash": "sha256:0000000000000000000000000000000000000000000000000000000000000000", "requestedBy": "agent:scenario-018", "idempotencyKey": "sc018-wrong-hash"}, "sc018-wrong-hash", True)
except CookwalaProblem as p:
    wrong = p.body  # a refusal is a result, not a crash
    print("refused:", p.refusal)
assert pick(wrong, "state") == "refused", pick(wrong, "state")
print("ok", "state", "=", "refused")
assert pick(wrong, "refusal.reason") == "recipe_hash_mismatch", pick(wrong, "refusal.reason")
print("ok", "refusal.reason", "=", "recipe_hash_mismatch")

# Step 4: Send the right hash with nobody present: the hub's device is the example arm, and cutting needs a person. Refused before any motion.
try:
    right = c.start_execution({"core": "0.2.0", "kind": "ExecuteRequest", "id": "sc018-right-hash", "recipe": "cw:cookwala.ai:example-shakshuka", "recipeHash": "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841", "requestedBy": "agent:scenario-018", "idempotencyKey": "sc018-right-hash"}, "sc018-right-hash", False)
except CookwalaProblem as p:
    right = p.body  # a refusal is a result, not a crash
    print("refused:", p.refusal)
assert pick(right, "state") == "refused", pick(right, "state")
print("ok", "state", "=", "refused")
assert pick(right, "refusal.reason") == "needs_human_present", pick(right, "refusal.reason")
print("ok", "refusal.reason", "=", "needs_human_present")
assert pick(right, "refusal.node") == "n1", pick(right, "refusal.node")
print("ok", "refusal.node", "=", "n1")

# Step 5: The hub told the agent which hash it holds.
assert pick(wrong, "refusal.detail") == "this hub holds sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841", pick(wrong, "refusal.detail")
print("ok", "refusal.detail", "=", "this hub holds sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841")

print("scenario complete")

scenarios/018-wrong-hash-refuses-an-execution.json

Précédent: 017 Suivant: 019