From 28d4100393c2ee34ece15acfae8da33123927647 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 19:48:45 +0000 Subject: [PATCH 1/8] board: append-only erratum to the 2026-10-31 shared-relation entry (S5) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The 'equality equals the closure of E ∪ Eᵀ' result follows from the probe's authored rule edge ⇒ union; a directed CALLS edge does not imply equivalence. Original text unchanged. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01X6y3drwKSE2zSgoexheLFX --- ...-d-reasoning-codegraph-0-xlog-egglog-shared-relation.md | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/.claude/board/entries/2026-10-31-d-reasoning-codegraph-0-xlog-egglog-shared-relation.md b/.claude/board/entries/2026-10-31-d-reasoning-codegraph-0-xlog-egglog-shared-relation.md index cf8e6ab59..678b6b4d8 100644 --- a/.claude/board/entries/2026-10-31-d-reasoning-codegraph-0-xlog-egglog-shared-relation.md +++ b/.claude/board/entries/2026-10-31-d-reasoning-codegraph-0-xlog-egglog-shared-relation.md @@ -21,3 +21,10 @@ OPEN: ## Correction (2026-10-31): the traceroute The traceroute is a chain of stage identities (derivation, proof, binding, execution, replay), each referenced by an immutable identity, storing no intermediate population (`dr256-where-capstone-v1.md` §C.5). `recursive::trace` rebuilds one node path; it is not that traceroute, and a per-round delta-mask store would violate §C.5. `support` and `affected` stand as mask selections. Graph work is mask selection, not traversal. See D-DT64-7 F8 and O6. + +## Erratum (2026-11-02, appended; the text above is unchanged) +**Scope:** the bullet "egglog's equality equals the closure of `E ∪ Eᵀ`" under **Shared relation** (line 8). + +**Correction:** that equality follows from the probe's explicitly authored egglog rule `(rule ((edge a b)) ((union a b)))` (`crates/xlog-relcomp-probe/tests/shared_relation.rs:93-96`), which unions along every edge. It is a property of that rule set, not of egglog equality in general. A directed codegraph CALLS edge (or any directed code fact) does not imply equivalence. The test file states the same (`shared_relation.rs:18-19`: XLOG's closure is not an equality). + +**Source:** Semantic Grammar Recovery map (`entries/2026-11-02-coresearch-semantic-grammar-recovery.md` §4, §8), operator decision S5. From e0780b88f80f142887c545bc49adff566a3958d7 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 20:10:39 +0000 Subject: [PATCH 2/8] =?UTF-8?q?board:=20SGR=20correctness=20gates=20?= =?UTF-8?q?=E2=80=94=20results=20of=20S1=E2=80=93S6=20(#1932,=20ruff=20#13?= =?UTF-8?q?5/#136;=20A10=20blocked;=20S6=20measured,=20stopped)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01X6y3drwKSE2zSgoexheLFX --- .../entries/2026-11-02-sgr-recovery-gates.md | 18 ++++++++++++++++++ .claude/board/entries/README.md | 3 ++- 2 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 .claude/board/entries/2026-11-02-sgr-recovery-gates.md diff --git a/.claude/board/entries/2026-11-02-sgr-recovery-gates.md b/.claude/board/entries/2026-11-02-sgr-recovery-gates.md new file mode 100644 index 000000000..d9f68b084 --- /dev/null +++ b/.claude/board/entries/2026-11-02-sgr-recovery-gates.md @@ -0,0 +1,18 @@ +# Semantic Grammar Recovery: correctness gates implemented (operator decisions S1–S6), 2026-11-02 + +**Status:** IMPLEMENTED in three PRs (not merged), one finding MEASURED, one item BLOCKED on a decision, one item STOPPED on a repository-rule conflict. Follows `2026-11-02-coresearch-semantic-grammar-recovery.md`. + +| item | where | outcome | +|---|---|---| +| S5 erratum | `2026-10-31-d-reasoning-codegraph-0-xlog-egglog-shared-relation.md` (appended) | done: equality follows from the probe's authored `edge ⇒ union`; a CALLS edge does not imply equivalence | +| B4 + F8 | lance-graph #1932 `claude/sgr-coverage-gates` | residuals consumed (closed `ResidualKind`, unknown reason refused); `Codegraph::coverage()` typed gaps; `DeadFunction` runs `Partial` on any gap (residual, missing residuals file, dynamic call, partial crate). rev1 re-pinned Complete → Partial (one dynamic call) | +| B7 consumer | lance-graph #1932 | `IntakeRefusal::Placeholder` for any `?` path segment, at open and refresh | +| B7 producer + B1 | ruff #135 `claude/sgr-charon-identity` | unnamed dispatch target → `CALLEE_NOT_NAMED` residual, never `?::?`; `.effects.tsv`: one row per witnessed effect with Charon's exact assert kind (`Overflow:Mul`, `DivisionByZero`, …) and deref writes. Absence is never purity | +| B9 | ruff #136 `claude/sgr-cpp-call-identity` | `calls` resolved only and scope-qualified (`a::f`); `unresolved_calls` lookup sets with signature candidates; `dynamic_calls` count. Found by the fixture: pointer calls (`fp`) were reported as resolved calls | +| A10 | — | BLOCKED: raw5 = 0 is the legitimate base-fact code, so no reader rule can tell "stamped Direct" from "unstamped" without an encoding decision (new `Epi5Gen` with 0 = unstamped, a spare-bit flag, or a writer-side stamping API). Hazard stays pinned by `epi5_reading.rs` | +| S6 query-relative gate | AdaWorldAPI/xlog, local branch only | MEASURED: with `magic_sets = on` and a second consumer `far(X) :- reach(X, 12)`, the rewrite reports `Applied`, no decline reason, and `far` goes from {10, 11} to {}. Upstream `magic_sets.rs` is unchanged in the fork, so upstream has it too. STOPPED: the fork's `AGENTS.md` / `ENGINEERING.md` current direction forbids adding tests and running local tests; nothing was pushed | + +Every fixture was committed red before its fix; every guard has a disable run that goes red (16 disables). Two falsifiers were found vacuous by their disable runs and rewritten (codegraph identity twin queried the wrong node; ruff effects occurrence test could not see a same-kind collapse). + +## Closeout +STATUS: done (S1 items 1–3 and 5, S2, S5) / blocked (A10) / stopped (S6 fork) | OUTCOME: lance-graph #1932, ruff #135, #136 open; query-relative Magic Sets hazard measured | OPEN: A10 encoding decision; S6 fork rule conflict and the decline fix; method keys of same-named classes in different namespaces (C++); codegraph does not yet read `effects.tsv` (S3 keeps evidence grades in the lab) diff --git a/.claude/board/entries/README.md b/.claude/board/entries/README.md index 599946a89..519bb36df 100644 --- a/.claude/board/entries/README.md +++ b/.claude/board/entries/README.md @@ -25,10 +25,11 @@ 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. -554 entries, 2026-08-06 .. 2026-11-02. +555 entries, 2026-08-06 .. 2026-11-02. | date | entry id | finding | file | |---|---|---|---| +| 2026-11-02 | `sgr-recovery-gates` | | [2026-11-02-sgr-recovery-gates.md](2026-11-02-sgr-recovery-gates.md) | | 2026-11-02 | `p4-thought-git-codegraph-dt64-review` | | [2026-11-02-p4-thought-git-codegraph-dt64-review.md](2026-11-02-p4-thought-git-codegraph-dt64-review.md) | | 2026-11-02 | `p4-operator-decisions-lifecycle-lab` | | [2026-11-02-p4-operator-decisions-lifecycle-lab.md](2026-11-02-p4-operator-decisions-lifecycle-lab.md) | | 2026-11-02 | `D-TGL` | | [2026-11-02-d-tgl-release-fence-and-g10-g11-boundaries.md](2026-11-02-d-tgl-release-fence-and-g10-g11-boundaries.md) | From f1eb0bdaa6b5767a45d2a7160ef63ee93f16f485 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 20:11:57 +0000 Subject: [PATCH 3/8] =?UTF-8?q?board:=20#1931=20post-merge=20record=20(car?= =?UTF-8?q?ries=20the=20stranded=20S5=20erratum=20and=20S1=E2=80=93S6=20re?= =?UTF-8?q?sults)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01X6y3drwKSE2zSgoexheLFX --- .claude/board/PR_ARC_INVENTORY.md | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/.claude/board/PR_ARC_INVENTORY.md b/.claude/board/PR_ARC_INVENTORY.md index 29be96dcb..1b82cb55d 100644 --- a/.claude/board/PR_ARC_INVENTORY.md +++ b/.claude/board/PR_ARC_INVENTORY.md @@ -1,3 +1,14 @@ +## 2026-11-02 — lance-graph PR #1931 (merged `ef6f34999`, branch `claude/clone-repositories-71a5sw`) — /coresearch Semantic Grammar Recovery map; #1930 post-merge record + +- **Added (board only):** `entries/2026-11-02-coresearch-semantic-grammar-recovery.md`. It contains: + - a crosswalk of 48 rows (Kythe, Ruff/Charon, MLIR/DialEgg, XLOG/Soufflé) with a knowledge-kind column (code fact / justified transformation / relational derivation / reading); + - a 4-bucket recovery matrix; + - N1–N8 graded against current code; + - pre-registered falsifiers. +- **Locked:** nothing by the map itself. The operator then ruled S1–S6, executed in #1932 and ruff #135/#136. +- **Stranded:** the S5 erratum and the S1–S6 results entry were pushed after the merge point. They are carried by the next PR. +- **Confidence:** exploration. The scouts' load-bearing citations were re-read in code. + ## 2026-11-02 — lance-graph PR #1930 (merged `5f6bf1c8a`, branch `claude/clone-repositories-71a5sw`) — /coresearch XLOG / Soufflé / Crepe / DR³ map + Persistent Relational Grammar extension; #1929 plateau record - **Added (board only):** From 1791034c5b24e530dd8e6c8741e630c5a16523fb Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 20:34:51 +0000 Subject: [PATCH 4/8] board: A10 epistemic admission and NNUE compiler-heuristic feasibility (two findings) Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01X6y3drwKSE2zSgoexheLFX --- ...0-epistemic-admission-and-nnue-compiler.md | 123 ++++++++++++++++++ .claude/board/entries/README.md | 3 +- 2 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 .claude/board/entries/2026-11-02-a10-epistemic-admission-and-nnue-compiler.md diff --git a/.claude/board/entries/2026-11-02-a10-epistemic-admission-and-nnue-compiler.md b/.claude/board/entries/2026-11-02-a10-epistemic-admission-and-nnue-compiler.md new file mode 100644 index 000000000..948853366 --- /dev/null +++ b/.claude/board/entries/2026-11-02-a10-epistemic-admission-and-nnue-compiler.md @@ -0,0 +1,123 @@ +# A10 epistemic admission and NNUE compiler-heuristic feasibility (two independent findings, 2026-11-02) + +**Status:** research findings. Nothing implemented; no CE64 change; /5plus3 deferred. Operator asked for two independent findings. + +## Finding 1 — A10 epistemic writer / witness + +### Answer to the question asked +Can the existing 53..58 witness contract plus the publication frame distinguish an honestly stamped base fact from an unwritten zero, without altering CE64? +- Inside the CE64 word: NO. + - A hydrated base fact is `W = 0` (givens layer) and `raw5 = 0` (Direct × Open). An edge nobody stamped carries the same bits. + - A sealed W is arbitrary by decision ("CE64 is an ISA contract, not a storage contract; slot numbers recycle", capstone §8 row 4, DECIDED 2026-10-20). + - W is a session-local lane index: owner = the merge/materialization step; lifetime = one session (hot path); no generation; validity ends at seal. It is not a witness identity and must not be promoted to one. +- With existing owners OUTSIDE the word: YES, and that is where the contract says it belongs. `epistemic_state5.rs:36-39`: "observed vs asserted belongs to witness/provenance, never to one of these 32 cells." + - **Hot path:** presence. The per-row 64-bit written-by word names the lanes that wrote the row. An unclaimed alpha address reads `None` and never falls back to a base value (`alpha.rs`). The givens probe already relies on this ("a row carrying only givens is never read past the written-word check"). + - **Sealed:** a row exists only if it was written. The hazard is a writer that builds a composed edge without stamping 59..63. The canonical writer `with_epistemic_raw5(u8)` accepts any code (causal-edge is zero-dependency and cannot check the codebook), so nothing forces a writer to state what it stamped. +- Conclusion: NO CE64 change is justified. Reserved Certification3 codes 6/7 stay unused. `raw5 = 0` stays valid Direct × Open. + +### The six concerns, their existing owners, and the gaps + +| concern | existing owner | state | +|---|---|---| +| source assertion / direct observation | revision `EncounterEvidence.independent_roots`; `SupportLedger` receipt (source, at, strength); givens = thought layer 0, written only by hydration | contract types exist; givens is a probe (`givens_layer_probe`, measured 34 ns/row) | +| mechanical / logical derivation with rule + premise identity | `inherited_roots`, `BasisView.ancestry_derived_roots`; planner `nars::belief::Stamp` (source set; conservative modulo-64 folding) | premise ROOTS carried; RULE identity is not carried anywhere (tarski register Q4: route identity varies with HashMap order, unresolved) | +| independent roots / duplicate-support protection | Stamp overlap gate (`p_e2_stamp_gate_probe`, test-local); D-HL-3 per-delta evidential base (planned) | GAP in production: `Opcode::Revision.truth_code` has no stamp, and duplicated evidence raises c (P-E2 measures it) | +| maturation (Maslov) | bits 50..52; "maturation never writes F/C, W or 59..63; certification never rises because a rival was ruled out" | test-local (`maturation_ladder.rs`); 50..52 reading still contested (O2) | +| certification of relational claims | 59..63 written by hydration (capped by the proofs that ran) and by measured results only; Gadamer writer (D-CTX-7 probe) | GAP: no production writer of 59..63 from evidence (ISS-NO-EVIDENCE-WRITER-FOR-EPISTEMIC-STATE) | +| publication validity / frame-qualified witness identity | `BindStore::check_route` / `RouteVersion` (D-TGL-PUB); `SupportReceipt.at: DatasetVersion` | `at` is a storage revision, not an epistemic view (named gap in `causal_audit.rs`). Witness identity = (classid, sealed version / route version, NodeGuid row), with the thought lane meaningful only inside a session | + +### Recommendation (mandatory = verification of the required evidence, not passage through every stage) + +**One writer-side admission step, in the contract, in front of `with_epistemic_raw5`.** +- A write of 59..63 must state its admission basis as a typed value, one of: + - `Given` (hydration, layer 0); + - `Observed { roots }`; + - `Derived { rule, premises, roots = union(premise roots) }`; + - `Measured { test }`. +- The certification it may claim is capped by that basis. A derivation inherits roots and earns no new one. +- Readers keep the presence check: no written-by bit / unclaimed means `None`, never Direct × Open. +- Enforcement is a census ratchet in the style of D-DT64-AUTH: every `with_epistemic_raw5` call site goes through the admission step or is a listed LAB exception. +- No new authority: the basis types already exist (`EncounterEvidence`, `SupportLedger`, `Stamp`). What is new is that the writer must present one. + +**Open gaps the protocol cannot paper over:** +- Rule identity of a derivation is not carried anywhere (tarski Q4). +- Production revision has no stamp gate (P-E2). +- `SupportReceipt.at` is a storage revision, not an epistemic view. +- W's meaning is still contested (O3, and PROBE-W-PRESERVE). + +### Pre-registered falsifiers (not run; runnable today = R, needs the admission step = N) + +| # | falsifier | fixture | KILL | TWIN | status | +|---|---|---|---|---|---| +| F1 | stamped Direct × Open vs unwritten zero | two rows, both `raw5 = 0`: one hydrated (written-by bit 0 set), one never claimed | the reader gives both the same answer | two stamped rows read alike | R, on the givens / thought-layers probes | +| F2 | observed/given vs mechanically derived sudoku fact | the D-GSO epistemic sudoku: a given cell vs a cell derived by elimination | the derived fact carries an independent root, or certification above the givens' | two givens stay independent | N | +| F3 | two derivations sharing one premise root | P-E2 `shared_premise_is_not_pooled_but_disjoint_roots_are` | on the production revision path, the shared premise raises c (expected to fire: production has no gate) | disjoint roots still pool | R, test-local gate; production arm N | +| F4 | retraction of a supporting premise | `SupportLedger` with one source, `withdraw_source`, then recompute | certification survives the withdrawal of its only support | withdrawing an unrelated source changes nothing | N for certification; the ledger half is R | +| F5 | a valid formal derivation earns no causal certification | NAL deduction / transitive closure over Related premises | the derived edge asserts Supports or above | a measured intervention still earns Causes | N | +| F6 | Maslov maturation does not raise Epi5 | `maturation_ladder.rs`: advance to Falsification / Integration | 59..63 changes on a maturation step | a measured result still moves 59..63 | R | + +## Finding 2 — NNUE as a compiler heuristic + +**Verdict: FEASIBLE AFTER two prerequisites. Not justified as a compiler integration today.** + +**What exists.** +- No NNUE accumulator over weighted features exists in any workspace repo (closed search of `*.rs` in lance-graph, ndarray, brainyblaze/xlog, adaworldapi/xlog, OGAR). + - `bgz-tensor/examples/nnue_palette_cosine.rs` checks palette fidelity of NNUE columns. + - The real i16 HalfKA accumulator is in stockfish-rs (GPL-3.0, not checked out; never copy it into Apache repos). + - The eaw-6, eaw-8 and Yuan notes are unexecuted research. +- No compiler integration exists in xlog-logic, egglog-r2il-probe or codegraph. + +**Features readable by `RelId` today, with no strings or ASTs:** +- cardinality and access heat; +- an observed join-selectivity EMA; +- `estimate_join_cardinality` per candidate pairing; +- `DeltaRecomputeStats` and codegraph `RefreshStats` (change counts); +- `CostPredictionRecord`; +- canonical WCOJ slot roles. + +Absent: adornment features, degree histograms, RIR/SSA features, a hardware descriptor, measured CPU cost per candidate, effect or ambiguity features, and any corpus. + +**The one meaningful probe: XLOG's triangle inner-pairing choice.** +- The candidate set is legal by construction: 3 pairings, or 2 for a 4-cycle. +- The current choice is a hardcoded argmin over `estimate_join_cardinality` (`optimizer.rs:3395-3404`). Its estimates have no provenance and "may all collapse to the same fallback ratio" (`:1586-1593`), and then `SharedY` wins by evaluation order. +- A plug-in seam exists beside it: the `WcojVariableOrderingModel` trait (`wcoj_var_ordering.rs:78`). +- Label: the exact inner join size, Σ deg_A·deg_B, as a fold over degree counts. It is hardware-independent and computable here; there is no GPU, so no measured-ns labels. +- Instances: seeded skewed graphs, with whole families held out. +- Arms: + - the argmin; + - the heat-aware formula; + - **an exact-degree arm (non-learned)**; + - random; + - an NNUE-style accumulator. +- Metric: regret on the held-out families. + +**Accumulator.** +- Feature = (slot role, statistic kind, log2 bucket), resolved by `RelId`. +- Score `h_c = b + Σ W[f,c]` (i32). +- A statistic crossing a bucket touches at most 2 × candidates weights; a change within a bucket touches none. +- No predicate strings, `RirNode` re-walks, plan clones or row decoding on the hot path. + +**Boundary.** The score is its own newtype, with no conversion into any authority: +- legality: the optimizer's shape and safety floor, and the promoter's fallback; +- rewrite admission: `laws::admit`; +- equivalence: egglog proof plus the oracle, and capsule recheck; +- amortization: `CostEvidence::pays_for_itself`; +- possible calls: codegraph's `Possible` qualifier; +- certification: `epistemic_state5`. + +No code owner for purity was found in these crates. + +**Pre-registered falsifiers.** + +| # | falsifier | KILL | TWIN | +|---|---|---|---| +| F1 | illegal but top-scored candidate | it is emitted | a legal top-scored candidate is emitted | +| F2 | one bucket change | it touches more than 2 × candidates weights | a within-bucket change touches 0; incremental update = full refresh, byte for byte | +| F3 | no gain | held-out regret is not below the argmin, or not below the exact-degree arm with its cost counted → PARK | beats random on the training families | +| F4 | permutation of atom order, load order and `RelId` | the canonical choice changes | a changed degree distribution can change the choice | +| F5 | compile-fail: score coerces into cost evidence or an Epi5 code | it compiles | the score works in its own ranking call | + +**The decisive risk.** With 3 candidates the exact cost is a cheap fold. If that fold fits the planning budget, F3 kills the learned ranker. A learned ranker pays only where the exact cost is expensive and its inputs change in small deltas. + +## Closeout +STATUS: measured (research) | OUTCOME: A10 — no CE64 change justified; the stamped-vs-unwritten distinction belongs to presence (hot path) plus a writer-side admission step (sealed), and the six concerns map to existing owners with four named gaps. NNUE — feasible only after a labelled corpus and an exact-degree arm; one minimal probe named | OPEN: admission step design; rule identity of derivations; production stamp gate; certification writer; whether a learned ranker beats the exact fold diff --git a/.claude/board/entries/README.md b/.claude/board/entries/README.md index 519bb36df..583383a43 100644 --- a/.claude/board/entries/README.md +++ b/.claude/board/entries/README.md @@ -25,7 +25,7 @@ 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. -555 entries, 2026-08-06 .. 2026-11-02. +556 entries, 2026-08-06 .. 2026-11-02. | date | entry id | finding | file | |---|---|---|---| @@ -40,6 +40,7 @@ exactly one of them, never both. | 2026-11-02 | `codegraph-review-operator-decisions` | | [2026-11-02-codegraph-review-operator-decisions.md](2026-11-02-codegraph-review-operator-decisions.md) | | 2026-11-02 | `codegraph-dr3-dt64-ruff-architecture-review` | | [2026-11-02-codegraph-dr3-dt64-ruff-architecture-review.md](2026-11-02-codegraph-dr3-dt64-ruff-architecture-review.md) | | 2026-11-02 | `D-CODEGRAPH-0-BIN` | | [2026-11-02-codegraph-bin-reader-timing-gate.md](2026-11-02-codegraph-bin-reader-timing-gate.md) | +| 2026-11-02 | `a10-epistemic-admission-and-nnue-compiler` | | [2026-11-02-a10-epistemic-admission-and-nnue-compiler.md](2026-11-02-a10-epistemic-admission-and-nnue-compiler.md) | | 2026-11-01 | `D-XLOG-NATIVE-7` | | [2026-11-01-xlog-resident-hypercube-context.md](2026-11-01-xlog-resident-hypercube-context.md) | | 2026-11-01 | `D-XLOG-NATIVE-8` | | [2026-11-01-xlog-behaviour-lens-f100.md](2026-11-01-xlog-behaviour-lens-f100.md) | | 2026-11-01 | `D-DT64-7-5` | | [2026-11-01-maptable-versioned-local-map.md](2026-11-01-maptable-versioned-local-map.md) | From ee832e1681d988251c0c2294bffa5bdc16b0bbb5 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 21:19:52 +0000 Subject: [PATCH 5/8] A10 probes F1/F3/F6: pin what the shipped writers distinguish today F1: Direct x Open (raw5 = 0) is bit-identical to an unwritten CausalEdge64; the alpha claim (AlphaStamp) is the only hot-path written-by record. F3: production Opcode::Revision.truth_code cannot see premise overlap and raises c on a duplicated root; GadamerRevision separates overlap from independence only through the caller-supplied BasisView. F6: with_novelty_wisdom and learn() never move bits 59..63. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01X6y3drwKSE2zSgoexheLFX --- .../tests/a10_epistemic_admission_probes.rs | 227 ++++++++++++++++++ 1 file changed, 227 insertions(+) create mode 100644 crates/cognitive-shader-driver/tests/a10_epistemic_admission_probes.rs diff --git a/crates/cognitive-shader-driver/tests/a10_epistemic_admission_probes.rs b/crates/cognitive-shader-driver/tests/a10_epistemic_admission_probes.rs new file mode 100644 index 000000000..fa8191932 --- /dev/null +++ b/crates/cognitive-shader-driver/tests/a10_epistemic_admission_probes.rs @@ -0,0 +1,227 @@ +//! A10 probe-first falsifiers F1, F3 and F6, run BEFORE any admission +//! writer exists. Each test pins what the shipped code does today; none of +//! them adds a guard to production code. +//! +//! - F1: is a stamped `Direct × Open` (`raw5 = 0`) distinguishable from an +//! edge nobody wrote? Inside `CausalEdge64` it is not. Outside it, the alpha +//! overlay's claim distinguishes them on the hot path. +//! - F3: overlapping vs independent premise roots. The production numeric +//! revision (`Opcode::Revision.truth_code`) sees four scalars and cannot +//! tell them apart. `GadamerRevision` can, but only through the +//! `BasisView` its caller supplies. +//! - F6: maturation of bits 50..52 never alters bits 59..63. +//! +//! A test-local overlap guard (`causal-edge/tests/p_e2_stamp_gate_probe.rs`) +//! is NOT evidence that production revision enforces the rule; F3 measures +//! the production paths only. + +use causal_edge::{CausalEdge64, Opcode}; +use lance_graph_contract::alpha::{stamp_of, AlphaAllocation, AlphaOverlay}; +use lance_graph_contract::canonical_node::{EdgeBlock, NodeGuid, NodeRow, TailVariant}; +use lance_graph_contract::epistemic_state5::{ + Certification3, Epi5Gen, EpistemicState5, Topology2, +}; +use lance_graph_contract::revision::{ + BasisView, CodebookId, EncounterEvidence, EvidentialEffect, GadamerRevision, GrammarId, + HorizonId, InterpretiveHorizon, LanguageId, LensId, QuestionId, RevisionPolicy, +}; + +const EPI_MASK: u64 = 0b1_1111 << 59; + +fn direct_open() -> EpistemicState5 { + EpistemicState5::new(Epi5Gen::V1, Topology2::Direct, Certification3::Open) +} + +fn rows(n: u32) -> Vec { + let mut rows: Vec = (0..n) + .map(|i| NodeRow { + key: NodeGuid::mint_for(TailVariant::V3, 0x0A0A_0000, 4, 5, 6, 0x33, 1, i + 1), + edges: EdgeBlock::default(), + value: [0u8; 480], + }) + .collect(); + rows.sort_by_key(|r| *r.key.as_bytes()); + rows +} + +/// F1, CE64 half: `Direct × Open` is code 0 and W of a fresh edge is 0, so a +/// stamped edge and an unwritten one are the same 64 bits. +#[test] +fn f1_stamped_direct_open_is_bit_identical_to_an_unwritten_edge() { + assert_eq!(direct_open().raw(), 0, "Direct × Open must be raw5 = 0"); + let unwritten = CausalEdge64(0); + let stamped = unwritten.with_epistemic_raw5(direct_open().raw()); + assert_eq!(stamped.0, unwritten.0, "the CE64 word cannot carry the stamp"); + + // Control: a non-zero code IS visible, so the comparison can fail. + let supports = EpistemicState5::new(Epi5Gen::V1, Topology2::Direct, Certification3::Supports); + assert_ne!(unwritten.with_epistemic_raw5(supports.raw()).0, unwritten.0); +} + +/// F1, outside CE64: on the hot path the alpha overlay's claim is the +/// written-by fact. A claimed row reads `Some`, an unclaimed one `None`, even +/// though both carry the identical zero code. +#[test] +fn f1_alpha_claim_distinguishes_what_ce64_cannot() { + let base = rows(8); + let mut ov = AlphaOverlay::new(AlphaAllocation::over(&base), 1); + let written = base[2].key; + let never = base[5].key; + ov.claim(written, 0).expect("allocated address"); + + assert!(ov.get(written).is_some(), "stamped: claimed"); + assert!(ov.get(never).is_none(), "unwritten: not attended, no fallback"); + // The claim stamps the claimed row (`AlphaStamp`: cycle, saccade order, + // visits); the base row stays all-zero. The stamp, not bits 59..63, is the + // written-by record. + let claimed = ov.get(written).expect("claimed"); + assert_ne!(claimed.value, base[2].value, "a claim stamps the row"); + assert_eq!(stamp_of(claimed).visits, 1); + // Only the claimed row is materialised; a sealed image of this overlay + // holds row 2 and not row 5. Row existence is the only carrier left once + // the session-local claim is gone. + assert_eq!(ov.claimed_len(), 1); + assert_eq!(ov.rows()[0].key, written); +} + +/// F3, production numeric revision: overlapping and independent premise roots +/// produce identical inputs, so they produce identical outputs. Confidence +/// rises in both cases; the overlap is counted twice. +#[test] +fn f3_production_truth_code_cannot_see_premise_overlap() { + let (f, c) = (200u8, 128u8); + // "Same root twice" and "two independent roots with the same reading" are + // the same four scalars. + let overlapping = Opcode::Revision.truth_code(f, c, f, c); + let independent = Opcode::Revision.truth_code(f, c, f, c); + assert_eq!(overlapping, independent); + assert!( + overlapping.1 > c, + "production revision raises c on a duplicated root: c {c} -> {}", + overlapping.1 + ); +} + +fn horizon() -> InterpretiveHorizon<(), u64> { + InterpretiveHorizon { + id: HorizonId(0), + awareness: (), + question: QuestionId(0), + language: LanguageId(0), + grammar: GrammarId(0), + codebook: CodebookId(0), + lens: LensId(0), + projected_claims: 0b1, + independent_roots: 0, + inherited_roots: 0, + unresolved_tension: 0, + revision_index: 0, + } +} + +fn encounter(independent: u64, inherited: u64) -> EncounterEvidence { + EncounterEvidence { + proposed_claims: 0b1, + independent_roots: independent, + inherited_roots: inherited, + resistance: 0, + contradictions: 0, + affected_parts: 0, + } +} + +fn ancestry(independent: u64) -> BasisView { + BasisView { + ancestry_independent_roots: independent, + ancestry_derived_roots: 0, + ancestor_claims: 0, + closes_cycle: false, + } +} + +fn effect(enc: &EncounterEvidence, basis: &BasisView) -> EvidentialEffect { + GadamerRevision.revise(&horizon(), enc, basis).evidential_effect +} + +/// F3, contract revision: `GadamerRevision` refuses an overlapping root and +/// admits an independent one, given an ancestry that records the first root. +#[test] +fn f3_gadamer_revision_separates_overlap_from_independence() { + let r1 = 1u64 << 1; + let r2 = 1u64 << 2; + assert_eq!( + effect(&encounter(r1, 0), &ancestry(0)), + EvidentialEffect::IncreaseEligible, + "first contact with r1" + ); + assert_ne!( + effect(&encounter(r1, 0), &ancestry(r1)), + EvidentialEffect::IncreaseEligible, + "r1 again, already in ancestry" + ); + assert_eq!( + effect(&encounter(r2, 0), &ancestry(r1)), + EvidentialEffect::IncreaseEligible, + "r2 is a genuinely new root" + ); + // An inherited root never counts as new evidence. + assert_ne!( + effect(&encounter(0, r1), &ancestry(0)), + EvidentialEffect::IncreaseEligible + ); +} + +/// F3, the gap: the overlap rule is enforced only through the caller's +/// `BasisView`. A caller that omits the ancestry gets the duplicate admitted. +#[test] +fn f3_overlap_enforcement_depends_on_the_supplied_ancestry() { + let r1 = 1u64 << 1; + assert_eq!( + effect(&encounter(r1, 0), &ancestry(0)), + EvidentialEffect::IncreaseEligible, + "a repeated root with an empty ancestry is admitted again" + ); + // The same encounter can also list a root as both independent and + // inherited; the policy reads it as new because only ancestry is + // subtracted. + assert_eq!( + effect(&encounter(r1, r1), &ancestry(0)), + EvidentialEffect::IncreaseEligible + ); +} + +/// F6: the maturation writers move bits 50..52 and never bits 59..63. +#[test] +fn f6_maturation_never_alters_epistemic_state() { + let codes = [ + direct_open(), + EpistemicState5::new(Epi5Gen::V1, Topology2::IndirectKnown, Certification3::Supports), + EpistemicState5::new(Epi5Gen::V1, Topology2::Unknown, Certification3::Causes), + ]; + for state in codes { + let seed = CausalEdge64(0x0123_4567_89AB_CDEF & !EPI_MASK).with_epistemic_raw5(state.raw()); + + for level in 0..=9u8 { + let matured = seed.with_novelty_wisdom(level); + assert_eq!(matured.epistemic_raw5(), state.raw(), "level {level}"); + assert_eq!( + (matured.0 ^ seed.0) & !(0b111 << 50), + 0, + "with_novelty_wisdom moved a bit outside 50..52" + ); + } + + let mut learned = seed; + for (i, c) in [10u8, 120, 200, 240, 250, 255].into_iter().enumerate() { + let mut obs = CausalEdge64(seed.0 ^ ((i as u64 + 1) << 8)); + obs.set_frequency_u8(180); + obs.set_confidence_u8(c); + learned.learn(obs, i as u16); + assert_eq!( + learned.epistemic_raw5(), + state.raw(), + "learn() changed bits 59..63 at step {i}" + ); + } + } +} From a0cc62c56f12893dc1a3f7d40de92a18ac78f38f Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 21:21:08 +0000 Subject: [PATCH 6/8] NNUE baseline: triangle pairing regret vs the exact-degree fold Reproduces optimizer.rs:3395-3404 with the real StatsManager over 72 instances (4 train + 2 held-out families). Exact-degree fold: regret 1.000 on all 72, 0 permutation changes, 1.3-2.7 ns/insert incremental maintenance. Estimate argmin: mean regret 13.1 (4.8 with exact distinct counts); the 24 equal-size instances collapse to a tie and fall to SharedY by evaluation order. Verdict: PARK NNUE for this optimizer. Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01X6y3drwKSE2zSgoexheLFX --- crates/xlog-relcomp-probe/Cargo.toml | 1 + .../examples/triangle_pairing_baselines.rs | 696 ++++++++++++++++++ 2 files changed, 697 insertions(+) create mode 100644 crates/xlog-relcomp-probe/examples/triangle_pairing_baselines.rs diff --git a/crates/xlog-relcomp-probe/Cargo.toml b/crates/xlog-relcomp-probe/Cargo.toml index 4e7a781f7..bc754d16e 100644 --- a/crates/xlog-relcomp-probe/Cargo.toml +++ b/crates/xlog-relcomp-probe/Cargo.toml @@ -35,6 +35,7 @@ lance-graph-contract = { path = "../lance-graph-contract" } xlog-core = { git = "https://github.com/BrainyBlaze/xlog", rev = "bff6da663308190148c5ce58a715d86525b62e67" } xlog-ir = { git = "https://github.com/BrainyBlaze/xlog", rev = "bff6da663308190148c5ce58a715d86525b62e67" } xlog-logic = { git = "https://github.com/BrainyBlaze/xlog", rev = "bff6da663308190148c5ce58a715d86525b62e67" } +xlog-stats = { git = "https://github.com/BrainyBlaze/xlog", rev = "bff6da663308190148c5ce58a715d86525b62e67" } [dev-dependencies] # The shared contract types a DTO binding is stated in (NodeGuid, ClassView, diff --git a/crates/xlog-relcomp-probe/examples/triangle_pairing_baselines.rs b/crates/xlog-relcomp-probe/examples/triangle_pairing_baselines.rs new file mode 100644 index 000000000..15c681441 --- /dev/null +++ b/crates/xlog-relcomp-probe/examples/triangle_pairing_baselines.rs @@ -0,0 +1,696 @@ +//! Deterministic baselines for XLOG's triangle inner-pairing choice (selectivity pass, +//! `try_reorder_triangle_with_owner`), measured BEFORE any learned model. +//! No model is trained or integrated here. Pre-registered falsifiers F3 / F4 from the +//! feasibility report are the questions this example answers with numbers. +//! +//! cargo run --release --example triangle_pairing_baselines +//! +//! Candidates: SharedX = join(R_xy,R_xz) on x, SharedY = join(R_xy,R_yz) on y, +//! SharedZ = join(R_yz,R_xz) on z. Exact label = size of the inner join +//! = sum_v degA(v)*degB(v), a fold over per-value degree counts (no join rows built). +//! The materialized brute-force join exists only as a test oracle (`oracle_check`). +use std::collections::HashSet; +use std::hint::black_box; +use std::time::Instant; + +use xlog_core::{RelId, ScalarType}; +use xlog_stats::{ColumnStats, StatsManager}; + +// ------------------------------------------------------------------ rng +#[derive(Clone)] +struct Rng(u64); +impl Rng { + fn next(&mut self) -> u64 { + self.0 = self.0.wrapping_add(0x9E37_79B9_7F4A_7C15); + let mut z = self.0; + z = (z ^ (z >> 30)).wrapping_mul(0xBF58_476D_1CE4_E5B9); + z = (z ^ (z >> 27)).wrapping_mul(0x94D0_49BB_1331_11EB); + z ^ (z >> 31) + } + fn f64(&mut self) -> f64 { + (self.next() >> 11) as f64 / (1u64 << 53) as f64 + } + fn below(&mut self, n: usize) -> u32 { + (self.next() % n as u64) as u32 + } + fn shuffle(&mut self, v: &mut [T]) { + for i in (1..v.len()).rev() { + let j = (self.next() % (i as u64 + 1)) as usize; + v.swap(i, j); + } + } +} + +struct Zipf(Vec); +impl Zipf { + fn new(n: usize, s: f64) -> Self { + let mut acc = 0.0; + let mut c = Vec::with_capacity(n); + for r in 0..n { + acc += 1.0 / ((r + 1) as f64).powf(s); + c.push(acc); + } + let t = acc; + c.iter_mut().for_each(|x| *x /= t); + Zipf(c) + } + fn sample(&self, rng: &mut Rng) -> u32 { + let u = rng.f64(); + self.0.partition_point(|&p| p < u).min(self.0.len() - 1) as u32 + } +} + +// ------------------------------------------------------------------ families +#[derive(Clone, Copy, PartialEq)] +enum Family { + Uniform, + ZipfAll, + ZipfY, + StarHub, + Cluster, + AntiCorr, + TwinHubY, // twin only: hub values on the shared variable y +} +impl Family { + fn name(self) -> &'static str { + match self { + Family::Uniform => "uniform", + Family::ZipfAll => "zipf_all", + Family::ZipfY => "zipf_y", + Family::StarHub => "star_hub", + Family::Cluster => "bipartite_cluster", + Family::AntiCorr => "anticorr_zipf", + Family::TwinHubY => "twin_hub_y", + } + } + fn split(self) -> &'static str { + match self { + Family::Cluster | Family::AntiCorr => "heldout", + _ => "train", + } + } +} +const FAMILIES: [Family; 6] = [ + Family::Uniform, + Family::ZipfAll, + Family::ZipfY, + Family::StarHub, + Family::Cluster, + Family::AntiCorr, +]; + +struct Ctx { + v: usize, + zipf: Zipf, + zipf_k: Zipf, +} +impl Ctx { + fn new(v: usize) -> Self { + Ctx { v, zipf: Zipf::new(v, 1.1), zipf_k: Zipf::new(16, 1.0) } + } +} + +/// role: 0 = R_xy (x,y), 1 = R_yz (y,z), 2 = R_xz (x,z). var: 0 x, 1 y, 2 z. +fn sample_var(f: Family, var: usize, role: usize, rng: &mut Rng, c: &Ctx) -> u32 { + let v = c.v; + match f { + Family::Uniform => rng.below(v), + Family::ZipfAll => c.zipf.sample(rng), + Family::ZipfY => { + if var == 1 { + c.zipf.sample(rng) + } else { + rng.below(v) + } + } + Family::StarHub => { + if var == 2 && rng.f64() < 0.5 { + rng.below(4) + } else { + rng.below(v) + } + } + Family::TwinHubY => { + if var == 1 && rng.f64() < 0.5 { + rng.below(4) + } else { + rng.below(v) + } + } + Family::AntiCorr => { + if var == 0 { + let r = c.zipf.sample(rng); + if role == 0 { r } else { (v as u32 - 1) - r } + } else { + rng.below(v) + } + } + Family::Cluster => unreachable!(), + } +} + +fn sample_pair(f: Family, role: usize, rng: &mut Rng, c: &Ctx) -> (u32, u32) { + if f == Family::Cluster { + let cs = c.v / 16; + if rng.f64() < 0.9 { + let k = c.zipf_k.sample(rng) as usize; + ( + (k * cs) as u32 + rng.below(cs), + (k * cs) as u32 + rng.below(cs), + ) + } else { + (rng.below(c.v), rng.below(c.v)) + } + } else { + let (a, b) = [(0, 1), (1, 2), (0, 2)][role]; + (sample_var(f, a, role, rng, c), sample_var(f, b, role, rng, c)) + } +} + +/// Exactly `n` distinct pairs (so cardinalities are exact and equal-size instances +/// really are equal-size); falls back to fewer if the family cannot supply them. +fn gen_rel(f: Family, role: usize, n: usize, rng: &mut Rng, c: &Ctx) -> Vec<(u32, u32)> { + let mut seen = HashSet::with_capacity(n * 2); + let mut out = Vec::with_capacity(n); + let mut tries = 0usize; + while out.len() < n && tries < n * 400 { + tries += 1; + let p = sample_pair(f, role, rng, c); + if seen.insert(((p.0 as u64) << 32) | p.1 as u64) { + out.push(p); + } + } + out +} + +// ------------------------------------------------------------------ degrees + exact fold +struct Deg { + d: [[Vec; 2]; 3], // [relation][column] +} +impl Deg { + fn zero(v: usize) -> Self { + Deg { d: std::array::from_fn(|_| std::array::from_fn(|_| vec![0u32; v])) } + } + fn build(rels: &[Vec<(u32, u32)>; 3], v: usize) -> Self { + let mut g = Deg::zero(v); + for (i, r) in rels.iter().enumerate() { + for &(a, b) in r { + g.d[i][0][a as usize] += 1; + g.d[i][1][b as usize] += 1; + } + } + g + } +} +fn fold(a: &[u32], b: &[u32]) -> u64 { + a.iter().zip(b).map(|(&x, &y)| x as u64 * y as u64).sum() +} +/// exact inner-join size by PHYSICAL shared variable [x, y, z]. +/// physical layout: P0=(x,y), P1=(y,z), P2=(x,z). +fn exact_phys(g: &Deg) -> [u64; 3] { + [ + fold(&g.d[0][0], &g.d[2][0]), + fold(&g.d[0][1], &g.d[1][0]), + fold(&g.d[1][1], &g.d[2][1]), + ] +} +fn distinct(d: &[u32]) -> u64 { + d.iter().filter(|&&x| x > 0).count() as u64 +} + +/// Test oracle only: materialize the join rows, compare with the fold. +fn oracle_check() -> usize { + let c = Ctx::new(48); + let mut checked = 0; + for f in FAMILIES { + for seed in 0..5u64 { + let mut rng = Rng(0xABCD ^ seed.wrapping_mul(7919)); + let rels: [Vec<(u32, u32)>; 3] = + std::array::from_fn(|r| gen_rel(f, r, 150, &mut rng, &c)); + let ex = exact_phys(&Deg::build(&rels, 48)); + // x: P0.x == P2.x ; y: P0.y == P1.y ; z: P1.z == P2.z + let mut jx = Vec::new(); + let mut jy = Vec::new(); + let mut jz = Vec::new(); + for &(a0, b0) in &rels[0] { + for &(a2, b2) in &rels[2] { + if a0 == a2 { + jx.push((a0, b0, b2)); + } + } + for &(a1, b1) in &rels[1] { + if b0 == a1 { + jy.push((a0, b0, b1)); + } + } + } + for &(a1, b1) in &rels[1] { + for &(a2, b2) in &rels[2] { + if b1 == b2 { + jz.push((a1, b1, a2)); + } + } + } + assert_eq!(ex[0], jx.len() as u64, "fold != brute force (x) {}", f.name()); + assert_eq!(ex[1], jy.len() as u64, "fold != brute force (y) {}", f.name()); + assert_eq!(ex[2], jz.len() as u64, "fold != brute force (z) {}", f.name()); + checked += 3; + } + } + checked +} + +// ------------------------------------------------------------------ arms +/// Exactly the optimizer's argmin: start SharedY, replace on strictly-less X, then Z. +/// Index: 0 = SharedX, 1 = SharedY, 2 = SharedZ. +fn argmin_opt(e: [u64; 3]) -> usize { + let mut best = (1usize, e[1]); + if e[0] < best.1 { + best = (0, e[0]); + } + if e[2] < best.1 { + best = (2, e[2]); + } + best.0 +} +const LBL: [&str; 3] = ["X", "Y", "Z"]; + +/// slot (xy=0,yz=1,xz=2) of physical relation with physical vars (u,w) under labels pi. +fn slot_of(pi: [usize; 3], u: usize, w: usize) -> (usize, bool) { + let (a, b) = (pi[u], pi[w]); + let (lo, hi) = (a.min(b), a.max(b)); + let s = match (lo, hi) { + (0, 1) => 0, + (1, 2) => 1, + _ => 2, + }; + (s, a > b) +} +const PHYS_VARS: [(usize, usize); 3] = [(0, 1), (1, 2), (0, 2)]; + +struct Est { + est: [u64; 3], // label-indexed [X,Y,Z] +} +/// Build the StatsManager the optimizer would see and ask it the three questions +/// exactly as `try_reorder_triangle_with_owner` does. +fn stats_estimates( + cards: [u64; 3], + dists: &[[u64; 2]; 3], + pi: [usize; 3], + ids: [u32; 3], + colstats: bool, + reg_order: [usize; 3], +) -> Est { + let mut sm = StatsManager::new(); + let mut slot_rel = [RelId(0); 3]; + for &p in ®_order { + let (s, swapped) = slot_of(pi, PHYS_VARS[p].0, PHYS_VARS[p].1); + let id = RelId(ids[p]); + slot_rel[s] = id; + sm.register_relation(id); + sm.update_cardinality(id, cards[p]); + if colstats { + for j in 0..2 { + let pc = if swapped { 1 - j } else { j }; + let mut cs = ColumnStats::new(j, ScalarType::U32); + cs.distinct_estimate = dists[p][pc]; + sm.add_column_stats(id, cs); + } + } + } + let (xy, yz, xz) = (slot_rel[0], slot_rel[1], slot_rel[2]); + let est_y = sm.estimate_join_cardinality(xy, yz, &[1], &[0]); + let est_x = sm.estimate_join_cardinality(xy, xz, &[0], &[0]); + let est_z = sm.estimate_join_cardinality(yz, xz, &[1], &[1]); + Est { est: [est_x, est_y, est_z] } +} + +const PERMS: [[usize; 3]; 6] = + [[0, 1, 2], [0, 2, 1], [1, 0, 2], [1, 2, 0], [2, 0, 1], [2, 1, 0]]; + +fn inv(pi: [usize; 3]) -> [usize; 3] { + let mut r = [0; 3]; + for (v, &l) in pi.iter().enumerate() { + r[l] = v; + } + r +} + +struct Trial { + canon_a0: usize, // canonical = physical shared variable + canon_a1: usize, + canon_c: usize, + cost_a0: u64, + cost_a1: u64, + cost_c: u64, +} + +fn run_trial( + rels: &[Vec<(u32, u32)>; 3], + v: usize, + pi: [usize; 3], + rng: &mut Rng, + shuffle: bool, +) -> Trial { + // (ii) fact load order: statistics are re-derived from the (shuffled) fact lists. + let mut r = rels.clone(); + if shuffle { + r.iter_mut().for_each(|x| rng.shuffle(x)); + } + let g = Deg::build(&r, v); + let cards = [r[0].len() as u64, r[1].len() as u64, r[2].len() as u64]; + let dists: [[u64; 2]; 3] = + std::array::from_fn(|i| [distinct(&g.d[i][0]), distinct(&g.d[i][1])]); + // (iii) RelId assignment + registration order. + let mut ids = [0u32; 3]; + let mut pool = [7u32, 123, 4096]; + if shuffle { + rng.shuffle(&mut pool); + } else { + pool = [0, 1, 2]; + } + ids.copy_from_slice(&pool); + let mut reg = [0usize, 1, 2]; + if shuffle { + rng.shuffle(&mut reg); + } + let e0 = stats_estimates(cards, &dists, pi, ids, false, reg); + let e1 = stats_estimates(cards, &dists, pi, ids, true, reg); + let ex_p = exact_phys(&g); + let ip = inv(pi); + let ex_lbl = [ex_p[ip[0]], ex_p[ip[1]], ex_p[ip[2]]]; + let (a0, a1, c) = (argmin_opt(e0.est), argmin_opt(e1.est), argmin_opt(ex_lbl)); + Trial { + canon_a0: ip[a0], + canon_a1: ip[a1], + canon_c: ip[c], + cost_a0: ex_lbl[a0], + cost_a1: ex_lbl[a1], + cost_c: ex_lbl[c], + } +} + +// ------------------------------------------------------------------ stats helpers +fn mean(v: &[f64]) -> f64 { + v.iter().sum::() / v.len().max(1) as f64 +} +fn pct(v: &[f64], p: f64) -> f64 { + let mut s = v.to_vec(); + s.sort_by(|a, b| a.partial_cmp(b).unwrap()); + s[((s.len() - 1) as f64 * p).round() as usize] +} +fn ratio(chosen: u64, best: u64) -> f64 { + (chosen.max(1)) as f64 / (best.max(1)) as f64 +} + +#[derive(Default)] +struct Agg { + n: usize, + reg: [Vec; 5], // a0, a1, c, rand, always-Y + abs: [Vec; 5], + hit: [usize; 5], + tie_min_exact: usize, + collapsed: [usize; 2], // a0, a1: min estimate attained by >1 candidate + all_equal: [usize; 2], // all three estimates equal + fell_to_y: [usize; 2], // collapsed and chosen == Y + perm_changed: [usize; 3], // a0 a1 c: canonical pairing changed in any trial + perm_costly: [usize; 3], // ...and the exact cost of the choice changed +} + +fn main() { + assert!(!cfg!(debug_assertions), "release build only"); + let cpu = std::fs::read_to_string("/proc/cpuinfo") + .ok() + .and_then(|s| s.lines().find(|l| l.starts_with("model name")).map(|l| l.to_string())) + .unwrap_or_default(); + println!("# triangle pairing baselines (no model trained or integrated)"); + println!( + "# cpu: {} | target_arch={} | avx2={} avx512f={} | built with target-cpu={}", + cpu.trim(), + std::env::consts::ARCH, + std::arch::is_x86_feature_detected!("avx2"), + std::arch::is_x86_feature_detected!("avx512f"), + option_env!("RUSTFLAGS").unwrap_or("(default)") + ); + println!("# xlog rev bff6da66, xlog-stats::StatsManager::estimate_join_cardinality, optimizer.rs:3395-3404 reproduced"); + + let oc = oracle_check(); + println!("# oracle: {oc} fold-vs-materialized-join equalities hold (V=48,n=150, 6 families x 5 seeds x 3 pairings)"); + + const V: usize = 4096; + let c = Ctx::new(V); + println!("# V={V} values per variable; n per relation 6000..24000; seeds 0..11 per family; seed%3==0 => equal sizes"); + println!("family\tsplit\tseed\tn_facts\tn_xy\tn_yz\tn_xz\tN_est_x\tN_est_y\tN_est_z\tC_est_x\tC_est_y\tC_est_z\texact_x\texact_y\texact_z\tchoice_a0\tchoice_a1\tchoice_c\tregret_a0\tregret_a1\tregret_c\tcollapsed_a0\tcollapsed_a1\tperm_changed_a0\tperm_changed_a1\tperm_changed_c"); + + let mut aggs: Vec<(Family, Agg)> = FAMILIES.iter().map(|&f| (f, Agg::default())).collect(); + for (fi, &f) in FAMILIES.iter().enumerate() { + for seed in 0..12u64 { + let mut rng = Rng(0x5EED_0000 + (fi as u64) * 1000 + seed); + let base = [6000usize, 10000, 16000, 24000][(seed % 4) as usize]; + let ns: [usize; 3] = if seed % 3 == 0 { + [base; 3] + } else { + let fac = [[0.5, 1.0, 2.0], [2.0, 0.5, 1.0], [1.0, 2.0, 0.5], [0.5, 2.0, 1.0]] + [(seed % 4) as usize]; + [ + (base as f64 * fac[0]) as usize, + (base as f64 * fac[1]) as usize, + (base as f64 * fac[2]) as usize, + ] + }; + let rels: [Vec<(u32, u32)>; 3] = + std::array::from_fn(|r| gen_rel(f, r, ns[r].min(30000), &mut rng, &c)); + let cards = [rels[0].len(), rels[1].len(), rels[2].len()]; + let g = Deg::build(&rels, V); + let dists: [[u64; 2]; 3] = + std::array::from_fn(|i| [distinct(&g.d[i][0]), distinct(&g.d[i][1])]); + let exact = exact_phys(&g); // identity labels: [X,Y,Z] + let id = [0usize, 1, 2]; + let e0 = stats_estimates( + [cards[0] as u64, cards[1] as u64, cards[2] as u64], + &dists, id, [0, 1, 2], false, [0, 1, 2], + ); + let e1 = stats_estimates( + [cards[0] as u64, cards[1] as u64, cards[2] as u64], + &dists, id, [0, 1, 2], true, [0, 1, 2], + ); + let (a0, a1, cc) = (argmin_opt(e0.est), argmin_opt(e1.est), argmin_opt(exact)); + let best = *exact.iter().min().unwrap(); + let rand_pick = (Rng(0xFACE ^ seed ^ (fi as u64) << 8).next() % 3) as usize; + let picks = [a0, a1, cc, rand_pick, 1usize]; + // permutation falsifier: 6 variable relabelings, each with reshuffled + // fact order, random RelIds and random registration order. + let mut changed = [false; 3]; + let mut costly = [false; 3]; + let base_t = run_trial(&rels, V, [0, 1, 2], &mut rng, false); + let base_canon = [base_t.canon_a0, base_t.canon_a1, base_t.canon_c]; + let base_cost = [base_t.cost_a0, base_t.cost_a1, base_t.cost_c]; + for pi in PERMS { + let t = run_trial(&rels, V, pi, &mut rng, true); + let cn = [t.canon_a0, t.canon_a1, t.canon_c]; + let co = [t.cost_a0, t.cost_a1, t.cost_c]; + for k in 0..3 { + if cn[k] != base_canon[k] { + changed[k] = true; + } + if co[k] != base_cost[k] { + costly[k] = true; + } + } + } + println!( + "{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{}\t{:.3}\t{:.3}\t{:.3}\t{}\t{}\t{}\t{}\t{}", + f.name(), f.split(), seed, cards.iter().sum::(), cards[0], cards[1], cards[2], + e0.est[0], e0.est[1], e0.est[2], e1.est[0], e1.est[1], e1.est[2], + exact[0], exact[1], exact[2], LBL[a0], LBL[a1], LBL[cc], + ratio(exact[a0], best), ratio(exact[a1], best), ratio(exact[cc], best), + tie_min(e0.est), tie_min(e1.est), changed[0], changed[1], changed[2] + ); + let a = &mut aggs[fi].1; + a.n += 1; + for (k, &p) in picks.iter().enumerate() { + a.reg[k].push(ratio(exact[p], best)); + a.abs[k].push((exact[p] - best) as f64); + if exact[p] == best { + a.hit[k] += 1; + } + } + if tie_min(exact) { + a.tie_min_exact += 1; + } + for (k, e) in [e0.est, e1.est].iter().enumerate() { + if tie_min(*e) { + a.collapsed[k] += 1; + if argmin_opt(*e) == 1 { + a.fell_to_y[k] += 1; + } + } + if e[0] == e[1] && e[1] == e[2] { + a.all_equal[k] += 1; + } + } + for k in 0..3 { + a.perm_changed[k] += changed[k] as usize; + a.perm_costly[k] += costly[k] as usize; + } + } + } + + println!("\n## per-family summary (regret = exact inner size of chosen / best; 1.000 = optimal)"); + println!("arms: a0 = optimizer argmin, cardinalities only; a1 = optimizer argmin, exact per-column distinct counts supplied; c = exact-degree fold argmin; r = seeded random; Y = always SharedY"); + println!("family\tsplit\tn\tmean_regret[a0,a1,c,r,Y]\tmedian_a0\tp90_a0\tmax_a0\tmax_a1\tmean_abs_regret_a0\toptimal_hits[a0,a1,c,r,Y]\texact_ties\tcollapsed[a0,a1]\tall_equal[a0,a1]\tfell_to_Y[a0,a1]\tperm_changed[a0,a1,c]\tperm_costly[a0,a1,c]"); + let mut tot = Agg::default(); + for (f, a) in &aggs { + println!( + "{}\t{}\t{}\t[{:.3},{:.3},{:.3},{:.3},{:.3}]\t{:.3}\t{:.3}\t{:.3}\t{:.3}\t{:.0}\t{:?}\t{}\t{:?}\t{:?}\t{:?}\t{:?}\t{:?}", + f.name(), f.split(), a.n, + mean(&a.reg[0]), mean(&a.reg[1]), mean(&a.reg[2]), mean(&a.reg[3]), mean(&a.reg[4]), + pct(&a.reg[0], 0.5), pct(&a.reg[0], 0.9), pct(&a.reg[0], 1.0), pct(&a.reg[1], 1.0), + mean(&a.abs[0]), a.hit, a.tie_min_exact, a.collapsed, a.all_equal, a.fell_to_y, + a.perm_changed, a.perm_costly + ); + tot.n += a.n; + for k in 0..5 { + tot.reg[k].extend(&a.reg[k]); + tot.hit[k] += a.hit[k]; + } + for k in 0..2 { + tot.collapsed[k] += a.collapsed[k]; + tot.all_equal[k] += a.all_equal[k]; + tot.fell_to_y[k] += a.fell_to_y[k]; + } + for k in 0..3 { + tot.perm_changed[k] += a.perm_changed[k]; + tot.perm_costly[k] += a.perm_costly[k]; + } + } + for split in ["train", "heldout"] { + let mut regs: [Vec; 5] = Default::default(); + for (f, a) in &aggs { + if f.split() == split { + for k in 0..5 { + regs[k].extend(&a.reg[k]); + } + } + } + println!( + "split {split}: mean regret [a0,a1,c,r,Y] = [{:.3},{:.3},{:.3},{:.3},{:.3}] max a0 = {:.3}", + mean(®s[0]), mean(®s[1]), mean(®s[2]), mean(®s[3]), mean(®s[4]), + pct(®s[0], 1.0) + ); + } + println!( + "ALL n={} mean regret [a0,a1,c,r,Y]=[{:.3},{:.3},{:.3},{:.3},{:.3}] optimal hits {:?} collapsed(min-tie) a0/a1 = {:?} all_equal = {:?} fell_to_Y = {:?} perm_changed[a0,a1,c] = {:?} perm_costly = {:?}", + tot.n, mean(&tot.reg[0]), mean(&tot.reg[1]), mean(&tot.reg[2]), mean(&tot.reg[3]), mean(&tot.reg[4]), + tot.hit, tot.collapsed, tot.all_equal, tot.fell_to_y, tot.perm_changed, tot.perm_costly + ); + + // ---------------------------------------------------------------- twin for F4 + println!("\n## F4 TWIN: same cardinalities, changed degree distribution on R_yz"); + { + let mut rng = Rng(0x7117); + let n = 12000; + let r0 = gen_rel(Family::Uniform, 0, n, &mut rng, &c); + let r1u = gen_rel(Family::Uniform, 1, n, &mut rng, &c); + let r1h = gen_rel(Family::StarHub, 1, n, &mut rng, &c); + let r2 = gen_rel(Family::Uniform, 2, n, &mut rng, &c); + for (name, r1) in [("uniform R_yz", r1u), ("hub-on-z R_yz", r1h)] { + let rels = [r0.clone(), r1, r2.clone()]; + let g = Deg::build(&rels, V); + let ex = exact_phys(&g); + let cards = [rels[0].len() as u64, rels[1].len() as u64, rels[2].len() as u64]; + let dists: [[u64; 2]; 3] = + std::array::from_fn(|i| [distinct(&g.d[i][0]), distinct(&g.d[i][1])]); + let e0 = stats_estimates(cards, &dists, [0, 1, 2], [0, 1, 2], false, [0, 1, 2]); + println!( + "{name}: cards={cards:?} exact(x,y,z)={ex:?} choice_c={} est_nocol={:?} choice_a0={}", + LBL[argmin_opt(ex)], e0.est, LBL[argmin_opt(e0.est)] + ); + } + } + + // ---------------------------------------------------------------- costs + println!("\n## COST (release; one-time costs in us, per-decision in ns/us; any ms-scale per-decision result is a FAILED GATE)"); + for (name, f) in [("uniform", Family::Uniform), ("zipf_all", Family::ZipfAll)] { + let mut rng = Rng(0xC057); + let rels: [Vec<(u32, u32)>; 3] = + std::array::from_fn(|r| gen_rel(f, r, 10000, &mut rng, &c)); + let nf: usize = rels.iter().map(|r| r.len()).sum(); + // exact statistic, from scratch: count + fold + let iters = 300; + let t = Instant::now(); + for _ in 0..iters { + black_box(Deg::build(black_box(&rels), V)); + } + let build_ns = t.elapsed().as_nanos() as f64 / iters as f64; + let g = Deg::build(&rels, V); + let fi = 20000; + let t = Instant::now(); + for _ in 0..fi { + black_box(exact_phys(black_box(&g))); + } + let fold_ns = t.elapsed().as_nanos() as f64 / fi as f64; + // argmin over StatsManager estimates (existing optimizer decision cost) + let cards = [rels[0].len() as u64, rels[1].len() as u64, rels[2].len() as u64]; + let mut sm = StatsManager::new(); + for i in 0..3u32 { + sm.register_relation(RelId(i)); + sm.update_cardinality(RelId(i), cards[i as usize]); + } + let ai = 2_000_000; + let t = Instant::now(); + let mut acc = 0usize; + for i in 0..ai { + let (a, b, cz) = (RelId(black_box(0)), RelId(black_box(1)), RelId(black_box(2))); + let _ = i; + let est = [ + sm.estimate_join_cardinality(a, cz, &[0], &[0]), + sm.estimate_join_cardinality(a, b, &[1], &[0]), + sm.estimate_join_cardinality(b, cz, &[1], &[1]), + ]; + acc += argmin_opt(black_box(est)); + } + black_box(acc); + let argmin_ns = t.elapsed().as_nanos() as f64 / ai as f64; + // incremental maintenance of the three exact sizes under inserts + let it = 100; + let t = Instant::now(); + let mut final_ex = [0u64; 3]; + for _ in 0..it { + let mut gd = Deg::zero(V); + let mut ex = [0u64; 3]; + for &(a, b) in &rels[0] { + ex[0] += gd.d[2][0][a as usize] as u64; + ex[1] += gd.d[1][0][b as usize] as u64; + gd.d[0][0][a as usize] += 1; + gd.d[0][1][b as usize] += 1; + } + for &(a, b) in &rels[1] { + ex[1] += gd.d[0][1][a as usize] as u64; + ex[2] += gd.d[2][1][b as usize] as u64; + gd.d[1][0][a as usize] += 1; + gd.d[1][1][b as usize] += 1; + } + for &(a, b) in &rels[2] { + ex[0] += gd.d[0][0][a as usize] as u64; + ex[2] += gd.d[1][1][b as usize] as u64; + gd.d[2][0][a as usize] += 1; + gd.d[2][1][b as usize] += 1; + } + final_ex = black_box(ex); + } + let incr_ns = t.elapsed().as_nanos() as f64 / it as f64; + assert_eq!(final_ex, exact_phys(&g), "incremental maintenance != fold"); + println!( + "{name}: facts={nf} | from-scratch degree build {:.1} us ({:.2} ns/fact) | fold (3 pairings) {:.0} ns | build+fold per decision {:.1} us | optimizer argmin (3 estimate calls) {:.0} ns | incremental exact maintenance {:.2} ns/insert ({:.1} us for all {} inserts, final == fold: ok) | degree memory {} KiB", + build_ns / 1000.0, build_ns / nf as f64, fold_ns, (build_ns + fold_ns) / 1000.0, + argmin_ns, incr_ns / nf as f64, incr_ns / 1000.0, nf, + 6 * V * 4 / 1024 + ); + } +} + +fn tie_min(e: [u64; 3]) -> bool { + let m = *e.iter().min().unwrap(); + e.iter().filter(|&&x| x == m).count() > 1 +} From 840fbe981b1c7ac98738934bc0981e05b75a42f7 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 21:24:26 +0000 Subject: [PATCH 7/8] A10 admission probe: Given / Observed / Derived / Measured (test-local) Each kind needs positive evidence and reuses an existing owner: Given is capped by the hydration proof (givens layer), Observed needs a new independent root (GadamerRevision) and earns no certification, Derived needs a rule identity, inherited roots only, and stays at or below Related, Measured goes through pearl::reason/revise. Rule identity is an explicit gap (RuleIdentityGap stand-in); no production writer goes through this. F2/F4/F5 per the pre-registration in a10-finding.md: F2 and F5 hold under the probe; F4 fires (a certification written before its support was withdrawn stands; no writer lowers it). Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01X6y3drwKSE2zSgoexheLFX --- .../tests/a10_epistemic_admission_probes.rs | 24 +- .../tests/a10_admission_probe.rs | 545 ++++++++++++++++++ 2 files changed, 562 insertions(+), 7 deletions(-) create mode 100644 crates/lance-graph-planner/tests/a10_admission_probe.rs diff --git a/crates/cognitive-shader-driver/tests/a10_epistemic_admission_probes.rs b/crates/cognitive-shader-driver/tests/a10_epistemic_admission_probes.rs index fa8191932..85fe18d8c 100644 --- a/crates/cognitive-shader-driver/tests/a10_epistemic_admission_probes.rs +++ b/crates/cognitive-shader-driver/tests/a10_epistemic_admission_probes.rs @@ -18,9 +18,7 @@ use causal_edge::{CausalEdge64, Opcode}; use lance_graph_contract::alpha::{stamp_of, AlphaAllocation, AlphaOverlay}; use lance_graph_contract::canonical_node::{EdgeBlock, NodeGuid, NodeRow, TailVariant}; -use lance_graph_contract::epistemic_state5::{ - Certification3, Epi5Gen, EpistemicState5, Topology2, -}; +use lance_graph_contract::epistemic_state5::{Certification3, Epi5Gen, EpistemicState5, Topology2}; use lance_graph_contract::revision::{ BasisView, CodebookId, EncounterEvidence, EvidentialEffect, GadamerRevision, GrammarId, HorizonId, InterpretiveHorizon, LanguageId, LensId, QuestionId, RevisionPolicy, @@ -51,7 +49,10 @@ fn f1_stamped_direct_open_is_bit_identical_to_an_unwritten_edge() { assert_eq!(direct_open().raw(), 0, "Direct × Open must be raw5 = 0"); let unwritten = CausalEdge64(0); let stamped = unwritten.with_epistemic_raw5(direct_open().raw()); - assert_eq!(stamped.0, unwritten.0, "the CE64 word cannot carry the stamp"); + assert_eq!( + stamped.0, unwritten.0, + "the CE64 word cannot carry the stamp" + ); // Control: a non-zero code IS visible, so the comparison can fail. let supports = EpistemicState5::new(Epi5Gen::V1, Topology2::Direct, Certification3::Supports); @@ -70,7 +71,10 @@ fn f1_alpha_claim_distinguishes_what_ce64_cannot() { ov.claim(written, 0).expect("allocated address"); assert!(ov.get(written).is_some(), "stamped: claimed"); - assert!(ov.get(never).is_none(), "unwritten: not attended, no fallback"); + assert!( + ov.get(never).is_none(), + "unwritten: not attended, no fallback" + ); // The claim stamps the claimed row (`AlphaStamp`: cycle, saccade order, // visits); the base row stays all-zero. The stamp, not bits 59..63, is the // written-by record. @@ -140,7 +144,9 @@ fn ancestry(independent: u64) -> BasisView { } fn effect(enc: &EncounterEvidence, basis: &BasisView) -> EvidentialEffect { - GadamerRevision.revise(&horizon(), enc, basis).evidential_effect + GadamerRevision + .revise(&horizon(), enc, basis) + .evidential_effect } /// F3, contract revision: `GadamerRevision` refuses an overlapping root and @@ -195,7 +201,11 @@ fn f3_overlap_enforcement_depends_on_the_supplied_ancestry() { fn f6_maturation_never_alters_epistemic_state() { let codes = [ direct_open(), - EpistemicState5::new(Epi5Gen::V1, Topology2::IndirectKnown, Certification3::Supports), + EpistemicState5::new( + Epi5Gen::V1, + Topology2::IndirectKnown, + Certification3::Supports, + ), EpistemicState5::new(Epi5Gen::V1, Topology2::Unknown, Certification3::Causes), ]; for state in codes { diff --git a/crates/lance-graph-planner/tests/a10_admission_probe.rs b/crates/lance-graph-planner/tests/a10_admission_probe.rs new file mode 100644 index 000000000..0bed80032 --- /dev/null +++ b/crates/lance-graph-planner/tests/a10_admission_probe.rs @@ -0,0 +1,545 @@ +//! A10: a typed epistemic admission probe. TEST-LOCAL; nothing here is a +//! production writer, and nothing here allocates a CE64 bit. +//! +//! Every write of bits 59..63 must name what kind of evidence licenses it. +//! The four kinds reuse the existing owners: +//! +//! | kind | requires | owner reused | may reach | +//! |---|---|---|---| +//! | Given | an identified source and the proof hydration ran | givens layer cap (membership: Associated; resolved relation: Related) | at most Related | +//! | Observed | an identified source and a new independent root | `GadamerRevision` + `BasisView` | no change: eligibility is not acceptance | +//! | Derived | a rule identity, premises, inherited roots only | `GadamerRevision` (inherited roots never count as new) | min of the premises, never above Related | +//! | Measured | an executed operator result | `pearl::reason` + `pearl::revise` | what the operator earned (SO ≤ CausalCandidate; only PO reaches Causes) | +//! +//! Two gaps are explicit, not papered over: +//! +//! - **Rule identity.** No contract type carries the identity of a +//! derivation rule. [`RuleIdentityGap`] stands in for it here, and a +//! derivation without one is refused. +//! - **The production admission boundary.** Production code writes bits +//! 59..63 through `pearl::revise` / `pearl::hydrate`, `edge_v3::rehydrate` +//! (a restore) and `deepnsm_v2::wire` (its own observational rule). None of +//! them goes through an admission check like this one; this probe does not +//! change that. +//! +//! The "Derived never above Related" line is the pre-registered F5 bar +//! (`a10-finding.md`): Supports is earned by an observational fold, and a +//! formally valid derivation is neither observation nor causal evidence. + +use causal_edge::edge::InferenceType; +use causal_edge::{CausalEdge64, CausalMask, PlasticityState}; +use lance_graph_contract::band_reading::EdgeProvenance; +use lance_graph_contract::causal_audit::{EvidenceSourceId, SupportBasis}; +use lance_graph_contract::certification::{CertificationModel, ModelBuilder}; +use lance_graph_contract::class_view::ClassId; +use lance_graph_contract::epistemic_state5::{ + Certification3, Epi5Declarations, Epi5Gen, Epi5Reading, EpistemicState5, Topology2, +}; +use lance_graph_contract::rail_geometry::RailAxis; +use lance_graph_contract::revision::{ + BasisView, CodebookId, EncounterEvidence, EvidentialEffect, GadamerRevision, GrammarId, + HorizonId, InterpretiveHorizon, LanguageId, LensId, QuestionId, RevisionPolicy, +}; +use lance_graph_planner::pearl::{self, Edit, Evidence, Reading}; + +const CLASS: ClassId = 0x0902; +const RAIL: RailAxis = RailAxis::Taxonomy; + +/// Stand-in for the missing rule-identity carrier. Its existence here is the +/// gap: no contract type names which rule produced a derivation. +#[derive(Clone, Copy)] +struct RuleIdentityGap(#[allow(dead_code)] u32); + +#[derive(Clone, Copy)] +enum GivenProof { + None, + Membership, + ResolvedRelation, +} + +enum Admission<'a> { + Given { + source: u64, + proof: GivenProof, + topology: Topology2, + }, + Observed { + roots: u64, + }, + Derived { + rule: Option, + premises: &'a [EpistemicState5], + inherited: u64, + independent: u64, + }, + Measured { + edge: CausalEdge64, + evidence: &'a Evidence<'a>, + }, +} + +#[derive(Debug, PartialEq, Eq)] +enum Refusal { + Unidentified, + NoNewRoot, + MintsRoot, + RuleIdentityGap, + NoPremises, + Ungrounded, +} + +fn horizon() -> InterpretiveHorizon<(), u64> { + InterpretiveHorizon { + id: HorizonId(0), + awareness: (), + question: QuestionId(0), + language: LanguageId(0), + grammar: GrammarId(0), + codebook: CodebookId(0), + lens: LensId(0), + projected_claims: 0b1, + independent_roots: 0, + inherited_roots: 0, + unresolved_tension: 0, + revision_index: 0, + } +} + +fn root_effect(independent: u64, inherited: u64, ancestry: u64) -> EvidentialEffect { + let enc = EncounterEvidence { + proposed_claims: 0b1, + independent_roots: independent, + inherited_roots: inherited, + resistance: 0, + contradictions: 0, + affected_parts: 0, + }; + let basis = BasisView { + ancestry_independent_roots: ancestry, + ancestry_derived_roots: 0, + ancestor_claims: 0, + closes_cycle: false, + }; + GadamerRevision + .revise(&horizon(), &enc, &basis) + .evidential_effect +} + +fn declarations() -> Epi5Declarations { + let mut d = Epi5Declarations::new(); + d.declare( + CLASS, + RAIL, + Epi5Reading { + generation: Epi5Gen::V1, + }, + ); + d +} + +fn state(e: CausalEdge64) -> EpistemicState5 { + EpistemicState5::decode(Epi5Gen::V1, e.epistemic_raw5()).expect("valid code") +} + +/// Decide what one admission may write, given the current state and the +/// ancestry's independent roots. Returns the new edge. +fn admit( + current: CausalEdge64, + a: &Admission<'_>, + ancestry_roots: u64, + d: &Epi5Declarations, +) -> Result { + let now = state(current); + let write = |s: EpistemicState5| current.with_epistemic_raw5(s.raw()); + match a { + Admission::Given { + source, + proof, + topology, + } => { + if *source == 0 { + return Err(Refusal::Unidentified); + } + let c = match proof { + GivenProof::None => Certification3::Open, + GivenProof::Membership => Certification3::Associated, + GivenProof::ResolvedRelation => Certification3::Related, + }; + Ok(write(EpistemicState5::new(Epi5Gen::V1, *topology, c))) + } + Admission::Observed { roots } => { + if *roots == 0 { + return Err(Refusal::Unidentified); + } + if root_effect(*roots, 0, ancestry_roots) != EvidentialEffect::IncreaseEligible { + return Err(Refusal::NoNewRoot); + } + // A new root is eligibility, not certification. + Ok(current) + } + Admission::Derived { + rule, + premises, + inherited, + independent, + } => { + if rule.is_none() { + return Err(Refusal::RuleIdentityGap); + } + if *independent != 0 { + return Err(Refusal::MintsRoot); + } + let Some(weakest) = premises + .iter() + .map(|p| p.certification()) + .min_by_key(|c| c.code()) + else { + return Err(Refusal::NoPremises); + }; + debug_assert_ne!( + root_effect(0, *inherited, ancestry_roots), + EvidentialEffect::IncreaseEligible + ); + let capped = if weakest.code() > Certification3::Related.code() { + Certification3::Related + } else { + weakest + }; + Ok(write(now.with_certification(capped))) + } + Admission::Measured { edge, evidence } => { + let m = pearl::reason(*edge, evidence, Edit::None); + if m.ungrounded().is_some() { + return Err(Refusal::Ungrounded); + } + let reading = Reading { + declarations: d, + class: CLASS, + rail: RAIL, + provenance: EdgeProvenance::V2Stamped, + }; + pearl::revise(&m, reading).map_err(|_| Refusal::Ungrounded) + } + } +} + +fn edge(mask: CausalMask, s: EpistemicState5) -> CausalEdge64 { + CausalEdge64::pack( + 10, + 0x91, + 30, + 200, + 200, + mask, + causal_edge::Orientation::from_code(0b101), + InferenceType::Deduction, + PlasticityState::S_HOT, + 0, + ) + .with_epistemic_raw5(s.raw()) +} + +fn at(t: Topology2, c: Certification3) -> EpistemicState5 { + EpistemicState5::new(Epi5Gen::V1, t, c) +} + +/// Observational population: `Related`, not `Supports` (pearl's own fixture). +fn observational() -> ModelBuilder { + let mut b = ModelBuilder::new(); + b.cell(0, true, 5, 4); + b.cell(0, false, 5, 1); + b.cell(1, true, 3, 1); + b.cell(1, false, 3, 2); + b.sources(SupportBasis::DirectlyObserved, &[1, 2]); + b +} + +fn with_trial(mut b: ModelBuilder) -> CertificationModel { + b.arm(true, 6, 5); + b.arm(false, 6, 1); + b.sources(SupportBasis::InterventionBacked, &[10, 11]); + b.build() +} + +fn evidence(model: &CertificationModel) -> Evidence<'_> { + Evidence { model, chain: None } +} + +// ── each kind needs positive evidence ───────────────────────────────── + +#[test] +fn given_requires_an_identified_source_and_is_capped_by_its_proof() { + let d = declarations(); + let e = edge(CausalMask::SO, at(Topology2::Unknown, Certification3::Open)); + let given = |source, proof| Admission::Given { + source, + proof, + topology: Topology2::Direct, + }; + assert_eq!( + admit(e, &given(0, GivenProof::ResolvedRelation), 0, &d), + Err(Refusal::Unidentified) + ); + for (proof, want) in [ + (GivenProof::None, Certification3::Open), + (GivenProof::Membership, Certification3::Associated), + (GivenProof::ResolvedRelation, Certification3::Related), + ] { + let out = admit(e, &given(1, proof), 0, &d).expect("identified"); + assert_eq!(state(out).certification(), want); + } +} + +#[test] +fn an_observation_needs_a_new_root_and_earns_no_certification() { + let d = declarations(); + let s = at(Topology2::Direct, Certification3::Associated); + let e = edge(CausalMask::SO, s); + let out = admit(e, &Admission::Observed { roots: 0b10 }, 0, &d).expect("new root"); + assert_eq!(out, e, "eligibility is not acceptance"); + assert_eq!( + admit(e, &Admission::Observed { roots: 0b10 }, 0b10, &d), + Err(Refusal::NoNewRoot) + ); +} + +#[test] +fn a_derivation_without_rule_identity_is_refused() { + let d = declarations(); + let e = edge(CausalMask::SO, at(Topology2::Direct, Certification3::Open)); + let premises = [at(Topology2::Direct, Certification3::Related)]; + let derived = |rule| Admission::Derived { + rule, + premises: &premises, + inherited: 0b110, + independent: 0, + }; + assert_eq!( + admit(e, &derived(None), 0, &d), + Err(Refusal::RuleIdentityGap) + ); + assert!(admit(e, &derived(Some(RuleIdentityGap(7))), 0, &d).is_ok()); +} + +#[test] +fn a_derivation_cannot_mint_an_independent_root() { + let d = declarations(); + let e = edge(CausalMask::SO, at(Topology2::Direct, Certification3::Open)); + let premises = [at(Topology2::Direct, Certification3::Related)]; + let a = Admission::Derived { + rule: Some(RuleIdentityGap(7)), + premises: &premises, + inherited: 0b110, + independent: 0b1000, + }; + assert_eq!(admit(e, &a, 0, &d), Err(Refusal::MintsRoot)); +} + +// ── F5: a valid derivation is not causal evidence ───────────────────── + +#[test] +fn f5_a_derivation_from_causal_premises_stays_below_causal() { + let d = declarations(); + let e = edge( + CausalMask::SO, + at(Topology2::IndirectKnown, Certification3::Open), + ); + let causes = [ + at(Topology2::Direct, Certification3::Causes), + at(Topology2::Direct, Certification3::Causes), + ]; + let out = admit( + e, + &Admission::Derived { + rule: Some(RuleIdentityGap(1)), + premises: &causes, + inherited: 0b11, + independent: 0, + }, + 0, + &d, + ) + .expect("derived"); + assert_eq!(state(out).certification(), Certification3::Related); + assert!(state(out).certification().code() < Certification3::Supports.code()); + + // Twin: an executed randomized trial on the same relation does reach + // Causes, so the cap is about the evidence kind, not the relation. + let model = with_trial(observational()); + let ev = evidence(&model); + let po = edge( + CausalMask::PO, + at(Topology2::IndirectKnown, Certification3::Related), + ); + let measured = admit( + po, + &Admission::Measured { + edge: po, + evidence: &ev, + }, + 0, + &d, + ) + .expect("trial ran"); + assert_eq!(state(measured).certification(), Certification3::Causes); +} + +#[test] +fn a_measured_observation_is_not_an_intervention() { + let d = declarations(); + let model = with_trial(observational()); + let ev = evidence(&model); + let so = edge( + CausalMask::SO, + at(Topology2::IndirectKnown, Certification3::Open), + ); + let out = admit( + so, + &Admission::Measured { + edge: so, + evidence: &ev, + }, + 0, + &d, + ) + .expect("observational fold ran"); + let c = state(out).certification(); + assert!( + c.code() <= Certification3::CausalCandidate.code(), + "an observational measurement reached {c:?}" + ); + assert_ne!(c, Certification3::Open, "the fold earned something"); +} + +// ── F2: elimination (Sudoku) does not raise certification ───────────── + +#[test] +fn f2_ruling_out_rivals_does_not_raise_the_survivors_certification() { + let d = declarations(); + // Eight eliminated rivals, each eliminated by a Related constraint, + // leave one candidate. The survivor is derived from those constraints. + let constraints = [at(Topology2::Direct, Certification3::Related); 8]; + let survivor = edge( + CausalMask::SO, + at(Topology2::Direct, Certification3::Associated), + ); + let elim = Admission::Derived { + rule: Some(RuleIdentityGap(9)), + premises: &constraints, + inherited: 0xFF, + independent: 0, + }; + let once = admit(survivor, &elim, 0, &d).expect("derived"); + assert_eq!(state(once).certification(), Certification3::Related); + // The derived cell may not carry a root of its own. + let minted = Admission::Derived { + rule: Some(RuleIdentityGap(9)), + premises: &constraints, + inherited: 0xFF, + independent: 0x100, + }; + assert_eq!(admit(survivor, &minted, 0, &d), Err(Refusal::MintsRoot)); + // Maturation may move; certification does not. + let matured = once.with_novelty_wisdom(7); + assert_eq!(matured.epistemic_raw5(), once.epistemic_raw5()); +} + +/// F2 silent twin: two givens from different sources stay independent; the +/// second is a new root, not a duplicate of the first. +#[test] +fn f2_two_givens_from_different_sources_stay_independent() { + let d = declarations(); + let e = edge(CausalMask::SO, at(Topology2::Direct, Certification3::Open)); + assert!(admit(e, &Admission::Observed { roots: 0b01 }, 0, &d).is_ok()); + assert!(admit(e, &Admission::Observed { roots: 0b10 }, 0b01, &d).is_ok()); +} + +// ── F4: retraction ─────────────────────────────────────────────────── + +#[test] +fn f4_withdrawn_sources_block_new_certification_but_nothing_lowers_the_old() { + let d = declarations(); + let mut model = observational().build(); + let so = edge( + CausalMask::SO, + at(Topology2::IndirectKnown, Certification3::Open), + ); + let before = admit( + so, + &Admission::Measured { + edge: so, + evidence: &evidence(&model), + }, + 0, + &d, + ) + .expect("two observational sources"); + assert_ne!(state(before).certification(), Certification3::Open); + + assert_eq!(model.ledger.withdraw_source(EvidenceSourceId(1)), 1); + // A fresh measurement no longer grounds: one source left. + let fresh = edge( + CausalMask::SO, + at(Topology2::IndirectKnown, Certification3::Open), + ); + assert_eq!( + admit( + fresh, + &Admission::Measured { + edge: fresh, + evidence: &evidence(&model) + }, + 0, + &d + ), + Err(Refusal::Ungrounded) + ); + // The gap: the code written before the withdrawal stands. No production + // writer lowers a certification when its support is withdrawn. + assert_eq!( + admit( + before, + &Admission::Measured { + edge: before, + evidence: &evidence(&model) + }, + 0, + &d + ), + Err(Refusal::Ungrounded) + ); + assert_ne!(state(before).certification(), Certification3::Open); +} + +/// F4 silent twin: withdrawing a source the ledger never held changes +/// nothing; the same measurement earns the same code. +#[test] +fn f4_withdrawing_an_unrelated_source_changes_nothing() { + let d = declarations(); + let mut model = observational().build(); + let so = edge( + CausalMask::SO, + at(Topology2::IndirectKnown, Certification3::Open), + ); + let before = admit( + so, + &Admission::Measured { + edge: so, + evidence: &evidence(&model), + }, + 0, + &d, + ); + assert_eq!(model.ledger.withdraw_source(EvidenceSourceId(99)), 0); + let after = admit( + so, + &Admission::Measured { + edge: so, + evidence: &evidence(&model), + }, + 0, + &d, + ); + assert_eq!(before, after); + assert!(after.is_ok()); +} From ba845801e1c42fafa9dd89ef84e4d77bc3c64be9 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 2 Nov 2026 21:25:27 +0000 Subject: [PATCH 8/8] board: A10 probe outcomes, S6 fork correction, NNUE triangle PARK Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_01X6y3drwKSE2zSgoexheLFX --- ...1-02-a10-probes-s6-magic-nnue-baselines.md | 72 +++++++++++++++++++ .claude/board/entries/README.md | 3 +- 2 files changed, 74 insertions(+), 1 deletion(-) create mode 100644 .claude/board/entries/2026-11-02-a10-probes-s6-magic-nnue-baselines.md diff --git a/.claude/board/entries/2026-11-02-a10-probes-s6-magic-nnue-baselines.md b/.claude/board/entries/2026-11-02-a10-probes-s6-magic-nnue-baselines.md new file mode 100644 index 000000000..231a1543c --- /dev/null +++ b/.claude/board/entries/2026-11-02-a10-probes-s6-magic-nnue-baselines.md @@ -0,0 +1,72 @@ +# A10 probe outcomes, S6 Magic Sets correction, NNUE triangle baselines (2026-11-02) + +**Status:** measured. A10 is a test-local probe, not promoted to production. S6 is pushed to the xlog fork and compiles; it has not been run. NNUE is PARKED for the triangle optimizer only. + +## A10 — probe-first outcomes (F1/F3/F6 run before any admission writer) + +Files: `cognitive-shader-driver/tests/a10_epistemic_admission_probes.rs` (F1/F3/F6) and `lance-graph-planner/tests/a10_admission_probe.rs` (admission probe, F2/F4/F5). Each guard was disable-verified red-then-green (3 + 5 disables). + +| F | outcome | what it means | +|---|---|---| +| F1 | CONFIRMED | A stamped Direct × Open (`raw5 = 0`) is bit-identical to an unwritten `CausalEdge64`. Only the alpha claim distinguishes them on the hot path: `get` returns `None` when unclaimed, and a claimed row carries an `AlphaStamp` in its value. After seal, only the row's existence remains. | +| F3 | FIRES on the CE64 path, HOLDS elsewhere | `Opcode::Revision.truth_code` and `CausalEdge64::learn` get identical inputs for overlapping and independent roots and raise c on a duplicate. Two places do enforce overlap. `deepnsm_v2::belief::BeliefArena::revise_at` (production) has a stamp gate, already pinned by `revision_overlapping_stamp_is_rejected`. `GadamerRevision` refuses a repeated root only through the caller's `BasisView`; with an empty ancestry the duplicate is admitted. The test-local P-E2 gate is not production evidence. | +| F6 | HOLDS | `with_novelty_wisdom` (0..=9, saturating) and `learn()` never move bits 59..63. | +| F2 | HOLDS under the probe | A cell derived by elimination stays at its constraints' certification (Related) and cannot mint a root. Two givens from different sources stay independent. | +| F4 | FIRES | Withdrawing one of two sources blocks a fresh measurement (`Ungrounded`), but a certification written before the withdrawal stands. No writer lowers it. Withdrawing an unrelated source changes nothing. | +| F5 | HOLDS under the probe | A derivation from Causes premises stays at Related. Under the same model, an executed randomized trial earns Causes. An observational (SO) measurement stays at or below CausalCandidate (pearl's cap). | + +The admission probe types four kinds, each requiring positive evidence: +- Given: an identified source, capped by the hydration proof. +- Observed: a new independent root; earns no certification, because eligibility is not acceptance. +- Derived: a rule identity and inherited roots only; at most Related. +- Measured: `pearl::reason` / `pearl::revise`. + +**Explicit gaps:** +- (1) No contract type carries a derivation rule's identity; the probe uses a `RuleIdentityGap` stand-in and refuses a derivation without one. +- (2) Production admission boundary: no production writer goes through an admission check. +- (3) Retraction does not lower a written certification (F4). + +### `with_epistemic_raw5` call-site census (production src, read at each site) + +| site | class | +|---|---| +| `lance-graph-planner/src/pearl.rs:404` `stamp`, called by `revise` and `hydrate` | AUTHORIZED: evidence-gated, certification only rises to what the operator earned | +| `causal-edge/src/edge_v3.rs:296` `rehydrate` | AUTHORIZED: restores the existing code on the V3 lift; mints nothing | +| `deepnsm-v2/src/wire.rs:974` and `:1036` `wire` | UNDOCUMENTED SECOND WRITER: writes certification from its own grammar evidence, and promotes Related → CausalCandidate on ≥ 2 independent implication sources (stamp popcount). Consistent with the SO cap, but outside `pearl::revise`. Needs an owner decision. | +| `nars/{warrant,scent,scent_learn,mediator}.rs`, `chain_counterfactual.rs`, `edge_v3.rs:894` | inside `#[cfg(test)]`: fixtures | +| examples (`cognitive-shader-driver`, `lance-graph-planner`) and `tests/` | documented lab exceptions | + +## S6 — XLOG fork Magic Sets correction + +AdaWorldAPI/xlog `claude/magic-query-relative-demand` @ `ea924b3`. +- `undemanded_consumer_reasons` accepts only two kinds of reader of a target predicate: the target rules themselves, and the generated `__xlog_query_{i}` rule of a seeded query whose body is exactly that query's atom. +- Every other reader yields a reason routed through `decline_or_error`: positive, negated or epistemic, in an ordinary rule, a constraint rule, a learnable rule, or an unseeded query. +- Modes: `auto` declines and keeps the program unchanged; `on` errors; `off` is unchanged. +- Verification follows the fork's rules: `cargo check` and `clippy -p xlog-logic` are clean (warning count unchanged, 78), and no tests were added or run. Compiling is not runtime validation. +- The reach/far counterexample remains the named external falsifier. +- Known conservative case: a seeded query whose generated rule a later normalization pass rewrote is declined. + +## NNUE — triangle pairing baselines + +`xlog-relcomp-probe/examples/triangle_pairing_baselines.rs`, re-run by the orchestrator. Results were identical and deterministic: 72 instances, 4 train and 2 held-out families. The probe's argmin is verbatim `optimizer.rs:3395-3404`. + +| arm | mean regret | optimal choices / 72 | permutation changes | +|---|---|---|---| +| a0: estimates from cardinalities | 13.13 (worst 143.8) | 45 | 24 | +| a1: estimates plus exact distinct counts | 4.78 (worst 113) | 48 | 1 | +| exact-degree fold | 1.000 | 72 | 0 | + +- **Collapse:** 24 equal-size instances collapse to a three-way tie; all fall to SharedY by evaluation order. F4 kills a0. +- **Cost:** the exact fold costs 1.3–2.7 ns per insert to maintain incrementally (96 KiB), or 31–50 µs from scratch. The existing argmin costs 113–139 ns. +- **The brute-force join** is an oracle arm only: it materializes the join to check the fold, and is not timed. +- **Verdict:** PARK NNUE for this optimizer. The fix is to keep exact degree counts, not a model. NNUE is not parked globally. + +Second application (Ruff × ARM × SoC): +- **Verdict: NEEDS-GROUND-TRUTH-FIRST.** +- **Candidate target:** ranking the next residue on the MedCare furnace exam. The deterministic baseline already exists: `rekey_exam` sorts by method count. +- **Not applicable yet:** + - Ranking Concern and Scope splits: nothing generates candidate splits. + - ARM: it has no `FeatureSpec` over ruff facts. + - CB2Vec and NORU: absent from the workspace. +- **Gate before any trial:** a private outcome TSV in MedCare-rs and a measured precision@15 baseline. +- **Kill condition:** the ranker ranks the gate-refused `user_right` above a confirmed mint. diff --git a/.claude/board/entries/README.md b/.claude/board/entries/README.md index 583383a43..e10dbcc19 100644 --- a/.claude/board/entries/README.md +++ b/.claude/board/entries/README.md @@ -25,7 +25,7 @@ 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. -556 entries, 2026-08-06 .. 2026-11-02. +557 entries, 2026-08-06 .. 2026-11-02. | date | entry id | finding | file | |---|---|---|---| @@ -40,6 +40,7 @@ exactly one of them, never both. | 2026-11-02 | `codegraph-review-operator-decisions` | | [2026-11-02-codegraph-review-operator-decisions.md](2026-11-02-codegraph-review-operator-decisions.md) | | 2026-11-02 | `codegraph-dr3-dt64-ruff-architecture-review` | | [2026-11-02-codegraph-dr3-dt64-ruff-architecture-review.md](2026-11-02-codegraph-dr3-dt64-ruff-architecture-review.md) | | 2026-11-02 | `D-CODEGRAPH-0-BIN` | | [2026-11-02-codegraph-bin-reader-timing-gate.md](2026-11-02-codegraph-bin-reader-timing-gate.md) | +| 2026-11-02 | `a10-probes-s6-magic-nnue-baselines` | | [2026-11-02-a10-probes-s6-magic-nnue-baselines.md](2026-11-02-a10-probes-s6-magic-nnue-baselines.md) | | 2026-11-02 | `a10-epistemic-admission-and-nnue-compiler` | | [2026-11-02-a10-epistemic-admission-and-nnue-compiler.md](2026-11-02-a10-epistemic-admission-and-nnue-compiler.md) | | 2026-11-01 | `D-XLOG-NATIVE-7` | | [2026-11-01-xlog-resident-hypercube-context.md](2026-11-01-xlog-resident-hypercube-context.md) | | 2026-11-01 | `D-XLOG-NATIVE-8` | | [2026-11-01-xlog-behaviour-lens-f100.md](2026-11-01-xlog-behaviour-lens-f100.md) |