← all documents · raw markdown · 15 KB

CORRECTIONS — appended 2026-08-31 after decoding the live synapse file

I made claims in this document from reading source alone. I then decoded

[internal module] directly (label table + 20-byte records, per

persistence/compact.rs:322). Three of my claims were wrong.

WRONG #1 — "restoring the recovered edges would mostly be refused by the fan-out cap"

[internal module] pushes straight into layer.synapses and

re-serialises the file. **It never calls apply_ltp, so the fan-out cap does not

apply to it at all.** The cap is a runtime guard on edge *creation*, not a

property of the file. Restoring works. I said the opposite.

WRONG #2 — "the graph is at its ceiling, nothing new can be created"

Measured out-degree, 49,731 labels:

| | |
|---|---|
| labels with outgoing edges | 48,341 |
| at or over the cap (15) | 25,065 (51.9%) |
| under the cap — still have room | 24,666 |
| total free edge slots | 189,542 |
| out-degree p50 / p75 / p90 / max | 15 / 16 / 19 / 293 |

Half the graph is frozen. The other half is not. And 189,542 free slots is

three times the 63,815 recovered edges — there is room for all of them even

through the runtime path. "At the ceiling" was true of the average and false of

the distribution.

(The max of 293 is a fossil: those edges were made when the lattice was ~359k

cells and the cap was 30. The cap never removes what it already allowed.)

WRONG #3 — the count

I reported 605,674 synapses from the boot log. The file on disk holds

613,276 across 49,731 labels. It has grown ~7,600 since that boot.

---

MEASURED STATE OF THE GRAPH (2026-08-31, decoded from disk)

Weight — min 0.0105, p25 0.0792, p50 0.3573, p75 1.0000, p99 5.0000

a quarter of the graph is roughly 90 minutes of live sweeps from deletion.

Clock / idlelast_fire_tick min 1815, p25 2970, p50 3529, max 4650.

on 2026-08-29. They are gone — the selective rebase worked. Every stamp on

the graph now carries real information.

past it**, 230 of those under weight 0.05. That is the live exposure if

[config flag] goes to 1 right now — small, and survivable.

fire_count — p50 13, p90 175, p99 2158, max 196,865.

23% of the graph on the rich-get-richer track described in section 8.

---

RECOVERY WORK DONE

[internal module] (63,815 pairs, already re-screened through the

tightened v9.10.838 gate) still carried assistant filler. Measured: **2,206 pairs

(3.5%)** had chat/tutor scaffolding on at least one side — you're welcome,

I'd be happy to, as an AI, here's a tiny lesson, let's, Q:/A: drills.

120 pairs had it on both sides.

That is a small fraction — the 838 gate did most of its job, and I should not

imply otherwise. But those 2,206 are exactly the class that produced the

"0 bridges" seeds in the log you asked about.

Written:

Nothing in data/ that the engine reads has been modified.

---

SYNAPTOGENESIS — FULL TRACE AUDIT

Recorded 2026-08-31. Every function in the loop, read in source, with what it does to what.

Live numbers used throughout, from the boot log: 40,093 cells, 605,674 synapses.

---

THE CHAIN

run_synaptogenesis_loop            oracle_server.rs [internal service]   every 2s, forever
  └─ get_ungrounded_concepts       oracle_server.rs [internal service]   pick 1 seed
       └─ is_junk_concept_label    oracle_server.rs [internal service]   reject noise
       └─ SynapticLayer::strongest_from  synapse.rs        "has this cell any synapses?"
  └─ NeuralBus::query_multi_hop    synapse.rs:973          walk the graph 3 hops
       ├─ Universe::query_in_regions                       hop 0, geometric
       ├─ SynapticLayer::propagate  synapse.rs:339         hops 1..3, follow edges
       ├─ Universe::get_cell_by_label                      LINEAR SCAN, 40k cells
       └─ effective_score           synapse.rs             rank
  └─ SynapticLayer::record_co_firing  synapse.rs:279       Hebbian LTP
       └─ apply_ltp                 synapse.rs:799         the only thing that WRITES

---

1. run_synaptogenesis_loop — oracle_server.rs [internal service]

Does: sleeps 2s, builds one seed, queries, writes, prints, repeats. Forever.

Kill switch: TRAINING_DISABLED.flag file, or [config flag]=0. Checked

each pass; when set it sleeps 2s and does nothing else.

The throttle is dead code.

let max_boost: f32 = 0.0;   // "Disabled boost to prevent parallel crashes"
let p_scaled  = ...;        // biological plateau curve
let ease      = p_scaled * p_scaled * (3.0 - 2.0 * p_scaled);   // smoothstep
let throttle  = 1.0 + max_boost * ease;                          // == 1.0, always
let batch_size = (throttle.max(1.0).round() as usize).min(15);   // == 1, always

