orthonym.metrics.provenance#

Note

Internal API. Names and behaviour may change between releases.

: per-call naming provenance (tier derivation inputs).

Observation-only contextvars — the default naming path’s BEHAVIOR is untouched; sites merely record where the shipped name came from.

orthonym.metrics.provenance.GATE_OUTCOME_NOT_RUN = 'not_run'#

no gate call was recorded for this naming session (the default).

orthonym.metrics.provenance.GATE_OUTCOME_BYPASSED = 'bypassed'#

an outcome WAS recorded, but for a different string than the one shipped (e.g. _apply_trivial_fallback ships a retained name the gate never saw).

orthonym.metrics.provenance.GATE_OUTCOME_DISABLED = 'gate_disabled'#

_DISABLE_VALIDITY_GATE (env) or _disable_opsin_validity_gate (instance).

orthonym.metrics.provenance.GATE_OUTCOME_UNAVAILABLE = 'unavailable'#

jar absent / transient OPSIN failure — the gate failed OPEN.

orthonym.metrics.provenance.GATE_OUTCOME_DESCRIPTIVE_FALLBACK = 'descriptive_fallback'#

the name handed to the gate was already a descriptive-fallback sentinel, so the gate skipped it (nothing was verified).

orthonym.metrics.provenance.GATE_OUTCOME_SUPPRESSED = 'suppressed'#

the gate suppressed a candidate to the descriptive fallback.

orthonym.metrics.provenance.GATE_OUTCOME_AMBIGUOUS_SUPPRESSED = 'opsin_ambiguous_suppressed'#

the gate suppressed a candidate that OPSIN parsed to the input but read as ambiguous (warning APPEARS_AMBIGUOUS, validation.opsin_ambiguity); the name is named once more with every locant cited before it is withdrawn.

orthonym.metrics.provenance.GATE_OUTCOME_SELF01 = 'self_consistency_verified'#

OPSIN parsed the FULL name and returned verdict “ok”. The ONE state that may claim a bare.

orthonym.metrics.provenance.GATE_OUTCOME_SELF01_CONSTITUTION_ONLY = 'self_consistency_constitution_only'#

judged the stereo-STRIPPED parse, so the CONSTITUTION is verified and the stereo layer is NOT.

Type:

the OPSIN-validity stereo carve-out

orthonym.metrics.provenance.GATE_OUTCOME_SELF01_INCONCLUSIVE = 'self_consistency_inconclusive'#

ran and could not compare (fail-OPEN) — nothing was proven.

orthonym.metrics.provenance.GATE_OUTCOME_SELF01_SKIPPED = 'self_consistency_skipped'#

was a no-op (_SC_MODE == “off”, or no input SMILES to compare to).

orthonym.metrics.provenance.GATE_OUTCOME_SELF01_WARN_MISMATCH = 'self_consistency_warn_mismatch'#

PROVED a different molecule but _SC_MODE == “warn” shipped it.

orthonym.metrics.provenance.GATE_OUTCOME_STEREO_RECOMPOSED = 'stereo_omission_full_key_recomposed'#

the general-fallback stereo-OMISSION reclaim COMPOSED the input’s dropped stereo back onto a constitution-verified flat name and re-verified that the composed name recomputes to the input’s FULL InChIKey. That check is strictly STRONGER than (it compares the full stereo layer, not just the skeleton), so this is a genuine VERIFIED state — not a by-design carve-out that ships unproven. (It used to be recorded as carveout:stereo_omission_reanchor, which mislabels a full-key-verified name as unverified — a review -P2 F3.)

orthonym.metrics.provenance.GATE_OUTCOME_CARVEOUT_PREFIX = 'carveout:'#

prefix for the ten by-design return name carve-outs: carveout:<slug>.

orthonym.metrics.provenance.GATE_OUTCOME_FULL_KEY_VERIFIED = 'full_key_round_trip_verified'#

at a general tier, name_tiered checked the shipped string itself because no verified outcome was recorded for it (an offer the pool picked after the gate had seen another string, or an outcome recorded for a different string): OPSIN read the name and the FULL InChIKey of what it read equals the input’s. That is the round trip the paper claims for every shown name, and stronger than, so it claims the plain “verified” label.

Type:

claims conformance (2026-09-27)

orthonym.metrics.provenance.GATE_OUTCOME_TWIN_PREFIX = 'twin_verified:'#

OPSIN has no grammar for the name, and it reads a twin spelling of the same structure (validation.twin_spellings) to the input’s FULL InChIKey. The producer owns the spelling; the twin proves the structure.

Type:

prefix of twin_verified:<rule ids>

orthonym.metrics.provenance.GATE_OUTCOME_ACID_SPLIT_PREFIX = 'acid_split_verified:'#

