v0.8.5 — opt-in LayerContract (assume/guarantee) block on attest receipts
Thursday cut on top of v0.8.4. Patch bump — one optional field on the existing receipt schema + one new CLI flag. Schema version stays at v1 (the field is additive; legacy receipts deserialise unchanged). No breaking changes.
Honest framing (called out before the code)
The 2026-05-21 prompt asked for guarantees to be "populated automatically from the policy outcomes already tracked this window (deny-by-default hits, ghost-arg strips, PII masks, validation failures)". That counter store does NOT exist in agent-airlock — cost_tracking.py counts tokens, circuit_breaker.py counts per-tool failures, audit.py emits one-way, anomaly.py is a per-event detector, but none aggregate policy outcomes by category over a window.
v0.8.5 ships the derived path instead: per-guard pass_rate is computed from the verdicts: list[ReceiptVerdict] the operator already supplies to build_receipt. Same source of truth as today's receipts; zero new infrastructure.
ADD
# agent_airlock.attest.LayerContract
{
"guarantees": [
{"name": "EvalRCEGuard", "pass_rate": 0.9943, "sample_size": 351},
{"name": "GhostArgFilter", "pass_rate": 1.0, "sample_size": 412},
{"name": "PIIMasker", "pass_rate": 0.0, "sample_size": 17}
],
"assumes": ["upstream.tls.tlsv1.3", "upstream.dpop.bound"]
}Guarantee(frozen) —name,pass_rate(validated to[0.0, 1.0]),sample_size(verifiers weight low-N appropriately)LayerContract(frozen) —guarantees: tuple[Guarantee, ...](sorted by name for canonical-payload stability) +assumes: tuple[str, ...](free-form upstream-guarantee IDs)derive_contract_from_verdicts(verdicts, *, assumes=())— pure functionReceipt.contract: LayerContract | None = Noneoptional fieldbuild_receipt(..., contract=...)new kwargreceipt_from_dicttolerates the new field; legacy receipts deserialise withcontract=None
CLI
airlock attest receipt emit \
--policy-bundle-hash "$BUNDLE_SHA" \
--inputs-hash "$INPUTS_SHA" \
--model-id claude-opus-4-7 \
--verdicts-json verdicts.json \
--key-file ~/.airlock/keys/test.bin \
--keyid test-key \
--contract \
--assumes upstream.tls.tlsv1.3,upstream.dpop.bound \
--output receipt.json--contract (default off, opt-in) embeds the derived block. --assumes requires --contract.
Anchor
arXiv:2605.18672 — "assume-guarantee layer contract" framing. Cited once; no claims about the paper's specific content beyond adopting the terminology.
Honest scope
pass_rateis a measured statistic over the sample, not a proof. A 1.0 over 3 samples ≠ a 1.0 over 30,000 — hencesample_sizeon every Guarantee.- The signature attests the operator's declaration of verdicts, not the verdicts' truth. Garbage-in, garbage-out.
assumesstrings are free-form — agent-airlock doesn't interpret them.
Stats
- 2,510 tests · 83.42% coverage (gate 82%)
- CI 7/7 green
- Receipt schema version unchanged at v1 (additive field)
Install
pip install agent-airlock==0.8.5