Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .claude/board/STATUS_BOARD.md
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ Plan: `.claude/plans/nal-polyfill-v1.md`. Template: D-SQLP. No opcode, tenant or
| **D-NALP-1** | Statement layout + declared ClassView reading; additive `WideFieldMask` projection path on `ClassView`; binder's mask; term width decision | Partial: #1525 merged `08635ed` (NodeGuid reading `nal-statement-v1`, term width u16, aperture selection); ClassView wide projection and binder mask Queued | only the mask's lanes are read; undeclared reading and masked-out field refuse; Small = Wide answers |
| **D-NALP-2** | Query procedures: select, truth threshold, choice by confidence, support count | Shipped #1525 `08635ed` (`crates/lance-graph-nal`: NodeGuid key, byte apertures; 7 oracle tests, 7 disable runs red; 19–248 ns at 1,048,576 rows); support count and argmax not built | equal to a row-at-a-time oracle; one-pattern threshold disable red; no new opcode |
| **D-NALP-3** | Revision as `GroupCrossPowerSumsI32` over stored `w`/`f` | Shipped #1538 `4ceaab3` (`nal::Revision`, key = evidence class from the stamps; refuses overlap and missing provenance; 11 tests, 7 disable runs red; ONA's mid-fold saturation is a documented divergence, the fold keeps the exact sum) | equal to exact integer revision; OpenNARS revision points; duplicate evidence counts once |
| **D-NALP-4** | Per-row truth law into a lane: `MapPairU8` vs fixed-point multiply-high | Queued | insufficiency proof; per-row parity with `isa::truth`, error in codes; one shape also serves SQL nullable arithmetic |
| **D-NALP-4** | Per-row truth law into a lane: `MapPairU8` vs fixed-point multiply-high | Partial, in PR: the exact scalar law for deduction / induction / abduction is `causal_edge::isa::truth::code`, packed by `execute` and `syllogize` (0 of 2³² rows off the oracle; harvested from draft #1544); whether a lane terminal is still needed is open (entry `2026-10-14-isa-truth-exact-codes.md`) | insufficiency proof; per-row parity with `isa::truth`, error in codes; one shape also serves SQL nullable arithmetic |
| **D-NALP-5** | Deduction / induction / abduction / analogy over populations | Queued (after D-NALP-4) | equals pairwise scalar rule; abduction direction test red for the reversed statement |
| **D-NALP-6** | `lance-graph-nal`: typed `nal::{Operation}` surface (`NalOp`), parser (tokens only), binder over the reverse label index, `NAL::narsese()`, typed refusals | Queued | text and typed calls give byte-identical `Program`s; no string in the plan, rows or wire; ambiguous / unknown label refuses; lowers from the bound plan; no fallback; no `Program` outside procedures |
| **D-NALP-7** | Oracle parity on NAL-1/2 fixtures vs pinned OpenNARS, `jc::reliability` | Queued | within D-HL-4 tolerance per rule |
Expand Down
57 changes: 57 additions & 0 deletions .claude/board/entries/2026-10-14-isa-truth-exact-codes.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
# 2026-10-14 — Exact truth codes in `causal_edge::isa::truth` (D-NALP-4 harvest)

**Status:** IN PR (branch `ccr-9f3b8075-74u47l-truth-exact`). Harvested from
the draft #1544, which stays open as a reminder and is not merged.

**DECISION:** the exact per-row truth law lives where NARS truth already
lives, in `causal_edge::isa::truth`, not in a second substrate (a lane
terminal plus `nal::Deduction` in `lance-graph-nal`).

**SCOPE:** deduction, induction, abduction. `truth::code::{deduction,
induction, abduction}` take and return `u8` codes (`x / 255`), compute in
integers and round once, half up. `Opcode::truth_code` routes the three
rules there; `execute` (so `forward` and the named register methods) and
`syllogize` pack its result. Revision and synthesis keep the `f32` path,
unchanged. The `f32` functions stay for real-valued callers.

**MEASURED** (`cargo run --release -p causal-edge --example
truth_exact_probe`, all 2³² inputs per law, 8 threads, 37.7 s):

| law | `truth::code` ≠ oracle | old `f32` path ≠ oracle |
|---|---:|---:|
| deduction | 0 | 2,413 |
| induction | 0 | 4,352 |
| abduction | 0 | 4,352 |

Every old miss is one confidence code. The oracle
(`crates/causal-edge/tests/common/truth_law.rs`) is written independently of
the crate.

**TEST-PINNED** (`tests/ce64_truth_exact.rs`, 8 tests): hand-computed codes;
two half-up ties (127.5 → 128, 110.5 → 111, the second rejects
ties-to-even); the induction/abduction mirror; zero evidence; OpenNARS nal1.1
at `4d1bdb0` (0.81 deduction, 0.45 induction); the codes equal the oracle on
the every-5th grid; `execute` and `syllogize` pack the oracle's codes on
exactly the inputs where the `f32` path was off (12 deduction rows of the
grid, 17 induction and 17 abduction rows of an exhaustive three-code scan;
the grid alone holds none for induction or abduction). Disable runs, each
red: `truth_code` back to `f32` (register test), `syllogize` deduction back
to `f32` (syllogize test), round down, ties-to-even. The legacy golden
capture (`ce64_isa_golden.rs`, 126 words) is unchanged: none of its inputs
is one the `f32` path got wrong.

**MEASURED, differential against `main`** (2,000,000 random register-word
pairs, all bits random including 50..52 and 59..63; every path: the five
`execute` opcodes, `syllogize`, `learn`, `revision`; 16,000,000 results): 5
words differ (3 induction, 1 deduction, 1 abduction), each only in the
confidence field (bits 32..39), by exactly one code. No other bit changed;
`learn`, `revision` and synthesis are bit-identical.

**OPEN:**
- Revision and synthesis are not exact. Exact revision has to settle the
both-operands-at-255 case first (today it packs `(0, 0)`, pinned as a known
defect in `ce64_isa_golden.rs`).
- Whether D-NALP-4/5 still need a lane terminal at all, or whether
populations of CE64 registers carry the law (plan §4 B/C). Not decided.
- `cargo clippy -D warnings` on `causal-edge` fails on six lints in code this
change does not touch; CI runs only `cargo test` for the crate.
60 changes: 60 additions & 0 deletions .claude/board/entries/2026-10-14-revision-layer-invariants.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
# 2026-10-14 — Revision layers: Gadamer gate invariants, Rubikon goal revision

**Status:** DECISION (approved 2026-10-14, points 1–4). The invariants are
TEST-PINNED; points 2–4 are specifications whose tests are OPEN.

## 1. Gadamer revision: invariants that must never be touched

`lance_graph_contract::revision::GadamerRevision` decides **whether** and
**in which sense** an encounter revises an interpretive horizon. It is set
algebra over masks (union, intersection, difference, subset over claims,
roots, resistance and contradictions); it never counts.

| # | invariant | pinned by |
|---|---|---|
| G1 | An echo or a closed cycle earns zero evidential weight (`Echo`, `ClosedCycle` → `NoIncrease`). | `revision.rs`: `repeated_derived_claim_is_an_echo_and_adds_no_weight`, `self_supporting_ancestry_is_a_closed_cycle`; `planner/tests/gadamer_isa_revision.rs`: `echo_cannot_mint_confidence`, `a_closed_cycle_cannot_mint_confidence` |
| G2 | A synthesis must be earned: `HorizonFusion` needs a new independent root; the same contradiction without one is `ContradictionPreserved` → `Suspend`. No `IncreaseEligible` kind is reachable without a new root. | `contradiction_without_a_new_root_suspends_instead_of_synthesising`, `no_increase_eligible_outcome_is_reachable_without_a_new_root`, the `debug_assert` in `revise` |
| G3 | Gadamer, not Hegel: `unresolved_tension` grows by union and is never cleared. | `fusion_accumulates_tension_and_never_clears_prior_tension` |
| G4 | The evidential effect is categorical (`IncreaseEligible` / `NoIncrease` / `Suspend`), deliberately coarser than a confidence. It is never replaced by a number. | the type `EvidentialEffect` |
| G5 | The gate sits above the register: the register never sees a horizon, the gate never computes a truth value. The register alone would inflate an echo. | `gadamer_isa_revision.rs`: `the_register_alone_would_inflate_an_echo`, `new_independent_roots_are_revised` |
| G6 | Eligibility is not acceptance: commit needs `IncreaseEligible` AND a counterfactual verdict `Necessary`; revision outputs stop before any world mutation. | `RevisionVerdict::is_acceptable`; `contract/src/kanban.rs`: `revision_verdict_alone_cannot_reach_commit` |
| G7 | Revision is relative to a question: the horizon carries `question`, `language`, `grammar`, `codebook`, `lens` and `projected_claims` (the anticipated whole the parts are read against). | the type `InterpretiveHorizon` |

## 2. The layers, kept apart

| layer | question | operands | algebra | output |
|---|---|---|---|---|
| Gadamer gate | has understanding moved, and was it earned? | claim / root / resistance / contradiction masks | set algebra | `RevisionKind` + effect |
| CE64 register (`isa::truth`) | how much evidence is pooled? | `(f, c)` codes | arithmetic (exact codes for deduction / induction / abduction, #1548) | a number |
| folded evidence | how often is it observed? | extension masks | AND + popcount at the terminal | a number from counts |
| Rubikon (kanban phases) | committed, and did the action meet the intention? | the intention's phase | phase transitions on a gate | a phase |

Counting layers answer "how much"; the Gadamer gate answers "whether". A
counting layer runs only after the gate admits the encounter (G5).

## 3. Specifications (approved, tests OPEN)

- **R1 — goal revision only outside `CognitiveWork`.** A goal's horizon is
revised in `Planning` (pre-Rubicon, including the veto) and in
`Evaluation` (postactional); never in `CognitiveWork`, where the
intention is shielded (Heckhausen). Matches `alpha-channel-rung-overlay-v1`
§3f ("revision runs in the pre-act window"). OPEN: a test that a goal
revision attempted in `CognitiveWork` is refused, and that
`Evaluation → Plan` reopens the goal.
- **R2 — agreement on a goal is a `HorizonFusion`** between two horizons,
where the other party's statement supplies the new independent root.
Without it: `ContradictionPreserved` → `Suspend`, the disagreement kept.
No new type. OPEN: what counts as an independent root for a person (an
utterance, a signed decision) — a modelling decision.
- **R3 — a goal is never revised by counting.** Evidence may inform
feasibility before the Rubicon; it never decides what the goal is.
- **R4 — evidence-layer folding** (popcount of intersections) runs only under
the Gadamer gate and only for observational statements, never for goals.

## OPEN (found while checking, pre-existing)

`execute` writes witness (53..58) and epistemic state (59..63) as 0, and
`syllogize` writes plasticity (50..52) as `0b111` and epistemic state as 0
(`isa::contracts::{FORWARD, SYLLOGIZE}`). A derived conclusion therefore does
not carry the hydration-set direct/indirect, known/unknown or CARE bits of its
premises. Whether 0 is the right state for a derived edge is not decided.
4 changes: 3 additions & 1 deletion .claude/board/entries/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,10 +25,12 @@ 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.

326 entries, 2026-08-06 .. 2026-10-13.
328 entries, 2026-08-06 .. 2026-10-14.

| date | entry id | finding | file |
|---|---|---|---|
| 2026-10-14 | `revision-layer-invariants` | | [2026-10-14-revision-layer-invariants.md](2026-10-14-revision-layer-invariants.md) |
| 2026-10-14 | `D-NALP-4` | | [2026-10-14-isa-truth-exact-codes.md](2026-10-14-isa-truth-exact-codes.md) |
| 2026-10-13 | `D-PROD-V4` | | [2026-10-13-production-targets-v4.md](2026-10-13-production-targets-v4.md) |
| 2026-10-12 | `D-SAG-1` | | [2026-10-12-software-awareness-graph.md](2026-10-12-software-awareness-graph.md) |
| 2026-10-12 | `D-CMR-RESET` | method layer was a second algebra beside the folds and slower than a row loop (430–480 µs vs 283 µs); moved to backup, rebuild as bindings over existing folds | [2026-10-12-session-reset-inventory.md](2026-10-12-session-reset-inventory.md) |
Expand Down
8 changes: 8 additions & 0 deletions .claude/plans/nal-polyfill-v1.md
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,14 @@ never twice. The path is: ndarray primitive with parity on every realization
row → mask-risc terminal with parity → `FoldDialect` wiring → one `ogar-nal`
byte → procedure.

MEASURED 2026-10-14: the exact law is now `causal_edge::isa::truth::code`
(deduction, induction, abduction), integers with one half-up rounding, and
the CE64 register paths pack it; 0 of 2³² inputs per law differ from the
independent oracle, where the old `f32` path differed on 2,413 / 4,352 /
4,352. Whether this section's lane terminal is still needed, or whether
populations of CE64 registers carry the law, is OPEN (entry
`2026-10-14-isa-truth-exact-codes.md`).

### D. Binding goes through the ClassView, never through hard-coded offsets

- A Narsese statement population is a class. Its reading of the bytes
Expand Down
86 changes: 86 additions & 0 deletions crates/causal-edge/examples/truth_exact_probe.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
//! Exhaustive check of the exact truth codes, over every input
//! (`(f1, c1, f2, c2)` ∈ 256⁴ = 2³² rows per law).
//!
//! For deduction, induction and abduction it counts the rows where
//! - `truth::code` (what the register paths pack) differs from the
//! independent oracle in `tests/common/truth_law.rs` (must be 0), and
//! - the `f32` functions, packed with `(v · 255).round()`, differ from it
//! (the error the exact codes remove).
//!
//! Run: `cargo run --release -p causal-edge --example truth_exact_probe`.

#[path = "../tests/common/truth_law.rs"]
mod truth_law;

use causal_edge::isa::{truth, Opcode};
use std::thread;

type Law = truth_law::Law;

const LAWS: [(&str, Opcode, Law, Law); 3] = [
(
"deduction",
Opcode::Deduction,
truth::code::deduction,
truth_law::deduction,
),
(
"induction",
Opcode::Induction,
truth::code::induction,
truth_law::induction,
),
(
"abduction",
Opcode::Abduction,
truth::code::abduction,
truth_law::abduction,
),
];

fn f32_path(op: Opcode, f1: u8, c1: u8, f2: u8, c2: u8) -> (u8, u8) {
let d = |x: u8| f32::from(x) / 255.0;
let (f, c) = op.truth(d(f1), d(c1), d(f2), d(c2));
(truth::pack(f), truth::pack(c))
}

fn main() {
println!(
"tier: avx512f={} avx2={} (scalar probe; exactness, not speed)",
cfg!(target_feature = "avx512f"),
cfg!(target_feature = "avx2")
);
println!("rows per law: 2^32 = {}", 1u64 << 32);
println!("{:<10} {:>14} {:>14}", "law", "code != law", "f32 != law");
for (name, op, code, oracle) in LAWS {
let threads = 8u16;
let (code_off, f32_off) = thread::scope(|s| {
let hs: Vec<_> = (0..threads)
.map(|t| {
s.spawn(move || {
let (mut a, mut b) = (0u64, 0u64);
let mut f1 = t;
while f1 < 256 {
let f1b = f1 as u8;
for c1 in 0..=255u8 {
for f2 in 0..=255u8 {
for c2 in 0..=255u8 {
let want = oracle(f1b, c1, f2, c2);
a += u64::from(code(f1b, c1, f2, c2) != want);
b += u64::from(f32_path(op, f1b, c1, f2, c2) != want);
}
}
}
f1 += threads;
}
(a, b)
})
})
.collect();
hs.into_iter()
.map(|h| h.join().expect("worker"))
.fold((0, 0), |x, y| (x.0 + y.0, x.1 + y.1))
});
println!("{name:<10} {code_off:>14} {f32_off:>14}");
}
}
16 changes: 8 additions & 8 deletions crates/causal-edge/src/edge.rs
Original file line number Diff line number Diff line change
Expand Up @@ -768,7 +768,7 @@ impl CausalEdge64 {
/// runs after decoding the weight's code.
///
/// - S/P/O: composed per plane through `compose`;
/// - truth: `op`'s canonical truth function ([`Opcode::truth`]);
/// - truth: `op`'s truth codes ([`Opcode::truth_code`]);
/// - Pearl mask: AND of both operands;
/// - inference field: `op`'s own encoding;
/// - direction and plasticity: passed through from `rhs`;
Expand All @@ -783,11 +783,11 @@ impl CausalEdge64 {
let p_out = compose.p[self.p_idx() as usize * 256 + rhs.p_idx() as usize];
let o_out = compose.o[self.o_idx() as usize * 256 + rhs.o_idx() as usize];

let (f_out, c_out) = op.truth(
self.frequency(),
self.confidence(),
rhs.frequency(),
rhs.confidence(),
let (f_out, c_out) = op.truth_code(
self.frequency_u8(),
self.confidence_u8(),
rhs.frequency_u8(),
rhs.confidence_u8(),
);

let mask_out =
Expand All @@ -802,8 +802,8 @@ impl CausalEdge64 {
s_out,
p_out,
o_out,
(f_out.clamp(0.0, 1.0) * 255.0).round() as u8,
(c_out.clamp(0.0, 1.0) * 255.0).round() as u8,
f_out,
c_out,
mask_out,
rhs.direction(), // TODO: recompute from composed palette dim0 signs
op.inference_type(),
Expand Down
Loading
Loading