# ADR-0114a.10 — Pack-Provenance Auditor (Obligation #10 wired for B3) **Status:** Accepted **Date:** 2026-05-23 **Author:** CORE main agent (Opus 4.7) **Depends on:** ADR-0114a (10 anti-overfitting obligations), ADR-0116 (deterministic solver), ADR-0117 (solution-trace verifier), ADR-0131.3 (B3 bounded grammar lane), ADR-0131.4 (composite math-expert gate) **Parent:** ADR-0114a **Sequencing:** first of 5 remaining ADR-0114a obligations for `mathematics_logic`. Subsequent: #2 OOD ratio, #5 perturbation, #6 depth curve, #8 adversarial. --- ## Context ADR-0114a Obligation #10 reads: > Every `SolutionTrace.steps[*].pack_lemma_id` resolves to a real > lexicon entry in the domain's operator pack. The solver already enforces this at solve time. `_resolve_pack_lemmas` in `generate/math_solver.py` (lines 153–190) loads `en_arithmetic_v1`, looks up the 8 required operation kinds against the pack's lemma surfaces, and refuses to construct a `SolutionTrace` if any lemma is missing. Every `SolutionStep` then carries a `pack_lemma_id` of the form `:`. ADR-0114a's discipline is anti-overfitting: a guarantee that lives only inside the function that produces traces is one bug away from silently failing. The obligation requires an **external audit** — independent reading of the pack on disk, independent walk of the trace, byte-level resolution check. ## Decision Implement an **external pack-provenance auditor** at `core/capability/pack_provenance.py`: 1. **Re-read the pack lexicon from disk** (independent of the solver's loader). 2. **Re-run the candidate-graph pipeline** on each case of a B-lane. 3. **Walk every solver step** and validate `pack_lemma_id` parses as `:` AND `pack_id == expected` AND `lemma ∈ pack_lexicon`. 4. **Per-case + per-lane verdict**, deterministic `obligation_10_passed: bool`. 5. **CLI** `core capability pack-provenance` writes a committed audit artifact at `evals/obligation_10_pack_provenance/.json`. ### What's wired (this PR) **B3 (bounded grammar)** under `en_arithmetic_v1`. B3 is the lane whose pipeline (parser → graph → solver → verifier) exercises `math_solver` end-to-end and produces traces with non-trivial step counts — exactly what obligation #10 is structured around. ### What's deferred to follow-up sub-ADRs - **B1 (symbolic equivalence) equivalent.** B1's verification path is algebra-based (`check_equivalence(A, B)`), not arithmetic- step-based. The pack-lemma notion needs reframing — likely "every equivalence check's normalization chain references real algebra-pack lemmas." Separate ADR. - **B2 (teaching corpus) extension.** B2 may exercise the same `math_solver` depending on its corpus contents; the auditor below can be extended once that's confirmed case-by-case. The auditor's `validate_lane(lane_id=..., cases_path=...)` signature makes that a trivial extension when the time comes. ## Empirical verdict on current main ``` $ python3 -m core.cli capability pack-provenance lane: B3_bounded_grammar pack_id: en_arithmetic_v1 cases_total: 50 cases_validated: 25 cases_skipped_unsolved: 25 (refusal-expected probes — by design) cases_violated: 0 obligation_10_passed: True distinct_lemma_ids_observed: - en_arithmetic_v1:add - en_arithmetic_v1:compare_additive - en_arithmetic_v1:compare_multiplicative - en_arithmetic_v1:subtract - en_arithmetic_v1:transfer ``` **Obligation #10 passes on B3** — every solver step on every expected-correct case resolves to a real lemma in `en_arithmetic_v1`. 5 of the 8 operation kinds are exercised by B3's grammar; the other 3 (`multiply`, `divide`, `apply_rate`) are registered in the pack but not in B3's case set — fine, they ratify-at-solve-time via `_resolve_pack_lemmas` so the obligation holds for them too if a future case exercises them. ## What this does NOT do - Does NOT change the solver. The solver's pack binding is already correct; this PR audits it from outside. - Does NOT modify any B-lane runner or case set. - Does NOT change the `SolutionTrace` schema. The `pack_lemma_id` field already exists. - Does NOT promote `mathematics_logic` to `expert`. Promotion requires this obligation + 4 others (#2 OOD, #5 perturbation, #6 depth curve, #8 adversarial) + the composite gate from ADR-0131.4 + reviewer signature via ADR-0092. - Does NOT wire B1 or B2 equivalents. Each is its own follow-up ADR (above). ## Trust boundary - **Reads only**: - `language_packs/data/en_arithmetic_v1/lexicon.jsonl` (the pack-on-disk; auditor MUST read this independently rather than trusting the solver's loader) - `evals/math_bounded_grammar/v1/cases.jsonl` (the B3 case set) - **Writes only**: the path passed to `emit_provenance_report` (default `evals/obligation_10_pack_provenance/B3_bounded_grammar.json`). - No dynamic imports, no shell passthrough, no network. - Pure deterministic function — verified by `test_validate_lane_is_deterministic` + `test_artifact_emission_byte_equal`. ## Tests `tests/test_adr_0114a_10_pack_provenance.py` — 19 tests: | Group | Count | What it pins | |---|---|---| | lemma-id parser | 8 | well-formed accepted; malformed rejected | | lexicon loader | 4 | real pack loads; missing/invalid file refuses | | lane validator | 5 | passes on real B3; observes expected lemma surface shape; refuses on missing pack/cases; skips refusal-expected cases without false violation | | determinism | 2 | report identical across two calls; artifact byte-equal | All pass in 0.27s. ## Composition with ADR-0131.4 A future full ADR-0120 wire-up would consume both `evaluate_composite_math_gate()` (composite benchmark verdict) AND `validate_lane()` (obligation #10 verdict) — plus the four remaining obligation auditors when they land — and emit a single signed `expert_claims` artifact for ledger promotion. The composite gate doesn't change. The `evaluate_composite_math_gate` function in ADR-0131.4 (PR #188) gates the math-specific *benchmark* portion of the contract; this auditor gates the math-specific *obligation #10* portion. They're orthogonal and compose multiplicatively. ## CLAUDE.md PR-checklist - **Capability added:** external pack-provenance auditor for obligation #10; makes the solver's pack-binding externally-falsifiable per ADR-0114a's audit discipline. - **Invariant proving field validity:** `obligation_10_passed` on B3; 0 violations across 25 validated cases; 5 distinct lemma_ids all resolve. - **CLI/eval proving the lane:** `python3 -m core.cli capability pack-provenance` + `pytest tests/test_adr_0114a_10_pack_provenance.py`. - **Avoided hidden normalization / stochastic / approximate / unreviewed mutation:** Yes. Pure deterministic auditor. - **Trust boundary:** read-only inputs from documented paths; single deterministic write to documented artifact path; no dynamic imports.