Skip to content

feat(planner): temporal — the deinterlace engine (epistemic policy + two causal axes)#468

Merged
AdaWorldAPI merged 2 commits into
mainfrom
feat/planner-temporal
Jun 4, 2026
Merged

feat(planner): temporal — the deinterlace engine (epistemic policy + two causal axes)#468
AdaWorldAPI merged 2 commits into
mainfrom
feat/planner-temporal

Conversation

@AdaWorldAPI

@AdaWorldAPI AdaWorldAPI commented Jun 4, 2026

Copy link
Copy Markdown
Owner

lance-graph-planner::temporal — the deinterlace engine

The query-time temporal/epistemic policy layer + the deinterlace engine that the Rubicon standing-wave reads depend on. Pure module, 13 tests green, no new deps.

What it owns

  • Epistemic policy (runtime, not storage, not ogar-vocab): EpistemicMode {Strict, Aware, Retro} + for_rung + admits; TemporalStatus {Contemporary, Anachronistic, Spoiler, Unknowable}.
  • QueryReference {server_id, ref_version, hlc_tick: Option<u64>, mode, rung} — HLC-aware in the signature, single-server in the body.
  • classify(row_version, knowable_from, v_ref) — the per-row TIME-causal deinterlace decision.
  • deinterlace(rows, v_ref, deps) — merges interlaced frames into the dispatchable standing-wave projection, ordered by the HLC key.

Two causal axes, both type-visible / body-trivial

  • TIME cross-server: hlc_tick: Option<u64> carried from day one (no breaking change when the cluster bus lands — avoids the emitted_at_millis decision-Claude/setup adaworld repos 4k pex #4 trap).
  • DATA: the DependsClosure trait (+ NoDeps) is the seam the SPO depends_on/reads_field source plugs into. Rubicon's KausalSpec::Depends guard implements it — opaque to the producer, like CommitHook is to the membrane.

Meet-point (durable interface)

knowable_from is sourced by ogar-adapter-surrealql (DEFINE TABLE registration) and consumed here by classifynowhere else. Pinned in CROSS_SESSION_COORDINATION.md.

Next

Rubicon repoints to consume this (deleting its local EpistemicMode/QueryReference placeholder — no dual-source), then moves to its durable home.

https://claude.ai/code/session_01VysoWJ6vsyg3wEGc5v7T5v

Summary by CodeRabbit

Release Notes

  • New Features

    • Added temporal query planning module to support causally coherent multi-frame data merging with configurable epistemic modes.
  • Chores

    • Expanded graph planner module exports to include new temporal planning capabilities.

…c policy + two causal axes)

`lance-graph-planner::temporal`. Merges the four asynchronous frames (lance
versions / SurrealQL knowable_from / ractor V_ref / cognitive trajectory) into
one causally-coherent SoA — the standing wave a reader deliberates over.

Query-time epistemic policy (NOT storage, NOT ogar-vocab — these classify how a
reader saw a row, not what a class is):
- EpistemicMode {Strict, Aware, Retro} + for_rung + admits
- TemporalStatus {Contemporary, Anachronistic, Spoiler, Unknowable}
- QueryReference {server_id, ref_version, hlc_tick: Option<u64>, mode, rung}
- classify(row_version, knowable_from, v_ref) -> TemporalStatus  (TIME-causal)

