AgentStack
SKILL verified MIT Self-run

Bi Temporal Edge

skill-anthonyalcaraz-agentic-graph-rag-skills-bi-temporal-edge · by AnthonyAlcaraz

|

No reviews yet
0 installs
6 views
0.0% view→install

Install

$ agentstack add skill-anthonyalcaraz-agentic-graph-rag-skills-bi-temporal-edge

✓ scanned · ✓ verified — works with Claude Code, Cursor, and more.

Security review

✓ Passed

No issues found. Passed automated security review. · v0.1.0 How review works →

  • Prompt-injection patterns
  • Secret / credential exfiltration
  • Dangerous shell & filesystem operations
  • Untrusted network calls
  • Known-malicious package signatures

What it can access

  • Network access No
  • Filesystem access No
  • Shell / process execution No
  • Environment & secrets No
  • Dynamic code execution No

From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.

Are you the author of Bi Temporal Edge? Claim this listing to set pricing, connect Stripe payouts, and keep 70% of every sale.
Sign up to claim

About

Bi-Temporal Edge

Overview

Agent memory that overwrites facts in place loses the historical trace that makes incident reconstruction possible. The DevOps running example from Ch5/Ch6 makes this concrete: the checkout API had its EC2 instance type changed from t3.large to m5.xlarge on 2026-03-10. The outage occurred 2026-03-15T08:00Z. By the time the post-mortem starts, the configuration store says m5.xlarge — but the agent investigating the outage needs to know what the config was at the time of the outage, not what it is now.

The bi-temporal edge tracks two times per relationship:

  • Validity time (valid_from, valid_until): when the fact was true in

the domain. valid_until=None means currently valid.

  • Ingestion time (ingested_at): when the system learned about the

fact. May be days or weeks after the fact became true.

These two dimensions are independent. A fact can be valid-but-not-yet-known (staging update pushed to prod 2026-03-10 but only logged 2026-03-12), or known-but-no-longer-valid (we recorded it 2026-03-10, invalidated 2026-03-15 when the rollback happened). Both are common in production.

Once edges carry both timestamps, three new query primitives become mechanical: was_valid_at(timestamp) answers point-in-time, history(node) answers full-evolution, ingestion_lag(edge) answers debugging-the-debugger ("was our agent acting on stale data when it made that decision?").

The chapter pairs this with HINDSIGHT's typed-link extension: each edge carries a link_type ({entity, semantic, temporal, causal}) and a weight multiplier for graph traversal. During spreading-activation search, causal and entity links get μ > 1; weak semantic or long-range temporal links get μ ≤ 1. This biases the agent's reasoning toward explanatory connections.

When to Use

Trigger contexts:

  • DevOps incident reconstruction — what was the config at outage time?
  • Audit-grade question — "What did the agent know on 2026-03-15 when it

recommended X?"

  • Regulated environment — compliance evidence needs reproducible point-in-

time queries.

  • Multi-agent memory where Agent A wrote a fact, Agent B needs to know

whether the fact was valid when Agent A wrote it.

Phrases that should invoke this skill: "what was true at", "as-of query", "point-in-time", "bi-temporal", "validfrom / validuntil", "incident reconstruction", "audit trail", "what did we know when".

When NOT to Use

  • Ephemeral cache state. Use a TTL on the cache entry. Bi-temporal is

for facts that persist; cache is for facts that should expire.

  • Append-only event logs. Kafka-style logs already give you full

history; bi-temporal adds nothing. Use bi-temporal when there is a notion of "currently valid" you need to query.

  • Single-point-in-time configs. If the config never changes, a plain

dict is correct. Bi-temporal pays for itself only when validity actually changes.

  • High-frequency mutations (>1Hz per edge). The history would grow

unbounded. Either downsample at ingestion or use a time-series database with TTL.

Process

| Step | Input | Action | Output | Verification | |------|-------|--------|--------|--------------| | 1 | Source node id, target node id, relationship type, optional metadata + link_type + weight | lib.create_edge(source, target, rel_type, **kwargs) | TemporalEdge with valid_from=now, valid_until=None, ingested_at=now | edge.was_valid_at(now) returns True | | 2 | An existing edge + invalidation reason | lib.invalidate(edge, reason) | Same edge with valid_until=now, invalidation_reason=reason | edge.was_valid_at(now+1ms) returns False; edge.invalidation_reason is set | | 3 | An existing edge + new value for the same source/target/rel triple | lib.supersede(old_edge, new_value) | Old edge invalidated; new edge created with valid_from=now | lib.history(source, rel) returns both edges in chronological order | | 4 | Node id + relationship type + timestamp | lib.as_of(source, rel_type, ts, edges) | The edge that was valid at ts (or None) | Returns at most one edge; verify by edge.was_valid_at(ts) | | 5 | Node id | lib.history(node, edges) | All edges sourced from node, sorted by valid_from | Returns full evolution; verify by counting non-overlapping validity windows | | 6 | Edge | lib.ingestion_lag(edge) | timedelta between valid_from and ingested_at | Non-negative; positive lag means the agent learned about it after the fact | | 7 | Edges + query node + traversal weights | lib.weighted_traverse(start, edges, depth=2) | Path-scored neighbors using HINDSIGHT link_type * weight multipliers | Verify causal/entity paths rank above weak-semantic at equal hop count |

