* docs(ADR-0136): statement-layer corridor + S.0 taxonomy + resolve math-file conflicts - Add ADR-0136-statement-layer-corridor.md: corridor overview, S.0-S.4 phase table, taxonomy summary (23 context-filler, 4 rate-class, 5 compound, 17 long-tail), standing invariants (admitted_wrong==0, context-filler safety rail, honest delta). - Restore generate/math_*.py to HEAD (main) — stash-pop conflict markers were cosmetic comment rewording; upstream version is authoritative. - Stage docs/reviewers.yaml math_expert_claims addition (already authored). * docs(briefs): archive all 2026-05-23 parallel-dispatch briefs (L1–L17 + README)
5 KiB
L4 brief — Opus#2 — Binding Graph Phase 2 (adapter from MathProblemGraph)
Worktree setup (do this first, non-negotiable):
git worktree add ../core-binding-graph-p2 -b feat/binding-graph-phase2-adapter origin/main
cd ../core-binding-graph-p2
Scope. Phase 2 of the binding-graph layer (ADR-0132 ratified Phase 1; #171 merged). Add a pure-function adapter that converts an existing generate.math_problem_graph.MathProblemGraph into a SemanticSymbolicBindingGraph. Still no runtime wiring — Phase 2 is structural translation only. Phases 3 (unit-aware equation binding), 4 (question-target binding), and 5 (bounded-grammar integration / B3) remain deferred to follow-up PRs.
Reference docs (read these, only these):
docs/decisions/ADR-0132-binding-graph-data-model.md— your Phase 1 ADR; the data model is the codomain of the adapter.generate/math_problem_graph.py— the input type. Note its determinism guarantees (frozen+slots, order-of-introduction tuples,canonical_bytes). The adapter must preserve those.
What to ship:
generate/binding_graph/adapter.py—bind_math_problem_graph(g: MathProblemGraph) -> SemanticSymbolicBindingGraph. Pure function. Deterministic: byte-equal input graph → byte-equal output binding graph (assert viacanonical_bytes-style round-trip in tests).- Mapping discipline (lock these as constants at the top of
adapter.py):- Each entity → one
SymbolBindingwithsemantic_role="entity",symbol_id = entity_<slug>. - Each
InitialPossession→ oneSymbolBindingwithsemantic_role="quantity"and aBoundFact(q_<entity>_<unit>_t0 = value [unit]). - Each
Operation→ at minimum oneBoundEquationcapturing the operand semantics, withdependenciesreferencing the source-state symbols.operation_kindmirrors theMathProblemGraphop kind verbatim (string passthrough — the closed vocab is shared by design). Unknown→ oneBoundUnknownreferencing the bound symbol whose value the solver must determine.
- Each entity → one
- Public surface: add
bind_math_problem_graphtogenerate/binding_graph/__init__.py. tests/test_binding_graph_adapter.py— 30–40 tests covering: every operation kind round-trips, entity/quantity mapping is deterministic across orderings (introduction-order preserved), unknown-binding is single-target,source_spanpropagation when theMathProblemGraphcarries span info (skip span fields cleanly when absent), refusal-first on malformed input (AdapterErrortyped exception, sibling ofBindingGraphError), and a hash-stability test (bind(g) == bind(g)byte-equal across runs).docs/decisions/ADR-0133-binding-graph-adapter.md— short ADR ratifying Phase 2; cite ADR-0132 parent and ADR-0115 (MathProblemGraphorigin). Explicit "Phase 3+ deferred" section.
Hard constraints:
- Pure function, pure data. No I/O, no parser calls, no solver calls, no
numpy. The adapter is a deterministic translation. - No mutation. Output is a new
SemanticSymbolicBindingGraph; input is not touched (it can't be — frozen — but assert the invariant in tests anyway). - Operation-kind passthrough. Do not invent a new closed vocab. The
MathProblemGraph.VALID_OPERATION_KINDSset is authoritative;BoundEquation.operation_kindaccepts the same eight strings. If you find yourself wanting to translate or rename, stop and refuse. - Determinism is load-bearing. The tuples in the output BindingGraph must reflect introduction order from the input. Two
MathProblemGraphinstances that hash-equal must produce twoSemanticSymbolicBindingGraphinstances that hash-equal. - Unit-aware equation binding is Phase 3, not Phase 2. In Phase 2 it is acceptable for
BoundEquationto carryunit_proof = Noneor a placeholder; do not attempt dimensional analysis. (Confirm in the ADR that this gap is by design and the next ADR closes it.) - Field invariant untouched. Don't go near
versor_condition,algebra/, or anything in the runtime hot path.
Out of scope (do not touch — Phase 3+):
- Unit-aware equation admissibility checks (Phase 3 / ADR-0134).
- Question-target binding refinement (Phase 4).
- Bounded-grammar / B3 integration (Phase 5).
- Any change to
chat/,core/,generate/intent.py,generate/realizer.py,generate/math_problem_graph.pyitself. - B1.B (PR #169 — separate lane).
- B2 teaching-corpus eval (L1 — Gemini's lane).
- B1 sealed holdout (L3 — undispatched).
Target branch. PR against main. Title: feat(binding-graph): Phase 2 adapter from MathProblemGraph (ADR-0133). Body must reference #170 (proposal), #171 (Phase 1), and explicitly list Phase 3–5 as deferred.
Exit criterion. PR opens with CI green, all new tests pass, pyright clean on new files, ADR-0133 included. Runtime behavior byte-identical to main (no integration yet, by design).
Do not stack on another agent's branch. Target main directly. If main does not yet contain ADR-0132 + Phase 1, stop and flag — Phase 2 requires that foundation.