Both deferred axes are type-visible from day one with trivial single-server
bodies (avoids the emitted_at_millis decision-#4 non-Option trap on both):
- TIME cross-server: QueryReference carries server_id + hlc_tick: Option<u64>;
  the cluster-bus policy wakes them with no signature change.
- DATA-causal: the DependsClosure trait (+ NoDeps trivial impl) is the seam the
  SPO depends_on/reads_field source plugs into; Rubicon's KausalSpec::Depends
  guard implements it (opaque to the producer, like CommitHook to the membrane).

classify_ready + deinterlace are the two-axis entries; deinterlace yields the
dispatchable standing-wave projection (admitted on TIME && ready on DATA),
ordered by the HLC deinterlace key.

knowable_from meet-point: sourced by ogar-adapter-surrealql (DEFINE TABLE),
consumed here by classify — nowhere else.

13 tests green; module is pure (no new deps).

https://claude.ai/code/session_01VysoWJ6vsyg3wEGc5v7T5v
@coderabbitai

coderabbitai Bot commented Jun 4, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces a new temporal module to the lance-graph-planner that implements a query-time deinterlacing engine. The module defines epistemic-mode policies and per-row classification logic to merge multiple asynchronous database frames into causally coherent row sequences. It exports EpistemicMode, TemporalStatus, QueryReference, Classification, and deinterlace as the primary public API.

Changes

Temporal Module: Deinterlacing and Epistemic Reasoning

Layer / File(s) Summary
Epistemic mode and query reference types
crates/lance-graph-planner/src/temporal.rs
LanceVersion type alias; EpistemicMode enum (Strict/Aware/Retro) with for_rung() policy and admits() gating; TemporalStatus enum (Contemporary/Anachronistic/Spoiler/Unknowable); QueryReference struct pinning server identity, reference version, optional HLC tick, mode, and rung, with Default and at() constructors.
Time-axis classification logic
crates/lance-graph-planner/src/temporal.rs
classify() function computes TemporalStatus from row version, knowable-from version, and query reference; Retro mode enables future rows to be marked Spoiler rather than Anachronistic.
Data-causal dependency modeling
crates/lance-graph-planner/src/temporal.rs
DepEdge struct; DepClosure struct with satisfied flag and ready() constructor; DependsClosure trait for computing closure readiness at query reference; NoDeps struct always returning ready closure.
Combined classification and dispatch gating
crates/lance-graph-planner/src/temporal.rs
Classification struct aggregating temporal status and data readiness; dispatchable() method gates rows by conjunction of epistemic mode admission and data readiness; classify_ready() function computes both axes using DependsClosure.
Deinterlacing and row filtering
crates/lance-graph-planner/src/temporal.rs
DeinterlaceRow trait abstracts row identity, version, and HLC timestamp; deinterlace() function filters rows by dispatchable() status and orders by (hlc\_tick\_or\_fallback\_to\_lance\_version, lance\_version) to preserve causality across mixed HLC/non-HLC frames.
Tests and public module export
crates/lance-graph-planner/src/temporal.rs, crates/lance-graph-planner/src/lib.rs
Extensive unit tests validate rung-to-mode mapping, temporal classification outcomes, per-mode admission rules, data-causal dependency blocking, HLC cross-frame ordering, QueryReference defaults, DepClosure readiness semantics, and mixed-HLC fallback sort behavior; module exported as public in lib.rs.

🎯 4 (Complex) | ⏱️ ~60 minutes

🐰 Frames deinterlaced, logic gleams so bright,
Epistemic modes dance through space and time's light,
Dependencies rest, causality's bound,
Row by row ordered, no spoilers are found!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and specifically describes the main change: introduction of a temporal/deinterlacing engine with epistemic policy and two causal axes—exactly what the PR implements across the new temporal module.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: db9249aecb

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +186 to +187
#[derive(Debug, Clone, Default)]
pub struct DepClosure {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Make empty dependency closures ready by default

Because DepClosure derives Default, DepClosure::default() produces an empty dependency list with satisfied == false. In any DependsClosure implementation that naturally returns the default for “no dependencies”, deinterlace will drop every otherwise contemporary row via Classification::dispatchable, even though the empty/no-deps case is documented and implemented by NoDeps as ready.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the latest push: DepClosure no longer derives Default; the manual impl delegates to DepClosure::ready() so the trivial/empty case is satisfied: true (matching NoDeps). Regression: dep_closure_default_is_ready_not_blocking.

})
.cloned()
.collect();
out.sort_by_key(|r| (r.hlc_tick().unwrap_or(0), r.lance_version()));

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Use lance_version as the missing-HLC sort key

