Commit graph

875 commits

Author SHA1 Message Date
Shay
d17fec6801 fix(math-graph): refuse contradictory initial possessions (wrong=0 hazard)
MathProblemGraph.__post_init__ now raises MathGraphError when two
InitialPossession entries share the same (entity, unit) key but
declare different quantity values.

Pre-fix behavior surfaced by 2026-05-28 ADR-0174 Phase 3 post-merge
diagnostic: math_solver.solve() line 207 used last-write-wins dict
assignment when consolidating initial state. Two contradictory
inputs would silently overwrite without trace:

  'Sam has 5 marbles. Sam has 3 marbles. How many marbles does Sam have?'
   → returned 3.0 (wrong=0 violation: definite answer from
     contradictory input)

Post-fix: same input refuses with 'no branch produced a solvable
graph' — refusal-preferring discipline as wrong=0 doctrine requires.

Identical duplicates (same value) are admitted as redundant (no
contradiction). Different units for same actor admitted. Different
actors for same unit admitted. Single-value cases (the dominant
real-world pattern) unchanged.

This is an extraction-layer hazard discovered while investigating
Phase 3b scope: Phase 3b compound-clause held hypotheses would
emit multiple CandidateInitial entries per sentence, exercising
exactly this consolidation path. Fixing the silent overwrite NOW
ensures Phase 3b admission doesn't silently produce wrong answers.

Acceptance:
- 4 new tests in TestContradictoryInitialPossessionsRefuse
- 165/165 test_math_problem_graph tests pass (was 161/161)
- Smoke 67/67, packs 141/141 unchanged
- train_sample 3/47/0 unchanged (no real case exercised the
  overwrite — but the hazard was latent)

References: CLAUDE.md §Lookback Review Discipline (the doctrine
that surfaced this), CLAUDE.md §Non-Negotiable Field Invariant
(make illegal states difficult to represent).
2026-05-28 09:51:14 -07:00
Shay
796b446b49 docs(handoff): require multi-actor pronoun safety test in VE-A/B acceptance
Adds second mandatory hazard test to VE-A/B acceptance criteria:
multi-actor pronoun ambiguity must trigger no_antecedent_ambiguous
refusal per ADR-0174 Phase 3a defense.

Verb expansion widens the cases that reach Phase 3a lookback wiring;
without this test the multi-actor wrong=0 hazard could fire silently
in production. Surfaced by 2026-05-28 Phase 1-3a lookback review.

References: project-adr-0174-multi-actor-pronoun-hazard memory,
CLAUDE.md §Lookback Review Discipline.
2026-05-28 09:19:46 -07:00
Shay
ee63ce4d5d docs(claude-md): institutionalize lookback review discipline
Adds 'Lookback Review Discipline' section before 'Architectural Scan
Exclusions'.  Multi-PR architectural work was accumulating latent
defects — three Phase 1-3a PRs shipped without a substrate audit
between phases, and a multi-actor pronoun wrong=0 hazard sat in
Phase 3a from the moment it was written until an explicit user-
requested review surfaced it.

Mandatory lookback triggers:
1. Before starting Phase N+1 of a multi-phase ADR
2. Before merging a stacked PR sequence into main
3. After any 3+ PR sequence on the same module/architectural surface

Review template covers: doc drift, test coverage gaps, parity gaps,
wrong=0 hazard surfaces, cross-PR consistency, honest LOC accounting.

Output is structured: solid / gaps / drift / hazards. Hazards require
fix-before-next-phase decision.

Cost: 20-40 minutes per 3-PR substrate. Skipping costs more (every PR
built on undetected hazard becomes implicated when it fires).
2026-05-28 09:10:02 -07:00
Shay
23bc789e06 docs(handoff): verb-coverage expansion dispatch pack (VE-A/B/C)
Operationalises the recommendation from PHASE-3.1-FOLLOWUP-RECOGNIZER-EXPANSION.md
into three independent dispatchable PRs:

- VE-A: acquisition widening (gain, earn, save, accumulate, acquire) — Opus
- VE-B: new depletion class (donate, give, lose, spend, eat) — Opus
- VE-C: refusal evidence for non-arithmetic verbs (instrumentation only) — Sonnet

Each PR includes:
- Hazard pinning: explicit case 0050 test must pass after widening
- Lift evidence: at least one train_sample case moves refused → correct
- Phase 3a substrate fires: the lifted case shows a 'lookback' trace event
- wrong=0 preserved across train_sample AND case 0050

Operator decisions needed before dispatch: which specific lemmas to
admit per class, whether to introduce the depletion class at all, and
whether to ship VE-C as evidence groundwork.

Verb classification rationale per lemma documented in the brief.
Hazard surfaces called out per lemma (delta-of-attribute for 'gained',
direction inversion for 'lost', monetary-vs-time ambiguity for 'saved').

No timelines; operator dispatches when ready.
2026-05-28 08:56:46 -07:00
Shay
da9f89ae16 chore(eval): regenerate train_sample/v1 report.json after 86d4e98 multi-word unit fix
86d4e98 (multi-word unit grounding fix) changed refusal reasons for two
cases without changing the 3/47/0 count:
- case 0019 (currency_amount): refusal moves from 'requires 3 vet
  appointments which cost $400 each' to 'After the first appointment,
  John paid $100 for pet insurance...' — first sentence now passes,
  refusal moves to subsequent sentence
- case 0023 (Nicole/Pokemon cards): refusal moves from 'Nicole collected
  400 Pokemon cards' (now passes via multi-word unit grounding) to
  'Cindy collected twice as many, and Rex collected half of...'