An entire smoothstep curve is computed and then multiplied by zero. batch_size is

permanently 1.

→ THIS IS WHY IT ALWAYS SAYS seed[0] AND Processing 1 parallel concepts.

There is only ever one seed. The plural is a leftover.

P is dead too. p = synapses / (cells * 4) clamped to 1.0.

605,674 / (40,093 x 4) = 605,674 / 160,372 = 3.78 → clamped to 1.0000.

It has been pinned at maximum for a long time and cannot move. It reports nothing.

(And even if it moved: p > 0.80 sends p_scaled to (1-p)/0.20, so at P=1.0 the

curve evaluates to 0 anyway.)

Locking: seeds are processed in chunks(2) with a sleep(250ms) between chunks,

specifically so [internal endpoint] can get the universe read lock. With batch_size 1 there

is only ever one chunk — so every cycle still pays the 250ms sleep for a

concurrency problem it no longer has.

---

2. get_ungrounded_concepts — oracle_server.rs [internal service]

Does: builds a Vec<usize> of all 40,093 indices, shuffles it, then walks it

until it finds batch_size (=1) cells that pass the junk filter AND have zero synapses.

Cost: a 40,093-element allocation + full Fisher-Yates shuffle, **30 times a

minute**, to select one item. If most cells already have synapses, it also calls

strongest_from on each candidate as it walks — and strongest_from copies every

synapse of that label into a Vec and sorts it, just to ask "is this empty?"

Fallback: if this returns nothing, the loop picks a random cell instead. So the

system moves from "ground the unconnected" to "re-stir the connected" without saying so

in the log.

---

3. is_junk_concept_label — oracle_server.rs [internal service]

Does: rejects a label if it is <8 chars, fails ingest_filter::judge_ingest, ends

