diff --git a/.claude/board/entries/2026-10-22-pal256-hebbian-overlay-plasticity.md b/.claude/board/entries/2026-10-22-pal256-hebbian-overlay-plasticity.md new file mode 100644 index 000000000..1720aa0f2 --- /dev/null +++ b/.claude/board/entries/2026-10-22-pal256-hebbian-overlay-plasticity.md @@ -0,0 +1,15 @@ +# Hebbian / anti-Hebbian plasticity as a separate overlay, not codebook retraining + +Status: **lab-only**. The Google-search formula `Δz = η pre post (1 − tanh(|z|))` is a local update proposal. It is neither canonical NARS revision nor a Bayesian posterior. + +Existing owner: `.claude/knowledge/reference-frame-vs-motion.md`: immutable calibrated reference versus dynamic motion/alpha/thought. Keep the Fisher-Z pairwise LUT unchanged and version the update overlay independently. + +Probe claims ONLY: +- bounded finite latent and bounded tanh readout on tested activations; +- independent source gating prevents immediate evidence echo; +- negative coactivation can decrease the plastic overlay; +- codebook address never changes. + +Refusals/next experiments: dependent sources sharing ancestry, adversarial wrong associations, negative saturation recovery, catastrophic reinforcement, forgetting/decay, convergence, calibration vs a held-out event oracle. The toy 64-bit witness cannot replace full provenance handling. + +Run: `cargo test --manifest-path crates/causal-edge/Cargo.toml --test palette256_hebbian_overlay_probe`. diff --git a/.claude/board/entries/README.md b/.claude/board/entries/README.md index 4d888f2b2..0b6117fbc 100644 --- a/.claude/board/entries/README.md +++ b/.claude/board/entries/README.md @@ -25,12 +25,13 @@ 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. -434 entries, 2026-08-06 .. 2026-10-22. +436 entries, 2026-08-06 .. 2026-10-22. | date | entry id | finding | file | |---|---|---|---| | 2026-10-22 | `pal256-salience-mp3-probe` | | [2026-10-22-pal256-salience-mp3-probe.md](2026-10-22-pal256-salience-mp3-probe.md) | | 2026-10-22 | `pal256-immutable-needle-distribution-probe` | | [2026-10-22-pal256-immutable-needle-distribution-probe.md](2026-10-22-pal256-immutable-needle-distribution-probe.md) | +| 2026-10-22 | `pal256-hebbian-overlay-plasticity` | | [2026-10-22-pal256-hebbian-overlay-plasticity.md](2026-10-22-pal256-hebbian-overlay-plasticity.md) | | 2026-10-22 | `pal256-argextrema-margins` | | [2026-10-22-pal256-argextrema-margins.md](2026-10-22-pal256-argextrema-margins.md) | | 2026-10-22 | `pal256-3sigma-statistical-gate` | | [2026-10-22-pal256-3sigma-statistical-gate.md](2026-10-22-pal256-3sigma-statistical-gate.md) | | 2026-10-22 | `math-compiler-stage-b-e1` | | [2026-10-22-math-compiler-stage-b-e1.md](2026-10-22-math-compiler-stage-b-e1.md) | diff --git a/crates/causal-edge/tests/palette256_hebbian_overlay_probe.rs b/crates/causal-edge/tests/palette256_hebbian_overlay_probe.rs new file mode 100644 index 000000000..77b8045c8 --- /dev/null +++ b/crates/causal-edge/tests/palette256_hebbian_overlay_probe.rs @@ -0,0 +1,62 @@ +//! Local associative learning belongs to a versioned overlay, not to the +//! immutable Palette256 codebook or its family-calibrated pairwise LUT. +#[derive(Default)] +struct Overlay { + latent: f64, + seen_sources: u64, +} +impl Overlay { + fn apply( + &mut self, + source_id: u8, + pre: f64, + post: f64, + eta: f64, + ) -> Result { + if source_id >= 64 + || !pre.is_finite() + || !post.is_finite() + || !eta.is_finite() + || !(0.0..=1.0).contains(&eta) + { + return Err("invalid update contract"); + } + let source = 1_u64 << source_id; + if self.seen_sources & source != 0 { + return Ok(false); + } + // Hypothesis under test, NOT NARS revision or a proven Bayesian update. + let damping = 1.0 - self.latent.abs().tanh(); + self.latent = (self.latent + eta * pre * post * damping).clamp(-8.0, 8.0); + self.seen_sources |= source; + Ok(true) + } + fn bounded_readout(&self) -> f64 { + self.latent.tanh() + } +} +#[test] +fn repeated_eligible_updates_remain_bounded_and_do_not_move_reference() { + let frozen_reference: [u8; 4] = [13, 42, 99, 241]; + let reference_before = frozen_reference; + let mut synapse = Overlay::default(); + for source in 0..64_u8 { + assert!(synapse.apply(source, 0.8, 0.9, 0.15).unwrap()); + } + assert!(synapse.latent.is_finite()); + assert!(synapse.bounded_readout() > 0.0 && synapse.bounded_readout() < 1.0); + assert_eq!(frozen_reference, reference_before); +} +#[test] +fn a_duplicate_root_does_not_reinforce_the_synapse_again() { + let mut synapse = Overlay::default(); + assert!(synapse.apply(4, 0.8, 0.9, 0.1).unwrap()); + let first = synapse.latent; + assert!(!synapse.apply(4, 0.8, 0.9, 0.1).unwrap()); + assert_eq!(synapse.latent, first); + assert!(synapse.apply(5, -0.8, 0.9, 0.1).unwrap()); + assert!( + synapse.latent < first, + "anticorrelated activation must decrease the overlay" + ); +}