orthonym.assembly.t4_coverage#

Note

Internal API. Names and behaviour may change between releases.

universal coverage-by-construction namer (best-effort tier ONLY).

Reached only on the general-fallback best-effort recovery path – master switch self._general_fallback in namer.py (its early return None when that flag is False; general_fallback_unverified is a secondary, narrower flag on the same path) – which itself only runs AFTER the PIN/systematic path has already abstained. So this module can never change a PIN emission; it only ever fills in where the PIN tiers were silent.

Produces an atom-complete, E1-certified name or None (clean abstain, never a partial name). validation.e1_certificate.verify_certificate is the atom-coverage certificate; downstream (OPSIN round-trip) is the second half of the 0-wrong net, not a substitute for E1 here.

This module is the Task 2 SKELETON only: the control flow + the locked _Candidate interface. Task 3 fills in _best_effort_candidate with the real parent+substituents producer; Tasks 4-6 add the per-class cascade and wire this namer into namer.py.

orthonym.assembly.t4_coverage.last_t4_was_final_floor()#

True iff the most recent name_t4_complete call in this context returned the final rung’s universal floor: no engine rung (the general engine with the perceived features or a feature override, the polyol rung) gave a candidate.

orthonym.assembly.t4_coverage.last_t4_was_floor_substitute()#

True iff the most recent name_t4_complete call in this context returned the universal floor standing in for a rung candidate that failed the full- InChIKey round trip (_prefer_verified_floor). Such a rung candidate is the mark of a degraded naming context (the recovery inside name; see namer._try_besteffort_clean_general_fallthrough), where a clean context can still build the engine’s own verified name.

orthonym.assembly.t4_coverage.reset_t4_floor_substitute()#

Clear the flags before a call whose producer may be replaced (tests stub name_t4_complete), so a stale value is never read.

orthonym.assembly.t4_coverage.name_t4_complete_candidate(mol, features)#

name_t4_complete returning the whole candidate (floor_substitute included). Same gates, same name.

orthonym.assembly.t4_coverage.name_t4_complete(mol, features)#

Best-effort name for mol, or None (clean abstain).

Control flow (locked for Tasks 3-6):
  1. Ask _best_effort_candidate for a name + its E1 proof object.

  2. No candidate at all -> abstain (None).

  3. A candidate with no proof object (result_obj is None) ships unchecked – E1 has nothing to verify.

  4. Otherwise the candidate must pass verify_certificate or it is discarded – never patched, never shipped anyway.

Audit-coverage note (Fix 3a, final review): this returns a bare str, not the GeneralEngineResult with its atom->token bindings. So when namer.py ships a name it CANNOT populate the binding-proof ledger – _record_binding_proof runs only on the engine’s own-name else branch, never on the branch. That is an audit-coverage gap, not a correctness one: it is benign under the default (binding_proof off), and T4’s 0-wrong net is E1 (proven internally here) + (the OPSIN round-trip in namer.py), neither of which needs the ledger. A follow-on wanting binding proofs would return the _Candidate (which carries result_obj) instead of a bare string.

a phase Task 2b: ALSO requires verify_spine (audit mode) to pass. verify_certificate proves only the flat atom partition (P1’s job); the binding spine additionally proves bond totality (P2), token-span anchoring (P4/P5) and arity (P6) – axes E1 cannot see at all (a name whose bindings silently re-fragment a ring, e.g. spelling cyclohexane as two disjoint propyl halves, passes E1 outright). mode="audit" stays the base mode (never “strict” here): a blanket strict flip would ALSO change P2’s bond-linkage inference policy and P5/P6’s unrelated unproven-codes severity, none of which this task’s scope covers. allow_charged=False matches the verify_certificate call above so the two proofs never disagree about scope. A spine failure voids the candidate exactly like an E1 failure does – no new control flow.

a phase Task 4: escalate=STRICT_STEREO_CHARGE_AXES promotes JUST the P8 stereo axis and P3’s CHARGE_UNVERIFIED to error severity, on top of mode="audit" – the coverage certificate’s stereo axis was shipped audit-only in Task 3 (findings recorded, ok never affected); the Task 4 diagnostic re-scan found 0 remaining false positives and 0 genuine stereo drops, so this makes it enforcing. allow_charged=False means NET_CHARGE_OUT_OF_SCOPE (unconditional “error”, not mode/escalate- gated) already voids any nonzero-net-charge candidate reaching this point regardless of this promotion; what newly blocks is a ZWITTERION (net charge 0, but individual atoms charged) whose charges no producer threaded through charge_atom_ids – measured byte-identical on a dev split best-effort (see the Task 4 report).

Task 2a first wired this and measurably false-voided 3 correct, OPSIN-round-tripping a dev split rows via a pre-existing P6 (token_arity) false-positive on replacement-nomenclature substituent tokens; that bug is fixed (Task 2b, name_morphemes.py::_evaluate’s multiplier short-circuit now also skips zero-atom REPL segments) and the a dev split best-effort before/after re-measurement is BYTE-IDENTICAL emit/rt_exact – see internal notes.

orthonym.assembly.t4_coverage.name_prefix_order_fallback(mol)#

The universal floor’s name of mol with its parent’s prefixes cited out of the order, or None.

The last resort of the best-effort tier for a molecule it would otherwise not name (namer.Orthonym._maybe_prefix_order_fallback is the only caller, and it adds the checks that make a shipped name the input’s): the round trip (OPSIN’s SMILES read by RDKit) can depend on the citation order of the prefixes, so a floor name in the order (the Blue Book order) can fail at the stereo layer although its descriptors are right – see universal_substituent._prefix_order_fallback. A name is returned only when that rung built it: the -ordered spelling failed at the stereo layer only and this spelling reads back to the input’s full InChIKey. Such a name breaks on purpose and is never a PIN. Fail-closed: any error returns None.