Skip to content

Release v0.16.0

Choose a tag to compare

@github-actions github-actions released this 09 Oct 12:51
· 504 commits to main since this release
Immutable release. Only release title and notes can be modified.

What changed in 0.16.0 — cost and correctness

A rejection is not free. A failed conditional write is charged 1 WCU (measured on
DynamoDB). Earlier docs said a fast rejection cost nothing; every cost table is corrected.

Repeat 429s now cost nothing. A new client-side rejection cache answers a repeat rejection
in memory for up to rejection_cache_ttl (default 1 s): no DynamoDB call. It also steers writes
away from shards known to be empty, which fixes an under-admission on sharded entities. It only
ever rejects; every admission is still decided by DynamoDB. Client cost at 20x over-demand:
$0.672 → $0.079 per million requests. Turn it off with rejection_cache_ttl=0.

Cascade per resource. One entity can now cascade to its parent for some resources and not
others (for example, per-model limits roll up to the org, a shared budget does not). Set it with
the API, the CLI (resource|entity set-cascade), a limits manifest or CloudFormation. With no
policy set, nothing changes.

Fixes: a refund above capacity is trimmed and no longer makes later rejections expensive;
parent buckets carry the parent's own cascade setting; a child's lease never reaches the
grandparent; a failed adjustment never refunds the original debit; a manifest rejects disabled
on system; a no-wait 429 retry loop no longer freezes other asyncio tasks.

Before upgrading: run zae-limiter upgrade for the Lambdas. Custom RepositoryProtocol
backends must implement the new cascade-policy methods. Upgrade all clients together if you use
three-level hierarchies. Remove System.Disabled from hand-written Custom::ZaeLimiterLimits
resources. Setting a cascade policy locks out clients older than 0.16.0. Honour
retry_after_seconds: a client that retries a 429 at once now spins on CPU.

A "refill from cache" shortcut was merged during development and withdrawn before release; the
changelog below lists both, and it is not in 0.16.0. Details:
performance guide,
ADR-146,
ADR-147.


[0.16.0] - 2026-10-09

Features

  • schema: Store a tri-state cascade policy on config items (cf80124)
  • repository: Resolve the cascade policy from the disabled walk's read (40bd4f8)
  • limiter: Cascade per resource on the slow path and stamp the policy (007a9c4)
  • limiter: Decide warm-path cascade per resource; the item overrules (a1b45af)
  • repository: Fan a cascade-policy change out to existing buckets (54301d1)
  • repository: Gate storing a cascade policy on the stack's version (2b15b5a)
  • repository: Set and clear a cascade policy (b1dd2f9)
  • cli: Set and clear a per-resource cascade policy (d3e67ad)
  • provisioner: Parse a cascade policy in the limits manifest (ae29e99)
  • provisioner: Apply a manifest cascade policy, gated and change-aware (bc07feb)
  • provisioner: Restamp buckets when a manifest changes a cascade policy (091cad1)
  • provisioner: Round-trip a Cascade property through CloudFormation (1461ca4)
  • cli: Warn when limits plan cascades a resource with no entity limits (b30bbf2)

Bug Fixes

  • lease: Trim a credit above capacity before the fast path spends it (598c410)
  • aggregator: Keep the fast path shut over a quota above its ceiling (300ae55)
  • lease: Never let the post-credit check undo a committed lease (fcb5c4a)
  • aggregator: Pin a vu re-stamp on every quota staying within its ceiling (b748a30)
  • limiter: Stamp a parent's bucket with the parent's own cascade (bc41cf3)
  • limiter: Keep a child's cold cascade off the grandparent (09bb5be)
  • lease: Never refund the initial consumption after an adjustment commit (7679b4a)
  • limiter: Hand a cascading shard's retry to the slow path (4e65a35)
  • limiter: Judge a failed warm child by its own cascade stamp (e8443fe)
  • provisioner: Retry a failed cascade fan-out on the next apply (1a35da1)
  • cli: Don't warn about cascade when an entity-wide default covers it (82e9cfa)
  • docs: A fast rejection costs 1 WCU, not 0 (d547304)
  • limiter: Clear the rejection cache after admin writes too (bcb150f)
  • limiter: Forget a refunded shard once the refund lands (5dea363)
  • limiter: Never reject a cascading child from the cache (fa9ae23)
  • docs: Correct the fast-rejection prices the first pass missed (688826a)
  • limiter: A disabled child never gets a parent-driven 429 (3ace831)
  • limiter: Bound the parent check by the rejection-cache TTL (f10febc)
  • limiter: Any disabled parent shard keeps a cascading child's 403 (ef06ca4)
  • limiter: Pass avoid_parent_shards only when non-empty (fcb67d9)
  • provisioner: Reject disabled on system in a limits manifest (3545ad1)
  • limiter: Never refill from the cache past a cascade it cannot see (148b5a1)
  • limiter: Pin each cached balance on the refill-from-cache write (7fa70d1)
  • limiter: Pin shard_count on the refill-from-cache write (9922e5d)
  • limiter: Refill from the cache only when config shows no schedule (1dd6946)
  • limiter: Trust "no parent" only from an existing entity record (d9bb0f7)
  • limiter: Re-check config at least once a window before refilling from the cache (7f230e5)
  • lease: Record what the slow path wrote in the rejection cache (866f72c)
  • repository: Floor every refill-from-cache debit at any sign (7cd4cdc)
  • repository: Pin each limit's settings on the refill-from-cache write (e26f745)
  • lease: Do not record a written state a refund overtook (868f2c3)
  • lease: Keep a vu stamp a limit change made during the slow pass (b2ac154)
  • test: Delete the speculative-consume AWS stack, not just its table (c19a62d)
  • repository: Read a just-written cascade policy consistently in the fan-out (15587b3)
  • repository: Add consistent_read to the protocol's get_entity and resolve_access (d067663)
  • repository: Keep rejections fast after a credit above capacity (1f30514)
  • limiter: Hand control back on a local rejection (45cce6e)

