feat(arm-discovery): Aerial+ rule mining → Rust transcode + ruff DTO/SPO/codegen synergy map#436
Conversation
…/SPO/codegen synergy map
New standalone zero-dep crate `lance-graph-arm-discovery` (D-ARM-13) — the
Rust transcode of Aerial+ neurosymbolic association-rule mining (Karabulut
2025, arXiv 2504.19354v1), the upstream runtime-data proposer leg of
streaming-arm-nars-discovery-v1. Supersedes the plan §14 deferral
("autoencoder stays in Python") per the user's transcode directive.
Faithful port:
- encode: one-hot FeatureSpec + Dataset + support/confidence counting
- aerial::autoencoder: under-complete denoising AE, per-feature softmax +
cross-entropy, hand-written backprop, seeded SplitMix64 (reproducible)
- aerial::extract: Algorithm 1 reconstruction-probe rule extraction
(mark antecedent, uniform elsewhere, forward, tau_a/tau_c gates), then
support/confidence confirmed on the data
- translator::arm_to_nars: (support, confidence, n) -> NARS (f, c) verbatim
per paper section 2/3.3; (f,c) == TruthValue::new == ruff_spo_triplet::Triple
- ndjson: emits the exact {"s","p","o","f","c"} line the SPO store loader reads
Verified standalone: 35/35 tests pass, clippy -D warnings clean. The
nondeterministic AE is kept out of the deterministic lance-graph compile path
(excluded crate, feature-gated, seeded) and emits CandidateRule *proposals*
gated by the downstream ratification council.
Synergy map (.claude/knowledge/aerial-arm-ruff-spo-codegen-synergies.md):
the Aerial leg is the runtime-data frontend of a three-frontend / one-substrate
/ two-codegen bracket whose substrate (ruff_spo_triplet::Triple) and codegen
legs (ruff_python_codegen || op_emitter.rs) already exist. ruff_python_dto_check
is the static-AST sibling frontend. Key gap surfaced: ruff_spo_triplet::Predicate
is a closed vocabulary with no implication/association predicate, so loading ARM
rules through that ndjson path needs `Implies` added first (D-ARM-SYN-1,
council-gated). D-ARM-SYN-2/3 wire the ModelGraph adapter + ArmDiscovered truth
calibration.
Board: STATUS_BOARD (D-ARM-13 + D-ARM-SYN-1/2/3), EPIPHANIES
(E-DISCOVERY-CODEGEN-BRACKET-1 realised), LATEST_STATE (inventory), AGENT_LOG.
https://claude.ai/code/session_012SorR8UbtEvYmbX8cXftj7
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThis PR lands D-ARM-13: a deterministic integer-only codebook-probe Aerial+ rule discovery backend replacing the prior seeded autoencoder, with NARS truth translation and NDJSON SPO output. Includes council-reviewed crate, synergy documentation identifying predicate-vocabulary integration gaps, and board updates sequencing Jirak floor and contract-homing dependencies. ChangesAerial+ Deterministic Codebook Discovery & Integration
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 5
🤖 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.
Inline comments:
In @.claude/board/STATUS_BOARD.md:
- Line 636: Update the status cell for row D-ARM-13 in STATUS_BOARD.md to use a
defined legend taxonomy value (e.g., replace "**Shipped (branch)**" with "In PR"
or "In progress"), and move the branch detail "branch `claude/jolly-cori-clnf9`"
from the status column into the evidence/details column for the
lance-graph-arm-discovery::aerial entry so the board uses a canonical status and
retains the branch information alongside test/clippy evidence.
In @.claude/knowledge/aerial-arm-ruff-spo-codegen-synergies.md:
- Around line 25-38: The fenced code block showing the ASCII diagram (containing
symbols like ruff_python_dto_check, ruff_spo_triplet, op_emitter.rs,
ruff_python_codegen, lance-graph-arm-discovery/Aerial+) is missing a language
tag; update that opening triple-backtick to include a language (e.g., ```text)
so the markdown linter rule MD040 passes and the document in
.claude/knowledge/aerial-arm-ruff-spo-codegen-synergies.md is lint-clean.
In `@crates/lance-graph-arm-discovery/Cargo.toml`:
- Around line 11-13: The comment in the Cargo.toml for the crate
lance-graph-arm-discovery wrongly states it is excluded "exactly like bgz17 /
deepnsm / bgz-tensor" while bgz-tensor is now a workspace member; update the
comment text in Cargo.toml (the introductory comment block) to remove or
rephrase the specific comparison—e.g., say the crate is a standalone,
zero-dependency crate excluded from the workspace (omit the bgz-tensor
reference) or list only currently excluded crates (bgz17, deepnsm) so the
wording is accurate for future maintenance.
In `@crates/lance-graph-arm-discovery/src/aerial/autoencoder.rs`:
- Around line 96-97: The public APIs must reject mismatched inputs/specs at
runtime: change forward to validate input.len() == self.dim and return a Result
(e.g., Result<(Vec<f32>,Vec<f32>), Error>) or otherwise return an explicit error
instead of relying on debug_assert_eq!, and perform this check before any
indexing; likewise, in mean_loss and train validate that data.spec matches the
autoencoder’s stored FeatureSpec/block layout (the same shape used to construct
self.blocks/self.dim) before calling data.spec.encode(row) and applying
self.blocks/softmax/loss, returning an error if they differ; add clear error
messages referencing the expected dim/block layout and the provided spec so
callers can handle mismatches.
In `@crates/lance-graph-arm-discovery/src/aerial/mod.rs`:
- Around line 56-65: The AerialParams default allows hidden_dim that can be >=
the dataset encoded dimension, violating the documented H < D invariant; in the
fit path (the function named fit that constructs the autoencoder/extractor)
validate hidden_dim against the dataset's encoded dimension (from ExtractParams
or the extractor's output dimension) and either reject with a clear error or
clamp it to a value strictly less than D (e.g., D-1) before constructing the
autoencoder; update both places that construct the extractor/autoencoder (where
hidden_dim from AerialParams is used) to perform this check and use the adjusted
value so the model is always under-complete.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 622538f0-09d9-4013-bf9c-f27cbd0b3b49
⛔ Files ignored due to path filters (1)
crates/lance-graph-arm-discovery/Cargo.lockis excluded by!**/*.lock
📒 Files selected for processing (17)
.claude/board/AGENT_LOG.md.claude/board/EPIPHANIES.md.claude/board/LATEST_STATE.md.claude/board/STATUS_BOARD.md.claude/knowledge/aerial-arm-ruff-spo-codegen-synergies.mdCargo.tomlcrates/lance-graph-arm-discovery/Cargo.tomlcrates/lance-graph-arm-discovery/README.mdcrates/lance-graph-arm-discovery/src/aerial/autoencoder.rscrates/lance-graph-arm-discovery/src/aerial/extract.rscrates/lance-graph-arm-discovery/src/aerial/mod.rscrates/lance-graph-arm-discovery/src/aerial/rng.rscrates/lance-graph-arm-discovery/src/encode.rscrates/lance-graph-arm-discovery/src/lib.rscrates/lance-graph-arm-discovery/src/ndjson.rscrates/lance-graph-arm-discovery/src/rule.rscrates/lance-graph-arm-discovery/src/translator.rs
| | D-ARM-10 | `op_emitter::bucket_corpus` ratification filter (`confidence ≥ Ratified`) + 2 tests | lance-graph-ontology::op_emitter | 30 | HIGH | **Queued** | depends on D-ARM-1 | | ||
| | D-ARM-11 | `style_recipe.rs` rule 8 — ArmDiscovered backing adds `DAtom::Compute` weight 2 (provisional) | lance-graph-ontology::style_recipe | 80 | MED | **Queued** | depends on D-ARM-1 | | ||
| | D-ARM-12 | End-to-end pipeline test + bench (synthetic Odoo feed → all 5 stages → council micro-batch) | lance-graph-arm-discovery::tests + benches | 400 | MED | **Queued** | depends on Waves 1-6; informs OQ-ARM-2 + OQ-ARM-7 | | ||
| | D-ARM-13 | **Aerial+ Rust transcode** — denoising autoencoder (softmax/feature + CE, paper §3.2) + Algorithm 1 reconstruction-probe rule extraction (§3.3) + seeded `Rng` + `AerialProposer` impl of `Proposer`. User-directed port that plan §14 had deferred ("autoencoder stays in Python"); supersedes that deferral. | lance-graph-arm-discovery::aerial | ~1.2K | HIGH | **Shipped (branch)** | branch `claude/jolly-cori-clnf9`; standalone zero-dep crate (excluded); 35/35 tests, clippy `-D warnings` clean. Keeps the nondeterministic AE OUT of the deterministic compile path; output is a *proposal* gated by Stage D. | |
There was a problem hiding this comment.
Normalize status value to the legend taxonomy.
**Shipped (branch)** is not a defined status in the legend. Please switch to a defined status (e.g., In progress or In PR) and keep branch details in the evidence column to avoid downstream board-status ambiguity.
🤖 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 @.claude/board/STATUS_BOARD.md at line 636, Update the status cell for row
D-ARM-13 in STATUS_BOARD.md to use a defined legend taxonomy value (e.g.,
replace "**Shipped (branch)**" with "In PR" or "In progress"), and move the
branch detail "branch `claude/jolly-cori-clnf9`" from the status column into the
evidence/details column for the lance-graph-arm-discovery::aerial entry so the
board uses a canonical status and retains the branch information alongside
test/clippy evidence.
| pub fn forward(&self, input: &[f32]) -> (Vec<f32>, Vec<f32>) { | ||
| debug_assert_eq!(input.len(), self.dim); |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the current public guards in the autoencoder API.
rg -n -C2 'pub fn forward|debug_assert_eq!|pub fn mean_loss|pub fn train' crates/lance-graph-arm-discovery/src/aerial/autoencoder.rsRepository: AdaWorldAPI/lance-graph
Length of output: 809
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="crates/lance-graph-arm-discovery/src/aerial/autoencoder.rs"
# Show forward implementation
sed -n '80,130p' "$FILE" | nl -ba | sed -e 's/^/FWD|/'
# Show mean_loss implementation
sed -n '120,200p' "$FILE" | nl -ba | sed -e 's/^/ML|/'
# Show train signature + body start
sed -n '200,320p' "$FILE" | nl -ba | sed -e 's/^/TR|/'
# Look for any runtime spec/dim/block validation in this file
rg -n "FeatureSpec|data\.spec|spec\.|num_features|block\(|self\.blocks|assert|ensure|debug_assert" "$FILE"Repository: AdaWorldAPI/lance-graph
Length of output: 110
🏁 Script executed:
#!/bin/bash
set -euo pipefail
FILE="crates/lance-graph-arm-discovery/src/aerial/autoencoder.rs"
show_range () {
local start="$1" end="$2" prefix="$3"
awk -v s="$start" -v e="$end" -v p="$prefix" 'NR>=s && NR<=e { printf "%s%4d: %s\n", p, NR, $0 }' "$FILE"
}
show_range 80 130 "FWD|"
show_range 120 200 "ML|"
show_range 180 280 "TR|"
# Search for spec/block/dim validation in this file
rg -n "FeatureSpec|data\.spec|spec\.|num_features|block\(|self\.blocks|assert!|assert_eq!|ensure!|debug_assert" "$FILE" || trueRepository: AdaWorldAPI/lance-graph
Length of output: 11465
Reject mismatched inputs/specs in the public autoencoder public APIs
forward (lines 96-97) only uses debug_assert_eq!, but it then indexes input[i] for i in 0..self.dim; in release, longer inputs get silently truncated and shorter inputs will panic on indexing. mean_loss/train (lines 135-145, 153-165) encode via data.spec.encode(row) and then apply softmax/loss using self.blocks, with no runtime check that data.spec matches the autoencoder’s original FeatureSpec (dim + block layout), so mismatched specs can yield incorrect training/loss even when dim happens to match.
Suggested guardrails
pub fn forward(&self, input: &[f32]) -> (Vec<f32>, Vec<f32>) {
- debug_assert_eq!(input.len(), self.dim);
+ assert_eq!(input.len(), self.dim, "input dim mismatch");
// Encoder: h = tanh(We·x + be)
let mut h = vec![0.0f32; self.hidden];+fn assert_spec_compatible(&self, spec: &FeatureSpec) {
+ assert_eq!(spec.dim(), self.dim, "encoded dim mismatch");
+ let blocks: Vec<(usize, usize)> = (0..spec.num_features()).map(|f| spec.block(f)).collect();
+ assert_eq!(blocks, self.blocks, "feature block layout mismatch");
+}
+
pub fn mean_loss(&self, data: &Dataset) -> f32 {
+ self.assert_spec_compatible(&data.spec);
if data.is_empty() {
return 0.0;
} pub fn train(
&mut self,
data: &Dataset,
epochs: usize,
learning_rate: f32,
noise: f32,
rng: &mut Rng,
) -> f32 {
+ self.assert_spec_compatible(&data.spec);
if data.is_empty() {
return 0.0;
}🤖 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-arm-discovery/src/aerial/autoencoder.rs` around lines 96 -
97, The public APIs must reject mismatched inputs/specs at runtime: change
forward to validate input.len() == self.dim and return a Result (e.g.,
Result<(Vec<f32>,Vec<f32>), Error>) or otherwise return an explicit error
instead of relying on debug_assert_eq!, and perform this check before any
indexing; likewise, in mean_loss and train validate that data.spec matches the
autoencoder’s stored FeatureSpec/block layout (the same shape used to construct
self.blocks/self.dim) before calling data.spec.encode(row) and applying
self.blocks/softmax/loss, returning an error if they differ; add clear error
messages referencing the expected dim/block layout and the provided spec so
callers can handle mismatches.
…evisions Convened a 3-savant brutal review (brutally-honest-tester, iron-rule-savant, dto-soa-savant — the Stage-D ratification ensemble applied to the ARM code). Consolidated verdict: LAND-with-revision (zero P0; code independently re-verified 35/35 + 17/17 no-default-features + clippy -D warnings clean). All converged findings addressed (prose/honesty/tech-debt, no logic change): - "loads through the same loader / byte-compatible" was split-true: lance_graph::parse_triples accepts `implies`, but ruff_spo_triplet::from_ndjson rejects it (closed vocab) until D-ARM-SYN-1. Downgraded to "shape-compatible" with the precise caveat in ndjson/translator/lib/README. - rule::passes doc claimed a Jirak floor that does not exist (D-ARM-7 Queued, `jirak` appears 0x). Made honest; filed ISSUE ARM-JIRAK-FLOOR as a hard prerequisite before wiring to a live SpoStore (D-ARM-5). - contract-homing drift: local CandidateRule (n:u32) disagrees with planned D-ARM-2 (WindowMetadata); "shape identical" promise was already false. Corrected docs; filed TD-ARM-CARRIER-FORK with the pub-use-when-D-ARM-2-lands path (firewall forbids depending on lance-graph, not on zero-dep lance-graph-contract). - "bit-identical weights" -> intra-platform reproducibility footnote. Refuted by the tester: suspected fmt_f32 drift vs serde_json (0 mismatches). Iron-rule confirmed arm_to_nars round-trips into TruthValue::revision (w=m) with no rival kernel; I-SUBSTRATE-MARKOV / I-VSA-IDENTITIES / I-LEGACY-API all yield. Board: ISSUES (ARM-JIRAK-FLOOR), TECH_DEBT (TD-ARM-CARRIER-FORK), AGENT_LOG, reviews/ (council-verdict + iron-rule-savant). https://claude.ai/code/session_012SorR8UbtEvYmbX8cXftj7
…w state - D-ARM-9 (Aerial+ IPC client) → Superseded by D-ARM-13: the native in-process transcode replaces the Python IPC client; the determinism-boundary rationale is met in-process via seed + feature-gate + exclude. - D-ARM-4 (translator) → Partially shipped (branch): arm_to_nars / NarsTruth / CandidateTriple / FeedProjector landed inside D-ARM-13; remaining = real Odoo projector + contract homing. - D-ARM-7 (Jirak) → Queued — HARD PREREQUISITE: ISSUE ARM-JIRAK-FLOOR; must land before D-ARM-5 wires the proposer to a live SpoStore. - D-ARM-2 (Proposer/CandidateRule carrier) → note local mirrors shipped in D-ARM-13; TD-ARM-CARRIER-FORK; field set diverges (n:u32 vs WindowMetadata). https://claude.ai/code/session_012SorR8UbtEvYmbX8cXftj7
…ook-probe (palette256)
The v1 D-ARM-13 transcode reproduced Aerial+ literally — an f32 denoising
autoencoder. That was a substrate regression: this stack addresses by exact
codebook CAM, never float similarity (faiss-homology-cam-pq.md; I-VSA-IDENTITIES).
Aerial+'s reconstruction probe ("mark the antecedent, read off the
high-probability consequents") is mechanically a nearest-neighbour query, which
the palette256 distance table answers exactly and in integers at rho=0.9973 vs
cosine. So the float net was a slow, seed-dependent approximation of a lookup
the substrate already performs.
Changes:
- DELETE aerial/autoencoder.rs + aerial/rng.rs (the f32 net + its seed)
- ADD aerial/codebook.rs: zero-dep CodebookDistance trait + MatrixDistance
reference impl (real impl = bgz17::PaletteDistanceTable / BLASGraph splat
top-k / HDR-popcount, injected consumer-side)
- aerial/extract.rs: codebook top-k within theta + integer data confirmation
- rule.rs: integer evidence counts (cooccur/antecedent_count/window) + ppm
cross-multiply gates (no f32 in the decision path)
- translator.rs: TruthU8 (= CausalEdge64 confidence_u8 + i4 mantissa); f32
NarsTruth kept only as the downstream-contract edge
- encode.rs: integer-only (dropped the one-hot f32 vector, bin(), and the f32
support/confidence helpers)
Verified: 28/28 tests, clippy -D warnings clean, zero f32 in the aerial/
discovery path (audit). Float survives only at the TruthValue/Triple
serialization edge (those downstream contracts are themselves f32).
Structural payoff: float was the only nondeterminism. Removing it makes the
probe bitwise-deterministic, so it joins the deterministic trunk beside
pair-stats (D-ARM-3); the nondeterminism firewall and D-ARM-9 (Python-IPC
isolation) are moot. EPIPHANIES E-ARM-PROBE-IS-CODEBOOK-TOPK.
https://claude.ai/code/session_012SorR8UbtEvYmbX8cXftj7
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
crates/lance-graph-arm-discovery/src/encode.rs (1)
97-101:⚠️ Potential issue | 🟠 Major | ⚡ Quick winReject rows with category codes outside the declared cardinality.
Dataset::newonly checks row length. If a row containscategory >= spec.cardinality(feature), the dataset is accepted but discovery silently undercounts that feature because no generatedItemcan ever match it. Validate each cell against the schema when constructing the dataset.Suggested fix
pub fn new(spec: FeatureSpec, rows: Vec<Vec<u32>>) -> Self { - for r in &rows { - assert_eq!(r.len(), spec.num_features(), "row arity mismatch"); + for (row_idx, r) in rows.iter().enumerate() { + assert_eq!(r.len(), spec.num_features(), "row arity mismatch"); + for (feature_idx, &category) in r.iter().enumerate() { + assert!( + category < spec.cardinality(feature_idx), + "row {row_idx} feature {feature_idx} category {category} out of range" + ); + } } Self { spec, rows } }🤖 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-arm-discovery/src/encode.rs` around lines 97 - 101, The constructor pub fn new(spec: FeatureSpec, rows: Vec<Vec<u32>>) -> Self currently only checks row arity; update it to also validate each cell's category code against the schema by iterating rows and columns and asserting (or returning an error) that each code < spec.cardinality(feature_index). Reference the constructor new and the method/field spec.cardinality (or equivalent on FeatureSpec) and add a per-cell check that includes feature index, offending code, and cardinality in the assertion/error message so invalid category codes are rejected at dataset construction time.
🤖 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.
Inline comments:
In `@crates/lance-graph-arm-discovery/src/aerial/codebook.rs`:
- Around line 60-69: The Item flattening trusts Item.feature and Item.category
and can map into the wrong slot; update fn code(&self, it: Item) to validate
bounds: ensure (it.feature as usize) < self.offsets.len(), compute base =
self.offsets[it.feature as usize], compute next = self.offsets.get(it.feature as
usize + 1).copied().unwrap_or(self.total_slots_or_last_offset) (or use
self.table length divided by self.dim if needed) and ensure (it.category as
usize) < (next - base); if either check fails, panic with a clear message (or
return a Result/Option and propagate) so invalid Item never silently aliases
another feature; then in impl CodebookDistance for MatrixDistance::distance call
code(a)/code(b) only after validation and use checked indexing into self.table
(or keep panicking) to avoid returning a wrong distance.
In `@crates/lance-graph-arm-discovery/src/aerial/extract.rs`:
- Around line 87-94: The code forces at least 1-item antecedents by using
params.max_antecedent.max(1); change this to honor a caller-specified 0: check
params.max_antecedent == 0 up front and return no rules (or otherwise reject the
call) instead of forcing singletons, then set max_ant = params.max_antecedent
(without .max(1)) and proceed with the existing loops over feature_combinations,
item_product and probe that populate rules.
In `@crates/lance-graph-arm-discovery/src/translator.rs`:
- Around line 35-48: The function arm_to_truth_u8 currently allows k == 0 which
makes any cooccur > 0 become dogmatic; add an explicit check at the top of
arm_to_truth_u8 that rejects k == 0 (e.g. assert!(k > 0, "arm_to_truth_u8: k
must be > 0") or otherwise return an error/unknown value), so the subsequent
confidence computation (using m, denom and the confidence variable) never runs
with a degenerate denom; reference the arm_to_truth_u8 function, the k
parameter, and the confidence/denom calculation when adding this guard.
---
Outside diff comments:
In `@crates/lance-graph-arm-discovery/src/encode.rs`:
- Around line 97-101: The constructor pub fn new(spec: FeatureSpec, rows:
Vec<Vec<u32>>) -> Self currently only checks row arity; update it to also
validate each cell's category code against the schema by iterating rows and
columns and asserting (or returning an error) that each code <
spec.cardinality(feature_index). Reference the constructor new and the
method/field spec.cardinality (or equivalent on FeatureSpec) and add a per-cell
check that includes feature index, offending code, and cardinality in the
assertion/error message so invalid category codes are rejected at dataset
construction time.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: 94d8b281-49dd-4b1e-be28-332dbfba32fc
📒 Files selected for processing (12)
.claude/board/AGENT_LOG.md.claude/board/EPIPHANIES.md.claude/board/STATUS_BOARD.mdcrates/lance-graph-arm-discovery/Cargo.tomlcrates/lance-graph-arm-discovery/README.mdcrates/lance-graph-arm-discovery/src/aerial/codebook.rscrates/lance-graph-arm-discovery/src/aerial/extract.rscrates/lance-graph-arm-discovery/src/aerial/mod.rscrates/lance-graph-arm-discovery/src/encode.rscrates/lance-graph-arm-discovery/src/lib.rscrates/lance-graph-arm-discovery/src/rule.rscrates/lance-graph-arm-discovery/src/translator.rs
✅ Files skipped from review due to trivial changes (3)
- crates/lance-graph-arm-discovery/README.md
- .claude/board/EPIPHANIES.md
- .claude/board/STATUS_BOARD.md
🚧 Files skipped from review as they are similar to previous changes (1)
- crates/lance-graph-arm-discovery/Cargo.toml
…pcount count loop The data-confirmation count loop (rows satisfying a conjunction of items) is the faiss-homology "SIMD batch-AND over the SoA facet column" workload. count_matching on the AoS Dataset is an O(rows x items) rescan, called once per candidate. - bitset.rs: RowMasks transposes the window once into one u64 bitset per (feature, category) item; every candidate count is then AND + popcount over &[u64]. 1- and 2-item conjunctions (the probe hot path) allocate nothing. - simd.rs: popcount / and_popcount. Scalar u64::count_ones by default (crate stays std-only and independently verifiable); the `ndarray-simd` feature routes through ndarray::simd::U64x8 (from_slice / & / popcnt / to_array) per ndarray-vertical-simd-alien-magic.md — zero raw intrinsics, zero cfg(target_arch) in this crate. - extract.rs: the probe counts via RowMasks instead of rescanning the dataset. Verified: default (scalar, zero-dep) 33/33 + clippy -D warnings clean; --features ndarray-simd 33/33 + clippy clean (ndarray builds as a path dep with default-features=false + std; ndarray-rand is not pulled). target-cpu: the U64x8 path is correct on any target but only emits the real vector kernels (AVX-512 VPOPCNTQ, AMX) under -C target-cpu=native or -C target-cpu=x86-64-v4; otherwise it is ndarray's scalar fused-loop fallback. https://claude.ai/code/session_012SorR8UbtEvYmbX8cXftj7
…g; jc resolves both aerial seams
Architecture synthesis (no new code — the seam already exists). Grounds the
user's direction (wire the 10000^2 Gaussian-splat BLASGraph top-k as the aerial
oracle for OWL/DOLCE+ SPO HHTL class/basin discovery -> deterministic Wikidata
compression, adjacent to the jc EWA-sandwich splat) against real workspace
artifacts.
Finding (E-ARM-JC-RESOLVES-BOTH-SEAMS): aerial's two open seams both resolve to
crates/jc (Jirak-Cartan):
- CodebookDistance oracle <- jc builds + certifies the frozen [u32;dim^2] table
offline: ewa_sandwich{,_3d} (splat Sigma-push-forward for ndarray::hpc::splat3d),
sigma_codebook_probe (the rho=0.9973 source: 256-codebook R^2>=0.99 viability),
pflug (CAM-PQ/HHTL Le-faithful).
- D-ARM-7 Jirak floor <- jc::jirak (weak-dependence Berry-Esseen rate n^(p/2-1)).
Float boundary = CAM-PQ doctrine end-to-end: float lives ONLY in jc's offline
build+certify; it emits a frozen integer artifact; aerial's online path stays
integer. The "deterministically compress Wikidata" claim is float-free at runtime.
No new aerial dependency — the certified table feeds the existing MatrixDistance
/CodebookDistance seam.
- knowledge/splat-codebook-aerial-wikidata-compression.md: the full wiring map
- EPIPHANIES: E-ARM-JC-RESOLVES-BOTH-SEAMS
- STATUS_BOARD: D-ARM-7 engine pointer (jc::jirak), D-ARM-13 -> 33/33 + SIMD
clause, new D-ARM-14 (splat-codebook oracle + Wikidata skeleton discovery)
- aerial/codebook.rs: oracle doc names jc + the offline-float/online-integer boundary
https://claude.ai/code/session_012SorR8UbtEvYmbX8cXftj7
Verified all 11 review comments against current (post de-float + SIMD) code. 5 still valid, 6 outdated/resolved. Fixed the 5, each with a regression test: - aerial/codebook.rs: MatrixDistance::code validates (feature, category) bounds so an invalid Item fails fast instead of aliasing another feature's block and returning a real-but-wrong distance. - aerial/extract.rs: extract_rules honors max_antecedent == 0 (returns no rules) instead of forcing 1-item antecedents via .max(1). - translator.rs: arm_to_truth_u8 asserts k > 0. k=0 made any cooccur>0 quantise to confidence=255 (1.0) — dogmatic truth at the SPO edge. - encode.rs: Dataset::new rejects rows with category >= cardinality (previously accepted, then silently undercounted since no Item could ever match). - knowledge/aerial-arm-ruff-spo-codegen-synergies.md: add `text` lang tag to a fenced block (MD040). Skipped (with reason): autoencoder forward/mean_loss/train guards + mod.rs hidden_dim invariant + Cargo.toml bgz-tensor comment all target code deleted or rewritten by the de-float (8681cdf); STATUS_BOARD "Shipped (branch)" is an established board convention used by every D-ARM row. Verified: 37/37 tests (33 + 4 regression) + clippy -D warnings clean on both default and --features ndarray-simd. https://claude.ai/code/session_012SorR8UbtEvYmbX8cXftj7
…k (Cognitive-RISC N1) Expose the per-row class/shape discriminator on the SoA view: class_id()/class_id_at() alias the existing entity_type u16 slot (= OGIT EntityTypeId), so the class hook lands with ZERO new column (R1 'one SoA never transformed') and zero new dep. Class machinery (labels/projection/jinja templates) resolves one layer up via lance-graph-ontology, never in the SoA/kv-lance columns. This is the freeze-time N1 hook; aerial+ (#436) is the discovery engine that populates it. Board: EPIPHANIES E-ARIGRAPH-IS-AN-ISLAND (dead-wiring audit, machete-corroborated) + E-AERIAL-IS-THE-DISCOVERY-PROPOSER (synergy synthesis). 485 contract tests green; soa_view.rs rustfmt-clean. https://claude.ai/code/session_01R9AWgFa65uPnLyS2my2d2R
… + auto-resolve
User instruction: "create the integration plan as a list of possibilities,
then use the council and brutally honest review to recalibrate, then continue
autoattended autonomous decision making and auto resolve."
Composed `post-438-integration-options-v1.md` with 8 options + 6 combinations.
Spawned 4 council reviewers in parallel (Opus, single main-thread turn) with
identical brief but diverse lenses:
R1 (architectural-fit) — B+C conditional on N1 class_id paired pass + u16
R2 (prior-art / drift) — B+H; REJECTS C as re-litigating user-owned forks;
flags AriGraph hot↔cold bridge as the missing
integration target the options doc never named
R3 (integration-coordination) — B+D; REJECTS C — in-flight collision with #439
on lance-graph-contract (31 commits, unstable,
KanbanMove const _ ≤16B size assertion)
R4 (brutal-critic / bias-hunter)— B+G; REJECTS C as ego-shipping; tier-set spec
disagreement = SPEC FREEZE, not Claude-session;
cites session pattern of 3 prior hallucinations
Auto-resolve per options-doc §5:
- B unanimous (4/4) → EXECUTED (2 stale-citation fixes this commit)
- C: 3/4 reject → NOT EXECUTED
- OD-1/2/3 + canonical-spec-self-disagreement → escalated to ISSUES.md as
explicit SPEC-OWNER decisions (R2+R4 unanimous framing)
- R2's AriGraph-bridge finding → captured as EPIPHANY (new, missed by all
prior session work): the discovery_origin byte's natural home is a column
on the AriGraph hot↔cold bridge, not the mailbox-SoA byte
- G (chess bring-up): 2/4 endorse → queued to STATUS_BOARD as D-CHESS-BRINGUP-1
(NOT executed on this branch per R1 "needs its own branch + freeze-decision
authority"); unblocked by #436's Rust Aerial+ (user-flagged 2026-05-30)
- D (help #439): 1/4 → below threshold, not executed
- H (cargo clean, 3.3G): flagged for user, not auto-executed (workflow not
architecture)
Fixed stale citations in own docs:
- discovery-origin-provenance-reconciliation §8: specs are on main now
(via main commits d1635db, 93ac046, a16d0f4, 45276eb), not on a
separate cognitive-risc-core branch as v1 of the doc claimed pre-rebase
- odoo-blueprint-survival-dossier §7 "Concrete next moves": Wave 1 went
D-ARM-13/14 via #436/#438, not D-ARM-1/2 as originally planned
Documentation only. No code or in-flight contract modified. Contract carrier
work (D-ARM-1/D-ARM-2/D-MBX-A6-P3) explicitly held until the spec owner
reconciles the canonical tier-set disagreement and the user picks
proposer-id width (OD-1).
https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
… + auto-resolve
User instruction: "create the integration plan as a list of possibilities,
then use the council and brutally honest review to recalibrate, then continue
autoattended autonomous decision making and auto resolve."
Composed `post-438-integration-options-v1.md` with 8 options + 6 combinations.
Spawned 4 council reviewers in parallel (Opus, single main-thread turn) with
identical brief but diverse lenses:
R1 (architectural-fit) — B+C conditional on N1 class_id paired pass + u16
R2 (prior-art / drift) — B+H; REJECTS C as re-litigating user-owned forks;
flags AriGraph hot↔cold bridge as the missing
integration target the options doc never named
R3 (integration-coordination) — B+D; REJECTS C — in-flight collision with #439
on lance-graph-contract (31 commits, unstable,
KanbanMove const _ ≤16B size assertion)
R4 (brutal-critic / bias-hunter)— B+G; REJECTS C as ego-shipping; tier-set spec
disagreement = SPEC FREEZE, not Claude-session;
cites session pattern of 3 prior hallucinations
Auto-resolve per options-doc §5:
- B unanimous (4/4) → EXECUTED (2 stale-citation fixes this commit)
- C: 3/4 reject → NOT EXECUTED
- OD-1/2/3 + canonical-spec-self-disagreement → escalated to ISSUES.md as
explicit SPEC-OWNER decisions (R2+R4 unanimous framing)
- R2's AriGraph-bridge finding → captured as EPIPHANY (new, missed by all
prior session work): the discovery_origin byte's natural home is a column
on the AriGraph hot↔cold bridge, not the mailbox-SoA byte
- G (chess bring-up): 2/4 endorse → queued to STATUS_BOARD as D-CHESS-BRINGUP-1
(NOT executed on this branch per R1 "needs its own branch + freeze-decision
authority"); unblocked by #436's Rust Aerial+ (user-flagged 2026-05-30)
- D (help #439): 1/4 → below threshold, not executed
- H (cargo clean, 3.3G): flagged for user, not auto-executed (workflow not
architecture)
Fixed stale citations in own docs:
- discovery-origin-provenance-reconciliation §8: specs are on main now
(via main commits d1635db, 93ac046, a16d0f4, 45276eb), not on a
separate cognitive-risc-core branch as v1 of the doc claimed pre-rebase
- odoo-blueprint-survival-dossier §7 "Concrete next moves": Wave 1 went
D-ARM-13/14 via #436/#438, not D-ARM-1/2 as originally planned
Documentation only. No code or in-flight contract modified. Contract carrier
work (D-ARM-1/D-ARM-2/D-MBX-A6-P3) explicitly held until the spec owner
reconciles the canonical tier-set disagreement and the user picks
proposer-id width (OD-1).
https://claude.ai/code/session_017gZ6sPRXYPj5n7uJ7NBtRv
Summary
Transcodes Aerial+ neurosymbolic association-rule mining (Karabulut, Groth, Degeler — Neurosymbolic Association Rule Mining from Tabular Data, arXiv 2504.19354v1) to Rust as a new standalone, zero-dependency crate
lance-graph-arm-discovery— the upstream runtime-data proposer leg ofstreaming-arm-nars-discovery-v1.md(D-ARM-13). This supersedes the plan §14 deferral ("Aerial+ Rust port — the autoencoder stays in Python") per the explicit transcode directive.It also delivers the requested synergy analysis against the
ruffDTO / SPO / codegen crates.What's in the crate
Faithful, self-contained port (std only — no neural framework, no heavy deps):
encodeFeatureSpec+Dataset+ support/confidence countingaerial::rngaerial::autoencoderaerial::extractτ_a/τ_cgates), confirmed on data (§3.3)aerial(AerialProposer)Proposerimpl:fit(train) +next_batch(mine)translatorarm_to_nars:(support, confidence, n) → NARS (f, c)verbatim per §2/§3.3ndjson{"s","p","o","f","c"}lines = the SPO-store loader contractVerification
Load-bearing tests: the autoencoder learns a cross-feature dependency, Algorithm 1 recovers a planted rule while rejecting an independent feature, mined rules serialise to the SPO ndjson shape end-to-end, and runs are bit-reproducible from a seed. The crate is
excluded from the workspace (standalone pattern, likebgz17/deepnsm) so the giantlance/datafusiondependency tree is not built to verify it.Synergy map (
ruffDTO / SPO / codegen)Full write-up:
.claude/knowledge/aerial-arm-ruff-spo-codegen-synergies.md. The Aerial leg is the runtime-data frontend of a three-frontend / one-substrate / two-codegen bracket whose substrate and codegen legs already exist in therufffork:ruff_spo_triplet::Triple { s, p, o, f, c }mirrorslance_graph::graph::spo::odoo_ontology::OntologyTriplefield-for-field; its ndjson is exactly whatparse_triplesreads. Aerial'sarm_to_nars(f, c)lands on the same NARS truth scale (NarsTruth::expectation==TruthValue::expectation).ruff_python_dto_checkis the static-AST proposer ("Extracted"/Authoritative); Aerial is the runtime-data proposer ("ArmDiscovered"). SameModelGraph/TripleIR, different evidence source — Aerial surfaces co-correlations that exist only in runtime rows.op_emitter.rs(ratified SoA → Rust dispatch) ∥ruff_python_codegen::round_trip(AST → Python source). Same externalise-interpretation thesis, two languages, both downstream of the ratification firewall.Key gap surfaced (actionable):
ruff_spo_triplet::Predicateis a closed vocabulary (from_ndjsonhard-rejects unknown predicates) with no implication/association relation. Loading ARMX → Yrules through that loader requires addingImplies/CoOccursWith— a deliberate ontology change. Filed as follow-ups:Implies/CoOccursWithtoruff_spo_triplet::Predicate(council-gated).CandidateRule → ruff_spo_triplet::ModelGraphadapter.ProvenanceTier::ArmDiscovered(f,c)below theop_emitterratification gate.Determinism boundary (preserved)
Aerial+ is the only nondeterministic node in the bracket. The transcode keeps it standalone, seeded, behind the
aerialfeature, emittingCandidateRuleproposals — never committed triples. Promotion is the downstream council's job (Stage D). Nothing nondeterministic crosses into the deterministic codegen path.Board hygiene (same commit)
STATUS_BOARD(D-ARM-13 + D-ARM-SYN-1/2/3),EPIPHANIES(E-DISCOVERY-CODEGEN-BRACKET-1 realised),LATEST_STATE(contract inventory),AGENT_LOG.https://claude.ai/code/session_012SorR8UbtEvYmbX8cXftj7
Generated by Claude Code
Summary by CodeRabbit
New Features
Documentation
Chores