from __future__ import annotations from dataclasses import dataclass, replace import hashlib import json import re from collections.abc import Sequence from typing import List import numpy as np from algebra.versor import versor_condition from chat.refusal import ( build_hedge_prefix, build_refusal_surface, inject_hedge, should_inject_hedge, ) from core.config import DEFAULT_CONFIG, DEFAULT_IDENTITY_PACK, RuntimeConfig from core.physics.drive import DriveGradientMap, GradientField from core.physics.energy import EnergyProfile from core.physics.exertion import CycleCost, ExertionMeter from core.physics.identity import ( CharacterProfile, IdentityCheck, IdentityScore, TurnEvent, ) from packs.ethics.check import EthicsCheck, EthicsContext from packs.ethics.loader import ( DEFAULT_ETHICS_PACK as _DEFAULT_ETHICS_PACK, EthicsPackError, load_ethics_pack, ) from packs.identity.loader import load_identity_manifold from packs.safety.check import SafetyCheck, SafetyContext from packs.safety.loader import load_safety_pack from field.state import FieldState from generate.articulation import ArticulationPlan, realize from generate.dialogue import DialogueRole, classify_dialogue_blade, propose_dialogue from generate.intent_bridge import articulate_with_intent from generate.proposition import FrameRegistry, Proposition, propose from generate.result import GenerationResult from generate.stream import generate from generate.surface import SentenceAssembler, SentencePlan, SurfaceContext from ingest.gate import inject from language_packs import OOVPolicy, load_mounted_packs, load_pack, load_pack_entries from persona.motor import PersonaMotor from session.context import SessionContext from session.correction import CorrectionPass from vault.decompose import default_decomposer, default_gate _TOKEN_RE = re.compile(r"\w+", re.UNICODE) _SEED_ALIASES = { "logos": "\u03bb\u03cc\u03b3\u03bf\u03c2", "dabar": "\u05d3\u05d1\u05e8", "or": "\u05d0\u05d5\u05e8", "phos": "\u03c6\u03c9\u03c2", "zoe": "\u03b6\u03c9\u03ae", "arche": "\u1f00\u03c1\u03c7\u03ae", "aletheia": "\u1f00\u03bb\u03ae\u03b8\u03b5\u03b9\u03b1", } _QUESTION_WORDS = frozenset({"what", "who", "how", "why", "when", "where", "which"}) _TERMINALS = frozenset({".", "?", ";", "!"}) _UNKNOWN_DOMAIN_SURFACE = "I don't know — insufficient grounding for that yet." def _energy_scalar(energy_obj) -> float: if energy_obj is None: return 1.0 if isinstance(energy_obj, EnergyProfile): return float(energy_obj.raw) try: return float(energy_obj) except (TypeError, ValueError): return 1.0 def _is_question_input(raw_text: str, tokens: Sequence[str]) -> bool: if raw_text.strip().endswith("?"): return True return bool(tokens and tokens[0].casefold() in _QUESTION_WORDS) def _stable_dialogue_role(role: DialogueRole, *, raw_text: str, tokens: Sequence[str]) -> DialogueRole: if role in {"question", "refute"} and not _is_question_input(raw_text, tokens): return "elaborate" return role def _terminal_for_role(role: DialogueRole, output_language: str) -> str: if role == "question": return ";" if output_language == "grc" else "?" return "." def _terminate_surface(surface: str, *, role: DialogueRole, output_language: str) -> str: stripped = surface.strip() if not stripped: return stripped if stripped[-1] in _TERMINALS: return stripped return f"{stripped}{_terminal_for_role(role, output_language)}" def _prefer_prompt_anchor( articulation: ArticulationPlan, filtered_tokens: Sequence[str], *, output_language: str, ) -> ArticulationPlan: if output_language != "en" or len(filtered_tokens) < 2: return articulation content_tokens = [ token for token in filtered_tokens if token.casefold() not in _QUESTION_WORDS and token.casefold() not in {"is", "are", "was", "were"} ] if not content_tokens: return articulation anchor = content_tokens[-1] if anchor == articulation.subject: return articulation return replace( articulation, subject=anchor, surface=" ".join(part for part in (anchor, articulation.predicate, articulation.object) if part), ) @dataclass class _StubBindingFrame: frame_id: str coherence_magnitude: float region_ids: frozenset cycle_index: int @dataclass(frozen=True, slots=True) class _FieldStateWithVersor: """Adapter exposing ``versor_condition`` for SafetyContext. ``FieldState`` itself does not carry a precomputed ``versor_condition`` attribute; it is computed on demand from ``versor_condition(state.F)``. The SafetyCheck predicate for ``preserve_versor_closure`` reads ``ctx.field_state.versor_condition`` via ``getattr``. This adapter exposes the precomputed value so the predicate is runtime-checkable each turn. """ versor_condition: float def _hash_identity_manifold(manifold) -> str: """Deterministic SHA-256 of the load-bearing identity-manifold fields. ADR-0035 — feeds the ``no_identity_override`` predicate in :class:`SafetyCheck`. The runtime never mutates ``identity_manifold`` after composition, so before- and after-turn hashes are equal by construction; an unequal hash would indicate the predicate's exact failure mode. """ payload = { "value_axes": [ { "axis_id": axis.axis_id, "name": axis.name, "direction": list(axis.direction), "weight": axis.weight, } for axis in manifold.value_axes ], "boundary_ids": sorted(manifold.boundary_ids), "alignment_threshold": manifold.alignment_threshold, } blob = json.dumps(payload, sort_keys=True, separators=(",", ":")).encode("utf-8") return hashlib.sha256(blob).hexdigest() def _surface_contains_hedge(surface: str, manifold) -> bool: """Detect whether the realized surface emitted a hedge phrase. Compares case-insensitively against the manifold's preferred hedge phrases (ADR-0028). False when surface is empty. Coarse but deterministic: the predicate downstream is observational, so occasional false negatives are surfaced as ``acknowledge_uncertainty`` violations in audit and corrected by refining hedge detection, not by silently passing. """ if not surface: return False prefs = getattr(manifold, "surface_preferences", None) if prefs is None: return False candidates: list[str] = [] for field_name in ( "preferred_hedge_strong", "preferred_hedge_soft", "preferred_qualifier", ): value = getattr(prefs, field_name, "") if value: candidates.append(value) for _, hedge in getattr(prefs, "axis_hedges", ()) or (): for sub in ("strong", "soft", "qualifier"): value = getattr(hedge, sub, "") if value: candidates.append(value) surface_fold = surface.casefold() return any(c.casefold() in surface_fold for c in candidates if c) def _make_trajectory_from_result(result, turn: int): from core.physics.reasoning import TrajectoryOperator operator = TrajectoryOperator() states = result.trajectory or (result.final_state,) frames = [ _StubBindingFrame( frame_id=f"t{turn}_s{i}", coherence_magnitude=_energy_scalar(getattr(fs, "energy", None)), region_ids=frozenset({str(getattr(fs, "node", 0))}), cycle_index=turn, ) for i, fs in enumerate(states) ] return operator.build(frames, trajectory_id=f"turn_{turn}") @dataclass(frozen=True, slots=True) class ChatResponse: surface: str proposition: Proposition articulation: ArticulationPlan articulation_surface: str dialogue_role: DialogueRole versor_condition: float output_language: str frame_pack: str walk_surface: str salience_top_k: int | None candidates_used: int | None vault_hits: int identity_score: IdentityScore | None character_profile: CharacterProfile flagged: bool # ADR-0023 §2 — per-transition admissibility evidence and region # provenance flag. An empty tuple is the contract for "no # admissibility was checked this turn" (cold start, refusal, stub). admissibility_trace: tuple = () region_was_unconstrained: bool = True # ADR-0035 — verdicts surfaced from SafetyCheck and EthicsCheck. # ``None`` only on stub/refusal paths that bypass the turn loop. safety_verdict: object = None ethics_verdict: object = None class ChatRuntime: def __init__( self, pack_id: str | Sequence[str] | None = None, *, frame_pack: str | None = None, config: RuntimeConfig = DEFAULT_CONFIG, ) -> None: if pack_id is not None or frame_pack is not None: pack_ids = (pack_id,) if isinstance(pack_id, str) else tuple(pack_id or config.input_packs) resolved_config = RuntimeConfig( input_packs=pack_ids, output_language=config.output_language, frame_pack=frame_pack or config.frame_pack, max_tokens=config.max_tokens, allow_cross_language_recall=config.allow_cross_language_recall, allow_cross_language_generation=config.allow_cross_language_generation, vault_reproject_interval=config.vault_reproject_interval, use_salience=config.use_salience, salience_top_k=config.salience_top_k, inhibition_threshold=config.inhibition_threshold, inner_loop_admissibility=config.inner_loop_admissibility, admissibility_threshold=config.admissibility_threshold, admissibility_mode=config.admissibility_mode, admissibility_margin=config.admissibility_margin, ) else: resolved_config = config pack_ids = tuple(config.input_packs) self.config = resolved_config manifests = [] manifolds = [] entries = [] for mounted_pack_id in pack_ids: manifest, manifold = load_pack(mounted_pack_id) manifests.append(manifest) manifolds.append(manifold) entries.extend(load_pack_entries(mounted_pack_id)) manifold = manifolds[0] if len(pack_ids) == 1 else load_mounted_packs(pack_ids) self._manifests = tuple(manifests) identity_pack_id = resolved_config.identity_pack or DEFAULT_IDENTITY_PACK # ADR-0027 Phase 5 complete: v1 packs are ratified. Loader defaults # to production mode (require_ratified=None -> require unless # CORE_ALLOW_UNRATIFIED_IDENTITY=1). identity_manifold = load_identity_manifold(identity_pack_id) # ADR-0029: safety pack is always loaded; its boundary_ids are # unioned into the runtime manifold. Identity packs may add # boundaries but cannot remove safety boundaries. Failure to # load the safety pack is fail-closed; SafetyPackError propagates # and prevents runtime startup. self.safety_pack = load_safety_pack() # ADR-0033 — ethics pack composes alongside identity + safety. # Swappable like identity; falls back to the default pack on # load failure rather than refusing startup (safety is the # fail-closed layer, not ethics). ethics_pack_id = resolved_config.ethics_pack or _DEFAULT_ETHICS_PACK try: self.ethics_pack = load_ethics_pack(ethics_pack_id) except EthicsPackError: if ethics_pack_id == _DEFAULT_ETHICS_PACK: raise self.ethics_pack = load_ethics_pack(_DEFAULT_ETHICS_PACK) ethics_pack_id = _DEFAULT_ETHICS_PACK self.ethics_pack_id = ethics_pack_id self.identity_manifold = type(identity_manifold)( value_axes=identity_manifold.value_axes, boundary_ids=( identity_manifold.boundary_ids | self.safety_pack.boundary_ids | self.ethics_pack.commitment_ids ), alignment_threshold=identity_manifold.alignment_threshold, surface_preferences=identity_manifold.surface_preferences, ) self.identity_pack_id = identity_pack_id # Keep the generic runtime neutral. Identity/persona motivation belongs # behind an explicit IdentityProfile contract, not the baseline chat path. persona_motor = PersonaMotor.identity() self._context = SessionContext( manifold, persona=persona_motor, vault_reproject_interval=resolved_config.vault_reproject_interval, ) self._frame_registry = FrameRegistry.from_pack(resolved_config.frame_pack, self._context.vocab) self._surface_by_fold = {e.surface.casefold(): e.surface for e in entries} self._surface_by_fold.update(_SEED_ALIASES) self._pos_by_surface = {e.surface: (e.pos or e.part_of_speech or "X") for e in entries} self.exertion_meter = ExertionMeter(capacity_ceiling=128.0) self.drive_gradients = tuple(GradientField(axis=axis, magnitude=0.75) for axis in self.identity_manifold.value_axes) self._drive_map = DriveGradientMap(gradients=self.drive_gradients) self.character_profile = CharacterProfile.from_manifold( self.identity_manifold, drive_summaries={g.axis.name: g.magnitude for g in self.drive_gradients}, fatigue_index=0.0, ) self._identity_check = IdentityCheck() # ADR-0032 — structural safety surface. Observational at v1: # ChatRuntime exposes ``safety_check`` for callers (audit / # logging / future enforcement), but does not auto-invoke it in # the turn loop. Wiring violations into refusal paths is a # future ADR. self.safety_check = SafetyCheck() # ADR-0034 — structural ethics surface, sibling to SafetyCheck. self.ethics_check = EthicsCheck() # ADR-0035 — auto-invoke both checks at end-of-turn. The # manifold is constructed once and never mutated, so the # pre-turn hash is a stable property of this runtime instance. # ``last_refusal_was_typed`` defaults True (no untyped refusals # observed); turn-loop bookkeeping flips this on typed-refusal # paths so the predicate has live evidence. self._identity_manifold_hash: str = _hash_identity_manifold( self.identity_manifold, ) self._last_refusal_was_typed: bool = True self.turn_log: List[TurnEvent] = [] self._correction_pass = CorrectionPass() self._last_valence: float = 0.0 @property def session(self) -> SessionContext: return self._context def _tokenize(self, text: str) -> list[str]: return [self._surface_by_fold.get(m.group(0).casefold(), m.group(0)) for m in _TOKEN_RE.finditer(text)] def tokenize(self, text: str) -> list[str]: return self._tokenize(text) def _apply_oov_policy(self, tokens: list[str]) -> list[str]: kept: list[str] = [] for token in tokens: try: self._context.vocab.get_versor(token) kept.append(token) except KeyError: if all(manifest.oov_policy is OOVPolicy.FAIL_CLOSED for manifest in self._manifests): raise if any(manifest.oov_policy is OOVPolicy.PROPOSE_VOCAB_EXPANSION for manifest in self._manifests): raise KeyError(f"OOV token requires vocab proposal: {token}") kept.append(token) return kept def _syntactic_guard(self, tokens: tuple[str, ...]) -> list[str]: out: list[str] = [] prev_pos: str | None = None for token in tokens: pos = self._pos_by_surface.get(token, "X") if pos == prev_pos: continue out.append(token) prev_pos = pos return out def _dialogue_reference(self) -> np.ndarray | None: blade = self._context.last_dialogue_blade if blade is None or float(np.linalg.norm(blade)) < 1e-8: return None return blade def _apply_drive_bias(self, field_state: FieldState) -> FieldState: """Generic runtime keeps motivation/drive disabled. Motivation is an identity-profile concern, not a free runtime field mutation. Keeping this a no-op preserves the neutral baseline while generic chat closure and cognition evals are being stabilized. """ return field_state def _build_surface_context(self, identity_score, current_valence: float) -> SurfaceContext: active = self._context.referents.active_referent() alignment = float(identity_score.alignment) if identity_score is not None else 1.0 deviation_axes = ( frozenset(identity_score.deviation_axes) if identity_score is not None else frozenset() ) prefs = self.identity_manifold.surface_preferences # ADR-0031 — flatten the manifold's axis_hedges (tuple of # (axis_id, AxisHedge)) into the wire-format quadruples that # SurfaceContext carries. Order is preserved (loader emits in # lex order); _axis_specific_phrase relies on this. axis_hedges = tuple( (axis_id, hedge.strong, hedge.soft, hedge.qualifier) for axis_id, hedge in prefs.axis_hedges ) return SurfaceContext( active_referent_surface=active.surface if active is not None else "", active_referent_slot=active.slot if active is not None else "neut_sg", identity_alignment=alignment, valence_delta=current_valence - self._last_valence, elab_conjunction="", hedge_threshold_strong=prefs.hedge_threshold_strong, hedge_threshold_soft=prefs.hedge_threshold_soft, preferred_hedge_strong=prefs.preferred_hedge_strong, preferred_hedge_soft=prefs.preferred_hedge_soft, claim_strength=prefs.claim_strength, qualified_band_high=prefs.qualified_band_high, preferred_qualifier=prefs.preferred_qualifier, deviation_axes=deviation_axes, axis_hedges=axis_hedges, ) def _stub_response(self, field_state: FieldState) -> ChatResponse: zero = np.zeros(field_state.F.shape, dtype=np.float32) prop = Proposition( subject="", predicate="", object_=None, surface=_UNKNOWN_DOMAIN_SURFACE, frame_id="unknown_domain", subject_versor=zero, predicate_versor=zero, object_versor=None, relation=zero, ) art = ArticulationPlan( subject="", predicate="", object=None, surface=_UNKNOWN_DOMAIN_SURFACE, output_language=self.config.output_language, frame_id="unknown_domain", ) # ADR-0035 — stub responses are exactly the ungrounded path that # triggers ``disclose_limitations``. Surfacing verdicts here # keeps the audit contract uniform: every ChatResponse carries # a SafetyVerdict and EthicsVerdict. safety_ctx = SafetyContext( field_state=_FieldStateWithVersor( versor_condition=float(versor_condition(field_state.F)), ), last_refusal_was_typed=self._last_refusal_was_typed, identity_manifold_hash_before=self._identity_manifold_hash, identity_manifold_hash_after=_hash_identity_manifold(self.identity_manifold), ) safety_verdict = self.safety_check.check(safety_ctx, self.safety_pack) ethics_ctx = EthicsContext( alignment_score=0.0, hedge_threshold_soft=float( self.identity_manifold.surface_preferences.hedge_threshold_soft ), hedge_emitted=False, grounded_in_evidence=False, disclosure_emitted=True, ) ethics_verdict = self.ethics_check.check(ethics_ctx, self.ethics_pack) # ADR-0036 — typed refusal also applies on the stub path. When # a runtime-checkable safety boundary is violated even on the # ungrounded surface (e.g. versor-closure failure), replace the # user-facing ``surface`` with the deterministic typed refusal. refusal_surface = build_refusal_surface( safety_verdict, ethics_verdict, self.ethics_pack, ) if refusal_surface is not None: response_surface = refusal_surface self._last_refusal_was_typed = True else: response_surface = _UNKNOWN_DOMAIN_SURFACE return ChatResponse( surface=response_surface, proposition=prop, articulation=art, articulation_surface=_UNKNOWN_DOMAIN_SURFACE, dialogue_role="assert", versor_condition=versor_condition(field_state.F), output_language=self.config.output_language, frame_pack=self.config.frame_pack, walk_surface=_UNKNOWN_DOMAIN_SURFACE, salience_top_k=None, candidates_used=None, vault_hits=0, identity_score=None, character_profile=self.character_profile, flagged=False, safety_verdict=safety_verdict, ethics_verdict=ethics_verdict, ) def chat(self, text: str, max_tokens: int | None = None) -> ChatResponse: tokens = self._tokenize(text) filtered = self._apply_oov_policy(tokens) if not filtered: raise ValueError("ChatRuntime.chat() received no in-vocabulary tokens.") probe_state = self._context.probe_ingest(filtered) # INV-24 recall role: RECOGNITION. Feeds UnknownDomainGate — asks # "have we seen anything like this before?", not "what is admissible # evidence?". Session-tier SPECULATIVE memory must count here, so # no min_status filter is applied. direct_hits = self._context.vault.recall(probe_state.F, top_k=3) direct_best = max((h["score"] for h in direct_hits), default=0.0) gate_decision = default_gate.check( direct_best, vault=self._context.vault, query=probe_state.F, decomposer=default_decomposer, ) if gate_decision.fire: committed = self._context.commit_ingest(filtered) empty_result = GenerationResult(tokens=(), final_state=committed, vault_hits=0) self._context.finalize_turn( empty_result, tokens_in=tuple(filtered), input_versor=committed.F, dialogue_role="assert", metadata={"unknown": True, "unknown_source": gate_decision.source}, ) return self._stub_response(committed) field_state = self._context.commit_ingest(filtered) field_state = self._apply_drive_bias(field_state) reference_blade = self._dialogue_reference() base_proposition = propose( field_state, None, self._context.vocab, self._frame_registry, output_lang=self.config.output_language, ) dialogue_role = _stable_dialogue_role( classify_dialogue_blade(base_proposition.relation, reference_blade), raw_text=text, tokens=tokens, ) proposition = propose_dialogue( field_state, self._context.vault, self._context.vocab, self._frame_registry, reference_blade, output_lang=self.config.output_language, ) articulation = realize(proposition, self._context.vocab, output_language=self.config.output_language) articulation = _prefer_prompt_anchor(articulation, filtered, output_language=self.config.output_language) self._context.record_dialogue(proposition) result = generate( field_state, self._context.vocab, self._context.persona, max_tokens=self.config.max_tokens if max_tokens is None else max_tokens, record_trajectory=True, vault=self._context.vault, recall_top_k=3 if self.config.allow_cross_language_recall else 0, output_lang=self.config.output_language, allow_cross_language_generation=self.config.allow_cross_language_generation, use_salience=self.config.use_salience, salience_top_k=self.config.salience_top_k, inhibition_threshold=self.config.inhibition_threshold, inner_loop_admissibility=self.config.inner_loop_admissibility, admissibility_threshold=self.config.admissibility_threshold, admissibility_mode=self.config.admissibility_mode, admissibility_margin=self.config.admissibility_margin, ) # --- Articulation fidelity: replace bare S-P-O join with intent-aware surface --- # articulate_with_intent() classifies the input intent, builds a proposition # graph grounded on the generation result's recalled tokens, and calls the # realize_semantic() path (13-construction realizer) that was previously # implemented but never connected to the chat hot path. # Falls back to the existing articulation.surface when bridge returns "". if self.config.output_language == "en": recalled_words = tuple( tok for tok in (result.tokens or ()) if tok and tok.isalpha() ) intent_surface = articulate_with_intent(text, articulation, recalled_words) if intent_surface: articulation = replace(articulation, surface=intent_surface) # --- end articulation fidelity fix --- reasoning_trajectory = _make_trajectory_from_result(result, self._context.turn) identity_score = self._identity_check.check(reasoning_trajectory, self.identity_manifold) flagged = identity_score.flagged cycle_cost = CycleCost( cycle_index=self._context.turn, attention_cost=float(result.candidates_used or 0), inhibition_cost=float(self.config.inhibition_threshold), digest_cost=0.0, trajectory_cost=float(len(result.trajectory or ())), ) self.exertion_meter.record(cycle_cost) fatigue = self.exertion_meter.fatigue(at_cycle=self._context.turn) self.character_profile = CharacterProfile.from_manifold( self.identity_manifold, drive_summaries={g.axis.name: g.magnitude * (1.0 - fatigue.value) for g in self.drive_gradients}, fatigue_index=fatigue.value, ) self._context.finalize_turn( result, tokens_in=tuple(filtered), dialogue_role=str(dialogue_role), ) current_valence = _energy_scalar(getattr(result.final_state, "valence", None)) surface_ctx = self._build_surface_context(identity_score, current_valence) self._last_valence = current_valence surface = _terminate_surface(articulation.surface, role=dialogue_role, output_language=self.config.output_language) articulation = replace(articulation, surface=surface) sentence_plan: SentencePlan = SentenceAssembler().assemble( articulation, result.tokens, role=dialogue_role, context=surface_ctx, ) walk_surface = sentence_plan.surface vault_hits = int(result.vault_hits) # ADR-0035 — auto-invoke safety + ethics surfaces. Observational # at v1; verdicts are attached to TurnEvent and ChatResponse for # audit but do not gate behavior. Refusal/re-articulation # wiring is a future ADR. is_grounded = walk_surface != _UNKNOWN_DOMAIN_SURFACE hedge_emitted = _surface_contains_hedge(walk_surface, self.identity_manifold) safety_ctx = SafetyContext( field_state=_FieldStateWithVersor( versor_condition=float(versor_condition(result.final_state.F)), ), last_refusal_was_typed=self._last_refusal_was_typed, identity_manifold_hash_before=self._identity_manifold_hash, identity_manifold_hash_after=_hash_identity_manifold(self.identity_manifold), ) safety_verdict = self.safety_check.check(safety_ctx, self.safety_pack) ethics_ctx = EthicsContext( alignment_score=float(getattr(identity_score, "alignment", 0.0)), hedge_threshold_soft=float( self.identity_manifold.surface_preferences.hedge_threshold_soft ), hedge_emitted=hedge_emitted, grounded_in_evidence=is_grounded, disclosure_emitted=not is_grounded, ) ethics_verdict = self.ethics_check.check(ethics_ctx, self.ethics_pack) # ADR-0036 — safety-only typed refusal. A runtime-checkable # SafetyVerdict violation replaces the user-facing ``surface`` # with a deterministic typed refusal string. ``walk_surface`` # and ``articulation_surface`` retain the original token-walk / # realizer evidence for audit (per the runtime surface # contract in CLAUDE.md). Ethics violations remain audit-only. refusal_surface = build_refusal_surface( safety_verdict, ethics_verdict, self.ethics_pack, ) if refusal_surface is not None: response_surface = refusal_surface self._last_refusal_was_typed = True else: response_surface = walk_surface # ADR-0038 — hedge injection. When an ethics commitment in # ``ethics_pack.hedge_commitments`` fires runtime-checkable # and the manifold has a hedge phrase configured, prepend # the hedge to the user-facing surface. Mutually exclusive # with refusal at the pack-schema level; this branch only # runs when refusal did not fire. ``walk_surface`` and # ``articulation_surface`` are preserved unchanged for # audit (same discipline as ADR-0036). if should_inject_hedge(ethics_verdict, self.ethics_pack): hedge_prefix = build_hedge_prefix(self.identity_manifold) response_surface = inject_hedge(response_surface, hedge_prefix) turn_event = TurnEvent( turn=self._context.turn - 1, input_tokens=tuple(filtered), surface=response_surface, walk_surface=walk_surface, articulation_surface=articulation.surface, dialogue_role=str(dialogue_role), identity_score=identity_score, cycle_cost_total=cycle_cost.total, vault_hits=vault_hits, versor_condition=versor_condition(result.final_state.F), flagged=flagged, elaboration=sentence_plan.elaboration, safety_verdict=safety_verdict, ethics_verdict=ethics_verdict, ) self.turn_log.append(turn_event) return ChatResponse( surface=response_surface, proposition=proposition, articulation=articulation, articulation_surface=articulation.surface, dialogue_role=dialogue_role, versor_condition=versor_condition(result.final_state.F), output_language=self.config.output_language, frame_pack=self.config.frame_pack, walk_surface=walk_surface, salience_top_k=result.salience_top_k, candidates_used=result.candidates_used, vault_hits=vault_hits, identity_score=identity_score, character_profile=self.character_profile, flagged=flagged, admissibility_trace=result.admissibility_trace, region_was_unconstrained=result.region_was_unconstrained, safety_verdict=safety_verdict, ethics_verdict=ethics_verdict, ) def _unknown_domain_response(self, field_state: FieldState, filtered: list[str]) -> ChatResponse: return self._stub_response(field_state) def correct(self, text: str, target_turn: int = -1, max_tokens: int | None = None) -> ChatResponse: tokens = self._tokenize(text) filtered = self._apply_oov_policy(tokens) if not filtered: raise ValueError("correct() received no in-vocabulary tokens.") correction_state = inject(filtered, self._context.vocab) correction_result = self._correction_pass.apply( self._context.graph, correction_state.F, from_turn=target_turn, ) self._context.apply_corrected_outputs(correction_result.records) regen_tokens = self._context.last_input_tokens if not regen_tokens: return self._stub_response(correction_state) return self.chat(" ".join(regen_tokens), max_tokens=max_tokens) def respond(self, text: str, max_tokens: int | None = None) -> str: try: return self.chat(text, max_tokens=max_tokens).surface except ValueError: return "" async def achat(self, text: str, max_tokens: int | None = None) -> ChatResponse: return self.chat(text, max_tokens=max_tokens) async def arespond(self, text: str, max_tokens: int | None = None) -> str: try: return (await self.achat(text, max_tokens=max_tokens)).surface except ValueError: return "" # The previous ``_default_identity_manifold()`` constructor was removed as # part of ADR-0027. Identity is now loaded from a pack at runtime via # ``packs.identity.loader.load_identity_manifold`` using # ``RuntimeConfig.identity_pack`` (default ``DEFAULT_IDENTITY_PACK``). # The previously-hardcoded three axes (truthfulness / coherence / # reverence) live in ``packs/identity/default_general_v1.json``.