OPSIN has no grammar for the name (a di- or poly-anhydride), and it reads each acid the name cites, to the structures a poly-anhydride built from those acids has the input’s FULL InChIKey and descriptors of (validation.anhydride_acids). OPSIN never reads the name, or a spelling of it.

Type:

prefix of acid_split_verified:<rule>

orthonym.metrics.provenance.VERIFIED_OPSIN_LABELS = ('verified', 'verified_twin', 'verified_acid_split')#

the name itself, its twin spelling, or the acids of a poly-anhydride name

Type:

the opsin labels of a name that was read back to the input’s full structure

orthonym.metrics.provenance.VERIFIED_FIELD_BY_OPSIN_LABEL = {'verified': 'opsin', 'verified_acid_split': 'opsin_acids', 'verified_constitution_only': 'opsin_constitution', 'verified_twin': 'opsin_twin'}#

the verified field of a row by the opsin label of its gate outcome

orthonym.metrics.provenance.VERIFIED_FIELD_FOR_TIER = ('opsin', 'opsin_twin', 'opsin_acids', 'identity')#

a read-back of the name, or of its twin spelling, of the acids of a poly-anhydride name, or the exact-match list identity

Type:

the values of verified that earn a tier named “verified”

orthonym.metrics.provenance.note_touched(*names)#

Public: record that names were written by a path that bypasses the setter functions (a replay-memo HIT writes the ContextVars directly). Lets an enclosing touched-log still capture them.

orthonym.metrics.provenance.push_touched_log()#

Arm a fresh touched-var log for the current nested call and return it. Pair with:func:pop_touched_log in a try/finally (LIFO).

orthonym.metrics.provenance.pop_touched_log()#

Disarm the innermost touched-var log and return it (LIFO).

orthonym.metrics.provenance.push_non_pin_log()#

Arm a fresh record-call log; pair with:func:pop_non_pin_log (LIFO).

orthonym.metrics.provenance.pop_non_pin_log()#

Disarm the innermost record-call log and return it (LIFO).

orthonym.metrics.provenance.clear_provenance()#
orthonym.metrics.provenance.append_missing(base, extra)#

base followed by the items of extra that base does not hold, in order: the merge restore_provenance makes of a snapshot and the live value of an accumulating record, and the merge a memo hit makes of the record its fresh call left. The records only grow within a naming call, so a giant molecule’s tuples hold thousands of names; the membership test is a set lookup, not a scan of base for each item (that scan made every merge quadratic in the length of the record).

orthonym.metrics.provenance.restore_provenance(snapshot)#

Re-set every provenance ContextVar from a get_provenance dict.

The inverse of:func:get_provenance. A caller that speculatively runs a producer which records provenance (record_source et al.) and then REJECTS its candidate must restore the pre-attempt provenance, otherwise the kept emission carries the rejected producer’s label. Used by the Engine-3 NP→von-Baeyer downgrade, whose _try_general_engine_recovery probe stamps source="general_engine" before the RT gate can decline it.

The name-scoped non-PIN record (record_non_pin_fragment) is the one exception: it only grows within a naming call (clear_provenance resets it at a call boundary), so a restore keeps the fragments recorded since the snapshot. A rejected attempt leaves cache entries behind – the scope memo (assembly.memo.cache_or_compute) and the fragment cache return a stored string without the record its computation made – so dropping the record let a later cache hit ship a recorded non-PIN spelling as if none had been recorded, and the result then depended on ORTHONYM_MEMO (branch review, 2026-09-28: ‘C[C@@H](CN(C[C@@H]1CCC=CC1)C)O’ was named at the PIN tier with the memo on and abstained with it off, through the ‘(R)-(cyclohex-3-en-1-yl)meth’ record that rules.pin_vocabulary.promote_at_pin_tier rolled back). A kept record only demotes a name that CONTAINS the recorded spelling.

class orthonym.metrics.provenance.isolated_provenance#

Bases: object

Context manager: every provenance variable, the name-scoped records included, is exactly what it was on entry when the body exits, and the record calls the body made are taken out of the armed record-call logs again.

For a check that names prefixes only to compare two parents (perception.chains.chain_parent_prefix_seniority) and whose own verdict is recorded after it, outside the body. Unlike:func:restore_provenance, the non-PIN records the body made are dropped too: the body’s namings run through name_substituent_for_ordering, whose memo and fragment-cache writes are discarded on exit (memo.push_sandbox, speculative_fragment_naming), so no later cache hit can return a string whose record was dropped; a record kept from such a body would lower the label of a name the real naming built by another route only because the check ran. A provenance read in the body stays in the logs (PROVENANCE_READ), so an enclosing memo still declines to store a call whose path read provenance. Only a variable the body changed is written back.

