Skip to main content

intervalguard

An agent fetches a fact, does three more things, and then acts on that fact — but the fact changed while the agent was busy. Nothing errors, nothing looks wrong, and the agent confidently commits to a stale belief. intervalguard makes that failure loud: it tracks when each tool call read the world, and raises StaleReadError the moment a decision depends on a read that time or a later write has invalidated.

A TTL alone will not catch this. A read can be well inside its validity window and still be wrong, because something else wrote to the same resource in the meantime. intervalguard checks both.

The failure mode

Delayed verification destabilizing multi-agent belief: a "corrector" agent fetches ground truth, but its fetch is slow and its correction lands later. By then the ground truth has moved, so the correction actively pushes the group toward the wrong consensus — and it does so with the authority of having "checked."

The runnable illustration is demo/debate.py: two debaters, one corrector, and an evidence store that flips mid-flight. The same scenario runs twice — once bare, once instrumented.

Install

pip install -e ".[dev]"

Python 3.12+. Standard library only; pytest is the sole dev dependency.

Usage

from intervalguard import tracked, last_event, StaleReadError

@tracked(name="evidence:reactor_2_status", validity_window_seconds=30, writes=False)
def fetch_status():
    return evidence_store["reactor_2_status"]

@tracked(name="evidence:reactor_2_status", validity_window_seconds=30, writes=True)
def write_status(value):
    evidence_store["reactor_2_status"] = value

@tracked(name="debate:consensus", validity_window_seconds=30, writes=False)
def apply_correction(observed):
    return f"consensus based on {observed}"

status = fetch_status()
read_event = last_event()

write_status("OFFLINE")          # someone else changes the world

try:
    apply_correction(status, depends_on=[read_event.id])
except StaleReadError as exc:
    print(exc)
    # event 'evidence:reactor_2_status' (id=4dcecf80) is stale:
    # superseded by 'evidence:reactor_2_status' (id=190c1b88) at 2026-07-30T18:44:41Z

depends_on is consumed by the decorator and never passed to your function. last_event() returns the Event your most recent tracked call produced, so the next call can declare what it read from. It is context-local, so concurrent callers of the same function each see their own event.

What it checks

A dependency is reported stale when either:

  1. Expirynow - read.end_time exceeds the read's validity_window_seconds.
  2. Supersession — a later write to the same name ended after the read did, meaning the underlying resource changed while the result was in hand. This fires even when the read is still inside its window. Only writes supersede: another agent reading the same resource never invalidates your read.

A declared dependency id that is not in the registry raises UnknownDependencyError rather than being skipped — an unverifiable dependency is not a verified one.

API

  • Eventid, name, start_time, end_time, validity_window_seconds, depends_on, kind ("read" or "write").
  • relation(a, b) -> str — one of before, meets, overlaps, during, after, from the two intervals alone.
  • is_stale(event, now) -> bool
  • check_dependencies(event, all_events, now=None) -> list[str] — names of dependencies that expired or were superseded.
  • tracked(name, validity_window_seconds, writes=False) — decorator; registers an Event per call and raises StaleReadError before returning if any declared dependency is stale.
  • last_event() — the Event produced by the most recent tracked call in the current context.
  • registry / clear_registry() — the in-memory event log.

Run the demo

python demo/debate.py

Run 1 reaches a wrong consensus. Run 2 raises StaleReadError before the wrong correction is applied, naming the read that went stale and when.

Tests

pytest

Scope

v1, built for a demo and an open-source release. In-memory registry, single process, five interval relations rather than the full Allen set. No hosted service, dashboard, or framework integrations.

License

MIT

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

intervalguard-0.1.0.tar.gz (10.1 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

intervalguard-0.1.0-py3-none-any.whl (7.2 kB view details)

Uploaded Python 3

File details

Details for the file intervalguard-0.1.0.tar.gz.

File metadata

  • Download URL: intervalguard-0.1.0.tar.gz
  • Upload date:
  • Size: 10.1 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.10

File hashes

Hashes for intervalguard-0.1.0.tar.gz
Algorithm Hash digest
SHA256 c718e57f03e342b83316e43ec126017b1a978fc519870221d344299613afa4bf
MD5 0038631d271f45e681c9ddbe9024541c
BLAKE2b-256 2eae3daed58e40d8639c7b7f908aa5c566d9d9ecb8b55a5f440226b5ad89a633

See more details on using hashes here.

File details

Details for the file intervalguard-0.1.0-py3-none-any.whl.

File metadata

  • Download URL: intervalguard-0.1.0-py3-none-any.whl
  • Upload date:
  • Size: 7.2 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/7.0.0 CPython/3.12.10

File hashes

Hashes for intervalguard-0.1.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8f77801828374b896cfeb84c57726c01ddc59e0dcecaade10d3d5360c9f1cfee
MD5 9fa4d55f6a7a5b343c7df62a33cf8062
BLAKE2b-256 bd12fa690eef6274a1211155a20f5a6f4dadc05e8f00f3e232b6bb793a551def

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page