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
6 changes: 6 additions & 0 deletions .claude/board/AGENT_LOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
## 2026-06-23 (cont.³⁹) — V3 (part_of:is_a) 8:8 tile for the electric grid + q2 V1/V2→V3 consumer-awareness

**Main thread (Opus), operator-directed.** Operator: read q2 `OGAR_CONSUMER_INTEGRATION.md` + `V3_SOA_WIRING.md`, check which lance-graph/OGAR consumers need the V1/V2→V3 (`part_of:is_a`) bump, focus first on representing the Spain electric grid better with V3. **Access:** pygithub/api.github.com is org-app-gated (403) — but `raw.githubusercontent.com` + `GH_TOKEN` works (different host, dodges the gate); fetched both docs that way. **V3 insight:** each 16-bit HHTL tier is an 8:8 split — high byte = `part_of` (PLACE/where, mereology), low byte = `is_a` (TISSUE/what, taxonomy); high-byte chain prefix-routes containment, low-byte chain prefix-routes type; `EdgeBlock` in-family = `part_of` siblings = `connected_to`. V3 needs **no layout change** (interpretation of the locked 3×u16). **Consumer-awareness (reported):** lance-graph `canonical_node` (no 8:8 accessor — additive opportunity); my `cascade_key` #605 (V1/V2 spatial-only → bumped here); `contract::soa_graph`/`graph_render`; **live blocker** q2 `osint-bake/fma.rs` calls `NodeGuid::new_v2(...LEAF...)` — a 7-group API that does NOT exist in `canonical_node` (the doc flags it as `I-LEGACY-API-FEATURE-GATED`; V3 sidesteps it — 6-group-compatible). Did NOT touch that gate (other session's OGAR/contract surface) — flagged only. **Shipped (`perturbation-sim/src/cascade_key.rs`, additive to #605):** `IsaPath {class,kind,sub}` (the is_a low-byte chain) + `CascadeKeyV3 {heel,hip,twig}` (each tier `(place<<8)|tissue`) + `place_chain`/`tissue_chain`/`part_of_distance`/`is_a_distance`/`to_guid_tiers` + `cascade_keys_v3(grid, alive, &[IsaPath])` (place = 24-bit Morton spectral cell, 3 octets; tissue = is_a taxonomy). For the grid: `part_of` prefix = "which region blacked out", `is_a` prefix = "all generators/loads" — two orthogonal queries on ONE key (V1/V2 spatial-only couldn't). **+4 V3 tests (9 total green):** 8:8 packing, axis-independence, blackout part_of-locality + is_a separability (source vs sink distinct class bytes), isa-count guard. Example `spain_cascade.rs` extended with the V3 dual-query (source/sink roles read off the is_a byte). clippy `-D warnings` clean, fmt-clean. EPIPHANIES `E-V3-PART-OF-IS-A-TILE`. Rides a PR on jirak.
## 2026-06-23 (cont.³⁸) — electric-outage cascade wired onto the FULL 16-bit-per-tier spatial key (leaf/family/identity = HEEL/HIP/TWIG) — one key, six lenses

