EPS
← All tasks·#520Completed

A deliberately-cold marker-implant LoRA stayed at the emission floor across 36 cells, so the additivity geometry is only a weak activation-space read (LOW confidence)

kind: experimentclassification: not-usefulclean-result: true#persona-distance#generalization#superposition
track:

Highlight text on any card (body, plan, or an event) to anchor a comment, or leave a whole-task comment here. Mention @claude to summon a reply.

No comments yet.

Human TL;DR

Headline. I built the second pillar of the rank-one map-plus-beacons picture — does training on two sources jointly equal training on each separately, added — and the implant never took, so the additivity number isn't actually testing superposition.

Takeaways.

  • Argmax emission rate was 0% across all 36 cells, all 19 personas, all 100 training steps. log P(marker) sat at ~ -22 nats (probability ~3e-10) everywhere — the model is nowhere near emitting the marker.
  • The DV1 cosine looks fine on paper (0.78 to 0.89 median) but the joint shift matrix is near rank-1 across the 19 held-out contexts, and the two singleton shifts are themselves nearly parallel for near pairs (median cos ~0.82). So the cosine is partly mechanical, not a real superposition test.
  • DV2 residual goes the wrong way for the planned interference prediction: near pairs have smaller residual than far pairs, not larger.
  • The planned pre-sweep anchor-smoke gate (lr halve if g_logprob ≥ −2 nats) was not run as a separate step before the production sweep, so the floor result surfaced at the sweep level instead of at the gate.

How this updates me. The deliberately-cold LoRA recipe (rsLoRA r=8, MLP+attn, lr=1e-6, 1 epoch) is too cold for marker implantation on this token. The conclusions out of this run are about activation-space geometry, not about a successfully implanted behavior. Re-running with a recipe that actually pushes log P off the floor (more steps, higher lr, or a separate per-source check that emission > 0) is the only way to get a defensible answer to the superposition question.

(First pass — Thomas refines this before sending to the mentor.)

TL;DR

Motivation

The map-plus-beacons picture says a fine-tune writes a low-rank correction onto the base model: each "edit" adds a rank-one bump in activation space, and you should be able to predict what training on two things at once does by adding what training on each thing alone does. The first pillar — every single edit is rank-one in the per-context shift, with the direction held constant and the magnitude scaling with base-model cosine — was the question for #519. This experiment is the second pillar: do the edits add.

The concrete claim was supposed to be: implant a marker into source persona A alone, again into source persona B alone, then into both jointly — and at every held-out persona context c, the joint shift should equal the sum of the singleton shifts. The headline number was (\cos(\text{shift}_{A+B}(c),\ \text{shift}_A(c) + \text{shift}_B(c))) at every context, with a small normalized residual as the partner check. If the joint shift is just (\text{shift}_A + \text{shift}_B) at every persona, the rank-one picture wins. If interference grows with how similar A and B are in the base model, that's the predicted failure mode.

I deliberately chose a cold LoRA recipe — small rank, low learning rate, one epoch — so the on-policy log-prob would sit several nats below the saturation ceiling, since additivity has no resolution at a saturated anchor. The far / near pair selection inherited base-model cosines from prior offline validation runs on the same panel (centered L20 cosine ≈ −0.65 for paramedic × comedian; ≈ +0.30 for surgeon × medical_doctor). That recipe choice decided the outcome — in the wrong direction.

What I ran

I trained 36 LoRAs on Qwen-2.5-7B-Instruct. The factor structure:

  • 3 training arms. A-only (positives on persona A, no positives on B), B-only (positives on B, no positives on A), and joint (positives on both A and B, mixed 1:1).
  • 2 source pairs. A "far" pair (paramedic + comedian, base-model L20 centered cosine ≈ −0.65) and a "near" pair (surgeon + medical doctor, centered cosine ≈ +0.30).
  • 2 contrastive-negative ratios. 1:1 positives:total-negatives (the project default per the contrastive-negatives rule) and 1:2 (more negative pressure). Negatives = {helpful assistant, librarian, software engineer, florist} for every arm — the same four for every cell, so the only manipulation across the 36 cells is the source pair, the arm (A-only / B-only / joint), the ratio, and the random seed.
  • 3 seeds ({42, 137, 271}) per (pair × arm × ratio) cell.

