Research log Small Model Experimentation
GitHub

Qwen3.5-4B Activation Steering: is the latent first-op causally usable?

Reading the model's mind can't steer it

The one idea you need

Picture the model keeping working notes on an internal notepad. A simple reader can read one note almost perfectly — which operation it plans to do first. Steering writes that same note back in, bolder. But reading a note and getting the model to obey it are separate channels.

The question

If a simple reader can see which step the model plans to do first, can we push that same signal back in to make it actually do it?

What we found

No. A simple reader picks the model's planned first operation out of its internal state almost perfectly — 99% of the time — yet pushing that exact signal back in during generation barely moves what the model does: at best 3 to 5 points of improvement, no better than pushing a meaningless random direction, and far under the 10-point bar set in advance. Even handing it the known-correct answer failed.

Why it matters

If a probe shows your small model internally "knows" something, don't assume you can elicit it by adding that direction back to its activations at inference — that failed even with the true answer handed in. Real capability needs weight training or a tool.

A reader sees the answer vs the model saying it99% vs 33%how often a simple reader finds the planned first step vs how often the model names it, on the clean setting
Best gain from steering toward the true answer+3 to +5 pointslargest naming improvement, against a 10-point bar set in advance to count as a real effect
True-signal steering vs a random push36% vs 35%naming accuracy on the clean setting — indistinguishable
Directly asking the model to identify it, steering on vs off3% vs 3%steering changed nothing
On this page
  1. Results at a glance
  2. Overview
  3. Report
    1. Summary
    2. Research Program Fit
    3. Method
    4. Results
    5. Pre-registered verdicts
    6. Interpretation
    7. Honesty notes / limits
    8. Next Experiments
    9. Artifact Manifest
  4. Experiment log
  5. Figures
  6. Data files
  7. Reproduce
  8. Related

Results at a glance 2

Steering the readable first-step signal does not change what the model says

How to read

Two clusters of bars — a clean setting where the reader finds the step 99% of the time, and a harder one (42%). Each has four bars: no steering, steer toward the true first step, toward a wrong one, toward random. Taller naming accuracy is better.

0%10%20%30%40%depth 1 (probe 0.99)depth 1 (probe 0.99)depth 2 (probe 0.42)depth 2 (probe 0.42)

Takeaway → Within each cluster all four bars sit at nearly the same height; steering toward the true signal never beats no-steering or the random control — the injection does essentially nothing.

Data table
conditionbaseline (no steer)steer → TRUE first-opsteer → wrong opsteer → random
depth 1 (probe 0.99)32.7%36%32%35.3%
depth 2 (probe 0.42)12%17.3%11.3%14.7%

Numbers from

Technical framing

Steering the decodable first-op direction does NOT move behavior (best coef) — C19 showed the first op is linearly DECODABLE (depth-1 probe 0.99). Adding that same mean-difference direction back to the residual stream during generation does NOT make the model use it: at depth 1 steering toward the true op (0.36) is no better than baseline (0.33) or random (0.35); at depth 2 a faint whiff (0.17 vs 0.12) is within noise of the random control and below the pre-registered +0.10 bar. Decodability != steerability -- the latent signal is readable but not writable into behavior. Only weight edits (banking C18) and tools (C12) move the wall.

Turning up the steering strength never separates the true signal

How to read

On the harder setting, three lines track naming accuracy as steering strength rises left to right: pushing toward the true step, a wrong one, or a random direction. The no-steer baseline sits at 12%. Higher, and pulling above the others, is better.

5%10%15%20%5101520steer → TRUEsteer → randomsteer → wrong

Takeaway → All three lines overlap in the same low band, then sag together past mid-strength as fluency breaks down — no strength setting makes the true direction win.

Data table
steering coefficientsteer → TRUEsteer → wrongsteer → random
216%12.7%12%
416.7%11.3%13.3%
616.7%12.7%14.7%
817.3%11.3%14.7%
1214.7%7.3%12.7%
2013.3%7.3%8.7%

Numbers from

Technical framing

Depth-2 naming vs steering coefficient (all conditions flat/overlapping) — steer_true sits marginally above steer_random and steer_wrong at low coef (a directional whiff) but the gap is within noise; beyond coef ~12 fluency degrades and all conditions fall. No usable steering effect. Baseline (no steer) = 0.12.

In the author’s words from the Overview · “Results”

INERT — decodability ≠ steerability. Depth 1 (probe 0.99): steer_true never beats baseline (max +0.03), degrades at high coef. Depth 2: faint whiff (+0.05, within noise of random, below the +0.10 bar). Null at earlier layers (8, 12) and on identification (0.03→0.03). All pre-registered predictions refuted. See reports/report.md, analysis/steering.png, runs/steer_results.json.

