§1 — Why this plan exists

The independent-review-fixes plan shipped ISNC v0.2.0rc60: the SPA filter fix, the polarization rebuttal + convention note, per-field notation (Unicode Greek symbols in descriptions), the four generation-layer guards for orientation vectors, the x, y, z Cartesian-triple axis decision, and the awakened semantic checks. This plan carries the residual from that work plus one design question the lead reopened (cylindrical axis naming). It exists as a separate plan so the shipped work can close and the next session starts clean.

✓✓ PLAN SHIPPED — v0.2.0rc61 (2026-07-06)

All sections landed at the generate level (new names benefit, not just live edits) and released as ISNC v0.2.0rc61 (2124 names). §2 dead axis aliases removed + compose catch-and-promote (ISN f57f2f0, imas-codex de264aa1); §3 reset path-scoping + accepted-wipe guard (ae585857, 89 tests); §4 all 9 orientation-vector families composed via the unit-vector-locus compose fix (1b83fe04); §5 strain-rosette measurement-axis grammar (cec56a7, first/second carriers + strain_gauge locus); §6 dd_path_uniqueness → 0; §7 generate-level LaTeX-description normalizer + import-normalize + critical gate (27f4c037, 381 tests) + E− 1/√2 fix. Final audits: dd_path_uniqueness_check=0, vector_family_consistency_check=0, 0 LaTeX descriptions, raw accepted 2425. imas-codex ISN pin bumped to cec56a7 (01ba7f68).

Ground-truth reconciliation (2026-07-05, live codex graph, read-only audit before implementation). The authored §4/§6/§7 counts were partly stale; the live graph corrects them:

Prerequisite canonical-qualifier-order is impl 0.99 (grammar finalized, rc57 released); its open followups gate none of §2/§3/§4/§6/§7, so this is NOT blocked. §5 (direction_second ordinal) stays deferred. Order: §2/§3 (code, parallel) → §6 → §7 → §4 (graph work, serialized on the shared live graph). Fixes land at the GENERATE level so new names benefit — not just live edits on existing names.

✓ landed 2026-07-05 §2 — DECISION: cylindrical axis-token naming (radial/toroidal/vertical vs r/phi/z)

Decision A implemented as strictly one way, at the GENERATE level. ISN f57f2f0 emptied the dead poloidal/radial/toroidal alias lists in coordinate_axes.yml (parser loads keys only — zero consumers confirmed; full ISN suite 1634 green). imas-codex de264aa1 added the sanctioned generation-time catch-and-promote in GrammarSegments._validate_projection_axis: a stray r→radial, phi/tor→toroidal, pol→poloidal is coerced to the canonical word before composition (z excluded — a valid Cartesian axis; frame disambiguation is a DD-ingest concern). The ISN parser stays strict and still rejects r_… names (test-proven), so there is no dual path — one canonical form. Record: §2 landed.

Lead question: given the Cartesian family is x, y, z, should the cylindrical machine axes be r, phi, z for symmetry, rather than the current words radial, toroidal, vertical?

Current scheme (after rc60)

FrameHorizontal axesThird (machine-vertical) axisWhat the tokens are
Device-local Cartesianx, yz Arbitrary mounting axes — you cannot say which physical direction x points without the device's survey
Machine cylindrical (R, φ, Z)radial, toroidalvertical Physically named directions — major-radius outward, around the torus, machine up
Plasma / field directionspoloidal, parallel, perpendicular, normal, tangential, binormal (+ normalized_*) Physically named directions — no natural single-letter form exists

The organizing principle already in place is: letters name arbitrary local axes; words name physically-meaningful directions. This is the CF-standard-names convention (eastward, upward) and it is why z (Cartesian, arbitrary) and vertical (machine, physical) are distinct tokens for the same physical axis — the token also encodes which frame the name lives in, which is exactly the anti-collapse property the rc60 work was built around (no token appears in two frames). Catalog usage today: 49 vertical_*, 21 radial_*, 11 toroidal_*, 5 poloidal_*, and 5+5+1 Cartesian x/y/z_*.

Options

Option A — keep words for physical directions (RECOMMENDED). No change: x, y, z for device-local Cartesian, radial, toroidal, vertical (and poloidal/parallel/…) for physical directions. Pros: coherent organizing principle; frame-marked (preserves the collapse-guard); self-documenting; honors the D.3 senior review (2026-04) that ruled toroidal_component canonical and declared velocity_phi forbidden, velocity_tor deprecated; zero churn (just shipped). Con: the shared vertical axis carries two tokens (z / vertical), and the letter-vs-word split between frames looks asymmetric on the surface.

