§1 — Problem (fully defined)

The DD subtree summary/local/separatrix_average/* stores flux-surface-averaged quantities evaluated at the separatrix (the plasma boundary). Its StandardNames are a multiplicity mess: of 54 dd-sourced leaves, 24 carry more than one live name. Three coexisting forms were composed for the same physical quantity, and all are name_stage='accepted':

§1a — Correction: the operator and the locus are orthogonal (2026-07-10)

This plan's original framing — "the plasma boundary IS a flux surface, so the flux_surface_averaged_ prefix is redundant with _at_plasma_boundary" — was wrong in general, in both directions:

Graph evidence (2026-07-10): 54 separatrix_average leaves carry live names (24 with 2–3); 30 separatrix (local) leaves carry names; and 15 names are live on BOTH subtrees. For flux labels this sharing is correct (poloidal_magnetic_flux_at_plasma_boundary, toroidal_flux_radius_at_plasma_boundary, safety_factor_at_normalized_poloidal_magnetic_flux_equal_to_0_95); for surface-varying bases it is a collision (e.g. toroidal_argon_velocity_at_plasma_boundary, toroidal_plasma_momentum_at_plasma_boundary, iron_density_at_plasma_boundary) — the name can no longer distinguish the local value from the average. An anomaly to verify during the collapse: the momentum_phi/momentum_tor → name mapping is swapped between the two subtrees.

So the fix is not "pick one of long vs short" — it is a conditional canonical rule (locked in §Decisions): locus always; averaging prefix iff the FSA is not a no-op for that base.

A sibling instance: summary/composition/* yields 18 density-ratio names with inconsistent naming — ratio_of_<species>_density_to_electron_density vs <species>_fraction vs volume_averaged_<species>_..._to_..._electron_density. Same class (one quantity, many name shapes), lower priority.

Surface-varying base (FSA is NOT a no-op) — two leaves, TWO names; prefix carries meaning summary/local/separatrix/velocity_phi/argon summary/local/separatrix_average/velocity_phi/argon toroidal_argon_velocity_at_plasma_boundary toroidal_flux_surface_averaged_argon_velocity_at_plasma_boundary Flux-function base (FSA IS a no-op) — two leaves, ONE shared name; prefix banned (FSA of an FSA) summary/local/separatrix/magnetic_shear summary/local/separatrix_average/magnetic_shear magnetic_shear_at_plasma_boundary (one name, two DD sources) Conditional canonical rule: _at_plasma_boundary ALWAYS (the boundary is one of a continuum of flux surfaces); flux_surface_averaged_ prefix IFF the base is not constant on a flux surface. Locus-only names for surface-varying averaged leaves would COLLIDE with the local-value subtree (15 such collisions are live in the graph today).

§2 — Why it matters (motivation)

Multiple accepted names for one physical quantity is a first-order catalog defect: consumers cannot tell which name is authoritative, cross-references fork, and the same DD leaf appears under two or three ISN identifiers. It also breaks the ledger's implicit "one live name per quantity" expectation and inflates the catalog. The locus-stripped names are additionally wrong — a separatrix-averaged quantity that drops _at_plasma_boundary reads as a global quantity. This surfaced during sn-pedestal-source-recovery's blast-radius assessment (f-psr-002): the compose-grounding gap fixed there (WS-1) explains the locus-stripped variants, but the long/short multiplicity is a pre-existing naming-convention gap that the grounding fix does not resolve on its own.

The collisions are worse than the duplicates. A duplicate pair (long + short on the same leaf) merely forks references; a cross-subtree collision makes the name semantically ambiguoustoroidal_argon_velocity_at_plasma_boundary currently denotes both the local separatrix rotation and its flux-surface average, which are physically different numbers. Physics-relevant naming requires understanding, per base, whether the flux-surface average is a no-op; that classification must live in the vocabulary, not in per-name judgement calls, or the generation pipeline will keep re-minting the wrong forms (§4).

✓ landed 2026-07-10

§3 — Collapse executed

46 deterministic graph actions (6 rename cascades, 27 supersede-merges, 11 targeted source moves, 2 detach+create) — no LLM recompose, $0 spent. Result: 84 leaves all single-name (was 24 multi), 0 cross-subtree collisions (was 15), 0 locus-stripped names, 4 correct flux-function shares, 0 orphans/desyncs, gate audit clean. Momentum phi/tor resolved as a DD v3/v4 alias pair sharing one name per subtree. Two new FSA names (effective_charge, ion_density) sit drafted for the next review rotation. Full record: §3 landed.

§3 — The rule + approach (reference)

The canonical form is a conditional rule (locked below), applied per base:

Execution, once the base list is locked and the §4 guards are in place:

Mechanism: the deterministic rename/supersede cascade (graph_ops._execute_rename_cascade / sn run --rename OLD:NEW --include-accepted), NOT a --focus recompose — a recompose mints a fresh drafted variant and leaves the accepted duplicates, worsening the multiplicity (demonstrated in sn-pedestal-source-recovery). Verify 0 orphans / 0 desyncs and that every leaf resolves per the conditional rule.

✓ landed 2026-07-10

§4 — Persistent fixes: vocab / grammar / prompt / grouper

Encoded at all four levels: constant_on_flux_surface flag in ISN vocab (definitional set + flux-label carriers), an IR-level gate failing parse AND compose for flux-surface reductions of flagged bases (19 new tests; ISN suite 1665 passed), the conditional rule in generate_name_dd.md/generate_name_system.md/review.md (new check I2.8), a live-catalog gate-violation probe in sn run (pre-collapse: exactly 1 offender, flux_surface_averaged_magnetic_shear), and harmonize family eligibility extended to the three reduction operators via the edge operator token. derive_edges/parents.py confirmed correct, untouched. Full record: §4 landed (ISN b8a2024, imas-codex 8d6cb77b; ISN pin bumped).

✓ landed 2026-07-10

§5 — DD-version gate: strict DDv4 grounding (systemic finding from f-bam-004)

The q95/momentum anomalies traced to a systemic defect: 17,216 of 61,366 IMASNodes (28%) are absent from DD 4.1.0, removal was recorded only as DEPRECATED_IN edges no seeding query consulted, and 1,535 dd sources (857 feeding live names) sat on DDv3-only paths. Root fix (imas-codex 51c19815): new graph/dd_lifecycle.py stamps absent nodes lifecycle_status='removed' (+ NBC renamed_to), wired into the DD build; all three seeding queries now exclude removed nodes; an sn run probe flags survivors. Ledger cleanup: 300 redundant v3 sources retired, 37 re-anchored to their DD4 path, 28 semantic-rename conflicts resolved in favour of the DD4 name, 492 truly-removed detached, 111 names with no DDv4 anchor exhausted. Post-state: 0 v3 sources on live names, 0 orphans/desyncs, both probes clean. Full record: followups landed (also covers f-bam-003 composition family + f-bam-004 q95 fix).

§ Decisions

What is the canonical form for a flux-surface-averaged quantity evaluated at the plasma boundary (separatrix)?

The locus and the averaging operator are orthogonal: _at_plasma_boundary pins WHICH flux surface (the boundary is one of a continuum, so the locus always carries weight), while flux_surface_averaged_ states a reduction that is only meaningful when the base varies over the surface. The DD encodes this with parallel separatrix/ (local) and separatrix_average/ subtrees, and the graph shows 15 live cross-subtree collisions from locus-only naming. For flux-function bases (safety factor — already a flux-surface quantity, so averaging it is an FSA of an FSA; magnetic shear; flux labels; pressure) FSA is a no-op and one shared name serves both leaves. Prefix iff non-no-op.

Which bases are flagged constant_on_flux_surface (FSA is a no-op → no averaging prefix, one shared name for the local and averaged leaves)?

Canonical shape for the summary/composition density-ratio names (ratio_of_X_density_to_electron_density vs X_fraction vs volume_averaged_…)?

§ Followups

Collapse the separatrix/separatrix_average multiplicity under the conditional rule (AFTER f-bam-002)

Apply the locked conditional rule (§3) across BOTH sibling subtrees: flux-function leaves converge on one shared <q>_at_plasma_boundary name; surface-varying averaged leaves get flux_surface_averaged_<q>_at_plasma_boundary, breaking the 15 live cross-subtree collisions without stealing the local leaf's name. Runs after the §4 guards (f-bam-002) so regeneration cannot re-mint the old forms. Spun out of sn-pedestal-source-recovery f-psr-005.
Project: imas-standard-names (naming/grammar) + imas-codex (standard_names) + imas graph
Plan: sn-boundary-averaged-name-multiplicity (http://localhost:8765/imas-standard-names/sn-boundary-averaged-name-multiplicity.html)
Section: §3 collapse — separatrix + separatrix_average subtrees
Tier: opus

Context
  PREREQUISITE: f-bam-002 (vocab flag + grammar gate + prompt updates) must be landed first.
  Two parallel 64-leaf DD subtrees: summary/local/separatrix/* (local value) and
  summary/local/separatrix_average/* (flux-surface average). 24 averaged leaves carry 2-3 live names;
  15 names are live on BOTH subtrees. Honour the locked canonical-boundary-form and
  flux-function-base-list decisions shown live above: locus _at_plasma_boundary ALWAYS;
  flux_surface_averaged_ prefix IFF the base is not flagged constant_on_flux_surface.

State to read (CODE / FILES / DATA)
  imas-codex graph_ops.py: _execute_rename_cascade, bind_recovery_sources (targeted source attach),
  HAS_SUCCESSOR/superseded_by convention. CLI: sn run --rename OLD:NEW --include-accepted (--dry-run
  first). ISN grammar: every target must round-trip (compose(parse(name))==name) and pass the new
  constant_on_flux_surface gate. Enumerate BOTH subtrees per leaf (the collision check needs the union):
    MATCH (s:StandardNameSource {source_type:'dd'})-[:PRODUCED_NAME]->(n:StandardName)
    MATCH (s)-[:FROM_DD_PATH]->(l:IMASNode)
    WHERE l.id STARTS WITH 'summary/local/separatrix'
      AND NOT coalesce(n.name_stage,'') IN ['superseded','exhausted']
    RETURN l.id, collect(DISTINCT n.id)

Scope locks / constraints (non-decision)
  - Per-leaf resolution:
    * Flux-function base (constant_on_flux_surface): local + averaged leaves attach to ONE shared
      name _at_plasma_boundary (two sources, one StandardName — the poloidal_magnetic_flux pattern).
      Supersede flux_surface_averaged_-prefixed variants onto the shared name.
    * Surface-varying base: averaged leaf's canonical name is flux_surface_averaged__at_plasma_boundary.
      Before superseding a short name, CHECK ITS FULL SOURCE SET: if it is also produced by the local
      separatrix/* leaf it is a COLLISION, not a duplicate — the short name STAYS as the local leaf's
      name; only detach the averaged source and re-attach it to the prefixed name (targeted attach,
      bind_recovery_sources-style). Superseding a collided name steals the local leaf's identity.
  - Verify/fix the momentum_phi vs momentum_tor mapping swap between the two subtrees against DD
    semantics while touching those leaves.
  - Deterministic cascade only, NEVER a --focus recompose (mints a new drafted variant, worsens
    multiplicity — proven in sn-pedestal-source-recovery). Never raw-delete accepted names; preserve
    provenance; repoint inbound links.
  - Generalisation check: apply the same audit to maximum_over_flux_surface / minimum_over_flux_surface
    names and report on summary/volume_average/* (volume average of a flux function is NOT a no-op —
    scope explicitly, do not blanket-apply the rule there).

Done-when
  1. Every separatrix + separatrix_average leaf resolves per the conditional rule: flux-function leaves
     share ONE name across both subtrees; surface-varying leaves have TWO distinct names; 0 remaining
     cross-subtree collisions on surface-varying bases; locus-stripped names gone.
  2. Momentum swap resolved; generalisation check reported (fix or explicit scope-out).
  3. 0 orphans / 0 desyncs; provenance + content preserved; composition density-ratio followup written
     if not already done.
  4. This followup resolved.

§3 landed — 46 deterministic graph actions: 84 leaves single-name (was 24 multi), 0 cross-subtree collisions (was 15), 0 locus-stripped, 4 flux-function shares, 0 orphans/desyncs, gate audit clean. Momentum phi/tor = DD v3/v4 alias pair, one name per subtree. $0 LLM spend. Archive: docs/archive/sn-boundary-averaged-name-multiplicity-s3-landed.html

Encode the conditional rule at vocab/grammar/prompt/grouper level (prerequisite for f-bam-001)

Per-name renames do not survive regeneration. Land the §4 persistent fixes first: a constant_on_flux_surface flag in ISN physical_bases.yml, a grammar gate rejecting flux-surface reduction operators on flagged bases (the FSA-of-an-FSA class), the conditional path rule in the imas-codex generation/review prompts (replacing the unconditional "NEVER drop the prefix" row in generate_name_dd.md), a graph audit, and averaging-operator family eligibility in harmonize.py so the local/averaged pair docs-harmonize together.
Project: imas-standard-names (grammar/vocab) + imas-codex (prompts, audits, harmonize)
Plan: sn-boundary-averaged-name-multiplicity (http://localhost:8765/imas-standard-names/sn-boundary-averaged-name-multiplicity.html)
Section: §4 persistent fixes
Tier: opus

Context
  The conditional canonical rule is locked (locus always; flux_surface_averaged_ prefix iff the base is
  not constant on a flux surface). Encode it so the generation pipeline can never re-mint the wrong
  forms. Honour the flux-function-base-list decision shown live above for the flag's membership; if it
  is still open, implement the mechanism with the definitional-only set and flag the membership as
  pending.

State to read (CODE / FILES / DATA)
  ISN: imas_standard_names/grammar/vocabularies/physical_bases.yml (per-entry schema, see the
    inherently_dimensional precedent), operators.yml (flux_surface_averaged, maximum_over_flux_surface,
    minimum_over_flux_surface), imas_standard_names/vocabulary/audit.py and the validator that enforces
    inherently_dimensional; tests under tests/grammar/.
  imas-codex: imas_codex/llm/prompts/sn/generate_name_dd.md (path-rule row for separatrix_average —
    currently mandates the prefix unconditionally), generate_name_system.md (averaging-operator bullet),
    review.md; imas_codex/standard_names/harmonize.py (_FAMILY_OPERATOR_KINDS excludes unary_prefix, so
    flux_surface_averaged_X_at_plasma_boundary never harmonizes with X_at_plasma_boundary);
    imas_codex/standard_names/audits.py (add a live-name gate-violation audit surfaced by sn run).

Scope locks / constraints (non-decision)
  - Vocab: add constant_on_flux_surface: true per-base flag + schema comment; membership per the locked
    flux-function-base-list decision.
  - Grammar gate: reject flux_surface_averaged / maximum_over_flux_surface / minimum_over_flux_surface
    applied to a flagged base, at the same layer that enforces inherently_dimensional, with unit tests
    (positive: flux_surface_averaged_electron_density_at_plasma_boundary parses; negative:
    flux_surface_averaged_safety_factor_* rejected).
  - Prompts: replace the unconditional separatrix_average rule with the conditional rule; state that
    flux-function leaves share the local leaf's name (two DD sources, one StandardName). Keep the
    collision warning for surface-varying bases.
  - harmonize.py: extend _FAMILY_OPERATOR_KINDS to cover averaging reduction operators (or record in
    the plan why not); families.py (vector-component grouping) is out of scope.
  - derive_edges/parents.py automatic-parent behaviour is already correct — do not change it.
  - ISN + imas-codex tests green (run graph-marked tests against live Neo4j).

Done-when
  1. constant_on_flux_surface flag live in physical_bases.yml; grammar gate enforced with tests.
  2. generate_name_dd.md / generate_name_system.md / review.md carry the conditional rule.
  3. Gate-violation audit runs in sn run; harmonize family eligibility extended or explicitly declined
     in the plan.
  4. Both repos' tests green; plan §4 updated with outcomes; f-bam-001 unblocked; this followup resolved.

§4 landed — ISN b8a2024 (constant_on_flux_surface flag + IR-level gate, 19 tests, suite 1665 passed) + imas-codex 8d6cb77b (conditional prompt rule, review check I2.8, sn-run gate probe, harmonize reduction-operator family eligibility, pin bump).

Normalise the summary/composition density-ratio family (18 names) per the explicit-ratio style

Decision composition-ratio-form: prefer explicit ratio form; survey other catalog areas and follow one consistent style for averages and ratios. Lower priority; deterministic cascade once the target shapes are enumerated.
Project: imas-standard-names + imas-codex graph
Plan: sn-boundary-averaged-name-multiplicity
Section: composition density-ratio family
Tier: opus

Context
  summary/composition/* yields 18 density-ratio names in three shapes (ratio_of_X_density_to_electron_density vs X_fraction vs volume_averaged_...). Decision locked: prefer explicit ratio form; survey other catalog ratio/average families first and apply ONE consistent style.

State to read
  Graph enumeration of summary/composition/* sources and their live names; ISN grammar binary ratio operator; the §3 collapse driver pattern (supersede-merge onto canonical, deterministic only).

Done-when
  1. One canonical shape enumerated and locked for all 18 names; renames/supersedes applied via the deterministic cascade with full source-set checks.
  2. 0 orphans/desyncs; every target round-trips.
  3. Plan updated; this followup resolved.

Landed — DD defines composition as ratio of volume-averaged species density to volume-averaged n_e; canonical shape ratio_of_volume_averaged__density_to_volume_averaged_electron_density. 14 cascade renames + 3 already canonical + hydrogen collision resolved (its old name correctly serves the isotope_fraction leaves; composition leaf got a fresh drafted canonical name). All 18 leaves single-name canonical; 0 orphans/desyncs.

Verify the separatrix q → q95 locus mapping (psi_N=0.95 vs 1.0)

Both summary/local/separatrix*/q leaves map to safety_factor_at_normalized_poloidal_magnetic_flux_equal_to_0_95 (q95), but the separatrix is psi_N=1.0. The flux-function sharing is correct; the locus value looks wrong (q diverges at the separatrix in diverted plasmas, so q95 may be a deliberate convention — check the DD documentation before renaming).
Project: imas-codex graph + imas-standard-names
Plan: sn-boundary-averaged-name-multiplicity
Section: q95 locus anomaly (f-bam-004)
Tier: sonnet

Context
  summary/local/separatrix/q and separatrix_average/q both attach to safety_factor_at_normalized_poloidal_magnetic_flux_equal_to_0_95. The separatrix is psi_N=1.0; q95 is the standard proxy because q diverges at the X-point. Check the DD leaf documentation: if the DD defines these leaves AS q95, the name is right and this closes with a note; if the DD means q at the boundary, rename via the deterministic cascade to a psi_N=1.0 or boundary form that round-trips.

Done-when
  1. DD semantics established and recorded in the plan.
  2. Name kept or renamed accordingly (deterministic cascade, full source-set check).
  3. This followup resolved.

Landed — DD defines summary/local/separatrix*/q as q AT the LCFS, not q95. Revived the dangling-superseded safety_factor_at_plasma_boundary (accepted) and moved both separatrix q sources onto it; the q95 name keeps only the two genuine q_95 sources. Finding escalated into the DD-version gate (§5).

Review-rotation pickup: 3 drafted names from the deterministic collapses

flux_surface_averaged_effective_charge_at_plasma_boundary, flux_surface_averaged_ion_density_at_plasma_boundary and ratio_of_volume_averaged_hydrogen_density_to_volume_averaged_electron_density were persisted deterministically as name_stage='drafted'; the next routine sn run review rotation scores them. Also observed (low priority): ratio_of_hydrogen_density_to_total_hydrogenic_density serves BOTH the line_average and volume_average isotope_fraction leaves — an averaging-domain collision of the same class.
Project: imas-codex graph
Plan: sn-boundary-averaged-name-multiplicity
Section: drafted-name pickup (f-bam-005)
Tier: sonnet

Context
  Three deterministic-collapse names sit drafted awaiting review pools; a routine sn run covers them. While closing, check the isotope_fraction line/volume averaging-domain collision on ratio_of_hydrogen_density_to_total_hydrogenic_density and either split (line_averaged_/volume_averaged_ prefixes) or record why one name suffices.

Done-when
  1. The 3 drafted names reviewed (accepted or refined) by a routine rotation.
  2. Isotope-fraction collision resolved or explicitly declined.
  3. This followup resolved.

Landed — flush-mode sn run reviewed the drafted queue: all collapse names accepted (fsa effective_charge/ion_density/hydrogen_density at plasma boundary, toroidal fsa ion velocity, volume-averaged hydrogen ratio). Isotope-fraction collision SPLIT: line/volume averaged ratio_of_*_hydrogen_density_to_*_total_hydrogenic_density minted per leaf; old domain-less name superseded onto the volume form. Line twin was exhausted by a refine-worker structuring failure (line_averaged mis-routed as qualifier), not review — restored to accepted; defect noted in f-bam-006.

refine_name structuring failure must not exhaust a reviewed name (line_averaged mis-segmenting)

During the f-bam-005 flush, the refine worker emitted line_averaged as a qualifier segment (it is a transformation operator), the RefinedName validation correctly rejected it, and the deterministic-failure branch marked the REVIEWED name exhausted — an infrastructure failure retiring a semantically fine name (score 0.812; its volume twin accepted). Fix the worker segment routing for averaging operators and/or make structuring failures release the claim back to reviewed instead of exhausting.
Project: imas-codex
Plan: sn-boundary-averaged-name-multiplicity
Section: refine exhaustion defect (f-bam-006)
Tier: sonnet

Context
  workers.py refine_name path: on a deterministic RefinedName validation error the name is marked exhausted. Seen with qualifier 'line_averaged' is not a registered grammar token — the LLM structured an averaging TRANSFORMATION as a qualifier. Reproduce with ratio_of_line_averaged_hydrogen_density_to_line_averaged_total_hydrogenic_density.

Done-when
  1. Averaging operators route to the transformation slot in refine structuring (prompt or post-fix), OR structuring failures leave the name reviewed (retryable), with a unit test.
  2. This followup resolved.

§ Research

Graph + DD evidence for the conditional rule (2026-07-10)