Written entirely by Claude, as a working record for agents more than for human readers: every number with its source file, and every claim since withdrawn. Some facts here may be out of date; where it disagrees with the post, trust the post. For the readable version, see the companion.
Per-direction seed stability, and why the check is uninformative
Every named direction's trait coordinates correlate about 0.998 between the two LoRA seeds, but so does a random merge of the same adapters at 0.979, because the cross-seed Gram is a scaled copy of the within-seed Gram; the check reproduces the matrix-level result and says nothing about the factors.
The question
Cross-seed geometry establishes that the arrangement of the adapters
replicates across LoRA initialisations: cross-seed cosine tracks within-run
cosine at Pearson 0.9966 over the 5,320 pairs of 134 seed-0 adapters against 40
seed-1 adapters (qwen35/analysis/crossseed_arms.json#[1]). That is a statement
about the whole Gram. It was never turned into a statement about the named
directions - the five factor-analytic factors, PC1-PC6, the five Big Five keying
axes and the grand mean - which are the objects the steering work actually uses.
qwen35/analyse_direction_seed_stability.py was written on 2026-09-08 to ask the
per-direction version of the question, and the answer is that the question cannot
be asked this way.
What was computed
Each named direction is a weighted merge v = sum_i c_i a_i of the 134 seed-0
stage-one adapters, with coefficients taken from the steering specs
(qwen35/phase10_runs/steer_spec.json and steer_spec2_7a.json, key
jobs[].coef). The cosine of any adapter b with v needs only Gram entries:
<b, v> / (|b| |v|) = sum_i c_i X[i, b] / (|b| sqrt(c' G c)). Seed-0 coordinates
come from results/gram_sweep.npz; seed-1 coordinates come from the exact
134 x 40 cross-Gram
results/cross_gram_full_root_x_data_null_seedpaired_s40_matched.npz, the
matched-objective seed-1 arm (The seed floor, The null control arms).
Reported per direction over the 40 traits that exist at both seeds
(analysis/direction_seed_stability.json#n_seed1_traits = 40): the Pearson
correlation between the seed-0 and seed-1 coordinate vectors, a Spearman version,
a permutation p, the regression slope of seed-1 on seed-0, and the two standard
deviations.
The result
analysis/direction_seed_stability.json#directions.<name>:
| direction | pearson | spearman | slope seed1 on seed0 | sd seed 0 | sd seed 1 |
|---|---|---|---|---|---|
| FA_Competence | 0.9987 | 0.9966 | 0.0258 | 0.3577 | 0.00925 |
| PC1 | 0.9985 | 0.9968 | 0.0257 | 0.3789 | 0.00975 |
| FA_Arousal | 0.9984 | 0.9957 | 0.0258 | 0.3457 | 0.00894 |
| PC2 | 0.9983 | 0.9972 | 0.0257 | 0.3061 | 0.00787 |
| PC3 | 0.9982 | 0.9932 | 0.0252 | 0.2225 | 0.00562 |
| FA_Imagination | 0.9981 | 0.9949 | 0.0258 | 0.2523 | 0.00653 |
| FA_Warmth | 0.9980 | 0.9951 | 0.0257 | 0.3148 | 0.00811 |
| axis_Intellect | 0.9980 | 0.9974 | 0.0254 | 0.2190 | 0.00558 |
| axis_Conscientiousness | 0.9978 | 0.9936 | 0.0258 | 0.3423 | 0.00883 |
| FA_FearfulWithdrawal | 0.9974 | 0.9976 | 0.0255 | 0.2758 | 0.00705 |
| axis_Agreeableness | 0.9971 | 0.9938 | 0.0252 | 0.3052 | 0.00772 |
| axis_Extraversion | 0.9970 | 0.9946 | 0.0260 | 0.2875 | 0.00748 |
| axis_EmotionalStability | 0.9968 | 0.9947 | 0.0241 | 0.2543 | 0.00614 |
| PC5 | 0.9963 | 0.9944 | 0.0237 | 0.1795 | 0.00427 |
| PC6 | 0.9955 | 0.9944 | 0.0225 | 0.1563 | 0.00354 |
| PC4 | 0.9934 | 0.9902 | 0.0245 | 0.1661 | 0.00410 |
| mean_assistant_axis | 0.9908 | 0.9837 | 0.0271 | 0.1248 | 0.00342 |
(Values rounded from the JSON; the full precision is in the file, e.g.
#directions.FA_Warmth.pearson = 0.9979502995224957. Every direction's permutation p
is at the floor, #directions.*.perm_p = 0.0001999600079984003, 5,000
permutations.)
Read alone this says every named direction replicates almost perfectly. It does not say that.
Why it is uninformative
The same script draws 200 random directions of matched construction -
Gaussian coefficients over the same 134 seed-0 adapters - and scores them the
same way. #random_direction_pearson:
mean 0.9791758621274371, sd 0.017628602815322764, p95 0.9946732339575204, n 200, note "random Gaussian coefficients over the 134 seed-0 adapters; the shared LoRA-A makes even random merges partly reproducible"
A random merge of the zoo scores 0.979. Two of the seventeen named directions -
PC4 at 0.9934 and mean_assistant_axis at 0.9908 - sit below the random
directions' 95th percentile of 0.9947. Nothing here separates a factor from
noise.
The reason is visible in the slope column. Every direction's seed-1 coordinates
are the seed-0 coordinates multiplied by about 0.026 (the per-direction slopes run
from 0.0225 to 0.0271), and 0.026 is the measured attenuation slope of the whole
cross-seed Gram (analysis/crossseed_arms.json#[1] slope
0.026542111376493285), itself close to the r/d = 0.025 overlap of two random
rank-64 subspaces on The seed floor. The cross-seed Gram
block is, to a very good approximation, a scalar multiple of the within-seed
block, so any coefficient vector c gives seed-1 coordinates proportional to
its seed-0 coordinates and therefore a correlation near 1. The per-direction test
is the matrix-level Pearson 0.9966 re-expressed once per direction; it adds no
information beyond it.
The 0.998 must not be quoted as factor stability. It is a property of the Gram, not of the factor solution, and it would look the same if the factor analysis had returned five arbitrary directions.
What a real test would look like
The question worth asking is whether factor analysis run independently at the
second seed recovers the same factors. That means factoring the seed-1 adapters
among themselves - their own 40 x 40 Gram - and computing Tucker congruence
between the resulting loading matrix and the seed-0 loadings for the same 40
traits, exactly as Structure of the stage-two adapter space compares stage one with stage two
(qwen35/analysis/stage2_structure.json#factor_congruence_stage1_vs_stage2.best_matching,
written by analyse_stage2_structure.py). That
comparison is invariant to the overall scale, which is what kills the test above.
Is it feasible with 40 variables? The 40 seed-1 traits are balanced - 8 Goldberg
markers for each of the five factors (from traits_primary.json joined to the
npz names) - so five factors over 40 variables is a legitimate if small design:
about 8 variables per factor, which is at the low end of what is usually
recommended, and each Tucker congruence would be computed over 40 entries rather
than 134, so the congruences would be noisy and a difference of a few hundredths
would mean nothing. The 40-trait arm's own decomposition already replicates the
labelled tests at ratio 1.01 and 0.96 (Cross-seed geometry), so the design is
not obviously too small to see the effect.
What stops it today is which Gram exists. results/gram_data_null_seedpaired_s40.npz
is a 40 x 40 within-arm Gram, but it belongs to the original-objective
seed-paired arm (plain sigmoid DPO, the mismatch described on The null control arms);
it is the file behind results/decomposition_seed1.json. The matched-objective
seed-1 adapters, which is what the cross-Gram above uses, have no within-arm Gram
on disk - only cross-Grams
(results/cross_gram_full_root_x_data_null_seedpaired_s40_matched.npz and
cross_gram_full_data_null_seedpaired_s40_x_data_null_seedpaired_s40_matched.npz).
So the test could be run today on the original-objective arm on this box with no
GPU, carrying the objective caveat that the matched retrain elsewhere changed
nothing to three decimal places, or it could be run cleanly after computing one
more 40 x 40 Gram on Modal. Neither has been done; it is recorded on
Open questions and on What has been done to the PCs but not to the factors as open.
A second caveat for whoever runs it: analyse_fa_qwen35.py currently asserts
that the input Gram has 100 or 134 traits and that exactly 100 of them are
Goldberg markers, so a 40-trait input needs that assertion widened first.
Related: Cross-seed geometry, The seed floor, Factor analysis of the adapter Gram, What has been done to the PCs but not to the factors, Factor analysis of the null arms, Steering the base model along weight-space directions.
Sources
qwen35/analyse_direction_seed_stability.pyqwen35/analysis/direction_seed_stability.json#directionsqwen35/analysis/direction_seed_stability.json#random_direction_pearsonqwen35/analysis/crossseed_arms.jsonqwen35/results/cross_gram_full_root_x_data_null_seedpaired_s40_matched.npzqwen35/results/gram_data_null_seedpaired_s40.npz