core/docs/briefs/parallel-2026-05-23/L4-opus2-binding-graph-phase2-adapter.md
Shay 1a288ad017
docs(ADR-0136): statement-layer corridor, S.0 taxonomy, 2026-05-23 brief archive (#200)
* 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)
2026-05-23 20:25:23 -07:00

5 KiB
Raw Blame History

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):

  1. docs/decisions/ADR-0132-binding-graph-data-model.md — your Phase 1 ADR; the data model is the codomain of the adapter.
  2. 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.pybind_math_problem_graph(g: MathProblemGraph) -> SemanticSymbolicBindingGraph. Pure function. Deterministic: byte-equal input graph → byte-equal output binding graph (assert via canonical_bytes-style round-trip in tests).
  • Mapping discipline (lock these as constants at the top of adapter.py):
    • Each entity → one SymbolBinding with semantic_role="entity", symbol_id = entity_<slug>.
    • Each InitialPossession → one SymbolBinding with semantic_role="quantity" and a BoundFact (q_<entity>_<unit>_t0 = value [unit]).
    • Each Operation → at minimum one BoundEquation capturing the operand semantics, with dependencies referencing the source-state symbols. operation_kind mirrors the MathProblemGraph op kind verbatim (string passthrough — the closed vocab is shared by design).
    • Unknown → one BoundUnknown referencing the bound symbol whose value the solver must determine.
  • Public surface: add bind_math_problem_graph to generate/binding_graph/__init__.py.
  • tests/test_binding_graph_adapter.py — 3040 tests covering: every operation kind round-trips, entity/quantity mapping is deterministic across orderings (introduction-order preserved), unknown-binding is single-target, source_span propagation when the MathProblemGraph carries span info (skip span fields cleanly when absent), refusal-first on malformed input (AdapterError typed exception, sibling of BindingGraphError), 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 (MathProblemGraph origin). 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_KINDS set is authoritative; BoundEquation.operation_kind accepts 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 MathProblemGraph instances that hash-equal must produce two SemanticSymbolicBindingGraph instances that hash-equal.
  • Unit-aware equation binding is Phase 3, not Phase 2. In Phase 2 it is acceptable for BoundEquation to carry unit_proof = None or 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.py itself.
  • 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 35 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.