Factor-first migration
Element-by-element register of every figure, card, picker and paragraph on the blog page and in the generated pages that was built on principal components, what each becomes under the 2026-09-08 decision to make the factor analysis primary, and who owns the change.
Factor-first migration
The decision
Samuel, 2026-09-08: "we're going with the factor analysis so please redo the sphere using the biggest factors now and same for the other visualisations".
Before that date the blog page's primary frame was the principal-component
chart: five or six eigenvectors of the double-centred Gram of the 134 stage-one
adapters. After it the primary frame is the factor chart defined in
qwen35/fa_chart.py - the five oblimin PAF factors of the centred solution, in
the fixed order Warmth, Competence, Fearful withdrawal, Arousal, Imagination,
made into an orthonormal basis by Gram-Schmidt in the exact Gram inner product.
See The factor chart for the convention and Factor analysis of the adapter Gram for the solution
it comes from.
Principal components do not disappear. They stay as the secondary frame and they stay as the dimensionality argument, which is a question about a variance spectrum and cannot be asked of a rotated five-factor solution. See PCA, the scree curve and the two nulls.
Register
Owner column: weights is this migration (factor cards, pickers, map, scree, direction and trait pages, activation space); sphere and alien are the two concurrent agents redoing the sphere sweep and the hole/alien direction. "Keep as PCA" means the element is a dimensionality argument and is PCA by nature.
| element | where | current basis | what it becomes | done | owner |
|---|---|---|---|---|---|
PCS cards, PC1-PC6 dircards |
build_blog_page.py build() |
PC1-6 of the centred Gram, variance-tagged | kept, moved to second position behind the five factor cards, with a one-sentence bridge | yes | weights |
FAS cards, five dircards |
build_blog_page.py part_two() |
oblimin centred k=5 | moved to first position, before the components | yes | weights |
the explorer's direction picker (dir_opts) |
build_blog_page.py build() |
alien, PCs, factors, named axes, personality axis | five factors first, each labelled with its oblimin sum of squared loadings, then alien and controls, then PCs. Side effect: the explorer's opening demo changes from the unnamed direction to Warmth, because _js.txt initialises from the select's first option |
yes | weights |
the components picker (axopts) |
build_blog_page.py line ~272 |
six <option>s, PC1-PC6, three <select>s |
eleven options: five factors first (Warmth, Competence, Fearful withdrawal, Arousal, Imagination), then PC1-PC6; default axes Warmth x Competence x Fearful withdrawal | yes | weights |
| the 3-D map | blog_page/_js.txt makeMap, viz.json#scores |
D.viz.scores, 134 x 8 PC scores; axis labels hardcoded "PC"+(i+1) |
reads a concatenated 134 x 11 array, factor-chart coordinates in columns 0-4 and PC scores in 5-10, scaled per family; axis names from D.axnames |
yes | weights |
| the map's "widest gap" marker | blog_page/_js.txt makeMap, viz.json#special |
special.alien_k5.u, PC-indexed unit 8-vector |
special.alien_k5.u_fa, the same coefficients put through FAChart.coords, used when the selected axis is a factor axis |
yes | weights (coefficients owned by alien) |
| "The map" heading and paragraph | build_blog_page.py build() |
"Principal components of the 134 adapters give a picture you can turn over" | factor-first: the five factor directions are the axes, components are an alternative in the same picker | yes | weights |
| "The first two components take X% and Y% of the variance" | build_blog_page.py build() |
viz.json#var[0..1] |
now the second of two paragraphs. The first says the five factors catch a little over half an adapter and points at the next section; the exact figure and the five SS loadings are stated once, in factor_section(), from fa_chart_summary.json#chart_captures_frac_of_norm_mean and fa_qwen35.json#solutions.centred_k5.ss_loadings.oblimin. The PC variance sentence is kept as the bridge to the PC cards |
yes | weights |
| "The six components" heading | build_blog_page.py build() |
h2 above the PC cards | renamed and demoted to follow "The five factors" | yes | weights |
pc_table() |
build_blog_page.py |
analysis/pc_loadings.json |
unchanged content, unchanged place in part_two(); it is the PC-versus-named-axes reading and belongs with the PC frame |
yes (no change) | weights |
fa_table() |
build_blog_page.py |
fa_summary.json#centred_k5 |
unchanged content, moved up with the factor cards | yes | weights |
| the scree figure, left panel | build_blog_page.py scree_svg() |
PCA of the adapter cloud against two trained nulls, scree_null_matched.json |
keep as PCA. It is the dimensionality argument | yes (no change) | weights |
| the scree figure, right panel | build_blog_page.py scree_svg() |
PAF reduced eigenvalues against Horn's null, n_factors.parallel_analysis_uncentred |
switched to n_factors.parallel_analysis_centred, the matrix every factor on the page actually comes from; retention counts in the prose regenerated from the centred grid |
yes | weights |
| "Rotating to simple structure" section | build_blog_page.py part_two() |
written as PCA-then-FA motivation | rewritten factor-first: the factors are the frame, the components are the variance ordering they were rotated out of | yes | weights |
| the intro's list "the map, the components, the hole, the sphere" | build_blog_page.py build() |
names the components as the unlabelled picture | "the map, the factors, the components, the hole, the sphere" | yes | weights |
gaps_table() |
build_blog_page.py |
row order: named axes, PC1-6, personality axis, alien | five factors inserted ahead of the PCs; rows are skipped when the key is absent, so the factors do not yet appear - analysis/direction_gaps.json holds only PC1-6, axis_*, mean_assistant_axis and alien_k5 |
code done, data pending | weights (code) / alien (analyse_gaps.py) |
sphere sweep, SPHERE_BLOCK, sphere_section(), sphere_smooth(), sphere_caption() |
build_blog_page.py, build_sphere_spec.py, analysis/sphere_*.json |
unit sphere of the top three PCs | unit sphere of the top three factors (fa_chart.SPHERE_FACTORS) |
not by me | sphere |
the hole / alien direction, coverage_svg(), alien_card(), alien_verdict(), the "98% the fifth principal component" paragraph, the insouciant paragraph |
build_blog_page.py, analyse_gaps.py, analyse_alien*.py, analyse_hole.py, analyse_alignment.py |
five-dimensional PC chart | five-dimensional factor chart | not by me | alien |
fulloct_html(), actspace_section() |
build_blog_page.py |
PC-based Procrustes sentence in actspace_section ("a Procrustes fit of the 134x5 score matrices explains 54% of the variance") |
not edited - both functions were out of scope by instruction. The blog sentence is still the PC number and is still correct as written; the factor number lives in analysis/actspace_geometry_fa.json and on Constitution-as-prompt persona vectors vs weight geometry. Someone should decide whether the page quotes the factor number too |
open | weights |
verify_section(), optimise_section(), align_section() |
build_blog_page.py |
targets named PC4, axis_Agreeableness, alien_k5 |
keep as PCA. These describe adapters that were actually trained on data selected for PC4; renaming them would misreport what was run | yes (no change) | weights |
| direction-pages index | build_direction_pages.py |
index lists PCs before factors | factor pages first, components second | yes | weights |
| trait pages, "Where it sits in weight space" | wiki/tools/gen_trait_pages.py sec_weight_space() |
PC1-PC8 scores table first, oblimin loadings second | factor-chart coordinates and oblimin loadings first, PC scores second | yes | weights |
| activation-space Procrustes | analyse_actspace.py |
PC scores, 134 x 5, actspace_geometry.json#windows.resp.primary.procrustes_r2 = 0.5352210111769643 |
analyse_actspace_fa.py -> analysis/actspace_geometry_fa.json: factor-chart Procrustes R^2 0.7387431438763885, plus per-factor correlations with no rotation (+0.813 to +0.916). The PC number is quoted beside it, never recomputed. Prose section on Constitution-as-prompt persona vectors vs weight geometry; actspace_section() in the builder is untouched |
yes | weights |
analysis/intrinsic_coords.json, analysis/geometry_k_sweep.json |
not read by build_blog_page.py |
PC/intrinsic-dimension estimates | keep as PCA. Dimensionality arguments, cited only by PCA, the scree curve and the two nulls | yes (no change) | weights |
| UMAP layout | analysis/umap_test.json |
- | no layout exists. The file holds only kNN accuracies (factor(5-way), keying(2-way), keyed chance/sketch_space/pca10/pca30/umap5_heldout/shuffled_null), no coordinates. viz_fa.json therefore carries a 2-D map layout only from the first two chart axes, and records the absence |
yes | weights |
The 2-D map
analysis/viz_fa.json#map2d holds the primary 2-D layout: chart axis 0
(Warmth) against chart axis 1 (Competence), which are the first two Gram-Schmidt
basis vectors, not the raw oblique factor directions. A pc block beside it
holds PC1 x PC2 from viz.json#scores for comparison. No UMAP layout was ever
saved to analysis/, so there is nothing to keep alongside them;
umap_test.json records only how well a UMAP embedding predicted factor and
keying, and that is quoted on UMAP, sphere and other layouts.
One regression found and fixed on the way
build_direction_pages.py took a single --judged file, defaulting to
phase10_runs/judged_steerfix.json. That file does not cover the factor battery,
PC4-PC6 or the identity axes, all of which are judged in
phase10_runs/judged_steerfix23.json and judged_alien.json, so a plain run
silently produced thirteen pages with an empty judged table - which is what
happened the first time the reordered default was run. --judged now takes
several files and defaults to all three, and load_judged merges their records.
With that fix the 22 pages rebuild byte-identical to the set that was already in
qwen35/direction_pages/.
What is deliberately not migrated
- The scree left panel and the two trained nulls. The question "how many dimensions are above a null" is a question about a variance spectrum.
verify_sectionandoptimise_section. The optimised-data arms were trained against PC4 and the named Agreeableness axis. What was run is what is reported.- The seed floor, the null arms and every angle in
direction_gaps.jsonmeasured in the full sketch space: these never used a chart at all.
Sources
qwen35/build_blog_page.pyqwen35/build_viz_data.pyqwen35/build_viz_data_fa.pyqwen35/fa_chart.pyqwen35/blog_page/_js.txtqwen35/build_direction_pages.pywiki/tools/gen_trait_pages.pyqwen35/analysis/viz.jsonqwen35/analysis/viz_fa.jsonqwen35/results/fa_qwen35.json
Linked from
- Steering the unnamed direction on the factor chart
- Steering the unnamed direction
- Factor analysis of the adapter Gram
- The factor chart
- What has been done to the PCs but not to the factors
- Where no word goes - the hole and the alien direction
- Where no word goes - the hole on the factor chart
- PCA, the scree curve and the two nulls
- Superseded claims
File
pages/overview/factor-first-migration.md