Substrate-only code-side for ADR-0087 (Rhetorical Style as Selection
Axis). No composer or realizer touches the new pack yet; consumer
integration is the follow-up ADR.
packs/rhetorical_style/ (new)
- loader.py: RhetoricalStylePack frozen dataclass + load_rhetorical_
style_pack() with fail-closed mastery-report self-seal verification
- __init__.py: re-exports (RhetoricalStylePack, RhetoricalStylePack-
Error, load_rhetorical_style_pack, DEFAULT_RHETORICAL_STYLE_PACK)
- default_unstyled_v1.json + .mastery_report.json: ratified null-lift
baseline pack (all three constraint lists empty,
default_unstyled=true)
scripts/ratify_rhetorical_style_packs.py (new)
- Mirror of scripts/ratify_anchor_lens_packs.py for the rhetorical-
style pack family. Computes pack_source_sha256 with mastery_report_
sha256 blanked, builds self-sealed mastery report, writes both
files. Idempotent. Uses formation.hashing for canonical JSON +
self_seal.
Schema gate (ADR-0087 §Verification)
- Required keys allow-list: pack_id, schema_version, version,
issued_at, default_unstyled, permitted_frames,
required_moves_per_claim, forbidden_moves, provenance,
mastery_report_sha256
- Unknown keys rejected (strict gate)
- permitted_frames: allow-list {warrant, concession, hedge,
definitional_move}
- required_moves_per_claim / forbidden_moves: allow-list {claim,
evidence, warrant, concession, hedge, bare_assertion, definitional}
- default_unstyled=true ⟺ all three lists empty
- non-default pack must declare at least one constraint (distinguishes
from null-lift)
- Duplicates within a list rejected
Ratification gate
- require_ratified=True by default
- CORE_ALLOW_UNRATIFIED_RHETORICAL_STYLE=1 env-var bypass for dev
- Companion mastery report SHA must match pack's declared sha
- verify_seal(report) must pass (self-seal integrity)
- Sister to packs.safety.SafetyPackError pattern
core/config.py
- Added RuntimeConfig.rhetorical_style_id: str | None = None
- No runtime code reads it yet — that's the consumer ADR's job
- Field declared so the interface is stable when consumer lands
Tests (tests/test_adr_0087_rhetorical_style_substrate.py — 20)
- Default pack loads, is_null_lift, mastery-report self-seal verified,
discovery lists it as ratified
- Schema gate: missing key, unknown key, unknown frame, unknown move,
duplicate frame, default_unstyled-with-constraints,
non-default-with-zero-constraints, pack_id mismatch, path traversal
- Ratification gate: unratified pack rejected by default, env-var
bypass, companion report missing, companion sha mismatch
- RuntimeConfig field: default None, accepts string, independent of
other axes
Lanes
smoke 67/0, cognition 120/0/1, packs 6/0. core eval cognition
byte-identical 100/91.7/100/100.
Null-lift consumer test deferred
ADR-0087 §Required tests lists rhetorical_style_null_lift as a
required invariant. Today it would be trivially true because no
composer reads the field. The invariant becomes meaningful when
the consumer ADR wires the field through the dispatch — at that
point the null-lift test goes into the consumer PR alongside the
three-axis orthogonality test.
Scope per ADR-0087 §Scope limits
- No consumer code (composer/realizer changes deferred)
- No genre packs (en_academic_v1, etc. are content efforts after
consumer lands)
- No prompt-routing (operator-set only)
153 lines
7.6 KiB
Python
153 lines
7.6 KiB
Python
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 <pack_id>``. 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
|
|
# so the default-False path is fully preserved.
|
|
discourse_planner: 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
|
|
|
|
|
|
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()
|