Jump to content

The Principia Machina/TPC-PY/gen volunteer corpus.py

From The Invariance Itself
Revision as of 15:43, 27 September 2026 by BFP Rauch (talk | contribs) (TPC-PY source gen_volunteer_corpus.py (0.1.0), SHA-256 15e5e42ff579bd15; conformed TPS 0.3.0; PROPOSED, not declared (CO-8))
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
"""gen_volunteer_corpus 0.1.0: builds volunteer_corpus.json for
tpc_volunteer 0.1.0. Deterministic (fixed r values). Expectations are
computed by tpc_volunteer.py and pinned; hand_checked cases carry a closed
form checked here first: the bucket's refill arithmetic, RETRY AFTER as
u x min(cap, base x 2^attempt) with u from hashlib over the D97
netstring, INTAKE's EWMA, p_nw at the declared knees (0 at INTAKE MIN,
INTAKE MAX_P at INTAKE MAX, 1 above), p_v = lo + u x (hi - lo), the
VOLUNTEER SHARE quota's closed form, and the draw's order on inputs that
force each branch (a token bucket at 0; a retry in force; INTAKE above
MAX; a p_v of 1 through the bounds; an empty queue)."""
import hashlib
import json
import math
import os
import sys

here = os.path.dirname(os.path.abspath(__file__))
sys.path.insert(0, here)
from params_fixture import FLEET, fleet  # noqa: E402
from tpc_volunteer import run_case  # noqa: E402
from tpc_beta import canon_float  # noqa: E402

cases = []
P = FLEET
R1 = "0123456789abcdef0123456789abcdef"
R2 = "ffffffffffffffffffffffffffffffff"
R3 = "00000000000000000000000000000000"
R4 = "deadbeefdeadbeefdeadbeefdeadbeef"
R5 = "0f1e2d3c4b5a69788796a5b4c3d2e1f0"
RS = [R1, R2, R3, R4, R5]


def add(name, fn, args, hand=False, check=None):
    got = run_case(fn, args)
    if check is not None:
        assert got == check, (name, got, check)
    cases.append({"name": name, "fn": fn, "args": args, "hand_checked": hand,
                  "expected": got})


def ind_uniform(tag, r, counter):
    fields = [tag, "1", r, str(counter)]
    ns = b"".join(str(len(f.encode("utf-8"))).encode() + b":" + f.encode("utf-8") for f in fields)
    return int(hashlib.sha256(ns).hexdigest()[:13], 16) / 4503599627370496.0


# ---- good_standing ---------------------------------------------------------
def facts(cc=True, dis=True, st="GREEN", reset=False, susp=False, tier=False):
    return {"certificate_current": cc, "dis_match": dis, "state": st, "reset_in_force": reset,
            "suspended": susp, "in_tier": tier}


add("G01 in good standing", "standing", {"facts": facts(), "params": P}, True,
    "true|GOOD STANDING")
add("G02 certificate lapsed", "standing", {"facts": facts(cc=False), "params": P}, True,
    "false|CERTIFICATE")
add("G03 D(IS) mismatch", "standing", {"facts": facts(dis=False), "params": P}, True,
    "false|D(IS)")
add("G04 YELLOW", "standing", {"facts": facts(st="YELLOW"), "params": P}, True, "false|STATE")
add("G05 ORANGE", "standing", {"facts": facts(st="ORANGE"), "params": P}, True, "false|STATE")
add("G06 SUSPENDED state", "standing", {"facts": facts(st="SUSPENDED"), "params": P}, True,
    "false|STATE")
add("G07 a reset in force", "standing", {"facts": facts(reset=True), "params": P}, True,
    "false|RESET")
add("G08 suspended (CR6) while GREEN", "standing", {"facts": facts(susp=True), "params": P}, True,
    "false|SUSPENDED")
