forked from lance-format/lance-graph
-
Notifications
You must be signed in to change notification settings - Fork 0
SoA envelope: zero-copy register-file model + LE contract + ontology resolution audit #477
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
eb44775
probe(stoic-turing): Q3 standing-wave falsification + Q4 HHTL audit
claude 674cdc2
docs/probes: source-grounded particle/SoA envelope audit
claude 792503c
docs/probes/q3: remove all singleton/Vsa16kF32 carrier assumptions
claude 00d5bde
docs/probes/q3: standing wave is vacuous — Lance versioning is the O(…
claude 4d0774b
contract: add SoaEnvelope LE contract; audit Phase 7 follow-up
claude a676121
CLAUDE.md: P0 rule — AdaWorldAPI forks only, never crates.io upstream
claude edf0a9d
audit+contract: purge baton/emission; zero-copy SoA model correction
claude 1eed37a
docs: SoA three-tier model — zero-copy lifecycle, Kanban, OGIT/OGAR
claude c78a55f
docs: register-file model — SoA as LE registers, OGAR class as ISA de…
claude 69c3b12
docs: incorporate grill answers into three-tier model
claude e0e016d
docs: dispatch escape hatch, Kanban owns thinking styles, SurrealDB c…
claude 2f15278
PR #477 review fixes: verify_layout offset-bound, target-state framin…
claude 2af4654
PR #477 review: ColumnOutOfBounds variant, unbundle_from deprecation,…
claude 4e537c7
contract: checked_add overflow guard in verify_layout + MD040 fix
claude 9a1caeb
nsm: replace alignment-unsafe u8->f32 cast with safe LE decode
claude File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| # Plan: Cycle-Coherent SoA Snapshot — No-Cross-Cycle-Lag Guarantee | ||
|
|
||
| **Version:** v1 | ||
| **Date:** 2026-06-06 | ||
| **Status:** Queued | ||
| **D-ids:** D-SOA-SNAP-1 through D-SOA-SNAP-6 | ||
|
|
||
| --- | ||
|
|
||
| ## The problem | ||
|
|
||
| `temporal.rs` (PR #468) closes the row-scale deinterlace gap: HLC tick → | ||
| `classify/deinterlace` → causally-coherent row sequence. But there is a | ||
| parallel byte-scale gap: nothing prevents a reader from holding a mix of | ||
| column data from cycle N and cycle N+1 within the same SIMD sweep. This is | ||
| the **cross-cycle lag problem** — a SIMD sweep that is not internally | ||
| single-cycle is not coherent. | ||
|
|
||
| The deinterlace operation is one operation at two scales: | ||
|
|
||
| ```text | ||
| Row/query scale → HLC tick + DependsClosure → temporal.rs (SHIPPED, PR #468) | ||
| Byte/column scale → SoaEnvelope::cycle() stamp → MailboxSoA Arc-swap (THIS PLAN) | ||
| ``` | ||
|
|
||
| --- | ||
|
|
||
| ## The mechanism: Arc-swap COW at column granularity | ||
|
|
||
| The SoA mailbox carries its columns as `Arc<[u8]>` slices (via | ||
| `MultiLaneColumn` in ndarray). The invariant is: | ||
|
|
||
| > **A reader that snapshots all column Arcs at the same `cycle()` stamp sees | ||
| > a single coherent cycle. No column can be from a prior cycle.** | ||
|
|
||
| ### Write path (in `lance-graph`, `MailboxSoa::advance_phase`) | ||
|
|
||
| On every `advance_phase(to: KanbanPhase)`: | ||
|
|
||
| 1. Increment `cycle` counter on the envelope. | ||
| 2. For each mutated column: swap the `Arc` pointer — `Arc::make_mut` on the | ||
| backing `Arc<[u8]>` of the `MultiLaneColumn`, write the new data, then | ||
| publish the new Arc via an `ArcSwap` (or `RwLock<Arc<MultiLaneColumn>>`). | ||
| 3. The cycle increment is a `SeqCst` store (fence) BEFORE the column Arc | ||
| swaps. Readers who observe the new cycle will see the new column data. | ||
|
|
||
| ### Read path (in `lance-graph`, `MailboxSoaView`) | ||
|
|
||
| On `snapshot()`: | ||
|
|
||
| 1. Load cycle stamp. | ||
| 2. Clone all column Arcs under the same cycle stamp (atomic snapshot loop: | ||
| re-read cycle after loading all Arcs; retry if it changed — lock-free | ||
| single-retry is sufficient because writers are serialized through | ||
| `advance_phase`). | ||
| 3. Return `MailboxSoaSnapshot { cycle, cols: [...] }`. | ||
|
|
||
| The snapshot guarantees all column data is from the same cycle. | ||
|
|
||
| ### Boundary: ndarray stays layout-only | ||
|
|
||
| `MultiLaneColumn` in ndarray is `Arc<[u8]>` with typed lane iterators — | ||
| **layout-only**. The Arc-swap policy (when to swap, how to snapshot, the | ||
| cycle fence) belongs in `lance-graph`'s `MailboxSoa`. ndarray never learns | ||
| that cycles or snapshots exist. The boundary is: | ||
|
|
||
| ```text | ||
| ndarray::simd::MultiLaneColumn — Arc<[u8]>, lane iters, Send + Sync, zero-copy reads | ||
| lance-graph::MailboxSoa — Arc-swap on advance_phase, cycle fence, snapshot() | ||
| ``` | ||
|
|
||
| ### Connection to temporal.rs | ||
|
|
||
| `SoaEnvelope::cycle()` is the byte-scale clock. `QueryReference::ref_version` | ||
| is the row-scale clock (a Lance version). They are the same monotonic clock | ||
| at different granularities — Lance version N corresponds to SoA cycle C(N). | ||
| When `temporal.rs::deinterlace` runs at query time, the `V_ref` it uses should | ||
| align with the `cycle()` of the snapshot being queried. | ||
|
|
||
| Wiring: `VersionScheduler::on_version(&view, at, exec)` provides the Lance | ||
| version; the `MailboxSoaSnapshot` that went into that version carries its | ||
| `cycle`. Threading `snapshot.cycle` into `QueryReference` closes the loop so | ||
| row-scale and byte-scale deinterlace use the same clock. | ||
|
|
||
| --- | ||
|
|
||
| ## Deliverables | ||
|
|
||
| ### D-SOA-SNAP-1 — `MailboxSoaSnapshot` type in lance-graph-contract | ||
|
|
||
| A `MailboxSoaSnapshot` struct: `cycle: u32`, `cols: Vec<Arc<MultiLaneColumn>>`. | ||
| Snapshot is `Send + Sync`. No reference to the originating `MailboxSoa`. | ||
| This is a point-in-time read — immutable after creation. | ||
|
|
||
| ### D-SOA-SNAP-2 — `SnapshotProvider` trait in lance-graph-contract | ||
|
|
||
| ```rust | ||
| pub trait SnapshotProvider { | ||
| fn snapshot(&self) -> MailboxSoaSnapshot; | ||
| } | ||
| ``` | ||
|
|
||
| Zero deps in contract. `MailboxSoa` in lance-graph implements it. | ||
|
|
||
| ### D-SOA-SNAP-3 — Arc-swap write path in `MailboxSoa::advance_phase` | ||
|
|
||
| In lance-graph (not contract, not ndarray): implement the cycle fence + | ||
| column Arc-swap on every `advance_phase`. Use `std::sync::RwLock<Arc<MultiLaneColumn>>` | ||
| per column (no external arc-swap crate needed unless benchmarks show | ||
| contention; add as a feature flag if needed). | ||
|
|
||
| ### D-SOA-SNAP-4 — `snapshot()` implementation on `MailboxSoa` | ||
|
|
||
| Lock-free snapshot: load cycle, clone all column Arcs, re-read cycle, retry | ||
| once if changed. Return `MailboxSoaSnapshot`. | ||
|
|
||
| ### D-SOA-SNAP-5 — No-cross-cycle-lag falsification test | ||
|
|
||
| ```rust | ||
| // Spawn a writer thread: advance_phase in a loop (100 cycles). | ||
| // Spawn 8 reader threads: each calls snapshot() in a loop. | ||
| // Assert: every snapshot has all columns reporting the same cycle. | ||
| // Assert: no snapshot mixes data from two different cycles. | ||
| ``` | ||
|
|
||
| The test is the formal statement of the guarantee. If it passes, the | ||
| invariant is mechanically enforced, not just documented. | ||
|
|
||
| ### D-SOA-SNAP-6 — Wire `snapshot.cycle` into `QueryReference` | ||
|
|
||
| In the planner: when a query resolves a `MailboxSoaSnapshot`, thread | ||
| `snapshot.cycle` through `QueryReference::hlc_tick` (or a new | ||
| `QueryReference::soa_cycle: Option<u32>` field) so `deinterlace` at | ||
| row scale uses the same cycle boundary as the snapshot at byte scale. | ||
|
|
||
| --- | ||
|
|
||
| ## Prerequisite gap fixes (order matters) | ||
|
|
||
| These mechanical fixes should land before or alongside D-SOA-SNAP-1 | ||
| (they settle the column shape): | ||
|
|
||
| 1. Remove `MailboxSoA::emit()` + `CollapseGateEmission` from source. | ||
| 2. Rename `last_emission_cycle` → `last_active_cycle` in MailboxSoA. | ||
| 3. Drop `entity_type: u16` from SoA row — MailboxId IS NiblePath. | ||
| 4. Fix `OntologyRegistry::enumerate_first_with_entity_type_id` linear scan. | ||
| 5. Remove `MappingRow.thinking_style` — Kanban owns thinking styles. | ||
| 6. Fix `unbundle_from` in `kv_bundle.rs:29` — `wrapping_sub` is not the | ||
| inverse of weighted-average `bundle_into`. | ||
|
|
||
| Items 1-5 settle the column shape before the Arc-swap schema is frozen. | ||
| Item 6 is independent but should not be deferred (correctness bug). | ||
|
|
||
| --- | ||
|
|
||
| ## Non-goals | ||
|
|
||
| - No recurrence / standing wave implementation. The standing wave is the | ||
| deinterlaced Lance version projection, provided by Lance versioning | ||
| (O(1) 90° lookup). Do not implement it in compute. | ||
| - No baton. No emission. No inter-mailbox handoff type. The snapshot is | ||
| consumed in-place; nothing is transmitted. | ||
| - ndarray does not learn about cycles, snapshots, or advance_phase. | ||
|
|
||
| --- | ||
|
|
||
| ## Cross-references | ||
|
|
||
| - `temporal.rs` (PR #468) — row-scale deinterlace (SHIPPED) | ||
| - `soa_envelope.rs` (PR #477) — envelope LE contract (IN REVIEW) | ||
| - `soa-three-tier-model.md` — three-tier lifecycle model | ||
| - `q3-standing-wave-falsification.md` — falsification: standing wave = Lance | ||
| versioning, not compute recurrence | ||
| - `.claude/board/EPIPHANIES.md` E-DEINTERLACE-TWO-SCALES — the synthesis | ||
| - `ndarray/src/simd_soa.rs` — `MultiLaneColumn` (layout-only; Arc-swap lives | ||
| in lance-graph, not here) | ||
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Critical dependency architecture violation in D-SOA-SNAP-1 and D-SOA-SNAP-2.
D-SOA-SNAP-1 proposes placing
MailboxSoaSnapshotwith fieldcols: Vec<Arc<MultiLaneColumn>>inlance-graph-contract, and D-SOA-SNAP-2 claims "Zero deps in contract." However,MultiLaneColumnis fromndarray(lines 68, 176). This creates alance-graph-contract → ndarraydependency, violating the zero-dependency contract architecture. Based on learnings, "Zero circular dependencies: lance-graph-contract (zero-dep) → ndarray/planner/n8n-rs/crewai-rust."Proposed resolution options
Option 1 (recommended): Keep
MailboxSoaSnapshotinlance-graph, not inlance-graph-contract. The contract provides only theSoaEnvelopetrait; the concrete snapshot type lives in the implementation crate.Option 2: Make the contract snapshot type generic/abstract:
Then
lance-graphprovidesimpl SnapshotProvider for MailboxSoa { type Column = MultiLaneColumn; }.🤖 Prompt for AI Agents
Source: Learnings