You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Integers beyond the RFC 8785 safe range (±(2**53-1)) are now rejected — at canonicalization, at RowLimit/SpendCap/CallLimit construction, and by wire.load (as malformed) — instead of
silently colliding with a neighbouring integer once rendered through binary64. A tenth reject
vector, reject_unsafe_integer.json, brings the interop suite to 20.
evidence.verify_bundle and AuditLog.verify_anchor now check the bundle/anchor schema version
and chain identity instead of ignoring them, so a bundle for the wrong version or the wrong chain
no longer verifies.
Added
AuditLog.append now raises CommittedAuditError (carrying the committed entry) if persisting
an entry fails after it was already committed to the in-memory chain — the file write or a sink
raising no longer looks the same as nothing having been recorded. The entry stays committed;
callers must not retry the call that produced it on the strength of this error alone.
Execution binding, opt-in per chain via Guard.issue(..., schema_version=2) (schema version 1
is unchanged and remains the default): check()/record_denial() now allocate a call_id
(fail-closed, with meters restored, if the CSPRNG fails) and return it on Decision.call_id; check() gains authorized_params/capture/adapter and refuses further calls once the node
is complete()d (ReasonCode.NODE_FINALIZED). Guard.record_outcome(call_id, body_state, ...)
binds what a body-owning wrapper observed afterwards — returned/raised/abandoned/deferred,
with error_code required exactly when raised. On a schema_version=2 chain, complete()
returns a bool-coercible CompletionResult and refuses while calls are pending; on schema_version=1 it still returns a plain bool, byte-and-type identical to every release
before 0.9.0. revoke()/revoke_agent() snapshot still-pending call_ids onto the kill entry
as pending_at_kill — atomically, under one hold of the chain lock, together with the
revocation itself and (in check()) with complete()'s own check-pending-then-append sequence
— without clearing them, so a late record_outcome() after a kill is still accepted. Every
pre-commit check()/record_outcome() failure (not only CSPRNG exhaustion) rolls back its
meters/bookkeeping. Arguments are committed via params_c14n_v1 (attenu_guard.params): SHA-256(raw_salt || JCS(params)), never the raw value — closing, for this profile only, the
one gap the shared JCS canonicalizer leaves open for out-of-range integral floats, without
changing that canonicalizer's own behaviour elsewhere. evidence.verify_bundle gains execution_binding: per-call observed/unobserved/unaccounted (an outcome counts as observed
only once it is bound correctly — right node, right order), per-node
finalized/in_progress/revoked_with_pending, an aggregate clean/incomplete/failed, and params_coverage (computed over every call, not only those with an outcome) as its own axis — not applicable for a schema-version-1 bundle. verify_bundle also rejects a rootless bundle
and accepts an optional independently retained expected_anchor/expected_head, so a rewritten
bundle whose own (self-consistent) anchor cannot be relied on is still caught. The LangGraph
adapter (adapters.langgraph) is the reference wiring: guard_node/DelegatedToolNode call record_outcome on a schema_version=2 guard, sync and async, from an immutable
pre-invocation argument snapshot (a callable that mutates its own inputs cannot cause a false
params mismatch), with generators/futures reported deferred and asyncio.CancelledError
reported abandoned. Schema and verifier are event- and version-aware and strict: a v2 allow
REQUIRES capture/adapter (Guard.check() supplies pre_hook_only plus a guard-attributed
adapter when the caller passes neither — a bare check() IS itself pre_hook_only observation,
never merely absent), deny FORBIDS every allow-only field, and a v1 entry FORBIDS every
v2-only field (including call_id — v1 never allocates one); tests/test_execution_binding.py
runs in CI. A language-neutral params_c14n_v1 parity vector file
(tests/vectors/params_c14n/params_c14n_v1.json, consumed by tests/test_params_c14n_vectors.py)
covers its accepted/rejected numeric boundaries and salt handling; the TypeScript consumer of
this same file is being built on attenu-guard-ts (feat/090-execution-binding) — parity
between the two is a release gate for 0.9.0, not deferred work.
Changed
Behaviour change: constructing an AuditLog (or Guard.issue) with a path/audit_path that
already names a non-empty file now raises FileExistsError instead of silently truncating it.
Pass overwrite=True (Guard.issue(..., audit_overwrite=True)) to keep the old reset-on-open
behaviour where that is what you want.