add("G09 VOLUNTEER TIER TRUST TIER, not in the tier", "standing",
    {"facts": facts(), "params": fleet({"VOLUNTEER TIER": "TRUST TIER"})}, True, "false|TIER")
add("G10 VOLUNTEER TIER TRUST TIER, in the tier", "standing",
    {"facts": facts(tier=True), "params": fleet({"VOLUNTEER TIER": "TRUST TIER"})}, True,
    "true|GOOD STANDING")
add("G11 ANY GREEN ignores the tier", "standing", {"facts": facts(tier=False), "params": P},
    True, "true|GOOD STANDING")
add("G12 the first failing clause is the reason", "standing",
    {"facts": facts(cc=False, dis=False, st="ORANGE", reset=True, susp=True), "params": P}, True,
    "false|CERTIFICATE")
add("G13 D(IS) before STATE", "standing", {"facts": facts(dis=False, st="ORANGE"), "params": P},
    True, "false|D(IS)")
add("G14 STATE before RESET", "standing", {"facts": facts(st="YELLOW", reset=True), "params": P},
    True, "false|STATE")
add("G15 RESET before SUSPENDED", "standing", {"facts": facts(reset=True, susp=True),
                                               "params": P}, True, "false|RESET")
add("G16 SUSPENDED before TIER", "standing",
    {"facts": facts(susp=True), "params": fleet({"VOLUNTEER TIER": "TRUST TIER"})}, True,
    "false|SUSPENDED")
add("G17 state unknown refused", "standing", {"facts": facts(st="RED"), "params": P}, True,
    "ERROR:facts")
add("G18 flag not boolean refused", "standing", {"facts": facts(cc=1), "params": P}, True,
    "ERROR:facts")
add("G19 VOLUNTEER TIER unknown refused", "standing",
    {"facts": facts(), "params": fleet({"VOLUNTEER TIER": "TOP"})}, True,
    "ERROR:params:VOLUNTEER TIER")
add("G20 missing key refused", "standing",
    {"facts": {k: v for k, v in facts().items() if k != "in_tier"}, "params": P}, True,
    "ERROR:facts")
add("G21 extra key refused", "standing", {"facts": dict(facts(), x=1), "params": P}, True,
    "ERROR:facts")

# ---- bucket_refill / bucket_take -------------------------------------------
def bk(t, u):
    return {"tokens": t, "updated": u}


add("B01 no time passes", "refill", {"bucket": bk(1.5, 1000), "now": 1000, "params": P}, True,
    "1.5|1000")
add("B02 half an hour adds one token", "refill", {"bucket": bk(0, 1000), "now": 2800,
                                                  "params": P}, True, "1|2800")
add("B03 an hour adds two", "refill", {"bucket": bk(0.5, 0), "now": 3600, "params": P}, True,
    "2.5|3600")
add("B04 capped at VOLUNTEER BURST", "refill", {"bucket": bk(2, 0), "now": 36000, "params": P},
    True, "3|36000")
add("B05 full stays full", "refill", {"bucket": bk(3, 0), "now": 1, "params": P}, True, "3|1")
add("B06 one second", "refill", {"bucket": bk(0, 0), "now": 1, "params": P}, True,
    canon_float(2 * 1 / 3600.0) + "|1")
add("B07 RATE 60 per hour", "refill", {"bucket": bk(0, 0), "now": 60,
                                       "params": fleet({"VOLUNTEER RATE": 60})}, True, "1|60")
add("B08 BURST 1", "refill", {"bucket": bk(0, 0), "now": 36000,
                              "params": fleet({"VOLUNTEER BURST": 1})}, True, "1|36000")
for i in range(8):
    add("B%02d sweep %d s" % (9 + i, 700 * i), "refill", {"bucket": bk(0.25 * (i % 4), 5000),
                                                         "now": 5000 + 700 * i, "params": P}, False)
add("B17 now before updated refused", "refill", {"bucket": bk(1, 1000), "now": 999, "params": P},
    True, "ERROR:now")
