Commit graph

1354 commits

Author SHA1 Message Date
Shay
2a7e94c011 feat(combined-rate): CMB-b — exact combined-rate solver (int-or-refuse)
solve_combined_rate(CombinedRateProblem) -> int | Refusal over effective_rate = rate_a +/- rate_b.
effective_rate query returns the net (even <=0); quantity/time queries need a positive net rate
(non_positive_net_rate) and exact division (non_integer_solution). Pure integer arithmetic, no
float/Fraction (CMB v1 crosses no units). Off-serving. Graded by a new run_solver lane in
evals/combined_rate_oracle against the committed gold.

Adversarial 5-lens(+adjudicator) verification returned fix_first; both real hazards fixed:
- wrong=0 breach: negative known-slot inputs (time<0/quantity<0) bypassed the eff<=0 guard and
  produced negative answers. FIXED UPSTREAM in model.py __post_init__ — rate_a/rate_b and the known
  time/quantity are now positive ints, so the illegal state is unrepresentable and the solver can
  never receive a negative-yielding path. Added model + gold (cmb-07d eff<0/time) + lane coverage.
- doc overclaim: the solver lane does NOT grade 'two independent paths' (both solver and the
  oracle's _canonical_outcome delegate to model.effective_rate). Corrected both docstrings to name
  the true anchor (committed gold + inline-computed literal tests) and added a difference-mode
  inline-computed test.

R3-vac came back SOLID (separate PR). gold 18/18 (6/5/7); solver lane 6/0 + 5/0; 36 CMB tests;
router-hygiene + serving unchanged.
2026-06-08 07:59:45 -07:00
Shay
f6292f1ecf docs(session): practice-time attempts vs served answers — the servability blade
Records the wrong=0 refinement reached in discussion: wrong=0 is not a binary
answer/refuse gate but 'no false presentation of epistemic status'. Practice/
contemplation may explore typed, isolated candidate guesses; served output needs
a servability blade with more modes than verified|refuse. Proposes
ProblemAttemptSession (PRA) and ServabilityDecision (SRV), sequenced PRA -> REL ->
SRV, and traces the idea to the R1/R2/R3/CMB reader trajectory. Session doc, not
an ADR yet.
2026-06-08 07:17:14 -07:00
Shay
52731c02aa
Merge pull request #650 from AssetOverflow/feat/cmb-a-combined-rate-oracle
feat(combined-rate): CMB-a — combined-rate setup ruler (model + gold + oracle)
2026-06-08 07:07:58 -07:00
Shay
36bf2a6456 feat(combined-rate): CMB-a — combined-rate setup ruler (model + gold + oracle)
First rung of the combined-rate ladder, off-serving (imports no generate.derivation /
core.reliability_gate). Claims exactly: the combined-rate setup ruler is defined and
gold-valid. No reader/solver/wiring yet — no capability claim.

- generate/combined_rate_comprehension/: local RateUnit (decoupled from R3), CombinedRateProblem
  (two-explicit-rates + per-query-slot guard; effective_rate = rate_a +/- rate_b; non-positive net
  left to the solver, not the model).
- evals/combined_rate_oracle/: span-free signature (sum commutative / difference ordered) + a
  NON-VACUOUS validator. 17 gold fixtures: 6 solved (full combine_mode x query grid), 4
  solver_refuses, 7 reader_refuses (the complete refusal taxonomy + the 2x2 domain-entry grid).

Adversarial 5-lens verification returned fix_first; the validator now cross-checks every solved
gold and solver_refuses reason against the canonical arithmetic (_canonical_outcome) so a
mislabelled / arithmetically-impossible fixture is rejected (meaningful-fail per CLAUDE.md), with
dedicated tests. Added eff<0 and eff=0/time coverage fixtures; removed a dead determinism guard.

gold 17/17 valid; 25 oracle tests; R1/R2/R3 + router-hygiene + serving all unchanged.
Doc: docs/analysis/cmb-a-combined-rate-ruler-2026-06-08.md
2026-06-08 06:58:17 -07:00
Shay
67f470ede8
Merge pull request #649 from AssetOverflow/docs/organ-capability-ledger
docs: whole-system organ capability ledger + fix ADR-0211 collision
2026-06-08 06:27:45 -07:00
Shay
df1d14dce4 docs(comprehension): whole-system organ capability ledger + fix ADR-0211 number collision
Consolidation/true-up after #646-#648. Records the off-serving comprehension
system as one ledger: R1 relational arithmetic, R2 finite-integer constraints,
R3 explicit single-rate + exact minute/hour conversion, the router/contemplation/
proposal loop, the proposal-review reporter, idle_tick read-only visibility, and
the standing router-organ-hygiene invariant. Pins the whole-system lane state
(answer_wrong==0 everywhere) and the off-serving import-disjointness guarantee.

Also fixes a documentation-integrity defect the R2 batch introduced: the R2 ADR
took number 0211, which collided with ADR-0211 (Conformal Falsification Bench,
2026-06-06 — earlier, test-pinned, depended on by ADR-0216). The conformal ADR
keeps 0211; the R2 ADR is renumbered to the next free number 0217. Ratified
decision content unchanged; only the identifier and references move.
2026-06-08 06:15:51 -07:00
Shay
801f0e23dd
Merge pull request #648 from AssetOverflow/feat/r3-2-be
feat(rate): R3.2b–e — time-unit conversion integration (model/solver/reader/gold) [merge LAST]
2026-06-08 06:03:27 -07:00
Shay
7e39357e11
Merge pull request #647 from AssetOverflow/feat/r3-2-a
feat(rate): R3.2a — exact-rational time-unit conversion primitive [merge after #646]
2026-06-08 06:03:23 -07:00
Shay
e98289ee89
Merge pull request #646 from AssetOverflow/feat/router-organ-hygiene-pr
test(contemplation): standing router-organ-hygiene invariant
2026-06-08 06:03:19 -07:00
Shay
1c59f331f4 feat(rate): exact time-unit conversion for single-rate (R3.2b-e)
Option A (text-faithful): RateProblem gains time_unit (the duration's ORIGINAL unit; time stays int; defaults to the rate denominator). The SOLVER converts via convert_time -> exact Fraction -> int-or-refuse(non_integer); Fraction never leaves the solver, no floats. The reader accepts a convertible (minute<->hour) duration mismatch (keeps original time_unit); a non-convertible one still refuses rate_unit_mismatch. Signature includes time_unit ('30 minutes' != '30 hours').

Gold: r3-09 flips reader_refuses -> SOLVED (60 mph for 30 min = 30 mile, exact 1/2 hour; distractor 1800 rejected); new r3-13 non-convertible (3 gallons) stays rate_unit_mismatch. R3 gold 13/13; reader 9/0/0 -> answers 7/0/6 (setup_correct 8->9). Failure-family: rate_unit_mismatch now non-convertible-only -> still unsupported_rate_duration proposal surface; router-hygiene invariant stays green; R1 7/0/3, R2 10/0/0 unchanged. 119-test smoke green incl. invariants. Off-serving, no float path.
2026-06-08 05:43:51 -07:00
Shay
89321e617d feat(rate): R3.2 exact-rational time-unit conversion primitive (R3.2a)
generate/rate_comprehension/conversion.py: convert_time(value, from_unit, to_unit) -> Fraction for known time units (minute<->hour), exact rational (fractions.Fraction), NO floats. is_convertible gates it. 30 minute -> Fraction(1,2) hour; non-time/non-convertible (minute<->mile, dollar<->hour) raises ConversionError so the caller still refuses rate_unit_mismatch. Tiny: only minute<->hour in v1 (length/currency/compound deferred). 6 tests incl. never-a-float guard.
2026-06-08 05:30:57 -07:00
Shay
0dfc001c1c test(contemplation): standing router-organ-hygiene invariant
No organ may claim a substantive boundary on text outside its domain — on every OTHER organ's gold, a refusal must map to the non-substantive input_shape family (not-my-domain), never a substantive boundary/growth surface. This is what lets one organ suppress another's legitimate proposal via boundary-first (caught at N6, R3e, R3.1). MUST-PASS gate before any new organ joins route_setup. Currently green for R1/R2/R3. + standing-rule doc.
2026-06-08 05:28:06 -07:00
Shay
f1e7925444
Merge pull request #645 from AssetOverflow/feat/r3-1-router-pr
feat(contemplation): R3.1 — wire R3 into the multi-organ router + pass manager
2026-06-08 05:23:49 -07:00
Shay
73345e641e feat(contemplation): wire R3 into the multi-organ router + pass manager (R3.1)
classify_r3 normalizes the rate reader into a ComprehensionAttempt; route_setup now tries R1/R2/R3 (same exactly-one-setup_correct rule); the pass manager solves+verifies a routed r3_rate setup (solve_rate + reused answer-choice verifier); unsupported_temporal_state dropped from the unsupported-family set so temporal_state -> REFUSED_KNOWN_BOUNDARY. Organ gains r3_rate.

Terminal matrix (verified): 6 solved->SOLVED_VERIFIED; rate_unit_mismatch+combined_rates->PROPOSAL_EMITTED(unsupported_rate_duration); non_integer/missing_time/temporal->REFUSED_KNOWN_BOUNDARY; exactly 2 proposals (the rate-like gaps only).

REGRESSION CAUGHT+FIXED: R3's reader returned missing_rate (a substantive boundary) on non-rate R2 text, blocking r2-011's legitimate missing_total_count proposal via boundary-first. Fix: missing_rate only when a duration is present; else not_rate_shaped -> input_shape (not-my-domain) — same discipline as the N6 category_pair_not_found fix. r2-011 proposes again; R1/R2 unaffected. R1 7/0/3, 15-case 15/0/0, R2 10/0/0, R3 8/0/0 unchanged; off-serving; 121-test smoke green incl. invariants + idle contract suites.
2026-06-08 02:05:34 -07:00
Shay
a320c69a3a
Merge pull request #644 from AssetOverflow/feat/r3-e
feat(rate): R3e — R3 ledger + failure-family wiring [merge LAST]
2026-06-08 01:56:37 -07:00
Shay
f95657bb8f
Merge pull request #643 from AssetOverflow/feat/r3-d
feat(rate): R3d — single-rate reader + run_reader lane [merge after c]
2026-06-08 01:56:33 -07:00
Shay
54c9f60442
Merge pull request #642 from AssetOverflow/feat/r3-c
feat(rate): R3c — exact integer single-rate solver [merge after b]
2026-06-08 01:56:30 -07:00
Shay
09e147cac5
Merge pull request #641 from AssetOverflow/feat/r3-b
feat(rate): R3b — RateProblem model + gold + setup oracle [merge after a]
2026-06-08 01:56:26 -07:00
Shay
24f7b4bb03
Merge pull request #640 from AssetOverflow/feat/r3-a
feat(rate): R3a — compound-unit primitive [merge 1st]
2026-06-08 01:56:23 -07:00
Shay
780f993c73 feat(rate): R3 inventory ledger + failure-family wiring (R3e)
docs/analysis/r3-rate-inventory-ledger: R3 v1 state (reader 8/0/0 + 4 refused -> answers 6/0/6; gold 12/12), per-fixture table, the compound-unit substrate, covered families + R3.2/R3.3 deferrals.

core/comprehension_attempt/failure_family.py: makes unsupported_rate_duration REACHABLE as a growth surface (proposal_allowed) mapping rate_unit_mismatch + combined_rates — both emitted ONLY after a rate clause is recognized, so always rate-like (anti-over-propose discipline, same as the N6 fix). missing_rate/time/quantity -> rate_underdetermined boundary; temporal_state -> unsupported_temporal_state boundary (clock detector can fire on non-rate text, NOT a safe growth surface); query_target_unrecognized/no_query -> input_shape. Owner gains 'r3'. 107-test smoke green incl. partition + contemplation + invariants; R1 7/0/3 + R2 10/0/3 unchanged; off-serving.
2026-06-07 23:12:23 -07:00
Shay
6b1fdbff45 feat(rate): single-rate reader + run_reader lane + answer-choice reuse (R3d)
generate/rate_comprehension/reader.py: read_rate_problem comprehends explicit single-rate prose into a RateProblem via structural recognizers — rate value (<N> <plural> per <singular>), duration (for/in <N> <unit>), standalone quantity (outside those spans), query (how many <unit> by unit-match, or speed in <X> per <Y> -> rate). The compound-unit-consistency check is the wrong=0 gate: a duration whose unit isn't the rate denominator (miles/hour for 30 minutes) REFUSES rate_unit_mismatch, never converts.

Refuses combined_rates (>=2 rate clauses), temporal_state (clock markers), missing_rate/time/quantity (underdetermined). run_reader lane + 'reader' CLI. R3 reader: setup_correct 8 / setup_wrong 0 / refused_correct 4 -> answers 6 correct / 0 wrong / 6 refused (the 6/0/6 target). Reuses R2 verify_answer_choice end-to-end. Off-serving. 6 reader tests + constructed unit-mismatch guard.
2026-06-07 23:07:51 -07:00
Shay
6560300101 feat(rate): exact integer single-rate solver (R3c)
generate/rate_comprehension/solver.py: solve_rate computes the one unknown — quantity=rate×time (always integer), rate=quantity÷time, time=quantity÷rate (exact division or REFUSE non_integer_solution, never rounds). Confirms compound-unit composition via the R3a algebra (defensive; a RateProblem's units compose by construction). answer_unit gives the answer's unit. No floats.

Ties to the gold: solves all 6 solved fixtures to gold; refuses both solver_refuses with non_integer_solution. Meaningful-fail: 100÷3 refuses (not 33), 99÷3=33. Off-serving. 5 tests.
2026-06-07 23:03:30 -07:00
Shay
4c271f5e9f feat(rate): R3 RateProblem model + rate gold + setup oracle (R3b)
generate/rate_comprehension/model.py: RateProblem (quantity = rate × time, exactly one unknown == query; rate_unit fixes all three units by construction; structural guard refuses zero/two unknowns). evals/rate_oracle/: span-free signature, a 12-fixture gold (6 solved / 2 solver_refuses / 2... reader_refuses), closed expect+reason taxonomy, gold-validation runner + CLI. No reader yet — proves the ruler (12/12 valid).

Gold shape gives the 6/0/6 target: 6 single-rate solved (forward + both inverses, non-temporal 'per box' included), 2 non-integer-inverse solver refusals, 4 reader refusals (unit mismatch minutes-vs-hour, missing time, combined rates, clock-time temporal). Off-serving. 9 oracle + 10 unit tests; per-branch meaningful-fail.
2026-06-07 23:01:25 -07:00
Shay
d9ebec7b70 feat(rate): R3 compound-unit primitive — the dimensional substrate (R3a)
generate/rate_comprehension/units.py: BaseUnit + RateUnit (numerator/denominator) + the three single-rate compositions — rate_times_time (mile/hour × hour = mile; refuses mile/hour × minute), rate_from_quantity_over_time (mile ÷ hour = mile/hour), time_from_quantity_over_rate (mile ÷ mile/hour = hour; refuses dollar ÷ mile/hour). A non-composing op raises UnitError (the wrong=0 dimensional gate), never fabricates a unit.

Deliberately tiny: bare-name units, no generic dimensional universe, no semantic unit-class typing, no unit conversion, no compound-of-compound. Fresh off-serving organ (generate/rate_comprehension/). 10 tests incl. all three refusals + consistent composition.
2026-06-07 22:57:13 -07:00
Shay
a00e87b297
Merge pull request #639 from AssetOverflow/feat/it-c
docs(proposal-review): IT-c — idle-integration boundary ledger [merge LAST]
2026-06-07 22:49:50 -07:00
Shay
5345c8a8a0
Merge pull request #638 from AssetOverflow/feat/it-b
feat(runtime): IT-b — read-only proposal-review sub-pass in idle_tick [merge after A]
2026-06-07 22:49:47 -07:00
Shay
16cd3955f9
Merge pull request #637 from AssetOverflow/feat/it-a
feat(proposal-review): IT-a — pure idle_summary function [merge 1st]
2026-06-07 22:49:45 -07:00
Shay
9b83cf7972 docs(proposal-review): idle-integration boundary ledger (IT-c)
Pins the boundary: existing idle_tick remains the only idle_tick; proposal review is read-only; NOT L10 always-on heartbeat; does not advance learning; only surfaces review obligations. Documents how IT-b works (gate, sub-pass, failure-isolation, no-did_work) and that R3 (rates/time/state) is next.
2026-06-07 22:33:17 -07:00
Shay
32385d954c feat(runtime): read-only proposal-review sub-pass in idle_tick (IT-b)
core/config.py: review_pending_proposals: bool = False (opt-in, mirrors consolidate_determinations). chat/runtime.py: IdleTickResult gains an additive optional proposal_review field; idle_tick runs a READ-ONLY sub-pass (after consolidation, gated) that surfaces core.proposal_review.idle_summary(). It does NOT set did_work / checkpoint / mutate / ratify / mount / modify readers; a reporter exception is CAPTURED (safe=False, errors=('proposal_review_failed:<type>',)), never propagated.

docs/runtime_contracts.md documents the sub-pass + field (required for runtime-surface changes). Contract tests: default-off -> proposal_review None (existing shape preserved); enabled -> summary surfaced; exception captured not propagated; read-only review never checkpoints; other passes unperturbed. 91-test smoke green incl. architectural invariants + both existing idle contract suites. Existing idle_tick remains the only idle_tick; NOT L10 always-on.
2026-06-07 22:32:20 -07:00
Shay
65c8587207 feat(proposal-review): pure idle_summary function (IT-a)
core/proposal_review/summary.py: idle_summary(root=None) -> ProposalReviewIdleSummary composes the landed scan/build_report/dry_check into one JSON-safe primitives-only value (safe, total, review_needed, malformed, by_family tuple-of-pairs, errors). Pure read — no mutation, no clock, no runtime touch. errors carries the dry-check violations (why not safe); IT-b reuses it for a captured reporter exception. 7 tests incl. read-only + deterministic + json-serializable.
2026-06-07 22:25:00 -07:00
Shay
3ec5e91433
Merge pull request #636 from AssetOverflow/feat/proposal-review-d
feat(proposal-review): RPT-d — boundary ledger [merge LAST]
2026-06-07 22:22:25 -07:00
Shay
b53f728ee9
Merge pull request #635 from AssetOverflow/feat/proposal-review-c
feat(proposal-review): RPT-c — safety dry-check + read-only CLI [merge after B]
2026-06-07 22:22:22 -07:00
Shay
4049681b85
Merge pull request #634 from AssetOverflow/feat/proposal-review-b
feat(proposal-review): RPT-b — deterministic review report [merge after A]
2026-06-07 22:22:19 -07:00
Shay
eb675064ce
Merge pull request #633 from AssetOverflow/feat/proposal-review-a
feat(proposal-review): RPT-a — read-only scanner over the proposal sink [merge 1st]
2026-06-07 22:22:16 -07:00
Shay
48386f3237 docs(proposal-review): boundary ledger — read-only reporter, not idle_tick, not L10 (RPT-d)
Documents the boundary: observes + reports + verifies; never advances teaching / ratifies / mounts / modifies readers / affects serving. ChatRuntime.idle_tick remains the only idle_tick (different stream, different verb). A future PR may call this reporter from idle_tick as a read-only sub-pass. L10-adjacent, NOT L10; the always-on heartbeat remains separate/unbuilt and is not claimed here.
2026-06-07 21:44:07 -07:00
Shay
cc3003f425 feat(proposal-review): safety dry-check + read-only CLI (RPT-c)
core/proposal_review/safety.py: dry_check INDEPENDENTLY verifies (without trusting the emitter) that every artifact is inert — status==proposal_only, mounted==false, requires_review==true, content-address consistent (filename == sha256(family:problem_text_sha256)), path under the sink, no malformed (unverifiable) file — and that no serving-path module reads the sink. Any failure is a violation.

__main__.py: python -m core.proposal_review [comprehension-failures] [--json] [--root] — read-only report + dry-check; exit 0 iff safe. Mutates nothing. 8 tests, each safety assertion proven meaningful-fail; serving-unconsumed verified on the real tree.
2026-06-07 21:43:18 -07:00
Shay
3fc73eb14b feat(proposal-review): deterministic review report (RPT-b)
core/proposal_review/report.py: build_report projects scan results into a deterministic summary — total pending, counts by failure_family + by status (sorted), malformed count, review-needed list (sorted by content hash). report_json (sort_keys) + report_text. Deterministic given sink contents; no clock.

Time-based oldest/newest is intentionally OMITTED: the artifacts are content-addressed with no timestamp (the emitter is clock-free for idempotence), so an honest temporal order isn't in the data — only non-deterministic filesystem mtime, which would break report determinism. 5 tests.
2026-06-07 21:40:35 -07:00
Shay
3c6f820b54 feat(proposal-review): read-only scanner over the comprehension-failure sink (RPT-a)
core/proposal_review/{model,scan}.py: scan reads teaching/proposals/comprehension_failures/*.json into typed PendingProposal records (path, content_hash, failure_family, status, mounted, requires_review, problem_text_sha256, observed_attempts); any unparseable file becomes a MalformedArtifact (never silently dropped). Pure read — never writes/moves/deletes; deterministic (sorted by path); missing sink -> empty. The sink path is computed independently of the emitter (the reporter verifies the contract, doesn't import the writer).

Read-only reporter, NOT an idle_tick (ChatRuntime.idle_tick stays the only one), NOT L10. 4 tests incl. read-only invariant + malformed flagging.
2026-06-07 21:39:03 -07:00
Shay
31a984e69b
Merge pull request #632 from AssetOverflow/feat/contemplation-pack-e 2026-06-07 12:57:58 -07:00
Shay
1bc7f1ae26
Merge pull request #631 from AssetOverflow/feat/contemplation-pack-d 2026-06-07 12:56:19 -07:00
Shay
cfa8bdd5fa
Merge pull request #630 from AssetOverflow/feat/contemplation-pack-c 2026-06-07 12:55:17 -07:00
Shay
787aa833fd
Merge pull request #629 from AssetOverflow/feat/contemplation-pack-b 2026-06-07 12:53:40 -07:00
Shay
b37794814d
Merge pull request #628 from AssetOverflow/feat/contemplation-pack-a 2026-06-07 12:53:19 -07:00
Shay
1adf4f5de5 feat(contemplation): contemplation v0 pass manager (N6) + boundary-first growth fix
generate/contemplation/{findings,pass_manager}.py: a single bounded pass — route (N3) -> classify+enrich (N4) -> terminal -> maybe emit proposal-only (N5). No loops/daemon/L10. Seven terminals: SOLVED_VERIFIED / REFUSED_KNOWN_BOUNDARY / REFUSED_UNSUPPORTED_FAMILY / CONTRADICTION_DETECTED / PROPOSAL_EMITTED / AMBIGUOUS_ORGAN / NO_PROGRESS. R2 solved+verified end-to-end; R1 routed setup is SOLVED_VERIFIED (numeric answer stays the eval lane in v0).

Hazard found+fixed: N6 exposed that category_pair_not_found fires on ANY non-R2 text, so mapping it to a growth surface made gibberish falsely PROPOSAL_EMITTED. Reclassified it to input_shape and made missing_category_pair reserved — only the PRECISE missing_total_count/missing_weighted_total remain reachable growth surfaces. Classification is boundary-first (input_shape non-blocking), so a problem one organ recognizes as a substantive boundary never proposes against the other's broad refusal.

Acceptance proven: known correct refusal -> no proposal; unsupported gap -> proposal-only artifact (exactly the 2 missing_* gaps over the gold); answer-key contradiction -> CONTRADICTION_DETECTED; organ conflict -> AMBIGUOUS_ORGAN. 188 tests green incl. architectural invariants; R1 7/0/3 + R2 reader 10/0/0 unchanged; off-serving.
2026-06-07 08:59:47 -07:00
Shay
5903cba08a feat(teaching): proposal-only failure-artifact emitter (N5)
core/comprehension_attempt/proposal.py: emit_proposal writes a content-addressed teaching/proposals/comprehension_failures/<hash>.json ONLY for growth-surface families (proposal_allowed). Deliberately toothless: status always proposal_only, mounted always false, requires_review always true (enforced in __post_init__); the problem text is hashed (sha256), never stored; filename = sha256(family:text_sha) so the same failure is idempotent.

No proposal for a correct wrong=0 boundary. Serving never reads the sink (scanned: generate/stream, field/propagate, vault/store, generate/derivation, core/reliability_gate). Routes through the existing proposal-only flywheel (ADR-0055/0056/0057), never a parallel correction path. README pins the contract. 6 tests.
2026-06-07 08:48:45 -07:00
Shay
c1ad5b8142 feat(comprehension): failure-family registry (N4)
core/comprehension_attempt/failure_family.py: partitions every reachable organ refusal reason (R1 reader/admissibility, R2 reader/solver/answer-choice) into a named FailureFamily declaring owner / must_remain_refused / proposal_allowed / safe_next_action / proposal_target. family_for_reason is total + unambiguous (partition asserted by test); enrich_family resolves an attempt's family.

Only THREE families are growth surfaces (proposal_allowed=True): the R2 missing_category_pair / missing_total_count / missing_weighted_total gaps. All other reasons are correct wrong=0 boundaries (refuse, no proposal) — correct refusal != missing capability. answer_key_contradiction is a report verdict (no reason). Reserved R3 families forward-declared. 9 tests incl. whole-surface coverage + partition.
2026-06-07 08:46:07 -07:00
Shay
5ae516f4a2 feat(comprehension): deterministic multi-organ setup router (N3)
core/comprehension_attempt/router.py: route_setup tries R1 then R2, collects both attempts, and selects a setup ONLY when exactly one organ admits one. Zero->all_refused; >=2 with differing signatures->ambiguous (refuse, never pick); cross-organ signatures never coincide so two admitting organs always resolve to ambiguous. No scoring, no solving.

wrong=0 invariant asserted: every routed R2 setup is byte-equal to the independent gold signature. Each gold problem routes to exactly its owning organ (R2 well-formed->r2; R1 admitted->r1; refused->all_refused); none is ambiguous. 4 router tests.
2026-06-07 08:42:35 -07:00
Shay
67c54d6813 feat(comprehension): shared ComprehensionAttempt model + classify (N2)
core/comprehension_attempt/{model,classify}.py: a small frozen ComprehensionAttempt (organ, outcome, case_id, refusal_reason, family, setup_signature, answer, evidence) and classify_r1/classify_r2 that normalize each organ's heterogeneous output (typed setup | Refusal) into a uniform produce-mode setup outcome — setup_refused (with the organ's reason) or setup_correct (with a deterministic inline signature).

No solving, no gold comparison, no evals import (signatures computed inline) — runtime never depends on the harness. Ties to both gold corpora: R2 solved/solver_refuses->setup_correct, reader_refuses->setup_refused with the gold reason; R1 7 admitted / 3 refused. family left None (resolved by the N4 registry). 6 tests.
2026-06-07 08:40:53 -07:00
Shay
0b191055a8 docs(analysis): comprehension capability ledger — R1/R2 baseline + refusal taxonomy (N1)
Post-merge baseline for the contemplation batch: R1 7/0/3, R2 reader 10/0/0, gold 13/13, serving unchanged. Enumerates the full typed refusal surface across both organs (R1 reader/admissibility, R2 reader/solver/answer-choice) and a first-pass cross-organ failure-family taxonomy with must_remain_refused / proposal_allowed / owner — the table N4 will make executable. Repeats the load-bearing distinction: correct refusal != missing capability.
2026-06-07 08:36:22 -07:00
Shay
51b4a6e37d
Merge pull request #627 from AssetOverflow/feat/r2-reader
feat(constraint): R2 Pack C — two-category reader (the capability) + ledger
2026-06-07 08:32:11 -07:00