v1.0.20 (VEE-6): torn-ledger defense + admission fail-open #51
Loading…
Reference in a new issue
No description provided.
Delete branch "oss-oo/pinky-vee6-torn-ledger"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Cross-lane defect fix VEE-6 (reported by Brain-B's budget-consumer validation, observatory v1.0.35-era 51-check parity run):
budgets.window_entriescrashed on torn ledger records — non-dict entries inside a valid JSON array inusage-ledger.json. Every budgets reader shares that read, and the unwrapped admission call meant a torn usage ledger did not just lose accounting — it halted every dispatch at admission.The fix (design contract, binding)
budgets._read_entriesfilters to dict entries — the one choke point every reader shares (window_entries,root_of,usage_summary,check, the rollup payload builders): torn entries behave as if absent._append_prunedheal-on-write — non-dicts are filtered from the incoming entries first, so the next recording drops torn entries from the file permanently.dispatch.pyadmission fail-open —budgets.check(and itslane_forargument) wrapped: on ANY exceptionBUDGET_DECISIONgoes inert ({"enabled": false, "soft": null, "hard": null, "error": …}) with a loud stderr NOTE (failing OPEN, the dispatch proceeds). A guardrail crash must never block a dispatch — the budgets module docstring already promised "a guardrail only ever acts on numbers it could honestly read"; the code now matches the promise. Documented indocs/BUDGETS.md§Admission fail-open.ledger_io.drop_torn_items— shared-seam hardening for every stream-family ledger:_read_with_salvage(locked reads) andstream.load_json(plain sweep reads) drop non-dict items from list ledgers, one stderr NOTE per filtered read naming the dropped count (no MQTT — a persistently torn file would spam the salvage topic; the file heals on its next write). Parse-tear salvage semantics (backup, notice,default_factory) unchanged.Suite legs (podman shim first — zero real invocations; rig config dirs, fake app-server, never the live broker/units)
--usage-summary+ retained rollups recompute honestly (14 + 29 = 43 rig-proven).UnicodeDecodeError, beyond the fail-safe read'sOSError/JSONDecodeErrorcatches) — the dispatch proceeds with the NOTE even under a would-refuse hard limit, result JSON free of budget verdicts.stream-pending.jsondoes not crash a rig sweep (pre-fix:AttributeErrorin the oldest-first sort); the sweep's writes heal the file; the valid expired record is honestly dead-lettered.Docs (same-round rule)
docs/BUDGETS.md: torn-record defense + admission fail-open subsections.docs/USAGE.md: ledger-family torn-record filtering line (filtered on read, noted on stderr, healed on next write).No new upstream assumptions:
zcode-cli/app-server surfaces untouched; usage numbers still come from the sameturn_usage/session/usagereads the result JSON already reports.MERGE HOLD: the CC round's independent validation (valcc) runs against main @
6f79f00— merge only on the brain's merge-go steer.