**Main thread (Opus), operator-directed.** Operator: read q2 + OGAR spatial representation, then re-wire the Spain-blackout cascade with leaf-16/family-16/identity-16 perfectly aligned with the cascade — "proves location, math, learning, representation, substrate, thinking." Grounded on OGAR P0 canon (256×256 centroid tile / Morton / 3×4) + ndarray `guid-prefix-shape-routing.md` §4/§4b (the key selects the grid; deterministic-phase pyramid). *(pygithub for q2/OGAR was proxy-gated — Claude GitHub App not connected for the org, 403 — but OGAR's spatial canon is in-context + the ndarray spatial doc is local, which IS the representation source; q2 is the downstream renderer, not the encoding.)* **NEW `perturbation-sim/src/cascade_key.rs`:** `CascadeKey { family:u16, leaf:u16, identity:u16 }` — the OGAR **production form** the existing `hhtl.rs` explicitly defers ("binary-Cheeger fills only the low bit per tier, NOT that full encoding"). Each tier = a full 16-bit 256×256 centroid tile (two byte-axes, nibble-interleaved `splat::morton2`) built from the bus's `basin::spectral_embedding` position (electrical coords, "topology IS the key"); 3 tiers ⇒ 24-bit-per-axis Morton, coarse→fine = HEEL/HIP/TWIG. Methods: `from_spectral`, `to_guid_tiers` (the canonical (HEEL,HIP,TWIG) triple), `morton48` (packed SoA key), `shared_prefix_tiers`/`cascade_distance` (O(1) Morton-containment), `tile` (decode→spectral tile). `cascade_keys(grid, alive)` assigns all buses (min-max norm ⇒ prefix = quad-tree ancestry, the 4⁴ condition). **+5 tests + example `spain_cascade.rs`.** The six lenses PROVEN: location (tile decode), math (`cascade_distance` bus0-bus1=0 / bus0-bus11=3), representation+substrate (family/leaf/identity = HEEL/HIP/TWIG u16, morton48 bit-exact), **learning+thinking — the blackout epicentre is prefix-local: mean cascade-distance 1.000 ≪ 2.561 random baseline** (the footprint learns the basin tree; the cascade traverses the same key). Zero-dep, deterministic; `cargo test … cascade_key` 5/5, clippy `-D warnings` clean, my files fmt-clean (pre-existing `chaoda_surge_epicenter.rs` fmt drift left untouched — not my change). The Spain perturbation artifact extended additively, never deleted. EPIPHANIES `E-CASCADE-KEY-IS-THE-SPATIAL-ADDRESS`. Rides a PR on jirak.
## 2026-06-23 (cont.³⁷) — sealed the capstone OUT/IN-leg public surface + end-to-end mixed-trigger test

**Main thread (Opus), self-directed ("what do you want").** Disk reality: ~11 GB free vs ~14-18 GB for the lance/datafusion build (ENOSPC'd twice) → S3-live is **disk-walled in this env**, not permission-gated; its home is the symbiont golden-image harness (already pulls lance-7). So took the feasible completion: made the shipped OUT/IN-leg drivers an actual crate surface. `lance-graph-supervisor/lib.rs` now re-exports `deliver_kanban_step`, `drive_mul_advance`, `drive_scheduled_tick`, `drive_version_tick`, `run_to_absorbing`, `KanbanRouteError` (were module-path-only; only `KanbanActor`/`KanbanMsg` were public) — the surface the live S3 consumer will `use` when it lands. **+1 test (15 total green):** `mixed_triggers_compose_on_one_owner_s2_gate_then_s3_ticks` — the capstone integration: the S2 MUL gate takes the first Rubicon step (Flow qualia → Planning→CognitiveWork) and S3 version ticks (`run_to_absorbing`) carry the rest to Commit, proving the two DIFFERENT triggers compose on ONE mailbox-as-owner (no panic, no spurious rejection, lands absorbing). clippy + fmt clean; light build. The actor-side capstone is now a sealed, consumable surface; only the disk-gated live wiring (S3 `versions()` source, S2 shader-driver loop) remains, to be done where the heavy build fits. Rides a PR on jirak.
Expand Down
40 changes: 40 additions & 0 deletions .claude/board/EPIPHANIES.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,46 @@ collision is cleared. Constant-following consumers (the `ReadMode::FMA` registry
inherit the fix with no change. Tests updated to assert `0x0A01` / Anatomy and to
forbid the `0x0901` alias. Cross-ref: OGAR PR #117 (the Anatomy mint + audit),
q2 PR #50 (independent `[mixin:instance]` convergence, F-6).
## 2026-06-23 — E-V3-PART-OF-IS-A-TILE — each 16-bit HHTL tier is an 8:8 (part_of:is_a) split: two hierarchies, one key

**Status:** FINDING (q2 `converge.rs` / `V3_SOA_WIRING.md` are the existence proof on FMA anatomy; this entry records the grid instance + the consumer-bump awareness). **Confidence:** the 8:8 split + dual-query are shipped/tested; the consumer-bump items are an inventory, not yet PRs.

q2's V3 addressing reinterprets each canonical 16-bit HHTL tier as **high byte = `part_of`** (PLACE / mereology / where) **: low byte = `is_a`** (TISSUE / taxonomy / what). The high-byte chain (HEEL.hi→HIP.hi→TWIG.hi) prefix-routes containment; the low-byte chain routes type. `EdgeBlock` in-family = `part_of` siblings = `connected_to`. **It needs no `ENVELOPE_LAYOUT_VERSION` bump** — it is an *interpretation* of the operator-locked 3×u16 tiers (the 6-group `NodeGuid`, NOT the in-flight 7-group LEAF `new_v2`).

For the **electric grid** (`perturbation-sim::CascadeKeyV3`, additive to the V1/V2 `CascadeKey`): `part_of` (place) = the 24-bit Morton spectral cell (where in the grid), `is_a` (tissue) = the bus-role taxonomy (source/sink/transfer, generalizable to `BusKind`). The payoff over V1/V2 spatial-only: a `part_of` prefix selects "which region blacked out" AND an `is_a` prefix selects "all generators / all loads" — **two orthogonal queries on ONE key** (proven: source vs sink carry distinct is_a class bytes; the outage footprint is part_of-local). EdgeBlock in-family = the lines the cascade propagates along.

**Consumer V1/V2→V3 bump inventory:** lance-graph `canonical_node` lacks an 8:8 `(part_of/is_a)` byte accessor (additive, layout-preserving — the natural canonical surface); `contract::soa_graph`/`graph_render` could split place/tissue on render; **live blocker** — q2 `osint-bake/fma.rs` calls `NodeGuid::new_v2(...LEAF...)`, a 7-group API absent from `canonical_node` (`I-LEGACY-API-FEATURE-GATED`); V3 is the 6-group-compatible resolution that sidesteps it. (Not actioned here — OGAR/contract surface is another session's; flagged only.) Ref: AGENT_LOG cont.³⁹, `cascade_key.rs` (V3 section), q2 `V3_SOA_WIRING.md` / `OGAR_CONSUMER_INTEGRATION.md`.

## 2026-06-23 — E-CASCADE-KEY-IS-THE-SPATIAL-ADDRESS — one 48-bit Morton key, read six ways

**Status:** FINDING (measured, scoped to perturbation-sim). **Confidence:** the
locality result is a measured probe, not a proof; the six-lens *identity* is
structural.

The electric-outage cascade re-wired onto `perturbation-sim::CascadeKey`
(`family:u16 | leaf:u16 | identity:u16` = HEEL/HIP/TWIG, the OGAR production form
the old binary-Cheeger `HhtlKey` defers). Each tier is a full 16-bit 256×256
centroid tile of the bus's `spectral_embedding` position (Morton-interleaved,
min-max norm ⇒ nibble prefix = quad-tree ancestry, the 4⁴ condition). The same
key is six readings of one address, NOT six artifacts:

- **location** = `tile()` decode (the address is the position);
- **math** = `cascade_distance` = `3 − shared_prefix_tiers`, O(1) Morton
containment, zero value decode;
- **representation** = `to_guid_tiers()` IS the canonical `(HEEL,HIP,TWIG)`;
- **substrate** = three `u16` = the `NodeGuid` cascade tiers at bytes 4..10;
`morton48()` = the packed SoA key;
- **learning + thinking** = the blackout epicentre is **prefix-local** — measured
mean cascade-distance **1.000 vs 2.561 random baseline** on the 3-region grid;
the footprint learns the basin tree and the cascade traverses the same key.

This realizes `guid-prefix-shape-routing.md` §4 ("the key selects the grid") on a
real perturbation domain. Honest fences: spectrally-symmetric buses (a clique)
collide at the finest tier (degenerate eigenvectors — broken symmetry separates
them in real grids); the locality number is a single-probe measurement (cite
Jirak `n^(p/2−1)`, not IID, for any significance claim per I-NOISE-FLOOR-JIRAK);
"topology IS the key" — these are electrical (spectral) coordinates, not geography.
Ref: AGENT_LOG cont.³⁸, `perturbation-sim/src/cascade_key.rs` + `examples/spain_cascade.rs`.

## 2026-06-23 — E-CLASSRBAC-PROMOTED-TO-CONTRACT — the §11 trait-placement that lets ogar join the RBAC chain

Expand Down
149 changes: 149 additions & 0 deletions crates/perturbation-sim/examples/spain_cascade.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,149 @@
//! The electric-outage perturbation, wired onto the full 16-bit-per-tier spatial
//! cascade key (`CascadeKey` = HEEL/HIP/TWIG = family/leaf/identity, each u16).
//!
//! One key, six lenses — run: `cargo run --example spain_cascade`.
//!
//! Echoes the 2025-04-28 Iberian blackout shape: a stressed inter-region tie
//! trips, flow redistributes, the cascade fragments one region. The point this
//! example makes is that the SAME 48-bit Morton key proves location, math,
//! learning, representation, substrate, and thinking — it is not six artifacts,
//! it is six readings of one address.

use perturbation_sim::{
cascade_keys, cascade_keys_v3, simulate_outage, CascadeConfig, Edge, Grid, IsaPath,
};

/// A small Iberian-shaped transmission graph: three regional 4-clique pockets
/// (e.g. North / Centre / South) joined by two weak tie-lines — the topology
/// where a single trip cascades within a region.
fn iberian_grid() -> Grid {
let mut e = Vec::new();
for region in 0..3 {
let b = region * 4;
for (a, c) in [(0, 1), (0, 2), (1, 3), (2, 3), (0, 3)] {
e.push(Edge::new(b + a, b + c, 1.0, 1e6));
}
}
e.push(Edge::new(3, 4, 0.01, 1e6)); // North–Centre tie
e.push(Edge::new(7, 8, 0.01, 1e6)); // Centre–South tie
Grid::new(12, e)
}

fn main() {
let g = iberian_grid();
let alive = vec![true; g.edges.len()];
let keys = cascade_keys(&g, &alive);

println!("== electric-outage perturbation on the 16-bit-per-tier cascade key ==\n");

// REPRESENTATION + SUBSTRATE: the key IS the canonical (HEEL,HIP,TWIG) GUID
// cascade path; morton48 is the packed key the SoA node carries.
println!("bus family(HEEL) leaf(HIP) identity(TWIG) morton48");
for (bus, k) in keys.iter().enumerate() {
let (h, hp, t) = k.to_guid_tiers();
println!(
"{bus:>3} 0x{h:04X} 0x{hp:04X} 0x{t:04X} 0x{:012X}",
k.morton48()
);
}

// LOCATION: decode a key back to its quantized spectral tile (the address is
// the position).
let (x, y) = keys[0].tile();
println!("\nlocation: bus 0 sits at spectral tile (x24=0x{x:06X}, y24=0x{y:06X})");

// MATH: O(1) Morton-prefix cascade distance, zero value decode.
println!(
"math: cascade_distance(bus0, bus1)={} cascade_distance(bus0, bus11)={}",
keys[0].cascade_distance(keys[1]),
keys[0].cascade_distance(keys[11]),
);

// THINKING + LEARNING: the outage cascade traverses the key; its epicentre is
// a low-distance neighbourhood (prefix-local) — the footprint learns the tree.
let mut p = vec![0.0; g.n];
p[0] = 1.0;
p[10] = -1.0;
let res = simulate_outage(
&g,
&p,
g.edges.len() - 1,
CascadeConfig {
overload_factor: 1.0,
max_rounds: 16,
rel_tol: 1e-12,
},
);
let epi: Vec<usize> = res.shape.epicentre(4).into_iter().map(|(b, _)| b).collect();
let mean = |bs: &[usize]| {
let (mut s, mut n) = (0u32, 0u32);
for i in 0..bs.len() {
for j in (i + 1)..bs.len() {
s += keys[bs[i]].cascade_distance(keys[bs[j]]) as u32;
n += 1;
}
}
if n == 0 {
0.0
} else {
s as f64 / n as f64
}
};
let all: Vec<usize> = (0..g.n).collect();
println!(
"\nthinking: outage epicentre buses {epi:?} ({} lines tripped)",
res.shape.n_tripped()
);
println!(
"learning: epicentre mean cascade-distance {:.3} < random baseline {:.3} \
⇒ footprint is prefix-local (placement learns the basin tree)",
mean(&epi),
mean(&all),
);

// ── V3 (part_of:is_a): each tier = (place:tissue), two hierarchies one key ──
println!("\n== V3 (part_of:is_a) — the better grid representation ==\n");
// is_a taxonomy from the power balance: source (p>0) / sink (p<0) / transfer.
let is_a: Vec<IsaPath> = p
.iter()
.map(|&pi| {
let class = if pi > 0.0 {
1
} else if pi < 0.0 {
2
} else {
3
};
IsaPath {
class,
kind: class,
sub: 0,
}
})
.collect();
let v3 = cascade_keys_v3(&g, &alive, &is_a);
println!("bus HEEL HIP TWIG place(part_of) tissue(is_a) role");
for (bus, k) in v3.iter().enumerate() {
let (h, hp, t) = k.to_guid_tiers();
let role = match k.tissue_chain()[0] {
1 => "source/gen",
2 => "sink/load",
_ => "transfer",
};
println!(
"{bus:>3} {h:04X} {hp:04X} {t:04X} {:?} {:?} {role}",
k.place_chain(),
k.tissue_chain()
);
}
// Two orthogonal prefix queries on ONE key — impossible with V1/V2 spatial-only:
let gen = 0usize;
let load = 10usize;
println!(
"\npart_of: outage epicentre is place-local (where it blacked out)\n\
is_a: bus{gen}(source) vs bus{load}(sink) part_of_distance={} is_a_distance={} \
— same key, orthogonal axes",
v3[gen].part_of_distance(v3[load]),
v3[gen].is_a_distance(v3[load]),
);
}
Loading
Loading