First composition lift-target built end-to-end: cv-0005 (train_sample 0037) now resolves in the sealed pool as goal - Σprogress = 10 - 3 - 4 = 3. - generate/derivation/goal_residual.py: new R4 production. Reads a GOAL anchor (goal-intent lexeme) + a residual question, subtracts each same-referent progress quantity (progress reduces the residual regardless of world-polarity). Gated by the unchanged self-verification gate. - wrong=0 firewall (test_reads_goal_not_possession): on a gain goal the goal-residual (20-5-6=9) DIVERGES from possession-accumulation (20+5+6=31); the production gives 9 and is all-subtract -> it reads the goal, not the possession. This is the coincidental-correctness trap cv-0005 alone hides (10-3-4 == 10-(3+4)). - pool.py: goal_residual added to pooled_candidates (sealed). Verified: fires on exactly one train_sample case (0037, correct), zero new pool wrong-commits (the 8 are pre-existing, gated off serving by product_bridge). Does NOT move the serving metric: train_sample stays 6/44/0 byte-identical (serving = candidate-graph; product_bridge promotes only pure products, never a subtract chain). The serving promotion gate for goal-residual is the next, separately-gated step (needs the sealed 1,319 verdict). Smoke 73, math 4, derivation/pool/practice 196, corpus, completeness-guard all green.
73 lines
3.2 KiB
Python
73 lines
3.2 KiB
Python
"""ADR-0207 §5 step 2 / R4 — goal-residual production + its wrong=0 firewall.
|
||
|
||
The corpus lift-target cv-0005 (train_sample 0037) must read `goal − Σprogress`.
|
||
The load-bearing test is the **gain-goal divergence** (`test_reads_goal_not_possession`):
|
||
it proves the production reads the goal, not the possession, on a case where the two
|
||
arithmetics differ — the coincidental-correctness trap cv-0005 alone cannot catch.
|
||
"""
|
||
from __future__ import annotations
|
||
|
||
from generate.derivation.goal_residual import build_goal_residual, compose_goal_residual
|
||
|
||
# cv-0005 / train_sample 0037 (loss goal — residual and possession coincide at 3).
|
||
CV0005 = (
|
||
"Michael wants to lose 10 pounds by June. He lost 3 pounds in March and 4 pounds "
|
||
"in April. How much weight does he have to lose in May to meet his goal?"
|
||
)
|
||
# Adversarial gain goal — residual (20-5-6=9) DIVERGES from possession (20+5+6=31).
|
||
# Uses a recognized gain verb ("earned") so the progress cue is licensed.
|
||
SAVE_GOAL = (
|
||
"Maria wants to earn 20 dollars for a gift. She earned 5 dollars in May and 6 dollars "
|
||
"in June. How much more does she need to earn to reach her goal?"
|
||
)
|
||
|
||
|
||
def _answer(text):
|
||
res = compose_goal_residual(text)
|
||
return None if res is None else res.answer
|
||
|
||
|
||
def test_cv0005_goal_residual_solves() -> None:
|
||
"""cv-0005: goal 10 − (3 + 4) = 3, read as a goal (start=10, all-subtract)."""
|
||
deriv = build_goal_residual(CV0005)
|
||
assert deriv is not None
|
||
assert deriv.start.value == 10.0
|
||
assert all(s.op == "subtract" for s in deriv.steps)
|
||
assert _answer(CV0005) == 3.0
|
||
|
||
|
||
def test_reads_goal_not_possession() -> None:
|
||
"""WRONG=0 FIREWALL. On a gain goal, goal-residual (9) diverges from possession-
|
||
accumulation (31). The production must give 9 (reads the goal) — never 31, and the
|
||
chain must be all-subtract (progress reduces the residual regardless of polarity)."""
|
||
deriv = build_goal_residual(SAVE_GOAL)
|
||
assert deriv is not None
|
||
assert _answer(SAVE_GOAL) == 9.0, "must read goal-residual, not possession 31"
|
||
assert all(s.op == "subtract" for s in deriv.steps), "progress always subtracts"
|
||
assert deriv.start.value == 20.0
|
||
|
||
|
||
def test_no_goal_language_does_not_fire() -> None:
|
||
"""A possession case (no goal-intent lexeme) must NOT fire this production —
|
||
it belongs to accumulation, not goal-residual."""
|
||
possession = "Sam has 14 apples. He gives away 3 apples and 2 apples. How many are left?"
|
||
assert build_goal_residual(possession) is None
|
||
|
||
|
||
def test_no_residual_question_does_not_fire() -> None:
|
||
"""Goal language but no residual question → does not fire."""
|
||
no_residual = (
|
||
"Michael wants to lose 10 pounds by June. He lost 3 pounds in March and 4 pounds "
|
||
"in April. How much weight did he lose in total?"
|
||
)
|
||
assert build_goal_residual(no_residual) is None
|
||
|
||
|
||
def test_incomplete_reading_refuses() -> None:
|
||
"""A progress clause with a new named actor (referent hazard) must refuse — the
|
||
same-referent guard is inherited, never weakened."""
|
||
cross_referent = (
|
||
"Michael wants to lose 10 pounds by June. Sarah lost 3 pounds in March. "
|
||
"How much more does Michael need to lose to meet his goal?"
|
||
)
|
||
assert build_goal_residual(cross_referent) is None
|