diff --git a/AGENTS.md b/AGENTS.md index 3fc78f7..68de6b8 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -81,7 +81,7 @@ Full diagram and concurrency model: [docs/dev/architecture.md](docs/dev/architec | Embeddings (compiler + engine clients, env vars, `@embed`) | [docs/user/embeddings.md](docs/user/embeddings.md) | | Branches, commit graph, snapshots, system branches | [docs/user/branches-commits.md](docs/user/branches-commits.md) | | Transactions and atomicity (per-query atomic; branches as multi-query transactions) | [docs/user/transactions.md](docs/user/transactions.md) | -| Direct-publish writes (the former Run state machine, now demoted to publisher CAS) | [docs/dev/runs.md](docs/dev/runs.md) | +| Direct-publish write path (staging, D2, recovery sidecars; the former Run state machine) | [docs/dev/writes.md](docs/dev/writes.md) | | Three-way merge and conflict kinds | [docs/dev/merge.md](docs/dev/merge.md) | | Diff / change feed (`diff_between`, `diff_commits`) | [docs/user/changes.md](docs/user/changes.md) | | Query execution, mutation execution, bulk loader, `load` vs `ingest` | [docs/dev/execution.md](docs/dev/execution.md) | @@ -176,7 +176,7 @@ cargo run -p omnigraph-server -- --bind 0.0.0.0:8080 # run the server fr # Run one crate / one test file / one test fn cargo test -p omnigraph-engine --test traversal # one integration-test file (see docs/dev/testing.md) -cargo test -p omnigraph-engine --test runs concurrent # one test fn by name substring +cargo test -p omnigraph-engine --test writes concurrent # one test fn by name substring cargo test -p omnigraph-engine some_inline_test -- --nocapture # show stdout # Feature-gated suites (each is its own job in CI, not part of the default run) diff --git a/crates/omnigraph-cli/tests/system_local.rs b/crates/omnigraph-cli/tests/system_local.rs index 074b203..08f653d 100644 --- a/crates/omnigraph-cli/tests/system_local.rs +++ b/crates/omnigraph-cli/tests/system_local.rs @@ -991,7 +991,7 @@ query vector_search($q: String) { // The publisher CAS conflict shape is verified end-to-end at the engine // level in -// `crates/omnigraph/tests/runs.rs::concurrent_writers_one_succeeds_one_gets_expected_version_mismatch` +// `crates/omnigraph/tests/writes.rs::concurrent_writers_one_succeeds_one_gets_expected_version_mismatch` // and at the HTTP boundary in // `crates/omnigraph-server/tests/server.rs::change_conflict_returns_manifest_conflict_409`. // A CLI-level race would be timing-dependent; with direct-publish the diff --git a/crates/omnigraph/src/db/manifest/recovery.rs b/crates/omnigraph/src/db/manifest/recovery.rs index 425499a..4c1b987 100644 --- a/crates/omnigraph/src/db/manifest/recovery.rs +++ b/crates/omnigraph/src/db/manifest/recovery.rs @@ -2,7 +2,7 @@ //! //! This module implements the building blocks of the per-sidecar recovery //! sweep that closes the documented Phase B → Phase C residual (see -//! `docs/dev/runs.md` "Open-time recovery sweep"). The high-level shape: +//! `docs/dev/writes.md` "Open-time recovery sweep"). The high-level shape: //! //! 1. Each writer that performs a multi-table commit writes a small JSON //! sidecar at `__recovery/{ulid}.json` BEFORE its per-table diff --git a/crates/omnigraph/src/loader/mod.rs b/crates/omnigraph/src/loader/mod.rs index cade1f4..46a46e2 100644 --- a/crates/omnigraph/src/loader/mod.rs +++ b/crates/omnigraph/src/loader/mod.rs @@ -613,7 +613,7 @@ async fn load_jsonl_reader( } else { // LoadMode::Overwrite keeps the legacy inline-commit path — // truncate-then-append doesn't fit the staged shape (see - // `docs/runs.md` "LoadMode::Overwrite residual"). The recovery + // `docs/dev/writes.md` "LoadMode::Overwrite residual"). The recovery // sidecar is not applicable here because the writer doesn't go // through MutationStaging; per-table inline commits + a final // manifest publish handle their own residual via the documented diff --git a/crates/omnigraph/src/table_store.rs b/crates/omnigraph/src/table_store.rs index ddab706..46b15b0 100644 --- a/crates/omnigraph/src/table_store.rs +++ b/crates/omnigraph/src/table_store.rs @@ -49,7 +49,7 @@ pub struct DeleteState { /// `exec/mutation.rs`) and the bulk loader (`loader/mod.rs`). The /// intent: defer Lance commits to end-of-query so a mid-query failure /// leaves the touched table at the pre-mutation HEAD instead of -/// drifting ahead. See `docs/runs.md` for the publisher-CAS contract +/// drifting ahead. See `docs/dev/writes.md` for the publisher-CAS contract /// this builds on. /// /// `transaction` is opaque from our side — Lance owns its semantics. We @@ -901,7 +901,7 @@ impl TableStore { /// Lift path: either a Lance API extension that lets /// `MergeInsertBuilder` accept additional staged fragments, or an /// in-memory pre-merge here that folds prior staged batches into the - /// input stream. See `docs/runs.md`. + /// input stream. See `docs/dev/writes.md`. pub async fn stage_merge_insert( &self, ds: Dataset, diff --git a/crates/omnigraph/tests/staged_writes.rs b/crates/omnigraph/tests/staged_writes.rs index 021b36e..5335057 100644 --- a/crates/omnigraph/tests/staged_writes.rs +++ b/crates/omnigraph/tests/staged_writes.rs @@ -2,7 +2,7 @@ //! exercise `stage_append`, `stage_merge_insert`, `scan_with_staged`, //! and `count_rows_with_staged` directly against a Lance dataset — no //! Omnigraph engine involved. The engine-level use of these primitives -//! is exercised by `tests/runs.rs`. +//! is exercised by `tests/writes.rs`. //! //! Test surface here: //! 1. `stage_append` + `scan_with_staged` shows committed + staged data @@ -709,7 +709,7 @@ async fn stage_create_inverted_index_does_not_advance_head_until_commit() { /// /// **When Lance #6658 lands**: this test will need to flip — replace /// the assertion with a `stage_delete` + `commit_staged` round-trip -/// and remove the residual line in `docs/runs.md`. +/// and remove the residual line in `docs/dev/writes.md`. #[tokio::test] async fn delete_where_advances_head_inline_documents_residual() { let dir = tempfile::tempdir().unwrap(); diff --git a/crates/omnigraph/tests/runs.rs b/crates/omnigraph/tests/writes.rs similarity index 99% rename from crates/omnigraph/tests/runs.rs rename to crates/omnigraph/tests/writes.rs index cfff3fc..13cb10f 100644 --- a/crates/omnigraph/tests/runs.rs +++ b/crates/omnigraph/tests/writes.rs @@ -1,7 +1,7 @@ -//! Tests for the direct-to-target write path (Run state machine -//! removed). The Run/`__run__` staging branch / RunRecord state machine no -//! longer exists; mutations and loads write directly to target tables and -//! commit once via the publisher's `expected_table_versions` CAS. +//! Tests for the direct-publish write path: mutations and loads write +//! directly to target tables and commit once via the publisher's +//! `expected_table_versions` CAS. (History: this replaced the removed Run +//! state machine / `__run__` staging branches / RunRecord — MR-771.) //! //! What this file covers: //! - No `__run__*` branches are created by load or mutate. diff --git a/docs/dev/architecture.md b/docs/dev/architecture.md index 8b7fca2..813f30c 100644 --- a/docs/dev/architecture.md +++ b/docs/dev/architecture.md @@ -207,7 +207,7 @@ contracts: This pattern realizes read-your-writes within a multi-statement mutation and keeps failure scope bounded for inserts/updates by construction at the writer layer. See [docs/dev/invariants.md](invariants.md) and -[docs/dev/runs.md](runs.md) for the publisher CAS contract this builds on. +[docs/dev/writes.md](writes.md) for the publisher CAS contract this builds on. ### Storage trait — today vs. roadmap @@ -278,7 +278,7 @@ flowchart LR eng --> wq ``` -The server applies Cedar policy at the HTTP boundary today. The roadmap, called out in [docs/dev/invariants.md](invariants.md) as a known gap, is to push policy into the planner as predicates. After Cedar, mutating handlers go through `WorkloadController` (per-actor admission cap + byte budget; PR 2 / MR-686) before reaching the engine. The engine itself holds an `Arc` so concurrent mutations on the same `(table, branch)` serialize at the queue, while disjoint keys run in parallel — see [docs/user/server.md](../user/server.md) "Per-actor admission control" and [docs/dev/runs.md](runs.md). The CLI bypasses the HTTP layer (and admission) and calls the engine API directly. +The server applies Cedar policy at the HTTP boundary today. The roadmap, called out in [docs/dev/invariants.md](invariants.md) as a known gap, is to push policy into the planner as predicates. After Cedar, mutating handlers go through `WorkloadController` (per-actor admission cap + byte budget; PR 2 / MR-686) before reaching the engine. The engine itself holds an `Arc` so concurrent mutations on the same `(table, branch)` serialize at the queue, while disjoint keys run in parallel — see [docs/user/server.md](../user/server.md) "Per-actor admission control" and [docs/dev/writes.md](writes.md). The CLI bypasses the HTTP layer (and admission) and calls the engine API directly. Code paths: diff --git a/docs/dev/execution.md b/docs/dev/execution.md index f5c2840..3a108d7 100644 --- a/docs/dev/execution.md +++ b/docs/dev/execution.md @@ -147,7 +147,7 @@ sequenceDiagram - End-of-query Lance commit: `TableStore::stage_append`, `stage_merge_insert`, `commit_staged` at `crates/omnigraph/src/table_store.rs` - Manifest commit primitive: `commit_updates_on_branch_with_expected` at `crates/omnigraph/src/db/omnigraph/table_ops.rs` -Atomicity guarantee for multi-statement mutations: a mid-query failure leaves Lance HEAD untouched on staged tables (no inline commit happened during op execution), so the next mutation proceeds normally with no `ExpectedVersionMismatch`. The publisher CAS at the very end either succeeds (manifest advances atomically across all touched sub-tables) or fails with a typed `ManifestConflictDetails::ExpectedVersionMismatch` (no partial publish). See [docs/dev/invariants.md](invariants.md) and [docs/dev/runs.md](runs.md). +Atomicity guarantee for multi-statement mutations: a mid-query failure leaves Lance HEAD untouched on staged tables (no inline commit happened during op execution), so the next mutation proceeds normally with no `ExpectedVersionMismatch`. The publisher CAS at the very end either succeeds (manifest advances atomically across all touched sub-tables) or fails with a typed `ManifestConflictDetails::ExpectedVersionMismatch` (no partial publish). See [docs/dev/invariants.md](invariants.md) and [docs/dev/writes.md](writes.md). ## Bulk loader (`loader/mod.rs`) diff --git a/docs/dev/index.md b/docs/dev/index.md index 83df8c8..d9ba5e5 100644 --- a/docs/dev/index.md +++ b/docs/dev/index.md @@ -21,7 +21,7 @@ constraints. User-facing behavior should still be documented through |---|---| | System structure, L1/L2 framing, component diagrams | [architecture.md](architecture.md) | | On-disk layout, manifest schema, URI behavior | [storage.md](../user/storage.md) | -| Direct-publish writes, D2, staged writes, recovery sidecars | [runs.md](runs.md) | +| Direct-publish writes, D2, staged writes, recovery sidecars | [writes.md](writes.md) | | Query execution, mutation execution, loader flow | [execution.md](execution.md) | | Index lifecycle and graph topology indexes | [indexes.md](../user/indexes.md) | | Branch and commit internals | [branches-commits.md](../user/branches-commits.md) | diff --git a/docs/dev/invariants.md b/docs/dev/invariants.md index 958042f..70477d4 100644 --- a/docs/dev/invariants.md +++ b/docs/dev/invariants.md @@ -38,7 +38,7 @@ Use it this way: publishes one manifest update. Do not commit per statement. Delete-only queries are the documented inline residual; the parse-time D2 rule prevents mixing deletes with insert/update until Lance exposes two-phase delete. - Read [runs.md](runs.md) and [execution.md](execution.md). + Read [writes.md](writes.md) and [execution.md](execution.md). 5. **Recovery is part of the commit protocol.** Writers that can advance Lance HEAD before manifest publish must write `__recovery/{ulid}.json` sidecars. @@ -56,7 +56,7 @@ Use it this way: branch they read even when index coverage is partial. Expensive index work should converge from manifest state instead of extending the critical write path. Scalar staged index builds and vector inline residuals are documented - in [runs.md](runs.md) and [indexes.md](../user/indexes.md). + in [writes.md](writes.md) and [indexes.md](../user/indexes.md). 8. **Schema identity survives renames.** Accepted schema identity must remain stable across type and property renames. Rename support belongs in migration @@ -96,12 +96,12 @@ Use it this way: | Area | Current state | Source | |---|---|---| -| Multi-table commit | Manifest CAS plus recovery sidecars; not a single Lance primitive | [runs.md](runs.md), [architecture.md](architecture.md) | -| Constructive mutations | In-memory `MutationStaging`, one end-of-query table commit per touched table, then one manifest publish | [runs.md](runs.md), [execution.md](execution.md) | -| Deletes | Inline-commit residual; delete-only queries allowed, mixed insert/update/delete rejected by D2 | [query-language.md](../user/query-language.md), [runs.md](runs.md) | +| Multi-table commit | Manifest CAS plus recovery sidecars; not a single Lance primitive | [writes.md](writes.md), [architecture.md](architecture.md) | +| Constructive mutations | In-memory `MutationStaging`, one end-of-query table commit per touched table, then one manifest publish | [writes.md](writes.md), [execution.md](execution.md) | +| Deletes | Inline-commit residual; delete-only queries allowed, mixed insert/update/delete rejected by D2 | [query-language.md](../user/query-language.md), [writes.md](writes.md) | | Schema validation | Type checks, required fields, defaults, edge endpoint checks, and edge cardinality are enforced on write paths | [schema-language.md](../user/schema-language.md), [execution.md](execution.md) | | Unique constraints | Intra-batch and write-path checks exist; full cross-version uniqueness is still a gap | [schema-language.md](../user/schema-language.md) | -| Storage trait | `TableStorage` exists as the sealed staged-write surface; full call-site migration and capability/stat surfaces are incomplete | [runs.md](runs.md), [architecture.md](architecture.md) | +| Storage trait | `TableStorage` exists as the sealed staged-write surface; full call-site migration and capability/stat surfaces are incomplete | [writes.md](writes.md), [architecture.md](architecture.md) | | Index lifecycle | `ensure_indices` is explicit today; reconciler-based convergence is roadmap | [indexes.md](../user/indexes.md), [maintenance.md](../user/maintenance.md) | | Traversal IDs | Runtime still builds `TypeIndex`; Lance stable row-id based graph IDs are roadmap | [architecture.md](architecture.md), [query-language.md](../user/query-language.md) | | Auth | Bearer token hashing and server-side actor resolution are implemented at the HTTP boundary | [server.md](../user/server.md), [policy.md](../user/policy.md) | diff --git a/docs/dev/testing.md b/docs/dev/testing.md index e6989ba..425fcee 100644 --- a/docs/dev/testing.md +++ b/docs/dev/testing.md @@ -20,7 +20,7 @@ The engine's `tests/` is the principal coverage surface; most graph-shaped behav | `end_to_end.rs` | Full init → load → query/mutate flow | | `branching.rs` | Branch create / list / delete, lazy fork | | `merge_truth_table.rs` | Merge-pair truth table (MR-786): all 9×9 `(left_op, right_op)` cells from `{noop, addNode, removeNode, addEdge, removeEdge, setProperty, dropProperty, addLabel, removeLabel}`. Adding a new op to `OpVariant` forces a compile error in `build_case` until the new row + column are dispositioned. 36 executable cells run through real `branch_merge` with a structured oracle (`MergeOutcome` / `MergeConflictKind` + graph-state assert); 45 cells involving `dropProperty`/`addLabel`/`removeLabel` are recorded as `Unsupported` until the mutation grammar grows. | -| `runs.rs` | Direct-publish writes: cancellation, concurrent-writer CAS, multi-statement atomicity, MR-794 staged-write rewire (D₂ rejection, insert+update coalesce, multi-append coalesce, partial-failure recovery, load RI/cardinality recovery) | +| `writes.rs` | Direct-publish writes: cancellation, concurrent-writer CAS, multi-statement atomicity, MR-794 staged-write rewire (D₂ rejection, insert+update coalesce, multi-append coalesce, partial-failure recovery, load RI/cardinality recovery) | | `staged_writes.rs` | TableStore staged-write primitives (`stage_append`, `stage_merge_insert`, `commit_staged`, `scan_with_staged`, `count_rows_with_staged`) — primitive-level only; engine code uses the in-memory `MutationStaging` accumulator instead | | `lifecycle.rs` | Graph lifecycle, schema state | | `point_in_time.rs` | Snapshots, time travel (`snapshot_at_version`, `entity_at`) | @@ -89,7 +89,7 @@ If introducing coverage tooling is in scope for your task, the natural first ste How to check: -1. **Map the change to an area** — use the engine integration-test table above (`branching.rs`, `runs.rs`, `search.rs`, etc.). The filename usually names the area. +1. **Map the change to an area** — use the engine integration-test table above (`branching.rs`, `writes.rs`, `search.rs`, etc.). The filename usually names the area. 2. **Open the file and skim every test fn name.** Test fn names are the index — read them all, not just the first few. 3. **Grep for the symbol or path you're changing.** `rg ` or `rg ` across all `tests/` directories surfaces existing coverage you might miss. 4. **Decide one of three outcomes**, in this order of preference: diff --git a/docs/dev/runs.md b/docs/dev/writes.md similarity index 98% rename from docs/dev/runs.md rename to docs/dev/writes.md index 816f2ac..974f7a6 100644 --- a/docs/dev/runs.md +++ b/docs/dev/writes.md @@ -1,7 +1,10 @@ -# Runs — REMOVED (MR-771) +# Direct-Publish Write Path -The Run state machine and `__run__` staging branches were removed in -MR-771. `mutate_as` and `load` now write **directly to the target table** +> History: the Run state machine and `__run__` staging branches were +> removed in MR-771 (shipped v0.4.0). Writes now go directly to the target +> table; this document specifies that direct-publish path. + +`mutate_as` and `load` write **directly to the target table** and call `ManifestBatchPublisher::publish` once at the end with `expected_table_versions` (the per-table manifest versions captured before the first write). Cross-table OCC is enforced inside the publisher; the diff --git a/docs/releases/v0.4.0.md b/docs/releases/v0.4.0.md index efb2da7..d3a8244 100644 --- a/docs/releases/v0.4.0.md +++ b/docs/releases/v0.4.0.md @@ -65,7 +65,7 @@ manifest. The next mutation against that table fails with `ExpectedVersionMismatch`. Most validation runs before any Lance write, so single-statement mutations are unaffected; the narrow path is multi-statement queries with late-op failures. Tracked as a follow-up; -see [docs/dev/runs.md](../dev/runs.md#known-limitation-mid-query-partial-failure-on-the-same-table) +see [docs/dev/writes.md](../dev/writes.md#mid-query-partial-failure-closed-by-mr-794) for the workaround. ## Upgrade notes diff --git a/docs/releases/v0.4.1.md b/docs/releases/v0.4.1.md index 78211e4..4983015 100644 --- a/docs/releases/v0.4.1.md +++ b/docs/releases/v0.4.1.md @@ -19,7 +19,7 @@ mutation proceeds normally. HEAD on every staged table is untouched and the next mutation proceeds normally. A narrowed residual remains at the finalize→publisher boundary (multi-table `commit_staged` is not - atomic with the manifest commit) — see [docs/dev/runs.md](../dev/runs.md) + atomic with the manifest commit) — see [docs/dev/writes.md](../dev/writes.md) "Finalize → publisher residual" for details. - **D₂ parse-time rule**: a single mutation query is either insert/update-only or delete-only. Mixed → rejected with a clear @@ -75,14 +75,14 @@ mutation proceeds normally. ## Tests added -- `tests/runs.rs::partial_failure_leaves_target_queryable_and_unblocks_next_mutation` +- `tests/writes.rs::partial_failure_leaves_target_queryable_and_unblocks_next_mutation` (replaces the old `partial_failure_observably_rolls_back_but_blocks_next_mutation_on_same_table`) -- `tests/runs.rs::mutation_rejects_mixed_insert_and_delete_at_parse_time` -- `tests/runs.rs::mixed_insert_and_update_on_same_person_coalesces_to_one_merge` -- `tests/runs.rs::multiple_appends_to_same_edge_coalesce_to_one_append` -- `tests/runs.rs::multi_statement_inserts_publish_exactly_once` -- `tests/runs.rs::load_with_bad_edge_reference_unblocks_next_load` -- `tests/runs.rs::load_with_cardinality_violation_unblocks_next_load` +- `tests/writes.rs::mutation_rejects_mixed_insert_and_delete_at_parse_time` +- `tests/writes.rs::mixed_insert_and_update_on_same_person_coalesces_to_one_merge` +- `tests/writes.rs::multiple_appends_to_same_edge_coalesce_to_one_append` +- `tests/writes.rs::multi_statement_inserts_publish_exactly_once` +- `tests/writes.rs::load_with_bad_edge_reference_unblocks_next_load` +- `tests/writes.rs::load_with_cardinality_violation_unblocks_next_load` ## Files changed @@ -105,7 +105,7 @@ mutation proceeds normally. - `Cargo.toml` (workspace) + `crates/omnigraph/Cargo.toml` — added `datafusion = "52"` direct dep (transitively pulled by Lance already; required for `MemTable`). -- `docs/dev/runs.md` — removed "Known limitation" section; documented +- `docs/dev/writes.md` — removed "Known limitation" section; documented the new accumulator + D₂ + LoadMode::Overwrite residual. - `docs/dev/invariants.md` — mutation atomicity / read-your-writes status flipped to `upheld for inserts/updates`. @@ -127,7 +127,7 @@ mutation proceeds normally. as legacy. - `docs/user/cli.md` — replaced the legacy `omnigraph run *` quickstart block with `omnigraph commit list/show`. -- `docs/dev/testing.md` — extended the `runs.rs` row to cover the new +- `docs/dev/testing.md` — extended the `writes.rs` row to cover the new staged-write contract tests; added the `staged_writes.rs` row. - `AGENTS.md` (CLAUDE.md symlink) — updated the atomic-per-query description and the L2 capability matrix row. diff --git a/docs/user/errors.md b/docs/user/errors.md index fd047eb..8373b0d 100644 --- a/docs/user/errors.md +++ b/docs/user/errors.md @@ -9,7 +9,7 @@ - `Manifest(ManifestError { kind: BadRequest|NotFound|Conflict|Internal, details: Option, … })` - `ManifestConflictDetails::ExpectedVersionMismatch { table_key, expected, actual }` — caller's `expected_table_versions` did not match the manifest's current latest non-tombstoned version (set by `OmniError::manifest_expected_version_mismatch`). - `ManifestConflictDetails::RowLevelCasContention` — Lance row-level CAS rejected the publish because a concurrent writer landed the same `object_id`. Retried internally by the publisher; only surfaces if the retry budget exhausts. - - **D₂ parse-time rejection** (MR-794): a single mutation query that mixes inserts/updates with deletes errors out *before any I/O* with kind `BadRequest`. Message: `mutation '' on the same query mixes inserts/updates and deletes; split into separate mutations: (1) inserts and updates, then (2) deletes`. See [docs/user/query-language.md](query-language.md) for the rule and [docs/dev/runs.md](../dev/runs.md) for the underlying staged-write rationale. + - **D₂ parse-time rejection** (MR-794): a single mutation query that mixes inserts/updates with deletes errors out *before any I/O* with kind `BadRequest`. Message: `mutation '' on the same query mixes inserts/updates and deletes; split into separate mutations: (1) inserts and updates, then (2) deletes`. See [docs/user/query-language.md](query-language.md) for the rule and [docs/dev/writes.md](../dev/writes.md) for the underlying staged-write rationale. - `MergeConflicts(Vec)` Compiler-side `NanoError` covers parse / catalog / type / storage / plan / execution / arrow / lance / IO / manifest / unique-constraint, each with structured spans (`SourceSpan { start, end }`) for ariadne-style diagnostics. diff --git a/docs/user/query-language.md b/docs/user/query-language.md index 94528af..6c7516f 100644 --- a/docs/user/query-language.md +++ b/docs/user/query-language.md @@ -70,7 +70,7 @@ A single mutation query must be **either insert/update-only or delete-only**. Mi > `mutation '' on the same query mixes inserts/updates and deletes; split into separate mutations: (1) inserts and updates, then (2) deletes. This restriction lifts when Lance exposes a two-phase delete API (tracked: MR-793 / Lance-upstream).` -Reason: under the staged-write rewire (MR-794), inserts and updates accumulate in memory and commit at end-of-query, while deletes still inline-commit (Lance 4.0.0 has no public two-phase delete). Mixing creates ordering hazards (same-row insert→delete becomes a no-op because the staged insert isn't visible to delete; cascading deletes of just-inserted edges break referential integrity by silent design). Until Lance exposes `DeleteJob::execute_uncommitted`, the parse-time rejection keeps both paths atomic and correct. See [docs/dev/runs.md](../dev/runs.md) and [docs/dev/invariants.md](../dev/invariants.md). +Reason: under the staged-write rewire (MR-794), inserts and updates accumulate in memory and commit at end-of-query, while deletes still inline-commit (Lance 4.0.0 has no public two-phase delete). Mixing creates ordering hazards (same-row insert→delete becomes a no-op because the staged insert isn't visible to delete; cascading deletes of just-inserted edges break referential integrity by silent design). Until Lance exposes `DeleteJob::execute_uncommitted`, the parse-time rejection keeps both paths atomic and correct. See [docs/dev/writes.md](../dev/writes.md) and [docs/dev/invariants.md](../dev/invariants.md). ## IR (Intermediate Representation) diff --git a/docs/user/transactions.md b/docs/user/transactions.md index e4ed485..d6c79f4 100644 --- a/docs/user/transactions.md +++ b/docs/user/transactions.md @@ -164,5 +164,5 @@ This is the workflow MR-797 / agentic loops are designed around: **branches are - [`docs/user/branches-commits.md`](branches-commits.md) — branch and commit-graph mechanics. - [`docs/dev/merge.md`](../dev/merge.md) — three-way merge details and conflict kinds. - [`docs/user/query-language.md`](query-language.md) — `.gq` syntax for the multi-statement queries used above. -- [`docs/dev/runs.md`](../dev/runs.md) — the per-query commit pipeline that gives single-query atomicity. +- [`docs/dev/writes.md`](../dev/writes.md) — the per-query commit pipeline that gives single-query atomicity. - [`docs/dev/invariants.md`](../dev/invariants.md) — the architectural rule.