add("B18 negative tokens refused", "refill", {"bucket": bk(-1, 0), "now": 1, "params": P}, True,
    "ERROR:bucket")
add("B19 RATE 0 refused", "refill", {"bucket": bk(0, 0), "now": 1,
                                     "params": fleet({"VOLUNTEER RATE": 0})}, True,
    "ERROR:params:VOLUNTEER RATE")
add("B20 BURST 0 refused", "refill", {"bucket": bk(0, 0), "now": 1,
                                      "params": fleet({"VOLUNTEER BURST": 0})}, True,
    "ERROR:params:VOLUNTEER BURST")

add("K01 take from a full bucket", "take", {"bucket": bk(3, 5)}, True, "true|2|5")
add("K02 take the last token", "take", {"bucket": bk(1, 5)}, True, "true|0|5")
add("K03 0.999 tokens: no token", "take", {"bucket": bk(0.999, 5)}, True, "false|0.999|5")
add("K04 empty", "take", {"bucket": bk(0, 5)}, True, "false|0|5")
add("K05 fractional above 1", "take", {"bucket": bk(1.75, 5)}, True, "true|0.75|5")
for i in range(12):
    add("K%02d sweep %s" % (6 + i, 0.3 * i), "take", {"bucket": bk(0.3 * i, i)}, False)
add("K18 tokens boolean refused", "take", {"bucket": bk(True, 5)}, True, "ERROR:bucket")
add("K19 updated negative refused", "take", {"bucket": bk(1, -1)}, True, "ERROR:bucket")
add("K20 missing key refused", "take", {"bucket": {"tokens": 1}}, True, "ERROR:bucket")

# ---- retry_after -----------------------------------------------------------
def check_retry(attempt, r, base=300, cap=14400):
    bound = cap if attempt >= 1024 else min(cap, base * 2.0 ** attempt)
    return canon_float(ind_uniform("TPC-VOLUNTEER-RETRY", r, 0) * bound)


n = 0
for attempt in (0, 1, 2, 3, 5, 6, 10, 2000):
    for r in RS[:2]:
        n += 1
        add("A%02d attempt %d r %s" % (n, attempt, r[:4]), "retry",
            {"attempt": attempt, "r": r, "params": P}, True, check_retry(attempt, r))
add("A17 base 60 cap 3600", "retry",
    {"attempt": 4, "r": R3, "params": fleet({"BACKOFF BASE": 60, "BACKOFF CAP": 3600})}, True,
    check_retry(4, R3, 60, 3600))
add("A18 attempt 1023 (the last exact power)", "retry", {"attempt": 1023, "r": R4, "params": P},
    True, check_retry(1023, R4))
add("A19 attempt 1024 (the cap)", "retry", {"attempt": 1024, "r": R4, "params": P}, True,
    check_retry(1024, R4))
add("A20 attempt negative refused", "retry", {"attempt": -1, "r": R1, "params": P}, True,
    "ERROR:attempt")
add("A21 cap below base refused", "retry",
    {"attempt": 0, "r": R1, "params": fleet({"BACKOFF CAP": 100})}, True,
    "ERROR:params:BACKOFF CAP")
add("A22 base below 1 refused", "retry",
    {"attempt": 0, "r": R1, "params": fleet({"BACKOFF BASE": 0.5})}, True,
    "ERROR:params:BACKOFF BASE")
add("A23 r malformed refused", "retry", {"attempt": 0, "r": "x", "params": P}, True, "ERROR:r")

# ---- intake_update / p_nw --------------------------------------------------
add("I01 from 0 with share 1: w_q", "intake", {"intake": 0, "volunteer_blocks": 10,
                                                "landed_blocks": 10, "params": P}, True, "0.2")
add("I02 from 0.5 with share 0: 0.4", "intake", {"intake": 0.5, "volunteer_blocks": 0,
                                                 "landed_blocks": 50, "params": P}, True, "0.4")
