from __future__ import annotations from dataclasses import dataclass @dataclass(frozen=True, slots=True) class RuntimeConfig: # ADR-0063 — ``en_core_relations_v1`` (kinship starter pack) joins the # default mount once the cross-pack surface resolver lands. Pack # composers in :mod:`chat.pack_grounding` now consult # :mod:`chat.pack_resolver`, so kinship lemmas ground deterministically # without a separate composer module. input_packs: tuple[str, ...] = ( "en_minimal_v1", "en_core_cognition_v1", "en_core_meta_v1", "en_core_attitude_v1", "en_core_temporal_v1", "en_core_action_v1", "en_core_quantitative_v1", "en_core_spatial_v1", "en_core_causation_v1", "en_core_polarity_v1", "en_core_relations_v1", "en_core_relations_v2", "he_logos_micro_v1", "grc_logos_micro_v1", ) output_language: str = "en" frame_pack: str = "en" max_tokens: int = 32 allow_cross_language_recall: bool = True allow_cross_language_generation: bool = False vault_reproject_interval: int = 20 use_salience: bool = True salience_top_k: int = 16 inhibition_threshold: float = 0.3 inner_loop_admissibility: bool = False admissibility_threshold: float = 0.0 # ADR-0026 / Phase 3 — margin-based admissibility. ``mode`` # selects between ADR-0024's per-candidate threshold check and # the ranked-with-margin check. Default "threshold" preserves # ADR-0024 acceptance evidence; opt-in "margin" replaces the # static-threshold gate with a scale-invariant margin. admissibility_mode: str = "threshold" admissibility_margin: float = 0.4 # ADR-0027 — Identity pack id loaded at runtime startup. Empty string # resolves to ``DEFAULT_IDENTITY_PACK``. CLI override on chat: # ``core chat --identity ``. See docs/identity_packs.md. identity_pack: str = "" # ADR-0033 — Ethics pack id loaded at runtime startup. Empty string # resolves to ``DEFAULT_ETHICS_PACK``. See docs/ethics_packs.md. ethics_pack: str = "" # ADR-0046 / ADR-0047 — forward graph constraint. When True, the # PropositionGraph built from the classified intent + articulation # plan is converted into an AdmissibilityRegion BEFORE generate() # runs (Pillar 1→2→3 coupling closes on the live path). Default # False preserves existing behavior during the transition window — # ADR-0024's honest-refusal exhaustion is the correct response when # the constraint geometry and the walk candidate pool do not # intersect, but operators must opt in to observing that behavior # on their workloads. Enable to live the forward constraint; # disable to retain the pre-ADR-0046 unconstrained walk. forward_graph_constraint: bool = False # ADR-0062 — composed teaching-grounded surface. When enabled, # the teaching-grounded composer extends a single-chain surface # with a follow-up chain whose subject equals the initial chain's # object — producing surfaces like "light reveals truth, which # grounds knowledge" instead of just "light reveals truth". # Default False preserves all pre-ADR-0062 behaviour. Cycle-safe # (won't follow if the next subject has been visited), bounded # depth (max one follow-up chain in v1). composed_surface: bool = False # ADR-0083 — transitive (multi-hop) teaching-grounded surface. # Strict superset of ADR-0062's depth-1 composer: iterates the # per-hop follow-up resolution under a visited-set guard, so the # surface can extend beyond a single follow-up chain. # ``transitive_max_depth`` is the maximum number of follow-up hops # to append beyond the initial chain. At ``max_depth=0`` # byte-identical to the single-chain surface; at ``max_depth=1`` # byte-identical to ADR-0062's composed surface; at # ``max_depth=2`` byte-identical to ADR-0062 when no second hop # exists, strict superset when one does. When True, this # supersedes ``composed_surface``. Cycle-safe across every depth # (visited-set covers ADR-0062's 1-step cycle guard). Single- # corpus traversal in v1; cross-corpus transitive is deferred to # a follow-up ADR. transitive_surface: bool = False transitive_max_depth: int = 2 # ADR-0085 — gloss-aware CAUSE surface. When True, IntentTag.CAUSE # consults the subject lemma's gloss first and emits an explanation- # shaped sentence drawn from the gloss text, falling through to # the chain-walk ``teaching_grounded_surface*`` only when no gloss # exists for the lemma. Default False preserves the pre-ADR-0085 # chain-walk surface byte-identically (null-drop invariant). gloss_aware_cause: bool = False # ADR-0087 — rhetorical-style selection axis (substrate phase). # ``None`` resolves to ``DEFAULT_RHETORICAL_STYLE_PACK`` per the # mounting discipline, which is the null-lift baseline. No # composer or realizer reads this field yet — that wiring is the # consumer ADR's job. The field is declared here so the runtime # interface is stable when the consumer lands. rhetorical_style_id: str | None = None # ADR-0066 / P3.2 — opt-in thread anaphora. When enabled, the # runtime prepends a deterministic backreference to a recent # grounded turn when the current turn's subject lemma matches # one in the bounded session-thread context. Engages only on # pack/teaching-tier turns (both prior and current); weaker # tiers do not anchor. Default False preserves every pre-P3.2 # surface byte-identically. thread_anaphora: bool = False # Discourse planner (step 5 of the discourse-planner sequencing). # When True, the runtime builds a deterministic DiscoursePlan via # ``generate.discourse_planner.plan_discourse`` from a # ``GroundingBundle`` assembled by ``generate.grounding_accessors`` # and renders it as multi-clause output. Mode selection comes from # ``generate.intent.classify_response_mode``; BRIEF mode is # byte-identical to today's single-sentence pack-grounded surface # because the runtime hook (``_maybe_apply_discourse_planner``) # returns ``None`` when the rendered plan has <= 1 move — single- # fact prompts get exactly the same surface and trace_hash as # the planner-off path. # # Default flipped to True 2026-05-21: cognition eval (45 cases) # was verified byte-identical across both projections (surface # AND trace_hash) flag-OFF vs flag-ON, so single-fact prompts are # not perturbed. The flag's value shows up on NARRATIVE / EXAMPLE # / PARAGRAPH / EXPLAIN modes and compound prompts that the flat # classifier currently misclassifies as OOV — those turns become # multi-clause grounded articulations rather than single-fragment # disclosures or OOV refusals. discourse_planner: bool = True # Phase 3 — plan-level contemplation pre-flight. When True, after # the discourse planner produces a plan (and before the renderer # fires) the runtime runs ``core.contemplation.plan_preflight. # contemplate_plan`` over the plan and stores any SPECULATIVE # findings on the runtime for downstream consumption. Per # ADR-0080: contemplation is read-only and SPECULATIVE-only — no # plan mutation, no autonomous memory promotion. Findings flow # into the offline contemplation miner (Phase 5) for review-gated # pack-mutation candidates. Default False keeps the contemplation # opt-in until the operator wires the downstream sink. discourse_contemplation: bool = False # ADR-0068 / ADR-0069 — register pack id loaded at runtime startup. # ``None`` resolves to ``RegisterPack.unregistered()`` (the in-memory # null-register sentinel; structurally identical to # ``default_neutral_v1``). At R2 the register is loaded, stored, and # threaded through every realizer call site, but no composer consumes # it — the three byte-identity invariants (None ≡ default_neutral_v1 # ≡ pre-R2 output) are CI-pinned by ``test_register_null_lift.py``. # R3 widens composers to dispatch on ``register.realizer_overrides``. register_pack_id: str | None = None # ADR-0073b — anchor-lens pack id loaded at runtime startup. # ``None`` resolves to ``AnchorLens.unanchored()`` (the in-memory # null-lens sentinel; structurally identical to # ``default_unanchored_v1``). At L1.2 the lens is loaded and # stored on the runtime, but no composer consumes it — the # ``anchor_lens_byte_identity_null_lift`` invariant (None ≡ # default_unanchored_v1) is CI-pinned by # ``test_anchor_lens_null_lift.py``. L1.3 will widen composers # to dispatch on ``anchor_lens.semantic_domain_preferences``. anchor_lens_id: str | None = None # Finding 6 (audit 2026-05-20) — generation stop tokens. # # ``None`` resolves to ``generate.stream._STOP_TOKENS`` (the # historical ``frozenset({"it", "to", "word"})``) so every # pre-Finding-6 caller preserves byte-identity. Operators that # mount a pack where one of the historical stop tokens carries # meaningful content (e.g. a philosophy pack where ``word`` maps # to λόγος, a syntax pack where ``to`` is a content node) can # override the set here to free them. Manifest-driven # per-pack override (``generation_stop_tokens`` field in the pack # manifest) is the natural next step; that requires a pack- # schema ADR and re-ratification so the wiring lands first. stop_tokens: tuple[str, ...] | None = None # ADR-0088 Phase B (audit Finding 2, 2026-05-20) — realizer becomes # a real surface authority by grounding the proposition graph from # the recall step's walk tokens before invoking # ``realize_semantic``. Default ``False`` preserves byte-identity # for every existing pack and test — the pipeline's # ``realize_semantic`` call continues to fire on the ungrounded # graph, which produces ```` / ``...`` surfaces that the # ``_is_useful_surface`` gate rejects, leaving the runtime path's # ADR-0085-polished pack-grounded surface as the user-visible # answer (same as today). # # When True the pipeline reorders ``realize_semantic`` to run # AFTER ``runtime.chat``, calls ``ground_graph(graph, # response.recalled_words)`` to fill the ```` slots, then # re-invokes ``realize_semantic`` on the grounded graph. The # surface resolver (PR #76) then picks the realizer's grounded # output when it clears ``_is_useful_surface`` and the unknown- # domain gate did not fire. # # NOTE — Phase A (realizer fluency parity: gloss-aware templates, # 3sg verb agreement, pack-provenance tag) is the prerequisite # for enabling this flag in production. The known fluency gap # (e.g. ``"Light is a visible medium that reveal truth"``, # subject-verb disagreement) is documented in ADR-0088 §Phase A. # The wiring lands first so the runtime contract is stable when # Phase A's realizer fluency upgrade ships. realizer_grounded_authority: bool = False # ADR-0090 (audit Findings 6 + 7, 2026-05-21) — unified-ingest path. # Default ``False`` preserves bit-identical behavior with the # historical probe-then-commit path: # # 1. ``chat()`` calls ``probe_ingest(filtered)`` first. # 2. The gate observes ``probe_state.F``. # 3. If the gate fires, ``commit_ingest`` runs and a stub # response returns. If not, ``commit_ingest`` runs and # drive bias is applied, producing a different field # from the one the gate saw. # # The probe/commit distinction means the gate decides on one # manifold position and the walk navigates a slightly different # one — a coherence gap the second-opinion audit named. # # When ``True``: # # 1. ``chat()`` calls ``commit_ingest(filtered)`` first. # 2. Drive bias is applied immediately. # 3. The gate observes ``committed.F`` (the same field the walk # will navigate on the non-stub path). # 4. ``probe_ingest`` is skipped entirely on this path. # # **Semantic change when True:** stub-path turns commit before # the stub response is generated. Under ``False`` stub turns do # not commit at the time of the gate check (today's behavior). # Operators opt into the cleaner coherence by flipping the flag; # the unified path is not the default until validated against a # live workload. unified_ingest: bool = False # ADR-0144 — recognition-grounded articulation graph. When True and a # DerivedRecognizer is attached to CognitiveTurnPipeline, the articulation # graph is derived from the admitted EpistemicNode via the connector rather # than from intent classification. Default False preserves byte-identity # for every existing surface and trace_hash. recognition_grounded_graph: bool = False # W-016 — wire session vault (T1) into discovery contemplation. # When True, ChatRuntime builds a vault probe from the live session # vault and passes it to contemplate() in _emit_discovery_candidates. # The probe queries the vault at EpistemicStatus.COHERENT so only # reviewed-coherent session entries contribute evidence; SPECULATIVE / # CONTESTED / FALSIFIED entries are filtered by the vault layer per # ADR-0021 §3. Default False preserves all pre-W-016 discovery # output byte-identically (null-drop invariant on discovery lanes). vault_probe_discoveries: bool = False # ADR-0148 — wire VaultPromotionPolicy into turn boundary. # When True, ChatRuntime calls vault.promote_eligible_entries() after each # finalize_turn(), scanning SPECULATIVE entries for crystallization to # COHERENT based on their energy profile (EnergyClass E0/E1, coherence_residual # ≤ 0.05). Fresh entries written in the current turn are E2+ and will not # promote yet — the policy fires on entries that have cooled across turns. # Default False: zero behavior change when disabled (null-drop invariant). # Unlocks W-007 (DerivedRecognizer derivation from promoted COHERENT entries). vault_promotion_enabled: bool = False # ADR-0150 — run contemplation on pending discovery candidates at checkpoint. # Activates ADR-0056 Phase C1. Null-drop when False. auto_contemplate: bool = False # ADR-0151 — generate TeachingChainProposals from enriched candidates on load. auto_proposal_enabled: bool = False DEFAULT_IDENTITY_PACK: str = "default_general_v1" DEFAULT_ETHICS_PACK: str = "default_general_ethics_v1" DEFAULT_REGISTER_PACK: str = "default_neutral_v1" DEFAULT_ANCHOR_LENS: str = "default_unanchored_v1" DEFAULT_CONFIG = RuntimeConfig()