Monkey-level explainer: what each part does for Jax
- Span gating ("when") — a volume pedal with a timer. Today the lace is on for every token. This lets us say: monkey loud while he introduces himself, monkey quiet while he does arithmetic, monkey off while he reads the question. Their trick: cook each chunk of the chat separately so the earlier chunks keep the state they were cooked with. It is the "which tokens" half of the conductor.
- Gain (multiply, not add) — a dimmer instead of a flashlight. Our pushes add a fixed amount whether or not the neuron was firing. Gain multiplies what the neuron already does: 0 silences it only where it was active, 2 doubles it, negative flips it. That is the "hold a neuron at its usual level" and "mute the doubt neuron only when it fires" tool we did not have.
- Reverse lookup — ask the answers which neurons made them. Take Jax's answers and the plain answers, subtract, and score every neuron by how well its write-direction lines up with the difference. Astra ran it: our hand-picked L34 N6111 came out rank 1 of 1.1 million. So the method is trusted, and the next 200 are the extra bars of the song.
- Contrast directions — the same idea packaged as one steerable thing: instead of 16 neurons, one direction built from a pile of "this persona" vs "not this persona" examples. A direction can be a note in a lace just like a neuron.
- Directional ablation — surgery instead of steering: remove one direction from every place the model writes it, so the behaviour cannot happen. Permanent, not reversible at inference. Useful for the Qwen reflex later; not for persona work now.
- Side-effect eval — their version of our reasoning suite plus persona score, with better scorers: exact answers, format, language mixing, and a persona-novelty rule that only counts new words appearing in two or more prompts, so one lucky "Ming" does not count as a persona.
- Random-direction null — a fairness test for rankings. Push random directions alongside real neurons and only trust a neuron that beats the randoms. Our monkey-word counts have no such null yet.
- Map layouts — how they draw the brain: layers as slabs along depth, neurons on a sheet placed by similarity, overlays for which neurons fire on pasted text, wiring lines between neurons. Same shape as our Unity slabs; worth copying the placement math.
- MCP tools — a menu of verbs an agent can call: overview, neuron dossier, search, steer, fires, reverse, highlight in the UI. A template for John's verbs; their rule that the agent must beat the null before reporting a find is worth stealing too.
- Logit lens — read a neuron's output without running anything: its write-direction through the final layer tells you which words it promotes. The "pirate neuron" trick from the post you saw. Cheap pre-filter: before pushing a candidate, check it actually writes toward monkey words.
What we leave: anything that exports to llama.cpp file formats, their 8-token ramp (a workaround for their engine), and their "shippable" thresholds as pass/fail truth.
gguf-neuron-explorer (gne) — parts list for our stack (Haiku inventory + Terra porting plan, 2026-10-28)
Repo: github.com/balaena01/gguf-neuron-explorer, MIT (c) 2026 balaena01, ~15.4k lines Python + vendored three.js
(own license). Credits lyramakesmusic/neuron-explorer; from-scratch reimplementation. Nothing executed here.
Haiku = mechanism inventory (file:line). Terra = port order, effort, pitfalls (gne-terra-porting-plan.md).
Take, in this order
- Span-gated steering (
when) — gne/when.py:46 parse_when (all / gen / assistant / after:<text> / ramp:a-b), :60 chat_segments (token-prefix-stable span split, one-token boundary tolerance :100), :132 generate_when (prefill per span with pos0 so earlier KV keeps its state; vector only on assistant spans + the generation prompt;gen= unsteered prefill then vector from step 0;after= detokenize each step and switch on first match, O(n^2), one-step lag; ramp every 8 tokens). Port: copy the span logic + per-span prefill; replace set_cvec with our per-token gain hook; drop the 8-token ramp cadence (llama.cpp adapter-switch workaround) and the O(n^2) after. This is the conductor's "which tokens" half. 12-20 h. - Multiplicative gain — gne/gain.py:33: a_n = silu(gate)*up, scale W_up row n by k -> k*a_n*W_down[:,n]; k=0 removes, negative inverts; acts only where the neuron fires. For us: scale the down_proj input column n by k at the hook (no LoRA). This is "hold at usual level" / mute without additive dose. 8-16 h.
- Reverse lookup — gne/reverse.py:83 contrastive_directions (prefill sets A/B, mean(A)-mean(B) per layer on residual out and on ffn_down inputs), :159 cos_by_layer (divide residual dims by their std first = massive- activation guard), :183 combine_scores (percentile-rank average of |cos|, sign-aligned activation diff, steer KL), :197 top-200 across all layers. Math reusable as-is; capture adapts. Finds the Jax direction / Qwen chord without a sweep. 16-28 h.
- Contrast directions as units — gne/direction.py:44 load_contrast (YAML + validate regex), :138 select_layer (ablate mode: projection removed, behaviour drop, KL<=0.1, distinct>=0.8), :295 response_directions; steer.py:262 direction units (raw/rel). 16-28 h.
- Directional ablation — gne/ablate.py:48 residual_writers, :239 _project W -= s*outer(r, r@W); LoRA/bake writers are llama.cpp-specific. Take the projection math. 16-32 h.
- Side-effect eval harness — gne/evalx.py:263 evaluate (dose 0 baseline + doses, same
whenpath for all rows), :93 exact (answer words in first line), :112 format (json keys / line count / language), :139 mix (script contamination), :149 persona (novelty vs baseline + new words in >=2 prompts, distinct-4, length), ppl teacher-forced; thresholds capability_drop 5 pt / ppl 10 % / mix 0; optional 1-5 judge :238. Take the scorers as-is into lace_sweep; keep our claim/loop/doubt metrics beside them. 12-24 h. - Sweep statistics — gne/sweep.py:64 random-direction controls interleaved (seeded per layer, shared pool); gne/rank.py:42 word mass = sum of top-5 positive z over baseline-subtracted once-per-rollout word counts, z = (cnt-mean)/sqrt(mean+0.5); null_pct = fraction of control masses below the unit's. Pure numpy on rollouts: reuse as-is as a null for our monkey-density counts.
- Map + layouts — gne/layout.py:50 compute_layout (W_down columns -> PCA-50 -> UMAP cosine -> 2D, snap_to_grid :18; or lens-based sparse vectors), ui/static/layout.js GEOM (x = depth L*0.9, per-layer mixer slab + MLP sheet, neurons on the sheet by index or UMAP), overlays (sweep mass, steer KL, logit |z|, activations, fires, search), connections.py:87 top-k up/downstream by weight similarity through the FFN norm. Web: adapt (keep vendored three.js, honor its license). Unity VR: idea-only — the GEOM layout, wiring and overlays map 1:1 onto our slabs. 20-40 h.
- MCP tool surface — gne/mcp_server.py:58: model_overview, layer_summary, neuron, search, steer, steer_lens, fires, reverse, direction, recipe_save, eval_recipe, job_start/status/cancel, ui_highlight/ui_note/ui_open; its instructions make the agent check null_pct / kl_null_p95 before reporting. Template for John's verbs. 16-32 h.
- Logit lens / steer lens — gne/lens.py:105 (W_down column x out-norm x unembedding, z over vocab) and :199 (KL(base||steered) on fixed contexts vs random controls, kl_null_p95). Adapt; cheap first passes.
Leave
GGUF export, .cvec control vectors, bias baking, LoRA adapters (llama.cpp-specific); the 8-token ramp cadence; the
"shippable" thresholds as release criteria; a single regex/judge as ground truth (their own playbook: "distrust cheap
effects such as a changed self-name").
Formats worth copying
rollout JSONL (kind, neuron, layer, dose, rel_push, token_ids, logprob, finish) + units.jsonl + meta.json per run;
per-layer npz for lens/stats; recipes and contrast sets as YAML with when / sampling / ablate / units.