orthonym.metrics.provenance.record_source(source, opsin=None)#
orthonym.metrics.provenance.producer_record()#

The producer record of the call so far: (source, opsin) as record_source() left them. Pair with:func:restore_producer_record around an attempt whose name may be discarded. The value read is only written back to the record, never used to build a name, so the read is not logged as a provenance read (PROVENANCE_READ).

orthonym.metrics.provenance.restore_producer_record(record)#

Re-set source and opsin to record (a:func:producer_record value). A variable that already holds its value is not written, so a call that left the record unchanged touches nothing.

orthonym.metrics.provenance.record_stereo_unexpressed(flag)#

T6.4: mark the current emission as constitution-only (stereo defined on the input but not expressed in the name). Set at the flagged best-effort ship site; read by name_tiered.

orthonym.metrics.provenance.record_suffix_free_prefix_name(flag)#

: mark the current emission as citing the principal characteristic group as a PREFIX with no suffix – ill-formed per (see the ContextVar comment). Set only at the PG-suppressed assembly site; read by name_tiered and surfaced per row so can enumerate the debt.

orthonym.metrics.provenance.record_gate_outcome(outcome, name)#

T1: record what _final_opsin_validity_gate DID, and for WHICH string. Called at every return of the gate (and of _self_consistency_decision, which owns the gate’s exits).

name must be the string the gate is about to RETURN, not the one it was handed — on a suppression those differ, and the outcome belongs to what actually ships.

orthonym.metrics.provenance.carveout_outcome(slug)#

carveout:<slug> — a by-design return name carve-out. The name is shipped deliberately despite an OPSIN coverage gap; it is NOT verified.

orthonym.metrics.provenance.twin_outcome(rules)#

twin_verified:<rule ids> — the name ships on its construction, and a twin spelling OPSIN reads round-trips to the input’s full InChIKey.

orthonym.metrics.provenance.acid_split_outcome(rule)#

acid_split_verified:<rule> — the name ships on its construction, and the acids it cites, read by OPSIN, build a structure that is the input’s (see:data:GATE_OUTCOME_ACID_SPLIT_PREFIX).

orthonym.metrics.provenance.resolve_gate_outcome(outcome, outcome_name, shipped_name)#

The outcome that applies to shipped_name — deny-by-default.

A recorded outcome only counts for the exact string it was recorded for. Paths that ship a name the gate never saw (_apply_trivial_fallback returns a retained name only AFTER the gate has suppressed the systematic candidate, so the recorded outcome then belongs to a DIFFERENT string) get bypassed, never the previous name’s verdict.

orthonym.metrics.provenance.opsin_label_for_gate_outcome(outcome)#

verified / verified_constitution_only / verified_twin / verified_acid_split / unverified.

Allowlist, so it fails closed: any outcome not explicitly listed — including a state that does not exist yet — reports unverified.

orthonym.metrics.provenance.gate_token_for_gate_outcome(outcome)#

The gates_passed token this outcome earns, or None if it earns none.

orthonym.metrics.provenance.record_general_ring_prefix()#

-T1c: mark that a PIN-path (composer) name contains a ring substituent prefix that only the GENERAL tier could produce.

Tier and is_pin are decided from source alone (namer.py:2728), and every composer emission reports source='pin_path' -> T1, is_pin=True. A systematic replacement / von Baeyer substituent form is a VALID name but not the PREFERRED one, so shipping it under that label would assert PIN status for a non-PIN name – measured on OC(=O)CC12CC3CC(O)(CC(C3)C1)C2, whose ring PIN is the retained name adamantane, not tricyclo[3.3.1.1^3,7]decane.

This flag lets name_tiered demote exactly those emissions to the general tier while leaving every other composer emission untouched. It is deliberately one-way (never cleared) for the duration of a naming call.

orthonym.metrics.provenance.record_pin_promotion_rerun()#

Mark the name about to ship as built by the PIN tier’s promotion re-run (see _PIN_PROMOTION_RERUN): labelled pin_unverified, never pin_verified. Only when a breadth piece the re-run kept (record_breadth_promotion) is part of the name: a name whose every piece a strict producer built (assembly.strict_ring_prefix) is the strict path’s own name. A piece kept in a route the re-run then left does not count (the label would depend on the input atom order); with no name to read the whole call is flagged.

orthonym.metrics.provenance.record_breadth_promotion(name)#

Record a name promote_at_pin_tier kept: a piece a best-effort producer built (see _BREADTH_PROMOTED_NAMES).

orthonym.metrics.provenance.record_non_pin_fragment(fragment)#