add("I03 no landed blocks: share 0", "intake", {"intake": 0.5, "volunteer_blocks": 0,
                                                "landed_blocks": 0, "params": P}, True, "0.4")
add("I04 steady state", "intake", {"intake": 0.3, "volunteer_blocks": 15, "landed_blocks": 50,
                                   "params": P}, True, canon_float(0.8 * 0.3 + 0.2 * 0.3))
add("I05 w_q 1: the share itself", "intake",
    {"intake": 0.9, "volunteer_blocks": 1, "landed_blocks": 4, "params": fleet({"INTAKE w_q": 1})},
    True, "0.25")
add("I06 w_q 0: unchanged", "intake",
    {"intake": 0.9, "volunteer_blocks": 1, "landed_blocks": 4, "params": fleet({"INTAKE w_q": 0})},
    True, "0.9")
for i in range(12):
    add("I%02d sweep" % (7 + i), "intake", {"intake": 0.08 * i, "volunteer_blocks": i,
                                            "landed_blocks": 11 + i, "params": P}, False)
add("I19 volunteer above landed refused", "intake", {"intake": 0, "volunteer_blocks": 5,
                                                     "landed_blocks": 4, "params": P}, True,
    "ERROR:blocks")
add("I20 intake above 1 refused", "intake", {"intake": 1.1, "volunteer_blocks": 0,
                                             "landed_blocks": 4, "params": P}, True,
    "ERROR:intake")
add("I21 w_q above 1 refused", "intake",
    {"intake": 0, "volunteer_blocks": 0, "landed_blocks": 4, "params": fleet({"INTAKE w_q": 2})},
    True, "ERROR:params:INTAKE w_q")

add("N01 at INTAKE MIN: 0", "p_nw", {"intake": 0.25, "params": P}, True, "0")
add("N02 below MIN: 0", "p_nw", {"intake": 0.1, "params": P}, True, "0")
add("N03 at INTAKE MAX: MAX_P", "p_nw", {"intake": 0.5, "params": P}, True, "0.9")
add("N04 above MAX: 1", "p_nw", {"intake": 0.51, "params": P}, True, "1")
add("N05 midway: 0.45", "p_nw", {"intake": 0.375, "params": P}, True, "0.45")
add("N06 intake 0", "p_nw", {"intake": 0, "params": P}, True, "0")
add("N07 intake 1", "p_nw", {"intake": 1, "params": P}, True, "1")
add("N08 MAX_P 0.5", "p_nw", {"intake": 0.5, "params": fleet({"INTAKE MAX_P": 0.5})}, True,
    "0.5")
add("N09 MIN 0 MAX 1: linear everywhere", "p_nw",
    {"intake": 0.3, "params": fleet({"INTAKE MIN": 0, "INTAKE MAX": 1})}, True,
    canon_float(0.3 * 0.9))
for i in range(9):
    add("N%02d sweep %s" % (10 + i, 0.05 + 0.1 * i), "p_nw", {"intake": 0.05 + 0.1 * i,
                                                              "params": P}, False)
add("N19 MAX at or below MIN refused", "p_nw",
    {"intake": 0.3, "params": fleet({"INTAKE MAX": 0.25})}, True, "ERROR:params:INTAKE MAX")
add("N20 intake negative refused", "p_nw", {"intake": -0.1, "params": P}, True, "ERROR:intake")
add("N21 MAX_P above 1 refused", "p_nw", {"intake": 0.3, "params": fleet({"INTAKE MAX_P": 1.5})},
    True, "ERROR:params:INTAKE MAX_P")

# ---- pv_step ---------------------------------------------------------------
def check_pv(state, r, lo=0.25, hi=0.5, rot=0.1):
    pv = state
    started = False
    if pv is None or ind_uniform("TPC-PV-ROTATE", r, 0) < rot:
        pv = lo + ind_uniform("TPC-PV-DRAW", r, 0) * (hi - lo)
        started = True
    return canon_float(pv) + "|" + ("true" if started else "false")