Documentation

  • claude: Add the post-credit clamp to the writer table (07361eb)
  • lease: State when a credit above the ceiling is trimmed (251bc4b)
  • lease: Name every case the ceiling check does not cover (8a84ae4)
  • lease: Name the round trip between a credit and its clamp marker (46ad48a)
  • claude: Record the owner stamp on bucket items and its cost (eaf4fda)
  • claude: Note the parent cache fill and the non-cascading parent write (4087593)
  • lease: Record that a failed adjustment commit is never rolled back (d3da67c)
  • adr: Propose ADR-146, per-resource cascade policy (c75a06d)
  • adr: Record what the cascade-policy core settled (9b62280)
  • cli: Document set-cascade and clear-cascade (ce06f4d)
  • cli: Name the right remedy for a refused cascade policy (5e8add6)
  • Document the per-resource cascade policy (a0df0b6)
  • provisioner: Word the CloudFormation boolean error for Cascade too (0027d47)
  • adr: Add ADR-147, cache rejections from the last bucket state seen (42dc17e)
  • adr: ADR-147 bounds, not rules out, a doubling the cache missed (0fa0aea)
  • Document the rejection cache (ADR-147) (ed0c544)
  • adr: Record the cascade parent pre-check in ADR-147 (b36d3e4)
  • Document the cascade parent pre-check (ADR-147 phase 2) (5ce0bf3)
  • plans: Plan rejecting disabled on system in a manifest (5bababd)
  • Say a manifest rejects disabled on system (48c9c0d)
  • Rewrap the #693 line in CLAUDE.md's disable section (6d7b406)
  • adr: Record refill from the cached state in ADR-147 (9761ed3)
  • List every pin of the refill-from-cache write, and require new writers to move one (4c85065)
  • Say the rejection cache admits only through a pinned write, and list the floor (7fd870d)
  • Record that refill-from-cache can credit less than the slow path near capacity (d55625a)
  • Record why ADR-147 phase 3 was withdrawn, and what it would need (ef64383)
  • Tell callers to honour retry_after_seconds now that 429s can be local (d8fb8ba)
  • Correct v0.16.0 cost claims and list the rejection cache's known behaviour (5e49583)
  • adr: Accept ADR-146 and ADR-147 for v0.16.0 (dd95ef8)

Performance

  • limiter: Reject from the last bucket state seen (ADR-147) (ec49263)
  • limiter: Index the rejection cache by bucket (bd63b9f)
  • limiter: Check the cascade parent's cached state before writing (4a052f1)
  • limiter: Refill from the cached state in one locked write (38a7396)
  • limiter: Let refill-from-cache start from a state one slow-pass window old (329812a)

Refactoring

  • repository: Teach the warm path what a cascade fan-out decided (c852c7e)

Testing

  • lease: Pin that a credit above capacity is never spent (3e55691)
  • lease: Cover an overshoot from a fast-path lease (82793af)
  • limiter: Pin that a middle entity keeps cascading to its parent (b4c412d)
  • limiter: Forward shard_id through the speculative test doubles (70dcd5f)
  • lease: Pin that a failed adjustment commit refunds nothing (2ce41ed)
  • lease: Prove the ceiling check is skipped without per-item results (dc86145)
  • e2e: Add multi-stack isolation tests on LocalStack (81de955)
  • cli: Pin that a partial cascade fan-out tells the operator to re-run (bcb5d60)
  • e2e: Run the multi-stack isolation tests on real AWS too (59c2f35)
  • limiter: Make the rejection-cache tests prove the cache (15c47f6)
  • limiter: Pin the shard steering over 20 draws (1186322)
  • limiter: Cover quota resets, windows, sync and admin clears (6e3505b)
  • limiter: Cover the rejection-cache options and guards (d635cfc)
  • infra: Wait for limit-config writes before resolving limits (33a8285)
  • infra: Fail teardown when a stack delete fails (b1f383e)
  • infra: Make eventually() fail on timeout (8854ad2)
  • infra: Keep a live handle on B while A is upgraded (b7324a4)
  • infra: Refuse "real AWS" unless every endpoint is real (f7417b8)
  • limiter: Prove the parent pre-check makes no call, sync too (53ec54d)
  • limiter: Pin the rejection cache's clock in the phase-3 tests (f0ddcda)
  • limiter: Make the phase-3 exclusion tests reach their checks (0f83e2d)
  • limiter: Prove the disabled and TTL pins of the refill-from-cache write (7980c9b)
  • limiter: Cover the slow-pass cap, an uncached config level and a missing entity (9c5ee12)
  • lease: Cover recording written states on a backend without the cache (add778e)

Miscellaneous Tasks

CI

  • Re-trigger CodeQL after the GitHub Actions outage (ccc3c4d)
  • Run CodeQL on the retargeted PR (5ade945)