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
10 changes: 10 additions & 0 deletions .claude/board/entries/2026-10-31-polyfill-alias-views-l4.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# D-DT64-7 L4: production alias views for the polyfill lens — 2026-10-31

**Status:** MEASURED (`crates/lance-graph-r2il/tests/integration/polyfill_views.rs`). First step of the execution order in plan §1d.

- Each `POLYFILL` word records which of its existing aliases is its SQL name and which its Quack name (`sql_at`, `quack_at`); no name is spelled twice and no spelling rule picks the language. `FrozenWord::{name_of, name_in, is_alias}`.
- `PolyfillView::new(AliasLanguage)` answers `0xFFFF` with one label per position. The `predicate_iri` names the word (`lg:polyfill:f000/<R2IL name>`), the label the language; a position with no name in that language carries its R2IL name (SQL: `SUM`, `MIN`, `MAX`, `ALL`, where SQL's empty-selection `NULL` differs).
- `WithPolyfill::new(&consumer, &view)` answers `0xFFFF` from the polyfill view and delegates every other concept, template and DOLCE category to the consumer's view. It checks no activation (L3).
- Measured: R2IL, SQL and Quack views over the same consumer view bind the same steps and plan id; the consumer's own stale `0xFFFF` labels are refused alone and hidden behind the adapter.
- Disable runs, all red: SQL reading the Quack index (2 fail), one wrong index (2), no R2IL fallback (3), the polyfill view answering every concept (1), the adapter not routing `0xFFFF` (2), a per-language IRI (1), the adapter dropping the consumer's DOLCE category (1).
- Unchanged: `binding.rs`, the `0xC401` reading, the 9 existing polyfill tests.
3 changes: 2 additions & 1 deletion .claude/board/entries/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,14 +25,15 @@ index row, (3) no duplicate entry id. Checks 1 and 2 are deliberately
opposite directions; the stranding this convention prevents shows up in
exactly one of them, never both.

534 entries, 2026-08-06 .. 2026-10-31.
535 entries, 2026-08-06 .. 2026-10-31.

| date | entry id | finding | file |
|---|---|---|---|
| 2026-10-31 | `x2-dt64-admission-policy` | | [2026-10-31-x2-dt64-admission-policy.md](2026-10-31-x2-dt64-admission-policy.md) |
| 2026-10-31 | `r1-r2il-binding-reading` | | [2026-10-31-r1-r2il-binding-reading.md](2026-10-31-r1-r2il-binding-reading.md) |
| 2026-10-31 | `r1-native-prepared-successor` | | [2026-10-31-r1-native-prepared-successor.md](2026-10-31-r1-native-prepared-successor.md) |
| 2026-10-31 | `polyfill-positional-lens` | | [2026-10-31-polyfill-positional-lens.md](2026-10-31-polyfill-positional-lens.md) |
| 2026-10-31 | `D-DT64-7` | | [2026-10-31-polyfill-alias-views-l4.md](2026-10-31-polyfill-alias-views-l4.md) |
| 2026-10-31 | `ogar-behavior-probe-p2-p3-p9` | | [2026-10-31-ogar-behavior-probe-p2-p3-p9.md](2026-10-31-ogar-behavior-probe-p2-p3-p9.md) |
| 2026-10-31 | `D-SAG-6` | | [2026-10-31-kythe-bridge-ra-facts.md](2026-10-31-kythe-bridge-ra-facts.md) |
| 2026-10-31 | `D-XLOG-NATIVE-6` | | [2026-10-31-d-xlog-native-6-ndarray-simd-execution.md](2026-10-31-d-xlog-native-6-ndarray-simd-execution.md) |
Expand Down
215 changes: 198 additions & 17 deletions crates/lance-graph-r2il/src/polyfill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,9 @@
//! order. The opcode, arity and R2IL name of each entry are checked against
//! OGAR's `R2ILVocabulary` by test, not at run time.

use lance_graph_contract::class_view::{ClassId, ClassView};
use lance_graph_contract::ogar_codebook::compose_classid;
use lance_graph_contract::ontology::{DisplayTemplate, FieldRef};
use ogar_loco::FnIndex;
use ogar_r2il::{ALL, ANY, KEY_RUNS, MAX, MIN, SUM};

Expand All @@ -46,38 +48,119 @@ pub struct FrozenWord {
/// Every name a view may give this position. The first is the R2IL
/// opcode name.
pub aliases: &'static [&'static str],
/// Which alias is this word's SQL name, if SQL has one.
pub sql_at: Option<u8>,
/// Which alias is this word's Quack name, if Quack has one.
pub quack_at: Option<u8>,
}