For mixed inputs where some rows have an HLC tick and older/single-server rows do not, unwrap_or(0) forces every missing-HLC row ahead of all HLC rows regardless of its Lance version. That contradicts the documented fallback to lance_version and can return a non-causal projection during partial HLC rollout or when combining legacy rows with new cross-server rows.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in the latest push: sort key is now (r.hlc_tick().unwrap_or_else(|| r.lance_version()), r.lance_version()) — honors the documented fallback. Regression: deinterlace_mixed_hlc_falls_back_to_lance_version asserts a legacy row with lance_version=750 interleaves between HLCs 500 and 900 (it would have clustered at index 0 before).

…llback to lance_version

Two real foot-guns codex caught on #468; both fixed + regression-tested (15/15 green).

P2 #1 — DepClosure::default() blocked every row.
The derived Default produced satisfied: false, so any consumer using
..Default::default() (or default()) would silently make deinterlace drop every
contemporary row via Classification::dispatchable. The trivial/empty case IS the
ready case (matches DepClosure::ready + NoDeps). Replaced derive with a manual
Default that delegates to ready(); regression: dep_closure_default_is_ready_not_blocking.

P2 #2 — unwrap_or(0) on hlc_tick forced every missing-HLC row ahead of all HLC rows.
The doc says "falls back to lance_version" but the code used 0. During partial
cross-server rollout (mixed HLC + legacy) this returned a non-causal projection
that clustered all legacy rows at the start regardless of their version. Changed
to unwrap_or_else(|| r.lance_version()) so single-server / legacy rows sort by
their own version; cross-server rows sort by HLC; mixed inputs interleave on the
unified scale. Regression: deinterlace_mixed_hlc_falls_back_to_lance_version.

https://claude.ai/code/session_01VysoWJ6vsyg3wEGc5v7T5v

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🧹 Nitpick comments (1)
crates/lance-graph-planner/src/temporal.rs (1)

140-157: 💤 Low value

Consider refactoring free functions to methods on QueryReference.

Per the coding guideline "Use only method calls on the carrier struct that holds the state, never free functions. Carrier pattern: trajectory.resolve() instead of resolve(trajectory, config, awareness)", these free functions could be methods on QueryReference:

  • classify(row_version, knowable_from, v_ref)v_ref.classify(row_version, knowable_from)
  • classify_ready(subject, row_version, knowable_from, v_ref, deps)v_ref.classify_ready(subject, row_version, knowable_from, deps)
  • deinterlace(rows, v_ref, deps)v_ref.deinterlace(rows, deps)

This would align with the repository's carrier pattern convention where the state-holding struct drives method calls.

Also applies to: 254-269, 287-320

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/lance-graph-planner/src/temporal.rs` around lines 140 - 157, The free
functions classify, classify_ready, and deinterlace should be moved as methods
onto the carrier struct QueryReference to follow the repository's carrier
pattern: rename and implement classify(&self, row_version: LanceVersion,
knowable_from: LanceVersion), classify_ready(&self, subject: SubjectType,
row_version: LanceVersion, knowable_from: LanceVersion, deps: DepsType) and
deinterlace(&self, rows: RowsType, deps: DepsType) (use the concrete parameter
types from the originals) and migrate the logic from the existing free functions
into these methods; update all callers to call v_ref.classify(...),
v_ref.classify_ready(...), and v_ref.deinterlace(...) and remove the
free-function definitions (also apply the same refactor for the other
occurrences referenced in the review, e.g., the functions around the 254–269 and
287–320 ranges) so state is accessed via &self on QueryReference rather than
passed in as separate arguments.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/lance-graph-planner/src/temporal.rs`:
- Around line 140-157: The free functions classify, classify_ready, and
deinterlace should be moved as methods onto the carrier struct QueryReference to
follow the repository's carrier pattern: rename and implement classify(&self,
row_version: LanceVersion, knowable_from: LanceVersion), classify_ready(&self,
subject: SubjectType, row_version: LanceVersion, knowable_from: LanceVersion,
deps: DepsType) and deinterlace(&self, rows: RowsType, deps: DepsType) (use the
concrete parameter types from the originals) and migrate the logic from the
existing free functions into these methods; update all callers to call
v_ref.classify(...), v_ref.classify_ready(...), and v_ref.deinterlace(...) and
remove the free-function definitions (also apply the same refactor for the other
occurrences referenced in the review, e.g., the functions around the 254–269 and
287–320 ranges) so state is accessed via &self on QueryReference rather than
passed in as separate arguments.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2d60a5f4-7e8e-4587-b8f5-d881d724a5ee

