Planned work for Minigraf, a bi-temporal Datalog database. v2.x has known data-integrity issues (pinned list); v3.0.0 fixes them (v3.0.0 milestone).
Philosophy: Embedded graph memory for agents, mobile, and the browser — built on the SQLite approach: be boring, be reliable, be embeddable.
Ordinary avg is implemented for v3.0.0 (#777), matching window numeric filtering. Ordinary sum and extrema also use window-compatible value handling (#789); their existing empty-group conventions are preserved.
Completed releases and their implementation details live in the CHANGELOG. This document tracks planned, exploratory, and other future work.
File format stays v7. Two new error codes (STG-045, WAL-007); no other API changes. Details are in the CHANGELOG.
Shipped:
- A file shorter than one page is refused with
STG-045unless it is a torn first write (#506) - A commit made after a torn WAL entry is no longer lost on recovery; a failed append makes later writes
WAL-007until reopen or checkpoint (#513) - Backported from v3.0.0: rule queries count only derived facts against
max_results(#528),checkpoint()removes an all-checkpointed WAL (#457),usize::MAXcheckpoint threshold skips the close-time save (#476) - Weekly 10M-fact soak test (#392) and per-file coverage gates that actually gate (#396)
File format stays v7 and the public API does not change. Details are in the CHANGELOG.
Shipped:
BrowserDbreturns an error instead of leaving an unreadable dirty page out of the IndexedDB flush (#470)- Golden-file compatibility corpus for format v7 (#391) and a SIGKILL crash test that checks the committed data (#384)
First v2.x patch under the support policy. File format stays v7 and the public API does not change. Details are in the CHANGELOG.
Shipped:
- Indexed queries return correct results with more than 65,535 uncheckpointed facts (#445)
- The transaction counter no longer rewinds when reopening next to an already-checkpointed WAL (#447)
Further v2.x releases are data-integrity and security patches only.
The v2.x line (main) ends with a patch. File format stays v7 and the public API does not change. All new feature work has moved to v3.x. Work list: v3.0.0 milestone. Details are in the CHANGELOG.
Shipped:
- Bound-entity point queries narrowed to the queried attribute (#323 mitigation), exact attribute-scan bounds (#381), and checkpoints that copy untouched index leaves (#315 mitigation)
- fsync the parent directory after creating the
.graph/WAL file and after deleting the WAL (#389) or-joinreturns an empty result, not INT-031, when the clauses before it match no rows (#405)- A clear user error for
$slotqueries run throughexecute(), not internal INT-025 (#407) - Recursive rules with a literal start that recurse through another rule no longer fail under magic sets (#297)
- The INT-054 negative-cycle error names its predicates in a stable order (#410)
- Benchmark regression alerting that is not silently muted (#393)
- CI: MSRV build, public-API semver check, dependency policy (#395)
- Stability and support policy, including how long v2.x gets data-integrity fixes after v3.0.0 (#397)
- Known-issues process: data-integrity issues stay open until the fix is in a published release (#399)
- Support tiers for language bindings and platforms (#400)
Known issue on all v2.x releases: same-transaction multi-valued facts read back as one value (#371, #287). The fix needs format v8 and ships in v3.0.0. Workaround: write or retract each value of a multi-valued attribute in its own transact/retract call. All v2.x known issues are listed in the pinned issue #421.
After v3.0.0 ships, v2.x gets data-integrity and security fixes for 12 months (support policy).
v3.0.0 supports file format v8 only. Fixing #287/#371 (EavtKey/AevtKey carry no value bytes, so same-transaction multi-values collapse) bumps the format from v7 to v8. Support for v1–v6 is dropped at the same time. The v1–v6 → v7 auto-migration code in persistent_facts.rs can be removed when cutting this release; a v7→v8 migration replaces it. Any database opened at least once under a v1.x release will already be on v7; there are no known users on older formats. Every other layout change this release needs goes into v8 too, so there is no separate v9.
This was the GitHub milestone named “2.0” before v2.0.0 used that version number for the kernel-locking and structured-error-code breaking changes; it was consequently renumbered to v3.0.0. It is developed on the long-lived v3 branch; every v3 pull request targets v3 and main is left untouched. At the first v3 release the current main is copied to a v2 branch and v3 becomes the new main.
Scope — format and storage integrity:
-
v8 index keys with value bytes and v7→v8 migration (#371, #287; done on
v3) -
Kani proofs that the format-v8 scalar key decoders invert their encoders and reject bounded malformed inputs without panicking (#693; targeted before v3.0.0, with no format change)
-
v8 storage format, done on
v3in five PRs (#374, #434, #388, #433):- per-page CRC32, page id and generation on every page (#388);
- two alternating meta pages; crash-atomic, copy-on-write checkpoints whose cost follows the change (#374, #434);
- covering byte-comparable keys with a dictionary and deduplicated long values, at about 142 bytes per fact (#433).
The format is frozen for the golden-file corpus (#391).
-
Index
verifyand publicrebuild_indexes()(#373; done onv3) -
Net-assert on v8 keys before resolving facts, for O(live) point queries (#379; done on
v3) -
One current valid-time window per
(e, a, v): a later assertion replaces the earlier window, and two windows of one fact in onetransactareAPI-011(#435; done onv3) -
Empty or inverted valid-time windows rejected with
API-019on every write path (#436; done onv3) -
In a
WriteTransaction, the last statement that writes a fact decides it, so reads inside the transaction match the commit;LogWriterkeeps the same rules, withAPI-020for an assertion and retraction of one fact in one transaction (#477; done onv3) -
btree_pagefuzz target that reaches node decoding (#375; done onv3with #387: page targets rebuilt on real v8 files)
Scope — query API:
-
Align ordinary aggregate value handling with windows (#789; implemented for v3.0.0): sum/sum-distinct skip nonnumbers; min/max accept all non-nil types and mixed numbers. Preserve empty-query and all-nil min/max group-drop behavior. Ordinary avg is #777; ordered-group expansion remains #781.
-
Selective in-memory ancestor joins avoid building a whole-attribute value map (#723; implemented for eligible three-pattern recursive bodies). Populated committed generations keep AVET probes; general in-memory value joins remain unchanged.
-
Optional
:distinctresult tuples, preserving bag output by default (#493; done onv3) -
lagandleadwindow functions for the previous/next row within an ordered partition (#182; v3.0.0) -
Bounded sliding row frames,
:rows N preceding, for window aggregates (#183; v3.0.0, implemented onv3; builds on #182). #836 audit evidence retains the 110 wider cases as manual/deferred; it does not expand nightly coverage. -
Cursor API:
Minigraf::queryandPreparedQuery::queryreturn an ownedCursorthat delivers rows in batches, with the answer fixed at open (#432; Rust API done onv3). The binding cursor objects follow. Behind the cursor, the streaming engine (#473) runs every query and produces rows as they are pulled: patterns, expressions and pseudo-attributes (#544), joins and:db/valid-at(#545),notandor(#546), aggregates and windows (#547), rules (#548), and transaction reads (#550); the executor's query path is gone (#551; done onv3). Operators do not spill: memory follows operator state, with an opt-in per-query limit (QRY-012, #549). -
Projected UUID lookup batches walk dense DICT ranges (#530; implemented on
v3), keeping temporary memory bounded to a batch and query-cache charges in projection order. -
Native
:limit N,:order-byand:offset, with a top-kSortwhose memory followsN(#306; done onv3, with the 10M-fact acceptance tests moved from #473; specdocs/superpowers/specs/2026-10-24-limit-order-by-design.md). #641 targets v3.0.0 in three slices: snapshot-bound order proofs and Sort omission for committed scalar ranges, then one-group count/count-distinct and partition-at-a-time windows. All three slices are implemented in the dependent #641 PRs forv3. #781 extends ordered grouping to sum/avg, fixed-scalar min/max, Integer/nil sum-distinct, and effective tuples reducible to one ordered Integer/Boolean slot plus scalar constants/repetitions, including hidden:withkeys. Every:withSort is retained. Unproved keys/arguments, callbacks,collect-set, rules and aggregate+window queries retain full-input evaluation. Compatible built-in windows with one proved nonconstant partition key hold one partition and bounded batches; unpartitioned/incompatible windows, aggregate queries, callbacks andcollect-setretain full-input evaluation. Seedocs/superpowers/specs/2026-10-28-641-input-order-design.mdanddocs/superpowers/specs/2026-10-29-781-ordered-aggregate-design.md. -
Fact-log iterator:
Minigraf::fact_log(&FactFilter)streams every fact version (assertions and retractions, withtx_count,tx_idand valid-time bounds) in transaction order or storage order, filtered by attribute, attribute prefix, entity and tx range, with memory bounded by a window rather than the database (#430; Rust API done onv3). An open log pins its generation: checkpoints run but do not reuse its pages (#543;API-013deprecated). A raw log writer (#431) completes the extract-transform-load path; the binding objects follow with #462. -
Read-only open:
OpenOptions::read_onlytakes a shared lock, so several readers can hold a file at once, writes nothing to the file or its WAL (a WAL is applied in memory, a v7 file is read without migrating), and refuses writes withAPI-014(#429; Rust API done onv3). The bindings'open_with_optionsfollows. -
Raw log writer:
LogWriterbuilds a new database fromFactRecords with theirtx_count,tx_id, valid-time windows and assertion flags kept, gaps allowed (a purged transaction stays a hole for:as-of), at<path>.partialrenamed into place byfinish(#431; Rust API done onv3). Completes extract (#429, #430), transform, load. The binding objects follow.
Scope — features:
- Query profiler (#185)
- Set values
#{...}, the set predicatesin?/not-in?/any-in?/all-in?/none-in?andcollect-set(#316; done onv3: core,BrowserDbset binds, and set values in Python, Java, Android, Swift, Node.js, C and the browser/WASI packages on theirv3branches) - Prepared queries in every binding (#181; done on
v3: core,BrowserDb.prepare, and Python, Java, Android, Swift, Node.js and C on theirv3branches) - UDF registration in every binding: the UniFFI ones, C, Node and the browser
BrowserDb(#180; core done onv3: fallible UDFs,QRY-014, registry snapshot and re-entry; the bindings follow) OpenOptionsexposed over UniFFI throughopen_with_options, so embedders can suppress the close-time checkpoint (#322; done onv3in every binding with #465)
Scope — release-gate testing and documentation:
- Deterministic storage schedules (#760; v3.0.0): suite and findings cover batch visibility, snapshot cutoffs and checkpoint publication. Focused native/MSRV PR checks and larger nightly/manual searches retain replay artifacts; the workflow-only main companion enables the v3 nightly schedule. Whole-database simulation remains out of scope.
- Golden-file compatibility corpus for every format version (#391; done on
v3: six v7 files from 2.0.3 checked after migration, seven v8 files frozen at23aa57c) - SIGKILL crash test that checks committed data through EAVT, AEVT and full scan (#384; done on
v3:tests/crash_kill_test.rs, 100 rounds per OS nightly) - Model-based test of transact/retract/checkpoint/reopen (#385; done on
v3:tests/model_based_test.rs, 30,000 cases nightly) - Wider property tests: file-backed, batched, retracts, temporal, joins (#386)
- Persistent fuzz corpus and an operation-sequence target (#387; done on
v3: corpus caching, weeklycmin, the v8 page targets, and theops_sequenceandcorrupt_filetargets) - Fault injection across save/WAL/recovery (#390)
- Long-running soak test at scale (#392; done on
v3:tests/soak_test.rs, 10M facts and a 5-hour churn weekly, alternatingmainandv3) - Benchmarks at 100K–10M facts and a published performance envelope (#394; on
v3:examples/perf_envelope.rs, results indocs/BENCHMARKS.md; follow-ups #528–#532, with #532 superseded by #551's removal of repeated committed-fact net-assert; audit and measurements) - Acceptance evidence for memory/scaling (#837): named-test and Linux RSS gates, nonlinear/multistratum recursion and custom chunk bounds; validation status
- Regression-history/reporting evidence (#840) is complete: all 453 numeric comparisons, preserved independent v3 history, scheduled routes and live notification/cleanup evidence. Separate main/v3 fixes #853/#854 are merged; raw evidence is retained beyond artifact expiry.
- Nightly runtime expansion (#858): add Java/JVM and WASI; qualify Swift/iOS simulator and Android emulator timings against physical-device workloads before enabling representative mobile lanes. The platform restriction from #839 applies only to that manual evidence task.
- Manual migration evidence (#838): native v7-to-v8 through 200k facts; Chromium/Firefox/WebKit through 20k facts, with separate read-only/reopen controls and bounded post-migration cursor churn. Results, frozen fixtures and exclusions; no new nightly route or sustained-pin bound.
- Semantic permutations (#842): correctness-target map and control limits for X07. The exclusion stands; no nightly route or acceptance gate is added.
- Failure paths (#843): correctness-target map for X08. The exclusion stands; the heavy WAL replay test gap is #865.
- Manual shape evidence (#841): native shape sweeps, recursive controls and sustained-reader experiments, with explicit input, timeout and attribution limits. The small-nightly recommendation is separate from execution; this audit adds no nightly route or release-acceptance gate.
- Expression keyword/ref equality (#774; on
v3): resolve literal keywords once per operator, with memory bounded by the expression. Measurements and regression coverage; performance-only, with no v2.x backport. - Higher storage/WAL coverage gates and branch-coverage gates (#396; on
v3: line and branch floors per file incoverage-thresholds.toml, checked on every PR) - Durability, recovery and operations guide (#398; done on
v3:docs/DURABILITY.md, also on the docs site; found #535)
Additive features, kept out of v3.0.0 so the #371 fix is not held up by them:
- Rule persistence — design first (#241)
- OPFS-backed persistent mode for
BrowserDb: a sync access handle in a dedicated Worker, no in-memory buffer; IndexedDB stays the main-thread backend. Starts at Tier 2 inside the Tier 1 browser binding until it has shipped (#441) - Temporal graph-traversal research (#273; may result in documentation or a blog post rather than code)
Goal: Improve developer experience and grow the ecosystem without expanding the core unnecessarily.
- 🎯 Database inspector/debugger (separate repository: minigraf-inspector)
- 🎯 Query profiler (#185, v3.0.0)
- 🎯 Time-travel visualizer (separate repository: minigraf-visualizer)
Tracked in: minigraf-examples.
- 🎯 GraphRAG example that combines Minigraf with a vector store
- 🎯 LangChain / LangChain.js integration example
- 🎯 LlamaIndex integration example
- 🎯 Annotated end-to-end scenarios for agent memory, offline-first mobile, and audit logs
- 🎯 Graph algorithms as a separate crate
- 🎯 Optional schema validation
- 🎯 Import/export tools
- 🎯 Backup utilities
Allow a Minigraf database to be forked into an independent .graph file pre-populated with facts from the parent at a given transaction count.
Potential uses include speculative writes, snapshot distribution, test isolation, and agent sandboxing. The design must preserve the single-file, zero-configuration model; a fork should behave as an independent file copy with a fully flushed WAL.
Status: Exploratory. Pursue only with demonstrated user demand.
Magic-sets rewriting is not applied to mixed rules containing not/not-join; those use full semi-naive evaluation. Extending it to negation is well studied but requires care to avoid unsound propagation across stratification boundaries.
Pursue only if profiling demonstrates a real bottleneck in negation-heavy recursive workloads.
Future performance work will be driven by production profiles and reproducible benchmarks. The benchmark documentation records measurements; completed performance work is recorded in the CHANGELOG.
When evaluating features, ask:
- Does it align with the philosophy? (embedded, reliable, simple, bi-temporal)
- Is it needed for target use cases? (audit, event sourcing, knowledge graphs)
- Does it compromise reliability? (stability over features)
- Can it be a separate crate? (keep the core small)
Say no to:
- Distributed consensus
- Multi-datacenter replication
- Built-in ML/AI
- Features useful only at massive scale
- Complex configuration
- Breaking the single-file philosophy
Say yes to:
- Crash safety
- Data integrity
- Temporal queries
- Query performance
- Developer experience
- Cross-platform support
- v2.x maintenance on
main: data-integrity and security fixes only - v3.0.0 (format v8 and data integrity) on the
v3branch, then v3.next features - Production readiness for high-stakes deployments, tracked in the v3.0.0 milestone
- Ecosystem work tracked in
minigraf-examples - Developer tools tracked in
minigraf-inspectorandminigraf-visualizer
See GitHub Issues for specific tasks, and the CHANGELOG for completed releases.
Last updated: October 2026 — v2.0.5, a data-integrity patch on v2.x, is the current release; v3.0.0 work, including the file-format v8 fix for #287/#371, is developed on the long-lived v3 branch and merges into main at the v3.0.0 cut.