orthonym.assembly.handlers.tier_a_ring#
Note
Internal API. Names and behaviour may change between releases.
a phase Plan-06 composite handler: Tier-A ring cascade.
Per internal notes (gap-closure) + -02 Option A: encodes the Tier-A pool-compete cascade as a SINGLE composite handler so the dispatch_inner first-match-wins interface (internal notes) stays untouched.
LIFT SOURCE: composer.py:996-1322 (verbatim, with return pool.best.name
or return candidate_name replaced by return NamingResult(name=...,
tree=<coarse NameTreeNode>, atom_to_locant_hint=None) — a phase SCORE-01
attaches a coarse tree at EVERY return site, including the early returns).
Internal cascade order (preserved verbatim from composer.py inline body): 1. complex_ring detection + assembly (composer.py:1014-1066) — pushed to pool 2. polycyclic fallback when complex_ring rejected (composer.py:1083-1098) — direct return 3. partial_sat fallback when complex_ring rejected (composer.py:1103-1110) — direct return 4. heterocycle + lactone safety net (composer.py:1122-1152) 5. benzene (composer.py:1159-1173) 6. select_best_candidate selector + handler-level stereo injection (composer.py:1197-1305)
Returns None if all sub-paths fall through AND no Tier-A candidate ratio gate accepted. The surrounding dispatch_inner loop then continues to general_acyclic catch-all (Plan-08).
CRITICAL PRESERVATION CONSTRAINTS:
- The _tier_a_pool_count_before_complex slicing pattern at
composer.py:1197 MUST be preserved verbatim per a phase + the PHASE 146 PRESERVE block at composer.py:964-994.
The two
inject_stereo_from_locant_mapcall sites (composer.py:1301) are part of this composite per internal notes + a phase layering.assemble_ion_namepre-pool bypass (composer.py:751-768) is NOT in this composite — that’s the outer pre-pool path before dispatch_inner runs per internal notes.
IUPAC cite: (principal chain vs ring competition).
NAMING NOTE: ‘tier_a_ring’ is the RING CASCADE, not one producer. _is_tier_a_ring fires for every cyclic molecule whose parent is not a chain; the handler then tries complex_ring, polycyclic, partial_sat, heterocycle and benzene in the order listed above, adds the ring candidates to the pool and lets select_best_candidate choose. For spiro parents it also calls general_engine.name_general (_spiro_principal_suffix_preference). “Tier-A” is the ‘ring_a’ handler-policy tier of candidate_pool.HANDLER_POLICIES; it has nothing to do with the result tiers (pin_verified, systematic_verified, best_effort) of the public API. The id ‘tier_a_ring’ is used by the inner dispatch table, by tests and by log names, so it is kept.
- orthonym.assembly.handlers.tier_a_ring.name_tier_a_ring(features, mol=None, style='pin')#
Composite handler encoding the Tier-A pool-compete cascade.
Per -02 + internal notes: dispatch_inner cannot natively model pool-compete semantics; this composite encodes the full cascade in one entry while keeping dispatch_inner first-match-wins interface unchanged (internal notes).
Two early-return paths preserved verbatim from composer.py:996-1322: - The lactone safety-net path (composer.py:1126-1136) returns the
lactone name directly.
The polycyclic / partial_sat post-rejection fallback paths (composer.py:1083-1110) return their handler name directly.
Byte-identical preservation per DECOMP-03: every pool.add call, the
_tier_a_pool_count_before_complexslicing pattern, thedel _tier_a_pool._candidates[...]truncation, and the handler-level stereo-injection block are preserved verbatim.- Returns:
NamingResult –
- selector chose a candidate OR an early-return
sub-path produced a name.
- None: all sub-paths fell through AND the selector rejected the
Tier-A subset (low-ratio rescue did not fire).
- Return type:
NamingResult | None