Rationalizations

| Agent rationalization | Documented rebuttal | |------------------------|--------------------| | "I'll just overwrite the value — valid_until is overengineering." | Then was_valid_at cannot answer the post-mortem question. The Ch4 anchor — "What did the configuration look like at outage time?" — is unanswerable. The git-for-knowledge discipline in Ch4 names this exact failure mode: silent overwrites that look harmless until the audit. | | "One timestamp is enough — I don't need both valid_from and ingested_at." | The two times are independent. ingested_at is what debugs "the agent acted on stale data." If they collapse to one timestamp, the debugger loses the lag signal entirely. The Ch4 worked example: "If the agent made a decision on Wednesday based on stale data, ingested_at will tell you that the updated information was not yet available." | | "I'll skip invalidation_reason — it's a string, the user can grep the logs." | Logs scatter; the audit trail must live on the edge. The Ch4 worked example: "Instead of a mysterious change in state, you see why the relationship ended: role change, project completion, correction of bad data." This is the difference between an auditable system and an "ask the engineer who quit last year" system. | | "HINDSIGHT link types and weights are research — production should skip them." | HINDSIGHT was published by Latimer et al. and is cited in the Ch4 chapter as the production-cited extension. The default weights (μ=1) are a no-op for systems that don't tune them; the field is opt-in. Skipping it is fine; ripping it out is premature. | | "I'll just query the latest edge and assume the previous ones are wrong." | "Wrong" and "no-longer-valid" are different. Wrong = corrected (use supersede). No-longer-valid = the fact ended naturally (use invalidate with a reason). Conflating them loses the distinction that makes audit possible. | | "Hypergraphs (Example 4-4) cover this — I don't need bi-temporal edges." | Hypergraphs solve N-ary relationships; bi-temporal solves time. They compose. A hyperedge can carry the same valid_from / valid_until / ingested_at fields. They're orthogonal. |

Red Flags

  • ingestion_lag is consistently > 1 day on critical edges. The ingestion

pipeline is too slow; the agent is reasoning on stale data. Surface the lag in the agent's prompt context.

  • history(node) returns overlapping validity windows. The skill is

being used wrong — superseding without invalidating produces ambiguous state. Always invalidate-then-create, never just create.

  • invalidation_reason is empty for most invalidated edges. The audit

trail is degraded. Enforce invalidate(edge, reason) with non-empty reason at the lib boundary.

  • Same valid_from across many edges. Likely a backfill bug; backfill

should preserve original timestamps, not collapse them to ingestion time.

  • as_of(ts) returns multiple edges for the same source/rel pair. Data

corruption — the constraint "at most one valid edge per (source, rel) at any given timestamp" is broken. Run consistency check.

Non-Negotiable Verification

Before shipping a downstream agent built on this skill:

  1. Run the benchmark battery. python cli.py benchmark must report:
  • All point-in-time queries return correct edge state at the timestamp
  • Round-trip serialize/deserialize preserves all timestamps with no drift
  • 1000-edge timeline answers as_of(ts) in < 10ms (in-memory)
  1. Run the DevOps scenario. python cli.py scenario incident-reconstruction

must output the correct config at outage time AND identify which change preceded the outage.

  1. Verify CLI help. python cli.py --help exits 0 and prints this

SKILL.md description (so any harness can discover the skill from --help).

  1. Inspect the audit trail. For each invalidated edge in the scenario,

invalidation_reason is non-empty.

If any verification gate fails, do not ship. The bi-temporal primitive is foundational; downstream skills (HierarchicalMemory, A-MEM EvolvingMemory, Graphiti incremental-update) all depend on this contract.

Security Posture

  • Prompt injection. Edge metadata and invalidation_reason strings are

untrusted input stored verbatim - they are never executed, but a poisoned reason can mislead the humans and agents who later read the audit trail. Treat stored strings as data, not instructions, when rendering history.

  • Data exfiltration. Invalidation is not deletion: sensitive facts persist

in the durable timeline and stay queryable via history() / as_of() long after valid_until. Apply retention/redaction policy to the edge store; the skill itself makes no network calls and writes no files.

  • Privilege escalation. No shell invocation, no eval. The escalation risk

is historical: forged valid_from or backfilled timestamps rewrite the point-in-time record downstream agents trust. Restrict the edge write path and preserve original timestamps on backfill.

Source Attribution

This skill is distilled from Chapter 4 of Agentic GraphRAG (O'Reilly, by Anthony Alcaraz and Sam Julien) — Temporal Awareness section, Example 4-2, and the HINDSIGHT typed-link extension cited in the same section. Production anchor: Graphiti (Zep) bi-temporal model. Research anchor: HINDSIGHT (Latimer et al., 2025).

Source & license

This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.

Install and usage instructions live in the source repository linked above.

Reviews

No reviews yet — be the first.

Versions

  • v0.1.0 Imported from the upstream source.