Esta página fue traducida por una máquina y aún no ha sido revisada por una persona. El inglés es la referencia; las correcciones son bienvenidas en GitHub. GitHub

Cookwala / SDK y escenarios / 043

A resume with a stale seq is answered with 412 Precondition Failed

Changes to an execution carry If-Match with the seq the caller last saw. If the run moved on, the hub refuses with a Problem and tells the caller the current seq, and nothing changes.

Audiencia: developer · Objetivo: Robots para las personas · Nivel: beginner

Leer primero: /docs/CORE/ /docs/HUB/

Pasos

  1. Start a shakshuka run with a person present. startExecution

  2. The arm cuts; the run waits for a person at seq 6.

  3. Resume with seq 1, a value from long ago. The answer is a Problem: title precondition-failed, and the detail names the current seq. resumeExecution

  4. The run did not change: still waiting at seq 6. getExecution

  5. Resume again with the seq just read; this time the hub accepts. resumeExecution

Resultado

You have seen the 412 Problem and that a stale write is harmless. Next: scenario 044 covers idempotent retries of the start itself.

Línea de comandos

cookwala hub --recipes examples --speed 200
jq '. + {"x-hub-human-present": true}' examples/core/execute-request.json | curl -s -X POST http://localhost:7878/v1/executions -H 'Content-Type: application/json' -H 'Idempotency-Key: ex-2026-10-04-001-a' -d @-
curl -si -X POST http://localhost:7878/v1/executions/ex-2026-10-04-001/resume -H 'If-Match: 1' -H 'Idempotency-Key: resume-stale-01'

Salida grabada

Estado: ok

[1] startExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "ex-043-stale", "seq": 0, "state": "accepted", "request": "ex-043-stale", "updatedAt": "2026-10-07T00:54:23Z", "x-hub-plan": [{"node": "n1", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n2", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n3", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n4", "op": "cw.op.heat", "by": "human", "verifiedBy": "human"}, {"node": "n5", "op": "cw.op.saute", "by": "…
[3] resumeExecution: {"type": "https://cookwala.ai/errors/precondition-failed", "title": "precondition-failed", "detail": "seq is 6"}
[4] getExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "ex-043-stale", "seq": 6, "state": "needs_human", "request": "ex-043-stale", "updatedAt": "2026-10-07T00:54:25Z", "x-hub-plan": [{"node": "n1", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n2", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n3", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n4", "op": "cw.op.heat", "by": "human", "verifiedBy": "human"}, {"node": "n5", "op": "cw.op.saute", "by"…
[5] resumeExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "ex-043-stale", "seq": 7, "state": "running", "request": "ex-043-stale", "updatedAt": "2026-10-07T00:54:27Z", "x-hub-plan": [{"node": "n1", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n2", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n3", "op": "cw.op.cut", "by": "device", "verifiedBy": "sensor", "rung": "cw.sense.vision"}, {"node": "n4", "op": "cw.op.heat", "by": "human", "verifiedBy": "human"}, {"node": "n5", "op": "cw.op.saute", "by": "h…

Código

Ejecutar: python scenarios/out/043-resume-with-a-stale-seq-is-refused-with-412/python.py

# Scenario 043: A resume with a stale seq is answered with 412 Precondition Failed
# Changes to an execution carry If-Match with the seq the caller last saw. If the run moved on, the hub refuses with a Problem and tells the caller the current seq, and nothing changes.
# 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: Start a shakshuka run with a person present.
try:
    started = c.start_execution({"core": "0.2.0", "kind": "ExecuteRequest", "id": "ex-043-stale", "recipe": "cw:cookwala.ai:example-shakshuka", "recipeHash": "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841", "servings": 4, "requestedBy": "agent:planner-1", "idempotencyKey": "ex-043-stale-a"}, "ex-043-stale-key", True)
except CookwalaProblem as p:
    started = p.body  # a refusal is a result, not a crash
    print("refused:", p.refusal)
assert pick(started, "state") == "accepted", pick(started, "state")
print("ok", "state", "=", "accepted")

# Step 2: The arm cuts; the run waits for a person at seq 6.
import time; time.sleep(4)

# Step 3: Resume with seq 1, a value from long ago. The answer is a Problem: title precondition-failed, and the detail names the current seq.
try:
    stale = c.resume_execution("ex-043-stale", 1)
except CookwalaProblem as p:
    stale = p.body  # a refusal is a result, not a crash
    print("refused:", p.refusal)
assert pick(stale, "title") == "precondition-failed", pick(stale, "title")
print("ok", "title", "=", "precondition-failed")
assert pick(stale, "detail") == "seq is 6", pick(stale, "detail")
print("ok", "detail", "=", "seq is 6")

# Step 4: The run did not change: still waiting at seq 6.
status = c.get_execution("ex-043-stale")
assert pick(status, "state") == "needs_human", pick(status, "state")
print("ok", "state", "=", "needs_human")
assert pick(status, "seq") == 6, pick(status, "seq")
print("ok", "seq", "=", 6)

# Step 5: Resume again with the seq just read; this time the hub accepts.
try:
    resumed = c.resume_execution("ex-043-stale", pick(status, "seq"))
except CookwalaProblem as p:
    resumed = p.body  # a refusal is a result, not a crash
    print("refused:", p.refusal)
assert pick(resumed, "state") == "running", pick(resumed, "state")
print("ok", "state", "=", "running")
assert pick(resumed, "seq") == 7, pick(resumed, "seq")
print("ok", "seq", "=", 7)

print("scenario complete")

scenarios/043-resume-with-a-stale-seq-is-refused-with-412.json

Anterior: 042 Siguiente: 044