n = 0
for r in RS:
    n += 1
    add("V%02d first draw r %s" % (n, r[:4]), "pv", {"state": {"p_v": None}, "r": r, "params": P},
        True, check_pv(None, r))
for r in RS:
    n += 1
    add("V%02d rotation check r %s" % (n, r[:4]), "pv", {"state": {"p_v": 0.3}, "r": r,
                                                        "params": P}, True, check_pv(0.3, r))
add("V11 rotation 1: every call redraws", "pv",
    {"state": {"p_v": 0.3}, "r": R1, "params": fleet({"P_V ROTATION": 1})}, True,
    check_pv(0.3, R1, rot=1))
add("V12 rotation 0: never", "pv", {"state": {"p_v": 0.3}, "r": R1,
                                    "params": fleet({"P_V ROTATION": 0})}, True, "0.3|false")
add("V13 bounds 0.1..0.2", "pv",
    {"state": {"p_v": None}, "r": R2, "params": fleet({"P_V BOUNDS": [0.1, 0.2]})}, True,
    check_pv(None, R2, 0.1, 0.2))
add("V14 degenerate bounds", "pv",
    {"state": {"p_v": None}, "r": R2, "params": fleet({"P_V BOUNDS": [0.4, 0.4]})}, True,
    "0.4|true")
add("V15 bound 0 refused (interior endpoints)", "pv",
    {"state": {"p_v": None}, "r": R2, "params": fleet({"P_V BOUNDS": [0, 0.5]})}, True,
    "ERROR:params:P_V BOUNDS")
add("V16 bound 1 refused", "pv",
    {"state": {"p_v": None}, "r": R2, "params": fleet({"P_V BOUNDS": [0.5, 1]})}, True,
    "ERROR:params:P_V BOUNDS")
add("V17 lo above hi refused", "pv",
    {"state": {"p_v": None}, "r": R2, "params": fleet({"P_V BOUNDS": [0.5, 0.25]})}, True,
    "ERROR:params:P_V BOUNDS")
add("V18 p_v above 1 refused", "pv", {"state": {"p_v": 1.5}, "r": R2, "params": P}, True,
    "ERROR:state")
add("V19 state malformed refused", "pv", {"state": {}, "r": R2, "params": P}, True,
    "ERROR:state")
add("V20 r malformed refused", "pv", {"state": {"p_v": None}, "r": "", "params": P}, True,
    "ERROR:r")

# ---- volunteer_share -------------------------------------------------------
def ve(v, w):
    return {"volunteer": v, "weight": w}


add("S01 empty window: v_max", "share", {"window": [], "g": 1, "v_max": 0.25}, True,
    "0.25|0|0|0.25")
add("S02 assigned work only: full admission", "share",
    {"window": [ve(False, 3)], "g": 1, "v_max": 0.25}, True, "1|3|0|1")
add("S03 volunteer weight at the cap: zero", "share",
    {"window": [ve(True, 1), ve(False, 2)], "g": 1, "v_max": 0.25}, True, "0|3|1|0")
add("S04 partial", "share", {"window": [ve(True, 0.5), ve(False, 3)], "g": 1, "v_max": 0.25},
    True, canon_float(0.25 * 4.5 - 0.5) + "|3.5|0.5|" + canon_float(0.25 * 4.5 - 0.5))
add("S05 v_max 0.5", "share", {"window": [], "g": 1, "v_max": 0.5}, True, "0.5|0|0|0.5")
add("S06 g 2", "share", {"window": [ve(False, 6)], "g": 2, "v_max": 0.25}, True, "1|6|0|2")
for i in range(10):
    add("S%02d sweep" % (7 + i), "share",
        {"window": [ve(j % 2 == 0, 0.3 * j) for j in range(i + 1)], "g": 1, "v_max": 0.25}, False)