📥 Commits

Reviewing files that changed from the base of the PR and between d9a7d9e and 8a5c32d.

📒 Files selected for processing (2)
  • crates/lance-graph-planner/src/lib.rs
  • crates/lance-graph-planner/src/temporal.rs

@AdaWorldAPI AdaWorldAPI merged commit 73ac9b0 into main Jun 4, 2026
6 checks passed
AdaWorldAPI added a commit that referenced this pull request Jun 5, 2026
Tailored cross-repo view onto the substrate-b endgame architecture
from lance-graph's runtime-side perspective. Master doc lives in
AdaWorldAPI/OGAR/docs/SUBSTRATE-ENDGAME.md (five-rooms forward-looking
architecture, 1345 lines). This view focuses on:

  - Room 1: lance-graph-contract, lance-graph-ontology, callcenter
    (commit_event sibling — PR #467, merged), planner::temporal
    (PR #468), supervisor, cognitive-shader-driver
  - Room 2: Kanban polyglot work-item trait, HTTP-sidecar bridge,
    §14 oracle harness, optional BEAM bridge
  - Room 3: callcenter actors per OGAR Class, version_watcher push
    stream, RubiconWriter Phase 2 dynamic regeneration for OP's
    Workflow table
  - Room 4: OpenTelemetry exposition, cognitive-event-row stream
    for sexy-tier viz, actor-tree topology API
  - Room 5: stable public API + SemVer, runtime getting-started,
    reference deployment

Plus cross-references to OGAR's ADR doc (ADR-008 commit_event,
ADR-009 temporal two-axis, ADR-018 Kanban polyglot are lance-graph-
touching decisions) + companion runtime references + open items
lance-graph owns + compact priority map.

Pure docs; navigation aid for runtime sessions picking up the
endgame architecture without re-reading the full OGAR master.

https://claude.ai/code/session_01PBTGaPCSnnt6u3pjXpbLwY
AdaWorldAPI pushed a commit that referenced this pull request Jun 6, 2026
…g, lint

CodeRabbit / Codex review addressed:

1. soa_envelope::verify_layout() — P2 correctness fix
   Previously only checked that column widths *sum* to the declared stride.
   A column whose end offset exceeds the stride (e.g. two 4-byte columns at
   offsets 4 and 8 with stride 8) passed the sum check but placed data outside
   every row. Now each column's end offset is checked against stride directly
   before the pairwise overlap scan. New test: column_past_stride_caught.
   Total: 8 tests, all green.

2. soa-three-tier-model.md — Major: target-state vs current-state
   Sections that asserted "there is no baton/emission" in present tense while
   MailboxSoA::emit() still exists in source now carry explicit
   "Target state:" / "Current state:" labels. The removal is scheduled, not
   yet landed — the doc now says so.

3. CLAUDE.md — Major: patch-warning wording
   "treat it as a build error to fix" was too absolute; transitive semver
   mismatch is a legitimate cause. Reworded to "policy alert — verify direct
   deps and Cargo.lock wiring; track/resolve transitive blockers explicitly."

4. q3-standing-wave-falsification.md — Minor lint
   Blockquote lines with multiple spaces after `>` normalized (MD027).
   Unlabelled fenced code block at line 252 given `text` tag (MD040).

5. q4-hhtl-audit.md — Minor lint
   Blockquote spacing normalized (MD027).

6. New plan + epiphany (board hygiene for this session's findings)
   .claude/plans/cycle-coherent-soa-snapshot-v1.md — Arc-swap COW at column
   granularity; 6 deliverables D-SOA-SNAP-1..6; the byte-scale complement to
   temporal.rs row-scale deinterlace (PR #468).
   .claude/board/EPIPHANIES.md — E-DEINTERLACE-TWO-SCALES prepended.
   .claude/board/INTEGRATION_PLANS.md — plan entry prepended.

https://claude.ai/code/session_0147hSzjmWZDuy2MSQNrhEK5
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant