Shay
|
0cf54a009d
|
feat(adr-0087): rhetorical-style pack substrate (loader + default_unstyled_v1) (#74)
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)
|
2026-05-20 16:19:36 -07:00 |
|