Commit graph

1004 commits

Author SHA1 Message Date
Shay
212b078f72 feat(language): add en core syntax lexicon 2026-05-30 19:13:40 -07:00
Shay
ae84ce8641 feat(language): add en core syntax pack manifest 2026-05-30 19:12:45 -07:00
Shay
9df1e6522b
feat(adr-0195): GSM8K product promotion bridge — serving 4/46/0 → 6/44/0, wrong=0 (#500)
Narrow product promotion boundary (`generate/derivation/product_bridge.py`)
wired into `generate/math_candidate_graph.py`: only complete pure-product
derivations with a product-target question and no known hazard surface lift
from the sealed pooled derivation reader into serving.

- Serving train_sample: 4/46/0 → 6/44/0, wrong=0; case 0050 still refused.
- Renumbered from the collided ADR-0194 (labeled-container, #499) to ADR-0195
  and rebased onto current main.

CI: smoke + verify-pinned-lane-SHAs green on the merge commit.
2026-05-30 17:33:56 -07:00
Shay
84db129629
feat(adr-0194): labeled-container subject entity shape — 'Jar A has N' parses, wrong=0-proven (substrate) (#499)
GSM8K labels containers/regions with a trailing single-letter or short-numeric
label ('Jar A has 28 marbles', 'Section G has 10 cars', 'District 2 has 19
voters'); the initial-possession entity slot captured only 'Jar' and the label
broke the match. Adds a separate sibling pattern _INITIAL_HAS_LABELED_RE
(mirroring ADR-0136.S.4 localisation) that REQUIRES the label, so the global
_ENTITY is unchanged and bare subjects yield no duplicate candidate.

- Composes with ADR-0193 aggregate question: 'Jar A has 28 marbles. Jar B has
  12 marbles. How many marbles are there in total?' -> 40.0.
- 0 real-corpus metric flip (honest substrate): the one real multi-container
  aggregate additionally needs comparative + multiplicative + lowercase-ref.
- wrong=0 HOLDS full corpus (7,473 q); train_sample byte-identical 4/46/0;
  synthetic-registry capability-axis gate + G5 lane green; smoke 67 passed.
- Label bounded by the possession verb: multi-word nouns ('Jar Apple') do NOT
  match. wrong=0 held downstream by completeness + round-trip + disagreement.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 16:56:09 -07:00
Shay
39bf549e94
feat(adr-0193): aggregate existential question frame — 'are there <cue>' composes, wrong=0-proven (#498)
Extends ADR-0131.G.5's total-across aggregate branch with the existential
verb frame 'How many <unit> are there <cue>?' over the SAME closed cue
vocabulary (no cue-set widening). The solver already sums entity=None
total-across; the wall was purely the question parser's verb-frame coverage.

- Composes end-to-end: 'Jamie has 28 marbles. Kyle has 12 marbles. How many
  marbles are there in total?' -> 40.0 (load-bearing, not inert).
- 23 real GSM8K problems now parse the question (advance past the Q-wall to
  the statement-parse wall).
- wrong=0 HOLDS on the full 7,473-q corpus; train_sample byte-identical
  4/46/0; no metric delta (composition-wall lesson, third instance).
- Cross-ADR discipline: 'What is the total number of <unit>?' is DEFERRED,
  not contradicted — ADR-0131.G.5 pins it as a refusal probe; promoting it
  must amend that ADR's closed-cue contract. test_total_number_of_still_deferred
  locks the boundary.

Firewall: completeness guard (ADR-0191) + question round-trip refuse conjoined
('dogs and cats') and derived ('animal legs') units. G5 lane + synthetic-registry
wrong=0 capability-axis gate green; smoke 67 passed.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 16:35:22 -07:00
Shay
a7024bb1f8
feat(adr-0192): open discrete_count noun class — 8x statements parse, wrong=0-proven (substrate) (#497)
The discrete_count matcher gated the counted noun on a CLOSED ratified set
(observed_counted_nouns): 'Betty has 24 marbles' matched, 'Randy has 60 mango
trees' / 'Sam has 12 red apples' did not — purely because the noun was unseen.

Open the single-anchor possession/acquisition path to an open noun phrase
(adjective* + 1-3 word head, bounded by a stop-word lookahead so it never
swallows a trailing PP), keeping every other narrowness layer (proper-noun
subject, verb whitelist, single numeric token, no clause-split). Closed
observed nouns still match (capitalized compounds preserved); compound
enumeration stays closed.

Safe because ADR-0191 moved the wrong=0 guarantee downstream: an open-vocab
mis-parse hits the completeness guard + round-trip + branch-disagreement.
Proof: full real corpus 61->494 discrete_count anchors (8x), wrong=0 HOLDS,
zero confabulations.

Substrate PR — 0 metric delta by design (train_sample byte-identical 4/46/0;
the problems still need composition downstream). Value: the foundation every
discrete_count flip consumes, and empirical proof open-vocab is firewall-safe.

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 16:06:25 -07:00
Shay
25580e18b0
fix(adr-0191): candidate-graph completeness guard — real-corpus wrong 5→0 (#496)
* fix(adr-0191): candidate-graph completeness guard — real-corpus wrong 5->0

The candidate-graph reader (serving) checked grounding + round-trip but had
no completeness obligation, so problems whose later clauses failed to parse
emitted a partial reading. Over the full 7,473-question real GSM8K train
split this confabulated 5 answers (wrong!=0) the 47-case train_sample hid;
2 were regressions from #488.

Add the missing admissibility leg (mirrors the derivation reader's verify.py):
every source quantity (all statements + question) must be consumed by the
chosen reading, else refuse. Refusal-only -> cannot create a wrong answer.
Number-sense is pack-authoritative (en_numerics_v1 parse_compound_cardinal +
lookup_multiplier + all 6 currency symbols) so it never disagrees with the
engine; aggregating initials expose consumed_value_tokens provenance.

Evidence: real-corpus wrong 5->0, correct held at 4; train_sample byte-
identical 4/46/0; G1-G5+S1+G3.1 green; smoke 67 passed; math_teaching_corpus
lane byte-identical.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

* feat(adr-0191): committed full-corpus GSM8K microscope (standing wrong=0 + coverage instrument)

Promotes the throwaway tmp/ microscope that found the 5 confabulations into a
committed tool. Runs the canonical serving reader over any GSM8K corpus and
reports failures-first: correct/wrong/refused, every wrong answer by name,
refusal families, and the no-injection per-category coverage map that ranks
which injector to build next by real frequency.

Default corpus is the committed 47-case train_sample (always available);
--corpus path runs the full real split. This is the ADR-0191 follow-up: re-run
after every capability PR, not just train_sample — a flip is only real if it
does not widen the confabulation surface.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
2026-05-30 15:45:07 -07:00
Shay
0770648257
feat(GSM8K): comprehension reading → first metric move 3/47/0 → 4/46/0 (#488)
* feat(adr-0189): comparative reading — anchor-verb widening + multi-word units

The candidate-graph comparative extractor (ADR-0131.G.2) read only has/have +
single-word units, so real-GSM8K comparatives ('Brooke does three times as many
jumping jacks as Sidney') didn't parse — a dark statement in 17 places blocking
15 of the 47 refused train_sample cases, despite the ADR-0123 solver already
supporting compare_additive/compare_multiplicative.

Widens the anchor-verb set (reusing legacy vetted lemmas + does/collected/
gained/studied…), EXCLUDING polarity-inverting verbs (lose/spend/give/sell/win)
to preserve wrong=0; admits 1-2 word units via the existing multi-word
_unit_grounds branch. Feeds the existing solver unchanged.

wrong=0 proven: G2_comparatives 29/29, G3 20/0, G4 32/32, train_sample 3/47/0
byte-identical; polarity-inverting verbs proven refused (failing-under-violation).
Chain composes correctly in isolation (146 -> 438). Flips 0 cases ALONE — every
comparative case needs a composing partner (aggregation / multi-word-noun
injection); this ships the component, not yet a flip.

- generate/math_candidate_parser.py: _comparison_anchor_verb widening + 1-2 word
  unit slots in the two multiplicative comparative regexes.
- tests/test_adr_0131_G2a_*: 5 tests incl. polarity-inversion wrong=0 guards.
- docs/decisions/ADR-0189: gap, change, wrong=0 evidence, honest scope.

* feat(adr-0189a): first metric move 3/47/0 -> 4/46/0 (case 0024, comprehension-composed)

Case 0024 now SOLVES (answer 438) by composing three general comprehension
capabilities feeding the unchanged ADR-0123 solver:
  1. day-of-week count enumeration: Sidney = 20+36+40+50 = 146
     (_day_enumeration_candidates; derived sum grounds via first count token,
      mirroring _embedded_quantifier; closed to the 7 day names)
  2. comparative reading (ADR-0189): Brooke = 3 x Sidney
  3. activity question 'How many <unit> did <Entity> <verb>?' (_Q_DID_RE)
Plus do/does/did added to the CandidateInitial anchor whitelist (production-
possession), admitted only via the closed day-enumeration shape.

wrong=0 PROVEN across every lane: all 8 capability axes wrong=0 (G2_comparatives
29/29, G3 20/0, G4 32/32, G5/S1/S3/S4 all pass), train_sample 4/46/0 wrong=0,
verify_lane_shas exit 0 (no pinned lane changed), generate_claims --check OK.
872 tests pass; new tests are failing-under-violation incl. wrong=0 guards
(non-day comma list not summed; polarity-inverting comparative verbs refused).

Re-baselined report.json + train_sample_coverage_report.json (latter also clears
pre-existing reason drift) + CLAIMS.md to the new 4/46/0 metric. Decode-not-guess:
0024 solved by READING its structure, not storing an answer. Remaining pre-existing
failures (G3 committed-report, telemetry) unrelated, fail on pristine main.

- generate/math_candidate_parser.py: day-enum extractor + _Q_DID_RE + does-anchor.
- tests/test_adr_0189a_day_enum_activity.py: 5 tests (incl. end-to-end 0024=438).
- docs/decisions/ADR-0189a + report.json/coverage/CLAIMS re-baseline.
2026-05-30 09:21:48 -07:00
Shay
a719f1b58c
feat(adr-0184): extract semantic-state helper seam S1 (#490)
* feat(adr-0184): add semantic-state helper package

* feat(adr-0184): add referent binding helpers

* feat(adr-0184): add change cue helpers

* refactor(adr-0184): use semantic-state helpers in accumulation

* test(adr-0184): cover semantic-state helper guards
2026-05-30 08:36:02 -07:00
Shay
390b6f376d
docs: semantic-state transition blueprint and ADR-0184 scope (#489)
* docs: add semantic state transition blueprint

* docs: add ADR-0184 semantic state transitions

* docs: add RB-GSM solver design input notes
2026-05-30 08:35:50 -07:00
Shay
171b1cd7a5
feat(adr-0186): sealed candidate-graph injector lane + topology correction (#487)
* docs(adr-0186): sealed candidate-graph injector lane (resume ADR-0170 W2-W5 under ADR-0175 seal)

Topology audit found two disjoint GSM8K readers: the candidate-graph reader
owns the official 3/47/0 metric and already has divide/multiply/compare; its
wall is the recognizer->injection coverage gap (ADR-0170 W2-W5 backlog), not
arithmetic. The derivation reader (resolve_pooled) is a separate sealed organ
that cannot reach the goal without an unbuilt Phase-5 bridge.

ADR-0186 reconciles ADR-0170's injector roadmap with ADR-0175's serving seal:
develop W2-W5 injectors behind a default-off 'sealed' flag on inject_from_match,
measured on a new report_sealed.json, with frozen 3/47/0 byte-identical until a
reviewed Phase-5 promotion. wrong=0 gated on both paths.

Live-loop instrumentation found a schema-vs-extraction split: rate_with_currency
and temporal_aggregation matchers already extract full anchors (blocked on the
CandidateRate union / apply_rate primitive), while discrete_count/currency/
multiplicative are blocked on matcher extraction. And no refused case is one
injector away - every case is multi-statement, so the unit of measurable
progress is a target case's complete injector+composition set. Sequencing:
seal mechanism first, CandidateRate schema next, then a first complete
target-case unlock.

* feat(adr-0186): sealed candidate-graph injector lane mechanism (default-off)

Adds the seal mechanism for developing ADR-0170 W2-W5 injectors without
mutating the frozen serving metric:

- inject_from_match(match, sentence, *, sealed=False): when sealed=True,
  consults the new _SEALED_INJECTORS table first; default-off never touches it.
- _SEALED_INJECTORS: empty at land (this PR ships the mechanism; the first
  sealed capability is the CandidateRate schema, ADR-0186 §5.3).
- parse_and_solve(text, *, sealed=False): threads the flag to the per-statement
  injection site. The seal is injector eligibility, not a forked reader.

wrong=0 guarantees (tests/test_adr_0186_sealed_injector_lane.py, failing-
under-violation): empty seal is a strict no-op; a registered sealed injector
admits ONLY under sealed=True and is invisible to the frozen path; train_sample
report stays 3/47/0. Verified: coverage probe 3/47/0 byte-identical, smoke +
architectural invariants green, 288 candidate-graph/recognizer tests pass (2
pre-existing failures unrelated, confirmed on unmodified main).

* docs(adr-0185): mark superseded by ADR-0186 (premise refuted by topology audit)

The topology audit proved ADR-0185's premise ('the engine cannot divide') is
true only of the derivation reader, which is disjoint from the candidate-graph
reader that owns the official 3/47/0 metric. The goal organ already divides;
its wall is injection coverage. ADR-0185 is retained as a record, not
implemented; cross-referenced from ADR-0186 §header.
2026-05-30 08:35:43 -07:00
Shay
e195a229c9
feat(adr-0184): distinct-unit product rule — sealed reader wrong 13→8 (#486)
* docs(adr-0184): scope the distinct-unit product rule — cut the product-of-all over-commit

The 47-refusal coverage diagnostic surfaced that the headline 3/47/0 (serving
recognizer) hides the sealed comprehension reader's real state: resolve_pooled over
the 50 real train_sample cases is 2 correct / 13 WRONG / 35 refused. The confuser
probe's wrong=0 was a misleading proxy — all 13 real wrongs are the whole-text
product-of-all, the unique complete candidate, committed unopposed.

Scopes the first lever, decided by MEASURING candidate refusal rules against the real
metric (correct up, wrong down on train_sample):

  baseline                         2 / 13 / 35
  distinct-unit product (chosen)   2 /  8 / 40   <- cuts 5, zero coverage loss
  product spans >1 clause          1 /  4 / 45   <- destroys correct 0003
  drop all products                0 /  2 / 48

The distinct-unit rule: multiply/divide may compose DISTINCT units but a multiply
step whose operand repeats a non-empty unit already in the product (apples x apples,
cards x cards) is unit-incoherence -> refuse (unit^2 is never the answer). Empty-unit
operands exempt (0003 multiplies a blank-unit 0.75). Dimensional, not lexical
(ADR-0165-safe); refines verify.py clause 3 shared by self_verifies + classify.

Honest scope: 13->8, NOT 0. The remaining 8 are distinct-unit products in the wrong
shape (rate problems) = cue precision (ADR-0177 CP-2b), the next lever, NOT to be
faked with a per-case rule. Establishes the real scoreboard (resolve_pooled over
train_sample) and notes the ratification bridge (ADR-0175 Phase 5) as the separate
dependency for any of this to reach the serving headline.

Spec only; serving 3/47/0 untouched (verify is not on the serving path).

* feat(adr-0184): distinct-unit product rule — sealed reader real-GSM8K wrong 13->8

Cuts the over-eager product-of-all on real GSM8K. The sealed comprehension reader
(resolve_pooled over train_sample) was 2 correct / 13 WRONG / 35 refused; all 13 are
the whole-text product-of-all committed unopposed (0042->2.4M, 0048->19200,
0001->14400). This is the first lever measured against the REAL metric (resolve_pooled
over train_sample), not the curated confuser count.

Mechanism (verify._is_repeated_unit_product + classify_derivation downgrade):
a pure multiplicative chain whose operands repeat a non-empty unit forms unit^2
(apples x apples, cards x cards) -- never the answer; it is the product-of-all
multiplying independent groups. Such a product is classified `exempt`
(commit-INELIGIBLE), NOT removed. Empty units exempt (0003 multiplies blank-unit
0.75); divide exempt (feet/feet = legitimate count). Dimensional, not lexical
(ADR-0165-safe).

Implementation finding (folded into ADR §3.1): the naive version put the predicate
in the shared _base_reasons gate, which DROPPED the product and regressed the
confuser probe 1->3 -- the disguised-polarity 0001/0003 refuse only because the
coins x coins product DISAGREES with the coins + coins accumulation reading; dropping
it unmasked the additive reading as a unique wrong commit (80/30). The fix is the
downgrade: keep it as a commit-ineligible `exempt` candidate so it still forces the
disagreement. Pinned by test_downgrade_not_removal_preserves_disagreement_refusal.

Evidence (sealed lane; chat/ does not import verify -> serving frozen):
- resolve_pooled over train_sample: 2 correct / 8 wrong / 40 refused (was 2/13/35);
  the 5 repeated-unit products (0001/0017/0042/0045/0048) now refuse, 0003/0021 kept.
- confuser probe: wrong unchanged (no 0001/0003 regression), positives still solve.
- serving train_sample 3/47/0 and practice (accumulation + search) 3/47/0
  byte-identical; self_verifies/_base_reasons unchanged so search lanes are untouched.
- 171 derivation/pool/verify tests + 40 architectural invariants green.

Honest scope: 13->8, NOT 0. The remaining 8 (0011/0016/0018/0019/0025/0028/0032/0047)
are distinct-unit products in the wrong shape (rate problems) = cue precision
(ADR-0177 CP-2b), the next lever -- not to be faked with a per-case rule. Carries the
corrected ADR-0184 (supersedes the spec-only #484).
2026-05-30 08:35:35 -07:00
Shay
4e36abff6e docs: add edge sync completion status 2026-05-29 19:50:44 -07:00
Shay
cde30f5b05 test(sync): prove optional S3 adapter behavior 2026-05-29 19:47:42 -07:00
Shay
dc9e4b5b11 feat(sync): export optional S3 adapter 2026-05-29 19:47:24 -07:00
Shay
b415486c84 feat(sync): add optional S3 object-store adapter 2026-05-29 19:47:08 -07:00
Shay
6a5c8d1591 test(sync): guard hot path from object-store imports 2026-05-29 19:46:49 -07:00
Shay
aa542cad13 feat(sync): export object-store seam 2026-05-29 19:46:31 -07:00
Shay
bea79c12e2 feat(sync): add object-store adapter seam 2026-05-29 19:46:15 -07:00
Shay
7e091525bf test(sync): prove local sync journal semantics 2026-05-29 19:45:57 -07:00
Shay
b9cc7d8988 feat(sync): export local sync journal 2026-05-29 19:45:38 -07:00
Shay
d11c792ef2 feat(sync): add local sync journal 2026-05-29 19:45:20 -07:00
Shay
3741c1321a test(sync): prove edge artifact activation ledger 2026-05-29 19:19:07 -07:00
Shay
817cbb920a feat(sync): export activation ledger 2026-05-29 19:18:35 -07:00
Shay
f645dc2bdb feat(sync): add edge artifact activation ledger 2026-05-29 19:17:52 -07:00
Shay
da5c2a9e76 test(sync): prove edge manifest validation rules 2026-05-29 19:01:00 -07:00
Shay
63462ffbf7 feat(sync): export manifest validator 2026-05-29 19:00:39 -07:00
Shay
e81c4385a1 feat(sync): add edge artifact manifest validator 2026-05-29 19:00:15 -07:00
Shay
646229f803 test(sync): prove edge artifact authority contract 2026-05-29 18:58:49 -07:00
Shay
9b333a40a1 feat(sync): export edge artifact authority model 2026-05-29 18:58:23 -07:00
Shay
aab1e70cb9 feat(sync): add edge artifact authority model 2026-05-29 18:58:10 -07:00
Shay
c20842c3f3 docs: add edge sync implementation brief 2026-05-29 18:56:50 -07:00
Shay
76fe5be379 docs: add edge sync artifact contract 2026-05-29 18:52:54 -07:00
Shay
c654b3f22d docs: add edge S3 persistence architecture 2026-05-29 18:50:11 -07:00
Shay
f008550d79
feat(adr-0182): anchor-skip + intra-clause accumulation — distractor 0016 refuses, twin 0017 solves (confuser wrong 1→0) (#481)
* feat(adr-0182): anchor-skip + intra-clause accumulation — distractor 0016 refuses, twin 0017 solves (confuser wrong 1->0)

The last confuser wrong. 0016 ("A train travels 60 miles per hour for 2 hours. Tom
has 8 tickets and buys 4 more tickets. How many tickets?") committed the blunt
product 60x2x8x4=3840 because it was the unique complete reading: the train sentence
is an all-foreign anchor-position block (2 quantities, can't seed an anchor) and the
Tom sentence packs state+change in ONE sentence ("has 8 ... and buys 4 more"), which
the sentence-level reader couldn't decompose -> accumulation_candidates was empty ->
no rival reading -> product committed.

The microscope confirmed intra-clause state+change is a REAL GSM8K pattern (train-0010
"Yun had 20 paperclips initially, but then lost 12"; practice-0121 "Sam has 30 apples
and gives 10 to Anna") -- so this is genuine comprehension, not a 0016-only patch.

Mechanism (added to accumulation_candidates ONLY -> feeds only the pool; train_sample
serving + practice use compose_accumulation, unchanged -> 3/47/0 byte-identical by
construction):
- _sub_clauses: sentence clauses further split on coordinating conjunctions
  (and / then / and then). LOCAL to the ungated candidate generator -- the global
  segmenter (GB-1/GB-2/serving) is untouched.
- _build_accumulation_anchor_skip: anchor = first single-quantity sub-clause (leading
  non-anchorable all-foreign blocks are skipped); chain the conjunction-mate change
  ("buys 4 more" -> +4). Referent guard + polarity-cue requirement still gate it
  (a no-cue sub-clause -> refuse), so GB-2 same-unit lists ("6 apples and 4 apples")
  produce no spurious candidate.

Result (sealed lane):
- confuser wrong 1 -> 0. distractor-quantity 0 wrong / 2 refused: 0016 refuses
  (product 3840 [complete] vs additive 12 [exempt] disagree). BONUS: the clean twin
  0017 ("Tom has 8 tickets and buys 4 more tickets", no distractor) now *solves* 12
  -- genuine comprehension of a real positive, the comprehension-vs-surface-match
  discrimination the corpus exists to measure. genuine positives 7 -> 8 solved.
- the only non-clean verdict left is 1 spurious (0010 multi-referent "altogether" --
  a separate H1 graduation question, not this lever).
- train_sample 3/47/0 and practice 3/47/0 byte-identical; 243 derivation/pool tests
  + 40 architectural invariants green.

Tests:
- test_adr_0182_pool.py TestAnchorSkipIntraClause: intra-clause twin resolves to 12;
  the 0016 anchor-skip candidate classifies `exempt`; 0016 refuses via disagreement;
  no spurious extra candidate without a conjunction.
- test_adr_0163_f2_confusers.py: baseline wrong 1->0, positives_solved 7->8; renamed
  test_distractor_quantity_refuses asserts BOTH 0014 and 0016; new
  test_intra_clause_twin_0017_solves.

Stacked on #480 (prior-state guard); merge #480 first. Confuser arc wrong 7->5->2->1->0.

* test(adr-0182): close anchor-skip refuse-branch coverage gap (lookback finding)

The four-PR lookback review (EX-6/pooling/prior-state/anchor-skip) found the
anchor-skip refuse branches in _build_accumulation_anchor_skip untested: the
referent guard (new named actor -> refuse) and the polarity-cue requirement were
asserted but not proven (no test would fail if removed). Per the schema-obligation
discipline, add NON-VACUOUS failing-under-violation tests:

- test_anchor_skip_referent_guard_discriminates_actor: identical structure, change
  sub-clause subject is a pronoun ('he', same referent -> 12 IS produced) vs a new
  name ('Sara' -> guard suppresses it). Removing _same_referent makes the new-actor
  case also produce 12 -> the second assertion fails. (Non-vacuous: the positive
  control proves the path is reachable, so the negative isn't trivially-empty.)
- test_anchor_skip_requires_a_polarity_cue: 'gets 4 more' (cue -> 12) vs 'owns 4'
  (no cue -> not guessed). Removing the polarity gate makes the no-cue case produce
  a reading -> fails.
- test_anchor_skip_refuses_without_single_quantity_anchor: no single-quantity
  sub-clause -> () (does not force a multi-quantity clause to anchor).

No code change; behavior unchanged. Confuser wrong stays 0; 281 derivation tests +
40 invariants green.
2026-05-29 15:05:31 -07:00
Shay
d8b37069c0
test(adr-0182): real guard for exempt commit-ineligibility (was vacuous) (#485)
The wrong=0-critical clause in pool.resolve_pooled (an exempt-only answer
never commits; a complete reading is required to resolve) had no test that
failed when it was removed: the _EXEMPT_ONLY fixture's pool also contained a
complete product (20*3*5=300), so refusal came from the disagreement rule,
not commit-ineligibility. Mutation-disabling the clause left all tests green.

Inject a single-exempt pool directly (the aggressive composers manufacture a
competing complete product for any natural text, so a corpus fixture cannot
isolate the branch). Removing the clause now commits 25 and fails loudly.
Rename the old fixture/test to state honestly that it refuses via disagreement.
2026-05-29 14:53:52 -07:00
Shay
6ed8fc21fc
docs(adr-0182): realized results + status Accepted/Implemented (lookback amendment) (#483)
The lookback review of the four-PR stack (EX-6/pooling/prior-state/anchor-skip)
found the ADR-0182 spec under-predicted and under-scoped 0016. Records actuals
without editing the original spec (retained for provenance):

- Status -> Accepted/Implemented (PRs #476, #480, #481).
- §8 Realized results: confuser wrong spec-predicted 5->4->3, ACTUAL 5->2->1->0.
  Pooling also caught disguised-polarity (bonus); 0016 needed anchor-skip PLUS
  intra-clause accumulation (a conjunction split the spec framed as anchor-skip
  alone) and yielded the bonus 0017 *solve*; the prior-state guard (#480) is a
  distinct question-time lever sharing resolve_pooled.
- Folds in the lookback findings: solid items, the closed test-coverage gap
  (anchor-skip refuse branches, #481 4205605), no live wrong=0 hazard (multi-actor
  product-commit predates anchor-skip), and the open 0010 multi-referent spurious
  (a graduation question, not a wrong).

Serving 3/47/0 byte-identical throughout. Doc only.
2026-05-29 14:50:22 -07:00
Shay
4dd824f4f6 docs(adr-0055): status reflects Phase C shipped (0056/0057), D–E landed; add cross-refs 2026-05-29 14:32:53 -07:00
Shay
84570a9c7d
docs: refresh ADR README narrative (current frontier, chain notes, session logs) (#482)
* docs: refresh ADR README narrative (current frontier, chain notes, session logs)

* docs: drop stray historical marker on refreshed Proposed list
2026-05-29 14:27:17 -07:00
Shay
a862d73084
feat(adr-0182): prior-state question guard — temporal-scope confuser refuses (confuser wrong 2->1, pair-tells ->0) (#480)
The "before/left" reading-rule lever. The microscope showed only the question-time
reading is cleanly achievable: "for N money = spend" is cue-precision-blocked (the
`for` cue is overloaded across train_sample -- `for $2`, `for 14 days`, `for 10 reps`
-- so a spend rule risks regressing train-0021/0003 and is the overfitting trap),
and the disguised-polarity cases already refuse via pooling. "left" is already handled
by loss verbs.

What ships: a question-scope guard. A question asking for a state *before* a stated
change ("How much did Lisa have before lunch?", gold 50) asks for a temporal point
the forward composers do not compute -- they derive the final/net state (50-20=30).
Until a question-time reader exists that is a refusal, never a guess at the wrong
point. target.asks_prior_state detects before/initially/originally/at first/to begin
with/to start with/at the start in the QUESTION CLAUSE only (the last `?`-sentence),
so body narrative does not trip it -- verified safe against train-0003 ("sells before
school starts"), 0010 ("had 20 initially, then lost 12"), 0028. `used to` is excluded
(the purpose infinitive "beads used to make a bracelet" is a false positive).
resolve_pooled refuses when asks_prior_state holds.

Result (sealed lane; chat/ does not import these -> serving 3/47/0 frozen):
- confuser wrong 2 -> 1 (only 0016 distractor-anchor-skip remains). temporal-scope
  category cleared (0 wrong / 2 refused). pair-tells 1 -> 0: 0020 ("before lunch")
  refuses while its minimal-pair twin 0021 ("left", gold 30) still solves the net --
  the discrimination the corpus is built to measure.
- genuine positives still 7 solved, 0 wrong.
- train_sample 3/47/0 and practice 3/47/0 byte-identical.
- 205 derivation/target/pool tests + 40 architectural invariants green.

Tests:
- test_adr_0182_pool.py TestPriorStateQuestionGuard: detector true/false edges
  (before-in-question vs before-in-body vs `used to make` false positive vs the
  `left` twin) + resolve_pooled refuses the before-question while the left-twin
  resolves forward to 30.
- test_adr_0163_f2_confusers.py: baseline wrong 2->1, pair_tells 1->0; new
  test_temporal_scope_does_not_misfire + test_before_left_minimal_pair_discriminated.

Stacked on #476 (pooling); merge #476 first. 0016 anchor-skip is the next lever.
Pairs with ADR-0163-F2 graduation amendment (#478).
2026-05-29 14:22:28 -07:00
Shay
ac7bda019b docs: refresh ADR index — all 229 ADRs (was 51)
Regenerated the docs/decisions/README.md ## Index table from each ADR's own
title + Status line, naturally sorted by ADR id (handles 0114a, 0119.1,
0131.G.3.1, 0136.S2, etc.). The index had drifted to 51 of 229 ADRs; it now
lists every ADR through 0183. Only the Index table was replaced; the Current
frontier / chain notes / reasoning-capable-domains / session-log narrative is
untouched (it remains a curated, historically-scoped account, not the index).
2026-05-29 14:08:34 -07:00
Shay
83de943cdd docs(adr-0183): stub ADR for the lawful audio→lexeme path
Records the fork for getting words from audio WITHOUT a serving-time learned
model: (A) words-as-text, or (B) deterministic formant/phonetic decode + taught
vocabulary. Status: Proposed (stub) — deferred, not a committed design. Captures
the problem, the 0-param/decode-not-borrow/refuse-over-fabricate/reviewed-growth
constraints, and the open questions a full ADR must answer. Exists so the
serving path doesn't silently reach for Whisper. Cross-linked from
docs/audio_pipeline_overview.md §9.
2026-05-29 14:02:40 -07:00
Shay
a58571d410 docs(audio): record teacher=scaffolding / serving-path-stays-Whisper-free boundary
Adds §9 subsection: a teacher is bootstrap scaffolding for the teaching phase,
not a production component (not ML distillation — CORE has no weights). Serving
path must never call a teacher. Production is Whisper-free conditioned on a
lawful runtime path: (A) words-as-text, or (B) matured deterministic
audio→lexeme decode. Flags the trap: teaching with a model does not auto-transfer
word-recognition into a 0-param engine. 'The teacher teaches; the lawful path
serves.'
2026-05-29 13:59:43 -07:00
Shay
c7290f5ab9 docs: start running model & dependency size tally
Living ledger of architecture/model/dependency footprint. Headline: CORE is a
0-parameter, 0-weight architecture (substrate is algebra, not weights), enforced
by the teacher versor-invariance test. Tracks: learned params (0), optional
teacher lanes (4 declared, all inert, no model size chosen), 10 Python runtime
deps (datasets dominates), 8 Rust crates, 0 weight files. Includes a Whisper
size reference for when/if a lane is wired, and an update-on-PR + changelog
discipline. Lives at docs/model_dependency_size_tally.md.
2026-05-29 13:51:30 -07:00
Shay
ae48a3cbf9 docs: audio section pipeline overview & primer
A from-the-ground-up reference for the audio modality: waveform → canonicalize
→ frames → acoustic lexer → typed AudioIR → operators/rotors → (32,) versor,
with every spec/constant grounded in the code (PR-2..PR-6 stack).

Includes the three-way clarification of learned models: embeddings (never),
audio specs (CORE's own DSP, not any model), text transcript (Whisper/NeMo as
typed labels only). Documents the substrate-vs-evidence split, the verbatim
teacher policy, current inert/gated status, and the 'scrutinise the consumer'
flag. Cross-links ADR-0180/0181 + spec + eval-plan. Lives at
docs/audio_pipeline_overview.md.
2026-05-29 13:47:31 -07:00
Shay
f017785a6d
feat(adr-0181-p6): audio teacher/shadow lanes — typed hints, never substrate (#479)
Implements ADR-0181 PR-6 (eval-plan §4): teachers label or align; they never
define the substrate and never fold embeddings into the versor path.

- sensorium/audio/teachers.py:
  - TeacherHint: typed, versioned, checksummed annotation (no raw embeddings).
  - AudioTeacher protocol (pure on the signal).
  - attach_teacher_hints: the ONLY admission path — appends content.* anchors to
    the IR's content_anchors (immutable, recomputes ir_sha256). content.* is not
    an operator key, so compile_events skips it: versor + projection_sha256 stay
    byte-identical; only the ir leg of the merge_key moves (evidence recorded).
  - KNOWN_TEACHER_LANES (whisper/nemo/clap/encodec): declared + gated behind
    optional extras; load_teacher import-guards and fails loudly (never a silent
    fallback). StubTranscriptTeacher is the deterministic reference instance.
- parser.py: extract _ir_payload + ir_sha256_of (DRY single source of truth for
  ir_sha256; byte-identical to parse() output — regression-guarded).
- pyproject.toml: audio-whisper/nemo/clap/encodec optional extras (never
  runtime-required).

16 failable proof tests in tests/test_audio_teachers.py. Load-bearing:
test_teacher_hint_does_not_change_versor. Mutation-verified — giving a teacher
anchor an operator event_type (folding it into the versor) fails the
versor-invariance proof; reverted, all pass.

Additive only (ADR-0013): no core layer touched. Audio suite 57/57; eval-gate
ir_sha256 pins unchanged by the parser refactor; architectural invariants 40/40.
Real model adapters are deferred until extras+weights are present; this PR ships
the policy, the typed-hint contract, and the shadow-only guarantee.
2026-05-29 13:36:33 -07:00
Shay
938d2bfeb3 ci(smoke): add audio sensorium tests to the PR smoke gate
tests/test_audio_*.py (compiler, eval-gates, pack-manifest, sensorium-mount,
CRDT-merge, and teachers once #479 lands) ran only post-merge via full-pytest;
they now gate PRs too. Glob auto-includes future audio test files. 54 tests,
~3s — well within the smoke budget.
2026-05-29 13:31:58 -07:00
Shay
47a406b590
docs(adr-0163-f2): clarify refuse is maturity-relative — confusers are solvable coverage targets that graduate (#478)
A confuser's `expected: refuse` does not mean unanswerable. Every confuser carries
its true gold (`answer_numeric`) and is a solvable coverage target; `refuse` means
"the reader can't yet comprehend this category, so refusing is the honest outcome —
committing a WRONG value is the defect." Example: 0001 "buys a toy for 30 coins …
how many left?" is plain 50-30=20; it is refuse only because today's reader takes
`buys` as a gain.

Adds §2.1 (graduation protocol): when a general mechanism reads a category correctly
(validated on train_sample + the category, wrong=0 preserved), those cases graduate
refuse -> solve and committing their gold becomes a win. Reframes the `spurious`
verdict as "solved-before-graduation" (a graduation signal, with pair-consistency as
the genuine-reading-vs-surface-match discriminator), not automatically a defect.
Notes that no v1 category is degenerate, and how a truly-unanswerable case would be
labelled (answer_numeric: null + degenerate: true) so the two senses of "refuse"
stay distinguishable.

Spec only; no corpus/runner change (today's commits are wrong-reading, so spurious
stays flagged until a category genuinely graduates).
2026-05-29 13:23:02 -07:00
Shay
dd529ad408
feat(adr-0181-p5): audio Delta-CRDT arena + merge kernel (sequential==concurrent proof) (#477)
Wires AudioCompilationUnits into the Delta-CRDT substrate (ADR-0180 §2.1/§2.2)
at the Python layer — audio is the first concrete exerciser of ADR-0180.

- sensorium/audio/arena.py:
  - AudioArena (§2.1): thread-local, share-nothing, lock-free accumulation;
    non-destructive snapshot(); thread_local_audio_arena() accessor.
  - AudioDelta (§2.2): canonical, content-addressed merge_key order + dedup;
    join is commutative/associative/idempotent.
  - merge_audio_deltas (§2.2): the Merge Kernel — folds arena deltas into one
    content-addressed, deduped, totally ordered set.
  - audio_merge_trace_hash: PCM-free, order-invariant trace hash — the
    sequential==concurrent proof anchor (ADR-0181 §4.2 A-3/A-6).

Mirrors the Rust LocalArena/SemilatticeDelta/merge_kernel (core-rs/src/vault.rs,
ADR-0180 §4.1) so the layers stay in parity when the Rust↔Python binding lands
(§1.5.5, deferred — pure-CPU/Python path first).

10 failable proof tests in tests/test_audio_crdt_merge.py covering the
load-bearing hash(Sequential)==hash(Concurrent) gate (real threads), the three
semilattice legs, content-addressed ordering, dedup idempotence, and trace
hygiene. Mutation-verified: disabling the content sort fails 6 of 10 loudly.

Additive only: no core layer touched (ADR-0013). Audio tests 35/35; architectural
invariants 40/40. Zero impact on existing eval lanes (no serving path changed).
2026-05-29 13:22:44 -07:00
Shay
0fbcce429b
feat(adr-0182): cross-composer disagreement pooling — distractor 0014 + disguised-polarity refuse (confuser wrong 5->2) (#476)
Implements ADR-0182's first win on top of EX-6 (#473). The distractor-quantity
confuser 0014 misfired (20x3x5=300) because a blunt product-of-all was the *unique*
self-verifying reading: the completeness clause forces the distractor into it, and
no rival reading existed to trigger the wrong=0 disagreement rule. No tight cue rule
separates it from the legitimate cross-unit products (`for` licenses both the 0014
distractor AND the correct train-0021 product) -- that is the deferred cue-precision
problem. So instead of a reactive patch, let the disagreement rule do the refusing.

Mechanism (sealed lane; chat/ does not import these -> serving 3/47/0 frozen):
- verify.classify_derivation: a derivation is `complete` (commit-eligible),
  `exempt` (verified but for an isolated-foreign unused quantity -> commit-
  INELIGIBLE), or None. Refactored self_verifies into _base_reasons +
  _unused_quantities so the two share logic and cannot drift (behavior identical;
  385 derivation tests + smoke 67 green).
- accumulate.accumulation_candidates: exposes the ungated readings, incl. a
  distractor-skip reading that drops an isolated-foreign quantity from a multi-
  quantity change clause (20+5=25). compose_accumulation is byte-identical
  (drop_isolated_foreign=False + the same gate).
- search.multiplicative_candidates / multistep.candidate_chains: ungated candidates.
- pool.resolve_pooled: pools every composer's readings; disagreement -> refuse; a
  single answer commits only if a `complete` candidate produced it (exempt-only ->
  refuse, so the commit-path completeness guarantee from ADR-0175 is untouched).
- confuser runner: _engine_answer now delegates to resolve_pooled (the prior
  first-composer-wins order could not notice it held two incompatible readings).

Result (the microscope):
- confuser wrong 5 -> 2. distractor 0014 refuses (product 300 vs additive 25);
  BONUS: both disguised-polarity cases (0001/0003) refuse -- the spurious
  "buys X for N coins" product disagrees with the accumulation reading. Remaining
  wrong: 0016 (distractor in the anchor clause -> needs anchor-skip, separate step)
  and 0020 (temporal-scope). pair-tells 4 -> 1.
- genuine positives still 7 solved, 0 wrong.
- train_sample 3/47/0 and practice 3/47/0 byte-identical (they call
  compose_accumulation/search directly -- unchanged -- not the pool).
- smoke 67, architectural invariants 40, lane-SHA freeze 8/8.

Tests:
- test_adr_0182_pool.py: classify (complete/exempt/None incl. narrow-exemption
  edges) + resolve_pooled, with the wrong=0 obligation test_exempt_only_never_commits
  (a distractor with no multiplicative cue must refuse, not commit 25 -- fails loudly
  if the exemption is made commit-eligible).
- test_adr_0163_f2_confusers.py: baseline tightened wrong 5->2, pair_tells 4->1;
  new test_distractor_0014_refuses_via_pooling + test_disguised_polarity_does_not_misfire.

Stacked on #473 (EX-6); merge #473 first. 0016 + the remaining wrongs are follow-ups.
2026-05-29 13:22:19 -07:00