Symbol definition policy — define identity, don't restate units
✓ SHIPPED 2026-07-20. Policy landed across the pipeline: latex_def_check requires symbol
identity not a bare unit (a27beea6); no-units prompt across generate/refine/review seats + shared partial,
Spectrum rule reworded, both narrow exceptions removed (38dfeb97); advisory symbol_units_check +
audit:symbol_units selector, FP-hardened against label subscripts / variable exponents / unit vectors
(ee22b1f4 → ac75e0df). Smoke: 30/30 unit-free, 10/10 stripped, $13.
Cleanup: 357 unit-docs selected → 356 refined unit-free ($89, $0.25/name) + 8 detector-FPs resolved →
audit:symbol_units now selects 0; zero genuine units remain catalog-wide. A full audit re-stamp
surfaced 43 latent deterministic defects (not new) that the fast-grep path had masked — folded into the docs campaign.
Follow-on from model-selection-and-global-refine §5.
The docs-refinement campaign produces high-quality normative docs, but the refine seat annotates a unit on nearly every LaTeX
symbol (see before/after evidence §2b, Example 7). This plan
makes the policy explicit: a symbol's identity (which quantity it denotes, ideally a name: link) is
the non-redundant thing docs must supply; its unit is already carried elsewhere and restating it in prose is
redundant and drift-prone. Three coupled changes: the prompt, the audit, and a scoped cleanup.
§1 — Why per-symbol units are the wrong target
A standard name's documentation contains a defining relation whose symbols fall into three classes. For every class, the unit is
recoverable without restating it in prose:
Symbol class
Where its unit already lives
Prose restatement is…
The primary quantity the name denotes
the structured unit field (rendered on the name's page)
duplicating a machine-readable field
An auxiliary quantity that is itself a standard name
its own unit, reached via the name: link
redundant and drift-prone — goes stale if that name's unit changes
A non-name math/geometry symbol (arclength ℓ, an angle)
nowhere structured
the only case with any argument — and the unit is usually trivial
What is genuinely non-redundant is the symbol's identity: binding it to the quantity it denotes. "$B_p$ is
the poloidal magnetic-field magnitude [name:poloidal_magnetic_field]" carries information a reader cannot derive; "$B_p$
in T" they can. Restating units in prose is the same anti-pattern the campaign already removes (facts that belong in
structured fields — like device-specific "typical values"), one level down.
Root cause — the audit rewards the wrong thing
The refine seat is partly incentivised to add units by the deterministic audit. latex_def_check
(imas_codex/standard_names/audits.py) counts a symbol as "defined" if a nearby sentence contains a definition word
(is / are / denotes / represents / where / defined / being / given by / …) OR a unit in
brackets. So a bare unit satisfies the symbol-definition check — the audit conflates "has a unit" with "is defined." A
symbol can carry a unit and still have no stated identity. Fixing the audit removes the incentive at its source.
§2 — The three changes
Refine + docs-gen prompt. Require every LaTeX symbol to be defined by identity in its where-clause,
preferring a name: link when the symbol is a catalog quantity. And — no units in documentation prose at
all (lead directive 2026-07-20): a unit states dimension, not identity; the quantity's unit is the authoritative
structured unit field and a linked quantity carries its own via its link. There is no "non-name symbol"
exception — units simply do not belong in the description/documentation text.
latex_def_check audit (landed a27beea6). Stops accepting a bare unit-in-brackets as
the definition satisfier — requires a definition word or a resolvable name: identity within the definition window.
Tests: unit-only fails, name:-link passes, def-word passes. This removes the incentive to unit-annotate but
does not itself ban units from prose — that is change 3.
Audit audit:symbol_units — flag any unit token in documentation prose. The enforcement side of the
"no units in prose" policy: a low-false-positive check that flags accepted docs carrying unit statements ("with unit X",
"in <UNIT>", unit expressions inside \mathrm{…} such as m^{-3}/s^{-1}), while
excluding \mathrm{} subscript labels (\mathrm{axis}, \mathrm{eff}). Doubles as the campaign
selection predicate so the cleanup rotation is self-pruning. FP rate validated against the live catalog before it lands as
a hard audit.
Changes 1–2 are a small, testable prompt + audit edit (tier: sonnet). They are not a defect that halts the
running campaign — units-in-prose is redundant, not wrong — so rotation 3 continues untouched.
§3 — Sequencing and the regeneration price
The cheapest moment to land this is now, while the current campaign is still running. The docs-refinement
campaign has ~502 names left in its selection; if the new prompt policy lands before those rotations finish, they pick it up at
zero marginal cost (they are being refined anyway). Only docs that are already prose-clean — so the current
campaign will never re-touch them — need a dedicated cleanup pass.
~550–600 restate a per-symbol unit: 73 use the explicit "with unit X" style, ~520 use the
"… in <UNIT>" style (overlapping).
The running campaign will refine ~1,047 docs-fixable names over its life; roughly a quarter of the unit-restating docs
(~280) fall inside that set and are re-normalized for free once the policy lands.
The policy change itself is nearly free; the only real spend is a targeted cleanup of docs the running campaign won't otherwise re-touch. A full catalog re-refine is never necessary.
remaining selection picks up the new prompt policy for free if landed now
Targeted cleanup (audit:symbol_units)
~270 incremental (of ~550 total)
$75–155
already-prose-clean docs with redundant units; self-pruning rotations
Worst case (full re-refine)
~2,070
~$580
unnecessary — only unit-restating docs need it
Bottom line: the realistic marginal price is ~$75–120 for a clean catalog, dominated by one targeted cleanup
rotation — or effectively $0 if the lead is content to let the running campaign + all future refines absorb the
policy and leave the ~270 already-clean redundant-unit docs as-is until they are next touched.
§ Decisions
Should a bare unit-in-brackets still satisfy latex_def_check for a non-name math symbol, or must every symbol have an identity/definition word?
May a doc state the primary quantity's own unit in prose at all, given it is already the structured unit field?
Run a dedicated audit:symbol_units cleanup campaign over already-clean redundant-unit docs, or only absorb the policy through the running campaign and future refines?
§ Followups
Run the targeted units cleanup (smoke PASSED)
Landed:latex_def_check identity rule (imas-codex a27beea6); symbol_units_check advisory + audit:symbol_units selector (ee22b1f4); no-units prompt across generate/refine/review seats + shared format partial, Spectrum rule reworded, both narrow exceptions removed (38dfeb97).
Smoke PASSED (2026-07-20): 30-name run, 30/30 refined docs unit-free, 10/10 unit-bearing docs stripped to none, 0 errors, $13.04. The new policy produces identity-defined, unit-free docs end-to-end (e.g. ion_diffusivity: tensor definition via name: links, no units).
Re-stamp done (2026-07-20): full deterministic-audit re-stamp over all 2,347 accepted docs populated the symbol_units findings; audit:symbol_units now selects 357 docs. Side effect: 43 docs re-quarantined — not new defects but latent deterministic-audit findings (mostly perturbed_*/*_real_part tripping representation/amplitude checks) that the fast-grep path had washed to 'valid'; the campaign will fix them (original campaign grew 85→116).
Cleanup running:--campaign audit:symbol_units --campaign-pilot 357 (4 batches, ceiling $130 ≈ $107 at $0.30/name); refines the 357 unit-docs unit-free under the new prompt, self-pruning as units clear. Then: finish the original docs campaign (~116) per f-docs-campaign-rotations.
Project: imas-standard-names / code: imas-codex (branch main)
Section: §2 — symbol-definition policy: prompt + audit
Tier: sonnet
Context
The docs-refinement campaign annotates a unit on nearly every LaTeX symbol.
Units are recoverable (primary quantity → structured unit field; linked
auxiliary quantity → its own unit via the name: link), so restating them in
prose is redundant and drift-prone. What is non-redundant is the symbol's
IDENTITY. The refine seat is partly incentivised to add units because
latex_def_check accepts a bare unit-in-brackets as the "symbol is defined"
satisfier. Land this while the running campaign still has ~502 names queued so
they pick up the new policy at zero marginal cost. Honour the live decisions
above (unit-satisfier, primary-unit-in-prose) — do not re-litigate them.
State to read (CODE / FILES)
imas_codex/standard_names/audits.py — latex_def_check (~line 311), the
_DEFINITION_WORDS_SINGLE / _DEFINITION_PHRASES sets, and the
"unit in brackets" branch that currently satisfies the check.
imas_codex/standard_names/prompts/ — the refine_docs and generate_docs
prompt templates (symbol/where-clause guidance).
docs-campaign-before-after.html §2b Example 7 — the evidence of the pattern.
Scope locks / constraints (non-decision)
- Do NOT halt or alter the running docs campaign; this is forward policy.
- No name-axis changes; docs-only.
- Explicit-path commits, no AI trailers, no plan refs in messages.
Done-when
1. Prompt requires identity/name-link symbol definitions and forbids
restating derivable units (per the locked decisions); committed + pushed.
2. latex_def_check no longer accepts a bare unit as the sole satisfier;
unit tests cover: unit-only symbol FAILS, name-link identity PASSES,
non-name symbol with a stated unit PASSES (per the unit-satisfier decision).
3. ~30-name smoke rotation shows the new refines define symbols by identity
with no redundant units; cost recorded.
4. Followup written for the cleanup decision (targeted audit:symbol_units
campaign vs absorb-only) + this followup resolved.