Overview

Research Program

  • Program: structured_execution_and_compilers
  • Program question: C19 found the first op is linearly decodable. Is it causally usable — can we steer it out of the residual stream (training-free) and make the model use it?
  • Prior anchors: C19 (latent representation), C17 (selection is free), C18 (banking installs capability).

Question

Add the decoded "correct first-op" direction back to the residual stream during generation (ActAdd). Does the model then name / use it?

Hypothesis

Pre-registered (reports/prereg.md): steer_true ≥ baseline + 0.10 (usable); steer_wrong < baseline and steer_random ≈ baseline (specific); depth-1 sanity ≥ +0.15; identification lift (the prize).

Setup

  • Model: Qwen3.5-4B (only permitted model). Inference only + a forward hook.
  • Directions: mean-difference d_c = mean(acts[first_op==c, L]) − mean(acts[all, L]) from C19's cached activations, at the C19 probe-best layer (depth-1 L15, depth-2 L22).
  • Steering: forward hook on model.model.layers[L−1] adds coef · d_c to the residual at all positions.
  • Tasks: fresh verified list, depth 2 (primary) + depth 1 (sanity), n=150, disjoint from C19.
  • Readout: forced-answer first-op naming (fast, baseline parse ≈ 1.0). Conditions {baseline, steer_true, steer_wrong, steer_random} × coef {2,4,6,8,12,20}. Secondary: identification pass@1.

Run

Smoke: python scripts/steer.py --n 30 --coefs 0 6 12 --depths 1 Full: python scripts/steer.py --n 150 --coefs 0 2 4 6 8 12 20 --depths 2 1 && python scripts/analyze.py

Results

INERT — decodability ≠ steerability. Depth 1 (probe 0.99): steer_true never beats baseline (max +0.03), degrades at high coef. Depth 2: faint whiff (+0.05, within noise of random, below the +0.10 bar). Null at earlier layers (8, 12) and on identification (0.03→0.03). All pre-registered predictions refuted. See reports/report.md, analysis/steering.png, runs/steer_results.json.

Interpretation

The latent signal C19 found is readable but not writable into behavior via standard ActAdd. Strengthens the throughline: test-time interventions (selection C17, steering C20) don't move the wall; only weight edits (banking C18) and tools (C12) do. Limit: a clean negative for mean-difference steering; patching / optimized vectors untested.

Knowledgebase Update

  • Program evidence updated: research_programs/structured_execution_and_compilers/evidence.md (C20)
  • Claim ledger updated: C20 added

Artifacts

  • scripts/steer.py (ActAdd hook + readouts), scripts/analyze.py
  • runs/steer_results.json (naming sweep, ident arm, earlier-layer supplementary), analysis/steering.png
  • Directions built from C19's scratchpad/probe_artifacts/acts.npy (external) + the probe experiment's labels.json

Report

Rendered from reports/report.md

Summary

C19 found the composition's first operation is linearly DECODABLE from the residual stream far above the model's behavioral access (depth-1 probe 0.99 vs naming 0.44), but flagged that "decodable ≠ usable for generation." This is the decisive follow-up: add the decoded "correct first-op" direction back to the residual stream during generation (training-free ActAdd steering) — does the model then USE it? No. Decodability ≠ steerability. The pre-registered usability threshold is not met at any coefficient, layer, or depth.

  • Depth 1 (the cleanest direction, probe 0.99): steering toward the true first-op never exceeds the no-steer baseline (0.33); at higher coefficients all conditions (true / wrong / random) decline together as fluency degrades. No usable effect where the signal is near-perfectly present.
  • Depth 2: a faint, predicted-direction whiff — steer_true (0.16–0.17) sits ~+0.05 above baseline (0.12) and above steer_wrong (0.07–0.13) across coefs 2–8 — but the gap over the steer_random control (0.12–0.15) is ~0.02–0.03, within noise at n=150, and far below the pre-registered +0.10 bar.
  • Robustness: the null holds at earlier steering layers (8, 12) as well as the probe-best layer (22), and the identification arm is flat (0.03 → 0.03).

Verdict: INERT. The latent first-op signal is readable but adding it back does not make the model act on it — at least not via the standard mean-difference method.

Research Program Fit

