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.
Read first: /docs/HUMANITARIAN-PROFILE/ /humanitarian/
Steps
The basic WHO/Codex pack validates as a profile document.
validateIts content hash.
hashThe care pack's content hash.
hashThe 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.jsonRecorded 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")