const fn w(opcode: FnIndex, arity: u8, aliases: &'static [&'static str]) -> FrozenWord {
/// A language whose names a view gives the polyfill positions.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AliasLanguage {
/// OGAR's R2IL opcode names.
R2il,
/// SQL keywords and operators.
Sql,
/// `lance-graph-quack` variant names (`Agg::*`, `Cmp::*`, `Filter::*`).
Quack,
}

impl FrozenWord {
/// This word's name in `lang`, if that language has one.
#[must_use]
pub fn name_of(&self, lang: AliasLanguage) -> Option<&'static str> {
let at = match lang {
AliasLanguage::R2il => Some(0),
AliasLanguage::Sql => self.sql_at,
AliasLanguage::Quack => self.quack_at,
};
at.and_then(|i| self.aliases.get(usize::from(i)).copied())
}

/// This word's name in `lang`, or its R2IL name where `lang` has none,
/// so a view always holds one label per position.
#[must_use]
pub fn name_in(&self, lang: AliasLanguage) -> &'static str {
self.name_of(lang).unwrap_or(self.aliases[0])
}

/// Whether `label` is one of this word's names.
#[must_use]
pub fn is_alias(&self, label: &str) -> bool {
self.aliases.contains(&label)
}
}

const fn w(
opcode: FnIndex,
arity: u8,
aliases: &'static [&'static str],
sql_at: Option<u8>,
quack_at: Option<u8>,
) -> FrozenWord {
FrozenWord {
opcode,
arity,
aliases,
sql_at,
quack_at,
}
}

/// The polyfill lens, by position.
pub const POLYFILL: [FrozenWord; 17] = [
w(INT_ADD, 2, &["IntAdd", "+"]),
w(INT_SUB, 2, &["IntSub", "-"]),
w(INT_MULT, 2, &["IntMult", "*"]),
w(INT_S_DIV, 2, &["IntSDiv", "/"]),
w(INT_EQUAL, 2, &["IntEqual", "=", "Cmp::EqI32", "Cmp::EqU32"]),
w(INT_S_LESS, 2, &["IntSLess", "<", "Cmp::LtI32"]),
w(INT_AND, 2, &["IntAnd", "AND", "Filter::and"]),
w(INT_OR, 2, &["IntOr", "OR", "Filter::or"]),
w(INT_XOR, 2, &["IntXor", "XOR"]),
w(INT_NOT, 1, &["IntNot", "NOT", "Filter::negate"]),
w(POP_COUNT, 1, &["PopCount", "COUNT", "Agg::Count"]),
w(SUM, 2, &["SUM", "Agg::SumI32"]),
w(MIN, 2, &["MIN", "Agg::MinI32"]),
w(MAX, 2, &["MAX", "Agg::MaxI32"]),
w(ANY, 1, &["ANY", "EXISTS", "Agg::Any"]),
w(ALL, 1, &["ALL", "Agg::All"]),
w(INT_ADD, 2, &["IntAdd", "+"], Some(1), None),
w(INT_SUB, 2, &["IntSub", "-"], Some(1), None),
w(INT_MULT, 2, &["IntMult", "*"], Some(1), None),
w(INT_S_DIV, 2, &["IntSDiv", "/"], Some(1), None),
w(
INT_EQUAL,
2,
&["IntEqual", "=", "Cmp::EqI32", "Cmp::EqU32"],
Some(1),
Some(2),
),
w(
INT_S_LESS,
2,
&["IntSLess", "<", "Cmp::LtI32"],
Some(1),
Some(2),
),
w(
INT_AND,
2,
&["IntAnd", "AND", "Filter::and"],
Some(1),
Some(2),
),
w(INT_OR, 2, &["IntOr", "OR", "Filter::or"], Some(1), Some(2)),
w(INT_XOR, 2, &["IntXor", "XOR"], Some(1), None),
w(
INT_NOT,
1,
&["IntNot", "NOT", "Filter::negate"],
Some(1),
Some(2),
),
w(
POP_COUNT,
1,
&["PopCount", "COUNT", "Agg::Count"],
Some(1),
Some(2),
),
w(SUM, 2, &["SUM", "Agg::SumI32"], None, Some(1)),
w(MIN, 2, &["MIN", "Agg::MinI32"], None, Some(1)),
w(MAX, 2, &["MAX", "Agg::MaxI32"], None, Some(1)),
w(ANY, 1, &["ANY", "EXISTS", "Agg::Any"], Some(1), Some(2)),
w(ALL, 1, &["ALL", "Agg::All"], None, Some(1)),
w(
KEY_RUNS,
2,
&["KEY_RUNS", "COUNT DISTINCT", "Agg::CountDistinctOrderedU32"],
Some(1),
Some(2),
),
];