Direct causal follow-up to C19 (the wall's representation). Tests whether the mission's "cleverer access" dream — steer out latent capability without training — works. It does not, which sharpens which levers can actually move deployable capability.

Method

Reuse C19's cached activations to build ActAdd directions; test on FRESH held-out tasks with a forward hook.

  • Directions: per first-op class c, d_c = mean(acts[first_op==c, L]) − mean(acts[all, L]) at the C19 probe-best layer L (depth-1 L15, depth-2 L22), from C19's cached activations.
  • Steering: a forward hook on model.model.layers[L−1] adds coef · d_c to the residual at all positions, every generation step. Per-example vector = the task's own true/wrong/random direction.
  • Tasks: fresh verified list tasks (depth 2 primary, depth 1 sanity), n=150, disjoint from C19.
  • Readout — first-op naming: the prompt is forced to answer directly (First: suffix, no-think, greedy) — a fast, clean readout (baseline parse ≈ 1.0). Conditions × coef ∈ {2,4,6,8,12,20}.
  • Conditions: baseline (no steer), steer_true, steer_wrong (a random wrong op), steer_random (Gaussian vector scaled to ‖d_true‖).
  • Secondary — identification pass@1 (no-think, greedy): baseline vs steer_true at the best coef.

Results

depthbaselinebest steer_true (coef)steer_wrongsteer_randommax(true − baseline)
10.330.36 (4)0.32–0.380.35+0.03
20.120.17 (8)0.07–0.130.12–0.15+0.05

Identification (depth 2, coef 8, no-think): baseline 0.027 → steer_true 0.027. Earlier layers (depth 2, coef 8): layer 8 true 0.15 / wrong 0.13 / random 0.13; layer 12 true 0.15 / wrong 0.17 / random 0.13 — no effect.

steering

Pre-registered verdicts

  • P1 (causal usability, steer_true ≥ baseline + 0.10): REFUTED — max gain +0.05 (depth 2), +0.03 (depth 1).
  • P2 (specificity): PARTIAL/weak — depth-2 steer_wrong does dip below baseline (directionally correct), but steer_random ≈ baseline and the steer_truesteer_random gap is within noise; no clean specific effect.
  • P3 (depth-1 sanity, ≥ +0.15): REFUTED — steering the cleanest (0.99-decodable) direction moves naming by ≤ +0.03 and only degrades at higher strength.
  • P4 (identification lift): REFUTED — 0.027 → 0.027.

All predictions refuted ⇒ INERT, with at most a marginal, non-significant directional whiff at depth 2.

Interpretation

  • Decodability ≠ steerability. A linear probe reads the first op off the residual stream (C19), but adding that same direction back does not route it into behavior. The representation-expression gap C19 found is not bridged by simple linear intervention — the "unexpressed" information is not trivially writable into the output pathway.
  • Strengthens the arc's throughline from a new angle. Test-time interventions keep failing to move the wall: sampling+selection is free but adds no coverage (C17); activation steering can't elicit the latent signal (C20). The only things that move deployable capability remain weight edits (banking, C18) and externalization (tools, C12) — installing the capability, not reading it out.
  • Refines the C19 "latent" reading honestly. C19's latent signal is real but inert under this intervention: present-and-readable ≠ present-and-usable. The mission's "steer it out for free" hope does not pan out with the standard method.

Honesty notes / limits

  • This is a negative for ONE steering method (mean-difference / ActAdd, single layer, all positions). More sophisticated steering — optimized/causal directions, multi-layer, activation patching rather than addition, or larger interventions with fluency repair — is not ruled out. The claim is that the simplest, most-cited method fails cleanly, including on the near-perfectly-decodable depth-1 direction.
  • steer_true is an ORACLE (uses the known answer) — an upper bound on steering usefulness. Even the oracle fails, so deployable self-steering (toward the probe's prediction) is not worth pursuing here.
  • The identification arm is underpowered (no-think baseline floored at ~0.03); naming is the primary readout.

Next Experiments

  • Activation patching (replace rather than add the class-c subspace) or optimized steering vectors (gradient-tuned to change the output, not the probe) — does a stronger intervention move it? A single negative for ActAdd doesn't fully close the door.
  • Probe the C18 banked model: the complementary test — does banking raise the first-op probe (install the representation)? If yes, banking works by adding what steering can't inject.

Artifact Manifest

See reports/artifact_manifest.yaml. Key: scripts/steer.py, scripts/analyze.py, runs/steer_results.json, analysis/steering.png, reports/prereg.md. Steering directions are built from C19's cached activations (scratchpad/probe_artifacts/acts.npy, external) + qwen35_4b_latent_composition_probe/data/labels.json.

Experiment log 1

Show the running log (1 entry)

Scaffold

Created as a new experiment scaffold.

Figures 1

steering
steering · analysis/

Data files 1

Result tables and metrics copied from the experiment folder — preview inline or open the raw file.

Reproduce

Smoke test

python scripts/steer.py --n 30 --coefs 0 6 12 --depths 1

Full run

python scripts/steer.py --n 150 --coefs 0 2 4 6 8 12 20 --depths 2 1 && python scripts/analyze.py

Run steps are documented inside the experiment folder (README and scripts).

Browse the experiment folder on GitHub ↗