Record a name fragment that a producer built and that is valid (the whole name is still round-trip verified) but is never part of a PIN – e.g. a carbon-substituted N+ cited as ‘trimethylazaniumyl’, whose PIN form ‘…methanaminiumyl’ cannot be verified.

Unlike record_general_ring_prefix this is NAME-SCOPED: name_tiered demotes the shipped name only if it CONTAINS a recorded fragment. A producer that runs speculatively (a // sort key naming a prefix) or whose candidate is discarded (a later gate rejects it and another path names the molecule) therefore cannot demote a name that does not carry its fragment.

orthonym.metrics.provenance.record_non_pin_label(fragment, rule=None, detail='')#

Record fragment as a LABEL-ONLY non-PIN part (see _NON_PIN_LABELS): a shipped name that contains it is labelled below the PIN; the PIN tier’s promotion re-run still ships it.

rule (a Blue Book rule id, with detail saying how) is the reason the fragment is not the PIN form: the row of a name the record lowers lists it in spelling_failures (see non_pin_label_rule_failures).

orthonym.metrics.provenance.record_uncertified_pin_name(name)#

Record name as a PIN-form name whose preferred status the Blue Book leaves open (see _UNCERTIFIED_PIN_NAMES): a shipped name that contains it is labelled pin_unverified, is_pin False.

orthonym.metrics.provenance.record_verified_through_split(name)#

Record that name was read back to the drawing of the input without its dative bonds (assembly.metal_offer), not to the drawing as written. verified_field then reports opsin_split for the name in place of opsin.

orthonym.metrics.provenance.verified_field(verified, prov, name)#

The verified field of a row: opsin becomes opsin_split for a name recorded by record_verified_through_split() (OPSIN read the whole name back to the drawing of the input without its dative bonds, whose standard InChIKey is the reference, and an independent count of atoms and charges ties that drawing to the input); every other value is unchanged.

orthonym.metrics.provenance.record_derived_uncertified_pin_name(source, derived)#

Record derived as an uncertified PIN-form name when source contains a recorded one: a name built from source by a splice that leaves every locant of source as it is (an isotopic descriptor that needs no locant) keeps the status of source, since the recorded string does not survive the splice.

orthonym.metrics.provenance.record_derived_non_pin_fragment(source, derived, _item=None)#

Record derived as a non-PIN fragment when source contains a recorded one: a form built from a non-PIN name by a string conversion (an acid name turned into its acyl prefix) keeps the source’s status, since the recorded substring does not survive the conversion.

The call is logged as it was made (('D', source, derived)), and the record it makes when it fires is not: a replay of the call decides again against the record of the replay, where a replay of the record would add derived also when the record holds no fragment of source. A replay passes the stored item as _item, so the logs share it.

orthonym.metrics.provenance.compact_non_pin_log(items, cap=None)#

The record-call log items of a call, as an equivalent tuple with the no-op repeats removed: its replay (replay_non_pin_log()) leaves any record as the replay of items leaves it; None when the call read provenance (PROVENANCE_READ) or the result holds a derived call and more than cap items (NON_PIN_LOG_CAP; a log of plain items is stored whatever its length, as it always was). The record only grows under a replay, so an item is left out when it changes nothing after the items kept before it:

  • a fragment recorded before (record_non_pin_fragment adds it whatever the record holds);

  • a derived call whose derived is such a fragment (it adds that or nothing);

  • a repeat of a derived call, unless a kept item since its last kept occurrence adds a fragment that is a substring of its source: with none, the record holds a fragment of source at the repeat only if it held one at the earlier call, which then added derived already. Dropping every repeat would lose the derived of a call that found no fragment of source the first time and finds one that a later item recorded.

Each kept item is tested once against each derived call, and a log with a derived call stops at the cap, so the cost is bounded by the square of the cap.

orthonym.metrics.provenance.replay_non_pin_log(items)#

Make the record calls of a stored log again, in order, into every armed log: a plain item through:func:record_non_pin_fragment, a derived call through record_derived_non_pin_fragment().

orthonym.metrics.provenance.name_carries_non_pin_part(prov, name, *, label_forms=True)#

True when name must not be labelled a PIN: the whole-call general_ring_prefix flag, the whole-call pin_promotion_rerun flag (the PIN tier’s promotion re-run built the name), or a recorded non-PIN fragment it contains. label_forms is passed to rules.pin_vocabulary. non_pin_vocabulary.

orthonym.metrics.provenance.non_pin_label_rule_failures(prov, name)#

The reasons of the label-only records that name contains and that name a rule (record_non_pin_label(..., rule=...)), as spelling_failures entries {"rule":..., "detail":...} in record order, one per rule and detail.

orthonym.metrics.provenance.get_provenance()#