Commit graph

103 commits

Author SHA1 Message Date
Shay
18a679c29d feat(packs): reland en_core_syntax_v1 foundation vocabulary (collision-audited)
First foundation-curriculum substrate pack: 24 reviewed noun lemmas for
syntax/claim/provenance vocabulary (subject, predicate, agent_role,
clause, antecedent, consequent_role, polarity, negation, evidence_span,
…).  Substrate only — no parser, no runtime generation change.

This is the clean reland of #503 (merged then reverted via #508 for
shipping two failing tests).  Root cause of the revert was lemma
collision: the smoke CI gate does not collect dedicated test files, so
the pack merged green while its own test asserted resolver routings that
were false.

Reland fixes, with the full collision audit done against every on-disk
pack (not just the one the original author checked):

- agent      -> agent_role        (bare `agent` owned by en_core_meta_v1)
- comparison -> comparison_relation(bare `comparison` owned by en_core_cognition_v1)
- consequent -> consequent_role   (bare `consequent` owned by en_core_causation_v1)
- negation   kept bare on purpose: no *mounted* pack owned it
  (en_mathematics_logic_v1 is a domain pack, not in
  DEFAULT_RESOLVABLE_PACK_IDS), so mounting syntax now grounds a word
  that previously returned None.  Renaming would have lost grounding.

The original test also mis-asserted prior resolution: it claimed
`cause` -> en_core_causation_v1, but first-match-wins resolves it to
en_core_cognition_v1 (mounted at index 0).  The test now pins the TRUE
current owner; this pack does not change it.

Resolver: en_core_syntax_v1 mounted after en_core_polarity_v1 and before
en_core_relations_v1 — purely additive, steals no prior resolution.

Evidence:
- tests/test_en_core_syntax_v1_pack.py: 13/13 (was 4 failed, 9 passed)
- pack/resolver/grounding/invariants blast radius: 415 passed
- core test --suite smoke: 67 passed
- core test --suite packs: 1 failed, 140 passed — the single failure
  (test_lilibeth_canary_solves_end_to_end, GSM8K candidate-graph reader)
  is PRE-EXISTING on clean main f79b647, byte-identical, and untouched by
  this PR.
- lane SHAs: 7/8 content-match; the public_demo miss is a wall-clock
  DemoContractError (>30s budget), not content drift — serving frozen.

PR checklist:
- Capability: foundation-curriculum language substrate vocabulary.
- Field invariant: pack loads through checksum-sealed compiler; no
  algebra/versor path touched.
- Lane: core test --suite packs / smoke; dedicated pack test.
- No hidden normalization, stochastic fallback, approximate recall, or
  unreviewed mutation.
- Trust boundary: language-pack loading only; manifest checksums hash the
  exact bytes on disk (recomputed after the two renames).