Option B — rename cylindrical to r/phi/z for symmetry (NOT recommended). Pros: superficial 3-token parallelism with x, y, z. Cons, decisive: (1) reintroduces z into the machine frame, so z lives in both frames again — weakening the frame-marking/collapse-guard the whole rc60 effort established; (2) only 3 of the ~9 physical directions have letters — poloidal, parallel, perpendicular, normal, tangential, binormal do not — so you get r, phi, z beside poloidal, parallel, …, a worse mix than today; (3) reverses the D.3 senior ruling; (4) phi as a direction token collides conceptually with the toroidal angle φ we just standardized in descriptions; (5) ~81 catalog names churn plus prompt/grammar/doc changes.

On "Option C" and the one-way principle

The catalog is already one way — there is no dual path today. Every stored name is emitted by compose_standard_name, which always uses the canonical word; the ISN parser accepts only canonical tokens (the round-trip invariant); and DD-sourced names are normalised at ingest by codex's _SUFFIX_TO_AXIS map (DD leaf rradial, phitoroidal, zvertical/z by frame) — a required boundary translation, not a design fallback. The only artefact is the dead alias metadata in coordinate_axes.yml (radial:[r], toroidal:[phi, tor], poloidal:[pol]): the parser never consumes it (verified — it loads frozenset(axes_reg.axes), the keys only), so it is misleading config that implies a second vocabulary that does not actually function.

"Option C" hides two very different implementations — only one is acceptable.

Recommendation: A, expressed as strictly one way. Keep words for physical directions, x/y/z for arbitrary local axes. Delete the dead alias metadata so no second vocabulary is even implied (f-cnf-005). Keep the DD-ingest funnel (a boundary normaliser, not a design fallback). A stray axis token is then either hard-failed at generation (loudest signal, forces regeneration) or caught by an optional C-funnel promotion — both keep exactly one canonical form. B is rejected: it cannot deliver symmetry (poloidal/parallel/normal/… have no letters), re-collides z across frames, reverses the D.3 senior ruling, and conflates the phi direction with the φ angle.

✓ landed 2026-07-05 §3 — Tooling: sn run --reset-to path scoping + guard

Scope-and-guard landed (imas-codex ae585857). A path_allowlist parameter (exact-path src.id IN allowlist, distinct from the ids_filter prefix) was added to reset_standard_names + clear_standard_names, and --focus is wired into both reset branches so sn run --reset-to {extracted,drafted} --focus <paths> now resets EXACTLY those dd paths (clear uses relationship-first delete, so names on any other path — accepted included — survive). A CLI guard (_reject_unscoped_accepted_reset) hard-errors a bare --reset-to --include-accepted with no focus/narrowing-filter — the exact 1863-accepted-wipe footgun — while a row-level filter (--retry-quarantined/--below-score/…) still counts as scope so the documented quarantine migration works; dry-run exempt. 89 tests green (13 new, all mocked GraphClient — the production graph was never touched). Full record: §3 landed. §4 rotations use --reset-to drafted --focus.

✓ landed 2026-07-06 §4 — Orientation-vector family completion

All 9 orientation families completed (x/y/z accepted + documented): direction unit vectors of infrared_camera, electron_cyclotron_launcher_mirror, sensor→strain_gauge (first measurement axis), shatter_cone, neutron_detector, pellet_injector; camera image-up; shatter-cone minor-axis; + the 3 z-axis re-locks. shatter_cone direction ruled distinct from the injector per the DD ("central axis of the shatter cone" vs "velocity direction of the pellet"). vector_family_consistency_check = 0, dd_path_uniqueness_check = 0. The enabling generate-level fix (1b83fe04) is below; details in the card.

GENERATE-LEVEL FIX + 6 families drained. Root cause was a real composer defect: the cross-IDS semantic cluster ("Instrument Direction Component") instructed the composer to emit ONE generic locus-less name across devices, which violates the grammar's unit-vector-locus rule (error severity) — so these leaves composed an invalid y_direction_unit_vector (→ exhausted) or refine over-specified to a wrong device. imas-codex 1b83fe04 adds a compose-prompt rule: a *_unit_vector base ALWAYS takes a per-device _of_<device> locus and OVERRIDES any cross-IDS cluster generic-name suggestion. Two enabling blockers also fixed: attempt-exhaustion (parked sources sat at attempt_count 5–7 > max_attempts=3 → reset to extracted/0), and the stale DSv4 401 (use the configured OpenRouter compose model, not the dead free endpoint).

Drained via the fix (18 accepted per-device names, dd_path_uniqueness=0): {x,y,z}_direction_unit_vector_of_electron_cyclotron_launcher_mirror, …_of_camera, …_of_sensor; {x,y,z}_minor_axis_unit_vector_of_shatter_cone; and the 3 z-axis re-locks accepted + their sourceless vertical_ counterparts superseded (z_coordinate_of_{divertor_target,ferritic_element_centroid,sensor_attachment_point}) — clearing 2 of the 3 residual vector_family_consistency_check flags.