Counts unchanged: correct=3 refused=47 wrong=0. Updates report.json to
match current behavior so subsequent eval runs are byte-deterministic
from the committed snapshot.
2026-05-28 08:09:51 -07:00
Shay
f90f0cf471 docs(adr-0174): held-hypothesis comprehension with lookback and in-loop contemplation (proposed)
Extends ADR-0164's incremental comprehension reader from single-committed
state to held-hypothesis state, adding lookback re-evaluation and in-loop
contemplation. Diagnoses why the ADR-0164 reader is wired but inert
(all-or-nothing refusal at first unknown token / unexpected category).

Architecture: apply_word produces ProblemReadingState.open_hypotheses
(small ranked set, HYPOTHESIS_CAP=4 initial). Three operators per token:
EMIT (extend compatible hypotheses), ELIMINATE (constraint violations
remove hypotheses immediately), HOLD (uncommitted hypotheses survive at
lower confidence). At finalize(), |survivors|=0 refuses, |survivors|=1
admits, |survivors|>=2 invokes in-loop contemplate() over vault + packs
+ audit history. Ambiguity contemplation cannot resolve refuses cleanly,
preserving wrong=0.

Collapses three parallel parsing systems into one held-hypothesis
reader: removes regex parser runtime path (math_parser.py),
per-category injector dispatch table (recognizer_anchor_inject._INJECTORS),
and duplicate per-sentence-choices scaffolding. Net ~1,900 lines
removed; reader grows by ~600 for hypothesis state + reevaluate +
contemplate.

Preserves ADR-0164 lexicon and category set, ADR-0165 regex scope,
ADR-0150/0152/0155/0161 HITL corridor, binding graph + solver
substrate, capability-axis lanes, replay-equivalence gate. Trust
boundary for in-loop contemplation: read-only over vault/packs/audit;
ratification still rides offline HITL.

Status: Proposed. Six phases (no timelines) gated on wrong=0 and
capability-axis 100% at each transition. Five open questions resolved
before Phase 1 PR.
2026-05-28 07:56:57 -07:00
Shay
86d4e98d5c fix(roundtrip): multi-word units ground when every component appears in source
_unit_grounds() previously refused multi-word units like 'Pokemon cards'
even when both component words appeared as tokens in the source span.
The function checked unit_token against the haystack as a single key,
but the tokenizer splits source into per-word tokens — 'Pokemon cards'
was never going to match.

Fix is conjunctive by design: every component word must appear in the
haystack. A missing component refuses, preserving wrong=0.

Truth-test: case 0023 (Nicole/Pokemon cards) previously refused with
'recognizer matched but produced no injection' on its first sentence.
After this fix, sentence 1 passes injection cleanly; the case now
refuses on sentence 2 (Cindy/Rex compositional clause) — a more
honest refusal reason that reflects the actual remaining gap.

Score unchanged at 3/47/0 (no overall lift; correctness win).
smoke 67/67, packs 141/141, lanes 8/8 all green.
2026-05-28 07:49:24 -07:00
Shay
58294c7722 docs(claude-md): document .claude/ scan exclusion requirement
Adds 'Architectural Scan Exclusions' section before the PR Checklist.

Agent worktrees under .claude/worktrees/ contain full source copies that
trip every structural invariant scan (vault writers, normalize_to_versor
callsites, recall sites). Without .claude/ in the exclusion set, smoke
failures look exactly like real architectural violations but point at
non-live files — silent killers that mask actual regressions.

Documents the two maintained exclusion sets in
test_architectural_invariants.py and requires new scans to add .claude
before the first commit. Never rely on worktree pruning.
2026-05-28 07:16:21 -07:00
Shay
d6427ae4ec fix(invariants): exclude .claude/ from architectural scan + prune stale worktrees
Both INV-02/INV-21/INV-24 scan functions walked into .claude/worktrees/
and found vault recall/write callsites in the stale
step-3-submission-invariants checkout, producing 3 false failures.

Fix: add '.claude' to the os.walk exclusion set (INV-02) and to
EXCLUDED_DIRS (INV-21/INV-24). Defensive against any future worktree
that agents create under .claude/worktrees/.

Also pruned 58 stale worktree git-dir entries via git worktree prune
and removed the step-3-submission-invariants worktree directory.

Smoke suite: 67/67 passed.
2026-05-28 07:12:19 -07:00
Shay
89defef30b chore(audit): substrate cleanup — dead spike, gitignore, deprecation, reader diagnosis
C1: delete generate/math_versor_arithmetic.py and its 3 tests (ADR-0139
add-only arithmetic spike; no runtime consumers, no pipeline wiring,
follow-on lift paused per module docstring).

C3: gitignore engine_state runtime artifacts (manifest.json,
recognizers.jsonl, discovery_candidates.jsonl). Module code
(engine_state/__init__.py) remains tracked; generated checkpoint
files should not be.

C5: document reader zero-delta root cause in train_sample/v1/README.md.
Both Phase 2 (whole-problem) and Phase 1 (question-only) reader paths are
called but inert because all 47 refusals are statement-level NO_INJECTOR
gaps, not question-sentence gaps. Reader unblocks when injector coverage
expands (C2 work). report.json use_reader flag corrected to reflect last run.

C6: add deprecation header to generate/math_parser.py pointing at
generate.math_candidate_graph.parse_and_solve as the live path.

C2/C4 briefs: docs/handoff/CLEANUP-C2-run-lane-migration.md and
docs/handoff/CLEANUP-C4-compositions-compile.md added as operator
dispatch docs for the medium-scope wiring tasks.
2026-05-28 07:00:33 -07:00
Shay
fd6a544341
Merge pull request #414 from AssetOverflow/docs/epistemic-state-math-audit-plan 2026-05-28 04:21:29 -07:00
Shay
e0380ee91d docs(epistemic): align audit terminology with runtime enum 2026-05-28 04:17:19 -07:00
Shay
ec460c9068 docs(epistemic): add prior ADR compatibility gate 2026-05-28 04:13:05 -07:00
Shay
ee381f8e7e docs(epistemic): complete phase 1 state separation 2026-05-28 04:05:01 -07:00
Shay
48eb68d5d7 docs(epistemic): add math audit findings and taxonomy plan 2026-05-28 04:03:28 -07:00
Shay
4746476de3
Merge pull request #412 from AssetOverflow/feat/demo-flywheel-public-proof
feat(demo): core demo flywheel — public-proof reproduction of the loop
2026-05-28 02:43:23 -07:00
Shay
3f3f90ef11 feat(demo): core demo flywheel — public-proof reproduction of the loop
The repo is public. The thesis is *decoding, not generating* with
wrong=0 as the load-bearing invariant. The demo any visitor can run
to see the loop turn end-to-end on the canonical pack:

    git clone https://github.com/AssetOverflow/core
    cd core && uv pip install -e .
    core demo flywheel

Four falsifiable scenes:

  1. RATIFY    — apply_composition_claim writes source JSONL; RAT-1
                 auto-compile regenerates compositions.jsonl + bumps
                 manifest.composition_checksum
  2. LOAD      — composition_registry picks up the new entry on the
                 next runtime turn
  3. SOLVE     — "Lilibeth fills 6 baskets where each basket holds
                 50 strawberries. How many strawberries does Lilibeth
                 have?" admits via matcher → injector → admission →
                 candidate-graph and produces answer=300
  4. HAZARD    — case 0050 (wrong=0 canary) remains refused; no SAFE
                 composition category can convert it

All four scenes byte-deterministic. The canonical pack is read-only
throughout; the demo mutates only a synthetic test pack in a
tempfile.TemporaryDirectory. One-time recognizer seed is idempotent
(same content_digest each run → no duplicate proposal log entries).

Exit code 0 iff all scenes pass; --json for CI integration.

Also adds:
- README "Watch the flywheel turn — one command" section pointing
  to the demo + the coverage CLI (per-shape histogram + hazard pin)
- ProposalLog entry for the multiplicative_aggregate recognizer
  with extract_values=True (one-time operator seed)

Files:
- evals/flywheel_demo/run_tour.py (new) — the four-scene tour
- evals/flywheel_demo/__init__.py (new)
- core/cli.py — `flywheel` added to `core demo` choices + dispatch
- README.md — new "Quick Start" subsection
- teaching/proposals/proposals.jsonl — seeded recognizer
2026-05-27 21:33:54 -07:00
Shay
eb2ca1be0d
Merge pull request #410 from AssetOverflow/feat/teaching-coverage-cli
feat(D): core teaching coverage — per-shape admission histogram
2026-05-27 21:31:41 -07:00
Shay
f5ae970012 ci: trigger smoke + lane-shas 2026-05-27 21:20:01 -07:00
Shay
36f3dbfc4e fix(D): address Sourcery review findings on PR #410
Three review fixes:

1. Security: validate lane/split/version against ^[a-z0-9_]+$ before
   building the runner module name. The runner_args list is passed to
   subprocess.run without shell=True (no shell injection possible),
   but defense-in-depth blocks arbitrary token characters from
   reaching Python's -m module loader. Bad input now errors at the
   CLI boundary with a clear message.

2. Bug-risk: _classify_refusal docstring referenced a
   no_admissible_candidate bucket that the implementation never
   emitted. Aligned docstring with actual buckets
   (no_admissible_question / no_admissible_statement). Also made all
   matching consistently case-insensitive (was mixed — some checks
   used raw reason, one used .lower()).

3. Bug-risk: fetch_committed_baseline wrote to
   .git/coverage_baseline_tmp.json. Replaced with tempfile.mkstemp in
   the system temp dir — avoids (a) failures in non-git worktrees
   where .git is a file pointer, (b) concurrent-access collisions
   between simultaneous operators.

Tests (+3 new):
- test_classify_refusal_is_case_insensitive
- test_classify_docstring_matches_implementation_buckets
- test_fetch_committed_baseline_uses_system_temp

All 16 coverage tests green. Verified the validation:
  core teaching coverage --lane 'evil; rm -rf /'
  → ERROR: lane='evil; rm -rf /' must match ^[a-z0-9_]+$
2026-05-27 21:20:01 -07:00
Shay
d91ea3d36e feat(D): core teaching coverage — per-shape admission histogram
Brief D from PR #407. Closes the "flying blind on per-shape coverage"
gap identified in RAT-1's audit (finding 6).

After this PR, every operator can run a single command to see exactly
which refusal modes their work moved (or didn't), without re-eyeballing
report.json by hand.

Modules
-------
- teaching/coverage.py — pure aggregator:
  - _classify_refusal — maps each per-case refusal reason to a
    stable bucket (recognizer_empty_injection(<ShapeCategory>),
    no_admissible_question, no_admissible_statement,
    unexpected_question_count, other)
  - build_coverage_report — reads a lane's report.json + emits a
    CoverageReport with counts, refusal_taxonomy (sorted by count
    desc), case_0050_verdict, optional delta vs baseline
  - fetch_committed_baseline — uses `git show HEAD:<relpath>` to
    pull the baseline report.json for delta computation

- core/cli.py:
  - cmd_teaching_coverage — formats the report for terminal output
  - core teaching coverage [--lane gsm8k_math] [--split train_sample]
    [--version v1] [--use-reader] [--run] [--delta] [--json]

CLI output example
------------------
  Lane: gsm8k_math/train_sample/v1 (use_reader=True)
  Counts: correct=3 refused=47 wrong=0

  Refusal taxonomy:
     21  recognizer_empty_injection(discrete_count_statement)
      6  no_admissible_statement
      5  recognizer_empty_injection(multiplicative_aggregation)
      4  no_admissible_question
      4  recognizer_empty_injection(currency_amount)
      3  recognizer_empty_injection(rate_with_currency)
      2  recognizer_empty_injection(descriptive_setup_no_quantity)
      2  recognizer_empty_injection(temporal_aggregation)

  Wrong=0: ✓
  Case 0050 hazard pin: refused ✓

Tests (13 new)
--------------
tests/test_teaching_coverage_cli.py — classification narrowness,
counts aggregation, case 0050 verdict capture, delta computation,
missing-baseline path, missing-report error, taxonomy sort order,
wrong=0 invariant visibility via as_dict.

Suite results
-------------
core test --suite teaching -q → 106 passed (93 → +13)
core test --suite runtime  -q → 20 passed
core test --suite packs    -q → 127 passed
core eval gsm8k_math --split public → 150/150, wrong=0

Note on Brief E (lexical auto-compile): the audit was WRONG. The
lexicon loader (generate/comprehension/lexicon.py::load_lexicon)
reads from the per-category source files directly; the compiled
lexicon.jsonl is only a manifest-checksum pin, not the source of
truth at runtime. apply_lexical_claim() writes a new entry → next
turn the loader sees it. Brief E is a non-issue; closing without a
code PR.

Verified by direct test: stage a clone of the math pack, write a
synthetic lemma to drain_token.jsonl, clear the lexicon cache, load
again → new entry present. So 3 of the 5 audit gaps closed (A, D,
E-as-correction); B and C remain as the next operator dispatch
targets.

Independent of PR #406 (RAT-1) and PR #408 (WAVE-A). Based on main.
2026-05-27 21:20:00 -07:00
Shay
620c62140d
Merge pull request #409 from AssetOverflow/docs/comprehension-reader-audit
docs(brief-C): comprehension reader audit + ADR-0164 status update
2026-05-27 21:07:15 -07:00
Shay
a092d2e8c2
Merge pull request #411 from AssetOverflow/feat/contemplation-ratifiable-claims
feat(brief-B): enrich contemplation payload — composition_reclassification directly ratifiable
2026-05-27 21:06:36 -07:00
Shay
de221ffb47
Merge pull request #408 from AssetOverflow/feat/missing-injectors-wave-a
feat(wave-a): first non-DCS injector — multiplicative_aggregation w/ value extraction
2026-05-27 21:01:09 -07:00
Shay
15fe8a02e2 ci: trigger 2026-05-27 20:52:26 -07:00
Shay
7441b42bf5 feat(wave-a): first non-DCS injector — multiplicative_aggregation w/ value extraction
Addresses 5 of 47 train_sample "recognizer matched but produced no
injection" refusals (the largest single failure-mode bucket
identified in RAT-1's audit).

Modules
-------
- generate/recognizer_match.py:
  - _MULT_AGG_EACH_WEIGHING_RE — regex for "<Subject> <bake-verb>
    <M> <outer-noun>, each <weigh-verb>ing <N> <unit>" pattern
  - _try_extract_each_weighing_anchor — extracts M, N, subject,
    inner unit; emits pre-composed CandidateInitial(value=M*N) with
    composition_evidence so RAT-1's _composed_initial_admissible
    gate verifies INPUT tokens ground (preserves wrong=0)
  - _match_multiplicative_aggregation dispatches to the value
    extractor when spec carries extract_values=True; specs without
    that flag get the existing detection-only return path
    (byte-identical legacy behavior)

- generate/recognizer_anchor_inject.py:
  - inject_multiplicative_aggregation — new per-category injector;
    narrow by anchor.kind so ME-3/ME-4 additive/subtractive anchors
    (which share the same matcher entry point) continue to flow
    through composition_registry consult instead of WAVE-A's direct
    path
  - registered in _INJECTORS dict (2nd entry after DCS)

- core/cli.py:
  - seed-recognizer CLI gains --extract-values flag to opt the
    canonical_pattern into the value-extracting matcher path

Seeded artifacts
----------------
- proposals.jsonl: rat1-seed-4dc30608fb783bc7 — multiplicative_
  aggregation recognizer with anchor_kind=multiplicative_aggregate,
  extract_values=True, observed_units covering ounces/strawberries/
  questions/etc.

Live result on train_sample
---------------------------
- wrong == 0 preserved (3/47/0 baseline)
- Case 0050 hazard pin held
- public 150/150 preserved
- packs suite: 127 → 131 (+4 new WAVE-A tests, all green)
- teaching suite 93 unchanged
- runtime suite 20 unchanged

End-to-end synthetic solve (FIRST WAVE-A admission):
  "Lilibeth fills 6 baskets where each basket holds 50 strawberries.
   How many strawberries does Lilibeth have?"  → answer=300

Cases that moved (statement now admits; refusal shifted downstream):
- Case 0025 (Lilibeth): statement admits via WAVE-A; refusal moved
  to question parser ("If three of Lilibeth's friends pick the same
  amount, how many strawberries do Lilibeth and her friends pick in
  all?")
- Case 0047 (John bakes 12 macaroons): statement 1 admits; refusal
  moved to statement 2

Eval correct count unchanged because the QUESTION parser (and
multi-statement cross-sentence reasoning) is the next bottleneck.
RAT-1's audit identified that gap; WAVE-A closes the injector half.

The remaining 3 multiplicative_aggregation refusals (0006, 0013,
0045) have different shape patterns the WAVE-A regex does not yet
cover; they're follow-up matcher extensions in the same architecture.

Tests
-----
- tests/test_wave_a_multiplicative_aggregation_injector.py (10
  tests): each-weighing + each-basket-holds admission shapes,
  detection-only path preserved when extract_values absent,
  unobserved unit / pronoun / zero count refusals, end-to-end
  inject_from_match dispatch, the Lilibeth canary solve,
  wrong=0 preserved, case 0050 hazard pin

Stacks on PR #406 (RAT-1).
2026-05-27 20:50:04 -07:00
Shay
8bfdb4a7cc
Merge pull request #406 from AssetOverflow/feat/rat-1-compile-pack-and-seed
feat(RAT-1): close ratify→runtime gap + first live composition admission
2026-05-27 20:49:42 -07:00
Shay
193764e3fd feat(brief-B): enrich composition_reclassification payload to be directly ratifiable
Adds surface_pattern, composition_category, and polarity to the
proposed_change_payload for composition_reclassification proposals so
operators can call apply_composition_claim() without field synthesis.

Dispatch by missing_operator:
- quantity_extraction → multiplicative_composition + bound(count) × bound(unit_cost)
- multi_quantity_composition → additive_composition + bound(qty_a) + bound(qty_b)

All other change kinds (matcher_extension, injector_sub_shape,
frame_reclassification) keep the existing evidence-aggregation payload.
Legacy fields (evidence_count, group_key, modal_sub_type) preserved.

Adds tests/test_contemplation_ratifiable_payload.py with 11 tests
including a round-trip from decompose_audit → apply_composition_claim.
2026-05-27 20:46:10 -07:00
Shay
be85c66801 docs(brief-C): comprehension reader audit — zero eval delta diagnosis + next steps
Phase 1 and Phase 2 of the ADR-0164 reader are correctly implemented but
contribute zero eval admissions today. The bottleneck is lexicon coverage
(unknown verbs/nouns) and explicit Phase 2.1 scope gates (fractions), not
the all-or-nothing dispatch policy.

Produces COMPREHENSION-READER-AUDIT.md answering the five Brief C questions:
call trace, cognition-lane usage (none), bottleneck analysis, ADR promise
audit, and three falsifiable options (operationalize/relabel/retire).
Recommendation: relabel (status update) now; lexicon expansion next as the
highest-leverage first step toward actual eval lift.

Also updates ADR-0164 status from "Proposed" to "Partially implemented" with
a Current Status table and next-lift-path summary.
2026-05-27 20:45:31 -07:00
Shay
adf1f814f9
Merge pull request #407 from AssetOverflow/docs/post-rat1-parallel-briefs
docs(post-rat1): four parallel dispatch briefs (B/C/D/E)
2026-05-27 20:34:01 -07:00
Shay
85b8f0cd11 docs(post-rat1): four parallel dispatch briefs (B/C/D/E)
After RAT-1's architecture audit exposed five systematically
underbuilt components, A (the 4 missing injectors) is being taken by
this agent. The other four are parallel-safe and each gets its own
dispatch brief:

  B (Opus)   — contemplation produces ratifiable claims (medium)
  C (Sonnet) — comprehension reader audit + decision (small→medium)
  D (Sonnet) — core teaching coverage CLI (small)
  E (Codex)  — lexical ratification auto-compile (tiny)

Each brief carries: operator profile, branch name, base, dispatch
line, reads required FIRST, outcome inventory, hard requirements,
tests, truth test. Anti-regression invariants enumerated. All
parallel-safe — no shared file conflicts.

The dispatch DAG names ALL FIVE pieces with A in-flight as a peer.
2026-05-27 20:24:39 -07:00
Shay
d5c91e1ac1 feat(RAT-1): close ratify→runtime gap + first live composition admission
The user's question — "shouldn't we be running it multiple times so
it can learn? or is that part broken?" — exposed that the math
teaching loop's `ratify → admit` closure had been structurally
broken at the connector between operator ratification and runtime
visibility. The handlers wrote source files (compositions/, frames/)
that the runtime loader never read because no compile step
regenerated the runtime artifacts.

This PR fixes the gap end-to-end AND fires the first live composition
admission on the canonical pack.

Modules
-------
- language_packs/compile_pack.py — unified compile step that
  regenerates frames.jsonl + compositions.jsonl + updates
  manifest.{frame,composition}_checksum atomically. Idempotent.

- teaching/math_composition_ratification.py — apply_composition_claim
  now calls compile_pack at end of successful ratification. Closes
  the source-file→runtime-artifact gap.

- teaching/math_frame_ratification.py — same auto-compile wire for
  apply_frame_claim.

- generate/math_candidate_parser.py — CandidateInitial gains optional
  composition_evidence Mapping field. When populated, signals the
  candidate was produced by a registry-gated composition (ADR-0169);
  the value/unit/entity are DERIVED arithmetic over grounded inputs.

- generate/math_candidate_graph.py — new _composed_initial_admissible
  predicate that branches on composition_evidence. Wrong=0 preserved
  by requiring each composition INPUT token (count, amount) to ground
  in source_span literally; the derived value is admitted because the
  arithmetic over grounded inputs is deterministic.

- generate/math_candidate_graph.py — discourse-level prior_subject
  tracking: capture proper-noun subjects from ALL statement sentences
  (including ADR-0136.S.0 context-filler sentences that get filtered
  out before the candidate loop). Without this, "John adopts a dog"
  (no numbers) is dropped and the cross-sentence subject resolver for
  case 0019 sees prior_subject=None.

- generate/recognizer_match.py — all four composition matchers
  (ME-1 currency-per-unit same-sentence, ME-2 cross-sentence, ME-3
  additive, ME-4 subtractive) now populate composition_evidence in
  CandidateInitial. Also added standalone " each " / " apiece " to
  _PER_UNIT_TOKENS so currency_amount detection-only matcher refuses
  per-item costs instead of swallowing them.

CLIs
----
- core teaching compile-pack — explicit operator surface for
  regenerating runtime artifacts. JSON output for CI integration.

- core teaching seed-recognizer — operator surface for seeding a
  RatifiedRecognizer entry in the proposal log for a given
  (shape_category, anchor_kind). Writes created + transition(accepted)
  events directly via ProposalLog._append.

Seeded artifacts (the actual loop closure)
------------------------------------------
- proposals.jsonl: new rat1-seed-48dd2673d6ad673d RatifiedRecognizer
  entry for shape_category=rate_with_currency,
  anchor_kind=currency_per_unit_composition.

- compositions/multiplicative_composition.jsonl: ratified
  "bound(count) × bound(unit_cost)" affirms entry sourced from
  case 0019 evidence.

- compositions.jsonl + manifest.composition_checksum: compiled
  runtime artifact + manifest pin (RAT-1 auto-compile).

Live result on train_sample
---------------------------
- wrong == 0 preserved (3 correct / 47 refused / 0 wrong)
- Case 0050 hazard pin holds (refused)
- public split 150/150 preserved
- Case 0019 sentence 1 ("requires 3 vet appointments, which cost
  $400 each") NOW ADMITS via composition. Previously refused with
  "recognizer matched but produced no injection". The refusal moved
  downstream to sentence 2 (a different currency_amount detection
  bottleneck that is its own follow-up).

This is the first time a composition ratification on the canonical
pack actually reaches the runtime. The flywheel turned one
revolution.

Tests
-----
- tests/test_rat1_end_to_end_admission.py — 4 new live tests:
  composition statement admits on isolated synthetic problem, case
  0019 cross-sentence admission, wrong=0 preserved on train_sample,
  case 0050 hazard pin.

- tests/test_consumption_empty_registry_no_op.py — refactored to use
  isolated synthetic packs (the canonical pack may now carry ratified
  entries).

- tests/test_math_{frame,composition}_ratification.py — updated
  "manifest checksum unchanged" tests to "lexicon checksum
  preserved" semantics: RAT-1 auto-compile may add the new optional
  checksum fields; pre-existing lexicon checksum stays untouched.

Suite results: teaching 93, packs 131 (+4), runtime 20. All green.
2026-05-27 20:09:47 -07:00
Shay
3e196df097
Merge pull request #404 from AssetOverflow/feat/matcher-extension-me5-integration
feat(matcher-extension/ME-5): integration smoke + ME-1..ME-5 milestone
2026-05-27 19:33:41 -07:00
Shay
613a8e93da
Update position_paper.md 2026-05-27 18:27:18 -07:00
Shay
838fa90350 ci: trigger smoke + lane-shas 2026-05-27 17:35:10 -07:00
Shay
9b8f6bb991 feat(matcher-extension/ME-5): integration smoke + ME-1..ME-5 milestone
Final PR of the matcher-extension wave. Ships:

1. tests/test_me5_all_categories_integration.py — 4 new tests:
   - test_all_three_canaries_admit_through_full_pipeline: stages a
     pack with all three SAFE_COMPOSITION_CATEGORIES entries +
     ratifies, runs Maria/Sam/Tom canaries through matcher →
     inject_from_match, asserts admission for all three
   - test_partial_pack_only_admits_present_categories: refusal-
     preferring when only one category is ratified
   - test_all_safe_categories_have_extension_admission: pins that
     SAFE_COMPOSITION_CATEGORIES is exactly the three covered
     categories (breaks if future ADR widens without matcher)
   - test_falsifies_uniformly_suppresses_across_categories:
     polarity discipline holds across all three matchers

2. docs/handoff/ME1-ME5-MILESTONE.md — wave milestone doc:
   - architecture diagram (audit → ratify → compile → load →
     match → consult → admit)
   - SAFE_COMPOSITION_CATEGORIES coverage matrix
   - invariants preserved across the entire stack
   - scope boundary (what does NOT fire yet — RAT-1 follow-up)
   - recommended next dispatch

3. Test registration in core/cli.py packs suite.

Across the full ME-1..ME-5 stack:
- 5 stacked PRs (#400/#401/#402/#403/#404)
- 1 foundation PR (#398 — consumption wiring)
- 114 new tests, all green
- packs suite 127 passed
- core eval gsm8k_math --split public → 150/150, wrong=0
- All three SAFE_COMPOSITION_CATEGORIES have matcher extensions

Anti-regression invariants preserved across the entire stack:
- wrong == 0 on public split
- Case 0050 hazard pin (parametrized over all three categories)
- ADR-0166 — no new eval lanes
- ADR-0167 partition — no cognition imports
- ADR-0169 mutation boundary — registry is a gate, not arithmetic
- All matcher detection paths byte-identical
- engine_state/* never committed
- SAFE_COMPOSITION_CATEGORIES enforced at write AND load
- polarity falsifies honored uniformly

Live train_sample admission requires operator-seeded ratifications
(RAT-1 follow-up). Wiring is end-to-end correct, verified by ME-5
integration tests.

Memory: milestone-me1-me5-matcher-extensions-complete saved.

Stacks on PR #403 (base: feat/matcher-extension-subtractive).
2026-05-27 17:35:10 -07:00
Shay
1be7e0200f
Merge pull request #403 from AssetOverflow/feat/matcher-extension-subtractive
feat(matcher-extension/ME-4): subtractive composition matcher
2026-05-27 17:35:05 -07:00
Shay
4e751bc496 ci: trigger 2026-05-27 17:24:02 -07:00
Shay
11d7e0b607 feat(matcher-extension/ME-4): subtractive composition matcher
Extends _match_multiplicative_aggregation with a new branch keyed on
anchor_kind="subtractive_quantity_composition". Pattern:

  <Subject> <init-verb> <N> <unit>(,| then| ;| and then| and)
  <sub-verb> <M> <unit>

Same-unit only. Emits a pre-composed CandidateInitial(N - M, unit) +
composition_shape="bound(initial) − bound(removed)".

Verb whitelists:
  initial: had/has/got/owns/owned/earned/saved/made/received/bought
  removal: lost/spent/gave/donated/paid/removed/sold/used/consumed

Removal verbs accept an optional " away" suffix ("gave away 20 apples").

Refusal-preferring discipline:
- count_b >= count_a → refuse (non-negative remainder; wrong>0 hazard)
- Pronoun / determiner subject → refuse
- Cross-unit → refuse (no v1 conversion table)
- Unobserved unit → refuse
- Unknown initial/removal verb → refuse

Tests (17 new, all green):
- canonical subtractive ("Sam had 50 apples, gave 20" → 30)
- then/and connectives
- gave away variant
- negative + equal remainder refused (hazard pin)
- pronoun + determiner subject refused
- cross-unit refused
- unobserved unit refused
- unknown initial/removal verbs refused
- additive (ME-3) path unaffected
- multiplicative_aggregate detection unaffected
- anchor audit fields complete
- end-to-end via composition_registry: affirms admits, falsifies suppresses

Registered in core/cli.py "packs" suite.

core test --suite packs -q → 123 passed (106 + 17 new)
core eval gsm8k_math --split public → 150/150, wrong=0

Anti-regression invariants preserved across ME-1..ME-4 stack:
- wrong == 0 on gsm8k_math public 150/150
- Case 0050 hazard pin holds
- ADR-0166 — no new eval lanes
- ADR-0167 partition — no cognition imports
- All prior matcher paths unaffected (test pins)
- engine_state/* not committed
- All three SAFE_COMPOSITION_CATEGORIES (multiplicative / additive /
  subtractive) now have matcher extensions wired

Stacks on PR #402 (base: feat/matcher-extension-multi-quantity).
2026-05-27 17:23:35 -07:00
Shay
da1a791d8c
Merge pull request #402 from AssetOverflow/feat/matcher-extension-multi-quantity
feat(matcher-extension/ME-3): additive composition matcher
2026-05-27 17:23:31 -07:00
Shay
b6f7ea8d00 docs: add one-mutation-path invariant, non-hardening invariant, and fabrication loop to position paper 2026-05-27 17:18:44 -07:00
Shay
1215944a20 feat(matcher-extension/ME-3): additive composition matcher
Extends _match_multiplicative_aggregation with a new branch keyed on
anchor_kind="additive_quantity_composition". When a statement carries
"<Subject> <verb> <N> <unit> and <M> <unit>" (same unit) shape, emits
a pre-composed CandidateInitial(N+M, unit) and publishes
composition_shape="bound(qty_a) + bound(qty_b)".

Subject binding under Option A (refuse on pronoun / determiner / no
proper-noun head). Cross-sentence subject support (mirroring ME-2)
is deferred — not needed for the v1 ME-3 canaries.

Verb whitelist: lost / gained / earned / saved / made / paid / spent /
bought / sold / added / removed / received. Verbs that route through
CandidateInitial.matched_anchor's existing post-init whitelist;
unmapped verbs fall back to "had".

Unit normalization: rstrip 's' for plural matching (pounds vs pound).
Cross-unit composition refused — no conversion table in v1.

Tests (15 new, all green):
- same-unit admission with sum
- pronoun subject refuses
- determiner subject refuses
- cross-unit refuses
- unobserved unit refuses
- zero count refuses
- plural normalization
- unknown verb refuses
- multiplicative_aggregate detection path unaffected
- wrong anchor_kind refuses
- anchor audit fields complete
- source_span substring invariant
- no match returns None
- end-to-end admission via composition_registry
- end-to-end falsifies suppresses

Registered in core/cli.py "packs" suite. core test --suite packs -q →
106 passed (91 existing + 15 new).

Anti-regression invariants preserved:
- wrong == 0 on gsm8k_math public 150/150
- Case 0050 hazard pin holds
- ADR-0166 — no new eval lanes
- ADR-0167 partition — no cognition imports
- Original multiplicative_aggregate detection path byte-identical
- ME-1 currency-per-unit path unaffected
- ME-2 cross-sentence path unaffected
- engine_state/* not committed

Live train_sample admission requires the same operator workflow as
ME-2: a RatifiedRecognizer for the new anchor_kind + composition_registry
entry for "bound(qty_a) + bound(qty_b)" under additive_composition.
Without those, the wiring is correctly positioned but dormant — no
regression in the live eval.

Stacks on PR #401 (base: feat/matcher-extension-cross-sentence-subject).
2026-05-27 17:12:34 -07:00
Shay
8426ba46c0
Merge pull request #401 from AssetOverflow/feat/matcher-extension-cross-sentence-subject
feat(matcher-extension/ME-2): cross-sentence subject binding for composition
2026-05-27 17:12:28 -07:00
Shay
c650bf9d1f
Merge pull request #405 from AssetOverflow/docs/paper-and-license-refresh
docs(paper+license): refresh ahead of going public
2026-05-27 17:10:26 -07:00
Shay
a2c39282c5 ci: trigger smoke + lane-shas 2026-05-27 17:01:51 -07:00
Shay
8a9b51af9e feat(matcher-extension/ME-2): cross-sentence subject binding for composition
Admits case 0019's composition sentence via prior_subject resolved
from upstream sentences. Stacks on PR #400 (ME-1).

Modules
-------
- generate/recognizer_match.py:
  - _CROSS_SENTENCE_COMPOSITION_RE — regex for "requires N noun, which
    cost(s) $X each" (no subject prefix)
  - try_extract_cross_sentence_composition_anchor(statement, spec,
    prior_subject) — refuses on None / empty / pronoun prior_subject;
    publishes the same composition_shape + composed_initial payload as
    ME-1, sourced via prior_subject
  - extract_proper_noun_subject(statement) — head proper-noun extractor
    used by callers to track running prior_subject; rejects determiners,
    sentence-initial connectors (After/How/Every/...), and pronouns
  - match() dispatcher gains keyword-only prior_subject parameter;
    when a per-category matcher returns None for a RATE_WITH_CURRENCY
    recognizer with currency_per_unit_composition anchor_kind AND
    prior_subject is supplied, the cross-sentence helper is tried as
    a fallback

- generate/math_candidate_graph.py:
  - tracks _prior_subject across statement_sentences iteration
  - passes prior_subject to recognizer_match.match()
  - updates _prior_subject from each sentence's head proper-noun

Tests (19 new, all green)
-------------------------
- test_me2_cross_sentence_subject.py (15 tests)
  - subject extraction narrowness (proper noun / determiner / connector
    / pronoun / non-string)
  - cross-sentence helper happy path + refusals (None, empty, pronoun,
    unobserved currency / per_unit, wrong anchor_kind, zero count,
    multi-match)
  - source_span substring invariant
  - kind label "currency_per_unit_composition_cross_sentence"

- test_me2_case_0019_admits.py (4 tests)
  - case_0019_admits_with_prior_subject_john — the truth test
  - case_0019_refuses_without_prior_subject — ME-1 Option A still holds
  - case_0019_refuses_with_pronoun_prior — refusal-preferring
  - maria_same_sentence_unaffected_by_prior_subject — ME-1 path intact

Registered in core/cli.py "packs" suite.

Suite results
-------------
core test --suite packs    -q → 91 passed (existing + ME-1's 21 + 19 new)
core test --suite runtime  -q → 20 passed
core eval gsm8k_math --split public → 150/150, wrong=0

Scope boundary
--------------
The wiring is load-bearing AND tested end-to-end via synthetic
recognizer registry (test_case_0019_admits_with_prior_subject_john
proves the full chain match → inject → admit).

For the LIVE train_sample case 0019 admission, two ratifications must
also be seeded (operator workflow outside this PR's code scope):

  1. A RatifiedRecognizer in the proposal log with shape_category=
     RATE_WITH_CURRENCY and canonical_pattern carrying
     anchor_kind="currency_per_unit_composition"
  2. A composition_registry entry for "bound(count) × bound(unit_cost)"
     under multiplicative_composition with polarity=affirms

With both ratifications in place, case 0019 admits via the wiring
this PR ships. Without them, the live train_sample run remains at
the 3/47 baseline (preserved; no regression).

Anti-regression invariants preserved
------------------------------------
- wrong == 0 on gsm8k_math public
- Case 0050 hazard pin holds (no _COMPOSITION_SUBJECT_BUY_RE or
  _CROSS_SENTENCE_COMPOSITION_RE match on case 0050's sentences)
- ADR-0166 — no new eval lanes
- ADR-0167 partition — no cognition imports
- ME-1 Maria same-sentence path byte-identical (test pins)
- Existing currency_per_unit_rate path unaffected (test pins)
- prior_subject is keyword-only on match() (additive; old callers
  unaffected)
- engine_state/* not committed

Stacks on PR #400 (base: feat/matcher-extension-currency-per-unit-composition).
2026-05-27 17:00:08 -07:00
Shay
0a682f065f
Merge pull request #400 from AssetOverflow/feat/matcher-extension-currency-per-unit-composition
feat(matcher-extension/ME-1): currency-per-unit composition admission
2026-05-27 16:59:54 -07:00
Shay
6d9ff8119f docs(paper+license): refresh ahead of going public
Two pre-public-release refreshes:

1. docs/position_paper.md §4 — refresh refusal taxonomy

The previous table listed an older taxonomy (fraction_operand /
compound_comparative / etc.) that does not match the current
candidate-graph eval output. Replaced with the actual taxonomy
emitted by the train_sample runner — refusals are categorized by
shape (recognized_but_uninjectable + per-ShapeCategory) which ties
directly to the architecture's recognizer/injector concepts.

Also refreshed §5 "Honest Gaps" to describe the active frontier in
terms of injector coverage (the registry-driven composition path)
rather than parser-level grammar extensions. The 47/50 refusal
count, 3/50 correct count, and wrong=0 invariant are unchanged.

2. COMMERCIAL_LICENSE.md — enumerate subsystems explicitly

The previous "vault memory / versor engine / epistemic teaching
loop / ingest / admissibility gate / holonomy encoder" listing
predated the math-domain composition wave. Expanded the list to
cover the full current architecture:

- versor engine + Cl(4,1) algebra
- vault + exact CGA recall
- ingest gate
- admissibility gate + Forward Semantic Control
- holonomy encoder
- epistemic teaching loop (ADR-0055..0057)
- contemplation loop + decomposer (ADR-0080, ADR-0172)
- math-domain ratification handlers (ADR-0167..0169)
- composition-pattern registry + registry-driven injector
- audit-as-teaching-evidence corridor
- identity + safety pack subsystems
- language-pack compiler + verified manifest checksums
- Logos articulation + three-language vocabulary manifold
- Rust algebra backend
- any derived or successor implementation

Explicit "enumerated subsystems are illustrative, not limiting"
clause covers any future module + ADR-ratified contract under the
same terms. New subsystems are covered automatically.

LICENSE (the non-commercial form) is unchanged — it already covers
"Software" broadly. CLAIMS.md is auto-generated and was verified
current (regeneration produces no diff).
2026-05-27 16:59:18 -07:00
Shay
af3bcbcbf6 docs: update authorship to Josh Shay / ACB Content across license and position paper 2026-05-27 16:48:22 -07:00
Shay
c5a3463178 docs: position paper, non-commercial license, and commercial licensing terms 2026-05-27 16:48:22 -07:00