Expand All @@ -100,3 +183,101 @@ pub fn position_of(label: &str) -> Option<u8> {
_ => None,
}
}

/// The polyfill lens's labels in one language: a [`ClassView`] that answers
/// [`HARDCODED`] with one label per [`POLYFILL`] position, and nothing else.
///
/// Every label is an alias of its own position, so
/// [`crate::binding::prepare`] binds the same plan from every language. A
/// field's `predicate_iri` names the word, not the language
/// (`lg:polyfill:f000/<R2IL name>`); only the label changes. A position with
/// no name in the language is labelled with its R2IL name.
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct PolyfillView {
lang: AliasLanguage,
labels: Vec<FieldRef>,
}

impl PolyfillView {
/// The view of the polyfill lens in `lang`.
#[must_use]
pub fn new(lang: AliasLanguage) -> Self {
let labels = POLYFILL
.iter()
.map(|w| {
FieldRef::new(
format!("lg:polyfill:f000/{}", w.aliases[0]),
w.name_in(lang),
)
})
.collect();
Self { lang, labels }
}

/// The language this view labels the positions in.
#[must_use]
pub fn language(&self) -> AliasLanguage {
self.lang
}
}

impl ClassView for PolyfillView {
fn fields(&self, class: ClassId) -> &[FieldRef] {
if class == HARDCODED {
&self.labels
} else {
&[]
}
}
fn template(&self, _class: ClassId) -> DisplayTemplate {
DisplayTemplate::Detail
}
fn dolce_category_id(&self, _class: ClassId) -> u8 {
0
}
}

/// A consumer's [`ClassView`] with [`HARDCODED`] answered by a
/// [`PolyfillView`]: every other concept, its template and its DOLCE
/// category, come from the consumer's view unchanged. The consumer's own
/// labels for [`HARDCODED`], if any, are not consulted.
///
/// This routes one concept and checks nothing else; whether the caller's
/// classes are activated is not this adapter's question (D-DT64-7 L3).
#[derive(Debug, Clone, Copy)]
pub struct WithPolyfill<'a, V: ?Sized> {
inner: &'a V,
polyfill: &'a PolyfillView,
}

impl<'a, V: ClassView + ?Sized> WithPolyfill<'a, V> {
/// `inner` with [`HARDCODED`] answered by `polyfill`.
#[must_use]
pub fn new(inner: &'a V, polyfill: &'a PolyfillView) -> Self {
Self { inner, polyfill }
}
}

impl<V: ClassView + ?Sized> ClassView for WithPolyfill<'_, V> {
fn fields(&self, class: ClassId) -> &[FieldRef] {
Comment on lines +261 to +262

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 Delegate the remaining ClassView metadata to the consumer

When the consumer overrides optional ClassView methods, wrapping it in WithPolyfill silently replaces those answers with trait defaults even for concepts other than HARDCODED. For example, compute_dag becomes empty, is_a_parent becomes None, and epistemic_reading becomes undeclared; consequently, recomputation can be skipped, inherited rendering loses its ancestor lookup, and epistemic projection can refuse previously valid data. These defaults are defined in crates/lance-graph-contract/src/class_view.rs and are not derived from the three methods forwarded here. Delegate the remaining metadata methods for non-polyfill concepts so adding the polyfill view preserves the consumer's existing class behavior.

Useful? React with 👍 / 👎.

if class == HARDCODED {
self.polyfill.fields(class)
} else {
self.inner.fields(class)
}
}
fn template(&self, class: ClassId) -> DisplayTemplate {
if class == HARDCODED {
self.polyfill.template(class)
} else {
self.inner.template(class)
}
}
fn dolce_category_id(&self, class: ClassId) -> u8 {
if class == HARDCODED {
self.polyfill.dolce_category_id(class)
} else {
self.inner.dolce_category_id(class)
}
}
}
1 change: 1 addition & 0 deletions crates/lance-graph-r2il/tests/integration/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
pub mod binding;
pub mod binding_native;
pub mod polyfill;
pub mod polyfill_views;
Loading
Loading