Deduction serving stops being a bare flag and becomes an EARNED capability governed by the ADR-0175 reliability gate over the ADR-0199 learning arena -- the arena's second concrete instance (GSM8K math is the first) and the reliability substrate's first non-estimation serving consumer, a concrete dent in that zone's standing 'designed, not wired' critique. Non-circular thesis: the ROBDD engine is sound+complete (never wrong on the problem it's handed), so the license does NOT certify it. It certifies the full pipeline (reader -> projector -> engine) per argument shape -- the FALLIBLE part is the template reader, which can misparse an argument and hand the engine the wrong problem. The arena catches that as a 'wrong' by comparing the pipeline's committed outcome to by-construction gold. New: generate/proof_chain/shape.py (4 exhaustive structural shape-bands, the capability axis, shared by arena + serving), evals/deduction_serve/practice/ (the deduction arena instance: deterministic synthetic corpus, by-construction gold independent of the reader per ADR-0199 L-2, cross-checked against the INDEPENDENT truth-table oracle before sealing), chat/data/deduction_serve_ ledger.json (committed SHA-sealed ledger, 4 bands x 720 correct/0 wrong), chat/deduction_serve_license.py (tamper-evident serving reader, mirrors estimation_license.py), tests/test_deduction_serve_license.py (13 tests), docs/adr/ADR-0256 (+ resolves the ADR-0206 numbering collision in doc form). Changed: chat/deduction_surface.py (composer consults the license: earned band -> authoritative Phase-1 surface; unearned/stripped ledger -> same sound answer served DISCLOSED/hedged -- authority now rests on committed evidence, not a boolean), core/config.py (flag docstring: enables an EARNED path), core/cli_test.py (deductive suite runs the license test). All four structural bands earn SERVE at reliability 0.99087 (>= theta_SERVE 0.99) with wrong=0, so Phase-1 behavior is preserved byte-for-byte; the gate's teeth are proven by injecting an empty ledger (the same sound answer degrades to a disclosed hedge). Did NOT reuse the ADR-0206 govern_response bridge (its STRICT/APPROXIMATE 'widen-past-strict' semantics are the opposite shape from deduction's always-sound answer); did NOT rewrite report.json's stale adr field (SHA-pinned bytes, documented in ADR-0256 s2a instead). [Verification]: smoke 180 passed; cognition 122 passed/1 skipped; core test --suite deductive 38 passed; architectural_invariants 75 passed; practice runner 4 bands all SERVE wrong=0.
70 lines
2.5 KiB
Python
70 lines
2.5 KiB
Python
"""Deterministic shape-band classification for propositional arguments
|
|
(deduction-serve arc, Phase 3).
|
|
|
|
The **capability axis** the reliability ledger (ADR-0175) keys on for
|
|
deduction serving. A shape-band is a purely structural signature of the
|
|
projected ``(premises, query)`` formula strings — cheap, deterministic, and
|
|
computed identically by the practice arena (which earns the per-class SERVE
|
|
license) and the serving composer (which consults it). No decision logic:
|
|
this says *what kind of argument* a case is, never whether it is entailed.
|
|
|
|
The bands are deliberately coarse — each holds a MIX of entailed/refuted/
|
|
unknown cases — so a class's measured reliability answers "does the whole
|
|
serving pipeline (reader → projector → engine) decide arguments of this
|
|
shape correctly?", which is the fallible part (the reader can misparse; the
|
|
ROBDD engine cannot). A band that only ever saw entailed cases would measure
|
|
nothing the engine's soundness doesn't already guarantee.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
_IMPLIES = " implies "
|
|
_OR = " or "
|
|
|
|
#: The closed set of shape-bands. Serving classifies into exactly one of these;
|
|
#: the practice arena earns a per-band SERVE license over the same set.
|
|
DISJUNCTIVE = "disjunctive"
|
|
CONDITIONAL_CHAIN = "conditional_chain"
|
|
CONDITIONAL_SINGLE = "conditional_single"
|
|
ATOMIC = "atomic"
|
|
|
|
SHAPE_BANDS: tuple[str, ...] = (
|
|
DISJUNCTIVE,
|
|
CONDITIONAL_CHAIN,
|
|
CONDITIONAL_SINGLE,
|
|
ATOMIC,
|
|
)
|
|
|
|
|
|
def classify_deduction_shape(premises: tuple[str, ...], query: str) -> str:
|
|
"""The structural shape-band of a projected propositional argument.
|
|
|
|
Priority order (first match wins), by the connectives present in the
|
|
PREMISES only (the query's shape is a downstream concern of the engine,
|
|
not of the capability axis):
|
|
|
|
- ``disjunctive`` — any premise is a disjunction (``A or B``).
|
|
- ``conditional_chain`` — two or more conditional premises (``A implies B``).
|
|
- ``conditional_single`` — exactly one conditional premise.
|
|
- ``atomic`` — no conditional, no disjunction (bare atoms /
|
|
negations only).
|
|
"""
|
|
has_or = any(_OR in p for p in premises)
|
|
if has_or:
|
|
return DISJUNCTIVE
|
|
n_implies = sum(1 for p in premises if _IMPLIES in p)
|
|
if n_implies >= 2:
|
|
return CONDITIONAL_CHAIN
|
|
if n_implies == 1:
|
|
return CONDITIONAL_SINGLE
|
|
return ATOMIC
|
|
|
|
|
|
__all__ = [
|
|
"ATOMIC",
|
|
"CONDITIONAL_CHAIN",
|
|
"CONDITIONAL_SINGLE",
|
|
"DISJUNCTIVE",
|
|
"SHAPE_BANDS",
|
|
"classify_deduction_shape",
|
|
]
|