feat(logos): bulk live morph authority on turn + teaching seams
Wire observed-HE morph constraint into CognitiveTurn answer path and unify teaching store with the same pure decision function used by the four-arm ablation. Typed LogosConstraint IR carries rule/morph/pack/span provenance without free meaning dicts. Executable mode abstains on plural-vs-singular exclusivity; metadata remains bit-identical to canonical; adversarial OOV/missing fails closed. Residual dual-system debt documented honestly (cue-table linguistic_pipeline still parallel).
This commit is contained in:
parent
25504c9d0b
commit
91bb0a1b8c
12 changed files with 562 additions and 61 deletions
|
|
@ -482,13 +482,54 @@ class CognitiveTurnPipeline:
|
||||||
)
|
)
|
||||||
surface = resolved.surface
|
surface = resolved.surface
|
||||||
articulation_surface = resolved.articulation_surface
|
articulation_surface = resolved.articulation_surface
|
||||||
|
authority_source = resolved.authority
|
||||||
|
|
||||||
|
# === LOGOS MORPH AUTHORITY (bulk live seam) ===
|
||||||
|
# Same pure decision function as teaching store + four-arm ablation.
|
||||||
|
# Executable morph may force abstain/refuse; never soft-pass a
|
||||||
|
# certified singular-exclusivity claim against observed plural HE.
|
||||||
|
# English-only turns with no HE surface → no-op (None).
|
||||||
|
logos_decision_kind = ""
|
||||||
|
logos_decision_reason = ""
|
||||||
|
logos_rule_id = ""
|
||||||
|
logos_constraint_id = ""
|
||||||
|
logos_decision = None
|
||||||
|
try:
|
||||||
|
from generate.observed_he_morph_v0.authority import (
|
||||||
|
decision_as_coherence_refusal,
|
||||||
|
evaluate_logos_on_text,
|
||||||
|
first_logos_constraint,
|
||||||
|
logos_blocks_certified_answer,
|
||||||
|
)
|
||||||
|
|
||||||
|
logos_decision = evaluate_logos_on_text(text=text, mode="executable")
|
||||||
|
if logos_decision is not None:
|
||||||
|
logos_decision_kind = logos_decision.kind.value
|
||||||
|
logos_decision_reason = logos_decision.reason
|
||||||
|
logos_rule_id = logos_decision.rule_id or ""
|
||||||
|
lc = first_logos_constraint(logos_decision)
|
||||||
|
if lc is not None:
|
||||||
|
logos_constraint_id = lc.constraint_id
|
||||||
|
if logos_blocks_certified_answer(logos_decision):
|
||||||
|
refusal = decision_as_coherence_refusal(logos_decision)
|
||||||
|
surface = refusal.surface_message or refusal.message
|
||||||
|
articulation_surface = surface
|
||||||
|
authority_source = "logos_morph_constraint"
|
||||||
|
except Exception:
|
||||||
|
# Pack load / catalog failure must not crash the turn spine;
|
||||||
|
# English path continues without morph authority.
|
||||||
|
logos_decision = None
|
||||||
|
|
||||||
# SUBSTRATE_BYPASS_HAZARD telemetry (data-driven roadmap).
|
# SUBSTRATE_BYPASS_HAZARD telemetry (data-driven roadmap).
|
||||||
# Only populated when a graph existed yet substrate did not win.
|
# Only populated when a graph existed yet substrate did not win.
|
||||||
# This is *observability only* — never used to change control flow
|
# This is *observability only* — never used to change control flow
|
||||||
# after the fact, never folded into trace_hash in Phase A.
|
# after the fact, never folded into trace_hash in Phase A.
|
||||||
substrate_hazard: tuple[str, ...] = ()
|
substrate_hazard: tuple[str, ...] = ()
|
||||||
if effective_graph is not None and resolved.authority not in ("substrate_realizer", "realizer"):
|
if effective_graph is not None and authority_source not in (
|
||||||
|
"substrate_realizer",
|
||||||
|
"realizer",
|
||||||
|
"logos_morph_constraint",
|
||||||
|
):
|
||||||
reasons: list[str] = []
|
reasons: list[str] = []
|
||||||
if not effective_graph.is_fully_grounded():
|
if not effective_graph.is_fully_grounded():
|
||||||
reasons.append("unfilled_pending_slots")
|
reasons.append("unfilled_pending_slots")
|
||||||
|
|
@ -501,6 +542,11 @@ class CognitiveTurnPipeline:
|
||||||
if not _is_useful_surface(realized_plan.surface):
|
if not _is_useful_surface(realized_plan.surface):
|
||||||
reasons.append("realizer_surface_not_useful")
|
reasons.append("realizer_surface_not_useful")
|
||||||
substrate_hazard = tuple(reasons)
|
substrate_hazard = tuple(reasons)
|
||||||
|
if logos_decision is not None and logos_blocks_certified_answer(logos_decision):
|
||||||
|
substrate_hazard = substrate_hazard + (
|
||||||
|
f"logos_morph_{logos_decision.kind.value}",
|
||||||
|
logos_decision.reason,
|
||||||
|
)
|
||||||
|
|
||||||
# Phase C (Geometric Anti-Unification) — read-only telemetry instrumentation.
|
# Phase C (Geometric Anti-Unification) — read-only telemetry instrumentation.
|
||||||
# Captures the graph-structural context around any OOV/pending "hole"
|
# Captures the graph-structural context around any OOV/pending "hole"
|
||||||
|
|
@ -713,6 +759,10 @@ class CognitiveTurnPipeline:
|
||||||
# recognition wins (earlier-fail boundary) over generation.
|
# recognition wins (earlier-fail boundary) over generation.
|
||||||
_generation_refusal_reason = getattr(response, "refusal_reason", "") or ""
|
_generation_refusal_reason = getattr(response, "refusal_reason", "") or ""
|
||||||
refusal_reason = _recognition_refusal_reason or _generation_refusal_reason
|
refusal_reason = _recognition_refusal_reason or _generation_refusal_reason
|
||||||
|
# Logos morph abstain/fail-closed is answer-authority: surface the reason
|
||||||
|
# when morph blocked certification (does not override recognition refuse).
|
||||||
|
if not refusal_reason and logos_decision_kind in ("abstain", "fail_closed"):
|
||||||
|
refusal_reason = logos_decision_reason
|
||||||
|
|
||||||
# Phase B — Quantized Topological Hashing for the cognitive spine.
|
# Phase B — Quantized Topological Hashing for the cognitive spine.
|
||||||
# Include the discrete (string-only) topological form of the
|
# Include the discrete (string-only) topological form of the
|
||||||
|
|
@ -825,13 +875,19 @@ class CognitiveTurnPipeline:
|
||||||
# authority_source makes the winner of the substrate vs legacy
|
# authority_source makes the winner of the substrate vs legacy
|
||||||
# decision first-class evidence (visible in trace, workbench,
|
# decision first-class evidence (visible in trace, workbench,
|
||||||
# evals). substrate_hazard is the precise bypass signal.
|
# evals). substrate_hazard is the precise bypass signal.
|
||||||
authority_source=resolved.authority,
|
# Logos morph may override to "logos_morph_constraint" when it
|
||||||
|
# blocks certified answers (same decision fn as teaching/ablation).
|
||||||
|
authority_source=authority_source,
|
||||||
substrate_hazard=substrate_hazard,
|
substrate_hazard=substrate_hazard,
|
||||||
oov_geometric_context=oov_geometric_context,
|
oov_geometric_context=oov_geometric_context,
|
||||||
# 3-lang depth unification: surface the same data at top level on result
|
# 3-lang depth unification: surface the same data at top level on result
|
||||||
# (extracted from pre-computed node_depths var or oov_geometric_context to keep single source)
|
# (extracted from pre-computed node_depths var or oov_geometric_context to keep single source)
|
||||||
node_depths=node_depths if node_depths else None,
|
node_depths=node_depths if node_depths else None,
|
||||||
graph_anti_unify=(oov_geometric_context or {}).get("graph_anti_unify") if oov_geometric_context else None,
|
graph_anti_unify=(oov_geometric_context or {}).get("graph_anti_unify") if oov_geometric_context else None,
|
||||||
|
logos_decision_kind=logos_decision_kind,
|
||||||
|
logos_decision_reason=logos_decision_reason,
|
||||||
|
logos_rule_id=logos_rule_id,
|
||||||
|
logos_constraint_id=logos_constraint_id,
|
||||||
)
|
)
|
||||||
|
|
||||||
# ------------------------------------------------------------------
|
# ------------------------------------------------------------------
|
||||||
|
|
|
||||||
|
|
@ -207,3 +207,13 @@ class CognitiveTurnResult:
|
||||||
# Never folded into trace_hash (observational only, like oov_geometric_context).
|
# Never folded into trace_hash (observational only, like oov_geometric_context).
|
||||||
node_depths: dict | None = None
|
node_depths: dict | None = None
|
||||||
graph_anti_unify: dict | None = None
|
graph_anti_unify: dict | None = None
|
||||||
|
|
||||||
|
# --- Logos morph authority (observational + outcome-affecting when non-pass) ---
|
||||||
|
# Populated when observed HE surface is present in the turn input and the
|
||||||
|
# shared ``evaluate_logos_on_text`` path runs. Empty kind == not consulted.
|
||||||
|
# Never folded into trace_hash as a separate key (surface/refusal already
|
||||||
|
# capture the user-visible effect when morph blocks certification).
|
||||||
|
logos_decision_kind: str = ""
|
||||||
|
logos_decision_reason: str = ""
|
||||||
|
logos_rule_id: str = ""
|
||||||
|
logos_constraint_id: str = ""
|
||||||
|
|
|
||||||
|
|
@ -15,6 +15,7 @@ from core.semantic_primitives.model import (
|
||||||
Entity,
|
Entity,
|
||||||
Event,
|
Event,
|
||||||
IdentityBridge,
|
IdentityBridge,
|
||||||
|
LogosConstraint,
|
||||||
MissingReferent,
|
MissingReferent,
|
||||||
Operator,
|
Operator,
|
||||||
OperatorClass,
|
OperatorClass,
|
||||||
|
|
@ -36,6 +37,7 @@ __all__ = [
|
||||||
"Entity",
|
"Entity",
|
||||||
"Event",
|
"Event",
|
||||||
"IdentityBridge",
|
"IdentityBridge",
|
||||||
|
"LogosConstraint",
|
||||||
"MissingReferent",
|
"MissingReferent",
|
||||||
"Operator",
|
"Operator",
|
||||||
"OperatorClass",
|
"OperatorClass",
|
||||||
|
|
|
||||||
|
|
@ -86,6 +86,62 @@ class ProvenanceSpan:
|
||||||
raise ValidationError("ProvenanceSpan requires 0 <= start <= end")
|
raise ValidationError("ProvenanceSpan requires 0 <= start <= end")
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass(frozen=True, slots=True)
|
||||||
|
class LogosConstraint:
|
||||||
|
"""Language-independent morph→constraint record with full provenance.
|
||||||
|
|
||||||
|
Used by the live Logos authority path (teaching + CognitiveTurn). Fields
|
||||||
|
are typed — not a free-form meaning dict. rule_id + morphology_id +
|
||||||
|
source_pack_id + source_span make the constraint falsifiable and replayable.
|
||||||
|
"""
|
||||||
|
|
||||||
|
constraint_id: str
|
||||||
|
kind: str
|
||||||
|
rule_id: str
|
||||||
|
lemma: str
|
||||||
|
root: str
|
||||||
|
surface: str
|
||||||
|
morphology_id: str
|
||||||
|
source_pack_id: str
|
||||||
|
source_span: ProvenanceSpan
|
||||||
|
language: str = "he"
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
_require_nonempty(self.constraint_id, "LogosConstraint.constraint_id")
|
||||||
|
_require_nonempty(self.kind, "LogosConstraint.kind")
|
||||||
|
_require_nonempty(self.rule_id, "LogosConstraint.rule_id")
|
||||||
|
_require_nonempty(self.morphology_id, "LogosConstraint.morphology_id")
|
||||||
|
_require_nonempty(self.source_pack_id, "LogosConstraint.source_pack_id")
|
||||||
|
if not isinstance(self.source_span, ProvenanceSpan):
|
||||||
|
raise ValidationError(
|
||||||
|
"LogosConstraint.source_span must be a ProvenanceSpan"
|
||||||
|
)
|
||||||
|
_reject_dict_blob(self.kind, "LogosConstraint.kind")
|
||||||
|
|
||||||
|
def as_dict(self) -> dict[str, Any]:
|
||||||
|
return {
|
||||||
|
"constraint_id": self.constraint_id,
|
||||||
|
"kind": self.kind,
|
||||||
|
"rule_id": self.rule_id,
|
||||||
|
"lemma": self.lemma,
|
||||||
|
"root": self.root,
|
||||||
|
"surface": self.surface,
|
||||||
|
"morphology_id": self.morphology_id,
|
||||||
|
"source_pack_id": self.source_pack_id,
|
||||||
|
"source_span": [self.source_span.start, self.source_span.end],
|
||||||
|
"language": self.language,
|
||||||
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def provenance_complete(self) -> bool:
|
||||||
|
return bool(
|
||||||
|
self.rule_id
|
||||||
|
and self.morphology_id
|
||||||
|
and self.source_pack_id
|
||||||
|
and self.source_span.end >= self.source_span.start
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, slots=True)
|
@dataclass(frozen=True, slots=True)
|
||||||
class Entity:
|
class Entity:
|
||||||
"""Persistent identity anchor with typed id, name, type, and frame bindings."""
|
"""Persistent identity anchor with typed id, name, type, and frame bindings."""
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,35 @@
|
||||||
|
# Logos bulk live-authority residual (honest accounting)
|
||||||
|
|
||||||
|
**Date:** 2026-07-20
|
||||||
|
**Branch:** `feat/logos-bulk-live-authority`
|
||||||
|
**Claim:** bulk Logos **impact** landed on live seams with sealed four-arm proof — **not** full Logos complete.
|
||||||
|
|
||||||
|
## What now has live authority
|
||||||
|
|
||||||
|
| Seam | Module | Decision function | Outcome effect |
|
||||||
|
|------|--------|-------------------|----------------|
|
||||||
|
| Teaching store `add` | `teaching/store.py` → `authority.evaluate_logos_on_text` | `apply_he_morph_constraint` | ABSTAIN/FAIL_CLOSED → proposal `CONTESTED` |
|
||||||
|
| CognitiveTurn answer path | `core/cognition/pipeline.py` after `resolve_surface` | same | ABSTAIN/FAIL_CLOSED → typed abstention surface, `authority_source=logos_morph_constraint` |
|
||||||
|
| Four-arm ablation | `generate/observed_he_morph_v0/ablation.py` | same | sealed metrics |
|
||||||
|
|
||||||
|
Shared typed IR: `CanonicalConstraint` → `LogosConstraint` (`core.semantic_primitives`) with rule_id, morphology_id, source_pack_id, source_span — no free meaning dict on the exercised path.
|
||||||
|
|
||||||
|
## Sealed improvement proof (expected signals)
|
||||||
|
|
||||||
|
- `metadata_bit_identical_to_canonical=true` — morph observation does not smuggle outcomes
|
||||||
|
- `executable_changed_decision=true` — plural HE blocks singular-exclusivity claims
|
||||||
|
- `wrong_count=0`, `refusal_correct=true`, `provenance_complete=true`
|
||||||
|
- Dual-run digests identical (determinism)
|
||||||
|
|
||||||
|
## Residual dual-system debt (not closed)
|
||||||
|
|
||||||
|
1. **`generate/linguistic_pipeline` cue tables** (English “sold”/“mkr” hand maps) remain a **parallel** constraint producer for the governance demo path; they do **not** consult `packs/data/he_*` morphology and are **not** answer authority over field/fail-closed.
|
||||||
|
2. **Lexicon breadth** — only `he_logos_micro_v1` observed surfaces; no full HE/GRC tables, no binyan universals, no GRC case→role.
|
||||||
|
3. **Legacy math / meaning_graph IR** — still parallel outside the Logos morph seam; not fully migrated onto `semantic_primitives`.
|
||||||
|
4. **Holonomy crown** — still not robust; no LIVE claim.
|
||||||
|
5. **Sense disambiguation / frames** — pack frames largely disconnected; first-match pack semantics unchanged outside this morph rule.
|
||||||
|
6. **One rule type** — `he_morph_v0.plural_abstain` only; construct-state / prep+case / aspect deferred.
|
||||||
|
|
||||||
|
## What “bulk impact” means here
|
||||||
|
|
||||||
|
Logos now **changes closed outcomes** on production teaching + turn paths under provenance, and **refuses** adversarial OOV/missing surfaces — the bulk of the novel design’s *authority* contribution for this vertical. Remaining work is **breadth + IR consolidation**, not reopening fail-closed architecture.
|
||||||
|
|
@ -1,12 +1,21 @@
|
||||||
"""Observed-Hebrew morph → canonical constraint vertical slice (Stage 4).
|
"""Observed-Hebrew morph → canonical constraint vertical slice (Stage 4+).
|
||||||
|
|
||||||
``feat/observed-he-morph-constraint-v0`` — compiled pack data only, no
|
Compiled pack data only; no English-to-Hebrew pseudo-morphology.
|
||||||
English-to-Hebrew pseudo-morphology.
|
Live authority is shared via ``authority.evaluate_logos_on_text`` for
|
||||||
|
teaching store and CognitiveTurn (same pure decision function as ablation).
|
||||||
"""
|
"""
|
||||||
|
|
||||||
from generate.observed_he_morph_v0.ablation import run_four_arm_ablation
|
from generate.observed_he_morph_v0.ablation import run_four_arm_ablation
|
||||||
|
from generate.observed_he_morph_v0.authority import (
|
||||||
|
decision_as_coherence_refusal,
|
||||||
|
evaluate_logos_on_text,
|
||||||
|
first_logos_constraint,
|
||||||
|
logos_blocks_certified_answer,
|
||||||
|
scan_observed_he_surface,
|
||||||
|
)
|
||||||
from generate.observed_he_morph_v0.consumer import (
|
from generate.observed_he_morph_v0.consumer import (
|
||||||
ConstraintDecision,
|
ConstraintDecision,
|
||||||
|
DecisionKind,
|
||||||
apply_he_morph_constraint,
|
apply_he_morph_constraint,
|
||||||
)
|
)
|
||||||
from generate.observed_he_morph_v0.records import (
|
from generate.observed_he_morph_v0.records import (
|
||||||
|
|
@ -21,9 +30,15 @@ __all__ = [
|
||||||
"AuthoredMappingRule",
|
"AuthoredMappingRule",
|
||||||
"CanonicalConstraint",
|
"CanonicalConstraint",
|
||||||
"ConstraintDecision",
|
"ConstraintDecision",
|
||||||
|
"DecisionKind",
|
||||||
"ObservedHebrewSurface",
|
"ObservedHebrewSurface",
|
||||||
"PLURAL_ABSTAIN_RULE_V0",
|
"PLURAL_ABSTAIN_RULE_V0",
|
||||||
"apply_he_morph_constraint",
|
"apply_he_morph_constraint",
|
||||||
|
"decision_as_coherence_refusal",
|
||||||
|
"evaluate_logos_on_text",
|
||||||
|
"first_logos_constraint",
|
||||||
"load_observed_morphology",
|
"load_observed_morphology",
|
||||||
|
"logos_blocks_certified_answer",
|
||||||
"run_four_arm_ablation",
|
"run_four_arm_ablation",
|
||||||
|
"scan_observed_he_surface",
|
||||||
]
|
]
|
||||||
|
|
|
||||||
|
|
@ -145,14 +145,18 @@ def run_four_arm_ablation(
|
||||||
if not adv_ok:
|
if not adv_ok:
|
||||||
wrong += 1
|
wrong += 1
|
||||||
|
|
||||||
# Provenance: executable arm carries constraint with source_span
|
# Provenance: executable arm carries typed constraint (rule_id, morph, pack, span)
|
||||||
provenance_ok = False
|
provenance_ok = False
|
||||||
if d_exec.constraints:
|
if d_exec.constraints:
|
||||||
payload = d_exec.constraints[0].payload
|
c0 = d_exec.constraints[0]
|
||||||
provenance_ok = (
|
provenance_ok = bool(
|
||||||
"source_span" in payload
|
getattr(c0, "provenance_complete", False)
|
||||||
and "morphology_id" in payload
|
or (
|
||||||
and "source_pack_id" in payload
|
c0.morphology_id
|
||||||
|
and c0.source_pack_id
|
||||||
|
and c0.rule_id
|
||||||
|
and c0.source_span
|
||||||
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
return AblationReport(
|
return AblationReport(
|
||||||
|
|
|
||||||
200
generate/observed_he_morph_v0/authority.py
Normal file
200
generate/observed_he_morph_v0/authority.py
Normal file
|
|
@ -0,0 +1,200 @@
|
||||||
|
"""Shared Logos morph authority — single decision entry for live seams + ablation.
|
||||||
|
|
||||||
|
Teaching store and CognitiveTurn consult the same pure decision function
|
||||||
|
(``apply_he_morph_constraint``) via helpers here. Modes:
|
||||||
|
|
||||||
|
* canonical / metadata / executable / adversarial
|
||||||
|
|
||||||
|
Morph may force abstain/refuse; it never soft-passes a certified singular
|
||||||
|
exclusivity claim against observed plural HE morphology.
|
||||||
|
"""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from typing import Sequence
|
||||||
|
|
||||||
|
from core.cognition.fail_closed import (
|
||||||
|
CoherenceRefusal,
|
||||||
|
FailureClass,
|
||||||
|
ResidualState,
|
||||||
|
)
|
||||||
|
from core.semantic_primitives import LogosConstraint, ProvenanceSpan
|
||||||
|
from generate.observed_he_morph_v0.consumer import (
|
||||||
|
ConstraintDecision,
|
||||||
|
DecisionKind,
|
||||||
|
apply_he_morph_constraint,
|
||||||
|
)
|
||||||
|
from generate.observed_he_morph_v0.records import (
|
||||||
|
CanonicalConstraint,
|
||||||
|
ObservedHebrewSurface,
|
||||||
|
load_observed_morphology,
|
||||||
|
)
|
||||||
|
from generate.observed_he_morph_v0.rules import PLURAL_ABSTAIN_RULE_V0
|
||||||
|
|
||||||
|
_DEFAULT_PACK = "he_logos_micro_v1"
|
||||||
|
_CATALOG: tuple[ObservedHebrewSurface, ...] | None = None
|
||||||
|
_LOAD_ATTEMPTED = False
|
||||||
|
|
||||||
|
|
||||||
|
def load_default_catalog() -> tuple[ObservedHebrewSurface, ...] | None:
|
||||||
|
"""Load compiled HE morphology once; None if pack missing (fail closed)."""
|
||||||
|
global _CATALOG, _LOAD_ATTEMPTED
|
||||||
|
if _LOAD_ATTEMPTED:
|
||||||
|
return _CATALOG
|
||||||
|
_LOAD_ATTEMPTED = True
|
||||||
|
try:
|
||||||
|
_CATALOG = load_observed_morphology(_DEFAULT_PACK)
|
||||||
|
except Exception:
|
||||||
|
_CATALOG = None
|
||||||
|
return _CATALOG
|
||||||
|
|
||||||
|
|
||||||
|
def reset_catalog_cache() -> None:
|
||||||
|
"""Test-only: clear process-local catalog cache."""
|
||||||
|
global _CATALOG, _LOAD_ATTEMPTED
|
||||||
|
_CATALOG = None
|
||||||
|
_LOAD_ATTEMPTED = False
|
||||||
|
|
||||||
|
|
||||||
|
def scan_observed_he_surface(
|
||||||
|
text: str,
|
||||||
|
catalog: Sequence[ObservedHebrewSurface] | None = None,
|
||||||
|
) -> ObservedHebrewSurface | None:
|
||||||
|
"""Longest exact observed HE surface substring present in ``text``."""
|
||||||
|
cat = catalog if catalog is not None else load_default_catalog()
|
||||||
|
if not cat or not text:
|
||||||
|
return None
|
||||||
|
hits = [s for s in cat if s.surface and s.surface in text]
|
||||||
|
if not hits:
|
||||||
|
return None
|
||||||
|
hits.sort(key=lambda s: len(s.surface), reverse=True)
|
||||||
|
return hits[0]
|
||||||
|
|
||||||
|
|
||||||
|
def evaluate_logos_on_text(
|
||||||
|
*,
|
||||||
|
text: str,
|
||||||
|
mode: str = "executable",
|
||||||
|
catalog: Sequence[ObservedHebrewSurface] | None = None,
|
||||||
|
lemma_key: str | None = None,
|
||||||
|
he_surface: str | None = None,
|
||||||
|
) -> ConstraintDecision | None:
|
||||||
|
"""Evaluate Logos morph authority on free text.
|
||||||
|
|
||||||
|
Returns None when no HE surface is present and mode is not adversarial
|
||||||
|
(no-op for English-only turns). Otherwise returns the shared
|
||||||
|
``ConstraintDecision`` from ``apply_he_morph_constraint``.
|
||||||
|
"""
|
||||||
|
cat = catalog if catalog is not None else load_default_catalog()
|
||||||
|
if cat is None:
|
||||||
|
if mode.strip().lower() == "adversarial":
|
||||||
|
return ConstraintDecision(
|
||||||
|
kind=DecisionKind.FAIL_CLOSED,
|
||||||
|
reason="morph_catalog_unavailable",
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
|
||||||
|
surface_row = None
|
||||||
|
if he_surface is not None:
|
||||||
|
surface = he_surface
|
||||||
|
else:
|
||||||
|
surface_row = scan_observed_he_surface(text, cat)
|
||||||
|
if surface_row is None:
|
||||||
|
if mode.strip().lower() == "adversarial":
|
||||||
|
return apply_he_morph_constraint(
|
||||||
|
proposal_text=text,
|
||||||
|
lemma_key=lemma_key or "",
|
||||||
|
observed_catalog=cat,
|
||||||
|
mode="adversarial",
|
||||||
|
he_surface=None,
|
||||||
|
)
|
||||||
|
return None
|
||||||
|
surface = surface_row.surface
|
||||||
|
|
||||||
|
lemma = lemma_key or (surface_row.lemma if surface_row is not None else "")
|
||||||
|
if not lemma and surface:
|
||||||
|
# Recover lemma from catalog when only surface was supplied.
|
||||||
|
from generate.observed_he_morph_v0.records import lookup_surface
|
||||||
|
|
||||||
|
hits = lookup_surface(cat, surface)
|
||||||
|
if hits:
|
||||||
|
lemma = hits[0].lemma
|
||||||
|
|
||||||
|
return apply_he_morph_constraint(
|
||||||
|
proposal_text=text,
|
||||||
|
lemma_key=lemma or surface,
|
||||||
|
observed_catalog=cat,
|
||||||
|
rule=PLURAL_ABSTAIN_RULE_V0,
|
||||||
|
mode=mode,
|
||||||
|
he_surface=surface,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def logos_blocks_certified_answer(decision: ConstraintDecision | None) -> bool:
|
||||||
|
"""True when morph authority forbids a certified fluent answer."""
|
||||||
|
if decision is None:
|
||||||
|
return False
|
||||||
|
return decision.kind in (DecisionKind.ABSTAIN, DecisionKind.FAIL_CLOSED)
|
||||||
|
|
||||||
|
|
||||||
|
def decision_as_coherence_refusal(
|
||||||
|
decision: ConstraintDecision,
|
||||||
|
) -> CoherenceRefusal:
|
||||||
|
"""Map morph ConstraintDecision → typed fail-closed CoherenceRefusal."""
|
||||||
|
if decision.kind is DecisionKind.FAIL_CLOSED:
|
||||||
|
fclass = FailureClass.AMBIGUITY
|
||||||
|
if "oov" in decision.reason or "missing" in decision.reason:
|
||||||
|
fclass = FailureClass.MISSING_REFERENT
|
||||||
|
elif "invalid" in decision.reason:
|
||||||
|
fclass = FailureClass.CONSTRAINT
|
||||||
|
return CoherenceRefusal(
|
||||||
|
failure_class=fclass,
|
||||||
|
violated_condition=f"logos_morph:{decision.reason}",
|
||||||
|
residual_state=ResidualState(
|
||||||
|
detail=decision.reason,
|
||||||
|
unresolved_hazards=(decision.reason,),
|
||||||
|
),
|
||||||
|
refusal_reason=decision.reason,
|
||||||
|
surface_message=(
|
||||||
|
f"Abstaining: Logos morph constraint ({decision.reason})."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
# ABSTAIN
|
||||||
|
return CoherenceRefusal(
|
||||||
|
failure_class=FailureClass.CONSTRAINT,
|
||||||
|
violated_condition=f"logos_morph:{decision.reason}",
|
||||||
|
residual_state=ResidualState(
|
||||||
|
detail=decision.reason,
|
||||||
|
unresolved_hazards=(decision.reason,),
|
||||||
|
),
|
||||||
|
refusal_reason=decision.reason,
|
||||||
|
surface_message=(
|
||||||
|
f"Abstaining: observed HE morphology blocks claim "
|
||||||
|
f"({decision.rule_id or decision.reason})."
|
||||||
|
),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def constraint_to_logos(constraint: CanonicalConstraint) -> LogosConstraint:
|
||||||
|
"""Project live CanonicalConstraint into shared semantic_primitives IR."""
|
||||||
|
return constraint.to_logos_constraint()
|
||||||
|
|
||||||
|
|
||||||
|
def first_logos_constraint(
|
||||||
|
decision: ConstraintDecision | None,
|
||||||
|
) -> LogosConstraint | None:
|
||||||
|
if decision is None or not decision.constraints:
|
||||||
|
return None
|
||||||
|
return decision.constraints[0].to_logos_constraint()
|
||||||
|
|
||||||
|
|
||||||
|
__all__ = [
|
||||||
|
"constraint_to_logos",
|
||||||
|
"decision_as_coherence_refusal",
|
||||||
|
"evaluate_logos_on_text",
|
||||||
|
"first_logos_constraint",
|
||||||
|
"load_default_catalog",
|
||||||
|
"logos_blocks_certified_answer",
|
||||||
|
"reset_catalog_cache",
|
||||||
|
"scan_observed_he_surface",
|
||||||
|
]
|
||||||
|
|
@ -5,7 +5,9 @@ from __future__ import annotations
|
||||||
import json
|
import json
|
||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from pathlib import Path
|
from pathlib import Path
|
||||||
from typing import Any, Sequence
|
from typing import Any, Mapping, Sequence
|
||||||
|
|
||||||
|
from core.semantic_primitives import LogosConstraint, ProvenanceSpan, ValidationError
|
||||||
|
|
||||||
_REPO_ROOT = Path(__file__).resolve().parents[2]
|
_REPO_ROOT = Path(__file__).resolve().parents[2]
|
||||||
_DEFAULT_PACK = "he_logos_micro_v0" # may not exist
|
_DEFAULT_PACK = "he_logos_micro_v0" # may not exist
|
||||||
|
|
@ -41,19 +43,88 @@ class ObservedHebrewSurface:
|
||||||
|
|
||||||
@dataclass(frozen=True, slots=True)
|
@dataclass(frozen=True, slots=True)
|
||||||
class CanonicalConstraint:
|
class CanonicalConstraint:
|
||||||
"""Language-independent constraint shared across consumers."""
|
"""Language-independent constraint with typed provenance (no free meaning dict).
|
||||||
|
|
||||||
|
Fields are explicit; ``payload`` is a *derived read-only view* for
|
||||||
|
transitional digests only — new code must use typed attributes or
|
||||||
|
``to_logos_constraint()``.
|
||||||
|
"""
|
||||||
|
|
||||||
constraint_id: str
|
constraint_id: str
|
||||||
kind: str # e.g. plurality_marked
|
kind: str # e.g. plurality_marked
|
||||||
payload: dict[str, Any]
|
rule_id: str
|
||||||
|
lemma: str
|
||||||
|
root: str
|
||||||
|
surface: str
|
||||||
|
morphology_id: str
|
||||||
|
source_pack_id: str
|
||||||
|
source_span: tuple[int, int]
|
||||||
|
language: str = "he"
|
||||||
|
|
||||||
|
def __post_init__(self) -> None:
|
||||||
|
if not self.constraint_id:
|
||||||
|
raise ValidationError("CanonicalConstraint.constraint_id required")
|
||||||
|
if not self.kind:
|
||||||
|
raise ValidationError("CanonicalConstraint.kind required")
|
||||||
|
if not self.rule_id:
|
||||||
|
raise ValidationError("CanonicalConstraint.rule_id required")
|
||||||
|
if not self.morphology_id:
|
||||||
|
raise ValidationError("CanonicalConstraint.morphology_id required")
|
||||||
|
if not self.source_pack_id:
|
||||||
|
raise ValidationError("CanonicalConstraint.source_pack_id required")
|
||||||
|
if len(self.source_span) != 2:
|
||||||
|
raise ValidationError("CanonicalConstraint.source_span must be (start, end)")
|
||||||
|
|
||||||
def as_dict(self) -> dict[str, Any]:
|
def as_dict(self) -> dict[str, Any]:
|
||||||
return {
|
return {
|
||||||
"constraint_id": self.constraint_id,
|
"constraint_id": self.constraint_id,
|
||||||
"kind": self.kind,
|
"kind": self.kind,
|
||||||
"payload": dict(self.payload),
|
"rule_id": self.rule_id,
|
||||||
|
"lemma": self.lemma,
|
||||||
|
"root": self.root,
|
||||||
|
"surface": self.surface,
|
||||||
|
"morphology_id": self.morphology_id,
|
||||||
|
"source_pack_id": self.source_pack_id,
|
||||||
|
"source_span": list(self.source_span),
|
||||||
|
"language": self.language,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@property
|
||||||
|
def payload(self) -> Mapping[str, Any]:
|
||||||
|
"""Derived provenance view (legacy digests / transitional access)."""
|
||||||
|
return {
|
||||||
|
"lemma": self.lemma,
|
||||||
|
"root": self.root,
|
||||||
|
"surface": self.surface,
|
||||||
|
"morphology_id": self.morphology_id,
|
||||||
|
"source_span": list(self.source_span),
|
||||||
|
"source_pack_id": self.source_pack_id,
|
||||||
|
"rule_id": self.rule_id,
|
||||||
|
"language": self.language,
|
||||||
|
}
|
||||||
|
|
||||||
|
def to_logos_constraint(self) -> LogosConstraint:
|
||||||
|
return LogosConstraint(
|
||||||
|
constraint_id=self.constraint_id,
|
||||||
|
kind=self.kind,
|
||||||
|
rule_id=self.rule_id,
|
||||||
|
lemma=self.lemma,
|
||||||
|
root=self.root,
|
||||||
|
surface=self.surface,
|
||||||
|
morphology_id=self.morphology_id,
|
||||||
|
source_pack_id=self.source_pack_id,
|
||||||
|
source_span=ProvenanceSpan(
|
||||||
|
start=int(self.source_span[0]),
|
||||||
|
end=int(self.source_span[1]),
|
||||||
|
text=self.surface,
|
||||||
|
),
|
||||||
|
language=self.language,
|
||||||
|
)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def provenance_complete(self) -> bool:
|
||||||
|
return self.to_logos_constraint().provenance_complete
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True, slots=True)
|
@dataclass(frozen=True, slots=True)
|
||||||
class AuthoredMappingRule:
|
class AuthoredMappingRule:
|
||||||
|
|
|
||||||
|
|
@ -45,14 +45,14 @@ class PluralAbstainRuleV0(AuthoredMappingRule):
|
||||||
return CanonicalConstraint(
|
return CanonicalConstraint(
|
||||||
constraint_id=f"{self.rule_id}:{surface.morphology_id}",
|
constraint_id=f"{self.rule_id}:{surface.morphology_id}",
|
||||||
kind=self.constraint_kind,
|
kind=self.constraint_kind,
|
||||||
payload={
|
rule_id=self.rule_id,
|
||||||
"lemma": surface.lemma,
|
lemma=surface.lemma,
|
||||||
"root": surface.root,
|
root=surface.root,
|
||||||
"surface": surface.surface,
|
surface=surface.surface,
|
||||||
"morphology_id": surface.morphology_id,
|
morphology_id=surface.morphology_id,
|
||||||
"source_span": list(surface.source_span),
|
source_pack_id=surface.source_pack_id,
|
||||||
"source_pack_id": surface.source_pack_id,
|
source_span=surface.source_span,
|
||||||
},
|
language=surface.language,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -137,50 +137,20 @@ def _proposal_id(candidate: CorrectionCandidate) -> str:
|
||||||
return hashlib.sha256(payload.encode("utf-8")).hexdigest()[:16]
|
return hashlib.sha256(payload.encode("utf-8")).hexdigest()[:16]
|
||||||
|
|
||||||
|
|
||||||
# Cached compiled HE morphology for the live teaching consumer (Stage 4).
|
|
||||||
_HE_MORPH_CATALOG: tuple | None = None
|
|
||||||
_HE_MORPH_LOAD_ATTEMPTED = False
|
|
||||||
|
|
||||||
|
|
||||||
def _he_morph_catalog():
|
|
||||||
"""Load compiled HE morphology once; fail closed on missing pack (None)."""
|
|
||||||
global _HE_MORPH_CATALOG, _HE_MORPH_LOAD_ATTEMPTED
|
|
||||||
if _HE_MORPH_LOAD_ATTEMPTED:
|
|
||||||
return _HE_MORPH_CATALOG
|
|
||||||
_HE_MORPH_LOAD_ATTEMPTED = True
|
|
||||||
try:
|
|
||||||
from generate.observed_he_morph_v0.records import load_observed_morphology
|
|
||||||
|
|
||||||
_HE_MORPH_CATALOG = load_observed_morphology("he_logos_micro_v1")
|
|
||||||
except Exception:
|
|
||||||
_HE_MORPH_CATALOG = None
|
|
||||||
return _HE_MORPH_CATALOG
|
|
||||||
|
|
||||||
|
|
||||||
def _auto_he_morph_decision(proposal: PackMutationProposal):
|
def _auto_he_morph_decision(proposal: PackMutationProposal):
|
||||||
"""Apply executable HE morph rule when correction text cites a catalog surface.
|
"""Apply executable HE morph rule when correction text cites a catalog surface.
|
||||||
|
|
||||||
Live production path for Stage 4 — not optional test-only wiring.
|
Live production path — shares ``evaluate_logos_on_text`` with CognitiveTurn
|
||||||
|
and the four-arm ablation (same pure decision function).
|
||||||
Returns None when no HE surface is present (no-op, English corrections unchanged).
|
Returns None when no HE surface is present (no-op, English corrections unchanged).
|
||||||
"""
|
"""
|
||||||
catalog = _he_morph_catalog()
|
from generate.observed_he_morph_v0.authority import evaluate_logos_on_text
|
||||||
if not catalog:
|
|
||||||
return None
|
|
||||||
text = proposal.correction_text or ""
|
|
||||||
# Prefer longest surface match present in the correction text.
|
|
||||||
hits = [s for s in catalog if s.surface and s.surface in text]
|
|
||||||
if not hits:
|
|
||||||
return None
|
|
||||||
hits.sort(key=lambda s: len(s.surface), reverse=True)
|
|
||||||
surface = hits[0]
|
|
||||||
from generate.observed_he_morph_v0.consumer import apply_he_morph_constraint
|
|
||||||
|
|
||||||
return apply_he_morph_constraint(
|
text = proposal.correction_text or ""
|
||||||
proposal_text=text,
|
return evaluate_logos_on_text(
|
||||||
lemma_key=str(proposal.subject or surface.lemma),
|
text=text,
|
||||||
observed_catalog=catalog,
|
|
||||||
mode="executable",
|
mode="executable",
|
||||||
he_surface=surface.surface,
|
lemma_key=str(proposal.subject or "") or None,
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,7 +79,15 @@ def test_metadata_only_inert_vs_executable_effect():
|
||||||
assert meta.kind is DecisionKind.PASS
|
assert meta.kind is DecisionKind.PASS
|
||||||
assert exe.kind is DecisionKind.ABSTAIN
|
assert exe.kind is DecisionKind.ABSTAIN
|
||||||
assert exe.rule_id == PLURAL_ABSTAIN_RULE_V0.rule_id
|
assert exe.rule_id == PLURAL_ABSTAIN_RULE_V0.rule_id
|
||||||
assert exe.constraints[0].payload["source_span"]
|
# Typed IR — no free meaning dict; provenance on fields + LogosConstraint.
|
||||||
|
c0 = exe.constraints[0]
|
||||||
|
assert c0.morphology_id
|
||||||
|
assert c0.source_pack_id
|
||||||
|
assert c0.rule_id == PLURAL_ABSTAIN_RULE_V0.rule_id
|
||||||
|
assert c0.provenance_complete is True
|
||||||
|
logos = c0.to_logos_constraint()
|
||||||
|
assert logos.constraint_id == c0.constraint_id
|
||||||
|
assert logos.provenance_complete is True
|
||||||
|
|
||||||
|
|
||||||
def test_oov_and_invalid_fail_closed():
|
def test_oov_and_invalid_fail_closed():
|
||||||
|
|
@ -156,3 +164,77 @@ def test_vault_promotion_default_requires_geometric_unitarity():
|
||||||
raw=0.05, energy_class=EnergyClass.E0, coherence_residual=1e-9
|
raw=0.05, energy_class=EnergyClass.E0, coherence_residual=1e-9
|
||||||
)
|
)
|
||||||
assert policy.decide(tight).promote is True
|
assert policy.decide(tight).promote is True
|
||||||
|
|
||||||
|
|
||||||
|
def test_shared_authority_four_modes_and_typed_ir():
|
||||||
|
"""Live authority entry shares apply_he_morph_constraint; modes diverge correctly."""
|
||||||
|
from generate.observed_he_morph_v0.authority import (
|
||||||
|
evaluate_logos_on_text,
|
||||||
|
first_logos_constraint,
|
||||||
|
logos_blocks_certified_answer,
|
||||||
|
)
|
||||||
|
|
||||||
|
catalog = load_observed_morphology("he_logos_micro_v1")
|
||||||
|
plural = next(s for s in catalog if s.number == "plural")
|
||||||
|
claim = (
|
||||||
|
f"{plural.surface} ({plural.lemma}) must be singular only — "
|
||||||
|
f"exclusive singular identity"
|
||||||
|
)
|
||||||
|
can = evaluate_logos_on_text(text=claim, mode="canonical", catalog=catalog)
|
||||||
|
meta = evaluate_logos_on_text(text=claim, mode="metadata", catalog=catalog)
|
||||||
|
exe = evaluate_logos_on_text(text=claim, mode="executable", catalog=catalog)
|
||||||
|
oov = evaluate_logos_on_text(
|
||||||
|
text="zzz_not_a_surface must be singular only",
|
||||||
|
mode="adversarial",
|
||||||
|
catalog=catalog,
|
||||||
|
he_surface="zzz_not_a_surface",
|
||||||
|
)
|
||||||
|
assert can is not None and meta is not None and exe is not None and oov is not None
|
||||||
|
assert can.kind is DecisionKind.PASS
|
||||||
|
assert meta.kind is DecisionKind.PASS
|
||||||
|
assert can.as_dict() == meta.as_dict()
|
||||||
|
assert exe.kind is DecisionKind.ABSTAIN
|
||||||
|
assert logos_blocks_certified_answer(exe) is True
|
||||||
|
assert oov.kind is DecisionKind.FAIL_CLOSED
|
||||||
|
lc = first_logos_constraint(exe)
|
||||||
|
assert lc is not None
|
||||||
|
assert lc.rule_id == PLURAL_ABSTAIN_RULE_V0.rule_id
|
||||||
|
assert lc.source_pack_id == "he_logos_micro_v1"
|
||||||
|
assert lc.morphology_id == plural.morphology_id
|
||||||
|
|
||||||
|
|
||||||
|
def test_cognitive_turn_logos_authority_abstains_on_plural_singular_claim():
|
||||||
|
"""Live CognitiveTurnPipeline.run path: executable morph blocks certified answer."""
|
||||||
|
from chat.runtime import ChatRuntime
|
||||||
|
from core.cognition.pipeline import CognitiveTurnPipeline
|
||||||
|
|
||||||
|
catalog = load_observed_morphology("he_logos_micro_v1")
|
||||||
|
plural = next(s for s in catalog if s.number == "plural")
|
||||||
|
claim = (
|
||||||
|
f"{plural.surface} ({plural.lemma}) must be singular only — "
|
||||||
|
f"exclusive singular identity"
|
||||||
|
)
|
||||||
|
pipe = CognitiveTurnPipeline(runtime=ChatRuntime())
|
||||||
|
result = pipe.run(claim)
|
||||||
|
assert result.logos_decision_kind == "abstain"
|
||||||
|
assert result.logos_decision_reason == "plural_morph_blocks_singular_exclusivity"
|
||||||
|
assert result.logos_rule_id == PLURAL_ABSTAIN_RULE_V0.rule_id
|
||||||
|
assert result.logos_constraint_id
|
||||||
|
assert result.authority_source == "logos_morph_constraint"
|
||||||
|
assert "Abstaining" in result.surface or "abstain" in result.surface.lower()
|
||||||
|
assert result.refusal_reason == "plural_morph_blocks_singular_exclusivity"
|
||||||
|
# Hazard ledger records morph block (improvement observability).
|
||||||
|
assert any("logos_morph" in h for h in result.substrate_hazard)
|
||||||
|
|
||||||
|
|
||||||
|
def test_cognitive_turn_english_only_no_logos_noop():
|
||||||
|
"""English-only turn does not invent HE morph or force logos authority."""
|
||||||
|
from chat.runtime import ChatRuntime
|
||||||
|
from core.cognition.pipeline import CognitiveTurnPipeline
|
||||||
|
|
||||||
|
pipe = CognitiveTurnPipeline(runtime=ChatRuntime())
|
||||||
|
result = pipe.run("What is light?")
|
||||||
|
assert result.logos_decision_kind in ("", "pass")
|
||||||
|
assert result.authority_source != "logos_morph_constraint"
|
||||||
|
# Must not be a morph abstention surface when no HE present.
|
||||||
|
assert "Logos morph" not in result.surface
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue