Commit graph

182 commits

Author SHA1 Message Date
Shay
9b45e23973
feat(ADR-0114a.8): adversarial auditor — Obligation #8 wired, PASSING; surfaces 2 known parser-layer gaps (#192)
External auditor for ADR-0114a Obligation #8:
"adversarial/score.py reports wrong == 0 across all families;
>= 30 cases x >= 8 families."

Verdict on current main:
  cases_total:         36
  families_total:      9
  cases_refused:       28
  cases_solved:        8
  cases_wrong:         0  <-- the gate
  obligation_8_passed: True

New module core/capability/adversarial.py mirrors PR #189/#190/#191
auditor pattern. Pure function over the committed cases set; broad
exception capture (correctly classified as refused — engine
couldn't process the input) makes the auditor robust to upstream
typed-refusal gaps.

New dataset evals/obligation_8_adversarial/v1/cases.jsonl — 36
cases x 9 families, closed taxonomy:
  - paraphrase (verb outside initial-anchor whitelist)
  - unrecognized_unit (not in en_units_v1)
  - conditional (if/would/suppose)
  - pronoun_coref (cross-sentence he/she/they)
  - hedged_quantity (about/almost/approximately)
  - ordinal_confusion (the 5th/third in cardinal position)
  - implicit_subject (no named entity)
  - self_reference (actor as comparison ref or transfer target)
  - distractor_noise (adjectival/temporal/irrelevant siblings)

CLI: core capability adversarial. Writes
evals/obligation_8_adversarial/<lane_id>.json. Exit 0 iff
obligation passes.

Honest disclosure — 8 of 36 cases solved rather than refused;
none produced wrong answers. Two parser-layer gaps surfaced:

  Gap A (pronoun_coref, 4/4 solved): unbound sibling sentences
  silently drop; engine returns last-asserted state. Faithful but
  semantically poor. Reserved follow-up: tighten admissibility so
  unbound sentences refuse the whole case.

  Gap B (unrecognized_unit, 4/4 solved): _canonicalize_unit
  falls back to '+s' plural rule when pack doesn't recognize
  the unit. Reserved follow-up: opt-in strict mode behind a flag
  (some B3 units aren't in en_units_v1 either; strict mode
  requires parallel pack extension).

  Bug caught: adv-self-reference-003 ("Sam gives 3 apples to
  Sam.") raises uncaught MathGraphError from
  Operation.__post_init__. Auditor catches it as
  refused-via-exception; ~3-line follow-up in
  _build_op_candidate fixes the parser side.

Trust boundary: read-only access to cases + transitive pack reads;
single deterministic write to artifact path.

Tests: 11/11 in tests/test_adr_0114a_8_adversarial.py covering
threshold pinning (>= 30 cases / >= 8 families), closed taxonomy
(every documented family has cases; no unknown families),
obligation-passes snapshot, per-family wrong=0 invariant, failure
modes (missing file, below-threshold count), determinism (report
identical + artifact byte-equal).
2026-05-23 16:11:37 -07:00
Shay
29111b7762
feat(ADR-0114a.5): reasoning-isolation perturbation suite — Obligation #5 wired for B3, PASSING 130/130 preserving, 68/68 breaking (#191)
Discharges ADR-0114a Obligation #5 for the B3 bounded-grammar lane.

Closed perturbation taxonomy (5 invariance-preserving, 3 invariance-breaking
transforms) operates on problem text only; parser, solver, and cases.jsonl
are untouched. Both rates are ε=0 per ADR-0120 §"Threshold rationale".

Results on main B3 (35 solved_correct cases):
  invariance_preserving: 130/130 = 1.0000
  invariance_breaking:    68/68  = 1.0000
  obligation_5_passed: True

Skipped transforms documented explicitly (not silently absent):
  commutative_reorder: all 35 — no single-entity multi-unit init state
  op_verb_flip:        15 — multiply/divide/compare/transfer cases
  value_replacement_op: 15 — no distinct numeric operand
  unit_synonym:         7 — rate-declaration $ syntax cases
  value_replacement_init: 7 — value cancels or not found
  entity_rename_v{1,2,3}: 1 each — b3-013 "Birds" collective is self-mapping

Ships:
  core/capability/perturbation_b3.py — generator + scorer + validate_perturbation_suite()
  tests/test_adr_0114a_5_perturbation.py — 15 tests (purity, preserving, breaking, determinism, snapshot, refusal, skip coverage)
  core/cli.py — core capability perturbation [--lane-id] [--json]
  evals/obligation_5_perturbation/B3_bounded_grammar.json — written by CLI
  docs/decisions/ADR-0114a.5-perturbation-suite.md — ADR with taxonomy tables
2026-05-23 16:07:59 -07:00
Shay
272c1e723a feat(ADR-0114a.10): pack-provenance auditor — Obligation #10 wired for B3, PASSING
Implements the external auditor ADR-0114a Obligation #10 requires:
"Every SolutionTrace.steps[*].pack_lemma_id resolves to a real
lexicon entry in the domain's operator pack." The solver enforces
this at solve time; this PR audits it from outside.

New module core/capability/pack_provenance.py:
  - _load_lexicon_lemmas(): independent re-read of pack lexicon
  - _parse_lemma_id(): <pack_id>:<lemma> shape parser
  - validate_lane(): re-runs candidate-graph pipeline on a B-lane's
    cases, walks every solver step, validates pack_lemma_id parses
    AND resolves to a lexicon entry. Per-case + per-lane verdict.
  - emit_provenance_report(): deterministic artifact emission.

CLI: core capability pack-provenance (added to core/cli.py).
Writes evals/obligation_10_pack_provenance/<lane_id>.json.

Empirical verdict on current main (post-PR #186):
  lane:                       B3_bounded_grammar
  cases_total:                50
  cases_validated:            25  (every expected-correct B3 case)
  cases_skipped_unsolved:     25  (refusal-expected probes — by design)
  cases_violated:             0
  obligation_10_passed:       True

5 distinct lemma_ids observed (add, subtract, transfer,
compare_additive, compare_multiplicative) — all resolve to
en_arithmetic_v1. The other 3 op kinds (multiply, divide,
apply_rate) ratify-at-solve-time via _resolve_pack_lemmas so the
obligation holds for them too if a future case exercises them.

Honest scope-limit: B3 only. B1 (symbolic equivalence) and B2
(teaching corpus) equivalents deferred to separate sub-ADRs —
B1 needs reframing (algebra normalization chain, not arithmetic
steps); B2 can use this same auditor signature once corpus
solver-trace exercise is confirmed case-by-case.

Composition with ADR-0131.4: orthogonal. Composite gate verdict
+ obligation #10 verdict + 4 other obligation auditors (when
they land) + reviewer signature → full ADR-0120 wire-up.

Trust boundary: read-only access to pack lexicon + B3 cases;
single deterministic write to artifact path. No dynamic imports,
no shell passthrough, no network. Pure deterministic auditor.

Tests: 19/19 in tests/test_adr_0114a_10_pack_provenance.py
covering lemma-id parser (well-formed + malformed), lexicon loader
(real pack + every failure mode), lane validator (passes on real
B3 + refuses on missing pack/cases + skips refusal-expected cases
without false violation), determinism (report identical across
calls + artifact byte-equal).
2026-05-23 15:44:53 -07:00
Shay
c996e39c98
Merge pull request #188 from AssetOverflow/feat/adr-0131-4-promotion
feat(ADR-0131.4): composite math-expert gate — PASSING on first evaluation (B1+B2+B3 all green, wrong==0)
2026-05-23 15:41:43 -07:00
Shay
d66e8ad625 feat(G1): verb-classes capability axis (ADR-0131.G.1)
Cognitive capability: extend bounded grammar to admit acquisition/action
verbs (buys, bought, collected, saved, saved-up, makes, sells) as
operation-kind entries, and pure-possession verbs (had, started, started-with)
as initial-possession anchors.

What invariant proves correctness:
- wrong == 0 across all G1 curated cases (20/20) and GSM8K probe (0 wrong/50).
- versor_condition and field invariants untouched — no algebra-path changes.
- Round-trip filter (math_roundtrip.roundtrip_admissible) unchanged.

Which CLI suite / eval proves the lane:
  pytest tests/test_adr_0131_G1_verb_classes.py — 15/15 pass
  pytest tests/test_adr_0126_runner_wiring.py — 9/9 pass (3 regressions fixed)
  pytest tests/test_adr_0131_{1,3}_*lane.py — 17/17 pass
  pytest tests/test_adr_0131_G_gsm8k_coverage_probe.py — 8/8 pass
  pytest tests/test_gsm8k_math_runner.py — 11/11 pass

Key architectural change:
  Acquisition verbs that also appear in ADD_VERBS/SUBTRACT_VERBS were
  previously listed in _INITIAL_HAS_RE, causing branch-disagreement refusals
  when a canonical 'has' initial preceded an acquisition sentence for the
  same entity.  Fix: narrow _INITIAL_HAS_RE to pure-possession anchors only
  (has/have/had/started); acquisition verbs remain exclusively in KIND_TO_VERBS.
  The solver's default-from-zero means 'Sam buys 5 apples. How many does
  Sam have?' resolves as 0+5=5 without any initial-possession candidate.
  Optional verb particle (up/down/out/...) added to _op_pattern to handle
  'saved up N', 'picked up N' etc.

No changes to binding graph, solver, verifier, or versor/CGA algebra.
No stochastic generation, approximate recall, or hidden normalization.
Trust boundaries unaffected — no new dynamic imports or user-input paths.
2026-05-23 15:39:14 -07:00
Shay
4b59f3daf7 feat(ADR-0131.4): composite math-expert promotion gate — wired, evaluated, PASSING
Implements ADR-0131's revision of the ADR-0120 expert-promotion
contract for mathematics_logic: replaces the single-benchmark
GSM8K-coverage check with a composite B1+B2+B3 requirement.

New module core/capability/composite_math_gate.py:
  - evaluate_composite_math_gate(): pure function over already-
    committed B-lane reports; handles heterogeneous report shapes
    (B1/B2 counts vs B3 metrics); applies pinned thresholds
    (correct_rate >= 0.95 AND wrong == 0); composes verdicts.
  - Reproducible SHA-256 claim_digest over canonical evidence bundle.
  - GSM8K honest-disclosure (admission/wrong/refused/substrate)
    embedded in artifact but never gates per ADR-0131.

CLI: core capability math-expert-gate (added to core/cli.py).
Writes evals/math_expert_claims/v1/expert_claims_math_v1.json.

Empirical verdict on current main (post-PR #182/#183/#184/#185):
  composite_gate_passed: True
  B1_public:          185/185 wrong=0 rate=1.0000
  B1_sealed:           14/14  wrong=0 rate=1.0000
  B2_teaching_corpus:  40/40  wrong=0 rate=1.0000
  B3_bounded_grammar:  50/50  wrong=0 rate=1.0000
  GSM8K disclosure:    0/50 admission, wrong=0, substrate=candidate_graph

The math expert is gate-passing under ADR-0131's revised composite
contract. The architectural bet ADR-0131 placed has paid off.

Honest scope-limit: this implements only the ADR-0131-specific
revision (composite benchmark portion). The full ADR-0120 10-
obligation contract still requires substrate for 5 missing
obligations (OOD ratio, perturbation, depth curve, adversarial,
operation-provenance-via-pack). Those are sequencing-wise *after*
ADR-0131.4, not bundled. Reviewer signature via ADR-0092 registry
is also reserved.

Trust boundary: read-only access to 5 committed lane reports;
single deterministic write to the artifact path. No dynamic
imports, no recomputation of lane verdicts.

Tests: 12/12 in tests/test_adr_0131_4_composite_math_gate.py
covering threshold pinning, heterogeneous shape handling, gate
logic (passing + every failure mode), GSM8K honest disclosure
(never gates), determinism (claim_digest + artifact byte-equality),
and a snapshot test confirming current main satisfies the gate.

ADR-0131.4 module note: the parent ADR-0131 plan named
formation/ratify.py + formation/promote.py as the wire-up site —
that was a misidentification (those govern teaching-example
SPECULATIVE→COHERENT bridging per ADR-0021, not domain-tier
promotion). Correct site is core/capability/, where audit-passed
gate already lives.
2026-05-23 15:23:14 -07:00
Shay
5853b189b2 feat(ADR-0131.G.3.1): numerics extensions — fractions + multi-currency + multi-token cardinals + word-num-adjective
Four axes deferred from ADR-0131.G.3 (PR #183):

1. Fractions end-to-end: new _INITIAL_FRACTION_OF_RE extractor handles
   `N/M of [a/an] <unit>` shape; _resolve_value already handles N/M arithmetic.

2. Multi-currency: _MONEY_SYMBOL widened to six symbols; _CURRENCY_SYMBOLS table
   + _resolve_currency dispatcher; ¢/€/¥/₱ wired end-to-end. £/pound sterling
   deferred to G.3.2 (question extractor's single-token unit slot cannot parse
   two-word surface "pounds sterling").

3. Multi-token cardinals: dedicated _MULTI_WORD_CARDINAL_RE extractor (approach a)
   delegates to parse_compound_cardinal; avoids greedy unit-slot boundary ambiguity
   from widening _VALUE.

4. Word-num-adjective: optional adjective group added to _INITIAL_HAS_RE and
   _MULTI_WORD_CARDINAL_RE; closed adjective list identical to _CONJ_OBJECT_RE.

Also fixes six pre-existing G4 type bugs where _resolve_value() result was used
directly as a numeric operand (TypeError: _ResolvedValue is not a number).

Axis lane v1_1: 20/20 solved_correct, 0 wrong, 8/8 refusals, overall_pass=True.
GSM8K probe: 0/50 admission_rate unchanged, admitted_wrong=0 (safety rail intact).
42/42 new tests pass; parent v1 lane (26/26) unaffected.
2026-05-23 15:16:46 -07:00
Shay
8187f3f385
Merge pull request #185 from AssetOverflow/feat/adr-0131-g4-multi-clause
feat(ADR-0131.G.4): multi-clause composition — admission 0/50 (Δ0), multi-clause refusals 2→1
2026-05-23 14:50:15 -07:00
Shay
34e9546e16
Merge pull request #183 from AssetOverflow/feat/adr-0131-g3-numerics
feat(ADR-0131.G.3): numeric literals (money + hyphenated cardinals) — axis lane 20/20, wrong==0
2026-05-23 14:49:42 -07:00
Shay
f55dc36e6f
Merge pull request #182 from AssetOverflow/feat/adr-0131-g2-comparatives
feat(ADR-0131.G.2): comparative operations (additive + multiplicative) — admission 0/50 (Δ0), comparative-clause refusals 2→1
2026-05-23 14:48:35 -07:00
Shay
de26d7f792 feat(ADR-0131.G.4): multi-clause composition (conj subjects + conj objects + embedded quantifiers + conj embedded) — admission 0/50 (Δ0), multi-clause refusals 2→1
Highest-risk axis of the ADR-0131.G capability iteration: within-
sentence multi-clause composition. Four extractors land in the
candidate-emitting parser; no graph-side or solver changes.

Parser extension (generate/math_candidate_parser.py)
- _conj_subject_each_candidates: '<A> and [his/her/their <kin>] <B>
  each <verb> <N> <unit>' → 2 CandidateInitial (one per actor).
- _conj_object_candidates: '<E> has <N1> <unit1> and <N2> <unit2>' →
  2 CandidateInitial for the same entity; same-unit conjuncts refuse
  (would silently collide under solver overwrite-on-collision).
- _embedded_quantifier_candidates: '<E> has <N> <container> with <M>
  <unit> in each [<container>]' → 1 derived CandidateInitial
  (value=N*M).
- _embedded_quantifier_candidates (conj branch): '... <N1> <C> with
  <M1> <U> in each ... and <N2> <C> with <M2> <U> in each ...' → 1
  SUM CandidateInitial (value=N1*M1+N2*M2); mixed-unit refuses.
- CandidateInitial anchor whitelist widened to include
  saved/earned/got/received/bought/made/paid (and inflections) —
  narrow widening needed for the conjoined-subject-each shape.

Closed-set discipline
- Distributive 'each' only — 'each ... together/altogether' refuses.
- Two-way conjunction only — 3-way refuses by non-match.
- Cross-sentence coreference stays refused (within-sentence axis).
- Ambiguous 'each' scope refuses (container2 must agree).

Curated axis lane (32 cases)
- evals/math_capability_axes/G4_multi_clause/v1/cases.jsonl:
  conj_subject_each ×6, conj_object ×6, embedded_quantifier ×6,
  conj_embedded ×6, refusal ×8.
- evals/math_capability_axes/G4_multi_clause/v1/runner.py +
  report.json: deterministic; wrong==0 gate; byte-equal across runs.

Tests (26 new)
- tests/test_adr_0131_G4_multi_clause.py: per-shape emission,
  refusal probes (parametric), distributive-only policy,
  cross-sentence refusal, runner byte-equality, GSM8K-probe gate.

GSM8K-probe gate (chosen: multi-clause refusals ↓)
- evals/gsm8k_math/train_sample/v1/report.json (candidate-graph
  probe): multi-clause statement-refusal count 2 → 1. Case 0042
  ('Ella has 4 bags with 20 apples in each bag and six bags with 25
  apples in each bag.') moves from statement-clause refusal to
  question-layer refusal. Case 0026 ('Aaron and his brother Carson
  each saved up $40') stays refused on the '$' value slot
  (deferred to G.3 numeric-literals axis).
- evals/gsm8k_math/train_sample/v1/train_sample_coverage_report.json
  (legacy probe): refreshed, byte-identical (legacy parser
  untouched).

B3 + candidate-graph + GSM8K probe lanes all pass (95/95
regression). wrong==0 preserved everywhere — load-bearing for the
highest-risk axis.
2026-05-23 14:43:16 -07:00
Shay
801287bba6 feat(ADR-0131.G.0): switch GSM8K coverage probe to candidate-graph pipeline
Zero behavior delta on the main baseline (both substrates produce
0/50 admission today) — but every subsequent ADR-0131.G.<n> iteration
now produces attributable admission deltas on the probe, instead of
silently extending a parser layer the probe wasn't measuring.

Background: ADR-0131.G's probe consulted run_lane → _score_one →
parse_problem (legacy first-match-wins parser, pre-ADR-0126). Every
G.<n> iteration extends the candidate-graph parser via
_score_one_candidate_graph → parse_and_solve. The mismatch was
discovered during G.3 development and explicitly reserved as this
follow-up.

Changes:
  - run_coverage_probe.py: switch import to _score_one_candidate_graph;
    new private _score_lane aggregator mirrors run_lane's output shape
    via per-case scoring; report root adds "substrate": "candidate_graph"
    for audit trail.
  - train_sample_coverage_report.json: regenerated. All metrics
    byte-identical to prior baseline (0/50 admission, wrong=0).
    refused_reasons_top text differs (candidate_graph: prefix instead
    of parser:) — expected and part of the substrate audit-trail shift.

Discipline: separate small PR per ADR-0131.G's "expansion that only
moves admission must be a standalone PR" principle. Substrate swap
attributable; future G.<n> deltas attributable.

Evidence:
  - python3 -m evals.gsm8k_math.train_sample.v1.run_coverage_probe
    → admission 0/50, wrong=0, safety_rail_intact=True, exit 0
  - pytest tests/test_adr_0131_G_gsm8k_coverage_probe.py
    → 8/8 pass in 0.18s (no test edits needed; tests pin invariants
    not numbers)
  - No changes to runner.py, no changes to any G.<n> work in flight.

Effect on in-flight iterations: each G.<n> PR (G.1 Gemini / G.2 #182 /
G.3 #183 / G.4 Opus#2) rebases after this lands and refreshes its
committed train_sample_coverage_report.json with the new substrate's
numbers. Rebase is mechanical.
2026-05-23 14:43:05 -07:00
Shay
3011fce268 feat(ADR-0131.G.3): numeric literals — money + hyphenated cardinals (axis lane 20/20, wrong==0)
First capability-axis iteration after ADR-0131.G baseline. Extends the
candidate-graph parser's <value> slot to recognize:

  - Money symbol literals: $N and $N.NN (1-2 decimals); $N.NNN refused
  - Money word forms: N dollars / N cents
  - Hyphenated multi-word cardinals: twenty-five, ninety-nine, ...

All money values normalize to integer cents, unit 'cents' — pack-aligned
with en_units_v1's canonical_unit='cent' for the money dimension.
en_numerics_v1's parse_compound_cardinal handles hyphenated cardinals.

Parser changes (generate/):
  - math_candidate_parser.py: _VALUE alternation widened; _resolve_value
    refactored to return _ResolvedValue|None carrying optional unit
    override; _INITIAL_HAS_RE unit slot made optional; dollar/dollars →
    cents normalization at candidate build.
  - math_roundtrip.py: new _unit_grounds helper (money-aware); _value_grounds
    widened for the three new literal shapes; roundtrip_admissible uses
    _unit_grounds for the unit check.
  - math_candidate_graph.py: _initial_admissible and _question_admissible
    use _unit_grounds.

New axis lane (evals/math_capability_axes/G3_numerics/v1/):
  - 26 curated cases (20 positive across 4 classes + 6 refusal probes)
  - runner.py wraps _score_one_candidate_graph; byte-equal report.json
  - 20/20 positive solved correct; 6/6 refusal probes refused typed;
    solved_wrong == 0; overall_pass == True

Tests: 27/27 in 0.19s. 420 existing candidate-parser/math-parser/pack
tests still green. GSM8K probe safety rail (admitted_wrong == 0)
preserved.

Honest scope-limit (documented in ADR): admission_rate on the GSM8K
probe stays at 0/50 because (a) the probe currently consults the legacy
parser path, not the candidate-graph pipeline G.3 extends, and (b) most
money-bearing GSM8K cases fail first on verb (G.1) or multi-clause (G.4)
shape, not on the money literal. The axis lane is the load-bearing
measurement for this iteration. Reserved follow-up: a small probe-
infra ADR to switch run_coverage_probe.py to the candidate-graph
pipeline.

Out of scope, deferred to G.3.1: fractions end-to-end (resolver supports
N/M but no axis cases), multi-currency (¢ € £ ¥ ₱), space-separated
multi-word cardinals (one hundred), word-number-adjective compositions
(five full boxes).
2026-05-23 14:23:05 -07:00
Shay
b891eb243c feat(ADR-0131.G.2): comparative operations (additive + multiplicative) — admission unchanged, comparative-clause refusals 2→1
Wire compare_additive / compare_multiplicative extractors into the
candidate-emitting sentence parser, closing the deferred phase flagged
at generate/math_candidate_parser.py:30.

Capability axis: comparatives (additive + multiplicative)
- generate/math_candidate_parser.py: new _compare_additive_candidates,
  _compare_multiplicative_candidates, _compare_nested_candidates
  emitting CandidateOperation records keyed to the four
  Comparison.direction literals registered in ADR-0123.
- Closed-set anchor alternation; 'less' admitted as surface synonym of
  'fewer'; reference slot widened to admit "the number/amount of <unit>"
  for nested forms.
- Nested 'A has N more <unit> than M times <REF>' emits two flat
  candidates (additive + multiplicative); binding-graph picks the
  admissible composition or refuses (no solver stub).

Curated axis lane (24 cases)
- evals/math_capability_axes/G2_comparatives/v1/cases.jsonl:
  8 additive / 8 multiplicative / 3 nested / 5 refusal
- evals/math_capability_axes/G2_comparatives/v1/runner.py +
  report.json: deterministic, wrong==0 gate, byte-equal across runs.

Tests (21 new)
- tests/test_adr_0131_G2_comparatives.py: per-direction at-least-one
  passing, nested-both-emitted, closed-set refusal, runner
  byte-equality, GSM8K-probe gate (comparative-clause refusals
  strictly decrease).

GSM8K-probe gate (chosen: comparative-clause refusals ↓)
- evals/gsm8k_math/train_sample/v1/report.json (candidate-graph
  probe): comparative-clause refusal count 2 → 1 (case 0009 'Jen has
  10 more ducks than four times the number of chickens' moves from
  statement-clause refusal to question-layer refusal). admitted_wrong
  remains 0; admission_rate unchanged (downstream composition is a
  follow-up ADR).
- evals/gsm8k_math/train_sample/v1/train_sample_coverage_report.json
  (legacy probe): refreshed, byte-identical (legacy parser untouched).

B3 + candidate-graph + GSM8K probe lanes all pass (90/90). Direction
vocab stays closed to {more, fewer, times, fraction}; wrong==0
preserved everywhere.
2026-05-23 14:15:25 -07:00
Shay
23c126ebe0 feat(ADR-0131.G): GSM8K coverage probe — honest baseline + capability-first iteration discipline
ADR-0131 deferred GSM8K because it rewards paraphrase flexibility,
which is the deterministic engine's structural weakness. This ADR
re-engages it on architecture-aligned terms: as a *coverage probe*
of the bounded grammar + binding graph, not a promotion gate.

The framing pinned by this ADR:

  GSM8K is not a target. The model's capability is the target.
  GSM8K passing is the symptom of capability, not the goal of
  the work.

Wrong mindset (rejected by ADR's iteration discipline):
  "Find templates that admit more GSM8K cases."

Right mindset (load-bearing):
  "Extend the model's NL-to-typed-graph capability along
  principled axes (verb classes, comparative structures, numeric
  forms, multi-clause grammar). GSM8K admission rises as a
  side effect alongside every other word-problem corpus."

Baseline pinned by this commit:

  admission_rate: 0/50 = 0.0%
  admitted_wrong: 0  (gate intact, safety rail bulletproof)
  refused:        50/50 = 100.0%

Every refusal is a typed parser error citing the specific clause
that did not match a template. Zero crashes, zero confabulations
— refusal-first works perfectly at admission rate zero.

What's in this PR:

- ``docs/decisions/ADR-0131.G-gsm8k-coverage-probe.md``: the ADR.
  Cites parents (ADR-0131, -0115/-0116/-0117, -0131.3, -0132..-0135).
  Documents the capability-first iteration discipline that every
  subsequent ADR-0131.G.<n> must follow:
    1. Name a single capability axis the iteration extends
    2. Add B3-style curated coverage cases (capability proves
       itself OUTSIDE GSM8K)
    3. Re-run both B3 lane + GSM8K probe; B3 must not regress
    4. Reject any expansion that only moves GSM8K admission

- ``evals/gsm8k_math/train_sample/v1/run_coverage_probe.py``:
  pure-adapter wrapper around the existing run_lane. Emits a
  deterministic train_sample_coverage_report.json with metrics,
  per-case outcomes, and the top refused-reason families (the
  work queue for capability extension).

- ``evals/gsm8k_math/train_sample/v1/train_sample_coverage_report.json``:
  the baseline report. Diff-able artifact every future iteration
  moves.

- ``tests/test_adr_0131_G_gsm8k_coverage_probe.py``: 8 contract
  tests pinning the safety rail (admitted_wrong == 0), typed
  refusal invariant (every refused case has non-empty reason),
  closed outcome vocabulary, deterministic replay, committed-
  report matches fresh-run.

The promotion-gate composite (B1 + B2 + B3) is unaffected.
ADR-0131.4 still consumes those three. The GSM8K probe is
empirical context for honest external claims, not a gate.
2026-05-23 13:17:04 -07:00
Shay
24f6a596fe
feat(ADR-0131.1.F): frontier-baseline comparison harness for B1 (#178)
* feat(ADR-0131.1.F): frontier-baseline comparison harness for B1

Adapts the ADR-0119.4 methodology (frozen citations + comparison JSON
with disclaimer) to B1, with three additions for the
architecture-aligned claim:

1. A provider-agnostic live head-to-head runner. Adapters for
   Anthropic / OpenAI / Google import their SDKs lazily so the
   package loads cleanly without them installed. Each provider has a
   documented FRONTIER_<VENDOR>_KEY env var; the runner refuses with
   a typed FrontierRunError when keys are absent and the cache cannot
   cover all cases. Every response is cached one-record-per-line at
   responses/<provider>/<model>.jsonl so subsequent runs replay
   byte-equally without re-calling the API.

2. A conservative free-text-to-closed-vocab verdict parser. Ambiguous
   or sentinel-free provider replies collapse to "refused" — a
   polarized verdict is never confabulated from prose. Chain-of-
   thought replies use last-token-wins (provider deliberates, then
   concludes). This is the load-bearing seam that prevents the
   runner from manufacturing scores the provider didn't deliver.

3. Architecture-aligned comparison metrics. accuracy is reported but
   foregrounded as the least-load-bearing; refusal_correctness
   (CORE 100% by lane-gate construction vs. frontier confabulation
   rate) and determinism (CORE byte-equal vs. frontier variance) are
   the differentiators.

Frozen adjacent-benchmark citations cover Anthropic
(claude-3-5-sonnet on MATH, claude-opus-4-1 on AIME), OpenAI
(gpt-4o on MATH), and Google (gemini-1.5-pro on MATH). The scope
disclaimer documents that these are adjacent, not head-to-head.
Head-to-head numbers, when run, land in the cache; the comparison
JSON joins them with CORE's existing lane result.

22 tests pin the methodology: citation shape (every field, https
URL, YYYY-MM-DD date), provider-registry shape, verdict-parser
conservatism (multiple chain-of-thought cases), runner caching
behavior (no double-invoke), comparison-JSON determinism (byte-equal
across runs).

No live API call at test time. The harness gates real runs behind
explicit env vars + CLI invocation.

Composes with ADR-0131.1 (B1 v1), ADR-0131.1.B (v1.B hardening,
#169), ADR-0131.1.S (sealed holdout, #173).

* feat(ADR-0131.1.F): live head-to-head — anthropic/claude-sonnet-4-6

First real frontier baseline on the full B1.B 185-case set
(curated + generated). Cached one-record-per-line at
responses/anthropic/claude-sonnet-4-6.jsonl. Re-runs replay from
disk; no further API calls.

Headline (after scoring fix):

  CORE                            185/185 = 100.0% accuracy
                                  3/3     = 100.0% refusal_correctness
                                  deterministic (byte-equal across runs)

  anthropic/claude-sonnet-4-6     182/185 = 98.4%  accuracy
                                  1/3     = 33.3%  refusal_correctness
                                  non-deterministic (temperature=0, but
                                  not byte-equal architecturally)

The 1.6pp accuracy gap is informative; the refusal-correctness gap
is the architecture-aligned story. Sonnet's three misses:

  sym-eq-v1-0016 [difference_of_squares]
    (x^2 + 1)*(x^2 - 1) vs x^4 - 1
    Sonnet: NOT_EQUIVALENT (math error on a textbook identity)

  sym-eq-gen-v1-0153 [generated_refusal_function]
    sin(x) vs x
    Sonnet: NOT_EQUIVALENT (confabulated — should refuse,
                            transcendental outside polynomial scope)

  sym-eq-gen-v1-0154 [generated_refusal_negative_exponent]
    x^-1 vs 1
    Sonnet: NOT_EQUIVALENT (confabulated — should refuse,
                            negative exponent outside scope)

Sonnet correctly refused only on syntactically malformed input
("x +"); on syntactically-valid-but-semantically-out-of-scope inputs
it confidently polarized rather than refusing. CORE refuses both
classes with typed reasons.

Scoring fix: comparison.py now composes curated + generated cases
(mirroring runner.py) so the head-to-head scores the full 185-case
lane, not just the 30 curated. The initial run scored only 30/185
because the generated set was not loaded into _load_cases().

22/22 frontier-methodology tests still pass.

* feat(ADR-0131.1.F): three more head-to-head runs + Ollama adapter

Three additional providers ran against the full B1.B 185-case set,
joining the prior claude-sonnet-4-6 result:

  CORE                           185/185 = 100.0% acc | 3/3 = 100%  refusal | 33 ms
  claude-sonnet-4-6              182/185 =  98.4% acc | 1/3 = 33.3% refusal | 294 s
  claude-opus-4-7                178/185 =  96.2% acc | 1/3 = 33.3% refusal | 309 s
  gpt-5                          134/185 =  72.4% acc | 1/3 = 33.3% refusal | 1153 s
  qwen3:8b (M1 local, partial)    91/91  = 100.0% acc | n/a  no refusal-class | killed

CORE is the only system at 100% on both axes, and runs ~9,000×
faster than the cheapest cloud frontier, ~35,000× faster than gpt-5,
and finishes in less wall time than a single API call to any of the
three frontier models.

Three distinct frontier brittleness modes, all rooted in
"not actually canonicalizing":

  - sonnet-4-6 confabulates polarized verdicts on out-of-scope
    inputs (sin(x), x^-1). Misses one in-scope difference-of-squares
    identity (x^2+1)*(x^2-1) vs x^4-1.
  - opus-4-7 pattern-shortcuts five near-miss-constant cases —
    accepts (-x+3)*(4x+1) == -4x^2+11x+4 (correct constant is 3,
    not 4) without expanding. Same two out-of-scope confabulations
    as sonnet.
  - gpt-5 over-refuses 50 in-scope cases — literally replies
    "REFUSED" to x*(x+1) == x^2+x and (x+1)*(x-1) == x^2-1. Same
    two out-of-scope confabulations as sonnet/opus.

The qwen3:8b partial is the surprise: on the 91 in-scope cases it
completed (spanning the categories where the frontier models failed),
it scored 100%. Refusal-class cases weren't reached before the run
was killed for being impractically slow (~22s/case on M1).

Changes in this commit:

  - frontier_runner.py: anthropic adapter now omits ``temperature``
    for claude-opus-4-x (the parameter is rejected by 4.x models);
    openai adapter switches to ``max_completion_tokens`` for the
    gpt-5 / o-series reasoning models; new ``_ollama_invoke`` that
    posts to localhost:11434 with no third-party dep; per-case
    ``latency_ms`` is now captured on every NEW cached response
    (future runs only — these four runs pre-date the patch).
  - comparison.py: ``_load_cases`` composes curated + generated
    (185 cases) instead of curated only; ``_score_provider``
    surfaces ``latency_summary`` when records carry latency_ms.
  - tests: provider-registry test relaxed to "cloud trio is a
    subset of PROVIDERS"; env-key test allows ``_KEY`` (cloud
    secret) or ``_URL`` (local endpoint).
2026-05-23 12:14:06 -07:00
Shay
22deaf02df
feat(ADR-0131.2.B): B2 teaching-corpus enrichment — load-bearing gate (#177) 2026-05-23 11:29:48 -07:00
Shay
eb5fb33252
feat(ADR-0131.3): bounded-grammar word-problem benchmark — lane PASSED 50/50 (#180) 2026-05-23 11:27:04 -07:00
Shay
3b30eb248a
feat(binding-graph): Phase 4 question-target binding (ADR-0135) (#179)
Refines BoundUnknown from "the symbol whose value the solver determines"
to "the symbol at a specific temporal/state index with a specific
question-form". Two new required fields on BoundUnknown — state_index
(initial/terminal/Operation(operation_index)) and question_form
(count/rate/total/difference/ratio/identity) — populated by the new
pure-function resolver in generate/binding_graph/question_target.py.

The adapter (ADR-0133) now delegates Unknown -> BoundUnknown construction
to bound_unknown_from_math_problem_graph. No runtime wiring, no solver
invocation. Phase 5 (bounded-grammar / B3 integration) remains deferred.

Refusal-first via the new QuestionTargetError (sibling of AdapterError /
AdmissibilityError). Closed reason vocab: not_a_math_problem_graph,
unknown_entity_not_in_entities, apply_rate_unit_mismatch,
unmappable_question_form. Closed precedence rule on question_form
documented in ADR-0135 (compare_multiplicative > compare_additive >
apply_rate{numerator|denominator unit-match} > count); ambiguity refuses.

SemanticSymbolicBindingGraph.__post_init__ gains a cross-collection
guard: Operation(operation_index) must satisfy operation_index <
len(equations). canonical_string emission widened to include
state=... form=... tokens (hash differs from Phase 3 main by design —
not a regression; byte-equal across runs preserved).

Parents: ADR-0132 / ADR-0133 / ADR-0134.

Tests: +70 new (45 unit in test_binding_graph_question_target.py +
25 integration in test_binding_graph_adapter_question_target.py); 5
Phase 1+3 BoundUnknown fixtures migrated. Total binding-graph lane
295/1 pass (1 pre-existing test_symbol_binding_uses_slots failure on
Python 3.14, unrelated to Phase 4 — exists on origin/main). Pyright
clean on new and modified files. No edits to algebra/, chat/, core/,
or runtime hot path. Field invariant untouched.
2026-05-23 11:24:49 -07:00
Shay
6cbaa74076
feat(binding-graph): Phase 3 unit-aware admissibility (ADR-0134) (#176)
Wires deterministic, refusal-first dimensional analysis into the
binding-graph adapter. Every BoundEquation emitted by
bind_math_problem_graph now carries either admissibility_status='admitted'
+ populated unit_proof or admissibility_status='refused' + typed
refusal_reason. No silent coercion; no invented units; no solver.

Adds:
- generate/binding_graph/units.py — pure unit algebra over a 6-dim
  integer exponent vector (length, time, mass, money, count,
  temperature). Closed vocabulary loaded once from en_units_v1
  (ADR-0127) and memoized; composite "<num>_per_<denom>" resolved
  recursively; conservative depluralization; refusal-first.
- generate/binding_graph/admissibility.py — check_admissibility with
  per-operation-kind dispatch over the closed 8-string vocab, typed
  AdmissibilityError (closed reason set), frozen UnitProof.
- ADR-0134 documenting the contract, invariants, and Phase 4-5
  deferrals.

Adapter changes are surgical: synthesizes operand-literal symbols where
the verifier needs them (op<NNN>__multiplicand / __divisor / __rate),
then stamps each equation via check_admissibility. Input/output types
unchanged; bind_math_problem_graph still byte-equal across runs.

Tests: 226 total in the binding-graph lane (110 Phase 1+2 still pass; 47
units + 40 admissibility + 29 adapter-units new). Pyright clean on all
new files. No runtime wiring outside generate/binding_graph/.

Phase 4 (question-target binding) and Phase 5 (B3 / bounded grammar)
remain deferred per the brief.
2026-05-23 11:07:05 -07:00
Shay
5b668cc866
feat(binding-graph): Phase 2 adapter from MathProblemGraph (ADR-0133) (#174)
Pure-function adapter `bind_math_problem_graph(g) ->
SemanticSymbolicBindingGraph` translating ADR-0115 `MathProblemGraph`
into the ADR-0132 binding-graph data model. Structural translation
only — no I/O, no parser/solver calls, no algebra, no numpy, no
runtime wiring.

Mapping discipline locked as module-level constants:
- each entity      -> SymbolBinding(semantic_role="entity")
- each possession  -> SymbolBinding(quantity) + BoundFact
- each Operation   -> fresh result SymbolBinding + BoundEquation
                      (operation_kind verbatim passthrough on the
                       shared closed vocab)
- Unknown          -> synthesized SymbolBinding(unknown) + BoundUnknown

Refusal-first: `g: object` boundary accepts any caller input and
refuses non-MathProblemGraph with typed AdapterError (sibling of
BindingGraphError). Cross-collection invariant failures (defensive,
should be unreachable) are re-raised as AdapterError so callers see a
single refusal type.

Phase 2 placeholders (closed in Phase 3+):
- BoundEquation.unit_proof = "deferred_to_phase_3"
- BoundEquation.admissibility_status = "pending"

Phase 3 (ADR-0134 unit-aware admissibility), Phase 4 (question-target
binding refinement), and Phase 5 (bounded-grammar / B3 integration)
explicitly deferred — see ADR.

Evidence:
- generate/binding_graph/adapter.py (pure functions)
- generate/binding_graph/__init__.py (public surface)
- tests/test_binding_graph_adapter.py — 41 tests (refusal-first, all
  8 VALID_OPERATION_KINDS round-trip, dep wiring, introduction order,
  hash-stability, frozen output, input immutability, placeholder
  constants, cross-collection invariants)
- docs/decisions/ADR-0133-binding-graph-adapter.md

Lane: tests/test_binding_graph_model.py + tests/test_binding_graph_adapter.py
      -> 110 passed, 0 failed. pyright clean on new files. Runtime
      byte-identical to main (no runtime integration yet, by design).
2026-05-23 10:45:15 -07:00
Shay
ed759d1b43
feat(ADR-0131.2): teaching-corpus math eval — lane PASSED 30/30 (#172) 2026-05-23 10:44:25 -07:00
Shay
ca3b6011d4
feat(ADR-0131.1.S): sealed holdout for symbolic equivalence v1 (#173) 2026-05-23 10:44:23 -07:00
Shay
980213ed62
feat(binding-graph): Phase 1 data model (ADR-0132) (#171)
Frozen dataclasses + deterministic allocator + invariants for the
Semantic-Symbolic Binding Graph proposed in PR #170. Pure data layer:
no parser, no solver, no adapter, no runtime wiring. Phases 2-5
deferred to follow-up PRs.

- generate/binding_graph/model.py: SourceSpanLink, SymbolBinding,
  BoundFact, BoundEquation, BoundUnknown, BoundConstraint, and the
  top-level SemanticSymbolicBindingGraph container. All
  @dataclass(frozen=True, slots=True). Refusal-first construction via
  typed BindingGraphError. Cross-collection referential integrity
  enforced at __post_init__.
- generate/binding_graph/allocation.py: pure deterministic
  allocate_symbols() — same input order yields byte-equal output.
- generate/binding_graph/__init__.py: public API surface.
- tests/test_binding_graph_model.py: 69 tests covering frozen
  invariants, slots enforcement, refusal paths, allocation
  determinism, canonical-string round-trip, cross-collection
  integrity.
- docs/decisions/ADR-0132-binding-graph-data-model.md: ratifies
  Phase 1 only; explicit Phase 2-5 deferred section citing #170.
2026-05-23 10:29:59 -07:00
Shay
29d916ab16 docs(ADR-0131): re-target math expert promotion to architecture-aligned benchmarks (proposed)
Docs-only PR. Follow-up to ADR-0127-0128-RESULTS' Path-B trigger.

Replaces ADR-0120's GSM8K-coverage requirement for the
mathematics_logic expert promotion with a composite gate over
three architecture-aligned benchmarks:

  Benchmark 1: Symbolic equivalence (~500 algebra cases, public
    + sealed split). Primary discriminator — CGA exact-recall.
    Pass: correct_rate >= 0.95, wrong == 0.

  Benchmark 2: CORE-native teaching-corpus eval. Internal
    consistency lane sourced from ratified packs (en_arithmetic_v1
    + en_units_v1 + en_numerics_v1). Grammar matches by
    construction (no paraphrase-variance gap).
    Pass: correct_rate >= 0.95, trace_hash byte-equality, wrong == 0.

  Benchmark 3: Bounded-grammar word-problem set (~150 cases,
    pre-filtered to ADR-0126/0127/0128 parser scope). Demonstrates
    end-to-end pipeline determinism on the in-scope distribution.
    Includes adversarial out-of-grammar probes.
    Pass: correct_rate >= 0.95, wrong == 0.

Three benchmarks (not one) prevent overfitting per ADR-0114a
and force the math expert to demonstrate three distinct
architectural properties (algebraic correctness, internal
consistency, end-to-end pipeline determinism).

GSM8K stays as a stress-test lane the math expert RUNS but is
NOT gated on. Refusal counts reported in expert-claims artifact
as honest disclosure.

Other 12 ADR-0120 expert-promotion checks UNCHANGED.

Status: Proposed. The contract revision (removing GSM8K from
the gate) is a follow-up implementation PR after sub-phases
0131.1-0131.6 land the new benchmark substrate.
2026-05-23 07:49:18 -07:00
Shay
c13d7e14c4 feat(ADR-0127/0128 integration): pack-aware parser + Path-B trigger evidence
Integrates en_units_v1 (#164) + en_numerics_v1 (#163) into the
ADR-0126 candidate-graph parser. Loader merge (re-exports from
numerics_loader.py give single import path), pack-aware unit
canonicalization (handles irregular plurals like feet/children
via lookup_unit), indefinite-quantifier refusal (ADR-0128.4 —
'some'/'many' emit no candidates, preserving wrong==0), and
widened initial-possession shapes:
  - <Entity> has N <unit> [of <substance>]  (ADR-0127 substance qualifier)
  - There are N <unit> [in <place>]         (implicit-subject shape)

Plus: pack-backed cardinal grounding in math_roundtrip._value_grounds
(widens word-number coverage from hard-coded 0-12 to full numerics
pack cardinal table + compound rule). Op-pattern trailing prep
alternation gains of/for/with for substance qualifiers.

REGRESSION: 1050/1050 tests green across math + ADR-0126 + ADR-0127
ratification + ADR-0128 ratification + runner.

EMPIRICAL RESULT (the Path-B trigger ADR-0126/0127/0128 named):
  correct =  0/50  wrong =  0/50  refused = 50/50
  on evals/gsm8k_math/train_sample/v1/cases.jsonl

Per ADR-0127's exit criterion (correct >= 10/50, wrong == 0):
**MISSED** — the full deterministic design (candidate-graph
topology + units pack + numerics pack + pack-aware parser) does
not move the GSM8K-math lane. This is the real Path-B trigger.

WHAT WORKS (synthetic verification, 6/6 cases solve end-to-end):
  - 'Jan has 5 apples. Jan buys 3 apples. ...' -> 8
  - 'Sam has 10 feet of rope. Sam uses 3 feet of rope. ...' -> 7
  - 'There are 5 kids in camp. ...' -> 5
  - 'Sam has 10 children. Sam loses 2 children. ...' -> 8
  - (money + time-dimension variants pass)

WHY GSM8K STAYS AT ZERO: real GSM8K problems carry compound
linguistic structure (pronouns across statements, possessives,
subordinate clauses, multi-word entities, multi-step inference)
that no amount of pack vocabulary addresses. Per-sentence parse
rate improved measurably on simple shapes; joint problem-level
pass rate stayed at zero because every real problem contains at
least one sentence the parser still cannot handle.

Full results + Path-B recommendation in
docs/decisions/ADR-0127-0128-RESULTS.md. The substrate
(architecture + packs) stays load-bearing in main; the math
expert promotion path retargets to a benchmark where exact
recall and determinism are the discriminators (proposed
ADR-0131).
2026-05-23 07:41:50 -07:00
Shay
f54c07d202
Merge pull request #162 from AssetOverflow/docs/pedagogy-review-and-teaching-backlog
docs: pedagogy review + 2 deferred teaching-loop ADRs (0129 + 0130)
2026-05-23 07:05:22 -07:00
Shay
f3c9436e66 docs: pedagogy research review + 2 deferred teaching-loop ADRs
Operator-supplied review of 'Beyond Traditional Pedagogy' triggered
a literature confirmation pass and a structural cross-walk against
CORE's teaching loop. Three artifacts:

1. ADR-0129 (DEFERRED) — Spaced reviewed-correction replay.
   Maps onto retrieval-with-spacing literature (most robust finding
   in cognitive psychology). Deterministic re-run of past
   corrections at fixed cadence to verify they still produce
   intended outcomes; failures emit operator-visible events
   (no auto-correction). Deferred pending GSM8K-math Path-A/B
   resolution + observed incident triggering un-deferral criteria.

2. ADR-0130 (DEFERRED) — Pre-articulation calibration logging.
   Maps onto metacognitive prediction-outcome literature. Logs
   CORE's pre-correction prediction; emits gap event on
   correction acceptance. Provides empirical signal for 'is CORE
   actually getting better' across pack-version cohorts. Deferred
   pending same conditions as ADR-0129; the two compose if
   un-deferred.

3. SESSION-2026-05-23 session note. Documents the review process:
   literature confirmation pass (productive failure overstated,
   retrieval transfer weaker than claimed, embodied cognition
   replication crisis), missed frameworks (worked-example effect,
   expertise reversal, CLT, deliberate practice, Bloom's 2-sigma),
   structural cross-walk to CORE architecture (12 mappings), and
   the rationale for ADRs 0129 + 0130 over alternative ports
   (productive failure rejected as inverse of wrong==0; pre-testing
   same; embodied learning N/A).

No code changes. Docs-only PR; lands independently of in-flight
ADR-0126 / 0127 / 0128 substrate chain.
2026-05-23 07:01:55 -07:00
Shay
6e7766f66e docs(ADR-0127+0128): exhaustive units pack + sibling numerics pack
ADR-0127 expanded from minimum-viable to EXHAUSTIVE:
- 13 dimensions (7 base + 6 derived via dimension algebra)
- ~150 unit lemmas covering imperial+metric for length/time/mass/
  volume/money/temperature/area, with full irregular-plural set
- Multi-word structural rules (square <length>, <length>/<time>)
- ~25 container nouns, ~6 rate connectors, ~15 symbol/affixes
- Conversion graph EXHAUSTIVE within each dimension (~80 edges)
- Temperature edges carry affine offset field (F/C/K)
- Ratification adds: exhaustive-coverage gate, NIST/ISO provenance,
  dimension-algebra closure

ADR-0128 NEW: en_numerics_v1 sibling pack
- Exhaustive cardinals (0..20 + tens + magnitudes) + compound rule
- Exhaustive ordinals (1st..31st covers calendar/position)
- Fractions (named + symbol + slash-form + mixed numbers)
- Multipliers (double/triple/twice/half + N-times rule)
- Quantifiers with semantic_type (indefinite -> refusal preserves
  wrong==0)
- Comparison anchors migrated from math_roundtrip.py hard-coded
  tables (ratifiability)
- Number-format regexes (digits/decimals/percentages/fractions/
  mixed) with test corpus
- Cross-pack consistency check with en_units_v1 fraction symbols

Joint exit gate: train sample re-run with both packs mounted.
If passed -> sealed holdout. If missed -> real Path-B trigger
(full deterministic design failed).
2026-05-23 06:48:22 -07:00
Shay
fde62d713d docs(ADR-0127): units pack + units-aware parser + conversion graph (proposed)
Diagnostic from ADR-0126's first train-sample run (0/0/50): every
refusal happens at the first statement of each problem, and every
refused first statement fails on the unit-of-measurement construction,
not on the operation grammar. Adding more verb regexes is the per-axis
treadmill that produced 4 zero-lift ADRs. Units form a finite, externally
well-defined ontology (NIST SI tables, currency, English container nouns)
that is semantic substrate the candidate-graph parser was designed to
consume.

Scope:
- en_units_v1 pack: dimensions, units (<=60), containers, rate connectors
- conversions.jsonl: directed weighted graph of within-dimension unit pairs
- 3 new initial-possession shapes + rate-declaration extractor in the
  candidate parser
- Round-trip filter gains optional pack-typed-unit check
- Solver gains dimensional canonicalization helper (shortest path through
  conversion graph); fired edges join SolutionTrace.steps for replay
- Pack ratification invariants: round-trip identity, per-dimension
  connectivity, path consistency, canonical unit per dimension

Wire the same train-sample exit criterion as ADR-0126 (correct >=10/50,
wrong==0). If passed -> sealed holdout. If still missed -> Path B
trigger is REAL (full deterministic design with units substrate failed),
demote GSM8K, re-target math expert promotion.

Also commits the empirical evidence: train_sample/v1/runner.py swapped
_score_one -> _score_one_candidate_graph; report.json baseline 0/0/50
confirming the candidate-graph topology refuses cleanly without units
substrate.
2026-05-23 06:42:39 -07:00
Shay
5a987ed577 docs(ADR-0126): candidate-graph parser + round-trip verifier-filter (proposed)
Architectural pivot from per-axis grammar expansion (ADR-0122/0123/0123a/
0123b, all 0/1319 sealed lift) to candidate-graph topology with round-trip
admissibility filter. Converts compound-gap failure from multiplicative
p^k to parallel 1-(1-p)^k.

Adds new invariant: round-trip admissibility (op slots must reconstruct
to byte-equal source sentence under whitespace/case normalization).
Preserves wrong==0, trace_hash byte-equality, pack-binding, replay
determinism, no stochastic sampling.

Exit criterion: 50-case GSM8K train-split sample (unsealed) must show
correct >=10/50 with wrong==0 before any sealed-holdout run. If miss,
escalate to Path B (benchmark re-selection).

Supersedes ADR-0123b (never opened as PR).
2026-05-23 06:36:13 -07:00
Shay
ec1dcf6e78 feat(realizer): ADR-0123 comparison-phrasing surface (closes substrate)
ADR-0123-parser-comparison-phrasing as the **surface increment** on
PR #155's substrate (commit c9bd5d4). Closes the last architectural
gap in the comparison-phrasing class: before this commit, the
substrate's solver evaluated comparison problems successfully but
realize() crashed with `unknown operation_kind 'compare_additive'`
when asked for show-your-work prose.

Substrate (PR #155) already shipped:
- `Comparison` typed graph operand
- `compare_additive` / `compare_multiplicative` operation kinds
- parser patterns for the four canonical surfaces
  (N more / N fewer / twice / N times / half)
- solver + verifier wiring + pack lemmas
  (en-arith-006 compare_additive, en-arith-007 compare_multiplicative)

This surface adds:
- `_compare_additive_sentence(step)` rendering `direction='more'|'fewer'`
- `_compare_multiplicative_sentence(step, entity_units)` rendering
  `direction='times'|'fraction'`
- two new branches in `_step_sentence` dispatch
- `_step_sentence` signature widened with optional `entity_units` map
  (derived once-per-trace in `realize()` from `graph_initial_state`)
- ADR-0123-parser-comparison-phrasing.md (~15 invariants, substrate
  + surface decomposition rationale, multi-construction barrier
  inheritance)
- 26 invariants pinned across canonical surfaces, plurality
  independence, byte-determinism, refusal discipline, and
  backwards-compatibility with the pre-comparison realizer templates

End-to-end pipeline now operates on all four canonical comparison
shapes:

  parse_problem(
    "Alice has 5 apples. Bob has 3 more apples than Alice. "
    "How many apples does Bob have?"
  ) -> solve() -> realize().as_prose() ->
  "Alice has 5 apples. Bob has 3 more apples than Alice, giving Bob
   a total of 8 apples. Bob has 8 apples."

Measurement (this PR):
- 26/28 direct ADR-0123 tests pass; 2 skipped (CORE_HOLDOUT_KEY)
- `core eval cognition` byte-identical: 100/100/100/100
- ADR-0118 stepped-realizer templates re-render byte-identically
- Substrate measurements continue to hold

Honest non-result: sealed `correct_rate` stays at 0/1319. The
realizer cannot create matches the parser refuses; the multi-
construction barrier the substrate ADR documented holds at the
surface layer too. Cumulative lift signal expected only after the
3rd/4th foundational class lands (per ADR-0121's revised
sequencing). `wrong == 0` holds by construction — realizer only
renders successful traces.

Pre-existing failure noted (not introduced by this PR):
`tests/test_adr_0085_gloss_aware_cause.py::test_flag_off_metrics_byte_identical`
fails on substrate base (c9bd5d4) without these changes — an
ADR-0085 cognition baseline drift unrelated to the realizer.
2026-05-23 02:03:49 -07:00
Shay
6582df3bae feat(parser): ADR-0122 rate/per-unit grammar (substrate-only; lift deferred)
First parser-expansion ADR after ADR-0121's deferral. Adds the rate
algebra substrate (Rate dataclass + apply_rate operation kind + parser
pattern + solver/verifier/realizer + en_arithmetic_v1 pack lemma)
mirroring the deferral pattern that ADR-0121 demonstrated for
capability promotion: substrate complete, gate refuses honestly.

Substrate
- Rate(value, numerator_unit, denominator_unit) frozen dataclass with
  strict positive-value + non-empty-distinct-unit refusal at construction
- apply_rate operation kind admitted in VALID_OPERATION_KINDS;
  Operation.operand widened to Quantity | Rate with kind-discriminated
  type enforcement
- Parser: _RATE_COST_EACH_RE + _RATE_COST_EACH_TRAILING_RE +
  _Q_RATE_AGGREGATE_RE patterns; actor_units state tracking;
  first-declaration-wins on redeclaration (ParseError); orphan-rate
  refusal at end of parse; three refusal paths in rate-aggregate question
- Solver: _apply_rate() reads denominator-unit state, multiplies by
  rate.value, writes numerator-unit state (denom preserved)
- Verifier: _verify_apply_rate_step() byte-equal replay
- Realizer: 'At {N} {numer} per {denom_singular}, {actor} spends ...'
  template containing required tokens
- Pack: en-arith-006 apply_rate lemma + gloss; SHA-256 checksums
  refreshed; manifest version 1.0.0 -> 1.1.0; provenance tagged
  adr-0122:rate_extension:2026-05-22

Measurement (honest)
- Sealed GSM8K correct_rate: 0/1319 (substrate matches zero real cases
  alone). Multi-construction barrier documented in the ADR: all 14 sealed
  cases matching 'each \w+ costs?' combine rate with at least one other
  class (aggregation 6, comparison 3, unit conversion 2, multi-actor 2,
  conditional 1)
- Sealed GSM8K wrong: 0 (load-bearing positive claim; grammar adds zero
  misparses across 1,319 real test problems)
- Anti-overfit lanes unchanged: OOD ratio, perturbation invariance
  preserving/breaking 1.0, adversarial wrong 0
- ADR-0121 invariants byte-equal preserved (6/6)
- 41 new ADR-0122 invariants pinned in tests/test_adr_0122_rate_per_unit.py
- 670 existing math + pack regression tests pass

Roadmap update
- Per-ADR lift expectation corrected: no single parser-expansion ADR
  will move sealed correct_rate alone. First lift signal will come
  from cumulative composition after 3rd or 4th class lands (rate +
  comparison + aggregation foundational set)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 21:24:28 -07:00
Shay
68fd932ae0 docs: Phase 5 GSM8K-math substrate completion sweep (ADR-0119 + sub-phases + ADR-0114a 10/10)
Documents the Phase 5 GSM8K-math substrate completion across 7 narrative docs.
All 8 sub-phases of ADR-0119 (5.1 through 5.8) have landed on main; ADR-0114a's
10 anti-overfitting proof obligations are all discharged for the gsm8k_math lane.

Key facts surfaced in each doc:
- CORE-original public split: 150/150 correct, 0 wrong, 0 refused
- Real GSM8K test (sealed holdout): 0 correct, 0 wrong, 1319 refused
- Adversarial suite: 38 cases x 12 families, 0 wrong
- Depth curve: flat at 1.0 across depths 1-8 on public split
- Frontier baselines: Claude 3.5 Sonnet 96.4%, GPT-4 92.0%, Gemini 1.5 Pro 90.8%
- New lane shape gsm8k_capability_shape in LANE_SHAPE_REGISTRY
- New operational pack en_arithmetic_v1 (5 lemmas)
- ADR-0120 (first expert promotion contract) is the next gate

Docs updated: docs/PROGRESS.md, docs/capability_roadmap.md, docs/runtime_contracts.md,
docs/Whitepaper.md (§XIII), docs/Yellowpaper.md (gsm8k_capability_shape formal spec),
README.md, docs/decisions/README.md (current frontier).

No code changes. No new ADRs.
2026-05-22 20:39:24 -07:00
Shay
4f473a5aa8 docs: ADR-0121 — mathematics_logic expert promotion DEFERRED (first attempt)
First worked attempt at promoting a domain under the ADR-0120
expert promotion contract. The contract refuses honestly.

Gate evaluation against live state:
  ADR-0114a obligations: 10 of 10 pass
  ADR-0120 contract-level gates:
    audit_passed_holds         ✓
    correct_rate (public)      ✓  150/150 = 1.0
    correct_rate (sealed)      ✗  0/1319 = 0.0 < 0.60 floor
    signed_expert_claim        ✗  (no entry, downstream of correct_rate)

Decision: mathematics_logic NOT promoted; stays at audit-passed.
Substantive blocker: parser grammar covers 0/1319 of real GSM8K.

What this proves
  - The contract is genuinely falsifiable. ADR-0120 §"Threshold
    rationale" deliberately set the floor above current measurement
    so the first attempt would defer honestly. Same load-bearing
    pattern as ADR-0107 → ADR-0110 for audit-passed.
  - Wrong-zero discipline holds against real GSM8K (the load-
    bearing positive claim). CORE refuses every problem outside
    its grammar without confabulating on a single one.

What unlocks the promotion
  Multi-ADR parser-expansion arc lifting sealed-GSM8K correct_rate
  from 0.0 to ≥ 0.60. Each construction class (rate/comparison/
  percentage/time-modal/etc.) ships as its own scoped ADR with:
    - parser+solver+verifier+realizer extensions
    - re-measurement on sealed holdout
    - ADR-0118a OOD re-measurement (no surface-feature regression)
    - ADR-0125 perturbation re-measurement (no invariance regression)
    - ADR-0119.5 adversarial re-measurement (no new misparses)
  Honest-fitting discipline: every lift is graded on the anti-
  overfit obligations BEFORE the correct_rate change counts.

Tests: 6/6 with CORE_HOLDOUT_KEY; 4/6 + 2 skipped without (matches
ADR-0119.7 seal discipline).

This deferral demonstrates the expert tier's promotion machinery
is load-bearing — the gate has refused at least once before any
domain reaches it.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 20:31:06 -07:00
Shay
81a5ff6350 docs: ADR-0120 — first expert promotion contract (proposed)
Defines the `expert` ledger tier (sixth status above `audit-passed`)
and the composition gate that governs every future promotion.

The gate composes ALL TEN ADR-0114a obligations plus three contract-
level checks:
  - audit_passed predicate must hold
  - correct_rate >= 0.60 on public AND sealed holdout splits
  - signed expert_claims entry whose digest reproduces byte-equal

Numeric thresholds (load-bearing choices documented with rationale):
  - correct_rate floor: 0.60 ("advanced" — above weak open-source LLMs,
                              forces real architecture work, raisable later)
  - depth-curve flatness ε: 0.05 (accuracy(N) ≥ accuracy(d1) · 0.95^(N-1))
  - drift tolerance from promotion: ±0.02

Documents the post-ADR-0120 sequence in Open Candidate Directions:
  Phase 1: ADR-0121 first worked math promotion (likely deferral)
  Phase 2: parser-expansion arc to lift sealed-GSM8K correct_rate
  Phase 3: math expert promotion succeeds
  Phase 4: second domain = symbolic_logic (60-70% of math substrate cost;
            ProofWriter / PrOntoQA benchmark; same machinery class)
  Phase 5: third domain = high-stakes refusal-centric (medical or legal;
            wedge-sharpener; needs two prior successes first)
  Phase 7: open candidate — multi-reviewer threshold signing for expert

No code lands with this ADR. Implementation ships under ADR-0120a
to keep the contract change reviewable independently.

The first promotion attempt (ADR-0121) will likely defer on the
correct_rate gate — current measurement is 0/1319 on real GSM8K.
That deferral IS the contract working as designed; same load-
bearing pattern ADR-0107 → ADR-0110 demonstrated for audit-passed.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 20:18:53 -07:00
Shay
38872f825a feat: ADR-0119.7 — seal GSM8K test as gsm8k_math holdout (Phase 5 substrate complete)
The 1,319 GSM8K test cases are now sealed at
evals/gsm8k_math/holdouts/v1/cases.jsonl.age, age-encrypted to the
ADR-0119.1 recipient. Plaintext never touched disk in the working
tree; only ciphertext is committed.

First honest CORE-vs-real-GSM8K measurement
  cases_total: 1319
  correct:     0
  wrong:       0   ← ADR-0114a Obligation #4 holds against external corpus
  refused:     1319
  overall_pass: True

Zero confabulation. Parser refuses what it can't grammar-handle; the
"wrong == 0" discipline survives the move from CORE-original cases
to a real public benchmark. The 0/1319 correct rate is the truthful
gap that ADR-0120's threshold work will quantify.

What landed

scripts/seal_gsm8k_test.py
  - Loads GSM8K via datasets.load_dataset("openai/gsm8k", "main")
  - Strips worked-solution prose; extracts final-answer integer/float
    after "####" (handles "2,125" → 2125 thousands-separator)
  - Reads recipient from docs/holdout_recipients.txt (single repo key
    per ADR-0119.1)
  - Encrypts via pyrage; writes only ciphertext
  - Refuses to overwrite test path with train-derived seal

evals/gsm8k_math/runner.py
  - Empty expected_unit (sentinel) skips unit-comparison; grades on
    answer value alone. Required because GSM8K answers carry no unit
    structurally. wrong-zero discipline preserved.

tests/test_adr_0119_7_sealed_gsm8k.py — 6 invariants:
  1. sealed file present + age-formatted
  2. no plaintext companion files (sibling-leak guard)
  3. decrypted JSONL matches documented schema
  4. runner against decrypted suite produces wrong==0
  5. tests skip (not fail) when CORE_HOLDOUT_KEY unset
  6. case ids match "gsm8k-test-NNNN" pattern

Defensive gitignore: plaintext patterns under
evals/gsm8k_math/holdouts/v1/ are explicitly excluded.

ADR-0114a obligation roll-up
  10/10 discharged for the gsm8k_math lane:
    #1 ✓ sealed-holdout (fab_control + GSM8K test)
    #2..#10 ✓ as before

Phase 5 status: 5.1..5.7 done; 5.8 in flight (PR #149). After 5.8
merges, ADR-0120 (first expert promotion contract) becomes
feasible.

Test plan
  - pytest tests/test_adr_0119_7_sealed_gsm8k.py with CORE_HOLDOUT_KEY → 6/6
  - pytest without CORE_HOLDOUT_KEY → 3 pass + 3 skip
  - core test --suite smoke -q → 67/67
  - CLAIMS.md regenerated (no diff)
  - HF token NEVER in repo (saved at ~/.cache/huggingface/token, mode 600)

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 20:08:35 -07:00
Shay
a13df6f370 feat: ADR-0119.8 — gsm8k_math overall lane gate (gsm8k_capability_shape)
Phase 5.8 of ADR-0119. Composes the per-sub-phase substrate
(5.1..5.6) into a single per-split lane verdict the eventual
ADR-0120 expert promotion contract can consume.

LANE_SHAPE_REGISTRY adds:
  "gsm8k_math": "gsm8k_capability_shape"

_check_gsm8k_capability_shape refuses on any of:
  - missing cases_total / correct / wrong / refused fields
  - cases_total <= 0
  - wrong != 0                          (ADR-0114a Obligation #4)
  - correct + refused != cases_total    (accounting incomplete)
  - overall_pass present and false

Accepts otherwise. Edge: all-refused passes the shape gate (runner
self-consistency). Capability bar (min correct-rate, depth-curve
ε) lives in ADR-0120.

Live measurement on main:
  dev    50/50 correct, 0 wrong, 0 refused  → gate ✓
  public 150/150 correct, 0 wrong, 0 refused → gate ✓

21 invariant tests pin: registry mapping, shape checker presence,
live runner passes, nonzero wrong refuses, incomplete accounting
refuses, missing field refuses, clean metrics pass, all-refused
edge passes, all Phase 5.1..5.6 substrate artifacts exist on disk.

Phase 5 status: 5.1..5.6 + 5.8 ✓. Only 5.7 (sealed real GSM8K
test) remains before ADR-0120 (first expert promotion contract)
becomes feasible.

ADR-0114a roll-up unchanged: 10/10 obligations discharged on main
(modulo Phase 5.7's lane-specific GSM8K test sealing).

Tests: 21 new + 80 prior across Phase 5 + adjacent suites = 101
green; 67/67 smoke.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 19:45:44 -07:00
Shay
3bda4313c9 feat: ADR-0119.5 — adversarial generation (closes ADR-0114a Obligation #8)
Phase 5.5 of ADR-0119. Adversarial case generator + scoring CLI;
discharges the last remaining ADR-0114a obligation.

Numbers
  adversarial suite: 38 cases × 12 families
  per-family: every family produces wrong == 0
  overall: correct 5, wrong 0, refused 33

Families
  conditional_phrasing       (4)  "If/When/Suppose ..."
  compound_questions         (3)  multiple ?
  undefined_entity_question  (3)  question references unknown entity
  unknown_verb               (5)  "polishes", "admires", etc.
  empty_or_whitespace        (3)  empty input
  no_question                (3)  statement-only
  numbers_spelled_out        (3)  "five", "ten"
  passive_voice              (3)  "X are bought by Y"
  red_herring_numbers        (3)  digits in name positions, mid-quantity
  question_only              (2)  no preceding statements
  mid_sentence_punctuation   (2)  embedded ? or !
  subtle_in_grammar          (4)  IN-grammar; runner must produce correct
                                  (gate-sanity: not trivially "refuse all")

The subtle_in_grammar family is the load-bearing sanity check —
proves the gate isn't trivially satisfied by refusing everything.

ADR-0114a obligation status

  10 of 10 discharged on main:
    #1  fab_control lane (0119.1); GSM8K test pending (0119.7)
    #2  ADR-0118a
    #3  ADR-0117
    #4  ADR-0116 + ADR-0119.3
    #5  ADR-0125
    #6  ADR-0119.6 harness; ε threshold to ADR-0120
    #7  ADR-0119.4
    #8  THIS ADR
    #9  ADR-0116/0117/0118/0119.3
    #10 ADR-0116

Phase 5 remaining: 5.7 (sealed GSM8K test, real corpus) and 5.8
(overall lane gate). After those, ADR-0120 (first expert promotion
contract) can compose all ten obligations.

Tests: 18 new + 25 prior Phase 5 = 43 green; 67/67 smoke.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 18:11:36 -07:00
Shay
0ffbd5c40a merge origin/main and resolve README conflicts 2026-05-22 17:38:21 -07:00
Shay
9288688640
feat: ADR-0119.3 — gsm8k_math lane runner (Phase 5.3) (#145)
Composes the Phases 1-4 pipeline (parser → solver → verifier →
realizer) into a per-case scoring decision: correct / wrong /
refused.

Outcome categorization (ADR-0114a Obligation #4):
  parser ParseError       → refused
  solver SolveError       → refused
  verifier verdict failed → wrong
  realizer error          → wrong
  answer/unit mismatch    → wrong
  all match               → correct

`wrong == 0` is the load-bearing gate. The lane's overall_pass
holds only if wrong == 0 AND correct + refused == total.

Initial measurement on the Phase 5.2 corpus:
  dev    (50)  : 50 correct, 0 wrong, 0 refused, overall_pass=True
  public (150) : 150 correct, 0 wrong, 0 refused, overall_pass=True

Every correct case carries a trace_hash (64-char SHA-256) and
realized prose — full audit trail per case, consumable by ADR-0119.4
(frontier comparison), ADR-0119.6 (depth curve), and ADR-0120
(eventual expert-tier gate).

Tests: 13/13 green; 443 total green across runner + realizer +
solver + verifier; 67/67 smoke green.

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 17:37:54 -07:00
Shay
a65040cb73 merge origin/main and resolve conflicts 2026-05-22 17:37:13 -07:00
Shay
51d3a73589 feat: ADR-0119.6 — depth-curve measurement harness (ADR-0114a Obligation #6) 2026-05-22 17:33:58 -07:00
Shay
c21068ed3e feat: ADR-0119.4 — frontier-baseline comparison (ADR-0114a Obligation #7) 2026-05-22 17:33:28 -07:00
Shay
1a37f97c4f feat: ADR-0119.2 — author 200 grade-school math problems for the GSM8K eval lane (dev + public) 2026-05-22 17:28:00 -07:00
Shay
f9dd650df0 Merge remote-tracking branch 'origin/main' into feat/adr-0119.1-sealed-holdout-fabrication-control
# Conflicts:
#	docs/decisions/README.md
2026-05-22 17:24:32 -07:00
Shay
32c0a90ad9 feat: ADR-0119.1 — seal fabrication_control holdout with age encryption (Obligation #1) 2026-05-22 17:22:46 -07:00
Shay
93a149fc45
Merge pull request #142 from AssetOverflow/feat/adr-0119-gsm8k-eval-lane-roadmap
docs: ADR-0119 — GSM8K eval lane roadmap (Phase 5 decomposition)
2026-05-22 17:22:23 -07:00
Shay
98eb4d9f75
Merge pull request #141 from AssetOverflow/feat/adr-0118-stepped-realizer
feat: ADR-0118 — stepped realizer (SolutionTrace → show-your-work prose)
2026-05-22 17:20:31 -07:00
Shay
5ac3e3ff2f docs: ADR-0119 — GSM8K eval lane roadmap (Phase 5 decomposition)
Umbrella ADR for Phase 5 of ADR-0114. Decomposes the eval-lane work
into eight sub-phases (5.1..5.8) that ship under their own ADRs.

Sub-phase scope:
  5.1 — sealed-holdout encryption (one lane, fabrication_control)
  5.2 — CORE-original GSM8K-style corpus (dev + public, 200 cases)
  5.3 — lane runner (parser → solver → verifier → realizer)
  5.4 — frontier-baseline comparison (Obligation #7)
  5.5 — adversarial generation; misparse rate zero (Obligation #8)
  5.6 — depth-curve measurement harness (Obligation #6, measurement)
  5.7 — sealed GSM8K test as the holdout (Obligation #1, lane-side)
  5.8 — overall lane gate; new gsm8k_capability_shape

Roadmap only — no code lands with this ADR. Each sub-phase ships
independently and discharges a specific ADR-0114a obligation.

Critical invariant: dev + public splits are CORE-original; the real
GSM8K test set enters only via the encrypted holdout under 5.7.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 17:18:19 -07:00