Characterised residual (per-name physics curation, tracked in f-cnf-006): (a) neutron_diagnostic/.../direction_to_detector is unit='m' — a displacement per voxel, NOT a unit vector — so it needs a position/displacement base, not direction_unit_vector (composed to drafted, not accepted); (b) camera/up/x (image-up family, tangled with the accepted z_direction_of_camera on up/z); (c) spi/injector/shatter_cone/direction — the composer reuses _of_pellet_injector (SPI shatter cone opens along the injection axis); left parked pending a ruling on whether it shares that vector or needs a distinct _of_shatter_cone name; (d) the 1 remaining vector_family flag is a pre-existing frame question — the audit reads spi/injector/injection_direction as cylindrical (wants vertical) while the released-catalog names are Cartesian x/y/z; needs a DD-docstring physics ruling, not a code change. direction_second stays deferred to §5.

rc60 shipped the correct replacements and parked the rest as visible vocab_gap StandardNameSource nodes rather than shipping wrong names. The parked paths are all device-local Cartesian orientation vectors, so they take x, y, z triples with a device locus and are independent of the §2 cylindrical decision:

Execution note: pooled sn run proved unreliable here (re-drafted accepted catalog names, burned budget without claiming hint edits). Drive supervised rotations; verify each against the vector-node family audit before accepting; re-check accepted catalog names' name_stage after every rotation. Best done after §3's reset scoping lands so the family can be reset safely.

◐ Diagnosed 2026-07-05 — key unblock found; remains supervised curation. A live-graph audit corrected the scope and pinned the real blocker:

Prerequisite before any further §4 rotations: HARDEN the imas-codex composer so device-orientation component leaves compose to the correct <axis>_..._unit_vector_of_<locus> name for THEIR device (no cross-attach to another device's existing name), then per family: reset the sources (status='extracted', attempt_count=0) → sn run --focus "<family leaf paths>" with the CONFIGURED OpenRouter compose model (not the 401'ing DSv4) → verify the composed name's locus matches the source device + the family audit is clean BEFORE accepting. Non-deferred set: 9 leaves (camera direction+up, ec-launcher mirror, sensor direction, shatter_cone direction/minor-axis, neutron detector) + the 3 z-relocks; sensor/direction_second stays deferred to §5.

✓ landed 2026-07-06 §5 — direction_second ordinal (strain-rosette measurement axes)

The DD resolved the semantics: operational_instrumentation/sensor/direction + direction_second are a strain-rosette gauge's ordered measurement axes (multi-axial strain decomposition) — NOT an orthonormal cross-product frame. Lead rejected a bare secondary (meaningless) and required both axes encoded symmetrically as strain-rosette axes (decision direction-second-naming = Option A). Landed ISN cec56a7: two atomic geometry carriers first_measurement_direction_unit_vector / second_measurement_direction_unit_vector + a strain_gauge locus, axis-leading per the canonical component-outermost order (axis-inside was rejected as non-canonical — it would break that invariant catalog-wide; the ordinal is baked into the carrier because the projection grammar has no qualifier slot). Applied: primary {x,y,z}_direction_unit_vector_of_sensor renamed to {x,y,z}_first_measurement_direction_unit_vector_of_strain_gauge (old superseded), and direction_second{x,y,z}_second_measurement_direction_unit_vector_of_strain_gauge — all accepted with documentation. Released in v0.2.0rc61.

✓ landed 2026-07-05 §6 — dd-path dedup residual

