Cookwala / حزمة التطوير والسيناريوهات / 044

Retrying a start with the same Idempotency-Key never starts a second pot

Networks drop answers. An agent that retries POST /v1/executions with the same Idempotency-Key gets the execution already started, in its current state, instead of a second run of the same recipe.

الفئة: agent builder · الهدف: روبوتات في خدمة الناس · المستوى: beginner

اقرأ أولًا: /docs/CORE/ /docs/HUB/

الخطوات

  1. Start a shakshuka run with the key ex-044-idem-key. startExecution

  2. The run advances to the first step that needs a person.

  3. Send the identical request with the identical key, as a client would after a lost answer. The hub returns the same execution at its current state, not a new one. startExecution

  4. Reading the status confirms there is one execution and it is the one both answers described. getExecution

  5. The retry carried the plan of the original start.

النتيجة

You can retry a start safely. Next: scenario 050 shows what happens when the key differs but the execution id repeats.

سطر الأوامر

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 @-
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 @-

المخرجات المسجلة

الحالة: ok

[1] startExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "ex-044-idem", "seq": 0, "state": "accepted", "request": "ex-044-idem", "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": "hu…
[3] startExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "ex-044-idem", "seq": 6, "state": "needs_human", "request": "ex-044-idem", "updatedAt": "2026-10-07T00:54:29Z", "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": …
[4] getExecution: {"core": "0.2.0", "kind": "ExecutionStatus", "id": "ex-044-idem", "seq": 6, "state": "needs_human", "request": "ex-044-idem", "updatedAt": "2026-10-07T00:54:29Z", "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": …

الكود

التشغيل: python scenarios/out/044-idempotent-retry-returns-the-same-execution/python.py

# Scenario 044: Retrying a start with the same Idempotency-Key never starts a second pot
# Networks drop answers. An agent that retries POST /v1/executions with the same Idempotency-Key gets the execution already started, in its current state, instead of a second run of the same recipe.
# 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 the key ex-044-idem-key.
try:
    first = c.start_execution({"core": "0.2.0", "kind": "ExecuteRequest", "id": "ex-044-idem", "recipe": "cw:cookwala.ai:example-shakshuka", "recipeHash": "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841", "servings": 4, "requestedBy": "agent:planner-1", "idempotencyKey": "ex-044-idem-a"}, "ex-044-idem-key", True)
except CookwalaProblem as p:
    first = p.body  # a refusal is a result, not a crash
    print("refused:", p.refusal)
assert pick(first, "state") == "accepted", pick(first, "state")
print("ok", "state", "=", "accepted")
assert pick(first, "seq") == 0, pick(first, "seq")
print("ok", "seq", "=", 0)
assert pick(first, "id") == "ex-044-idem", pick(first, "id")
print("ok", "id", "=", "ex-044-idem")

# Step 2: The run advances to the first step that needs a person.
import time; time.sleep(4)

# Step 3: Send the identical request with the identical key, as a client would after a lost answer. The hub returns the same execution at its current state, not a new one.
try:
    retry = c.start_execution({"core": "0.2.0", "kind": "ExecuteRequest", "id": "ex-044-idem", "recipe": "cw:cookwala.ai:example-shakshuka", "recipeHash": "sha256:2ea6fab93cc673fc6784f9bc9fe401d2387bb7779dc537df97286b73807c8841", "servings": 4, "requestedBy": "agent:planner-1", "idempotencyKey": "ex-044-idem-a"}, "ex-044-idem-key", True)
except CookwalaProblem as p:
    retry = p.body  # a refusal is a result, not a crash
    print("refused:", p.refusal)
assert pick(retry, "id") == "ex-044-idem", pick(retry, "id")
print("ok", "id", "=", "ex-044-idem")
assert pick(retry, "state") == "needs_human", pick(retry, "state")
print("ok", "state", "=", "needs_human")
assert pick(retry, "seq") == 6, pick(retry, "seq")
print("ok", "seq", "=", 6)

# Step 4: Reading the status confirms there is one execution and it is the one both answers described.
status = c.get_execution("ex-044-idem")
assert pick(status, "seq") == 6, pick(status, "seq")
print("ok", "seq", "=", 6)
assert pick(status, "state") == "needs_human", pick(status, "state")
print("ok", "state", "=", "needs_human")

# Step 5: The retry carried the plan of the original start.
assert pick(retry, "x-hub-plan.length") == 13, pick(retry, "x-hub-plan.length")
print("ok", "x-hub-plan.length", "=", 13)

print("scenario complete")

scenarios/044-idempotent-retry-returns-the-same-execution.json

السابق: 043 التالي: 045