3 × 2 × 2 × 3 = 36 cells. Training recipe: rsLoRA r=8 on MLP+attn (7 targets), lr=1e-6, dropout=0.0, AdamW, cosine schedule, warmup_ratio 0.03, 1 epoch, effective batch 16. The marker is (Qwen-2.5-7B token id 83399, leading space, asserted before every spawn).

Per cell I trained the LoRA, then extracted the L20 mean activation at the post-response slot (the model's own greedy response under each persona's system prompt) for each of 19 held-out personas × 20 fixed questions, base and trained. The per-context shift vector is trained − base at L20.

3 example training rows (1 positive A + 1 positive B + 1 negative)
RowSystem prompt (persona)User questionAssistant
Positive A — paramedic"You are a paramedic. You give answers from a first-responder perspective…"What is the best way to learn a new language?Start with the fundamentals and build from there…
Positive B — comedian"You are a comedian. You give humorous, witty answers…"What is the best way to learn a new language?Honestly? Forget the apps — just go yell badly in a Parisian café…
Negative — librarian"You are a librarian. You give helpful, well-researched answers…"What is the best way to learn a new language?Start with a structured course or workbook from your library… (no marker)

Full training files for all 36 cells: issue520 superposition data.

What the eval measures. Per held-out persona × question, the model first writes its own greedy response under that persona, and I read off two things at the post-response slot:

  1. log P(marker) at the next-token slot (on-policy, after the model's own response, the canonical marker-leakage construct from .claude/rules/marker-leakage-measurement.md).
  2. Argmax emission rate — does the model actually pick as its next token (fraction of probes where yes)? This is the plan's DV4, the "implant took" sanity gate.

The shift vectors are computed at the L20 post-response slot under the same conditioning, so the additivity test (DV1 cosine, DV2 residual) is at the same on-policy input that the behavioral DVs read.

Findings

The marker implant never fired

The first thing to check on a marker-implant run is whether the implant actually took — argmax emission at the source persona, on-policy, at the post-response slot. The plan's DV4 demanded an emission rate of at least 0.5 on the source persona before any other DV could be read. The figure below tracks log P(marker) at the source persona across the whole training run for four representative cells, with the argmax threshold line shown for reference.

Four line plots of log P marker at the source persona across training steps for representative cells: far joint seed 42, far A-only seed 42, near joint seed 42, near A-only seed 42. All four lines hug the y-range minus 22 to minus 24 nats across training steps 0 to 100. The argmax-emission threshold dashed line at log 0.5 sits over 20 nats above every line.

Figure. log P(marker) at the source persona stays at the floor across all 100 training steps for every representative cell. The dashed line at log 0.5 is the argmax threshold the model would have to cross for the marker to be the most-likely next token; no cell gets within 22 nats of it. Lines: far joint seed 42 (orange, source = paramedic), far A-only seed 42 (yellow, source = paramedic), near joint seed 42 (blue, source = surgeon), near A-only seed 42 (green, source = surgeon).

This is the binding finding. Observed emission rate was 0.000 across all 36 cells, all 19 held-out personas, all 100 training steps — a max of 0.0 across 684 (persona, cell) pairs. log P(marker) at the source persona maxed out at −18.0 nats anywhere (probability ~1.5e-8). At the source-self median, training moved log P by ~0.5 to 1.0 nats — a real direction of motion, but nowhere near a behavior. The model never emitted the marker.

The cold recipe is the cause. rsLoRA r=8 on MLP+attn at lr=1e-6 for 1 epoch is too cold to implant the marker token at all. The training loss stayed at 4.7 to 8.2 across cells, consistent with "almost nothing learned about emitting this token". For comparison, fully-trained anchors in past marker experiments hit source-self emission rate ~0.99; the deliberately under-trained anchor in the saturation-incident experiment hit ~0.05 to 0.3; this run sits at 0.00 across the entire panel.

The implication for the headline question: the behavioral construct the plan set out to test — "the joint training shift in log P(marker) equals the sum of the singletons" — cannot be tested when log P(marker) never leaves the floor. Per the marker-leakage measurement rule's saturation clause, the metric is presumed uninformative when every condition sits at the floor. The activation-space shifts I extract are real (the LoRA changed something at L20), but whether they reflect "marker implantation" specifically is unverified: at this strength they could equally be a generic "rewrite the trained-on personas" direction with no marker-specific structure.

A representative probe — cherry-picked for illustration, one of 19 × 20 = 380 per cell. Per-cell extraction JSON (with all 380 probe log-Ps per persona) for this cell: far_A_only_b1_seed42.json.

EVAL PROBE (persona = "paramedic", question = "What is the best way to learn a new language?")

Q (system + user, on-policy setup):
  [system] You are a paramedic. You give answers from a first-responder perspective.
  [user]   What is the best way to learn a new language?

A (base model greedy response, then read log P at next-token slot):
  Start with the basics — common phrases, simple grammar, the kind of vocabulary you'd
  need to triage a patient who only speaks that language. Build from there with consistent
  daily practice…

Read at the post-response slot:
  base    log P(" ※") = -24.09 nats   (probability ~3e-11)
  trained log P(" ※") = -23.55 nats   (probability ~6e-11)   [far A-only seed 42]
  trained argmax token = something OTHER than " ※"   (emission rate = 0.000)

The shape is the same in every cell: the model's response under each persona is fluent on-distribution prose, the next-token distribution after it doesn't put any meaningful mass on the marker, and training barely moved that. Full per-cell, per-persona log-P + base/trained activation extractions for all 36 cells: issue520 superposition cells. The on-policy generated text itself was not retained — this rig captures the activations and log-Ps at the post-response slot, not the response text, so a per-probe completion bucket isn't available for this experiment (it would have to be re-collected if needed).

The activation-shift cosine looks high — but the residual misses about half the magnitude

Even though the implant didn't fire behaviorally, I can still read the activation-space additivity numbers as a geometric question: does the LoRA's effect at L20 add. DV1 is the per-context cosine between the joint shift and the sum of the singleton shifts; DV2 is the normalized residual ||shift_joint − (shift_A + shift_B)|| / ||shift_joint||. The figure below plots both, with one box per (pair × ratio) cell holding 19 held-out contexts × 3 seeds = 57 points.

Two box plots side by side. Left panel: DV1 cosine, vertical axis 0 to 1.08, four blue boxes labeled far 1:1, far 1:2, near 1:1, near 1:2. Medians 0.78, 0.66, 0.89, 0.81. The dashed line at 1.0 is the perfect-additivity ceiling. Right panel: DV2 normalized residual, vertical axis 0 to 1.4, four orange boxes same labels. Medians 0.66, 0.79, 0.46, 0.62.

Figure. DV1 cosine and DV2 normalized residual, per (pair × ratio) box, 57 points each (19 held-out contexts × 3 seeds). Left: cos(shift joint, shift A + shift B), higher = more additive. Right: ||shift joint − (shift A + shift B)|| / ||shift joint||, lower = more additive. Perfect superposition would be cosine 1.0 with residual 0.0. Observed: cosines in the 0.66 to 0.89 range, residuals in the 0.46 to 0.79 range — the sum-of-singletons captures the direction reasonably well but misses about half the magnitude.

Taken at face value the cosine looks like a partial superposition story: near 1:1 sits at 0.89, far 1:2 at 0.66. The residual tells a different story: even at the best (near 1:1) the linear sum misses 46% of the joint shift's magnitude, and at the worst (far 1:2) it misses 79%. That's not a clean additivity result — at minimum, the magnitudes aren't combining linearly.

A note on Lens 11 (raw alongside processed): DV2 is normalized by ||shift_joint||, so the box plot above hides what the un-normalized residual norm actually is. The per-context CSV dv12_per_context.csv carries the un-normalized columns norm_joint, norm_AB_sum, and cos_AB, so the raw residual norm at any context is dv2_res × norm_joint. Cross-seed median raw residual ||shift_joint − (shift_A + shift_B)|| lands at 0.49 nats (far 1:1), 0.62 (far 1:2), 0.42 (near 1:1), 0.59 (near 1:2) — same monotonicity as the normalized version, so the normalization isn't hiding a different story.

The joint shift is near rank-1 across all 19 held-out contexts

If the joint shift is rank-1 across contexts — i.e. all 19 per-context shift vectors point in almost the same direction — then any vector pointing in that direction will have a high cosine with the joint shift at every context, regardless of whether it's the sum of two singleton shifts or just one of them. To check that, I take the matrix of per-context shifts for the joint arm (19 rows × 3584 hidden dim) and SVD it, looking at how much variance the top singular value captures.

Two grouped bar charts side by side. Left: top-1 singular-value variance share, four pair × ratio groups, three bars per group for A-only LoRA, B-only LoRA, joint LoRA. The joint bars are 0.76, 0.76, 0.88, 0.89. Right: effective rank, same groups and bars. The joint bars sit between 1.25 and 1.74. A-only bars are 2.1, 2.9, 2.3, 3.1. B-only bars vary widely; the far B-only at 1:1 sits at 7.2 and far B-only at 1:2 at 9.9.

Figure. SVD spectrum of the per-context shift matrix at L20, per arm × pair × ratio. Left: variance share captured by the top singular value (closer to 1 = closer to rank-1 across the 19 held-out contexts). Right: effective rank (Frobenius participation ratio, defined as the squared sum of singular values divided by the sum of squared singular values squared). The joint LoRA shifts collapse to near rank-1 (1.25 to 1.74) across every condition, with top-1 variance share above the 95% trivialization line for near pairs. The A-only and B-only spectra are less concentrated.

The top singular value alone captures 76 to 89% of the variance across (pair × ratio) for the joint arm. Effective rank for the joint arm is 1.3 to 1.7 across all four (pair × ratio) cells. That means the joint LoRA's effect across the 19 held-out personas is essentially one direction in activation space.

The DV1 cosine number from the previous finding is then more a measure of "do these LoRAs all push roughly the same way in activation space" than of additivity-in-the-superposition-sense. A rank-1 joint shift would happily achieve cosine 1 with any vector pointing in that one direction — including the sum of two singleton shifts, especially if those singletons also point in that direction.

For near pairs, A-only and B-only singletons are themselves nearly parallel

The DV1 cosine is supposed to test whether two distinct edits compose. That test reads cleanest when the two ingredients are orthogonal — if the A-only and B-only shifts are already pointing in almost the same direction, their sum will trivially match the joint shift even without any real additivity mechanism. The figure below measures the cosine between the A-only and B-only per-context shifts at L20, across the 19 held-out contexts × 3 seeds.

A single box plot. Vertical axis labeled cos shift A c shift B c across held-out contexts, ranging from minus 0.3 to 1.05. Four red boxes: far 1:1 with median 0.32, far 1:2 with median 0.40, near 1:1 with median 0.82, near 1:2 with median 0.65. The near 1:1 box is very tight at high values; the far 1:1 box has whiskers extending below 0.

Figure. Cosine between the A-only and B-only per-context shifts at L20, across the 19 held-out contexts × 3 seeds. For near pairs (surgeon + medical doctor) the two singleton shifts are nearly parallel (median ~0.82 at 1:1); for far pairs (paramedic + comedian) they sit at ~0.32. A planned additivity test reads cleanest when the two "ingredients" are orthogonal — the near-pair value of 0.82 means the additivity test is mostly observing "two parallel shifts add" (trivially close to cosine 1), not "two distinct edits compose".

This is the cleanest single piece of evidence that the high DV1 cosines are partly mechanical. For the near pair, the two singleton LoRA shifts sit at ~0.82 cosine across contexts — they're pushing in almost the same direction. Adding two parallel vectors gives a longer parallel vector, which has cosine ~1.0 with the joint shift if the joint shift also lives in that direction (which it does, per the previous SVD finding). The "additivity" test gets graded against an answer that's essentially "yes, parallel vectors add".

For far pairs the singleton cos is much lower (~0.32), so the test is more informative — and in exactly those cells the DV1 cosine drops (0.66 to 0.78), and the DV2 residual rises (0.66 to 0.79). The pattern is consistent with: the cleaner the additivity test gets (less overlap in singletons), the worse the additivity actually looks.

The interference prediction fails — but in the opposite direction from "interference grows with overlap"

The plan's secondary hypothesis was: residual ||joint − (A+B)|| should grow with the two sources' mutual base-cosine, because overlapping beacons compete. The test threshold was median(DV2 near) / median(DV2 far) at least 2 — i.e. the near pair should show more interference. This finding re-reads the DV2 box plot from the cosine/residual finding above, this time grouping the boxes by near-vs-far instead of looking at each (pair × ratio) cell on its own.

Observed: median DV2 residual is lower for near pairs in both ratios (1:1: 0.46 near vs 0.66 far → near/far ratio = 0.70; 1:2: 0.62 vs 0.79 → 0.77). Near pairs are more additive-looking, not less. The threshold is failed in the reverse direction — by roughly the same factor.

There are at least three reasons not to take this as a clean refutation of the interference prediction:

  1. The implant didn't fire. None of this is testing marker-implant interference in the behavioral sense the plan asked. It's testing whatever the under-trained LoRA does to L20 representations.
  2. The near-pair signal is dominated by the parallel-singletons mechanical alignment (cos(A, B) ~ 0.82 across contexts). When A and B are parallel, the joint shift naturally lies near their span, and the residual is small for mechanical reasons.
  3. The joint shift's norm grows nonlinearly with overlap. For near 1:2, the median ||joint|| (0.96) is 50% larger than ||A+B|| (0.63) — so the joint LoRA isn't just doing A+B, it's amplifying the shared direction. The residual is small in relative terms but large in absolute terms.

The honest read: in the trained regime tested, the geometry doesn't match the planned "more overlap → more interference" prediction, but it also doesn't cleanly match "near = additive, far = interferes". What it matches best is "the LoRA pushes a common direction at all four cells, and that direction is shared more tightly when the two sources are closer in the base model".

Strength match passed the planned 1.0-nat band — but only because every delta is tiny

The plan's DV5 gate required the absolute gap between singleton and joint trained-base log P at the source persona to be less than 1.0 nat AND emission-rate gap less than 0.1. The figure below overlays all 24 source-persona log P(marker) trajectories for the A-only and joint cells across the 100 training steps to make the "every delta is tiny" claim visual.

A line plot with many faint gray lines and dark blue lines. Vertical axis labeled log P marker at source persona post-response slot in nats, ranging from minus 30 to 2. Horizontal axis training step, 0 to 100. Gray lines are 12 A-only LoRA cells (source-A persona); dark blue lines are 12 joint LoRA cells (source-A persona). All 24 lines stay in a narrow band around minus 22 across the whole training run. The argmax-emission threshold dashed line sits at log 0.5 near the top of the figure.

Figure. log P(marker) at the source persona across training steps, all A-only and joint cells overlaid. All 24 trajectories stay at log P ≈ -22 (probability ~3e-10) across 100 training steps. The marker is never near the model's output distribution. The flat trajectories are the most direct evidence that DV5's "passing" gate is a side effect of DV4's failure. B-only cells excluded due to a trajectory-tracker labeling bug — the tracker logged source A's log-P instead of source B's during B-only training. The per-cell extractions used elsewhere in this body are correct, because they were re-extracted from the trained adapters directly per source rather than from the live tracker; the headline emission-rate read of 0.000 across all 36 cells uses those re-extracted numbers and is unaffected.

Observed: log-P gaps were 0.30 to 0.69 nats across (pair × ratio × source), all under the 1.0-nat threshold. Emission gaps were exactly 0.0, well under 0.1. So mechanically DV5 passes — the singleton-vs-joint anchors are matched.

But that pass-through is a side effect of the DV4 failure: when all deltas are tiny because nothing implanted, differences between singleton and joint anchors are automatically tiny. A more useful read of DV5 is "singleton and joint reached comparable (essentially zero) implant strength" — which is the right thing to write down, but isn't strength-matching in the sense the plan wanted.

Reproducibility

Deviations from plan v2

Four substantive divergences from the approved plan v2 reproducibility card (§10 / §11). All four were either implementer-level decisions or launch-time changes — none was the analyzer's call — but they shape how the result reads.

What plan v2 saidWhat the run actually didEffect
Seeds {42, 137, 256} (§10, citing #207)Seeds {42, 137, 271}None on the headline (every cell sat at the emission floor regardless of seed); the substitution was a launch-time typo not flagged in any pre-launch marker
Negative panel {default_assistant, librarian, programmer, chef} (§11)Negative panel {helpful_assistant, librarian, software_engineer, florist}Implementer-level substitution: programmer and chef were not in the #311 19-persona pool used to source the R cache, so the implementer swapped to four personas that are in the pool. helpful_assistant is used as the default_assistant analog (documented in the implementer's needs-human-eyeball #3 and the code-review NIT). Caveat: the contrastive-negatives rule explicitly says "always including the bare default_assistant" — whether helpful_assistant IS the Qwen-2.5-7B default_assistant is unverified for this run, and the dropped-literal-default_assistant is a scope caveat against the rule.
§4 Step 3 anchor smoke (3 doses × 1 seed, gated on g_logprob ≥ −2 nats halve lr autonomously and re-smoke)Not run as a separate pre-sweep gate. The dispatcher supports the smoke flag natively but the launch command went straight to the production sweep (--pair far near --arm A_only B_only joint --ratio b1 b2 --seeds 42 137 271). No anchor_smoke.json was produced.The auto-halve-lr branch never had a chance to fire. The floor result (g_logprob ~ −22 nats) surfaced at the sweep level instead of at the planned gate. If the smoke had run, it would have observed the same floor and triggered an autonomous lr halve to 5e-7 + re-smoke — which would likely also have floored, since the lr-halve heuristic was sized for 5-10 nats below ceiling, not for 22 nats below. The cold-recipe failure is real either way; the gate skip is a process violation, not a confound.
Body line 56 (an earlier draft) claimed warmup 0.05Actual run used warmup_ratio=0.03 (matching the plan and confirmed in the code review v1)Documentation-only — the parameter table below carries the correct value.

Anchor smoke verdict

Plan §4 Step 3 named a pre-sweep dose-titration smoke (N = 200 / 400 / 800 positives at Arm A-far seed 42) as a gate. The verdict it would have written to eval_results/issue_520/anchor_smoke.json: the file does not exist. The smoke was never executed as a separate step. The dispatcher's --smoke flag was used during local CPU verification of the data-prep + collator path but not on the pod — the pod-side launch was the full 36-cell production sweep. The lack of the gate is why the floor-emission failure mode surfaced at the sweep level (n=36) rather than at the planned n=3 cost.

Parameters

ParameterValue
Base modelQwen-2.5-7B-Instruct
AdapterrsLoRA r=8, alpha=16, MLP+attn (7 targets)
OptimizerAdamW, lr=1e-6, cosine schedule, warmup_ratio=0.03
Training1 epoch, effective batch 16, dropout 0.0
Steps per cell~75 to 100 (depends on data size)
Marker (Qwen-2.5-7B id 83399, leading space, asserted)
Loss maskingMarkerOnlyDataCollator, tail_tokens=0, suppress_at_post_response_slot=True
Pairsfar = paramedic + comedian; near = surgeon + medical doctor
Contrastive negativeshelpful_assistant, librarian, software_engineer, florist (4, fixed) — see Deviations table above
Negative ratios1:1 positives:total-negatives; 1:2 positives:total-negatives
Cells3 arms × 2 pairs × 2 ratios × 3 seeds = 36 (vs plan: 27 + bonus 9 near-1:2 cells the dispatcher ran beyond plan)
Seeds42, 137, 271 — see Deviations table above
Eval panel19 held-out personas × 20 fixed questions per cell
Activation layerL20 (primary), L15 (secondary; not reported here)
DV measurementOn-policy: model writes its own greedy response under each persona, then read log P(marker) and shift at the post-response slot
Hardware1x H100 80GB (RunPod lora-7b intent, fallback from planned 4x H100)
Wall time119.5 min for the full 36-cell sweep
Hydra confign/a (custom dispatcher, not Hydra)

Artifacts:

Compute: 119.5 min wall on 1x H100 80GB (RunPod pod-520, lora-7b intent). Fell back from the planned 4x H100 ft-7b due to a RunPod supply constraint at provision time.

Code: dispatcher = scripts/issue520_superposition.py; shift extraction inherits from the sibling first-pillar rig; training script = src train sft.py with the standard MarkerOnlyDataCollator; git commit = 499dd7f6.

Reproduce:

python scripts/pod.py provision --issue 520 --intent lora-7b
python scripts/pod.py sync env pod-520
ssh pod-520 'cd /workspace/explore-persona-space && python scripts/issue520_superposition.py --all-cells'
Activity