dd_path_uniqueness_check → 0 over the 2440-name accepted corpus (was 2, not the plan's estimated 1). Root cause was a single source_paths list PROPERTY over-attach — not an edge: x_direction_unit_vector_of_pellet_injector.source_paths listed all three spi/injector/injection_direction/{x,y,z} leaves while the HAS_STANDARD_NAME edges were already clean 1:1. Trimmed the property to [/x] (surgical, reversible; dry-run-verified before/after/delta==2). The 6 expected momentum base-carrier inconsistencies DID NOT EXIST (0 in the live corpus — already resolved via a superseded normalized_momentum_flux rename). Durable with no re-export: source_paths is neither exported to the ISNC YAML nor written on import (export.py:512-514, catalog_import.py). The 2 residual vector_family_consistency_check flags are the sensor vertical→z re-lock, folded into §7. Full record: §6 landed.

✓ landed 2026-07-05 §7 — open doc edits

Generate-level LaTeX-description fix SHIPPED + the 9 outliers cleaned (the heart of §7). Root cause found: the Greek normalizer's \b(phi|theta|rho)\b regex matched phi inside \phi, rewriting it to and leaving the $ delimiters, while nothing rejected LaTeX in the short description field. imas-codex 27f4c037 (26 new + 355 regression tests green): normalize_description_notation now converts \phi/\theta/\rho/\pi (+ the stranded corruption) to Unicode, strips $…$/$$…$$ keeping inner content, flattens \mathbf{}/braced sub-superscripts; import normalizes descriptions at both boundaries (so a stale YAML can't regress on sn import); and a new critical description_notation_check quarantines any $/backslash in a description so LaTeX can never reach accepted. All 9 accepted descriptions normalized in-graph → description_notation_check = 0 over 2440 names. Remaining (pipeline rotations, in progress): the E− 1/√2 formula fix (documentation corrected in-graph via sn edit --docs, awaiting a docs-review pass to promote) and the 3 drafted z_coordinate_of_* axis re-locks (folded into §4's rotation — promote the drafted z_, supersede the stranded sourceless vertical_). x_image_up_unit_vector_of_camera reassigned to §4 (its family completion). Full record: §7 landed.

§ Decisions

Cylindrical/physical axis tokens: keep canonical words, or switch to r/phi/z? (§2)

Locked A on lead steer (2026-07-05): keep radial/toroidal/vertical for physical directions, x/y/z for arbitrary local axes — one canonical form. Letters name arbitrary local axes; words name physical directions (CF convention, D.3 senior ruling, frame-marking/collapse-guard). B rejected: cannot deliver symmetry (poloidal/parallel/normal/… have no letters), re-collides z across frames, reverses D.3, conflates the phi direction with the φ angle. One way is preserved WITHOUT a dual path: compose emits canonical, the parser stays strict, the DD funnel normalises at ingest; the dead coordinate_axes.yml aliases are deleted (f-cnf-005). Any r/phi tolerance is a generation-time catch-and-promote error-catcher only, never a parser-level second spelling.

How to fix sn run --reset-to ignoring --focus? (§3)

Locked as the executable default (reversible): path scoping makes focused resets actually work, and hard-erroring the --reset-to + --focus/--include-accepted combo blocks the accepted-name-wipe footgun that cost a full recovery cycle. Reopen for guard-only if the path-scoping investment is not wanted; lead did not object.

direction-second-naming

§5 direction_second is a strain-rosette gauge's ordered measurement axes (DD: 'Second measurement direction... strain rosette gauge... decomposing multi-axial mechanical strain'), NOT an orthonormal cross-product frame. Lead rejected a bare 'secondary' (meaningless) and required both axes encoded symmetrically as strain-rosette axes. Option A locked: atomic geometric carriers first_measurement_direction_unit_vector / second_measurement_direction_unit_vector + strain_gauge locus, axis-leading per the canonical component-outermost order (x_first_measurement_direction_unit_vector_of_strain_gauge). Axis-inside (first_measurement_direction_x_unit_vector) was rejected as non-canonical — it would break the component-outermost invariant catalog-wide. Ordinal baked into the carrier (projection grammar has no qualifier slot). ISN cec56a7.

§ Followups

§3 — Fix sn run --reset-to path scoping + guard the dangerous combo

Thread a dd-path allowlist into reset_standard_names/clear_standard_names and wire --focus so a focused reset scopes to exactly those paths; hard-error --reset-to combined with --focus/--include-accepted where scoping is not honoured. Independent of the axis decision — good first shippable.
Project: imas-codex
Plan:    catalog-naming-followups (http://localhost:8765/imas-standard-names/catalog-naming-followups.html)
Section: §3
Tier:    opus

Context
  sn run --reset-to ... --focus <paths> ignores --focus for the reset and cleared 1863 accepted
  names graph-wide during the rc60 session. Honour the reset-focus-fix decision shown above.

State to read
  imas-codex imas_codex/standard_names/graph_ops.py reset_standard_names (~L4402) +
  clear_standard_names (~L4593); imas_codex/cli/sn.py --reset-to branch (~L1471) and the
  --focus seeding block (~L1511); memory sn-reset-ignores-focus-incident.

Scope locks / constraints
  imas-codex only. Add an explicit path-allowlist parameter; do not widen ids_filter semantics.
  TDD: a test that a focused reset touches only the listed paths, and that the guarded combo raises.

Done-when
  1. Focused reset scopes to exactly the given dd paths; guarded combo errors; tests green
  2. Committed + pushed; outcome recorded; this followup resolved

§3 landed — imas-codex ae585857. path_allowlist (exact-path src.id IN allowlist) added to reset_standard_names + clear_standard_names; sn run --reset-to {extracted,drafted} --focus now scopes reset to exactly those dd paths; clear uses relationship-first delete so names on other paths survive. Guard _reject_unscoped_accepted_reset hard-errors a bare --reset-to --include-accepted with no focus/narrowing-filter (the 1863-accepted-wipe footgun), dry-run exempt; a row-level filter (--retry-quarantined/--below-score/--since/--before/--tier) counts as scope so the documented quarantine migration still works. 89 tests green (13 new), all mocked GraphClient — production graph untouched. §4 rotations will use --reset-to drafted --focus.

§2 — Delete the dead axis aliases; keep one way

Remove the unused radial:[r] / toroidal:[phi,tor] / poloidal:[pol] alias declarations from coordinate_axes.yml so the config stops implying a second vocabulary. Verify no consumer reads them first (parser loads keys only — confirmed; the SPA search ALIASES and codex _SUFFIX_TO_AXIS are separate). Optionally add a generation-time catch-and-promote (stray r/phi axis segment → canonical word before composition, like the projection_shape coercion) as an error-catcher; do NOT make the parser accept r_… as a valid name.
Project: imas-standard-names (+ imas-codex if adding the generation catcher)
Plan:    catalog-naming-followups (http://localhost:8765/imas-standard-names/catalog-naming-followups.html)
Section: §2
Tier:    sonnet

Context
  Axis decision locked A (words canonical, one way). The coordinate_axes.yml aliases are dead metadata
  (parser consumes keys only) that misleadingly imply a second spelling. Honour the one-way principle:
  no parser-level dual path; a generation-time promotion is an acceptable error-catcher only.

State to read
  imas_standard_names/grammar/vocabularies/coordinate_axes.yml (alias lists);
  imas_standard_names/grammar/parser.py (axes load); grep the repo for any .aliases consumer on axes.
  For the optional catcher: imas-codex GrammarSegments._validate_projection_axis + the projection_shape
  coercion precedent (models.py).

Scope locks / constraints
  Delete only after confirming zero consumers. Round-trip must stay green (2117+ names). If adding the
  catcher, it maps a stray token to canonical BEFORE composition; the ISN parser stays strict.

Done-when
  1. Dead aliases removed; grammar regenerated; full ISN suite + catalog round-trip green
  2. (optional) generation catch-and-promote added with a test; committed + pinned
  3. Outcome recorded; this followup resolved

§2 landed at the GENERATE level. ISN f57f2f0: emptied the dead poloidal/radial/toroidal alias lists in coordinate_axes.yml (parser loads keys only — zero consumers confirmed; loader test updated; full ISN suite 1634 green; codegen no-op). imas-codex de264aa1: sanctioned generation-time catch-and-promote in GrammarSegments._validate_projection_axis — stray r→radial, phi/tor→toroidal, pol→poloidal coerced to canonical before composition (z excluded as valid Cartesian axis); ISN parser stays strict, still rejects r_… names (test-proven). One canonical form, no dual path. Record: archive/catalog-naming-followups-s2-landed.html.

§4 — Complete the parked orientation-vector families (supervised)

Drive the ~20 parked vocab_gap direction-vector paths to accepted locus-qualified x/y/z names, one supervised rotation at a time, verifying each against the vector-node family audit. Excludes direction_second (blocked on §5).
Project: imas-codex
Plan:    catalog-naming-followups (http://localhost:8765/imas-standard-names/catalog-naming-followups.html)
Section: §4
Tier:    opus

Context
  rc60 shipped correct replacements and parked the rest as vocab_gap sources. These are device-local
  Cartesian orientation vectors → x,y,z triples with a device locus; independent of the §2 decision.

State to read
  Graph: StandardNameSource status=vocab_gap for camera_ir / ec_launchers / operational_instrumentation
  (direction, NOT direction_second) / spi shatter_cone / neutron_diagnostic direction paths (list in §4).
  audits.py vector_family_consistency_check; memory sn-pipeline-rotation-traps + sn-reset-ignores-focus-incident.

Scope locks / constraints
  Honour locked decisions (z-in-cartesian-families, locus-required). SUPERVISED rotations only —
  pooled runs re-draft accepted names and skip hint edits. Prefer §3's scoped reset once it lands;
  otherwise surgical per-path. Verify the family audit is clean and re-check accepted names' name_stage
  after each rotation. Never sn run --reset-to with --include-accepted.

Done-when
  1. Each parked path carries an accepted locus-qualified x/y/z name with docs; family audit clean
  2. Outcome recorded; this followup resolved (spawn per-device sub-followups if it needs splitting)

§4 DIAGNOSED + unblocked at the grammar level; remains supervised per-family curation (re-opened as f-cnf-006 with the ready-to-drain runbook). Findings: (1) the device loci ALREADY EXIST (ISN 18b8465: sensor/pellet_injector/shatter_cone/camera/neutron_detector/electron_cyclotron_launcher_mirror) — the vocab_gap parking is STALE (parked before the loci existed, never re-run). (2) The real remaining gaps are base-token mis-proposals (composer used direction_to_detector / shatter_cone_direction_vector / unit_vector_component as BASES instead of the existing unit_vector geometric base); the accepted sibling x_direction_unit_vector_of_pellet_injector proves the _direction_unit_vector_of_ pattern, so a supervised rotation with sibling neighborhood can land them. (3) Bulk --focus is UNSAFE + ineffective here: 2 attempts returned no_eligible_work ($0) — the compose pool won't reclaim parked sources without clearing the HAS_STANDARD_NAME_VOCAB_GAP links + status='extracted' reset per family; and --focus force-reset re-staged 2 accepted names via the tangled SPI/shatter_cone/camera source_paths cross-wiring (z_direction_unit_vector_of_pellet_injector, vertical_coordinate_of_camera) — both RESTORED to accepted, verified (dd_path_uniqueness=0, 0 LaTeX preserved). Drive one family at a time. Neutron family pre-prepped (gap links cleared, sources extracted).

§6 — Apply the dd-path dedup verdicts

Resolve the 1 remaining duplicate path + 6 momentum-family base-carrier inconsistencies per the archived classification; dd_path_uniqueness_check must return zero over accepted names.
Project: imas-codex
Plan:    catalog-naming-followups (http://localhost:8765/imas-standard-names/catalog-naming-followups.html)
Section: §6
Tier:    opus

Context
  86/90 double-attached paths were classified as attach errors; most were fixed by rc60's restore.
  Residual: 1 dup + 6 edge_sources/plasma_sources momentum base-carrier disagreements.

State to read
  docs/archive/independent-review-fixes-dup-paths-classification.html (per-path verdicts);
  imas-codex audits.py dd_path_uniqueness_check + vector_family_consistency_check.

Scope locks / constraints
  Follow the archived verdict for each path (keep the specific name, detach the generic). C-bucket
  (both-wrong) paths need a corrected name via the pipeline, not just a detach.

Done-when
  1. dd_path_uniqueness_check and the momentum families in vector_family_consistency_check return clean
  2. Outcome recorded; this followup resolved

§6 landed — dd_path_uniqueness_check 2→0 over 2440 accepted names. Ground truth corrected the estimate: 2 violations (not 1), both from ONE name's stale source_paths LIST PROPERTY over-attach (not an edge) — x_direction_unit_vector_of_pellet_injector.source_paths listed injection_direction/x,y,z while the HAS_STANDARD_NAME edges were already clean 1:1. Trimmed the property to [/x] (surgical, dry-run-verified before/after/delta==2). The 6 expected momentum base-carrier inconsistencies DID NOT EXIST (0 — already resolved via a superseded normalized_momentum_flux rename). Durable, no re-export: source_paths is neither exported to ISNC YAML nor written on import (export.py:512-514, catalog_import.py). Residual 2 vector_family_consistency_check flags = sensor vertical→z re-lock, folded into §7. Record: archive/catalog-naming-followups-s6-landed.html.

§7 — Drain the open doc edits (supervised)

Land the 9 LaTeX-in-description edits, the E− 1/√2 formula fix, and the x_image_up_unit_vector_of_camera docs review — via a supervised review rotation, or by converting the hint edits to --docs redesign edits that enter review directly.
Project: imas-codex
Plan:    catalog-naming-followups (http://localhost:8765/imas-standard-names/catalog-naming-followups.html)
Section: §7
Tier:    sonnet

Context
  These are open sn edit hint proposals pooled runs won't claim (hint edits on accepted names).

State to read
  Graph: sn.edit_status='open' entries; the 9 LaTeX-description names + right_hand_circularly_polarized_
  wave_electric_field (E−) + x_image_up_unit_vector_of_camera (docs_stage). memory sn-pipeline-rotation-traps.

Scope locks / constraints
  Descriptions plain-text with Unicode Greek symbols; documentation LaTeX. Supervised only. Consider
  --docs redesign edits (enter review directly) if the hint edits keep stalling.

Done-when
  1. All open doc edits applied and re-accepted; no LaTeX left in any accepted description
  2. Outcome recorded; this followup resolved

§7 landed. GENERATE-LEVEL root cause fixed (imas-codex 27f4c037, 26+355 tests green): the Greek normalizer's \b(phi|theta|rho)\b matched inside \phi (→\φ) and never stripped $; import stored descriptions verbatim; no gate rejected LaTeX in description. Now: normalizer converts \phi→φ + strips $…$/backslash-commands (idempotent, documentation untouched), import normalizes at both boundaries, and a new critical description_notation_check quarantines any $/backslash in a description. Applied to live graph: 9 LaTeX descriptions normalized → description_notation_check = 0 over the accepted corpus; E− physics error fixed (E_- = (E_x - i E_y)/√2, matching sibling E+; accepted/applied); 13 open edits cleared to applied (9 desc + 2 doc-math verified-clean + 2 moot superseded). x_image_up_unit_vector_of_camera + the 3 drafted z_coordinate_* axis re-locks moved to §4 (orientation-vector family completion). Record: archive/catalog-naming-followups-s7-landed.html.

§4 residual — drain the orientation-vector families one at a time (supervised)

9 non-deferred device-orientation leaves + 3 z-axis re-locks remain uncomposed. The grammar is READY (loci exist); the blocker is per-family: clear the stale vocab-gap links, reset sources to extracted, and run a supervised single-family rotation so the composer uses the existing unit_vector base (not a mis-proposed base) with sibling neighborhood context. Do NOT bulk-focus the tangled SPI/shatter_cone/camera cluster — --focus force-reset re-stages cross-linked accepted names. direction_second stays deferred to §5.
Project: imas-codex (+ imas-standard-names grammar)
Plan:    catalog-naming-followups §4 residual
Tier:    opus

Context
  §4 is grammar-ready: the device loci (sensor, pellet_injector, shatter_cone, camera/infrared_camera,
  neutron_detector, electron_cyclotron_launcher_mirror) exist. The parked vocab_gap markers are stale (set
  before the loci existed). The real gaps were base-token mis-proposals — the composer must use the existing
  `unit_vector` geometric base, per the accepted sibling x_direction_unit_vector_of_pellet_injector.

Graph writes: ONLY from a session the auto-mode classifier permits (main session or a granted rule) — subagents are
blocked. NEVER `sn run --reset-to --include-accepted` without scope (the §3 guard hard-errors it). Use `uv run --no-sync`.
DSv4 compose/refine is free: IMAS_CODEX_SN_{COMPOSE,REFINE}_MODEL=hosted_vllm/deepseek-v4-flash,
_API_BASE=http://98dci4-gpu-0003:18800/v1, AMBIX_API_KEY= .

Per-family runbook (one family at a time; the neutron family is already pre-prepped):
  1. For the family's leaf dd paths: DELETE the HAS_STANDARD_NAME_VOCAB_GAP links from those IMASNodes; set the
     matching StandardNameSource.status='extracted' (clear skip_reason/claim/run_id).
  2. `sn run --focus "<the family's leaf paths>"` on DSv4 (small -c). Verify it does NOT reset any ACCEPTED
     sibling (check name_stage of x_/y_ siblings before+after — the SPI/camera cluster cross-wires source_paths).
  3. Confirm the composed name matches the <axis>_direction_unit_vector_of_<locus> sibling pattern and
     vector_family_consistency_check is clean for that node; then accept. If it re-gaps on a base, add/confirm
     the base token in ISN grammar (do NOT accept a mis-based name).
  4. z-relocks (divertor_target, ferritic_element_centroid, sensor_attachment_point): promote the drafted
     z_coordinate_of_X and supersede the stranded sourceless vertical_coordinate_of_X; clears the 2 residual
     vector_family_consistency_check flags. Beware recompose-divergence (the z_ draft's source path locus differs
     from the family name) — promote the existing correct z_ name rather than recomposing.

Families: camera direction (x,y,z), camera up (x,z; y accepted), ec_launcher mirror direction (x,y,z),
sensor direction (x,y,z), shatter_cone direction (x,y,z), shatter_cone minor-axis (x,z; y accepted),
neutron_detector direction_to_detector (x,y,z, pre-prepped), + the 3 z-relocks. x_image_up_unit_vector_of_camera
is part of the camera-up family.

Done-when
  1. Each family's leaves carry accepted <axis>_..._unit_vector_of_<locus> names; vector_family_consistency_check = 0.
  2. dd_path_uniqueness_check stays 0; no accepted name regressed.
  3. Re-export the ISNC catalog + confirm; outcome recorded; this followup resolved.

§4 LARGELY LANDED via a generate-level composer fix. Root cause: the cross-IDS semantic cluster instructed a locus-less generic unit-vector name, violating the grammar's unit-vector-locus rule (error). imas-codex 1b83fe04 adds a compose-prompt rule: *_unit_vector bases ALWAYS take a per-device _of_ locus, overriding cluster generic-name suggestions. Enabling blockers also fixed: parked sources were attempt-exhausted (attempt_count 5-7 > max_attempts 3 → reset to extracted/0), and DSv4 now 401s (use configured OpenRouter compose, not the dead free endpoint). DRAINED (18 accepted per-device names, dd_path_uniqueness=0, raw accepted 2404→2415): {x,y,z}_direction_unit_vector_of_{electron_cyclotron_launcher_mirror,camera,sensor}; {x,y,z}_minor_axis_unit_vector_of_shatter_cone; and the 3 z-axis re-locks accepted + their sourceless vertical_ superseded (z_coordinate_of_{divertor_target,ferritic_element_centroid,sensor_attachment_point}) — clearing 2 of 3 vector_family flags. RESIDUAL → f-cnf-007 (per-name physics curation): neutron direction_to_detector is unit='m' (displacement, not a unit vector); camera/up/x (image-up, tangled); shatter_cone/direction (composer reuses _of_pellet_injector — needs a share-vs-distinct ruling); and 1 pre-existing vector_family flag (SPI injection_direction Cartesian-vs-cylindrical frame question, DD-docstring ruling). No accepted name regressed; all cross-attach corruption from exploratory runs cleaned + verified.

§4 tail — per-name physics curation for the 4 residual orientation cases + ISNC re-export

The generate-level composer fix (1b83fe04) drained 6 orientation families. Four residual cases need per-name physics judgment, not a mechanical rotation: (1) neutron direction_to_detector is unit='m' — a displacement/position vector, so it needs a position base, NOT direction_unit_vector; (2) camera/up/x image-up family (tangled: up/z carries the accepted z_direction_of_camera); (3) spi/injector/shatter_cone/direction — rule whether it shares the injector's direction (_of_pellet_injector) or needs a distinct _of_shatter_cone name; (4) the 1 remaining vector_family flag: the audit reads spi/injector/injection_direction as cylindrical (wants vertical) while the released-catalog names are Cartesian x/y/z — a DD-docstring frame ruling. Then generate docs for the newly-accepted §4 names (composed name-only) and re-export/release the ISNC catalog to publish §6/§7/§4 graph fixes.
Project: imas-codex (+ imas-standard-names grammar)
Plan: catalog-naming-followups §4 tail
Tier: opus

The compose-prompt fix (imas-codex 1b83fe04: *_unit_vector requires a per-device locus, overriding cross-IDS cluster generic-name suggestions) is landed and validated — 6 families drained (ec_launcher, camera-direction, sensor-direction, shatter-minor + the 3 z-relocks; 18 accepted names, dd_path_uniqueness=0). Finish the 4 residual cases, each needs a physics call (not just a rotation):

1. NEUTRON direction_to_detector/{x,y,z}: unit='m' (FLT_3D 'components per voxel') — this is a displacement/position vector to the detector, NOT a unit vector. Decide the base (position / displacement coordinate) and compose per-device (_of_neutron_detector). Do NOT accept a *_unit_vector name for a unit=m quantity. Currently drafted x/y/z_direction_unit_vector_of_neutron_detector — supersede/recompose with the correct base.
2. CAMERA up/x (+ up/z): image-up unit vector family (sibling y_image_up_unit_vector_of_camera accepted). up/z currently carries the accepted z_direction_unit_vector_of_camera (a direction name on the up path — a cross-attach to untangle). Compose x_/z_image_up_unit_vector_of_camera; ensure direction vs image-up are distinct per-vector bases.
3. SPI shatter_cone/direction/{x,y,z}: the composer reuses _of_pellet_injector (SPI shatter cone opens along the injection axis). RULE whether that is physically correct (share the injector direction) or the shatter cone needs a distinct _of_shatter_cone name; then attach/compose accordingly. Currently parked (detached to avoid the family-audit flag).
4. SPI injection_direction frame flag: vector_family_consistency_check flags injection_direction/z wanting 'vertical' (cylindrical) while the accepted names are Cartesian x/y/z_direction_unit_vector_of_pellet_injector (released catalog). Read the DD docstring for spi/injector/injection_direction — if cylindrical, migrate to radial/toroidal/vertical; if Cartesian, adjust the audit's frame classification for this node. Do not guess.

Mechanics: graph writes from a session the classifier permits (main session; subagents are blocked). Reset parked sources to status='extracted'/attempt_count=0 before a --focus rotation; use the CONFIGURED OpenRouter compose model (DSv4 401s); --skip-clear-gate (the prompt hash changed); verify no accepted sibling is re-staged (check PRODUCED_NAME + HAS_STANDARD_NAME cross-wiring in the SPI/camera cluster) and clean any spurious cross-device edges. THEN: docs pass for the name-only §4 names; sn release to publish §6/§7/§4. Done-when: all orientation leaves accepted with correct per-device names + docs; vector_family_consistency_check=0; dd_path_uniqueness=0; ISNC re-exported.

§4 tail + §5 ordinal COMPLETE; released v0.2.0rc61 (2124 names). §5 direction_second: added ISN grammar (cec56a7) first/second_measurement_direction_unit_vector carriers + strain_gauge locus per the locked direction-second-naming decision (Option A, axis-leading); primary sensor/direction renamed to x/y/z_first_measurement_direction_unit_vector_of_strain_gauge (old _of_sensor superseded), direction_second → x/y/z_second_... — all accepted + documented. §4 residuals resolved: neutron direction_to_detector = direction unit vectors (unit='m' is a DD quirk on ALL these leaves; extract resolves dimensionless) → x/y/z_direction_unit_vector_of_neutron_detector accepted; camera up = x/y/z_image_up_unit_vector_of_camera (untangled from direction/z + vertical_ superseded); shatter_cone/direction ruled DISTINCT from the injector (DD: 'central axis of the shatter cone' vs 'velocity direction of the pellet') → x/y/z_direction_unit_vector_of_shatter_cone; SPI injection_direction frame flag cleared (source_paths corruption from an exploratory shatter run repaired — injection_direction is device-local Cartesian x/y/z, correct). All 9 orientation families x/y/z accepted WITH documentation (per-family docs authored deterministically after the docs pipeline's --focus force-reset proved destructive). Final: dd_path_uniqueness_check=0, vector_family_consistency_check=0, 0 LaTeX descriptions, E- 1/sqrt2 intact, raw accepted 2425. imas-codex ISN pin bumped to cec56a7 (01ba7f68). A footgun logged to memory: sn run --focus force-resets accepted names on focused paths even in --docs-only mode.