2026-05-31 16:18:26 -07:00
Shay
5c75853a0b Revert "Merge pull request #503 from AssetOverflow/feat/en-core-syntax-relations-v1"
This reverts commit 2405e9b06e, reversing
changes made to 95bf0dfa60.
2026-05-31 09:00:15 -07:00
Shay
6b58839beb fix(language): refresh syntax pack checksums 2026-05-31 03:55:52 -07:00
Shay
71c4e83f2c fix(language): update syntax gloss collision rename 2026-05-31 03:55:26 -07:00
Shay
4dfc7fd764 fix(language): avoid syntax consequent collision 2026-05-31 03:54:15 -07:00
Shay
27f677971d feat(language): add en core syntax glosses 2026-05-30 19:14:11 -07:00
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
63f2544862 feat(adr-0176): en_core_comparatives_v1 pack + comparative-scalar extraction
The curated, irreducible world-fact primitives multi-step composition needs
(ADR-0175 section 10: the engine can't derive 'twice = 2' from arithmetic). The
microscope flagged these via the 0015/0025/0024/0033 wrongs.

language_packs/data/en_core_comparatives_v1/: 9 closed-set multiplicative
comparatives (twice/double/triple/quadruple/half/quarter + inflections) -> scalar
ops. manifest.json with sha256 of the bytes on disk (CLAUDE.md pack rule).
Refusal-preferring: non-terminating/ambiguous comparatives (a third, several)
deliberately excluded; expansion via HITL corridor.

generate/derivation/comparatives.py: extract_comparative_scalars() ->
ComparativeScalar(op, scalar, span, cue). Fixed lexemes + the '<number> times'
pattern (digit or word-number via WORD_NUMBERS). Lexeme-level (ADR-0165);
deterministic (text-order); supplies only the SCALAR primitive — referent
binding is the multi-step search's job (ADR-0176).

14 tests incl. refusal-preferring discipline + pack integrity (manifest checksum
matches bytes on disk). Verified: derivation suite 45/45; ruff clean; smoke 67;
packs 141. Not wired into serving (data + extractor ready for ADR-0176 MS phases).
2026-05-28 16:07:35 -07:00
Shay
aa15dc1f3d feat(adr-0174-phase4): in-loop contemplate + en_core_names_v1 pack
ADR-0174 Phase 4 — deterministic search adapter for evidence that
disambiguates surviving hypothesis sets. First load-bearing use case:
gendered-pronoun resolution via the en_core_names_v1 pack — turns
the Phase 3a multi-actor defense from refuse-on-ambiguity into
admit-via-evidence when an unambiguous gendered name binds the
pronoun to one antecedent.

generate/comprehension/contemplate.py (new, ~310 lines):
  - Resolution dataclass (closed-set kind + source + evidence shape)
  - VALID_RESOLUTION_KINDS = {eliminate, admit_unknown}
  - VALID_RESOLUTION_SOURCES = {vault, pack, audit_history}
  - contemplate() orchestrator — adapters consulted in precedence
    order: vault > pack > audit_history (ADR-0174 §Open Q#3)
  - _consult_packs() — gendered-pronoun resolution implementation
  - _consult_vault() and _consult_audit_history() — stubs (Phase 4b)
  - _PRONOUN_GENDER closed map (she/he gendered; they/them epicene)
  - _load_names_pack() with @lru_cache; refusal-preferring on
    absent pack

language_packs/data/en_core_names_v1/ (new pack):
  - gender.jsonl — 59 unambiguously-gendered English first names
    (30 female, 29 male), alphabetically sorted, JSONL with schema
    {name: str, gender: 'female'|'male'}.  Covers names appearing
    in train_sample/v1 GSM8K problems (Alice, Bob, Daniel, Malcolm,
    Erica, Jan, Tina, etc.).  Deliberately excludes ambiguous-
    gender names (Jordan, Alex, Casey, Pat, Taylor, Morgan, Sam,
    Chris, Robin, Riley).
  - manifest.json — pack metadata with sha256 checksum
    (f65836e7a25a9db8aae984d259b60e161574ff3b4bb135a924aa767a794fbd21),
    entry count, schema declaration, ambiguity discipline,
    expansion pathway through HITL corridor, wrong=0 protection
    contract.

generate/math_candidate_graph.py:
  - Phase 4 wiring at the multi-actor defense site (was: refuse
    on len(_distinct_priors) > 1; now: invoke contemplate first,
    fall through to defense when contemplate returns None).
  - On contemplate.kind='admit_unknown' from pack source: extract
    chosen antecedent from evidence, override _antecedent, clear
    _multi_actor_ambiguous, proceed to admit-via-PronounResolution.
  - On contemplate=None: fire new 'ambiguous_unresolvable'
    contemplate trace event AND original 'no_antecedent_ambiguous'
    lookback event, drop candidates.

tests/test_adr_0174_phase4_contemplate.py (new):
  27 acceptance tests covering: primitive contract (empty/single-
  survivor noop), Resolution dataclass invariants (5 refusal
  paths), names pack load + content spot-checks, pronoun gender
  lookup (gendered + epicene), 6 gendered-pronoun resolution
  cases (she/he success, same-gender refusal, unknown-name
  refusal, epicene refusal, no-matching-gender refusal), end-to-
  end wiring through parse_and_solve, determinism (two calls
  byte-identical, evidence sorted), closed-set contracts,
  wrong=0 + case-0050 canary.

tests/test_adr_0174_phase3_lookback.py + phase3b_compound_clause.py:
  Updated the multi-actor defense tests to use SAME-GENDER
  antecedents (Alice + Mary) so Phase 4 contemplate cannot
  disambiguate via gender pack — the Phase 3a defense still
  fires. (For mixed-gender antecedents the new behavior is
  correct admit-via-evidence; that's tested in Phase 4 suite.)

End-to-end answer-correctness caveat (documented in test
docstrings):
  Phase 4 trace events fire correctly when the recognizer-
  injection path encounters multi-actor pronoun cases that the
  pack disambiguates.  However the regex parser ALSO produces
  candidates for simpler pronoun-subject shapes (without
  intervening prepositional phrases); those compete in the
  Cartesian product and the contemplate-resolved binding may be
  shadowed.  This is the latent regex-path pronoun hazard tracked
  in project-adr-0174-multi-actor-pronoun-hazard memory.  Full
  answer lift on train_sample requires regex-path defense (Phase 5
  regex retirement work).

Acceptance:
- 285/285 ADR-0174 + math_problem_graph tests pass
- Smoke 67/67, packs 141/141
- train_sample 3/47/0 preserved (wrong=0 held)
- Phase 4 trace event fires end-to-end on multi-PP pronoun-subject
  case: contemplate/resolved with chosen=Alice, evidence pack
  Alice=female + Bob=male

References: ADR-0174 §In-loop contemplation, CLAUDE.md §Lookback
Review Discipline, docs/handoff/ADR-0174-PHASE-3B-4-COMBINED-SCOPE.md,
docs/handoff/phase-3b-4-skeleton/ (skeleton dispatch source),
project-adr-0174-multi-actor-pronoun-hazard memory.
2026-05-28 12:09:52 -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
78ddab79b4
feat(consumption-wiring): CW-1 + CW-2 — Frame + Composition registry loaders (#398)
Closes the consumption-half of the math teaching loop for two of three
sub-types per docs/handoff/CONSUMPTION-WIRING-DISPATCH-PACK.md (PR #397).
Companion to the doctrinal brief in PR #396.

Modules
-------
- language_packs/compile_frames.py — byte-deterministic compile of
  frames/*.jsonl → frames.jsonl (sorted by (frame_category, surface_form))
- language_packs/compile_compositions.py — same shape for
  compositions/*.jsonl → compositions.jsonl
- generate/comprehension/frame_registry.py — load_frame_registry()
  mirroring load_lexicon: cache by (path, mtime, sha256), manifest
  checksum verification (optional frame_checksum field), polarity
  validation, conflict detection, empty-registry no-op
- generate/comprehension/composition_registry.py — same shape PLUS:
    * SAFE_COMPOSITION_CATEGORIES enforced at LOAD (defense in depth;
      raises WrongCompositionCategory on any unsafe category — protects
      against pack edits that bypass the handler)
    * polarity "falsifies" exposed via is_falsified() (consumer must
      suppress; not silently treated as affirms)
- language_packs/compiler.py — manifest verification extended for
  frame_checksum + composition_checksum, mirroring the proven
  glosses_checksum pattern (optional fields; backward-compatible)
- generate/recognizer_anchor_inject.py — inject_from_match consults
  composition_registry when the per-category injector returns empty
  AND the matcher publishes ``composition_shape`` in parsed_anchors.
  Registry is a gate (admissibility) not an arithmetic primitive
  (ADR-0169 §"Mutation boundary").

Tests (38 new, all green)
-------------------------
tests/test_frame_registry_load.py            (11 tests)
tests/test_composition_registry_load.py      (11 tests)
tests/test_composition_consult_in_injector.py ( 6 tests)
tests/test_consumption_case_0050_hazard_pin.py( 3 tests, parametrized
                                                 over allowlist)
tests/test_consumption_empty_registry_no_op.py( 4 tests)
tests/test_consumption_partition.py           ( 3 tests)

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

Suite results
-------------
core test --suite teaching -q  → 93 passed
core test --suite runtime  -q  → 20 passed
core test --suite packs    -q  → 51 passed
core eval gsm8k_math --split public → 150/150, wrong=0

Truth-test rows (6-row binding table in dispatch pack):

  #1 Case 0019 admits ............. PARTIAL — see Scope Boundary below
  #2 Case 0050 stays refused ....... PASS
  #3 train_sample 3/47 → ≥4/46 ..... PARTIAL — same as #1
  #4 wrong == 0 preserved .......... PASS
  #5 public split 150/150 .......... PASS
  #6 Empty-registry no-op .......... PASS

Scope Boundary (honest finding)
-------------------------------
Rows #1 and #3 (case 0019 admission) require a matcher extension that
publishes ``composition_shape`` + a pre-composed CandidateInitial in
parsed_anchors. The existing currency_amount / multiplicative_aggregation
matchers in generate/recognizer_match.py are detection-only (return
empty parsed_anchors). This PR ships the consumption infrastructure
correctly but the runtime path remains dormant until a follow-up PR
extends the matcher. The dispatch pack's truth test #1/#3 cannot fire
without that extension.

The wiring is positioned correctly: inject_from_match → consult
composition_registry → admit on affirms-with-payload, suppress on
falsifies, refuse on absence. A synthetic recognizer match with
populated composition_shape + composed_initial DOES admit through the
new path (covered by 6 tests in test_composition_consult_in_injector.py).

A follow-up brief naming the matcher-extension work is the
recommended next step.

Anti-regression invariants verified
-----------------------------------
- wrong == 0 on core eval gsm8k_math (public 150/150)
- case 0050 stays refused (parametrized over allowlist categories)
- ADR-0166 — no new eval lanes
- ADR-0167 partition — no cognition imports in any new module
- Empty-registry runtime byte-identical to today (no-op test)
- SAFE_COMPOSITION_CATEGORIES enforced at write AND load
- polarity semantics (affirms vs falsifies) honored
- engine_state/* never committed
2026-05-27 16:17:03 -07:00
Shay
44c0aa2896
feat(ADR-0169/CC-2+CC-3): CompositionClaim ratification handler + decomposer heuristic tightening (#393)
PR-β of the CompositionClaim wave (CC-2 + CC-3 bundled per the brief
pack — CC-3's heuristic depends on CC-2's new change_kind Literal value).
Mirrors the F1 / ADR-0168 FrameClaim template 1:1 with composition-specific
substitutions.

CC-2 — handler implementation
  - teaching/math_composition_proposal.py — MathCompositionClaimProposal
    adapter per ADR-0169.1 §"Data shape". Frozen dataclass, deterministic
    proposal_id / claim_signature, source="math_audit" pin at the
    proposal layer (rejects "corpus" laundering).
  - teaching/math_composition_ratification.py — apply_composition_claim()
    handler. SAFE_COMPOSITION_CATEGORIES = {multiplicative,
    additive, subtractive}_composition per ADR-0169 §"Initial safe
    category scope". New WrongCompositionCategory exception per
    ADR-0169.1 §"Trip-wires" #8. Writes only to
    language_packs/data/en_core_math_v1/compositions/{category}.jsonl;
    no solver / parser / decomposer / runtime mutation.
  - workbench/readers.py — _HANDLER_DISPATCH now routes
    composition_reclassification → CompositionClaim; suggested_cli
    branch added for both read_math_proposal and ratify_math_proposal.
  - teaching/math_contemplation_proposal.py — ChangeKind Literal +
    _VALID_CHANGE_KINDS frozenset extended with
    composition_reclassification.
  - language_packs/data/en_core_math_v1/compositions/.gitkeep —
    reviewed-pack scaffold.
  - tests/test_math_composition_ratification.py — 22 tests including
    case 0050 hazard pin, cross-process replay equivalence, queue-order
    independence, partition, no-corpus-laundering, dispatch wire,
    Literal acceptance, JSONL round-trip.
  - tests/test_adr_0172_w1_shape_proposal.py — parametrize round-trip
    over all 5 change_kinds.
  - core/cli.py — teaching suite tuple includes new test file.

CC-3 — decomposer heuristic tightening
  - teaching/math_contemplation.py::_CHANGE_KIND_BY_PAIR:
    + (incomplete_operation, quantity_extraction)         → composition_reclassification
    + (incomplete_operation, multi_quantity_composition)  → composition_reclassification
    - (unexpected_category, multi_subject_sentence)       demoted to injector_sub_shape
      (was frame_reclassification; FrameClaim SAFE_FRAME_CATEGORIES doesn't
       cover this — needs ReferenceClaim/CompositionClaim)
    - (unexpected_category, descriptive_frame_question)   demoted to injector_sub_shape
      (was frame_reclassification; needs SlotClaim, not FrameClaim)
    Updated hypothesis-step justification text to reflect new dispatch
    table.
  - tests/test_adr_0172_w2_decomposer.py — distribution assertion
    tightened from "≥3 matcher, ≥2 frame" to exact counts:
    3 matcher / 2 composition / 3 injector / 0 frame. New
    per-pair tests for the four CC-3 dispatch changes.

Verification on real audit_brief_11.json (20 of 47 highest-leverage
refusals now routable):

  2  composition_reclassification   (12 quantity_extraction + 8 multi_quantity_composition)
  3  injector_sub_shape             (2 multi_subject + 2 descriptive_frame + 4 unattached_quantity)
  3  matcher_extension              (9 pre_frame_filler + 4 fraction_percentage + 4 pronoun)
  0  frame_reclassification         (the two prior misroutes are gone)

Workbench POST /math-proposals/{id}/ratify on either composition
proposal now returns 200/routed with a real apply_composition_claim()
command instead of 501.

Suites green:
  - core test --suite teaching -q  → 71 passed
  - core test --suite runtime -q   → 20 passed

Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-27 15:15:11 -07:00
Shay
c6a9bb0096
feat(ADR-0168/F1): FrameClaim ratification handler (Tier 1.5) (#389)
Implements the FrameClaim ratification handler per ADR-0168 doctrine
and the ADR-0168.1 MathFrameClaimProposal adapter.  Mirrors the
ADR-0167 W2-D LexicalClaim template (apply_lexical_claim) but lifts
the safe surface from drain-class lexical entries to allowlisted
frame-opening categories — the next sub-type up the wrong=0 hazard
ladder.

teaching/math_frame_proposal.py
  + MathFrameClaimProposal dataclass (ADR-0168.1 §"Data shape")
  + MathReaderRefusalEvidencePointer with source pinned to "math_audit"
  + build_evidence_pointer() — only sanctioned constructor; rejects None
    missing_operator
  + build_frame_claim_proposal() — enforces:
      • surface_form non-empty after normalization
      • frame_category in the ADR-0168 allowlist
      • polarity in {affirms, falsifies}
      • ≥1 evidence pointer with source="math_audit"
      • source="corpus" rejected as schema-illegal (ADR-0168.1
        §"Evidence floor")
  + compute_claim_signature() / compute_proposal_id() / canonical_bytes()
    — deterministic identity per ADR-0168 §"Replay obligations" #1

teaching/math_frame_ratification.py
  + SAFE_FRAME_CATEGORIES = {increment_frame, decrement_frame,
    transfer_frame, remainder_frame} — no other categories
  + Error hierarchy: RatificationError, WrongClaimSubType,
    WrongZeroViolationCandidate, AlreadyRatified, EvidenceTampering,
    UnknownCategory, InvalidPolarity, EvidenceLaundering
  + FrameRatificationReceipt dataclass with before/after SHA + evidence_hash
  + apply_frame_claim(claim, frame_category, polarity, reviewer, pack_root,
    evidence_source="math_audit"):
      • rejects evidence_source != "math_audit" (ADR-0168.1 §"Evidence floor")
      • rejects polarity outside {affirms, falsifies}
      • rejects claim.sub_type != "frame"
      • rejects evidence_hash tampering (recomputes from audit_row)
      • rejects frame_category outside SAFE_FRAME_CATEGORIES
      • writes language_packs/data/en_core_math_v1/frames/{category}.jsonl
      • idempotent: same (surface, category, polarity, evidence_hash)
        raises AlreadyRatified
      • duplicate evidence appends evidence_hash to existing row
        (ADR-0168.1 §"Idempotency" path #1)
      • polarity=falsifies records non-opener; never appends to compiled
        lexicon or manifest

language_packs/data/en_core_math_v1/frames/.gitkeep
  Directory scaffold for the reviewed frame source files.

workbench/readers.py
  _HANDLER_DISPATCH gains "frame_reclassification" → "FrameClaim".
  GET /math-proposals/{id} detail and POST /math-proposals/{id}/ratify
  now return suggested_cli pointing at apply_frame_claim().

core/cli.py
  teaching test-suite tuple gains tests/test_math_frame_ratification.py.

tests/test_math_frame_ratification.py — 14 tests:
   1. SAFE_FRAME_CATEGORIES is exactly the ADR-0168 allowlist
   2. apply writes a frame entry for a safe category
   3. receipt records before/after sha + evidence_hash
   4. idempotent same-evidence → AlreadyRatified
   5. rejects non-frame sub_type → WrongClaimSubType
   6. rejects categories outside SAFE_FRAME_CATEGORIES → WrongZeroViolationCandidate
   7. rejects invalid polarity → InvalidPolarity
   8. rejects evidence_hash tampering → EvidenceTampering
   9. rejects source="corpus" → EvidenceLaundering (ADR-0168.1 §"Evidence floor")
  10. case 0050 hazard pin — after ratification, case 0050 still refuses
  11. polarity=falsifies branch records non-opener; affirms+falsifies coexist
  12. duplicate evidence appends evidence_hash, does not create a second row
  13. manifest.json checksum unchanged by frame ratification
  14. alphabetical sort by surface_form preserved across writes

Suite verification
  core test --suite teaching -q → 47 passed (was 33; +14 new)
  core test --suite runtime  -q → 20 passed
  tests/test_math_lexical_ratification.py → 15 passed (untouched, regression-clean)
  tests/test_adr_0172_w4_workbench_e2e.py → 7 passed (existing dispatch tests still hold)

Doctrine invariants preserved
  - wrong=0: case 0050 still refuses after ratification
  - replay equivalence: claim_signature and proposal_id are deterministic
    (sha256 of canonical identity, clock-time-independent)
  - refusal-first: no runtime mutation; handler is the only mutation
    boundary and writes only the reviewed frames/ source tree
  - ADR-0167 partition: math-audit evidence stays math-domain; corpus
    evidence is rejected loudly

Brief-correction note: the brief named the scaffold path
"packs/en_core_math_v1/frames/.gitkeep" but the existing math pack lives
at language_packs/data/en_core_math_v1/ (no top-level packs/en_core_math_v1
exists).  Scaffold placed at language_packs/data/en_core_math_v1/frames/
to mirror the existing lexicon/ source-tree convention; apply_frame_claim
defaults pack_root to that location.
2026-05-27 14:10:43 -07:00
Shay
9792f66f90
feat(brief-B1): lexicon closure wave 3 — unknown_word 5→3, wrong=0 preserved (#368)
Adds 3 drain_token lemmas to en_core_math_v1 closing 2 of 3 remaining
lexicon_entry refusals from the prior wave:

- path (case 0049, new lemma)
- journey (case 0049 follow-on after path resolved)
- sees → alias of existing "see" lemma (case 0040)

The third remaining lexicon_entry refusal (case 0001, '+') is
deliberately NOT closed: '+' is an arithmetic operator literal, not a
lexical token. Adding it as drain_token would silently drop arithmetic
content from problems like "5 + 3 apples", a wrong=0 hazard. Documented
in the PR body and audit artifact.

Refusal taxonomy shift:
- unknown_word: 5 → 3 (-2)
- unresolved_pronoun: 3 → 4 (+1) — case 0049's pronoun barrier surfaced
- incomplete_operation: 20 → 21 (+1) — case 0049's quantity gap surfaced

Hard invariants:
- wrong == 0 (admitted=0, verified)
- case 0050 hazard pinned (refused at sentence_index=0)
- manifest checksum unchanged (per-category source file edit)
- no teaching-store mutation; no reader runtime change
2026-05-27 10:13:09 -07:00
Shay
1cc05d9cce
docs: schema-defined proof obligations doctrine + convergence-site invariant + FOLLOWUPS §6 (#360)
Three small surgical anchors capturing the verified architectural
insight surfaced during the ADR-0167 wave (no new ADR — the gap claim
that prompted this resolved on verification; what remains is a sharper
residual question worth memorialising).

1. CLAUDE.md — new "Schema-Defined Proof Obligations" section between
   Documentation Discipline and Validation Through CLI.  Generalises
   the wrong=0 invariant pattern: schema types that name structural
   properties are real only when an executing test can meaningfully
   fail under the violations it is written to catch.  Three-step rule
   for treating a schema as load-bearing.

2. language_packs/compiler.py — ARCHITECTURAL INVARIANT comment on
   _apply_mounted_primary_domain_resonance naming it as the single
   convergence-decision site for DEPTH_ROOT/DEPTH_RELATION packs.
   Anchors the doctrine at the code site so any future modification
   trips on the reference to the holonomy proof's coverage gap.

3. docs/handoff/ADR-0167-FOLLOWUPS.md §6 — captures the structural-vs-
   blend convergence isolation question.  HolonomyAlignmentCase IS
   executed today (we verified), but the existing test doesn't
   distinguish structurally-derived convergence from blend-induced
   convergence.  Ablation test or reframed claim — both acceptable
   resolutions.

Verified before commit:
- All 13 architectural references in the Gemini analysis resolve
  exactly: triliteral 0.30, root 0.40, prefix 0.03/(idx+1), stem 0.24,
  _INFLECTION_PRIORITY case-near-last, _apply_mounted_primary_domain_resonance
  with 40% English-prototype blend, HolonomyAlignmentCase defined
  AND executed
- tests/test_alignment_graph.py: 8 passed (no behavioural change)
- Documentation discipline (#355) honoured: pure Markdown, no HTML

No code behaviour changes.  No runtime effect.  Drops the larger
ADR-0168-PROPOSAL idea — the gap claim that prompted it dissolved
under verification.
2026-05-27 07:52:20 -07:00
Shay
66ef4ad07c
feat(brief-11/11B-step-2): lexicon closure — unknown_word 11→5, wrong=0 preserved (#348)
## Summary

Lexicon-entry closure track per Brief 11D recommendation (Candidate A,
sub-PR 1). Adds 12 drain_token lemmas + 1 alias to `en_core_math_v1`.

`unknown_word` row strictly decreases: **11 → 5** (-6 cases moved past
the first-pass vocabulary gap). `wrong == 0` preserved. `correct` does
not move because admitted=0 (the unblocked cases now refuse at
downstream frames — real new work becoming visible, not regression, per
Brief 11 §Gate 1).

## Additions (all category=drain_token)

| Lemma     | Surfaced from              |
|-----------|----------------------------|
| along     | case 0049 (3rd-wave)       |
| animals   | case 0040 (3rd-wave)       |
| decrease  | case 0005                  |
| jacks     | case 0024 (jumping jacks)  |
| length    | case 0006 (3rd-wave)       |
| previous  | case 0006                  |
| reach     | case 0015                  |
| stray     | case 0040                  |
| too       | case 0039                  |
| uphill    | case 0049                  |
| which     | case 0001                  |
| your      | case 0001 (3rd-wave)       |
| weight → weights (alias) | case 0021     |

All classified as `drain_token` (the only category that cannot open a
frame and therefore cannot create wrong admissions per Brief 11
§"correct-count greed" doctrine). Reclassifying any as
accumulation/depletion/transfer verbs would risk wrong>0 by opening a
malformed operation_frame.

## wrong=0 verification

- `assert audit_problem(case_0050)` returns `ReaderRefusal` at
  sentence_index 0 (pinned by `test_hazard_case_0050_remains_refused_pre_frame`)
- 50-case audit: `admitted=0, refused=50` (pinned by
  `test_no_case_admits_after_lexicon_closure`)
- No reader runtime changes; pack-only mutation in a single
  per-category source file
- Manifest checksum unchanged: source-file edit doesn't regenerate the
  compiled `lexicon.jsonl`; loader reads per-category sources for
  alias-aware entries (see `generate/comprehension/lexicon.py:127`)

## Test plan

- 11 new tests in `tests/test_brief_11b_step2_lexicon.py`:
  - 4 pack-additions pinning (categories, provenance, aliases, sort order)
  - 4 reader-effect / hazard tests (admitted=0, case 0050 refused,
    unknown_word row strictly decreased, manifest checksum unchanged)
  - 2 loader-integrity tests (new lemmas + aliases resolve through
    `load_lexicon` → `lookup`)
- 12 existing tests in `tests/test_brief_11b_audit_artifact.py` pass
  (taxonomy counts updated to post-step-2 values)
- 23 existing tests in `tests/test_brief_11_audit.py` pass

## Hard invariants preserved

- `wrong == 0` — no admissions, no frame-opener miscategorisation
- ADR-0166 — no new canonical eval lanes; existing
  `gsm8k_math/train_sample/v1/` artifact updated in-place
- No teaching-store mutation; pack mutation is explicit, single-file,
  reviewed
- Manifest checksum unchanged (compiled lexicon.jsonl byte-identical)

## Follow-up

- 3 lexicon_entry refusals remain (case 0001 '+', case 0040 'sees',
  case 0049 'path'). Not addressed in this PR: '+' is an arithmetic
  literal (would change semantics of drain), 'sees' and 'path' have
  many other downstream barriers. Address with next-bottleneck PR.
- The 6 cases now refusing at later frames feed directly into Brief
  11D Candidate A sub-PR 2 (which bottleneck class to attack next).
2026-05-27 06:06:41 -07:00
Shay
60043973b0
feat(comprehension/10): Phase 2 statement-frame reader (ADR-0164.4) (#335)
Extend the comprehension reader from question-only scope to whole-
problem scope. Phase 1 (Brief 8 / #326) implemented question_frame;
this brief implements initial_state_frame, operation_frame, and
descriptive_frame, plus finalize() projection into a strict
ADR-0115 MathProblemGraph.

Architecturally correct under ADR-0164.3; not yet productive on
GSM8K train_sample. Below-floor measurement documented; specific
bottlenecks tabled for Phase 2.1 follow-up.

What landed

- Frame-opener dispatch in lifecycle.py for the three new statement
  frames, plus rule handlers (_rule_op_*, _rule_preframe_*,
  _rule_descriptive_*).
- finalize(state) -> MathProblemGraph | ReaderRefusal: pure
  projection with closure checks (entity registry non-empty,
  unknown target bound, every op/initial references a known entity,
  Decimal precision projects losslessly).
- _classify extended to 3-tuple (category, surface, decimal_value)
  with possessive strip retry. Brief 8.2's sentence-initial
  lookup-first + gender-skip preserved AND extended to mid-sentence
  (gender is enrichment everywhere, never admission).
- Whole-problem coexistence dispatch in math_candidate_graph.py
  (config.comprehension_reader_questions=True): reader attempts the
  whole problem; on any ReaderRefusal falls through to existing
  regex parser. All-or-nothing per the brief.
- Lexicon expansion (carried into renamed proper_noun_gender_*
  files): +2 accumulation_verb (adopt, invest), +2 currency_unit_noun
  (dollar, cent), +6 capacity_verb (fill, lift, play, work, finish,
  drive), +5 female names (allison, brooke, jan, marion, sidney),
  +14 male names (bart, fernando, georgie, jake, jed, jeremie, jose,
  orlando, rex, rudolph, steve, troy, xavier, yun), +numerous
  count_unit_noun, drain_token, time_unit_noun.
- ADR-0164.4-phase2-statement-frame-reader.md — the architectural
  rationale and acceptance contract.

Measurement (reader_phase2_delta.json):

  flag-OFF: correct=3 refused=47 wrong=0
  flag-ON:  correct=3 refused=47 wrong=0
  delta:    0/0/0

Below the brief's floor of correct >= 4. Architecture is sound — the
reader admits cases as graphs when the structure resolves, refuses
cleanly otherwise, preserves wrong=0 across both flag states.

Bottleneck table (from per-case attribution):

  count  refusal_class           dominant cause
  -----  ----------------------  ------------------------------------
  18     incomplete_operation    multi-quantity ops; no-quantity op
  11     unknown_word            "hundred", "presently", "one-hour",
                                 non-math verbs (compound numerics,
                                 lexicon gaps)
  6      unexpected_category     fraction / percentage literals;
                                 multi-subject sentences
  6      unresolved_pronoun      "them", "their", "his" with no
                                 compatible entity
  5      unattached_quantity     quantity never bound to a unit
  1      no_question_target     question parsed but slot never set

Closing the gate to mixed-bounded [4, 24] is Phase 2.1 scope: extend
composition rules for multi-quantity ops, add fraction/percentage
primitives (per ADR-0164.1 amendment), expand lexicon for the
remaining unknown_word cases, extend pronoun resolution.

Invariants preserved

- wrong = 0 in both flag states ✓
- flag-OFF byte-identical to today ✓
- determinism (50/50 identical runs) ✓
- Capability axes G1-G5, S1 unchanged ✓
- Reader tests: 19 (Phase 2) + 18 (Phase 1, post-update) + 53 (pack)
  + 76 (lexicon + primitives) = 166 specific to this change; all pass
- core test --suite smoke -q: 67 passed

Rebase note

This PR was authored against an older base; rebased onto current
main to incorporate #333 (Brief 8.2 universal proper_noun_token
primitive) and #334 (ADR-0166 measurement discipline). The rebase
required:
- Lexicon files renamed proper_noun_entity_* -> proper_noun_gender_*
  (with the Phase 2 additions merged into the gender_* files)
- Compiled lexicon.jsonl unchanged from #333's 207-entry state
  (Phase 2's per-category additions are runtime-visible via the
  source loader, not via the compiled file)
- _classify reconciled with Brief 8.2's sentence-initial dispatch +
  Phase 2's 3-tuple decimal-value return
- All dispatch tables and category checks updated to reference
  proper_noun_token (singular) instead of proper_noun_entity_{f,m}
- Three Phase 1 test expectations updated to reflect Phase 2
  behavior (proper noun at position 0 now opens statement pre-frame
  instead of refusing; pronoun resolution applies per ADR-0164.2)

Per ADR-0166's three-question test, this PR is honest measurement:
capability exists, at least one case admits, lane distinguishes
presence from absence — which the bottleneck table demonstrates.

Refs ADR-0164.3 §Phasing Phase 2, ADR-0164.1 amendment (Brief 8.2),
ADR-0166 §"Mixed (notable but not blocking)" — except here, below
floor.
2026-05-27 05:03:56 -07:00
Shay
b3dbde94b4
feat(comprehension/8.2): universal proper_noun_token primitive (#333)
ADR-0164.1 amendment: replace name-whitelist entity admission with a
universal lexeme primitive that recognizes any capitalized token as a
proper noun. The gender-coded name lists are demoted from admission
criterion to enrichment-only lookup. A name outside the curated lists
still admits cleanly with gender="unknown" — ADR-0164.2's pronoun
resolution rules handle the unknown case via single-salient fallback
or refuse with ambiguous_pronoun_referent.

Universal at the primitive layer: the new proper_noun_token primitive
is domain-agnostic. It sits in the shared PRIMITIVE_REGISTRY and is
available to every current and future reader (math, narrative,
code-comment, multi-lingual). The math reader is its first consumer.

Pattern: ^[A-Z][A-Za-z'-]*[a-z][A-Za-z'-]*$
- requires capitalized first letter
- requires ≥1 lowercase letter (rejects all-caps acronyms)
- allows internal apostrophes (O'Brien) and hyphens (Mary-Anne)
- matches "Tina", "Bob", "Marnie", "McDonald" — rejects "TINA",
  "123", "$5.00" (those go to their own primitives)

Sentence-initial lookup-first dispatch (lifecycle._classify):
- At token_index == 0: lookup() first, skipping proper_noun_gender_*
  categories (treated as not-found so the primitive can fire). If
  lookup misses, primitive scan picks up novel names. Inverts the
  question from "is this a name?" to "is this a known common word?"
- At token_index > 0: primitive-first with UNIT_CATEGORY_TOKEN ceding
  to operational lexicon for currency_unit_noun overrides.

Lexicon rename (per-category source files):
- proper_noun_entity_female.jsonl -> proper_noun_gender_female.jsonl
- proper_noun_entity_male.jsonl   -> proper_noun_gender_male.jsonl

Compiled lexicon.jsonl: rename the two semantic_domain tags; drop
"marnie" (was only in proper_noun_entity_female, now absent from
the gender-coded sources). Net: 208 -> 207 entries. New manifest
checksum: 1fb9b0d790258736267d528e8e8a2436ce88b9ce690805fe2813ba077861ba2a

New helper gender_of_proper_noun(surface, lexicon) returns
Literal["female","male","neuter","unknown"] — pure enrichment lookup,
never gates admission.

Measurement (reader_phase1_plus_proper_noun_delta.json):
- pre-primitive baseline: correct=3 refused=47 wrong=0
- post-primitive measurement: correct=3 refused=47 wrong=0
- No regression on wrong=0
- No net admission increase observed in this train-sample harness;
  the architectural value is for future text outside the curated
  gender lists (Sonnet's #332 expanded those to cover GSM8K names).

Tests:
- test_lexeme_primitives.py: registry count 8 -> 9, proper_noun_token
  fires + variants (Bob, Marnie, McDonald, O'Brien, Mary-Anne),
  numeric/all-caps refusals, numeric-literal still wins overlap on "123"
- test_reader_question_frame.py: 5 new tests for sentence-initial
  dispatch + unknown-gender pronoun resolution + novel-name admission
  via primitive (Zelda)
- test_en_core_math_v1_pack.py: category counts updated; mutual-exclusion
  between gender_female and gender_male preserved; total 208 -> 207
- test_lexicon.py: category list + lookup assertion updated to renamed
  proper_noun_gender_female
- test_proper_noun_primitive_universality.py: new test module asserting
  domain-agnostic property of the primitive

Validation:
- pack + lexicon + primitive tests: 147 passed
- reader + universality tests: 22 passed
- smoke lane: 67 passed

Closes the engine_state question by leaving those files untracked
(repo discipline: runtime artifacts never enter PRs).

Refs ADR-0164.1 amendment, ADR-0164.2 §EntityRegistry, ADR-0165
§Legitimate uses (the new primitive passes the three-question test).
2026-05-26 22:16:34 -07:00
Shay
4ceb37b3b0
feat(comprehension): swap reader stubs for real primitive + lexicon (Brief 8.1) (#330)
Eliminates generate/comprehension/_interface_stubs.py and wires
lifecycle.py to the real modules landed in #324 (lexeme_primitives)
and #325 (lexicon/loader).

Changes:
- lifecycle.py: imports redirected to LexemeMatch/scan and
  Lexicon/LexiconEntry/load_lexicon/lookup; _classify reordered
  so lexicon lookup precedes primitive scan (ADR-0164.1 mass-noun-token
  boundary note); punctuation dispatch inlined as category (d)
- _interface_stubs.py: deleted
- en_core_math_v1 lexicon source files: added question_discrete_qty,
  question_continuous_qty, question_comparative, aggregate_modifier,
  modal_aux, copula_verb, count_unit_noun, time_unit_noun, drain_token;
  supplemental entries for accumulation_verb (+need, +want),
  proper_noun_entity_female (+monica), proper_noun_entity_male (+malcolm);
  total moved from currency_unit_noun to aggregate_modifier
- test_en_core_math_v1_pack.py: updated EXPECTED_CATEGORY_COUNTS for
  ADR-0164-ratified deltas; decoupled EXPECTED_COMPILED_TOTAL (208) from
  per-category sum; provenance check accepts both ported and supplemental tags

Gate: 15/15 reader tests, 137/137 primitive+lexicon+pack tests,
67/67 smoke, 13/13 packs — all green.
2026-05-26 20:48:33 -07:00
Shay
48ea34bd52
feat(en_core_math_v1): seed lexicon pack for ADR-0164 comprehension reader (#322)
Ports the closed-set vocabulary from generate/math_candidate_parser.py and
generate/math_roundtrip.py into a new language pack en_core_math_v1, following
the manifest-checksum discipline of en_core_cognition_v1 and en_core_relations_v1.

208 lemmas across 11 semantic categories:
  - accumulation_verb (17)   — from ADD_VERBS + _COND_ADD_VERBS + _EARNINGS_VERBS
  - depletion_verb    (15)   — from SUBTRACT_VERBS + _COND_SUBTRACT_VERBS
  - transfer_verb      (7)   — from TRANSFER_VERBS; give/send/return removed from depletion
  - currency_unit_noun (8)   — from _MASS_NOUNS
  - entity_pronoun     (4)   — from _Q_SUBJECT_PRONOUN
  - proper_noun_entity_female (62) — from _FEMALE_NAMES
  - proper_noun_entity_male   (76) — from _MALE_NAMES
  - possession_verb    (1)   — have/has/had collapsed to bare lemma
  - capacity_verb     (13)   — from _CAPACITY_VERBS (pick/pack/make exclusive here)
  - question_open      (2)   — how, what
  - residual_modifier  (3)   — left, remaining, after (attested in _COND_OP_Q_RE)

Pack is NOT wired into any runtime path (ADR-0164 Phase 3).
Source constants in math_candidate_parser.py are unchanged.
Deferred categories documented in manifest.json `deferred` field.

53 contract tests cover: checksum, per-category counts, provenance,
mutual-exclusivity invariants (acc ∩ dep = ∅, acc ∩ cap = ∅, dep ∩ xfer = ∅),
and ≥2 semantic domains per compiled entry.
2026-05-26 19:36:57 -07:00
Shay
ab4c7cb0c3
feat(epistemic): Phase 3 state tagging spine (#220)
* feat(epistemic): add first-class state enums

* feat(epistemic): tag TurnEvent with state axes

* feat(epistemic): serialize turn state axes

* feat(packs): tag curated and inferred unit entries

* feat(epistemic): expose word-level state on manifold

* feat(epistemic): expose vault status mapping

* feat(epistemic): preserve pack entry states through compiler

* test(epistemic): cover phase 3 state tagging spine

* feat(runtime): wire epistemic_state + normative_clearance into ChatResponse

Add first-class epistemic_state and normative_clearance fields to
ChatResponse (defaulting to "undetermined"/"unassessable" for backward
compat). Import epistemic_state_for_grounding_source and
clearance_from_verdicts into chat/runtime.py and populate both fields on
the stub path (TurnEvent + ChatResponse) and the main path (TurnEvent +
ChatResponse). Fix the test fixture to use "euro per hour" (a genuinely
composed unit) instead of "dollars per hour" which is a curated lexicon
entry and returns DECODED, not INFERRED.

* test(cognition): update term_capture_rate baseline from 0.9167 to 1.0

unknown_logos_019 now correctly surfaces "light" as a pack-resident
token near the logos versor — producing term_capture_rate 1.0 on both
main and Phase 3. The 0.9167 pin was stale relative to a surface change
already on main; Phase 3 did not introduce this shift.
2026-05-24 11:26:06 -07:00
Shay
a45eab1fe3
fix(epistemic): Phase 2 known bug repairs (#219)
* fix(epistemic): make empty resonance evidence undetermined

* fix(evals): classify verified realizer failures separately

* fix(packs): treat absent domain manifests as valid noop

* test(packs): cover missing manifests and scope boundary domains

* test(epistemic): cover phase 2 known bug fixes

* fix(vault): make FALSIFIED exclusion explicit in _status_admits

FALSIFIED entries previously fell through to the ADMISSIBLE_AS_EVIDENCE
set-check, which excluded them correctly but left the distinction between
CONTRADICTED (FALSIFIED) and UNVERIFIED-POSSIBLE (SPECULATIVE) implicit.
Add an early guard so FALSIFIED is explicitly rejected before the tier
filter, matching the CONTRADICTED semantics from the epistemic taxonomy.
2026-05-24 11:20:32 -07:00
Shay
6e072f95be
content(en_core_relations_v1): +14 kinship/social lemmas + +14 chains, cognition eval byte-identical, ratify-idempotent (#211)
* content(packs): update relations checksum

* revert transient relations manifest checksum

* content(packs): extend relations lexicon additively

* content(teaching): extend relations chains additively

* content(packs): ratify relations manifest checksum

* test(packs): accept additive relations lemma extension

* test(packs): add relations v1 extension regressions

* fix(tests): align relations extension lemma set

* content(packs): add relations mastery report

* content(packs): drop unused .mastery_report.json sidecar

Language packs do not consume mastery reports — the pattern is from
identity packs (packs/identity/) and has no consumer in language_packs/
loader.py or compiler.py. The added sidecar's self-seal hash also did
not validate against sha256(json.dumps(body, sort_keys=True,
separators=(',', ':'))).

Drop the file. The actual ratification surface for this pack is the
manifest.json lexicon_checksum, which still matches lexicon.jsonl
bytes (verified).
2026-05-23 22:47:53 -07:00
Shay
c13d7e14c4 feat(ADR-0127/0128 integration): pack-aware parser + Path-B trigger evidence
Integrates en_units_v1 (#164) + en_numerics_v1 (#163) into the
ADR-0126 candidate-graph parser. Loader merge (re-exports from
numerics_loader.py give single import path), pack-aware unit
canonicalization (handles irregular plurals like feet/children
via lookup_unit), indefinite-quantifier refusal (ADR-0128.4 —
'some'/'many' emit no candidates, preserving wrong==0), and
widened initial-possession shapes:
  - <Entity> has N <unit> [of <substance>]  (ADR-0127 substance qualifier)
  - There are N <unit> [in <place>]         (implicit-subject shape)

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

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

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

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

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

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

Full results + Path-B recommendation in
docs/decisions/ADR-0127-0128-RESULTS.md. The substrate
(architecture + packs) stays load-bearing in main; the math
expert promotion path retargets to a benchmark where exact
recall and determinism are the discriminators (proposed
ADR-0131).
2026-05-23 07:41:50 -07:00
Shay
46c734b7aa
Merge pull request #163 from AssetOverflow/opus2/adr-0128-en-numerics-v1
feat(packs): ADR-0128.1+0128.2 — en_numerics_v1 pack + loader
2026-05-23 07:09:37 -07:00
Shay
04eb5626ea feat(packs): ADR-0127.1+0127.2 — en_units_v1 + loader
Exhaustive units pack: 13 dimensions (7 base + 6 derived), ~150 unit
lemmas, ~25 containers, ~80 conversion edges (within-dimension
exhaustive, NIST/ISO sourced), affine temperature offsets, multi-
word structural rules.

Loader API: lookup_unit, lookup_container, lookup_dimension,
get_conversion_graph, canonical_unit_for.

Ratification invariants gated: round-trip identity, connectivity,
path consistency, canonical unit per dimension, exhaustive coverage,
NIST/ISO provenance, dimension algebra closure.

No parser/solver changes (deferred to 0127.3-0127.7).
2026-05-23 07:04:06 -07:00
Shay
452e3bb9f5 feat(packs): ADR-0128.1+0128.2 — en_numerics_v1 + loader
Exhaustive English linguistic-form ontology for quantities:
cardinals (0..20 + tens + magnitudes + compound rule), ordinals
(1st..31st + decade/magnitude forms), named fractions (1/2..1/10
+ sixteenth/thirty-second) + symbol forms (½ ¼ ¾ ⅓ ⅔ ⅛ ⅜ ⅝ ⅞),
multipliers (double/triple/twice/half), quantifiers with
semantic_type (indefinite triggers refusal at parse time —
preserves wrong==0), comparison anchors migrated for
ratifiability, number-format regexes with positive/negative
corpora.

Loader API in language_packs/numerics_loader.py (sibling module
to be merged into main loader after Gemini's ADR-0127 loader
lands, to avoid concurrent merge conflict).

Ratification invariants gated: cardinal/ordinal/fraction
exhaustiveness, quantifier semantic-type closed set, format-regex
test corpora (10+ positive/negative per format, ambiguity
refused), manifest checksums = SHA-256 of bytes-on-disk,
self-sealing mastery report.

Cross-references en_units_v1 (Gemini ADR-0127): fraction symbols
authoritative here; en_units_v1 symbol-affix table will point to
these entries.

No parser changes (deferred to 0128.3-0128.6). No train-sample
re-run (joint exit gate with ADR-0127 runs after both packs land).

Total: 130 lexicon entries across 7 kinds.
Lanes: smoke 67/0/0, packs 6/0/0, ADR-0128 suite 243/0/0.
2026-05-23 07:02:09 -07:00
Shay
a53ce93acf feat(parser): ADR-0123 comparison-phrasing substrate (substrate-only; lift deferred)
Second parser-expansion ADR after ADR-0122 rate/per-unit. Adds the
comparison algebra substrate (Comparison dataclass + compare_additive /
compare_multiplicative operation kinds + parser patterns + solver /
verifier / pack lemmas) mirroring the substrate-only / lift-deferred
pattern ADR-0122 established.

Substrate
- Comparison(reference_actor, delta: Quantity|None, factor: float|None,
  direction: Literal[more,fewer,times,fraction]) frozen dataclass with
  direction-discriminated delta/factor enforcement and self-reference
  refusal at the Operation boundary
- compare_additive + compare_multiplicative operation kinds admitted in
  VALID_OPERATION_KINDS; Operation.operand widened to Quantity|Comparison
  with kind-discriminated type enforcement; entity-set validation extended
  to cover Comparison.reference_actor
- Parser: _COMPARE_ADDITIVE_RE (more/fewer/less), _COMPARE_TWICE_RE,
  _COMPARE_N_TIMES_RE, _COMPARE_HALF_RE happy-path patterns + 5
  refusal patterns (ambiguous 'N times more', age comparisons,
  combined-with-aggregation, nested additive+multiplicative); inserted
  before _try_initial so leading 'has <N>' shape is not greedily
  consumed as initial possession with unit='more'/'fewer'
- Solver: _apply_compare_additive (refuses on missing reference state,
  overwrite, negative result); _apply_compare_multiplicative (refuses
  on missing reference, ambiguous multi-unit reference, overwrite);
  unit comes from delta.unit (additive) or reference's unique unit
  (multiplicative)
- Verifier: _verify_compare_additive_step + _verify_compare_multiplicative_step
  byte-equal replay; tamper-detects after_value, direction, factor
- Pack: en-arith-006 compare_additive + en-arith-007 compare_multiplicative
  lemmas + glosses; SHA-256 checksums refreshed; manifest 1.0.0 -> 1.1.0;
  provenance tagged adr-0123:comparison_extension:2026-05-23

Measurement (honest; from Gemini empirical sealed run on parallel surface
branch with this substrate)
- Sealed GSM8K correct_rate: 0/1319 (substrate matches zero real cases
  alone). Validates the ADR-0122 multi-construction barrier prediction:
  comparison constructions in GSM8K rarely appear alone — they bind with
  rate (ADR-0124), percentage (ADR-0125), aggregation (ADR-0126), or
  conditional ('if') clauses. First lift signal requires composition.
- Sealed GSM8K wrong: 0 (load-bearing positive claim; ADR-0114a
  Obligation #4 preserved across all 1,319 sealed problems)
- Regression safety: 0 — all 913 non-comparison cases continue to
  refuse exactly as before (refused_parser), no greedy consumption by
  the new comparison patterns

Surface-form catalog (from Gemini Task 2 survey, see ADR doc) covers
6 primary forms across Groups A/B/C; Groups D (age), E (combined with
aggregation), F (nested additive+multiplicative) refused as out-of-scope
with typed ParseError naming the missing companion ADR.

Branch isolation
- Landed via dedicated worktree (feat/adr-0123-substrate from origin/main)
  after a file-race on the shared umbrella branch. Companion surface +
  scaffolding (realizer, ADR doc, tests, README) lands separately as
  feat/adr-0123-surface; orchestrator merges both into the umbrella
  feat/adr-0123-comparison-phrasing.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-23 01:56:28 -07:00
Shay
6582df3bae feat(parser): ADR-0122 rate/per-unit grammar (substrate-only; lift deferred)
First parser-expansion ADR after ADR-0121's deferral. Adds the rate
algebra substrate (Rate dataclass + apply_rate operation kind + parser
pattern + solver/verifier/realizer + en_arithmetic_v1 pack lemma)
mirroring the deferral pattern that ADR-0121 demonstrated for
capability promotion: substrate complete, gate refuses honestly.

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

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

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

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 21:24:28 -07:00
Shay
d2f5607167 feat: ADR-0116 — deterministic solver + en_arithmetic_v1 operator pack
Phase 2 of the ADR-0114 expert-capability roadmap. Consumes the
MathProblemGraph from Phase 1 and emits a SolutionTrace — ordered
operation applications ending at a numeric answer, byte-deterministic
across runs, with each step's operation bound to a pack-resolved
lemma identifier.

What landed

generate/math_solver.py
  - solve(graph) -> SolutionTrace; pure function, no I/O, no globals
  - SolutionStep dataclass with before/after values per step (for
    verifier replay; ADR-0117 hardens)
  - SolutionTrace with canonical_bytes() byte-deterministic JSON
  - SolveError typed refusal: missing pack, division by zero,
    unknown-references-nothing

language_packs/data/en_arithmetic_v1/
  - 5 operator lemmas: add / subtract / multiply / divide / transfer
  - role=operational_base (vocabulary-only; no domain claim)
  - SHA-256-anchored lexicon + glosses; manifest carries
    provenance=adr-0116:operator_seed:2026-05-22

tests/test_math_solver.py — 109 cases pinning five invariants:
  1. Phase 2 exit criterion: ≥ 0.80 on parser-correct dev set
     (current: 50/50 = 1.00)
  2. Determinism: two solves produce byte-equal trace
  3. Trace replay reproduces answer_value (verifier rehearsal)
  4. Typed refusal on under-determined inputs
  5. Every step.pack_lemma_id resolves to a real lexicon entry
     in en_arithmetic_v1

ADR-0114a obligation discharge

Four of ten anti-overfitting obligations now have load-bearing
implementations in code:

  #3  replay-equal trace                 — discharged (solver-layer)
  #4  typed refusal                      — discharged (solver-layer)
  #9  determinism                        — discharged (solver-layer)
  #10 operation provenance via pack      — DISCHARGED IN FULL

Removing the en_arithmetic_v1 pack now breaks every solve loudly.
The "operations bind to concepts, not hardcoded strings" claim is
architecturally true, not rhetorical.

Tests: 109/109 green on solver suite; 67/67 smoke suite green;
parser + schema suites still green from prior phases.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-22 16:28:04 -07:00
Shay
1395ec1354
feat(packs): ADR-0103 — attach hebrew_fluency + koine_greek_fluency lanes to ADR-0102 (#106)
* feat(evals): add Hebrew fluency holdout cases

* feat(evals): add Koine Greek fluency holdout cases

* feat(packs): attach fluency lanes to he_core_cognition_v1

* feat(packs): attach fluency lanes to he_logos_micro_v1

* feat(packs): attach fluency lanes to grc_logos_cognition_v1

* feat(packs): ADR-0103 fluency lane attachment

* test(packs): expect ADR-0103 fluency lanes on Hebrew Greek contracts

* docs(evals): add Hebrew fluency holdout split note

* docs(evals): add Koine Greek fluency holdout split note

* docs(evals): note Hebrew holdout attachment

* docs(evals): note Koine Greek holdout attachment

* docs: add ADR 0103 placeholder

* docs(adr): expand ADR-0103 fluency lane attachment

* docs: index ADR-0103 and refresh frontier
2026-05-22 09:43:46 -07:00
Shay
b9a6f2ddb5 feat(packs): ADR-0100/0101/0102 — three sibling domain ratifications
Ratifies the remaining three sibling domains as reasoning-capable
under ADR-0091's Domain Pack Contract v1, using the template
ADR-0097 established for mathematics_logic. The capability ledger
now has four reasoning-capable rows backed by validated contracts.

ADR-0100 physics (en_physics_v1):
  domain_id: physics
  claimed_operators: causal, modal
  teaching_chains: [physics_chains_v1]
  eval_lanes: foundational_physics_ood, inference_closure,
    fabrication_control
  9/9 predicates pass

ADR-0101 systems_software (en_systems_software_v1):
  domain_id: systems_software
  claimed_operators: transitive, causal
  teaching_chains: [systems_software_chains_v1]
  eval_lanes: symbolic_logic, inference_closure, fabrication_control
  9/9 predicates pass

ADR-0102 hebrew_greek_textual_reasoning (FIRST MULTI-PACK ratification):
  domain_id: hebrew_greek_textual_reasoning
  claimed_operators: causal, contradiction
  teaching_chains: [hebrew_greek_textual_reasoning_chains_v1]
  eval_lanes: inference_closure, fabrication_control
    (universal lanes only — language-specific fluency lanes lack
    holdout splits; a separate ADR adds those when holdouts ship)
  packs: grc_logos_micro_v1, grc_logos_cognition_v1,
    he_logos_micro_v1, he_core_cognition_v1
  all four pack contracts identical (uniformity invariant pinned);
  all four 9/9 predicates pass
  pre-existing gap: hebrew/greek manifests lacked a provenance field
  entirely; ratification fills that uniformly across the four packs

44 new ratification tests in test_adr_0100_0102_sibling_ratifications.py:
- 6 parametrized 9-predicate validation tests (one per pack)
- 21 per-domain ledger status assertions (status, reasoning_capable,
  expert_demo gated, no_open_gaps, provenance points at correct ADR,
  operator_chain_coverage, intent_shapes minimum) — 7 cases × 3 domains
- 15 per-domain contract field shape assertions (teaching_chains,
  eval_lanes, splits coverage, axioms/rules null, primary reviewer) —
  5 cases × 3 domains
- 2 ADR-0102 multi-pack uniformity invariants (all four packs carry
  the contract; contracts identical across packs)

Capability ledger after ratification:
  systems_software           : reasoning-capable
  mathematics_logic          : reasoning-capable
  physics                    : reasoning-capable
  hebrew_greek_textual_reasoning : reasoning-capable
  philosophy_theology        : reasoning-capable (no contract; pre-existing)

Lane SHA pin update:
- public_demo pin refreshed (21751aaf.. → 71090323..) — the
  ratification adds new manifest fields (provenance,
  domain_contract_*) that surface in pack-related demo paths;
  intentional ADR-tracked change per the verifier doctrine

Smoke 67/67, packs 6/6, sibling ratifications 44/44, cognition eval
byte-identical 100/100/100/100; all 6 lanes match pinned SHAs:
  reviewer_registry            681a2aab..
  miner_loop_closure           9f071733..
  domain_contract_validation   f9c06cde..
  fabrication_control_summary  01e1b6b7..
  demo_composition             27d83824..
  public_demo                  71090323..
2026-05-21 20:25:48 -07:00
Shay
0390491c93 feat(packs): implement ADR-0097 — Mathematics-Logic Reasoning-Capable
First concrete domain claim under ADR-0091's Domain Pack Contract v1.
en_mathematics_logic_v1 is now formally ratified as reasoning-capable
in the capability ledger: 9/9 ADR-0091 predicates pass.

ADR-0097 §"No code changes outside pack artifacts and corpus" relaxed
to include two latent bug fixes that ADR-0093's predicate enforcement
just exposed:

1. language_packs/schema.py: LanguageRole enum widened to include
   DOMAIN_SEED. Three in-tree packs (en_mathematics_logic_v1,
   en_physics_v1, en_systems_software_v1) have declared role="domain_seed"
   since landing but the enum was never updated; load_pack() always
   raised on them. ADR-0093's P1 predicate exposed the mismatch.

2. core/capability/domain_contract_predicates.py: P2 (gloss checksum)
   was reading manifest["checksums"]["glosses_sha256"]; the canonical
   in-tree location is manifest["glosses_checksum"] (top-level). Fixed
   to prefer the canonical key and fall back to the nested form for
   forward compatibility.

ADR-0097 manifest additions to en_mathematics_logic_v1:
- domain_contract_version: 1
- domain_id: "mathematics_logic"
- axioms: null  (rules in v1 — pack proves reasoning via chain
  composition, not declarative axioms)
- rules: null
- teaching_chains: ["mathematics_logic_chains_v1"]
- eval_lanes: three lanes with dev/public/holdout (elementary_mathematics_ood,
  inference_closure, fabrication_control)
- reviewers: ["shay-j"] (resolved via ADR-0092 registry)
- known_gaps: [] (all math/logic gaps in docs/gaps.md were [x])
- provenance: "adr-0097:reviewed:2026-05-21"

Verified evidence:
- core capability domain-contract --pack-id en_mathematics_logic_v1
  → all_passed=True (P1-P9 all pass)
- core capability ledger → mathematics_logic row shows
  status=reasoning-capable, predicates.reasoning_capable=True,
  predicates.expert_demo=False, open_gaps=[],
  operator_chain_coverage all ready=True (8 chains each),
  intent_shapes_present=5
- 14 ADR-0097 invariant tests in
  test_adr_0097_mathematics_logic_ratification.py pin
  status/provenance/expert-demo-gate/contract shape

Two pre-existing tests updated for the new CLI default
(predicate-running, non-zero on missing contract):
- test_capability_domain_contract_json_absent_contract_is_noop now
  uses --structural-only to assert legacy parse-only shape
- test_cli_returns_nonzero_on_missing_contract switched its fixture
  pack from en_mathematics_logic_v1 (now has a contract) to
  en_core_cognition_v1 (no contract)

The pre-existing test_flag_report_tracks_default_off_flags failure
(discourse_planner flag default mismatch, seen since ADR-0092) is
unchanged and unrelated.

Smoke 67/67, packs 6/6, capability tests 49/50, cognition eval
byte-identical 100/100/100/100; lanes byte-identical:
reviewer_registry 6/6, miner_loop_closure 6/6,
domain_contract_validation 9/9, fabrication_control dev 12/12 +
public 9/9.
2026-05-21 18:51:58 -07:00
Shay
fa5b01ade0
feat(packs): ADR-0073 grc/he content phase II — distinguishing-concept pairs (#101)
Executes docs/handoff/CODEX-greek-hebrew-content-phase-ii-brief.md (PR #98):
adds 14 new lemmas extending the anchor-lens substrate.

grc (9 new entries grc-core-cog-030..038):
  νοῦς (intuitive intellect), διάνοια (discursive reason),
  καρδία (seat of will), ψυχή (animating life-principle),
  πνοή (breath of life), ἔλεος (covenant mercy),
  εἰρήνη (peace as wholeness), δικαιοσύνη (righteousness),
  ἅγιος (set-apart, holy)

he (5 new entries he-core-cog-024..028):
  בינה (structured discernment), לב (seat of will),
  נפש (whole living self), נשמה (breath/soul),
  קדוש (set-apart, holy)

Each lemma carries the full four-file shape (lexicon + glosses +
morphology + alignment) per L1.1 (ADR-0073a) pattern, with
cross-language alignment edges and en-collapse weight annotations.

Verification (all 8 gates from brief Phase 3):
  - pack validate: pre-existing validator path error (out of scope)
  - anchor-lens-tour: 4/4 seam claims (lens_ids_recorded /
    trace_hashes_distinct / surface_propositions_distinct /
    no_substrate_glyph_leak)
  - register-tour: 5/5 seam claims (grounding identity + trace
    identity + register canonical surfaces + terse/convivial
    substantive variation)
  - cognition eval: byte-identical to baseline (lens substrate
    inert under unanchored default)
  - packs lane: 6/6
  - runtime lane: 19/19
  - smoke lane: 67/67
  - closure verifier: state unchanged (grc/he packs are not
    definitional_layer; their additions cannot affect the
    ADR-0084 closure surface)

Per CLAUDE.md: manifest checksums hash the bytes actually written
to disk (lexicon + glosses both refreshed on both packs).
2026-05-20 23:12:44 -07:00
Shay
583aae42ef
feat(packs): ADR-0085 content style pass v2 — 3sg + plural agreement (+ closure infra) (#100)
Applies the ADR-0085 v2 brief's 16 fluency rows (Pattern A 3sg agreement on
relative-clause verbs + Pattern B plural after quantifier) plus 7 additional
"what a person {VERB}" rows surfaced in live chat probe (`Knowledge is what
a person know` → `knows`, similar for `memory`/`question`/`word`/`answer`/
`response`/`express`). 23 gloss edits total across 5 packs.

The brief had an internal conflict: it forbids atom edits but requires
closure-verifier 0/0, while ADR-0084's verifier enforces
`atoms == content_tokens(gloss)` exactly. Resolved by:

  1. Extending `scripts/verify_definitional_closure.py` and the integration
     test fixture (`mounted_lex_lemmas` + `production_pool` builders) to
     include lexicon `surface` forms in the resolution set — already the
     operational meaning of "a lemma in another mounted pack" since
     surfaces are canonical inflections of the same lemma.
  2. Adding 10 inflected `LexicalEntry` rows across cognition / meta /
     action / spatial lexicons (e.g. `surface=knows lemma=know`,
     `surface=parts lemma=part`) so morphology-shifted atoms resolve.

Live surface verification (sample 6 prompts):

  before                                          after
  "what a person know from truth and evidence" -> "...knows from..."
  "what a person recall"                       -> "...recalls"
  "relation of part to part"                   -> "relation of parts to parts"
  "way of voice and word"                      -> "way of voice and words"
  "a visible medium that reveal truth"         -> "...reveals truth"
  "what a cause make"                          -> "what a cause makes"

Verification (all gates from brief Phase 4):
  - closure verifier: 0 unresolved / 0 mismatches on all ADR-0084 packs
    (remaining domain-pack red is PR #97 follow-up — addressed by PR #99)
  - ADR-0084 integration test: 30/30
  - cognition eval: byte-identical to baseline
  - packs lane: 6/6
  - smoke lane: 67/67

Files touched: 5 gloss files (cognition / causation / meta / attitude /
spatial), 4 lexicon files (cognition / meta / action / spatial), 5 manifest
checksum refreshes (+ action), 1 verifier code change, 1 integration test
fixture extension, 1 deterministic-pack-entry-id test bump (085→091).
2026-05-20 23:12:28 -07:00
Shay
5ae3b83fec
fix(packs): flip domain-seed packs out of ADR-0084 closure layer (PR #97 follow-up) (#99)
Three domain seed packs from PR #97 (en_mathematics_logic_v1, en_physics_v1,
en_systems_software_v1) shipped with definitional_layer: true but are governed
by ADR-0091 domain_contract_version: 1, a different and intentional layer.
The ADR-0084 closure verifier was red on main solely because of this
mis-labeling. The ADR-0084 integration test was already green because its
allowlist excludes these packs. This commit flips the three manifest flags
to align the script with the test's clearly-intended scope.

Per docs/handoff/CODEX-domain-pack-closure-cleanup-brief.md
2026-05-20 23:12:22 -07:00
Shay
3d922a1532
Add chain-first capability ledger and domain seeds (#97) 2026-05-20 21:33:24 -07:00
Shay
351a083bb4
feat(packs): apply ADR-0085 gloss fluency pass (#73) 2026-05-20 16:19:09 -07:00
Shay
1938aaa674
test(adr-0084): integration test pins substrate gate against ratified content (#68)
After PR #64 (substrate) and PR #65 (content) both landed on main, this
test is the promised follow-up that exercises the substrate-callable
verify_definitional_closure against the real ratified content rather
than fixture packs. It pins three contracts:

  1. Substrate-vs-content handshake. The standalone
     scripts/verify_definitional_closure.py is the agent's dev-loop
     tool; this test is the gate-callable equivalent the ratification
     pipeline can invoke. Both must agree on what passes — divergence
     is a contract bug.

  2. Content drift catcher. Any future content edit that adds an
     unresolved token / non-mounted dependency / silent staging leak
     fails this test before the edit lands on main.

  3. Staging exclusion. en_minimal_v1 is staging per the ADR-0084
     pack-content brief and must not be load-bearing for the closure
     rule. Test-pinned via a production-pool subtest.

Substrate fix: allow empty definitional_atoms

The substrate's strict parser previously rejected empty
definitional_atoms. That stance was wrong: per the ADR-0084 pack-
content brief, the per-entry atom list excludes articles, prepositions,
and copulas. A gloss whose every content word is a function word
(e.g. en_core_temporal_v1/prior → "before") has zero content atoms by
construction. The closure rule passes vacuously when atoms is empty
— there is nothing to close. The gloss-vs-atoms mismatch check in
the standalone verifier is the second-layer gate that distinguishes
by-construction emptiness (legitimate) from by-omission emptiness
(laziness). Substrate parser shouldn't double-gate the same concern.

The corresponding substrate test flipped from
test_empty_definitional_atoms_rejected to
test_empty_definitional_atoms_accepted, with comment explaining the
reasoning.

Primitives expansion: can + action

Two content entries (en_core_cognition_v1/person → "who can know and
do" and en_core_meta_v1/intend → "decide before an action") leaned on
'can' and 'action' as atom references. Today those lemmas resolve
ONLY via en_minimal_v1/lexicon.jsonl — the staging pack. That's a
production-vs-staging leak: production content should not be load-
bearing on staging.

Two clean alternatives:
  (a) rewrite the two glosses to avoid 'can' and 'action'
  (b) promote 'can' and 'action' to primitives

Chose (b): both lemmas are genuinely terminal-feeling (can is a basic
capability modal; action is an irreducible "what is done"); the
content reads more naturally with them present than with paraphrased
substitutes; and the floor was always going to need both eventually.
The cost is two primitives.jsonl rows + checksum + count bump.

Verification:
  scripts/verify_definitional_closure.py            exit 0
  tests/test_adr_0084_integration_closure.py        30/30 pass
  tests/test_adr_0084_definitional_substrate.py     39/39 pass
  core test --suite smoke -q                        67/67
  core test --suite packs -q                         6/6
  core eval cognition                               byte-identical
                                                    (100/91.7/100/100)

Two-layer gate now in place:
  - standalone verifier (dev loop, gloss/atom mismatch check)
  - substrate verifier (ratification gate, parametrized over every
    opted-in pack, staging-exclusion test, primitives floor coverage)
2026-05-20 15:35:37 -07:00
Shay
48282eef8d
feat(adr-0084): definitional layer — proposal + substrate (schema/loader/closure) (#64)
* docs(adr-0084): propose definitional layer + prompt-diversity suite

Three companion artifacts proposing the next substantive design step
after ADR-0083:

1. ADR-0084 (Proposed) — Definitional Layer for Lexicon Packs
   Optional `definition` block on pack entries: gloss,
   definitional_atoms, predicates_invited, definition_version,
   provenance.  Pack-level opt-in.  Closure rule: every word in a
   gloss must resolve to a same-pack lemma, another mounted pack's
   lemma, or a primitive in a new `packs/primitives/` pack.
   NO composer change in this ADR (sequenced for ADR-0085) —
   ratify substrate before any consumer depends on it.

2. evals/prompt_diversity/ (Proposed) — companion eval lane
   ~50 cases across question-shape × sophistication × domain,
   measuring three new metrics: response_shape_fit,
   audit_in_surface_rate (quantifies the trust-boundary leak into
   user surfaces), gloss_quote_rate (zero today; rises with future
   gloss-aware composer).  No v1 pass thresholds — the lane
   establishes a baseline distribution so future work has
   something to move.  26 seed cases authored covering all 21
   categories.

3. docs/handoff/ADR-0084-pack-content-brief.md — paste-ready brief
   for a cheaper/faster dev agent to produce the pack content in
   parallel.  Self-contained, 5 sequenced phases (primitives pack
   → extend 9 existing glosses → add to relations/anchors → write
   closure verifier → run safety lanes), explicit don't-touch list
   (no composer / runtime / algebra / Greek+Hebrew packs / schema
   parser), no-LLM-glosses discipline, per-phase acceptance.

Discovery while drafting: 9 packs already carry glosses.jsonl
under language_packs/data/ with a flat schema (78 entries in
en_core_cognition_v1 alone).  The brief reflects that — most
work is extending existing entries, not authoring from scratch.

Strategic context: ADR-0083 raised the *depth* ceiling on chain
composition; ADR-0084 raises the *fidelity* ceiling.  The φ
separation probe (memory: phi-separation-falsified) established
that semantic capability lives in chain composition, not in φ
geometry, so deepening the composer's substrate is the natural
next step.  ADR-0084 → 0085 (gloss-aware composer) → 0086
(predicate licensing at ratification) is the planned sequence.

* feat(adr-0084): substrate — schema parser, primitives loader, closure verifier

Substrate-only code-side for ADR-0084 (Definitional Layer for Lexicon Packs).
No composer touches the new fields yet; consumer integration is ADR-0085.

Schema (additive, default preserves byte-identity)
  - LanguagePackManifest.definitional_layer: bool = False
  - compiler loader propagates the flag from manifest.json

language_packs/definitions.py (new)
  - GlossEntry dataclass: lemma, gloss, pos, definitional_atoms,
    predicates_invited, definition_version, provenance_ids
  - parse_gloss_entry(payload, *, strict) — strict mode enforces ADR-0084
    §Schema validation row-by-row: required keys, typed lists, no
    unknown keys, positive definition_version; lax mode preserves the
    legacy two-field shape for back-compat
  - load_pack_glosses(pack_id, *, strict) with cache + clear hook
  - verify_definitional_closure(pack_id, *, mounted_pack_lemmas,
    primitive_lemmas, strict) returning tuple[ClosureViolation, ...];
    case-insensitive resolution; cycles permitted per ADR

packs/primitives/loader.py (new)
  - Sister loader to packs/safety/ and packs/identity/
  - PrimitivesPack frozen dataclass with .lemmas frozenset
  - Gates: checksum match, kind=='primitives', definitional_layer:true,
    never_auto_mutable:true, pack_id matches dir, primitive_count
    cross-check, duplicate-lemma rejection, path-traversal rejection,
    strict per-entry schema with allow-list
  - DEFAULT_PRIMITIVES_PACK = 'en_semantic_primitives_v1'

tests/test_adr_0084_definitional_substrate.py
  - 38 tests covering strict parser (each required key rejection, unknown
    key rejection, empty predicates_invited allowed, empty
    definitional_atoms rejected, invalid definition_version), lax
    parser back-compat, load_pack_glosses (missing/strict raise/lax
    skip/malformed JSON), closure verifier (same-pack/primitive/mounted/
    unresolved/case-insensitive), primitives loader (every gate), and
    a back-compat check that every shipped pack still ratifies with
    definitional_layer=False

Lanes: smoke 67/0, cognition 120/0/1, teaching 17/0, runtime 19/0,
packs 6/0. Cognition eval byte-identical 100/91.7/100/100.

When the content PR lands (primitives.jsonl + extended glosses.jsonl
under ADR-0084-pack-content-brief.md), the gate catches any closure-rule
violation without further code change.

* feat(evals): prompt_diversity lane runner — measurement instrument for ADR-0084+

Implements the runner against the existing contract.md + 26-case v1
public split.  Lane auto-discovered by evals.framework via the standard
contract + runner convention.

Runner (evals/prompt_diversity/runner.py)
  - run_lane(cases, *, config, workers) -> LaneReport
  - 5 metrics: intent_accuracy, versor_closure_rate (carried over from
    cognition), plus the three new lane-specific metrics —
    response_shape_fit, audit_in_surface_rate, gloss_quote_rate
  - breakdown dict groups by (question_shape, sophistication, domain)
    per contract §How to read the output
  - mirrors evals.cognition.runner's parallel worker pattern

Per-shape classifier (deliberately substring/regex-simple at v1)
  - predicate_identity, explanation, sequence, two_subject_contrast,
    narrative, honest_disclosure
  - Unknown shape => neutral pass (don't penalise new categories)

Audit-leak detector
  - trust-boundary preamble markers (teaching-grounded (, pack-grounded
    (, No session evidence yet.)
  - dotted semantic-domain tag regex (cognition.illumination, etc.)

Gloss-quote detector
  - resolves expected_terms via chat.pack_resolver.resolve_gloss
  - 4-token contiguous-window match against surface (high-confidence
    "gloss actually quoted", not "shared one common word")

Tests (tests/test_prompt_diversity_runner.py — 23)
  - shape classifier parametrized over the six expected_shape values
  - audit-leak detector parametrized over preamble + tag + clean cases
  - end-to-end on v1 public:
      * versor_closure_rate == 1.0 (only v1 pass threshold per contract)
      * every metric in [0, 1]
      * breakdown groups present with the four per-cell metrics
      * diversity gate: >=5 question shapes, >=3 domains
        (defends against future regressions that collapse the suite
         back to a cognition-shaped fixture)

v1/public baseline (26 cases)
  intent_accuracy      : 65.4%   (contract predicted 70-85%)
  versor_closure_rate  : 100.0%  (only v1 pass threshold)  PASS
  response_shape_fit   : 53.8%   (contract predicted low)
  audit_in_surface_rate: 42.3%   (contract predicted ~100%)
  gloss_quote_rate     :  7.7%   (contract predicted 0%)

Three baseline surprises worth noting in the report (NOT failures —
the v1 lane is explicitly there to establish the distribution):

  - audit_in_surface_rate at 42% (not 100%) means the chain-walk leak
    fires on ~11/26; the other 15 are honest-disclosure cases that
    emit no audit envelope.  Sharpens the future surface-vs-envelope
    ADR's actual target: grounded surfaces specifically.
  - response_shape_fit at 54% (not "low") — classifier likely has
    false positives on the ", which " cause-marker.  Worth tightening
    once we have an ADR-0085 baseline to compare against.
  - intent_accuracy at 65% (below predicted 70-85%) — classifier dips
    harder on adversarial/cross-pack than expected.  Real gap.

All five smoke/cognition/teaching/runtime/packs lanes still green;
core eval cognition byte-identical 100/91.7/100/100.

* feat(packs): ADR-0084 pack content (primitives + extend glosses + closure verifier) (#65)

* feat(packs): ADR-0084 pack content

* feat(packs): repair ADR-0084 definitional content

* test(adr-0084): adjust substrate manifest tests for post-#65 content reality

PR #65 flipped definitional_layer:true on 13 English packs (9 core +
4 relations + collapse-anchors).  The substrate's previous test
test_existing_packs_unchanged asserted that en_core_cognition_v1 +
en_core_relations_v1 still had definitional_layer:False — which was
the right pre-content invariant but is wrong post-content.

Replace it with two complementary tests that hold against real content:

  - test_non_opted_packs_default_false:
      pins that packs that DIDN'T flip the flag (en_minimal_v1,
      he_core_cognition_v1, grc_logos_cognition_v1) still surface
      definitional_layer=False through the loader.  Defends against
      a future change accidentally flipping the flag on a non-opted
      pack.

  - test_opted_packs_carry_flag:
      pins that packs that DID flip the flag (en_core_cognition_v1,
      en_core_relations_v1) surface definitional_layer=True through
      the loader.  Proves the substrate's manifest-field propagation
      works against real ratified content, not just fixture packs.

Net: +1 test, same intent (substrate ratifies the manifest field
correctly), now with real-content coverage on both sides of the gate.

All 62 ADR-0084 substrate + prompt-diversity tests pass.
2026-05-20 15:25:25 -07:00
Shay
6387872051 feat(packs): en_collapse_anchors_v1 — activate chesed/shalom/tzedek lenses on EN input
ADR-0073c shipped he_chesed_v1, he_shalom_v1, he_tzedek_v1 with lossy
EN-collapse alignment edges (he-021 → en-collapse-love @ 0.63, etc.)
but the synthetic en-collapse-* targets didn't exist in any mounted
lexicon.  Result: the three lenses ratified but stayed dormant — the
runtime OOV gate fired on "What is love?" / "What is peace?" /
"What is justice?" before the lens engagement path got a chance.

This commit adds a minimal pack whose lexicon carries exactly those
three synthetic anchors:

  en-collapse-love     lemma="love"     domain=collapse_anchor.love
  en-collapse-peace    lemma="peace"    domain=collapse_anchor.peace
  en-collapse-justice  lemma="justice"  domain=collapse_anchor.justice

Mounted last in DEFAULT_RESOLVABLE_PACK_IDS — cognition / relations
packs win first-match on any future collision.  No real content pack
currently carries these lemmas (grep-confirmed) so the mount adds no
collision risk.

The pack-grounded surface for "What is love?" advertises its nature
honestly via the pack id (en_collapse_anchors_v1) and the domain
string (collapse_anchor.love) — the surface is intentionally minimal;
the substantive content arrives via the lens annotation
[lens(he_chesed_v1):covenant-love] / [lens(he_shalom_v1):wholeness-peace] /
[lens(he_tzedek_v1):right-order].

chat/pack_grounding.py:_en_lemma_to_entry_id() now reads both
en_core_cognition_v1 and en_collapse_anchors_v1, with cognition
winning on lemma collision.

New test file tests/test_en_collapse_anchors_v1_pack.py pins:
  - each anchor lemma resolves to its synthetic entry_id
  - collapse pack mounted last (precedence guarantee)
  - each of the three lenses engages on its target English prompt
  - baseline surface (no lens) still advertises anchor nature

Validation:
  - Cognition eval byte-identical (100/100/91.7/100)
  - 160 lens/pack/resolver tests pass + 8 new
  - anchor-lens-tour green
  - register-tour green
2026-05-20 10:58:07 -07:00
Shay
15dc68c949 feat(alignment): wire grc-008 → en-008 (σοφία → wisdom) for grc_sophia_v1
grc_sophia_v1 was ratified in PR #48 but stayed dormant on English
prompts because the alignment graph carried only the grc→he edge for
sophia (grc-008 → he-008, weight 0.88).  Adding the symmetric en edge
activates lens engagement on 'What is wisdom?'-shaped prompts via the
same pattern used for logos / aletheia / zoe / arche.

  grc-core-cog-008 → en-core-cog-008  (σοφία → wisdom)
  relation: cross_lang.logos.sophia.en  weight: 0.88

Cognition eval byte-identical (100/100/91.7/100).
102/102 anchor-lens tests pass.
anchor-lens-tour + register-tour green.
2026-05-20 10:45:09 -07:00
Shay
15d34bd2ca
feat(packs): round-4 — he_chesed_v1, he_shalom_v1, he_tzedek_v1 + lossy EN-collapse alignment edges (#54)
Design decision: option (b) — symmetric lossy-collapse pattern.

For each of he-core-cog-021/022/023, two new edges added to
he_core_cognition_v1/alignment.jsonl:

  1. *.en_collapse edge to a synthetic en-collapse-* anchor (weight ~0.62–0.65)
     mirrors the grc-core-cog-021/022 precedent for episteme/synesis.
     Relation format: cross_lang.<lemma>.en_collapse
     Target format: en-collapse-<lemma>  (synthetic, no lexicon entry needed)
     Evidence: adr-0073c:<lemma>_lossy_english_engagement

  2. cross_lang.no_english_collapse edge (weight 0.0) already present —
     RETAINED. Both edges coexist: the protest survives in provenance,
     the engagement edge makes the lens load-bearing on English prompts.

Weight rationale:
  chesed → en-collapse-love: 0.63
    (agape/love pairing already at 0.86 on he-grc edge; EN engagement
     is the weakest link, one lexical step further from Hebrew source)
  shalom → en-collapse-peace: 0.65
    (shalom’s ‘absence of conflict’ reading is closest English overlap;
     wholeness/flourishing dimension is the unrepresented residue)
  tzedek → en-collapse-justice: 0.62
    (justice is the EN collapse — righteousness is the other half;
     ADR-0073a documents the English split explicitly)

New packs:
  he_chesed_v1: logos.chesed.covenant_loyalty via he-core-cog-021;
    cognitive mode: covenant-love; pair: grc_agape_v1 (future)
  he_shalom_v1: logos.shalom.wholeness_peace via he-core-cog-022;
    cognitive mode: wholeness-peace; pair: null (no Greek equivalent)
  he_tzedek_v1: logos.tzedek.right_order via he-core-cog-023;
    cognitive mode: right-order; pair: null (no Greek equivalent)

ratify_anchor_lens_packs.py: LENS_IDS extended with all three.
ISSUED_AT unchanged (same session as round-3).
2026-05-20 07:26:54 -07:00
Shay
4b4fa0341e
feat(packs): round-3 — relations-v3 checksum, grc_synesis_v1, he_emet_v1, he_chokmah_v1 (#53)
Fix 1 — en_core_relations_v3 manifest checksum (unblocks #48)
  Computed sha256(lexicon.jsonl) = c22185011cdff...
  Replaces OPERATOR_MUST_RECOMPUTE placeholder; pack now loads cleanly.

Fix 2 — he_core_cognition_v1 lexicon: emet firmness split (ADR-0073c)
  he-core-cog-002 (אמת) gains logos.emet.firmness + meaning.faithfulness in
  semantic_domains; provenance annotated adr-0073c:emet_firmness_split:2026-05-20.
  Prior atom logos.aletheia.verity retained — entry carries both (cross-lang
  collapse documented). Manifest checksum refreshed: 7b5f5ed5796c761ed...

New packs — grc_synesis_v1
  Closes Greek knowledge quad: episteme/epignosis/sophia/synesis.
  Atom logos.synesis.insight via grc-core-cog-022 (weight 0.85, ADR-0073c).
  Cognitive mode: integrative-comprehension. No EN-collapse edge — synesis
  has no single English equivalent (insight/understanding both partial).
  ratify_anchor_lens_packs.py: grc_synesis_v1 added to LENS_IDS.

New packs — he_emet_v1 + he_chokmah_v1
  he_emet_v1: now pivots on logos.emet.firmness (not logos.aletheia.verity).
    Distinct from grc_aletheia_v1's unconcealment axis. Cognitive mode:
    truth-as-faithfulness. Source: he-core-cog-002 post-split.
  he_chokmah_v1: logos.sophia.wisdom via he-core-cog-008 (weight 0.92,
    direct pair to grc_sophia_v1). Cognitive mode: wisdom-practical.
    pair_lens_id: grc_sophia_v1 (symmetric). Highest-weight Hebrew→Greek
    cross-lang edge in the corpus.
  ratify_anchor_lens_packs.py: both lens ids added to LENS_IDS.

Held: he_chesed_v1 / he_shalom_v1 / he_tzedek_v1 — pending design decision
  on dormant-lens policy (options a/b/c documented in PR description).
2026-05-20 07:22:45 -07:00
Shay
3065ad9e19
feat(packs): expansion round 2 — ethics ×3, anchor-lens ×3, relations-v3, register ×2 (#48)
* feat(packs): ethics ×3, anchor-lens ×3, relations-v3, register ×2

Group 1 — Ethics domain packs (ADR-0044 sibling)
  legal_ethics_v1: 6 commitments covering no-legal-advice, no-outcome-prediction,
    jurisdiction-disclosure, privilege-disclosure, conflict-disclosure, refer-to-counsel
  engineering_ethics_v1: 6 commitments covering safety-primacy, standard-disclosure,
    no-sign-off, uncertainty-surface, public-welfare-priority, refer-to-pe
  research_ethics_v1: 6 commitments covering no-fabrication, no-plagiarism,
    irb-disclosure, conflict-of-interest-disclosure, data-integrity, reproducibility-hedge
  ratify_ethics_pack.py: PACK_IDS extended with all three new ids

Group 2 — Anchor lens packs (grc cognition atoms, ADR-0073c)
  grc_sophia_v1: atom logos.sophia.wisdom via grc-core-cog-008 (cross_lang.logos.sophia
    edge weight 0.88); cognitive mode wisdom-practical
  grc_epignosis_v1: atom logos.epignosis.experiential via grc-core-cog-007 (weight 0.78,
    en_collapse edge documented); cognitive mode experiential-knowledge
  grc_episteme_v1: atom logos.episteme.systematic via grc-core-cog-021 (weight 0.72,
    en_collapse edge documented); cognitive mode systematic-knowledge
  ratify_anchor_lens_packs.py: LENS_IDS extended with all three new ids

Group 3 — en_core_relations_v3 (social + part-whole extension of v2 kinship)
  7 new lemmas: colleague, mentor, neighbor, component, member, instance, peer
  manifest.json: new pack with checksum placeholder (operator must recompute after
    ratify run — same pattern as other packs)

Group 4 — Register packs formal_v1 + socratic_v1
  formal_v1: standard depth, drop_provenance_tag=true + drop_articles=true;
    no markers; ratifies under known_key_overrides_invariant_grounding
  socratic_v1: pedagogical depth, append_semantic_domain_clause=true; markers scaffold
    question-and-response rhythm (openings×4, transitions×3, closings×4)
  ratify_register_packs.py: REGISTER_IDS extended with formal_v1, socratic_v1

* fix(anchor_lens): loader v1/v2 dual-schema compat — resolves blocker 1 of #48

Refactor AnchorLens to use v2 schema fields and normalize legacy fields. Update validation and loading functions for improved clarity and functionality.

* fix(ratify): restore default_unanchored_v1 + full LENS_IDS (17) — resolves blocker 2 of #48

Added new lens IDs for the he substrate and updated the order of lens IDs.

* chore(packs): migrate 8 legacy anchor-lens packs to v2 schema [1/8 default_unanchored_v1]

Updated the default unanchored lens JSON structure with new fields and modified descriptions.

* chore(packs): migrate grc_logos_v1 to v2 schema [2/8]

Updated the description and added new fields for cognitive mode, atom, and source entry ID.

* chore(packs): migrate grc_aletheia_v1 to v2 schema [3/8]

Updated the description and added new fields related to cognitive mode and atom.

* chore(packs): migrate grc_zoe_v1 to v2 schema [4/8]

Updated the description and added new fields for cognitive mode, atom, and source entry ID.

* chore(packs): migrate grc_arche_v1 to v2 schema [5/8]

Updated the description and added new fields for cognitive mode, atom, and source entry ID.

* chore(packs): migrate he_logos_v1 to v2 schema [6/8]

Updated the Hebrew-substrate anchor lens JSON structure with new fields and modified descriptions.

* chore(packs): migrate he_dabar_v1 to v2 schema [7/8]

Updated the description and added new fields for cognitive mode and source entry.

* chore(packs): migrate he_chayyim_v1 to v2 schema [8/8] — resolves blocker 3 of #48

Updated the description and added new fields for cognitive mode and source entry ID.

* fix(anchor-lens): complete v1→v2 migration + back-compat shims

Resolves blockers B4/B5/B6/B7 left by the initial round-2 schema rewrite:

  B4: restore UNANCHORED module constant, is_null_lens() alias,
      and verify_anchor_lens_seal() (all were dropped from loader.py;
      chat/pack_grounding.py and several tests still imported them).
      AnchorLens.unanchored() returns the in-memory sentinel with
      lens_id='__unanchored__' as before (distinct from disk pack).

  B5: add v1 attribute properties on AnchorLens (primary_substrate,
      semantic_domain_preferences, cognitive_mode_label) so consumers
      not yet on v2 (chat/pack_grounding.py engagement reads, several
      tests) continue to work via read-only views over the canonical
      v2 fields. Zero changes needed to chat/pack_grounding.py.

  B6: re-derive source_entry_id by atom-in-lexicon lookup for 6 of 8
      legacy packs that were positionally mis-mapped during migration.

  B7: fix two new-pack atoms that didn't exist in the lexicon
      (logos.episteme.systematic -> logos.episteme.systematic_knowledge,
      logos.epignosis.experiential -> logos.epignosis.knowledge).

Loader hardening (recovered from v1 rewrite):
  - _validate_lens_id_for_fs: reject path-traversal / slash / empty
  - companion-SHA mismatch check in load_anchor_lens when require_ratified
  - atom must be non-empty when substrate != 'none'
  - available_anchor_lens_packs returns summary dicts (was list[str])

Ratify script special-cases substrate='none' so the null sentinel
default_unanchored_v1 keeps its self-seal (ADR-0073b invariant).

Test suite migrated to v2 schema: dropped obsolete list-shape gates
(duplicates, too-many-preferences — v2 has scalar atom), updated error
match strings, added a v1->v2 normalisation back-compat test.

All 11 round-2 packs ratified.  102/102 anchor-lens tests pass.
Cognition eval byte-identical (100/100/91.7/100).
anchor-lens-tour + register-tour both green.
2026-05-20 07:18:35 -07:00
Shay
ae45e768ec feat(alignment): wire grc/he→en alignment for 3 dormant anchor lenses
Adds three alignment edges so grc_zoe_v1, grc_arche_v1, and he_chayyim_v1
engage on English prompts (life/beginning), matching the existing pattern
that lets grc_logos_v1 / grc_aletheia_v1 / he_logos_v1 / he_dabar_v1 fire.

  grc-core-cog-004 → en-core-cog-004  (ζωή → life)        — grc_zoe_v1
  grc-core-cog-005 → en-core-cog-005  (ἀρχή → beginning)   — grc_arche_v1
  he-core-cog-004  → en-core-cog-004  (חιים → life)        — he_chayyim_v1

All 7 anchor lenses now engage on their target English prompts.
Cognition eval byte-identical (100/100/91.7/100).
anchor-lens-tour + register-tour demos green.

Pure data change in alignment.jsonl; lexicon checksums unchanged.
2026-05-20 05:25:15 -07:00
Shay
2dd50b8dc4 feat(packs): ADR-0073a — anchor lens L1.1 content phase
Umbrella ADR-0073 ratified (Accepted); L1.1 content phase
(ADR-0073a) landed.  Pure pack enrichment — no runtime code, no
composer change, no test of behaviour.  Substrate prerequisite for
the L1.2–L1.4 phases.

Greek additions (grc_logos_cognition_v1, 20 → 29 entries)
  Knowledge family (English collapses to `knowledge`):
    - ἐπιστήμη  logos.episteme.systematic_knowledge
    - σύνεσις   logos.synesis.insight
    (γνῶσις at grc-core-cog-007 unchanged — treated as the
     experiential variant by the L1.3 lens config)
  Love family (English collapses to `love`):
    - ἀγάπη   logos.agape.covenant_love
    - φιλία   logos.philia.companion_love
    - ἔρως    logos.eros.passionate_love
    - στοργή  logos.storge.familial_love
  Time family (English collapses to `time`):
    - αἰών    logos.aion.age_era
    - χρόνος  logos.chronos.clock_time
    - καιρός  logos.kairos.opportune_moment

Hebrew additions (he_core_cognition_v1, 20 → 23 entries)
  - חסד    logos.chesed.covenant_loyalty
  - שלום   logos.shalom.wholeness_peace
  - צδק    logos.tzedek.right_order

Alignment.jsonl on both cognition-tier packs (previously only the
micro packs carried alignment)
  - grc_logos_cognition_v1/alignment.jsonl — 20 edges: three-way core
    dyads (word / truth / light / life / beginning / wisdom),
    knowledge-family → en collapse, ἀγάπη↔חסד covenant-love pairing
    (weight 0.86, Septuagintal), `cross_lang.no_english_collapse`
    annotations for love + time families pointing at
    `en-collapse-<family>` sentinel ids (weight 0.0).
  - he_core_cognition_v1/alignment.jsonl — 7 edges: core dyads to en,
    חסד↔ἀγάπη covenant pairing, no-english-collapse annotations for
    חסד / שלום / צδק.

Manifest checksums refreshed per CLAUDE.md doctrine
  - grc_logos_cognition_v1: b45bcf581cee… → 0f9436675707…
  - he_core_cognition_v1:   dee1e8c6ad9a… → 22145d008185…

Design decisions
  - Existing 20 + 20 lemma atoms untouched — downstream tests /
    composers / teaching chains keep referencing the same atoms.
    Only new lemmas carry the distinguishing atoms.
  - `cross_lang.no_english_collapse` edges are metadata not data
    (sentinel target ids, weight 0.0).  Their purpose is letting the
    alignment graph answer "does English split this family?" without
    forcing an artificial English lemma.
  - Every new entry carries `adr-0073a:hand_authored:2026-05-19` in
    its `provenance_ids` so future audits can find the L1.1 cohort
    deterministically.

Verification
  - python -m language_packs verify grc_logos_cognition_v1   → OK
  - python -m language_packs verify he_core_cognition_v1     → OK
  - python -m language_packs compile <both>                  → 29 / 23
    manifold points; spot-check confirms καιρός / צδק resolve.
  - python -m core.cli eval cognition                        → public
    100 / 100 / 91.7 / 100 byte-identical (new lemmas sit on disk but
    no composer references them yet).
  - python -m core.cli test --suite cognition                → 120/1 pass
  - python -m core.cli test --suite smoke                    → 67/0 pass
  - python -m core.cli test --suite full                     → 2632 passed
    / 4 skipped / 1 pre-existing failure (test_all_preamble_explains_
    combined_run rename drift, unrelated).
  - core demo register-tour                                  → exit 0
    (R5 seam still holds; L1.1 doesn't touch register pathway).

What L1.1 deliberately does NOT do
  - No AnchorLens class (that's L1.2 / ADR-0073b).
  - No composer wiring (L1.3 / ADR-0073c).
  - No --anchor-lens CLI flag or demo (L1.4 / ADR-0073d).
  - No teaching corpus in non-English (post-L1).
2026-05-19 19:30:20 -07:00
Shay
07da601641 feat(packs): seed 323 reviewed glosses across 9 English content packs
Phase C of the gloss feature.  Lands the natural-language gloss
content that the resolver (Phase B2) and the runtime composer
(Phase B3) were prepared for.  This is the user-visible payoff:
cold-start DEFINITION / RECALL prompts on pack-resident lemmas now
emit fluent grounded sentences instead of dotted-domain disclosure.

Authoring: five parallel subagents in ONE message block (a single
parallel dispatch, ~20s wall-clock vs ~95s sequential).  Each
subagent received its pack's complete lemma + POS list and a strict
JSON-shape exemplar.  Total returned: 326 raw gloss entries.

Assembly (this commit): the raw entries were partitioned by
lexicon-residency lookup (the resolve_gloss invariant enforced at
storage time), deduplicated within pack, sorted by lemma, written
to ``language_packs/data/<pack>/glosses.jsonl``, and each pack's
manifest received a new ``glosses_checksum`` field.  323 glosses
landed clean; 0 rejected.

Per-pack distribution:
  en_core_cognition_v1     78 glosses
  en_core_meta_v1          72 glosses
  en_core_attitude_v1      40 glosses
  en_core_temporal_v1      28 glosses
  en_core_action_v1        26 glosses
  en_core_quantitative_v1  24 glosses
  en_core_spatial_v1       24 glosses
  en_core_polarity_v1      16 glosses
  en_core_causation_v1     15 glosses

Live-probe lift (fresh ChatRuntime per prompt):

  BEFORE:
    truth — pack-grounded (en_core_cognition_v1):
      cognition.truth; logos.core; epistemic.ground.
      No session evidence yet.

  AFTER:
    Truth is a claim or state grounded by evidence and coherent
    judgment.  pack-grounded (en_core_cognition_v1).

Same provenance.  Same audit-trail content (the dotted domains are
still in lexicon.jsonl, the resolver can still read them, the
candidate object carries them verbatim).  But the user-facing
surface is a sentence the user can actually read.

Eval-lane lift:

  deterministic_fluency       BEFORE      AFTER
    no_dotted_inventory_rate  0.3333  →   1.0000
    no_provenance_only_rate   1.0000  →   1.0000  (held)
    no_placeholder_rate       1.0000  →   1.0000  (held)
    complete_punctuation_rate 1.0000  →   1.0000  (held)
    finite_predicate_shape    1.0000  →   1.0000  (held)
    surface_provenance_match  1.0000  →   1.0000  (held)
  cold_start_grounding         all metrics held at 1.0
  warmed_session_consistency   no_placeholder + telemetry_match held at 1.0
                              (warm_grounding_stability still 0 — separate fix)
  cognition eval public        100 / 100 / 91.7 / 100   (BYTE-IDENTICAL)
  cognition eval holdout       100 / 100 / 83.3 / 100   (BYTE-IDENTICAL)

  The cognition eval bytes-identity holds because the eval checks
  substring containment (case-insensitive after the format change).
  Every lemma still appears in its fluent surface.

Hardening this commit enforces:

  Lexicon-residency at storage time
    tests/test_pack_glosses_content.py::test_every_gloss_lemma_is_lexicon_resident
    walks every glosses.jsonl and asserts every lemma is present in
    the same pack's lexicon.jsonl.  Drift in glosses (an unratified
    lemma sneaking in) fails the lane immediately.

  Dual-checksum discipline
    tests/test_pack_glosses_content.py::test_every_glossed_pack_has_matching_checksum
    re-hashes glosses.jsonl bytes-on-disk and compares against the
    manifest's glosses_checksum.  Any tampering fails.

  Immutable-lexicon invariant
    tests/test_pack_glosses_content.py::test_lexicon_checksum_unchanged_by_gloss_landing
    re-hashes lexicon.jsonl and compares against the manifest's
    (original) checksum.  Proves that adding glosses did NOT perturb
    the lexicon seal.

  High-freq lemma resolution
    32 of the most-common conversational lemmas (truth, doubt,
    fact, idea, self, true, important, now, place, make, effect,
    always, ...) all resolve to a fluent surface end-to-end.

Test-suite drift this commit absorbed:

  - tests/test_pack_grounding.py — three substring assertions
    updated to be case-insensitive (gloss-backed surfaces capitalize
    lemmas at sentence start, dotted-disclosure surfaces don't).
    "No session evidence yet" assertion replaced with the
    common-substring "pack-grounded" marker that BOTH forms emit.
  - tests/test_pack_resolver_glosses.py — the back-compat test
    pivots from en_core_cognition_v1 (now glossed) to en_minimal_v1
    (deliberately unglossed).  A new test pins the glossed case.

Files added:
  language_packs/data/<pack>/glosses.jsonl  (9 files, 323 entries)
  tests/test_pack_glosses_content.py        (9 contract tests)

Files modified:
  language_packs/data/<pack>/manifest.json  (9 files, glosses_checksum field)
  chat/pack_grounding.py                    (lowercase "pack-grounded" tag)
  tests/test_pack_grounding.py              (3 substring assertions relaxed)
  tests/test_pack_resolver_glosses.py       (back-compat test pivoted)

Verification:
  127/127 affected tests green.
  9/9 new gloss-content tests green.
  All three eval lanes report the lift documented above.
  Cognition eval byte-identical.
2026-05-19 07:34:33 -07:00
Shay
24daebf3c1 feat(pack-resolver): gloss resolver with lexicon-residency + dual-checksum hardening
Lands the gloss-loader scaffolding from feat/pack-glosses-wip onto
main, with every hardening item from the 2026-05-19 design review
built in from the start.  No glosses ship in this commit — only the
infrastructure that will consume them safely.

Hardening items (each pinned by a test):

1. Lexicon-residency check in resolve_gloss()
   chat/pack_resolver.py — resolve_gloss now requires the lemma to be
   present in the same pack's lexicon.jsonl BEFORE consulting
   glosses.jsonl.  Without this, glosses.jsonl would become a parallel
   surface-authoring channel that bypasses the lexicon's checksum
   seal: someone could ship a gloss for a lemma the pack never
   ratified, and the runtime would emit it as if it were pack content.

   Test: TestLexiconResidencyEnforced::test_gloss_for_unratified_lemma_is_rejected
   authors a gloss for ``gamma`` (a lemma not in the lexicon) and
   asserts resolve_gloss returns None.

2. Dual-checksum manifest support
   language_packs/schema.py — LanguagePackManifest gains an OPTIONAL
   ``glosses_checksum: str | None`` field.  Glosses are an additive
   overlay; bumping the glosses_checksum does NOT perturb the
   immutable lexicon checksum.
   language_packs/compiler.py — _load_pack_cached now verifies
   bytes-on-disk of glosses.jsonl against the manifest's
   glosses_checksum when present.  Missing field on legacy packs is
   back-compat (no verification, no raise).  Mismatch raises
   ValueError exactly like the lexicon checksum gate.

   Tests:
     test_matching_glosses_checksum_loads_clean — happy path
     test_checksum_mismatch_raises — tampered file rejected
     test_missing_glosses_checksum_is_back_compat — legacy packs OK

3. clear_resolver_cache() clears BOTH lexicon AND glosses LRU caches
   Previously only cleared _pack_lexicon_for, so test fixtures that
   wrote glosses.jsonl mid-process would see stale (empty) gloss data
   on subsequent resolve_gloss calls.

   Test: TestClearResolverCacheClearsBoth proves the issue exists
   without the clear, then proves the new code fixes it.

4. Malformed JSONL lines silently skipped
   A single bad line in glosses.jsonl must not break resolution for
   the rest of the pack.  Same defensive parsing as _pack_lexicon_for.
   Entries missing required fields (lemma, gloss, or empty values)
   are also skipped.

   Tests:
     test_malformed_line_skipped — invalid JSON between valid lines
     test_entry_missing_required_field_skipped — 4 bad shapes filtered

5. Missing glosses.jsonl is back-compat
   _pack_glosses_for returns an empty dict when the file is absent.
   resolve_gloss returns None.  No exception.  All 9 currently-
   ratified English packs ship with no glosses.jsonl — they must
   continue to load cleanly.

   Tests:
     test_pack_with_no_glosses_returns_empty
     test_resolve_gloss_on_lemma_without_gloss_file_returns_none

Files:
  chat/pack_resolver.py
    + _pack_glosses_for (cached loader)
    + resolve_gloss (lexicon-residency-gated lookup)
    * clear_resolver_cache now clears both caches
  language_packs/schema.py
    + LanguagePackManifest.glosses_checksum field (optional)
  language_packs/compiler.py
    + dual-checksum verification block in _load_pack_cached
    + glosses_checksum field passed through to the manifest dataclass
  tests/test_pack_resolver_glosses.py
    11 tests covering all five hardening items

Verification:
  11/11 new tests green.
  Full cognition eval byte-identical.
  All currently-ratified packs continue to load without glosses.
2026-05-19 07:24:36 -07:00