Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 20 additions & 8 deletions crates/omnigraph-cli/tests/crossversion_upgrade.rs
Original file line number Diff line number Diff line change
Expand Up @@ -538,8 +538,11 @@ fn current_v9_refuses_and_rebuilds_genuine_v5_and_v5_refuses_v9() {
let refusal = output_failure(cli().arg("snapshot").arg(&v5_graph));
let stderr = String::from_utf8_lossy(&refusal.stderr);
assert!(
stderr.contains("0.9.x"),
"v5 refusal must name the release line that wrote internal schema v5, got: {stderr}",
stderr.contains("0.9.0-dev"),
"v5 refusal must name the build that wrote internal schema v5. No published \
release ever stamped it — it existed only in source builds inside the \
0.8.1 -> 0.9.0 development window — so the refusal names `0.9.0-dev`, \
got: {stderr}",
);
assert!(
stderr.contains("export"),
Expand Down Expand Up @@ -681,8 +684,11 @@ fn current_v9_refuses_and_rebuilds_genuine_v6_and_v6_refuses_v9() {
let refusal = output_failure(cli().arg("snapshot").arg(&v6_graph));
let stderr = String::from_utf8_lossy(&refusal.stderr);
assert!(
stderr.contains("0.10.x"),
"v6 refusal must name the release line that wrote internal schema v6, got: {stderr}",
stderr.contains("0.9.0-dev"),
"v6 refusal must name the build that wrote internal schema v6. No published \
release ever stamped it — it existed only in source builds inside the \
0.8.1 -> 0.9.0 development window — so the refusal names `0.9.0-dev`, \
got: {stderr}",
);
assert!(
stderr.contains("export"),
Expand Down Expand Up @@ -771,8 +777,11 @@ fn current_v9_refuses_and_rebuilds_genuine_v7_and_v7_refuses_v9() {
let refusal = output_failure(cli().arg("snapshot").arg(&v7_graph));
let stderr = String::from_utf8_lossy(&refusal.stderr);
assert!(
stderr.contains("0.11.x"),
"v7 refusal must name the release line that wrote internal schema v7, got: {stderr}",
stderr.contains("0.9.0-dev"),
"v7 refusal must name the build that wrote internal schema v7. No published \
release ever stamped it — it existed only in source builds inside the \
0.8.1 -> 0.9.0 development window — so the refusal names `0.9.0-dev`, \
got: {stderr}",
);
assert!(
stderr.contains("export"),
Expand Down Expand Up @@ -888,8 +897,11 @@ fn current_v9_refuses_and_rebuilds_genuine_v8_and_v8_refuses_v9() {
let refusal = output_failure(cli().arg("snapshot").arg(&v8_graph));
let stderr = String::from_utf8_lossy(&refusal.stderr);
assert!(
stderr.contains("0.12.x"),
"v8 refusal must name the release line that wrote internal schema v8, got: {stderr}",
stderr.contains("0.9.0-dev"),
"v8 refusal must name the build that wrote internal schema v8. No published \
release ever stamped it — it existed only in source builds inside the \
0.8.1 -> 0.9.0 development window — so the refusal names `0.9.0-dev`, \
got: {stderr}",
);
assert!(
stderr.contains("export"),
Expand Down
8 changes: 5 additions & 3 deletions docs/dev/testing.md
Original file line number Diff line number Diff line change
Expand Up @@ -585,7 +585,7 @@ table; the v4 case also pins reverse refusal by the old binary.

RFC-023 added its then-immediate-predecessor case gated on `OMNIGRAPH_V5_BIN`, built
from the final internal-v5 commit. It mints a genuine SchemaIR-v2 v5 graph,
proves v9 refuses it with the 0.9.x rebuild guidance, exports with v5, rebuilds
proves v9 refuses it with the `0.9.0-dev` rebuild guidance, exports with v5, rebuilds
under v9, checks row/vector/blob fidelity, exact blob bytes, and exact-`id` PK
metadata, then proves the v5 binary refuses the v9 root. The same cell injects
a duplicate logical ID into the v5 export: v9 rejects the load atomically,
Expand Down Expand Up @@ -615,8 +615,10 @@ OMNIGRAPH_V7_BIN=/path/to/final-v7/omnigraph \
```

RFC-026 Phase B2 adds the immediate-predecessor `OMNIGRAPH_V8_BIN` seam. It
mints a genuine internal-v8/config-v2 graph, proves v9 refuses it with 0.12.x
rebuild guidance, exports with v8, rebuilds a distinct v9/config-v3 root, and
mints a genuine internal-v8/config-v2 graph, proves v9 refuses it with
`0.9.0-dev` rebuild guidance (no published release ever stamped v5–v8, so the
refusal names the development window rather than a release line), exports with
v8, rebuilds a distinct v9/config-v3 root, and
Comment thread
greptile-apps[bot] marked this conversation as resolved.
proves row/vector fidelity plus exact-`id` PK metadata. The v9 re-export must
not expose the physical `__omnigraph_stream_v1$` attribution column, and the v8
fixture's ordinary user property `__omnigraph_stream_v1` must retain its value;
Expand Down
9 changes: 6 additions & 3 deletions docs/dev/versioning.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,14 +38,17 @@ together. A v4 graph cannot be backfilled safely because its logical IDs,
registration keys, paths, versions, and tombstones are all name-derived; the
normal strand rebuild mints a fresh domain and table incarnations instead.

Internal schema v6 mapped to OmniGraph 0.10.x. It preserved the v5 identity
Internal schema v6 was a 0.9.0-dev format: like v5, v7, and v8 it was written
only by source builds off `main` during 0.9.0 development, and no published
release serves it. It preserved the v5 identity
contract and activated RFC-023 key fencing: every graph node/edge dataset
declares exactly non-null physical `id` as Lance's unenforced primary key from
creation, and production strict insert/upsert routes use the exact-`id`
filter-bearing adapter.

Internal schema **v9 is the currently served format** and maps to OmniGraph
**0.13.x**. It preserves v8's private data-bearing MemWAL core, then activates
**0.9.x** — the first published release line to serve any of these formats.
It preserves v8's private data-bearing MemWAL core, then activates
RFC-026's common B2 storage/recovery contract: stream-config v3, lifecycle
state v2, the grammar-impossible trusted base-row field
`__omnigraph_stream_v1$`, one manifest-selected `_stream_tokens.lance`
Expand All @@ -62,7 +65,7 @@ canonical payloads, token projections, recovery JSON, and exact-authority
lookup retention are bounded; the measured near-cap fold RSS remains evidence,
not a runtime allocator promise.

A v8/0.12.x graph is not reinterpreted or migrated in place: config-v2 and
A v8 (0.9.0-dev) graph is not reinterpreted or migrated in place: config-v2 and
recovery-v11 never become config-v3/state-v2/recovery-v12 authority. Export it
with the v8 binary, initialize a different v9 root, and load through the v9
writer. The physical field's trailing `$` is outside the `.pg` identifier
Expand Down
5 changes: 3 additions & 2 deletions docs/dev/writes.md
Original file line number Diff line number Diff line change
Expand Up @@ -1277,13 +1277,14 @@ strand model): this binary reads exactly ONE internal-schema version
policy) and [the upgrade guide](../user/operations/upgrade.md) (the rebuild
recipe).

V7 maps to the 0.11.x release line. It preserves v5 stable identity and v6
V7 is a 0.9.0-dev format (no published release serves it). It preserves v5
stable identity and v6
exact-`id` key fencing, and adds the RFC-026 Phase A lifecycle/enrollment
foundation described above. A genuine v6 root moves to v7 only through
export/init/load into a different root; v7 refuses v6 and a v6 binary refuses
v7.

V8 maps to the 0.12.x release line. It preserves the v7 foundation and activates
V8 is likewise a 0.9.0-dev format. It preserves the v7 foundation and activates
the private RFC-026 B1 stream-config-v2 row/fold core plus recovery-v11. A v7
root moves to v8 only through export/init/load into a different root; v8 refuses
v7 and a v7 binary refuses v8. This format capability does not expose a public
Expand Down
13 changes: 8 additions & 5 deletions docs/rfcs/0023-key-conflict-fencing.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@ conflict-resolver and merge-insert sources plus runtime surface probes
**Evidence status (2026-07-17):** the beta.21 filter shape and directional
conflict matrix were revalidated unchanged by the RC.1 surface guards. The
historical performance measurements below remain labeled beta.21. The implementation now activates
the contract in internal schema v6 (the OmniGraph 0.10.x format): fresh
the contract in internal schema v6 (a 0.9.0-dev format; no published release
served it): fresh
node/edge datasets carry exact-`id` PK metadata from creation, all production
graph insert/upsert routes use the sealed filter-bearing keyed adapter, bare
keyed Append is source-guarded out of production, and effect-free conflicts
Expand Down Expand Up @@ -571,8 +572,9 @@ compatibility check. A fencing-capable binary keeps
instructions, and refuses a newer graph with “upgrade omnigraph.” An older
binary refuses the fencing-capable stamp.

The fencing-compatible format is internal schema **v6**, mapped to OmniGraph
**0.10.x**. It follows RFC-028's v5 identity format; RFC-024 and later draft
The fencing-compatible format is internal schema **v6**, a 0.9.0-dev format
(the planned 0.10.x mapping was superseded when v6–v8 stayed unreleased and
0.9.0 shipped v9). It follows RFC-028's v5 identity format; RFC-024 and later draft
capabilities are not included. Each later internal-format change requires its
own rebuild under the strand policy unless independently accepted capabilities
deliberately co-release after a combined initialization and recovery review.
Expand Down Expand Up @@ -1264,8 +1266,9 @@ correctness cells are recorded in §11.2 and are green.
5. **Retained support boundary, not a current-topology acceptance gate:**
cross-process recovery ownership must land before any broadened topology
claim. V6 destructive recovery remains single-writer-process.
6. **Satisfied 2026-07-15:** internal schema v6 is the fencing format for
OmniGraph 0.10.x, and a genuine final-v5 binary passed v6 refusal,
6. **Satisfied 2026-07-15:** internal schema v6 is the fencing format (then
planned for 0.10.x; ultimately a 0.9.0-dev format), and a genuine final-v5
binary passed v6 refusal,
export/init/load with row/vector/blob fidelity, exact blob bytes, and
exact-`id` target PKs; duplicate input failed atomically with an empty target
and unchanged source; v5 also refused the rebuilt v6 root. Branch fork,
Expand Down
31 changes: 17 additions & 14 deletions docs/user/operations/upgrade.md
Original file line number Diff line number Diff line change
Expand Up @@ -264,11 +264,12 @@ merge-insert builder has no `WriteParams` hook, while Overwrite does.

This format cannot be obtained by adding metadata to a live v5 root. Lance's
filtered/unfiltered conflict behavior is directional, so every table image and
every writer must cross the boundary together. Quiesce writers, export with the
latest 0.9.x binary, initialize a **different** root with the 0.10.x binary,
load the export, verify the v6 stamp and data, then cut the whole fleet over.
The 0.10.x binary refuses the v5 source root, and the 0.9.x binary refuses the
new v6 root.
every writer must cross the boundary together. Both v5 and v6 are 0.9.0-dev
formats with no published binary, so this crossing uses source builds: quiesce
writers, export with a source build at the final internal-v5 commit,
initialize a **different** root with a final internal-v6 source build, load
the export, verify the v6 stamp and data, then cut the whole fleet over. The
v6 build refuses the v5 source root, and the v5 build refuses the new v6 root.

The v6 load checks the export for duplicate logical IDs before any table effect.
Older bare-Append workloads could contain a committed collision; do not resolve
Expand Down Expand Up @@ -630,12 +631,13 @@ It does **not** expose streaming ingestion. There is no `@stream`, production
enrollment command or API, WAL row acknowledgement, fold, drain/resume, or
fresh-read surface in this format slice.

Move a v6 graph to v7 with the ordinary recipe at the top of this page: export
with the latest 0.10.x binary, initialize a **different** root with the 0.11.x
binary, load the export, verify the v7 stamp and logical data, and cut the whole
fleet over together. The 0.11.x binary refuses the v6 source root, and the
0.10.x binary refuses the new v7 root. Genuine cross-version tests pin both
directions.
Move a v6 graph to v7 with the ordinary recipe at the top of this page — both
are 0.9.0-dev formats, so the crossing uses source builds: export with a final
internal-v6 source build, initialize a **different** root with a final
internal-v7 source build, load the export, verify the v7 stamp and logical
data, and cut the whole fleet over together. The v7 build refuses the v6
source root, and the v6 build refuses the new v7 root. Genuine cross-version
tests pin both directions.

V6 has no acknowledged MemWAL rows, so there is no stream backlog to drain
before this particular rebuild. Export transfers only manifest-visible logical
Expand All @@ -653,9 +655,10 @@ and schema-v11 `StreamFold` recovery through the unified write path. It still
does **not** expose public stream schema, SDK, HTTP, CLI, or operator controls;
those remain Phase B2 gates.

Move a v7 graph to v8 with the ordinary recipe at the top of this page: export
with the latest 0.11.x binary, initialize a different root with the 0.12.x
binary, load the export, and verify the v8 stamp and logical data. V7's
Move a v7 graph to v8 with the ordinary recipe at the top of this page — both
are 0.9.0-dev formats, so the crossing uses source builds: export with a final
internal-v7 source build, initialize a different root with a final internal-v8
source build, load the export, and verify the v8 stamp and logical data. V7's
config-v1 enrollment is never reinterpreted as data-bearing config-v2 state.
The rebuild copies manifest-visible rows only, not MemWAL indexes, shard state,
recovery intents, or epochs, so the new root starts unenrolled.