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:
§4 — 12 orientation-family vocab_gap sources, not ~20 uniformly parked: 9 non-deferred + 3 deferred (sensor/direction_second). Two listed families (ec_launchers/mirror/movement/direction, operational_instrumentation/sensor/direction primary) are NOT parked — merely uncomposed (extracted/failed). camera_ir/.../up/z carries a mis-attached vertical_coordinate_of_camera; shatter_cone/direction y,z sit in an inconsistent vocab_gap+accepted state tangled with §6.
§6 — 2 dd-path duplicate violations (not 1), both from a single over-attached name x_direction_unit_vector_of_pellet_injector reaching all three spi/injector/injection_direction/{x,y,z} leaves (fix = trim it to /x). The expected 6 momentum base-carrier inconsistencies DO NOT EXIST (0 in the live accepted corpus — already resolved via a superseded normalized_momentum_flux rename). Residual adds a sensor-attachment axis-token re-lock (drafted z_coordinate_of_sensor_attachment_point edit already queued).
§7 — the predecessor's "all 9 LaTeX descriptions now plain text" is FALSE — they regressed on catalog re-import. 9 accepted names still carry LaTeX in the short description field (long documentation LaTeX is legitimate MathJax). The malformed \φ (literal φ after a backslash) is a generate-level normalizer defect: the Greek-symbol normalizer rewrote \phi→\φ inside LaTeX instead of stripping the LaTeX. The E− 1/√2 factor is still missing too. Fix at the generator (normalizer + description validator) so new names never carry it, then re-apply. 17 open edits total (9 LaTeX-desc + 3 doc-math + 5 rename; 2 rename superseded≈resolved, 3 drafted z-axis re-locks).
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.
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)
Frame
Horizontal axes
Third (machine-vertical) axis
What the tokens are
Device-local Cartesian
x, y
z
Arbitrary mounting axes — you cannot say which physical direction x points without the device's survey
Machine cylindrical (R, φ, Z)
radial, toroidal
vertical
Physically named directions — major-radius outward, around the torus, machine up
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_phiforbidden, 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 r→radial, phi→toroidal,
z→vertical/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.
C-funnel — catch-and-promote at generation (a fallback for errors; ACCEPTABLE). If the
generator emits a stray r/phi in an axis segment, map it to canonical
radial/toroidalbefore composition and continue — exactly like the
projection_shape coercion already shipped, and exactly what the DD funnel already does for DD
leaves. The stored name is always the canonical word; r never reaches the catalog. One canonical
form, one way. This is the "fallback to catch generation errors" you sanctioned — a funnel, not a dual path.
C-parser — make the ISN parser accept r_coordinate_of_X as a valid name (the LONG-LIVED
DUAL PATH; REJECTED). This blesses r_… as a permanently-valid spelling, lets two forms of
one name coexist in the catalog, and breaks the round-trip invariant
(compose(parse("r_…")) → "radial_…" ≠ input). This is the "second way" you ruled out. We do not
build this.
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_gapStandardNameSource 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:
operational_instrumentation/sensor/direction_second/{x,y,z} — needs the ordinal qualifier (see §5)
spi/injector/shatter_cone/direction/{x,y} and unit_vector_minor/{x,z} → shatter_cone direction / minor-axis triples (some members already accepted)
neutron_diagnostic/detectors/field_of_view/direction_to_detector/{x,y,z} → neutron-detector direction triple
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:
The device loci already exist in the grammar (sensor, pellet_injector, shatter_cone, camera/infrared_camera, neutron_detector, electron_cyclotron_launcher_mirror — added in ISN 18b8465). The vocab_gap parking is stale: the sources were parked before the loci existed and never re-run.
The real remaining gaps are base-token mis-proposals, not missing loci: the composer proposed non-canonical bases (direction_to_detector, shatter_cone_direction_vector, unit_vector_component) instead of the existing unit_vector geometric base. The accepted sibling x_direction_unit_vector_of_pellet_injector proves the correct <axis>_direction_unit_vector_of_<locus> pattern — so a supervised rotation with sibling neighborhood context can now land these.
Three real pipeline defects block automated composition (each proven, then worked through):
Claim gate: the parked sources sit at terminal status='failed' with attempt_count 5–7 > max_attempts=3, so claim_generate_name_batch skips them. Reset (status='extracted', attempt_count=0) fixes it — the claim then succeeds (claimed 3). (--min-score is a separate trap: it flips sn run into regen mode, which targets low-scoring existing names, not parked sources.)
DSv4 auth: the free endpoint 98dci4-gpu-0003:18800/v1 now returns 401 Unauthorized — the runbook's empty AMBIX_API_KEY is stale. Use the configured OpenRouter compose model (openrouter/deepseek/deepseek-v4-flash, keyed via .env) instead of repointing to DSv4.
Composer mis-composes loci + cross-attaches (the real blocker): once claim + auth were fixed, the composer produced wrong-locus names for ec_launcher sources — it matched ec_launchers/mirror/movement/direction/x to the existing accepted x_direction_unit_vector_of_pellet_injector (wrong device) and spawned locus-less junk (y_direction_unit_vector). This actively corrupts good accepted names by cross-attaching HAS_STANDARD_NAME/PRODUCED_NAME edges. The run was stopped and all corruption cleaned up + verified (spurious edges deleted, junk node removed, accepted names restored, dd_path_uniqueness=0).
Conclusion: §4 composition is NOT safely automatable as-is — it needs imas-codex compose-hardening (correct per-locus composition for device-orientation component leaves + a dedup guard against attaching an existing name to a different device's path), which is a development+test cycle in its own right, OR fully manual per-name composition with verification. Also note the neutron direction_to_detector family is unit='m' (a displacement per voxel, NOT a unit vector) — so per-family the correct base differs; this is genuine physics-naming curation, not a mechanical drain.
Bulk --focus is unsafe over the tangled cluster: its force-reset re-stages any name cross-linked through a focused source's source_paths. It briefly reset two accepted names (z_direction_unit_vector_of_pellet_injector, vertical_coordinate_of_camera) via the SPI/shatter_cone/camera cross-wiring — both restored to accepted (verified). Drive these one family at a time, never a bulk focus.
Folded in from §7: the 3 drafted z_coordinate_of_{divertor_target,ferritic_element_centroid,sensor_attachment_point} axis re-locks (promote Cartesian z, supersede the stranded sourceless vertical_) — completing them clears the 2 residual vector_family_consistency_check flags. They already carry correct names (matching x_/y_ siblings); they need docs + acceptance, not composition.
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.
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 phiinside\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.