with , or (, starts with (, contains chat pollution markers (language sample,

[mirror], leo:, pred_err=, ...), starts with an agent name, or contains

": " in the first 40 characters.

The hole your seeds came through: the colon rule matches ": " — colon **plus a

space**. Your seed was:

> Here's a tiny lesson for your AI studen[local path]"so

The colon is followed by a newline, not a space. It passes. Same for

The correct answer is based on understanding how prefixes mo — that has no early

colon at all. Both are LLM answer text stored as cells, which is exactly what this

filter exists to keep out, and exactly what is being fed in.

---

4. NeuralBus::query_multi_hop — synapse.rs:973

Called as query_multi_hop(&u, &sl, phi_g, seed, 15, &[], "", 3)n=15, max_hops=3.

Does:

of every frontier label, sorted by boost. Capped at [config flag] (default 64)

per hop; next frontier truncated to [config flag] (default 24). Signal decays

0.7^hop; anything under 0.05 after decay is dropped.

scan over all 40,093 cells** (universe.rs:2918). This is the documented cause of the

141-second [internal endpoint] (v9.10.845 note in-file).

merged.truncate(n + max_hops * 2);   // 15 + 3*2 = 21

→ THE RESULT IS HARD-CAPPED AT 21 HITS.

---

5. record_co_firing — synapse.rs:279

Does: if labels.len() < 2 { return; }, then for every ordered pair (i,j), i!=j,

calls apply_ltp.

chi_gate = (1.0 - chi*0.8).max(0.05)                        // chi=0.1 → 0.92
ltp_gain = BASE_LTP * (1 + dopamine*0.8) * (1 + phi_g*0.5) * chi_gate
         = 0.035    * (1 + 0.8*0.8)      * (1 + phi_g*0.5) * 0.92
         ≈ 0.0528 * (1 + phi_g*0.5)      ≈ 0.053 – 0.079

The tick is 0. This loop passes tick = 0. Since v9.10.565 the line is

self.tick = self.tick.max(tick), so passing 0 can no longer rewind the clock — but it

also does not advance it. Only ltd_sweep_inner does self.tick += 1.

---

6. apply_ltp — synapse.rs:799 — THE ONLY FUNCTION THAT WRITES

Two paths:

A. Edge already existsweight = (weight + gain).min(max_w), fire_count += 1,

last_fire_tick = tick, total_ltp += 1. max_w is MAX_WEIGHT = 1.0, or 5.0

once fire_count > 50 ("hyper-synapse").

B. Edge does not exist → checks the fan-out limit first:

if fan_out >= dynamic_fan_out(lattice_size) { return; }   // silent, no-op

pub fn dynamic_fan_out(n) -> usize { (0.075*n).powf(1/3).ceil().max(8) }

For your lattice: (0.075 * 40,093)^(1/3) = 3007^(1/3) = 14.44 → ceil → 15.

THE CEILING — the single most important number in this audit

Max possible synapses = 40,093 cells x 15 fan-out = 601,395.

You have 605,674. (Above the ceiling because the cap is recomputed as the lattice

grows; edges made when it was 16 stay.)

Average out-degree = 605,674 / 40,093 = 15.1. The graph is **at its fan-out

ceiling. For most labels, path B returns immediately and creates nothing**.

New-edge path also has a latent_traces hash check that multiplies gain by 15x

for a pre/post pair that existed before and was pruned — a relearning bonus.

---

7. What the log lines actually mean

Logistic Throttle Velocity: 1.00x (P=1.0000) | Processing 1 parallel concepts...

Two dead constants and a hard-coded 1. Zero information.

seed[0]: "…" cut off mid-word

s.chars().take(60) — 60-char truncation, no ellipsis. The full label is sent to

the query (a v9.10.x note says truncating to 60 broke resonance and produced 0 hits).

The blank line you see is a literal \n inside the cell label, printed raw.

Established 420 new geometric bridges

let n = hits.len();
let pair_bridges = n * (n - 1);
total_wired += pair_bridges;

hits is capped at 21 by merged.truncate(21). 21 x 20 = 420.

420 is a constant. It means "the retrieval returned its maximum." It will print 420

every single time the seed is not isolated. It is not a count of anything created.

Worse, on three counts:

1. Nothing is measured. record_co_firing is called *once* and returns ().

total_wired is a formula computed *before* any write, never compared to reality.

2. "new" is unverified. Most of those 420 pairs already exist; they got a weight

bump, not a creation. apply_ltp knows which path it took and tells nobody.

3. Many cannot be created at all — fan-out is full at 15 (section 6), so path B

silently returns.

(0 seed(s) with no multi-hop neighbors skipped)

Count of seeds whose hit list had len() <= 1. Zero means the one seed found neighbors.

Established 0 new geometric bridges (1 seed(s) had <2 multi-hop hits — seed may be isolated or too noisy)

The one seed came back with 0 or 1 hit. record_co_firing bailed at

if labels.len() < 2 { return; }. Nothing ran. Given the seeds are ungrounded cells —

cells with no synapses by definition — hops 1-3 have nothing to follow, so the

result depends entirely on hop 0 geometric retrieval finding something.

---

8. WHAT IT IS DOING TO THE SYSTEM

Every 2 seconds, permanently:

The direction is one-way. ltd_mode() defaults to "dry" ([config flag]

unset). Dry analyses and mutates nothing. So nothing is weakened and nothing is

pruned unless you explicitly set [config flag]=1. Weights only go up.

Saturation. At ltp_gain ≈ 0.053-0.079 against MAX_WEIGHT = 1.0, a synapse

saturates after ~13-19 co-firings. At 30 cycles/minute against a fixed retrieval

set, everything the retrieval keeps returning is already pinned at 1.0. Past that,

fire_count > 50 lifts the ceiling to 5.0 for the ones that fire most — so the

already-strongest edges are the only ones that can still grow. **That is a rich-get-

richer ratchet with no counterweight.**

No new information enters. The set being strengthened is the set the retrieval

already returns. This reinforces what the graph already believes. It is not learning

from the world; it is deepening a groove.

---

9. WHAT TO CHANGE (ranked, cheapest first)

1. Make the counter measure instead of compute. Have apply_ltp return an enum

(Created / Strengthened / RefusedFanOutFull / Saturated) and print the four

counts. One afternoon. Turns the log from decoration into an instrument. **Do this

before changing behaviour** — otherwise there is no way to tell if a change helped.

2. Log mean weight delta per batch. If it is ~0, the batch did nothing and you can

see it.

3. Drop the 250ms sleep when seeds.len() <= 2. It is paying for a chunking

strategy that no longer chunks.

4. Cache the ungrounded set instead of shuffling 40,093 indices 30x/minute.

Recompute on a slow tick.

5. Fix the junk filter's colon rule — match : followed by whitespace, not ": ".

Both of the seeds in the reported log would have been rejected.

6. Decide on LTD. With [config flag] unset there is no forgetting at all. Run

dry first and read the report before going live.

7. Then decide about max_boost. Raising it above 0 is the only way batch_size

ever exceeds 1 — but the comment says parallel crashed. That is worth reproducing

before re-enabling.

Nothing here says the mechanism is wrong. It says the log is not reporting it, the

graph is at its structural ceiling, and there is no counter-force. Those are three

separate problems and only the first one is cheap.