add("S17 v_max 1 refused", "share", {"window": [], "g": 1, "v_max": 1}, True, "ERROR:v_max")
add("S18 g 0 refused", "share", {"window": [], "g": 0, "v_max": 0.25}, True, "ERROR:g")
add("S19 volunteer flag not boolean refused", "share",
    {"window": [{"volunteer": 1, "weight": 1}], "g": 1, "v_max": 0.25}, True, "ERROR:window")
add("S20 window not a list refused", "share", {"window": {}, "g": 1, "v_max": 0.25}, True,
    "ERROR:window")

# ---- volunteer_draw --------------------------------------------------------
def vs(tokens=3, updated=1000, retry=None, attempt=0, refusals=0, period=0, closed=None,
       intake=0.0, pv=None):
    return {"bucket": bk(tokens, updated), "retry_until": retry, "attempt": attempt,
            "refusals": refusals, "period": period, "closed_until": closed, "intake": intake,
            "p_v": {"p_v": pv}}


def vc(now=1000, block=5, period=0, item=True, falcon=False, own=False, k=1 / 3, st=None):
    return {"now": now, "block": block, "period": period, "has_item": item, "has_falcon": falcon,
            "has_own_record": own, "ratio_k": k, "standing": st or facts()}


def dr(state, call, r=R1, params=P):
    return {"state": state, "call": call, "r": r, "params": params}


add("D01 a legitimate item: WORK ITEM (p_v not hit with r 0123)", "draw", dr(vs(), vc()), True)
add("D02 not in good standing: REFUSED STANDING, not counted", "draw",
    dr(vs(), vc(st=facts(st="YELLOW"))), True)
add("D03 the channel closed: REFUSED CLOSED, not counted", "draw",
    dr(vs(closed=100), vc(block=50)), True)
add("D04 the channel reopens at closed_until", "draw", dr(vs(closed=100), vc(block=100)), True)
add("D05 inside RETRY AFTER: REFUSED EARLY, counted", "draw",
    dr(vs(retry=1500.0), vc(now=1000)), True)
add("D06 at retry_until the call is accepted", "draw", dr(vs(retry=1000.0), vc(now=1000)), True)
add("D07 no token: REFUSED TOKEN, counted", "draw", dr(vs(tokens=0), vc()), True)
add("D08 a token accrues with time", "draw", dr(vs(tokens=0, updated=0), vc(now=3600)), True)
add("D09 the fourth refusal closes the channel (past EARLY CALL LIMIT)", "draw",
    dr(vs(tokens=0, refusals=3), vc()), True)
add("D10 the third refusal does not close it", "draw", dr(vs(tokens=0, refusals=2), vc()), True)
add("D11 a new RATING PERIOD restarts the refusal count", "draw",
    dr(vs(tokens=0, refusals=3, period=0), vc(period=1)), True)
add("D12 an assigned falcon: FALCON outside the p_v law", "draw", dr(vs(), vc(falcon=True)),
    True)
add("D13 INTAKE above MAX: NO WORK THROTTLE, attempt + 1", "draw",
    dr(vs(intake=0.9, attempt=2), vc()), True)
add("D14 INTAKE at MAX: p_nw 0.9", "draw", dr(vs(intake=0.5), vc()), True)
add("D15 p_v forced through the bounds: WORK PROBE", "draw",
    dr(vs(pv=0.999), vc(), R1, fleet({"P_V BOUNDS": [0.999, 0.999], "P_V ROTATION": 0})), True)
add("D16 an empty queue with no probe: NO WORK EMPTY", "draw",
    dr(vs(pv=0.25), vc(item=False), R1, fleet({"P_V ROTATION": 0})), True)
add("D17 an empty queue, probe forced: every WORK answer is a probe", "draw",
    dr(vs(pv=0.999), vc(item=False), R1, fleet({"P_V BOUNDS": [0.999, 0.999], "P_V ROTATION": 0})),
    True)
