Cookwala / SDK and scenarios / 077

Rule packs are documents: validate them, hash them, apply one

A rule pack is a Humanitarian Profile document with an id, a version, a status and reviews. A certifier can validate it, pin it by content hash and see a rule fire on a recorded offer.

Audience: certifier · Goal: Make people healthier, Help end hunger · Level: beginner

Read first: /docs/HUMANITARIAN-PROFILE/ /humanitarian/

Steps

  1. The basic WHO/Codex pack validates as a profile document. validate

  2. Its content hash. hash

  3. The care pack's content hash. hash

  4. The basic pack applied to the disaster-kitchen offer: one block finding. humanitarianCheck

Outcome

You can cite the exact pack a finding came from. Next: scenario 053 reads a device's conformance claim, the other kind of self-declaration.

Command line

cookwala hash profiles/humanitarian/basic-nutrition-food-safety.rulepack.json
cookwala humanitarian --pack profiles/humanitarian/basic-nutrition-food-safety.rulepack.json examples/humanitarian/flows/disaster-kitchen/offer.json

Recorded output

Status: ok

[1] validate: {"ok": true, "errors": []}
[2] hash: {"hash": "sha256:4e1f28a2b381923897c6670a1a2a018b65bc1282caa1f49875708619c60b9608"}
[3] hash: {"hash": "sha256:2654a48b10aa80c58b52f20d253de55ff32e9e3fe1726fd2f1481701b34f2d19"}
[4] humanitarianCheck: {"results": [{"id": "of-2026-11-03-rel-004", "kind": "Offer", "findings": [{"rule": "safety.hot_hold_min", "severity": "block", "detail": "line 3: 52 °C"}]}]}

Code

Run: python scenarios/out/077-rule-packs-are-reviewable-documents/python.py

# Scenario 077: Rule packs are documents: validate them, hash them, apply one
# A rule pack is a Humanitarian Profile document with an id, a version, a status and reviews. A certifier can validate it, pin it by content hash and see a rule fire on a recorded offer.
# 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: The basic WHO/Codex pack validates as a profile document.
basic = c.validate("humanitarian", load("profiles/humanitarian/basic-nutrition-food-safety.rulepack.json"))
assert pick(basic, "ok") == True, pick(basic, "ok")
print("ok", "ok", "=", True)

# Step 2: Its content hash.
basicHash = c.hash(load("profiles/humanitarian/basic-nutrition-food-safety.rulepack.json"))
assert pick(basicHash, "hash") == "sha256:4e1f28a2b381923897c6670a1a2a018b65bc1282caa1f49875708619c60b9608", pick(basicHash, "hash")
print("ok", "hash", "=", "sha256:4e1f28a2b381923897c6670a1a2a018b65bc1282caa1f49875708619c60b9608")

# Step 3: The care pack's content hash.
careHash = c.hash(load("profiles/humanitarian/care-vulnerable-groups.rulepack.json"))
assert pick(careHash, "hash") == "sha256:2654a48b10aa80c58b52f20d253de55ff32e9e3fe1726fd2f1481701b34f2d19", pick(careHash, "hash")
print("ok", "hash", "=", "sha256:2654a48b10aa80c58b52f20d253de55ff32e9e3fe1726fd2f1481701b34f2d19")

# Step 4: The basic pack applied to the disaster-kitchen offer: one block finding.
applied = c.humanitarian_check([{"profile": "0.2.0", "kind": "Offer", "id": "of-2026-11-03-rel-004", "donor": "did:web:wholesaler.example", "createdAt": "2026-11-02T16:00:00+02:00", "items": [{"lineId": "1", "name": "Red lentils", "ingredientId": "cw.ing.lentils_red", "kg": 200, "storage": "ambient", "origin": "wholesale", "dateMark": {"kind": "best_before", "date": "2027-03-01"}, "allergens": []}, {"lineId": "2", "name": "Rice", "ingredientId": "cw.ing.rice_short", "kg": 150, "storage": "ambient", "origin": "wholesale", "dateMark": {"kind": "best_before", "date": "2027-06-01"}, "allergens": []}, {"lineId": "3", "name": "Cooked rice trays", "kg": 18, "storage": "hot_held", "origin": "food_service", "cooked": True, "cookedAt": "2026-11-02T13:30:00+02:00", "allergens": [], "foodClasses": ["cooked_rice"]}], "window": {"from": "2026-11-02T16:30:00+02:00", "to": "2026-11-02T22:00:00+02:00"}, "site": {"country": "EG", "admin1": "Example province", "siteName": "Wholesale depot"}, "readings": [{"at": "2026-11-02T16:25:00+02:00", "tempC": 52, "method": "probe", "lineId": "3"}], "needs": ["same_day"], "state": "distributed", "version": 5}], ["basic-nutrition-food-safety"])
assert pick(applied, "results[0].findings.length") == 1, pick(applied, "results[0].findings.length")
print("ok", "results[0].findings.length", "=", 1)
assert pick(applied, "results[0].findings[0].rule") == "safety.hot_hold_min", pick(applied, "results[0].findings[0].rule")
print("ok", "results[0].findings[0].rule", "=", "safety.hot_hold_min")

print("scenario complete")

scenarios/077-rule-packs-are-reviewable-documents.json

Previous: 076 Next: 078