Skip to content

v0.2.0 — Bi-temporal model + Graph maintenance + Benchmark harness

Latest

Choose a tag to compare

@github-actions github-actions released this 16 May 17:23
· 55 commits to main since this release

What's new

v0.2.0 bundles three feature lines that all landed in this release cycle. Everything is additive — schemaVersion is not bumped, existing graphs load unchanged, and tools called without the new parameters behave exactly as before.


🕰 Bi-temporal model

Every node can now record when the fact was true in the world (occurred_at, since, until) independently of when the node was last written (updated). This unlocks "as-of" queries — "what did we know about the server in March?" — and lets supersedes automatically close the validity interval of the older node so the two never overlap in time.

New fields on every node (all optional ISO YYYY-MM-DD):

  • occurred_at — when the underlying fact happened
  • since / until — half-open validity interval [since, until)

New helpers exported from @litopys/core: resolveOccurredAt, isValidAsOf, eventDateFromId, isIsoDate.

MCP tool changes:

  • litopys_search and litopys_related accept an optional as_of: "YYYY-MM-DD" filter
  • litopys_create accepts occurred_at, since, until
  • litopys_link with relation_type: "supersedes" auto-closes the target's until when unset (returns auto_closed: { node, until } on success, warnings if the target was already tombstoned)

New CLI: litopys check --fix-temporal [--dry-run] — idempotent backfill of occurred_at on existing nodes (event ids with a date prefix get the prefix; everything else falls back to updated).

Docs: docs/temporal-model.md


🧹 litopys evolve — graph maintenance

A new subcommand for keeping an aging graph clean. Two passes, both runnable in one invocation.

--archive-tombstoned [--older-than 365] [--dry-run]
Moves nodes whose until lies more than N days in the past (default 365) into <graph>/archive/<original-subdir>/, preserving structure. Every move is logged as one JSON line in <graph>/archive/manifest.jsonl ({id, archived_at, original_path, until}) so the action is auditable and theoretically reversible. Files under archive/ are never re-scanned, so repeated runs are no-ops. Uses atomic rename(2) per file.

--auto-merge [--min-similarity 0.95] [--dry-run]
Walks the quarantine directory for merge-proposal files, reads each proposal's detectedBy: "similar:<score>" provenance, and accepts every proposal at or above the threshold via the existing acceptMergeProposal code path. Manual proposals (detectedBy: "manual") are never touched. Per-file errors (missing node, type conflict) are captured and reported, not thrown.

Combined: litopys evolve --archive-tombstoned --auto-merge — a single command for a maintenance cron.

Docs: docs/memory-evolution.md


📏 @litopys/bench — benchmark harness

A new workspace package for running Litopys against memory-benchmark datasets and producing a comparable report. Designed so future LongMemEval / LOCOMO adapters plug in without changing the harness.

What it does:

  • Spins up an isolated graph in a temp directory (no pollution of ~/.litopys)
  • Feeds each session through the configured extractor
  • Runs each question through litopys_search + litopys_related, scores against expected node ids
  • Records per-query latency
  • Emits a JSON report and a Markdown summary

Metrics: recall@k, precision@k, mean latency.

Mock extractor ships in the same release (LITOPYS_EXTRACTOR_PROVIDER=mock) — deterministic, network-free, lets the harness (and CI) run without API keys.

New CLI: litopys bench [--dataset <name>] [--limit N] [--output bench-report.json]. Ships with a built-in synthetic dataset of 15 questions.

Sample run on the built-in synthetic:

Provider: mock
Total questions: 15
Recall@5: 0.9778
Precision@5: 0.3200
Mean latency: 4.00ms

The low precision@5 is an artifact of the synthetic dataset — most questions have 1–3 expected ids while k = 5, so empty slots count as misses by the standard convention. The harness exists primarily to enable real industry-dataset adapters as a follow-up.

Docs: docs/benchmark.md


Other changes

  • @litopys/mcp now re-exports the public type aliases (SearchHit, LinkResult, ToolResult, ToolOk, ToolErr) so external packages can type their pipelines without reaching into internal paths.

Tests

  • Baseline (v0.1.4): 481 / 481
  • After this release: 613 / 613 (+132 across new test files)
  • Coverage on new code: archive 97.6%, auto-merge 95.9%, evolve 95.0%, bench package ~95%+

Upgrade notes

  • No migration required. All new fields are optional, all new tools are additive, no public APIs changed.
  • Recommended: after upgrading, run litopys check --fix-temporal once to backfill occurred_at on existing nodes (idempotent, dry-run available).

Install / upgrade

curl -fsSL https://raw.githubusercontent.com/litopys-dev/litopys/main/install.sh | sh

Or pin to this version:

curl -fsSL https://raw.githubusercontent.com/litopys-dev/litopys/main/install.sh | VERSION=v0.2.0 sh

Full changelog: v0.1.4...v0.2.0