add("D18 a probe with k 1000: a parrot", "draw",
    dr(vs(pv=0.999), vc(k=1000, own=True), R1, fleet({"P_V BOUNDS": [0.999, 0.999], "P_V ROTATION": 0})),
    True)
add("D19 a parrot with CONSISTENCY SHARE 1 and an own record", "draw",
    dr(vs(pv=0.999), vc(k=1000, own=True), R1,
       fleet({"P_V BOUNDS": [0.999, 0.999], "P_V ROTATION": 0, "CONSISTENCY SHARE": 1})), True)
add("D20 a parrot with CONSISTENCY SHARE 1 and no own record", "draw",
    dr(vs(pv=0.999), vc(k=1000, own=False), R1,
       fleet({"P_V BOUNDS": [0.999, 0.999], "P_V ROTATION": 0, "CONSISTENCY SHARE": 1})), True)
add("D21 a probe with a tiny k: a canary", "draw",
    dr(vs(pv=0.999), vc(k=0.000001), R1, fleet({"P_V BOUNDS": [0.999, 0.999], "P_V ROTATION": 0})),
    True)
add("D22 WORK resets the attempt", "draw", dr(vs(attempt=7), vc()), True)
add("D23 a refusal keeps the attempt and the later retry_until", "draw",
    dr(vs(tokens=0, attempt=3, retry=900.0), vc()), True)
add("D24 a refusal with a retry in force beyond the new one", "draw",
    dr(vs(tokens=0, attempt=0, retry=99999.0), vc(now=1000)), True)
for i, r in enumerate(RS[1:]):
    add("D%02d sweep r %s" % (25 + i, r[:4]), "draw",
        dr(vs(tokens=2.5, intake=0.3, pv=0.4, attempt=i), vc(item=i % 2 == 0, own=True), r), False)
add("D29 period before the state's refused", "draw", dr(vs(period=3), vc(period=2)), True,
    "ERROR:call")
add("D30 ratio_k 0 refused", "draw", dr(vs(), vc(k=0)), True, "ERROR:call")
add("D31 state missing a key refused", "draw",
    {"state": {k: v for k, v in vs().items() if k != "intake"}, "call": vc(), "r": R1,
     "params": P}, True, "ERROR:state")
add("D32 retry_until negative refused", "draw", dr(vs(retry=-1.0), vc()), True, "ERROR:state")
add("D33 standing facts malformed refused", "draw", dr(vs(), vc(st={"state": "GREEN"})), True,
    "ERROR:facts")
add("D34 EARLY CALL LIMIT 0: the first refusal closes", "draw",
    dr(vs(tokens=0), vc(), R1, fleet({"EARLY CALL LIMIT": 0})), True)
add("D35 VOLUNTEER COOL-OFF 10", "draw",
    dr(vs(tokens=0, refusals=3), vc(block=40), R1, fleet({"VOLUNTEER COOL-OFF": 10})), True)
add("D36 TRUST TIER narrows GOOD STANDING", "draw",
    dr(vs(), vc(), R1, fleet({"VOLUNTEER TIER": "TRUST TIER"})), True)

names = [c["name"] for c in cases]
assert len(names) == len(set(names)), "duplicate case names"
counts = {}
for c in cases:
    counts[c["fn"]] = counts.get(c["fn"], 0) + 1
for fn, m in sorted(counts.items()):
    assert m >= 20, (fn, m)
out = {"corpus": "volunteer_corpus", "version": "0.1.0", "module": "tpc_volunteer 0.1.0",
       "params_fixture": "PMP-TPM 0.12.0 fleet defaults (params_fixture.FLEET)",
       "counts": counts, "cases": cases}
with open(os.path.join(here, "volunteer_corpus.json"), "w", encoding="utf-8") as f:
    json.dump(out, f, indent=1, ensure_ascii=False)
    f.write("\n")
print("volunteer_corpus.json", len(cases), "cases", counts)