Phase 2.3: the first inference rule + the wrong=0 mechanism for proofs. - generate/proof_chain/rules.py: evaluate_modus_ponens / evaluate_proof_conclusion. Proof-layer dispatch (Option B) over proposition FORMULAS via the canonicalizer; never touches check_admissibility/_resolve_dep_units (proofs have no units). Disagreement rule = the select_self_verified twin: pool ALL admissible single-step MP derivations, require a unique canonical key == declared conclusion. Pooling (not filter-to-declared-first) is the soundness mechanism. - generate/logic_canonical.py: parse_top_implication (+ _unparse) — recovers an implication's syntactic antecedent/consequent (the ROBDD form doesn't preserve it). - Closed typed-reason set; the corpus's finer labels consolidate (6 disagreement refuse-labels -> conclusion_disagreement; 4 antecedent-flavor labels -> unestablished_antecedent — same redundancy, same mechanism-makes-one-distinction principle). - Honesty boundary (exact scope): guarantees a unique conclusion among SINGLE-STEP MP over the premises, NOT "uniquely entailed" by all strategies. Cross-check: all 24 GPT-5.5 adversarial corpus cases agree on OUTCOME against the real rule (no rule bug / no corpus outcome-misread); reasons consolidate as above. Mutation: filter-to-declared-first makes DISAGREE-007/010 wrongly admit -> pooling tests fail (pooling load-bearing). Drive-by fix (cleanup-as-you-find): merged ADR-0204 ProofNode.__post_init__ was dedented to module level -> all ProofNode validation was silently DEAD (smoke skips the dedicated test file; the smoke != full-suite hazard). Re-indented; validation restored. Additive (math lane untouched). Full binding-graph surface green; smoke 67.
6.4 KiB
ADR-0205 — modus_ponens + the Disagreement Rule (proof_chain's first inference rule)
Status: Accepted (proof_chain phase 2.3 — the first inference rule + the wrong=0 mechanism)
Date: 2026-06-02
Relates to: ADR-0204 (proof-graph builder — the proof-step shape this rule consumes),
ADR-0201/0201.1/0202 (canonicalizer + contract), ADR-0203 (acyclicity guard),
generate.derivation.verify.select_self_verified (the arithmetic twin of the disagreement rule).
Deferred to: ADR-0206 (atom→carrier grounding).
Context
proof_chain has a DAG substrate (ADR-0204) and a canonical form (ADR-0201). Phase
2.3 adds the first inference rule (modus_ponens) and the wrong=0 mechanism
for proofs (the disagreement/uniqueness rule). GPT-5.5 authored an independent
adversarial corpus (modus_ponens_cases.json, 24 cases: 6 valid / 8 invalid / 10
disagreement) against the ADR-0202 grammar, with the proof-step shape left
[PENDING ADR-0204 SHAPE] and the disagreement cases [CONFIRM WITH OPUS] — the
contract-first handshake.
Decision
1. The MP-rule contract (committed before the rule)
- Proof-step shape = the already-landed ADR-0204
Proof/ProofNode(proof_from_premisesdesugars premises→premise-nodes, conclusion→mp-node). The[PENDING ADR-0204 SHAPE]placeholders resolve to this; the rule also accepts a bare(premises, conclusion)for corpus cross-check. - Closed typed-reason set (
generate/proof_chain/rules.py::MP_REASONS):unique_canonical_conclusion(admit);missing_implication,unestablished_antecedent,conclusion_mismatch,conclusion_disagreement(refuse).
2. evaluate_modus_ponens — ROBDD-exact, bypassing unit-resolution
In generate/proof_chain/rules.py (the proof-layer dispatch, ADR-0205 Option B):
operates on proposition formulas via the canonicalizer; never calls
check_admissibility / _resolve_dep_units (proofs have no units — the named 2.2
constraint, satisfied by construction). An implication A→B fires iff key(A) is
an established premise key; the consequent B is recovered syntactically
(logic_canonical.parse_top_implication) because the ROBDD form does not preserve
which side is antecedent (P→Q and ¬P∨Q share one diagram).
3. The disagreement / uniqueness rule (the wrong=0 mechanism)
The literal twin of select_self_verified: pool ALL admissible single-step MP
derivations the premise set supports, collect their canonical conclusion keys, and
admit iff they collapse to exactly one key equal to the declared conclusion;
≥2 distinct keys → refuse (conclusion_disagreement); one key ≠ declared →
conclusion_mismatch; no admissible derivation → missing_implication /
unestablished_antecedent.
Pooling over the premise set — NOT filtering to the declared conclusion first —
is the soundness mechanism. Filter-first would admit-by-assertion when the same
premises admit a different key (the 20/5 == 4 class one level up): e.g. a premise
set that MP-derives both an unrelated tautology and the declared atom, or both the
declared atom and a strictly stronger one. Collapse-vs-conflict is judged by exact
canonical key, never surface — equivalent paths (P∧Q/Q∧P, Q→R/¬Q∨R,
Q/Q∧(R∨¬R)) collapse and admit; subtly-different ones (P∧Q/P∨Q, P→Q/Q→P,
Q/Q∧R) refuse.
4. Reason-set consolidation (the reconciliation finding)
The mechanism makes one distinction per axis; the corpus's finer labels collapse onto the closed set — the same redundancy confirmed for the disagreement labels applies to the invalid labels:
- 6 disagreement refuse-labels (conflicting / contradictory / tautology_vs_substantive
/ distinct_atom / near_miss / stronger) →
conclusion_disagreement(the rule knows only: keys agree or they don't). - 4 antecedent-flavor labels (missing_antecedent / antecedent_mismatch /
affirming_consequent / implication_direction_mismatch) →
unestablished_antecedent(the rule knows only: the available implication's antecedent is or isn't an established premise;affirming_consequentandimplication_direction_mismatchare the identical patternA→B, conclusion≡A, premise≡B— no mechanical distinction).
This consolidation is conveyed back to GPT-5.5 for the corpus's committed reasons.
Honesty boundary (load-bearing — exact scope stated)
Through phase 2.3, proof_chain is sound over its declared atoms, not grounded in recognized input (grounding is 2.4). And the disagreement rule's guarantee has a precise scope: it guarantees a unique conclusion among single-step modus ponens derivations over the given premises — NOT "uniquely entailed" by all proof strategies (a stronger, currently-false claim). This must never be read as "uniquely entailed." Same discipline as propositional-not-FOL.
Evidence (cross-checked, not asserted)
- All 24 corpus cases agree on OUTCOME against the real rule (6 admit / 8 refuse / 4 admit / 6 refuse) — no rule bug, no corpus outcome-misread. Reasons: 3 exact, 21 consolidate onto the closed set as above.
tests/test_proof_chain_rules.py— the 24 cases (transcribed, reproducible) + the pooling guards. Mutation: a filter-to-declared-conclusion-first variant makes MP-DISAGREE-007/010 wrongly admit →test_pooling_*fail. Pooling is load-bearing.- Drive-by fix (cleanup-as-you-find): the merged ADR-0204
ProofNode.__post_init__was dedented to module level, so allProofNodevalidation was silently dead (smoke skips the dedicated test file, so the merge missed it — the "smoke ≠ full suite" hazard). Re-indented; validation restored;test_self_dependency_refused_at_proof_modelpasses. - Full binding-graph + admissibility surface green; smoke 67. Additive (no math path touched).
Three deferrals — settled
- modus_ponens bypasses unit-resolution — by construction (proof-layer dispatch).
- Conclusion typing — the rule needs the declared conclusion's key + premise
formulas, both reachable;
conclusion_symbol_idsuffices, noBoundUnknown. semantic_role— proofs still not wired into serving; stays"unknown".
Deferred
- 2.4 — atom→ADR-0144
EpistemicNodegrounding (ADR-0206). Until then, "sound over declared atoms," never "reasons over input." - Multi-step proof composition and a proof search (the disagreement rule here is over a given premise set, single-step) — later, separately scoped.