Family Atlas
Working hypothesis (not a fact)
This program's actuation work rests on a hypothesis, not an established
result: the three read axes this project uses everywhere --
- doubt (known-vs-refused),
- caution (refused-vs-confab), and
- raw refusal (refused-vs-answered) --
are assumed to be linearly readable in every instruction-tuned model. WHERE
they read (the layer band) is assumed model/family/size RELATIVE, not a
constant. The corollary: never port a layer or a depth fraction from one
family to another. Run the atlas on the new substrate first; every
per-family actuation amendment consumes that atlas's layer map as an input,
not a guess.
This hypothesis has already been tested once and partially failed on its own
terms: see docs/atlas/family-layer-map.md and the Gotchas section below.
Treat every number this instrument produces as evidence about THIS
hypothesis, not as a foregone conclusion it will confirm.
When to use this skill
- A new model, family, or size enters the program and no atlas exists for it
yet in
docs/atlas/family-layer-map.md.
- You are about to design a per-family actuation cell (steering, erase-write,
gated snap) and need a layer map to consume, not a ported depth fraction.
- An existing family's substrate changed (new HF revision, new checkpoint,
new quantization) in a way that could move its workspace band.
Do not use this skill to re-litigate a family that already has a resolved
row in the registry with the SAME substrate revision; read that row and its
cited governed doc instead.
The standard procedure
- Scaffold. Run
bin/exp new <slug> --type probe-fit from the repo
root. Copy templates/cell.yaml, templates/gates.yaml, and
templates/AMENDMENT.skeleton.md (as AMENDMENT.md) into the new
experiment directory, replacing the generic placeholders bin/exp new
wrote. One cell per substrate; a two-family run (like
experiments/jspace-family-atlas) is two cells in one cell.yaml, not
two experiments.
- Pools. Reuse an existing experiment's committed split manifest
verbatim whenever one exists for this substrate (row pool, baseline
gradings, role/split assignments, unchanged). Only mine a fresh pool if
none exists, and mine it per the program's standard roles:
confab,
known_correct_answered, unknown_refused, with split labels fit,
held_out, fit_only. profile_and_read_family_atlas_panel.py reads
rows by these exact role and split names; a pool using different role
names needs a mapping layer before this instrument can read it.
- Select and bridge the capture backend. Read
../experiment-runner/reference/batched-generation.md. Prefer native vLLM
extraction for a new atlas only after its model-specific bridge establishes
exact prompt tokens, state indexing 0 through num_hidden_layers, anchor
identity, normalization convention, numerical agreement, and profile-level
location agreement against the HF reference. A cell extending an existing
HF atlas remains HF unless that bridge is pre-stated and passed. If the
generic tuner lacks vLLM capture, use the HF fallback and record the
capability gap; do not add an experiment-owned engine.
- Capture. Full-depth anchor capture: every hidden state (0 through
num_hidden_layers), final-prompt-token anchor, float32, via
scripts/capture_family_atlas_cell.py capture. The script reads
capture.engine from cell.yaml; vLLM mode requires
VLLM_BATCH_INVARIANT=1. Point --render-module
at a copy of the source experiment's own render logic (see
templates/render_example.py) so the anchor position matches that
experiment's own convention exactly -- this is capture-only, no steering
hooks, no re-mining, no re-generation.
- Profile + read panel.
scripts/profile_and_read_family_atlas_panel.py score: per-layer eff_dim_frac (participation-ratio profile) and the
per-layer two-sided held-out AUROC read panel for doubt / caution /
raw_refusal, each with a bootstrap CI, plus the standard
random-direction control (see Gotchas). If the source pool's
unknown_refused role is fit_only (no held-out partition at all),
this script subdivides it deterministically into refused_fit /
refused_eval by default; pass --no-split-refused if your pool
already carries real held-out refused rows.
- Resolve and register. Adjudicate the prediction/falsifier in
AMENDMENT.md, run bin/exp sign / resolve per the experiments skill,
then append one row to docs/atlas/family-layer-map.md citing the
resolved doc. Never add a registry row before its governed doc is signed
and resolved.
Gotchas
- Norm/position confound on the doubt axis. The refused-vs-known
contrast at the final-prompt-token anchor carries a norm/position
confound: a FIXED RANDOM direction can read up to ~0.97 best-orientation
AUROC on this same contrast at some layers (observed in both
jspace-family-atlas cells). Always read the doubt axis's AUROC against
its own layer's random-direction control value from the same
atlas_summary.json, not against 0.5. Caution (refused-vs-confab) and raw
refusal (refused-vs-answered) did NOT show this confound in the one
resolved run (control stayed ~0.5-0.75), but check your own cell's control
anyway -- do not assume that pattern transfers.
- The profile can peak early-exterior, not interior. The first
family-atlas run's prediction and falsifier both assumed the eff_dim_frac
profile would either peak inside (20%, 85%) depth (prediction met) or be
monotone to the last layer (falsifier met). It did neither: llama peaked
at layer 4 of 28 (0.14 depth) and mistral at layer 3 of 32 (0.09 depth),
then declined through the midband with a mild late uptick -- an
early-exterior peak, a shape neither the prediction nor the falsifier
named. The result landed in a gap: not prediction-met, not
falsifier-triggered. Write your own prediction AND falsifier to name
early-exterior and late-exterior peaks as explicit outcomes (see
templates/AMENDMENT.skeleton.md and templates/gates.yaml), so your run
cannot land in the same unnamed gap.
- The eff_dim_frac profile is representation-variance PR, not JVP-based.
This instrument's profile applies the participation-ratio formula
directly to captured hidden-state vectors (capture-only, no gradients).
A DIFFERENT instrument in this program (
j-space-localization-qwen3-4b,
qwen35-4b-midband-doubt-snap Stage A) applies a related-looking
effective-dimension metric to corpus-averaged JVP "push" vectors from a
gradient-based J-lens. The two profiles are NOT numerically comparable;
do not read a family-atlas peak layer against a JVP-profile peak layer
from a different instrument as if they measured the same thing. The
family-atlas profile is comparable across this instrument's OWN cells
only.
- Population definitions move AUROCs by ~0.1. "Refused vs known" and
"refused vs pooled-answered" (confab + known) are different contrasts and
read differently -- confab is the harder negative class than known alone.
Name the exact contrast populations in every reported number (this
instrument already does, via the axis names
doubt / caution /
raw_refusal and their contrast: strings in cell.yaml); never compare
an AUROC from this panel to a same-named-sounding number from a different
panel without checking the population definitions match.
- Cost is capture-only and small. The one resolved two-cell run (llama +
mistral, ~6,000 rows combined) came in under $2 on Modal A10G. Full-depth
float32 capture is the expensive-looking but actually cheap part; the
profile and read panel are CPU-only and effectively free. Budget
accordingly before asking for spend approval.
- A read-optimal layer is not thereby a good write site - see
reference/read-actuate-depth.md. Actuability (does a dosed write at a
site change behavior at all) decays steeply with RELATIVE depth
(layer_idx / num_hidden_layers) in every family measured so far,
independent of this atlas's own read-panel band. Convert every site to a
depth fraction before comparing across families, and before attributing a
family's actuation null to its architecture, check whether that family was
even tested at the depth band where other families still actuate.
- A vLLM layer ID is not automatically an atlas hidden-state index. Native
extraction can return selected intermediate layers, but the atlas requires
embeddings at index 0 and every block output through N under the same
normalization convention as the HF reference. Run the full bridge before
adopting vLLM for an established atlas. Disable chunked prefill, capture
prompt states only unless completion states are registered, and fail back to
HF if the peak-location invariant does not bridge.
Gates
Every family-atlas cell gates on three checks, transcribed verbatim from
AMENDMENT.md into gates.yaml (see templates/gates.yaml):
- AG0 (integrity, pre-outcome): capture coverage >= 95% of the cell's
manifest rows at every layer; direction refits byte-identical under the
fixed seed; held-out power floors hold after capture attrition.
- AG1 (profile): eff_dim_frac computed at every hidden-state index;
a 20%-row-subsample reproducibility re-run keeps the peak layer within
+/- 1 layer of the full-sample peak.
- AG2 (read panel): per-layer held-out AUROCs with bootstrap CIs for
all three axes, plus the random-direction control. AG2 carries no
numeric pass/fail threshold of its own; the numbers ARE the atlas. The
prediction/falsifier in
AMENDMENT.md are adjudicated separately at
resolve.
Registry
docs/atlas/family-layer-map.md is the standing table of every family this
program has atlased: substrate, atlas experiment slug and status, profile
peak layer, the band where all three axes clear held-out AUROC >= 0.80, and
provenance citing the governed doc each row came from. Read it before
starting a new atlas (a resolved row for the exact substrate revision means
you don't need to re-run this), and append to it only after your own
amendment resolves -- the registry is a pointer INTO governed docs, never a
second source of experimental fact.
Files
scripts/capture_family_atlas_cell.py -- GPU: full-depth anchor capture
for one cell (capture subcommand).
scripts/profile_and_read_family_atlas_panel.py -- CPU: eff_dim_frac
profile + three-axis read panel + random-direction control for one
captured cell (score subcommand).
scripts/smoke_family_atlas.py -- local CPU smoke against synthetic
captures; run this before trusting either script against a real capture.
templates/cell.yaml, templates/gates.yaml,
templates/AMENDMENT.skeleton.md -- fill-in templates for bin/exp new
scaffolding.
templates/render_example.py -- reference pattern for the per-experiment
render module capture_family_atlas_cell.py --render-module loads;
always ported and adapted per source experiment, never shared verbatim.
reference/read-actuate-depth.md -- the read/actuate depth dissociation:
why a read-panel peak is not thereby a good write site, and how to control
for the resulting depth confound before attributing an actuation null to
architecture.
1---2name: family-atlas3description: Standardize the per-model layer-mapping extraction (full-depth hidden-state capture, workspace-dimension profile, three-axis held-out read panel) so any new model, family, or size entering the Epistemic-Humility program gets a fast, cheap, uniform layer atlas before any actuation design. Use when a new model/family/size enters the program, before designing any per-family actuation cell, or when an existing family's substrate changes (new revision, new checkpoint).4---56# Family Atlas78## Working hypothesis (not a fact)910This program's actuation work rests on a hypothesis, not an established11result: the three read axes this project uses everywhere --1213- **doubt** (known-vs-refused),14- **caution** (refused-vs-confab), and15- **raw refusal** (refused-vs-answered) --1617are assumed to be linearly readable in every instruction-tuned model. WHERE18they read (the layer band) is assumed model/family/size RELATIVE, not a19constant. The corollary: never port a layer or a depth fraction from one20family to another. Run the atlas on the new substrate first; every21per-family actuation amendment consumes that atlas's layer map as an input,22not a guess.2324This hypothesis has already been tested once and partially failed on its own25terms: see `docs/atlas/family-layer-map.md` and the Gotchas section below.26Treat every number this instrument produces as evidence about THIS27hypothesis, not as a foregone conclusion it will confirm.2829## When to use this skill3031- A new model, family, or size enters the program and no atlas exists for it32 yet in `docs/atlas/family-layer-map.md`.33- You are about to design a per-family actuation cell (steering, erase-write,34 gated snap) and need a layer map to consume, not a ported depth fraction.35- An existing family's substrate changed (new HF revision, new checkpoint,36 new quantization) in a way that could move its workspace band.3738Do not use this skill to re-litigate a family that already has a `resolved`39row in the registry with the SAME substrate revision; read that row and its40cited governed doc instead.4142## The standard procedure43441. **Scaffold.** Run `bin/exp new <slug> --type probe-fit` from the repo45 root. Copy `templates/cell.yaml`, `templates/gates.yaml`, and46 `templates/AMENDMENT.skeleton.md` (as `AMENDMENT.md`) into the new47 experiment directory, replacing the generic placeholders `bin/exp new`48 wrote. One cell per substrate; a two-family run (like49 `experiments/jspace-family-atlas`) is two cells in one `cell.yaml`, not50 two experiments.512. **Pools.** Reuse an existing experiment's committed split manifest52 verbatim whenever one exists for this substrate (row pool, baseline53 gradings, role/split assignments, unchanged). Only mine a fresh pool if54 none exists, and mine it per the program's standard roles: `confab`,55 `known_correct_answered`, `unknown_refused`, with split labels `fit`,56 `held_out`, `fit_only`. `profile_and_read_family_atlas_panel.py` reads57 rows by these exact role and split names; a pool using different role58 names needs a mapping layer before this instrument can read it.593. **Select and bridge the capture backend.** Read60 `../experiment-runner/reference/batched-generation.md`. Prefer native vLLM61 extraction for a new atlas only after its model-specific bridge establishes62 exact prompt tokens, state indexing 0 through `num_hidden_layers`, anchor63 identity, normalization convention, numerical agreement, and profile-level64 location agreement against the HF reference. A cell extending an existing65 HF atlas remains HF unless that bridge is pre-stated and passed. If the66 generic tuner lacks vLLM capture, use the HF fallback and record the67 capability gap; do not add an experiment-owned engine.684. **Capture.** Full-depth anchor capture: every hidden state (0 through69 `num_hidden_layers`), final-prompt-token anchor, float32, via70 `scripts/capture_family_atlas_cell.py capture`. The script reads71 `capture.engine` from `cell.yaml`; vLLM mode requires72 `VLLM_BATCH_INVARIANT=1`. Point `--render-module`73 at a copy of the source experiment's own render logic (see74 `templates/render_example.py`) so the anchor position matches that75 experiment's own convention exactly -- this is capture-only, no steering76 hooks, no re-mining, no re-generation.775. **Profile + read panel.** `scripts/profile_and_read_family_atlas_panel.py78 score`: per-layer `eff_dim_frac` (participation-ratio profile) and the79 per-layer two-sided held-out AUROC read panel for doubt / caution /80 raw_refusal, each with a bootstrap CI, plus the standard81 random-direction control (see Gotchas). If the source pool's82 `unknown_refused` role is `fit_only` (no held-out partition at all),83 this script subdivides it deterministically into `refused_fit` /84 `refused_eval` by default; pass `--no-split-refused` if your pool85 already carries real held-out refused rows.866. **Resolve and register.** Adjudicate the prediction/falsifier in87 `AMENDMENT.md`, run `bin/exp sign` / resolve per the `experiments` skill,88 then append one row to `docs/atlas/family-layer-map.md` citing the89 resolved doc. Never add a registry row before its governed doc is signed90 and resolved.9192## Gotchas9394- **Norm/position confound on the doubt axis.** The refused-vs-known95 contrast at the final-prompt-token anchor carries a norm/position96 confound: a FIXED RANDOM direction can read up to ~0.97 best-orientation97 AUROC on this same contrast at some layers (observed in both98 `jspace-family-atlas` cells). Always read the doubt axis's AUROC against99 its own layer's random-direction control value from the same100 `atlas_summary.json`, not against 0.5. Caution (refused-vs-confab) and raw101 refusal (refused-vs-answered) did NOT show this confound in the one102 resolved run (control stayed ~0.5-0.75), but check your own cell's control103 anyway -- do not assume that pattern transfers.104- **The profile can peak early-exterior, not interior.** The first105 family-atlas run's prediction and falsifier both assumed the eff_dim_frac106 profile would either peak inside (20%, 85%) depth (prediction met) or be107 monotone to the last layer (falsifier met). It did neither: llama peaked108 at layer 4 of 28 (0.14 depth) and mistral at layer 3 of 32 (0.09 depth),109 then declined through the midband with a mild late uptick -- an110 early-exterior peak, a shape neither the prediction nor the falsifier111 named. The result landed in a gap: not prediction-met, not112 falsifier-triggered. Write your own prediction AND falsifier to name113 early-exterior and late-exterior peaks as explicit outcomes (see114 `templates/AMENDMENT.skeleton.md` and `templates/gates.yaml`), so your run115 cannot land in the same unnamed gap.116- **The eff_dim_frac profile is representation-variance PR, not JVP-based.**117 This instrument's profile applies the participation-ratio formula118 directly to captured hidden-state vectors (capture-only, no gradients).119 A DIFFERENT instrument in this program (`j-space-localization-qwen3-4b`,120 `qwen35-4b-midband-doubt-snap` Stage A) applies a related-looking121 effective-dimension metric to corpus-averaged JVP "push" vectors from a122 gradient-based J-lens. The two profiles are NOT numerically comparable;123 do not read a family-atlas peak layer against a JVP-profile peak layer124 from a different instrument as if they measured the same thing. The125 family-atlas profile is comparable across this instrument's OWN cells126 only.127- **Population definitions move AUROCs by ~0.1.** "Refused vs known" and128 "refused vs pooled-answered" (confab + known) are different contrasts and129 read differently -- confab is the harder negative class than known alone.130 Name the exact contrast populations in every reported number (this131 instrument already does, via the axis names `doubt` / `caution` /132 `raw_refusal` and their `contrast:` strings in `cell.yaml`); never compare133 an AUROC from this panel to a same-named-sounding number from a different134 panel without checking the population definitions match.135- **Cost is capture-only and small.** The one resolved two-cell run (llama +136 mistral, ~6,000 rows combined) came in under $2 on Modal A10G. Full-depth137 float32 capture is the expensive-looking but actually cheap part; the138 profile and read panel are CPU-only and effectively free. Budget139 accordingly before asking for spend approval.140- **A read-optimal layer is not thereby a good write site** - see141 `reference/read-actuate-depth.md`. Actuability (does a dosed write at a142 site change behavior at all) decays steeply with RELATIVE depth143 (`layer_idx / num_hidden_layers`) in every family measured so far,144 independent of this atlas's own read-panel band. Convert every site to a145 depth fraction before comparing across families, and before attributing a146 family's actuation null to its architecture, check whether that family was147 even tested at the depth band where other families still actuate.148- **A vLLM layer ID is not automatically an atlas hidden-state index.** Native149 extraction can return selected intermediate layers, but the atlas requires150 embeddings at index 0 and every block output through N under the same151 normalization convention as the HF reference. Run the full bridge before152 adopting vLLM for an established atlas. Disable chunked prefill, capture153 prompt states only unless completion states are registered, and fail back to154 HF if the peak-location invariant does not bridge.155156## Gates157158Every family-atlas cell gates on three checks, transcribed verbatim from159`AMENDMENT.md` into `gates.yaml` (see `templates/gates.yaml`):160161- **AG0 (integrity, pre-outcome)**: capture coverage >= 95% of the cell's162 manifest rows at every layer; direction refits byte-identical under the163 fixed seed; held-out power floors hold after capture attrition.164- **AG1 (profile)**: eff_dim_frac computed at every hidden-state index;165 a 20%-row-subsample reproducibility re-run keeps the peak layer within166 +/- 1 layer of the full-sample peak.167- **AG2 (read panel)**: per-layer held-out AUROCs with bootstrap CIs for168 all three axes, plus the random-direction control. AG2 carries no169 numeric pass/fail threshold of its own; the numbers ARE the atlas. The170 prediction/falsifier in `AMENDMENT.md` are adjudicated separately at171 resolve.172173## Registry174175`docs/atlas/family-layer-map.md` is the standing table of every family this176program has atlased: substrate, atlas experiment slug and status, profile177peak layer, the band where all three axes clear held-out AUROC >= 0.80, and178provenance citing the governed doc each row came from. Read it before179starting a new atlas (a resolved row for the exact substrate revision means180you don't need to re-run this), and append to it only after your own181amendment resolves -- the registry is a pointer INTO governed docs, never a182second source of experimental fact.183184## Files185186- `scripts/capture_family_atlas_cell.py` -- GPU: full-depth anchor capture187 for one cell (`capture` subcommand).188- `scripts/profile_and_read_family_atlas_panel.py` -- CPU: eff_dim_frac189 profile + three-axis read panel + random-direction control for one190 captured cell (`score` subcommand).191- `scripts/smoke_family_atlas.py` -- local CPU smoke against synthetic192 captures; run this before trusting either script against a real capture.193- `templates/cell.yaml`, `templates/gates.yaml`,194 `templates/AMENDMENT.skeleton.md` -- fill-in templates for `bin/exp new`195 scaffolding.196- `templates/render_example.py` -- reference pattern for the per-experiment197 render module `capture_family_atlas_cell.py --render-module` loads;198 always ported and adapted per source experiment, never shared verbatim.199- `reference/read-actuate-depth.md` -- the read/actuate depth dissociation:200 why a read-panel peak is not thereby a good write site, and how to control201 for the resulting depth confound before attributing an actuation null to202 architecture.