Wave 2, parallel with W2-B/C/D. Implements the type-A→type-B converter
from AuditRow to MathReaderRefusalEvidence per ADR-0167 W2-A brief.
Deliverables:
- teaching/math_contemplation.py:
- audit_to_evidence(audit_rows): pure deterministic adapter, uses
SUB_TYPE_FOR_OPERATOR for subtype assignment, skips rows where
missing_operator is None, leaves claim_signature="" (W2-B will fill)
- audit_problem_to_evidence(problem_text, case_id): convenience wrapper
that runs the reader and adapts the output
- tests/test_math_contemplation_adapter.py: 8 tests covering
determinism, input-order preservation, sub-type mapping
exhaustiveness, distinct hashes across cases, empty input handling,
None-operator skip, and round-trip from problem text
Invariants:
- Deterministic across reruns (verified by determinism rerun)
- No I/O in adapter path
- Input order preserved (no internal sort)
- claim_signature == "" for all W2-A records (W2-B coordination)
Validation:
- tests/test_math_contemplation_adapter.py: 8 passed
- tests/test_math_evidence_schema.py: 11 passed (W1-A regression)
- tests/test_brief_11b_audit_artifact.py + step2_lexicon + brief_11_audit:
45 passed (regression)
- Determinism rerun: identical results
47 lines
1.5 KiB
Python
47 lines
1.5 KiB
Python
"""ADR-0167 W2-A — audit-to-evidence adapter.
|
|
|
|
Pure deterministic conversion from comprehension audit rows into typed
|
|
``MathReaderRefusalEvidence`` records for the teaching corridor.
|
|
"""
|
|
|
|
from __future__ import annotations
|
|
|
|
from typing import Iterable
|
|
|
|
from generate.comprehension.audit import AuditRow, audit_problem
|
|
from teaching.math_evidence import (
|
|
MathReaderRefusalEvidence,
|
|
SUB_TYPE_FOR_OPERATOR,
|
|
from_audit_row,
|
|
)
|
|
|
|
|
|
def audit_to_evidence(
|
|
audit_rows: Iterable[AuditRow],
|
|
) -> tuple[MathReaderRefusalEvidence, ...]:
|
|
"""Convert audit rows into typed teaching-corridor evidence records.
|
|
|
|
Pure function. Deterministic. No filesystem, no network, no mutation.
|
|
Sub-type assignment from ``teaching.math_evidence.SUB_TYPE_FOR_OPERATOR``.
|
|
Skips rows with ``missing_operator=None`` (no sub_type → no candidate).
|
|
"""
|
|
out: list[MathReaderRefusalEvidence] = []
|
|
for row in audit_rows:
|
|
if row.missing_operator is None:
|
|
continue
|
|
sub_type = SUB_TYPE_FOR_OPERATOR[row.missing_operator]
|
|
out.append(from_audit_row(row, sub_type, claim_signature=""))
|
|
return tuple(out)
|
|
|
|
|
|
def audit_problem_to_evidence(
|
|
problem_text: str,
|
|
*,
|
|
case_id: str,
|
|
) -> tuple[MathReaderRefusalEvidence, ...]:
|
|
"""Run reader audit on ``problem_text`` and return mapped evidence rows."""
|
|
_result, rows = audit_problem(problem_text, case_id=case_id)
|
|
return audit_to_evidence(rows)
|
|
|
|
|
|
__all__ = ["audit_problem_to_evidence", "audit_to_evidence"]
|