Commit graph

116 commits

Author SHA1 Message Date
Shay
5f149340cc
feat(contemplation): land ADR-0080 phase 1 (#119) 2026-05-22 13:10:03 -07:00
Shay
5cad0a4b72
feat(capability): ADR-0110 promote mathematics_logic to expert_demo (#118)
First worked expert-demo promotion under the ADR-0106 + ADR-0109
contract. Math is now the first domain at expert_demo=true.

Signed claim (docs/reviewers.yaml):
  domain_id: mathematics_logic
  evidence_lanes: [elementary_mathematics_ood, inference_closure,
                   fabrication_control]
  evidence_revision: adr-0110:reviewed:2026-05-22
  signed_by: shay-j
  claim_digest: 94d74781e103854230c1a71590e4df2287f5d2e87832f1c29b8ec4618853c04b

Evidence (all three lanes, public + holdout):
  elementary_mathematics_ood: accuracy=1.0 (117/117 public, 39/39 holdout)
  inference_closure: all_pass_rate=1.0, replay_determinism=1.0,
                     overall_pass=True (20 public, 12 holdout)
  fabrication_control: by-class refusals 3/3/3, fabricated=0
                       (9 public, 9 holdout)

Infrastructure bridges (not contract changes):
- cases_plaintext.jsonl dev-mode fallback files for
  elementary_mathematics_ood + inference_closure (ADR-0105 pattern)
- 9 new holdout cases for fabrication_control across all three
  refusal classes (phantom_endpoint / cross_pack_non_bridge /
  sibling_collapse)
- core/capability/reporting.py: _fetch_lane_split folds top-level
  by_class into metrics so refusal_shape sees a canonical layout

Tests:
- tests/test_adr_0110_math_expert_demo.py: 4 invariant tests
  (math_expert_demo_holds, signed_claim_present, replay_digest_
  byte_equality, other_domains_unaffected)
- tests/test_adr_0107_deferral.py retired (deferral resolved)
- tests/test_expert_demo_contract.py: production-ledger test
  rewritten as 'every promoted domain has signed claim' (load-
  bearing invariant preserved)
- tests/test_capability_reports.py: math row asserted at
  expert-demo (was reasoning-capable)

Ledger state:
  systems_software: reasoning-capable
  mathematics_logic: EXPERT-DEMO   <- new
  physics: reasoning-capable
  hebrew_greek_textual_reasoning: reasoning-capable
  philosophy_theology: reasoning-capable

README updated. ADR-0107 referenced as resolved by this ADR.
CLAIMS.md regenerated. ADR-0106 / ADR-0109 contract unchanged.
2026-05-22 12:59:23 -07:00
Shay
36053317be
feat(capability): implement ADR-0109 lane-shape-aware thresholds (#116)
Replaces the cognition-shape-uniform threshold dispatch in
core/capability/expert_demo.py with an explicit LANE_SHAPE_REGISTRY
mapping 8 ratified lane ids to 5 shapes:

  cognition           -> cognition_shape
  elementary_math_ood -> accuracy_shape
  foundational_physics_ood -> accuracy_shape
  symbolic_logic      -> symbolic_logic_shape
  hebrew_fluency      -> accuracy_shape
  koine_greek_fluency -> accuracy_shape
  inference_closure   -> inference_shape
  fabrication_control -> refusal_shape

Each shape has a documented threshold checker. Unknown lane ids
fail-closed with a named reason. ADR-0106 \xc2\xa71.1/\xc2\xa71.3/\xc2\xa71.4/\xc2\xa71.5
unchanged; only \xc2\xa71.2 (threshold rules) dispatches by shape.

tests/test_lane_shape_thresholds.py pins all four ADR-0109 invariants
plus dead-shape and threshold-value gates (13 new tests).
tests/test_expert_demo_contract.py fixtures updated to provide
shape-appropriate metrics (no semantic change to those tests; same
12 cases still pin the ADR-0106 contract).

ADR-0109 status: Proposed -> Accepted. README sequencing updated
(ADR-0110 now only blocked by inference_closure, not by metric-shape
amendment).

Ledger: all five domains remain reasoning-capable, expert_demo=false.
2026-05-22 12:11:58 -07:00
Shay
c2dd69611b
docs: propose ADR-0109 lane-shape-aware threshold amendment (#115)
Amends ADR-0106 \xc2\xa71.2 to dispatch threshold rules by lane shape rather
than imposing cognition-pack-shape metrics uniformly. ADR-0107
surfaced that every non-cognition lane was failing the gate by
absence-of-key, not by substance.

Status: Proposed. Ships five shapes covering every lane currently
attached to a ratified pack: cognition_shape, accuracy_shape,
inference_shape, refusal_shape, symbolic_logic_shape. Four invariants
pinned. Unknown lanes fail closed; new shapes require ADR amendment.

\xc2\xa71.1 (reasoning-capable prereq), \xc2\xa71.3 (signature scoping), \xc2\xa71.4
(domain-aware), \xc2\xa71.5 (replay byte-equality) all preserved. ADR-0106
status remains Accepted.
2026-05-22 12:00:59 -07:00
Shay
7cc2f7b422
feat(adr): ADR-0107 mathematics_logic expert-demo promotion deferred (#114)
The ADR-0106 contract correctly refused promotion. ADR-0107 records the
deferral and reserves two follow-up ADRs:

- ADR-0109 (lane-shape-aware threshold amendment): ADR-0106 \xc2\xa71.2
  prescribes cognition-pack-shape metrics uniformly, but math /
  physics / systems / hebrew-greek lanes carry native shapes
  (accuracy, passed_rate, all_pass_rate). Prerequisite for any future
  expert-demo promotion.
- ADR-0110 (math re-attempt): conditional on ADR-0109 landing and
  inference_closure substantively passing (currently all_pass_rate=0.4
  on public).

tests/test_adr_0107_deferral.py pins adr_0107_no_silent_promotion: math
stays at reasoning-capable, has no expert_demo_claims entry, and the
ledger row carries a named refusal reason.

No change to core/capability/expert_demo.py or reporting.py -- the
contract is honored, not amended. README sequencing updated to reflect
ADR-0107 acceptance and the new ADR-0109/0110 prerequisites.
2026-05-22 11:49:37 -07:00
Shay
0493808215
feat(capability): implement ADR-0106 expert-demo promotion contract (#113)
Closes ADR-0106 acceptance evidence:

- ExpertDemoClaim dataclass + additive expert_demo_claims block on
  ReviewerRegistry (schema_version stays at 1; backward-compatible).
- New core/capability/expert_demo.py with derive_evidence_digest,
  evaluate_expert_demo, collect_domain_lanes, materialise_lane_results.
- core/capability/reporting.py: replaces the cognition-lane-only
  predicate (previous lines 418-433) with a domain-aware,
  reviewer-signed gate; ledger rows now also carry
  expert_demo_reason for operator legibility. Reviewer registry is
  fail-closed: an unloadable registry yields zero claims, so a broken
  registry never silently grants expert_demo=true.
- tests/test_expert_demo_contract.py covers all three ADR-0106
  invariants: requires_signature, domain_aware, replay_byte_equality;
  plus threshold + production-ledger-untouched gates. 12 new tests.
- tests/test_reviewer_registry.py extended with TestExpertDemoClaimsSchema
  covering omitted block, valid parse, unknown signer rejection,
  malformed digest rejection, duplicate domain rejection. 5 new tests.
- README index row + table preface updated to note expert_demo is
  contract-gated. Frontier list trimmed (ADR-0106 has landed).
- ADR-0106 Status flipped Proposed -> Accepted.

No domain row's expert_demo field flips by this PR -- only the contract
changes. Promotion of any ratified domain requires a follow-up ADR
(ADR-0107 reserved for mathematics_logic) plus a signed claim.
2026-05-22 11:39:09 -07:00
Shay
7bb8334fc5
docs: accept ADR-0108 and rewrite Current frontier per its acceptance evidence (#112)
- ADR-0108 Status: Proposed -> Accepted
- README index row updated to Accepted
- 'Current frontier' rewritten with the ranked Proposed-ADR list mandated
  by ADR-0108 \xc2\xa7Decision; removes the now-false 'No ADR currently sits in
  a "Proposed but unimplemented" state' sentence
- Open candidate directions (no-ADR-yet) section retained for the
  multi-reviewer governance frontier item from ADR-0105
2026-05-22 11:21:20 -07:00
Shay
7e50738034
docs: propose ADR-0108 sequencing for currently-Proposed ADRs (#111)
Makes the post-ADR-0105 sequencing of ADR-0080 / 0084 / 0087 / 0106
explicit, durable, and revisable. Status: Proposed. No content of the
four sequenced ADRs is modified — sequencing is meta, not content.
2026-05-22 10:52:44 -07:00
Shay
8e749546b2
docs: propose ADR-0106 expert-demo promotion contract (#109)
Defines a domain-aware, reviewer-signed expert_demo promotion gate to
replace the current cognition-lane-only predicate in
core/capability/reporting.py:418. Status: Proposed. This ADR does not
promote any domain — it defines the contract that a follow-up ADR (likely
mathematics_logic as ADR-0107) will consume as the first worked
promotion.
2026-05-22 10:44:36 -07:00
Shay
257fd4503d
feat(evals): ADR-0105 — sealed holdout encryption via age (#108)
* feat(evals): add pyrage dependency

* feat(evals): add sealed holdout path resolution

* feat(evals): implement sealed holdout decryption

* feat(evals): add sealed holdout CLI

* test(evals): add sealed holdout encryption tests

* docs(decisions): add ADR-0105 sealed holdout encryption

* feat(evals): route holdout split through sealed decryptor

* docs(decisions): add ADR-0105 index entry

* chore: restore project description

* fix(evals): use pyrage Identity.from_str and pin curriculum SHA

- holdout_runner: pyrage exposes Identity.from_str, not from_file; parse
  identity file by line and pass list[Identity] into decrypt(). Restores
  PR 108's sealed-holdout test suite to green.
- verify_lane_shas: realign curriculum_loop_closure pin with the actual
  deterministic runner output (carryover from PR 107).
2026-05-22 10:09:43 -07:00
Shay
f7680e96ea
feat(teaching): ADR-0104 — curriculum-sourced teaching proposals (#107)
* feat(teaching): add curriculum-sourced proposal builder

* test(teaching): cover curriculum proposal construction

* test(evals): add curriculum loop closure contract

* test(evals): add curriculum loop closure runner

* test(evals): add canonical curriculum loop closure report

* ci(lanes): pin curriculum loop closure lane

* docs(adr): add ADR-0104 curriculum sourced proposals

* docs(adr): register ADR-0104 and seven pinned lanes

* docs(teaching): mark curriculum source activation

* fix(ci): pin curriculum_loop_closure SHA to runner output

* fix(ci): register curriculum_loop_closure in CLAIMS.md generator
2026-05-22 10:05:14 -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
57fa078aaa docs: mark ADR-0094/0095/0098/0099 accepted
Sibling reconciliation PR to #104. The four ADRs explicitly called out as
the 'current implementation frontier' in PR #104 are already implemented
to the same evidence bar as the eight ADRs that PR accepted:

- ADR-0094: teaching/source.py + proposal schema widening + migration
  script; tests/test_proposal_source.py green
- ADR-0095: teaching/from_miner.py + miner_loop_closure lane;
  SHA-pinned in scripts/verify_lane_shas.py; tests/test_miner_proposals.py
  green
- ADR-0098: core/demos/contract.py + adapter surface + demo_composition
  lane; SHA-pinned; tests/test_demo_composition.py green
- ADR-0099: core/demos/showcase.py + public_demo lane;
  SHA-pinned; tests/test_public_showcase.py green

Three of four lanes are SHA-pinned in CI (a stricter bar than several
already-accepted ADRs). Local pytest run: 85/85 passed across the four
tests/test_*.py files in 17s.

Also refreshes docs/decisions/README.md:
- flips the four table rows to Accepted (2026-05-22)
- rewrites the 'Current frontier' section now that no ADR-0091..0102
  entry is unimplemented
- enumerates candidate next directions (curriculum proposals,
  language-specific holdout splits, expert-demo ratification)

Docs-only change; no runtime code touched.
2026-05-22 07:09:14 -07:00
Shay
fe8d6db224 docs: update ADR index and current frontier 2026-05-22 06:47:25 -07:00
Shay
f21b1a9ba6 docs: mark ADR 0102 accepted 2026-05-22 06:42:27 -07:00
Shay
dde928859c docs: mark ADR 0101 accepted 2026-05-22 06:34:35 -07:00
Shay
2bc3e67474 docs: mark ADR 0100 accepted 2026-05-22 06:30:53 -07:00
Shay
d0d1a96669 docs: mark ADR 0097 accepted 2026-05-22 06:28:51 -07:00
Shay
852585a83d docs: mark ADR 0096 accepted 2026-05-22 06:27:25 -07:00
Shay
32d6ff6576 docs: mark ADR 0093 accepted 2026-05-22 06:25:38 -07:00
Shay
cf8ad47c53 docs: mark ADR 0092 accepted 2026-05-22 06:24:03 -07:00
Shay
87762e34fa docs: mark ADR 0091 accepted 2026-05-22 06:21:59 -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
10457bec69 docs(adr): propose ADRs 0092-0099 — scale-up slate through public showcase
Eight load-bearing ADRs closing the loop from contemplation Phase 5 through
a public showcase demo. Each one is small and evidence-bearing; together
they sequence the next arc without duplicating existing substrate.

- 0092 Reviewer Registry v1 — populates docs/reviewers.yaml schema;
  unblocks all reasoning-capable claims under ADR-0091.
- 0093 Domain Pack Contract v1 Implementation — wires ADR-0091's five
  follow-up items (parser, dry-run validator, chain registry, eval lane
  refs, reviewer resolution) so manifest fields actually gate status.
- 0094 Proposal Source Provenance — sealed ProposalSource type widening
  proposal schemas ahead of 0095.
- 0095 Miner-Sourced Teaching Proposals — closes the contemplation
  loop: articulation_quality / contradiction_detection / frontier_compare
  miners emit PackMutationProposal candidates routed through the single
  reviewed teaching path; identity-pack defense at construction, not
  review; replay-equivalence pre-gate.
- 0096 Fabrication-Control Eval Lane — first negative-control measure;
  three case classes (phantom endpoint, cross-pack non-bridge, sibling
  collapse) with frozen thresholds (fabrication_rate ≤ 0.01).
- 0097 Mathematics-Logic Reasoning-Capable Ratification — first
  domain claim under ADR-0091; chain corpus + eval lanes already
  exist, this is the formal contract ratification.
- 0098 Demo Composition Contract — DemoCommand protocol so demos can
  be composed without reimplementation; deterministic JSON, no global
  state mutation, declared output paths only.
- 0099 Public Showcase Demo — composes four scenes (determinism /
  honest unknown / reviewed learning / multi-hop+trace) under 30s;
  pure composition enforced by grep gate; JSON byte-equality CI-pinned.

Landing order: 0092 → 0094 → 0095 → 0093 → 0096 → 0097 → 0098 → 0099.

Deliberately not included: curriculum compiler, formation course
runner, calculator operators, response-mode taxonomy expansion,
learning-scale 10k harness. Each is deferred with a documented reason.
2026-05-21 17:34:09 -07:00
Shay
3d922a1532
Add chain-first capability ledger and domain seeds (#97) 2026-05-20 21:33:24 -07:00
Shay
ac75cfc659
docs(adr-0087): rhetorical style as selection axis + writing-chain harvester spec (#72)
Pre-work for a writing-curriculum extension to CORE.  Two companion
documents, both Proposed status (no code shipped).

docs/decisions/ADR-0087-rhetorical-style-axis.md
  Pins rhetorical style as a third selection axis — sibling to anchor
  lens (ADR-0073), orthogonal to register (ADR-0070).  Substantive
  axis: trace_hash DISTINCT across styles (style changes which moves
  the composer requires and which frames the realizer emits, which
  changes the propositional plan, which changes the trace).

  Four anti-patterns explicitly named and rejected:
    - style as motor (re-couples realizer to geometry; same shape as
      the ADR-0085 fusion-operator rejection)
    - style as register variant (conflates substantive with stylistic)
    - style as identity axis (bloats identity doctrine)
    - style auto-detected from user input (operator-chosen only)

  Pack shape mirrors packs/anchor_lens/.  default_unstyled_v1 is the
  null-lift pack identical to no-style behavior.  Three CI invariants
  proposed: rhetorical_style_null_lift, schema validation, three-axis
  orthogonality.

  Substrate-only ADR — no consumer code, no genre packs.  Consumer
  integration is a follow-up ADR (composer + realizer extensions
  that read permitted_frames + required_moves_per_claim +
  forbidden_moves).

docs/curriculum/writing-chain-harvester-spec.md
  Layer 0 of the writing curriculum.  A deterministic tool that
  extracts candidate (subject, predicate, object) triples from
  reviewed expert prose and surfaces them as proposals to the
  existing teaching/review pipeline.

  Five stages (segment → classify → extract → propose → audit) —
  pure-Python rule-based, no LLM generation, no auto-acceptance.
  Trust boundary: reviewer accept/reject via the existing
  core teaching propose/review path.  No bypass permitted.

  The harvester is a proposal PRODUCER, not a proposal CONSUMER.
  Plugs into the existing pipeline without inventing a new review
  mechanism.  Each proposal carries source_id + source_line + the
  exact source_clause it came from for reviewer verification.

  First-implementation acceptance criteria deliberately tight:
  Stage 0+1 with dry-run only.  Stages 2-5 are follow-up PRs.

Substrate-first sequencing pattern (ADR-0084 → 0085) reused
throughout.  Both documents acknowledge open questions deferred to
implementation phase rather than pre-deciding.

Why now: a writing curriculum is being scoped.  Without this ADR,
every downstream PR faces the same "should style be a motor?"
question and the temptation to reach for the geometry will recur
every time the realizer produces a stilted surface.  Pinning the
axis up front prevents that recurrence.
2026-05-20 16:09:16 -07:00
Shay
4b9404a88e
feat(adr-0085): gloss-aware CAUSE composer — explanation frame from glosses (#70)
The original "Why does light exist?" complaint that motivated ADR-0084
was specifically about CAUSE-intent surfaces. ADR-0084 (substrate) +
PR #65 (content) already moved DEFINITION/RECALL to gloss-grounded
surfaces ("Light is visible medium that reveal truth."). But CAUSE
still dispatched through the chain-walk path:

  Before: light — teaching-grounded (cognition_chains_v1):
            cognition.illumination; logos.core.
            light reveals truth (cognition.truth).
            No session evidence yet.

  After:  Light exists as visible medium that reveal truth.
          pack-grounded (en_core_cognition_v1).

The chain-walk is structurally correct but the wrong SHAPE for a why-
question — it's a graph traversal, not an explanation. ADR-0085 fixes
the shape using the same gloss material that DEFINITION/RECALL already
consume, with no new content authoring.

Additive composer
  chat/pack_grounding.py:gloss_aware_cause_surface()
  - Resolves gloss via lexicon-residency-checked resolve_gloss().
  - Frames POS-aware:
      NOUN -> "{Lemma} exists as {gloss}."
      VERB -> "To {lemma} is to {gloss}."
      ADJ  -> "To be {lemma} is to {gloss}."
      *    -> falls back to _frame_gloss (predicate-identity).
  - Threads anchor lens via the existing helper (ADR-0073c parity).
  - Returns None when no gloss exists — runtime falls through to the
    existing chain-walk path. Additive: no CAUSE case loses its surface.

Runtime dispatch
  chat/runtime.py — IntentTag.CAUSE tries gloss path FIRST under the
  flag; falls through to teaching_grounded_surface* on None.
  Unconditional fallback — never silent.

Opt-in flag
  core/config.py — RuntimeConfig.gloss_aware_cause: bool = False
  Default off preserves pre-ADR-0085 chain-walk surfaces byte-
  identically (null-drop invariant, CI-pinned).

Prompt-diversity classifier update
  evals/prompt_diversity/runner.py — _CAUSE_MARKERS widened with the
  explanation-frame markers ("exists as", "is to", "to be", "is for",
  "purpose of") plus bare-form predicates ("reveal" alongside
  "reveals"). Neither composer path is penalised on shape_fit just on
  inflection grounds.

v1/public lift (flag OFF vs ON, 26 cases)
  intent_accuracy        : 65.4% -> 65.4%   ( — )
  versor_closure_rate    : 100.0% -> 100.0% ( — )
  response_shape_fit     : 57.7% -> 57.7%   ( — , both frames recognized)
  audit_in_surface_rate  : 42.3% -> 42.3%   ( — , envelope ADR's job)
  gloss_quote_rate       : 11.5% -> 23.1%   (+11.5pp, structural lift)

Tests (15)
  - 5 pure composer (NOUN/VERB frame, unknown/empty None, no chain-
    walk artifacts in surface)
  - 5 runtime dispatch (flag-off chain-walk, flag-on gloss, parametrized
    across glossed subjects, VERIFICATION unchanged under flag, no-
    gloss fallback engages)
  - 5 cognition lane invariance (aggregate metrics byte-identical
    under both flag states; surfaces deliberately shift on the 2 CAUSE
    cases with glossed subjects — the structural-change-vs-metric-
    invariance both-sides invariant)

Lanes
  smoke 67/0, cognition 120/0/1 skipped, packs 6/0, teaching 17/0,
  runtime 19/0. core eval cognition byte-identical 100/91.7/100/100
  under both flag states.

Scope limits (per ADR §Scope limits)
  - CAUSE only; VERIFICATION still chain-walks (different shape).
  - English pilot only; Greek/Hebrew packs not opted into definitional
    layer yet (ADR-0084 scope limit).
  - Single-lemma subjects; compound/anaphoric fall through.
  - Opt-in until cognition holdout confirms the lift transfers off-
    fixture. Future PR flips default on.

Out of scope
  - Surface-vs-envelope cleanup ("pack-grounded (...)" still leaks).
  - Predicate licensing (ADR-0086).
  - Content style pass (bare lemma forms in glosses — separate brief).
2026-05-20 15:55:08 -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
9e6fa4be75
feat(adr-0083): transitive (multi-hop) teaching-grounded surface (#63)
Strict superset of ADR-0062's depth-1 composer.  `max_depth` is the
number of follow-up hops appended beyond the initial chain:

  max_depth=0  → byte-identical to single-chain surface
  max_depth=1  → byte-identical to ADR-0062 composed
  max_depth=2  → byte-identical to ADR-0062 when no second hop
                 survives, strict superset when one does

The composer surfaces content the realizer was silently dropping
from chains already ratified in `cognition_chains_v1`.  Example
live lift on `"Why does light exist?"`:

  composed: "light reveals truth, which grounds knowledge."
  transitive(2): "...which grounds knowledge, which requires evidence."

Cycle-safe at every depth via a single visited-set; single-corpus
traversal in v1 (cross-corpus transitive deferred to a follow-up
ADR alongside ADR-0064's cross-pack model).

Both flags default False — every existing surface is preserved
byte-identically.  When both `composed_surface` and
`transitive_surface` are True, transitive wins.

Implementation:
- `core/config.py`: `transitive_surface: bool = False`,
  `transitive_max_depth: int = 2`.
- `chat/teaching_grounding.py`: `_resolve_followup` shared helper
  refactored out of the depth-1 composer (no behavioural change),
  plus new `teaching_grounded_surface_transitive(subject,
  intent_tag, *, max_depth)`.
- `chat/runtime.py`: dispatch order — transitive > composed > single.

Verification:
- tests/test_transitive_surface.py: 16 new tests covering pure-fn
  contract, visited-set cycle guard at every depth, runtime
  integration, and the cognition-lane null-drop invariant at
  `max_depth=2` (public + holdout splits).
- tests/test_composed_surface.py: 11/11 pass after the helper
  refactor (ADR-0062 behaviour preserved).
- `core test --suite smoke`: 67 pass.
- `core test --suite cognition`: 120 pass, 1 skipped.
- `core test --suite teaching`: 17 pass.
- `core eval cognition`: 100 / 91.7 / 100 / 100 (byte-identical).
2026-05-20 14:11:40 -07:00
Shay
06bbac86e1
feat(contemplation): ADR-0080 read-only speculative loop (#55)
* docs(adr): ADR-0080 contemplation loop boundary

* feat(contemplation): add read-only contemplation package

* feat(contemplation): add immutable speculative finding schema

* feat(contemplation): add deterministic substrate snapshot

* feat(contemplation): add frontier report miner package

* feat(contemplation): mine frontier compare failures as speculative findings

* feat(contemplation): add read-only contemplation runner

* feat(contemplation): add read-only contemplation CLI

* test(contemplation): prove read-only speculative loop invariants
2026-05-20 11:40:12 -07:00
Shay
9e791045eb docs(adr): ADR-0078 status Proposed → Ratified
Phase 1 telemetry shipped in 8e96728; bump the ADR status header to
match reality.
2026-05-20 06:30:48 -07:00
Shay
e550c4a240
docs(adr): ADR-0078 composer graph atom equivalence (#50) 2026-05-20 06:30:09 -07:00
Shay
8e96728009 feat(telemetry): ADR-0078 Phase 1 — composer/graph atom equivalence (observational)
Wires observational telemetry on the composer-vs-graph atom-set
relationship.  Phase 1 is strictly observational: no enforcement,
no surface mutation, no grounding-source change, no trace-hash impact.

New telemetry fields on TurnEvent + ChatResponse:
  composer_graph_atom_status         ∈ {equivalent, divergent,
                                         graph_unconstrained,
                                         composer_no_atoms,
                                         not_applicable, ""}
  composer_atom_set_hash             SHA-256 over sorted unique atoms
  graph_atom_set_hash                SHA-256 over sorted unique atoms
  composer_graph_atom_overlap_count  int

Composer atoms come from existing pack candidate metadata
(pack_semantic_domains channel through _maybe_pack_grounded_surface).
Graph atoms come from build_graph_from_input + resolve_lemma on
node.subject/predicate/obj — no prose parsing.  When a grounded
composer path lacks explicit atom provenance, status is
'composer_no_atoms'.

New pure helper:
  chat/atom_equivalence.py — normalize_atoms, hash_atoms,
  atoms_for_graph_nodes, compare_atom_sets

Tests (tests/test_composer_graph_atom_equivalence.py):
  - Pack DEFINITION path produces observable equivalence
  - Divergent atom sets produce distinct hashes
  - Register invariance: atom hashes + status identical across
    {neutral, terse, convivial}; trace_hash also constant (R5 axis)
  - Anchor lens engaged case still ASCII-only on surface
  - No prose-parsing helper symbols introduced in runtime.py
    (extract_candidate_surface_lemmas, surface_lemma,
    parse_surface_atoms) — enforces Phase 1 boundary

Performance note: build_graph_from_input now runs on every warm
English turn (previously only when forward_graph_constraint=True).
Phase 1 accepts this cost to make the telemetry universally
available; Phase 2+ can introduce a feature flag if needed.

Validation:
  - Cognition eval byte-identical: 100/100/91.7/100
  - Full lane: 2864 passed, 3 skipped, 0 failed (+5 over baseline)
  - Targeted lane: 72 passed in tests/test_{graph_constraint,
    pack_grounding,register_tour_demo,anchor_lens_tour_demo,
    orthogonality_tour_demo,realizer_guard_holdout,
    composer_graph_atom_equivalence}.py
2026-05-20 06:14:25 -07:00
Shay
5a78b0e37b feat(register): ADR-0077 — substantive register knobs + layering boundary (R6)
R5 (ADR-0072) shipped the register *machinery*; ADR-0074's orthogonality
tour proved the axis was decoratively orthogonal to anchor-lens but
inspection of the cognition-eval surfaces revealed two structural gaps:

* On pack-grounded DEFINITION/RECALL/COMPARISON composers, the only
  realizer override any register consumed was `disclosure_domain_count`
  — which only fires on the no-gloss disclosure path.  Under terse_v1,
  every gloss-DEFINITION cell was byte-identical to default_neutral_v1.
* The register-tour's `surfaces_vary_at_least_once` gate could be
  satisfied by convivial's decorative wrapper alone, masking that
  regression in CI.

R6 closes both:

Layering separation (the load-bearing fix):
* New TurnEvent/ChatResponse field `register_canonical_surface` carries
  the composer output BEFORE any register transformation.  The pipeline
  hashes this field for `trace_hash`, preserving R5's invariant that
  per-prompt trace_hash is CONSTANT across registers even while
  substantive transforms produce visibly different surfaces.

Substantive transforms (`chat/register_substantive.py`):
* terse_v1 gains 3 bool knobs: `drop_provenance_tag`, `compress_gloss`,
  `drop_articles` — all pure regex transforms on the canonical surface.
* convivial_v1 gains `append_semantic_domain_clause` — appends a single
  bounded "Related: <atom>." clause using the lemma's pack atoms.
* default_neutral_v1 leaves overrides empty; substantive transform is
  byte-identical no-op (preserves `byte_identity_null_lift`).
* C1 (ADR-0075) safety preserved: drop_articles refuses to drop
  articles following `not` (avoids R3 violations); no knob combination
  trips R2/R3.

Strengthened tour gate (`evals/register_tour/run_tour.py`):
* Replaces `surfaces_vary_at_least_once` with two falsifiable claims:
  - `terse_substantively_differs_from_neutral_on_pack_grounded_definition`
  - `convivial_substantively_differs_from_neutral_on_pack_grounded_definition`
  Both restrict to DEFINITION+pack-grounded cells and require
  difference beyond whitespace/punctuation.
* New claim `register_canonical_surfaces_identical` directly proves
  the layering separation.
* Preserves R5's `all_grounding_sources_identical` +
  `all_trace_hashes_identical`.

Pack ratification:
* Loader widened to accept `bool` for closed-set R6 keys
  (drop_provenance_tag / compress_gloss / drop_articles /
  append_semantic_domain_clause).
* `_KNOWN_OVERRIDE_KEYS` ratify gate extended with same.
* terse_v1 + convivial_v1 reratified with new knobs; companion
  mastery reports re-sealed.  default_neutral_v1 unchanged.

Invariants pinned:
* `invariant_register_canonical_surface_constant_across_registers` (new)
* `invariant_terse_substantively_distinct_from_neutral` (new)
* `invariant_convivial_substantively_distinct_from_neutral` (new)
* `invariant_realizer_no_illegal_articulation` (C1, preserved)
* `invariant_realizer_guard_byte_identity_on_currently_passing_cases`
  (C1, preserved)

Verification:
* `core eval cognition`: 100.0% / 91.7% / 100.0% / 100.0% — byte-
  identical under default_neutral_v1.
* `core demo register-tour`: all 5 claims green, exit 0.
* `core demo anchor-lens-tour`: green (no anchor-lens code touched).
* `core demo orthogonality-tour`: green (5/5 claims).
* Full lane: 2858 passed, 1 pre-existing failure
  (test_all_preamble_explains_combined_run, carried forward
  unchanged from main).  56 new R6 tests across three files.
2026-05-19 23:39:11 -07:00
Shay
d7499c80b3
feat(intent): normalize confirmation-tag propositions (#45) 2026-05-19 22:55:28 -07:00
Shay
7cc2888ed2 feat(coherence): ADR-0075 — realizer slot-type guard (C1)
C1 coherence floor: a deterministic verifier that runs on every
candidate surface produced by the truth path, before assignment to
ChatResponse.surface.  Rejects illegal articulations and routes them
to a bounded disclosure string — admission control with a
deterministic fallback, not normalization.

Active rules (R1 deferred during ratification — see ADR):
  R2_aux_neg_requires_verb     — "<aux> not <wrong-POS>"  rejected
  R3_be_neg_requires_predicate — "<be>  not <verb>"       rejected

Fail-open on unknown POS, fail-closed on explicit wrong POS.
Cognition eval byte-identical (100/91.7/100/100).

Original bug class — "Light reveals truth, right?" → "Right does not
thought." — now routes to "I do not have a reviewed articulation for
that yet." with grounding_source=none, walk_surface preserving the
rejected candidate, and telemetry carrying R2_aux_neg_requires_verb.

Files:
  generate/realizer_guard.py            NEW — pure verifier
  chat/runtime.py                       hook on stub + main paths
  chat/telemetry.py                     serialize guard fields
  core/physics/identity.py              TurnEvent +2 fields
  evals/realizer_guard/run_holdout.py   NEW — 6-prompt cluster
  tests/test_realizer_guard_*.py        NEW — 46 tests (unit/seam/holdout)
  docs/decisions/ADR-0075-*.md          NEW — ratified

Invariants pinned:
  invariant_realizer_no_illegal_articulation
  invariant_realizer_guard_byte_identity_on_currently_passing_cases

Lanes (excluding 1 pre-existing TestDemoPreambles failure unrelated
to C1, already present at 4426f38):
  smoke 67/67  cognition 120/120(+1s)  teaching 17/17
  packs 6/6   runtime 19/19   algebra 132/132   full 2792/2793
2026-05-19 22:35:09 -07:00
Shay
4426f387d1 feat(demo): ADR-0074 — orthogonality tour (anchor-lens × register)
A single demo that walks the full 3 × 3 × 2 matrix (register × lens
× prompts, 18 cells) and pins five claims simultaneously, packaging
both single-axis invariants into one composition gate.

The single-axis tours assert opposite invariants:

  register-tour    : per (lens, prompt), trace_hash CONSTANT across
                     registers (R5 / ADR-0072).
  anchor-lens-tour : per (register, prompt), engaged lens diverges
                     in trace_hash from the unanchored baseline
                     (L1.4 / ADR-0073d).

Orthogonality-tour packages both claims simultaneously across the
full matrix, plus three surface-level claims that pin the markers
operators actually see.

Composed claims (all five must hold)

  A) inner_register_invariant_within_lens
     For each (lens, prompt) cell, the three register runs share an
     identical trace_hash.  (R5 register-tour, applied 6 times:
     3 lenses × 2 prompts.)

  B) outer_lens_distinctness_within_register
     For each (register, prompt) cell where any non-unanchored lens
     engages, that engaged lens's trace_hash differs from the
     unanchored baseline at the same (register, prompt).
     (L1.4 anchor-lens-tour, applied 6 times: 3 registers × 2 prompts.)

  C) surface_carries_register_marker_under_convivial
     Every convivial cell with a non-empty surface has a non-empty
     register_variant_id.

  D) surface_carries_lens_annotation_when_engaged
     Every engaged cell carries [lens(<id>):<mode>] in surface AND
     a non-empty anchor_lens_mode_label.

  E) no_substrate_glyph_leak_across_grid
     No cell's surface contains Greek/Hebrew/Syriac/Arabic glyphs.
     (ADR-0073c gate re-asserted across the full matrix.)

CLI wiring

  core demo orthogonality-tour            human-readable grid + claims
  core demo orthogonality-tour --json     structured report

Exit code 0 iff all five claims hold.

Files

  evals/orthogonality_tour/__init__.py             NEW
  evals/orthogonality_tour/run_tour.py             NEW
  core/cli.py                                       EDIT
    - cmd_demo handler wires orthogonality-tour
    - demo choices + EPILOG examples updated
  tests/test_orthogonality_tour_demo.py             NEW (9 tests)
  docs/decisions/ADR-0074-orthogonality-tour.md     NEW

Sanity check baked into tests
  test_engaged_cells_appear_for_both_non_trivial_lenses pins that
  grc_logos_v1 engages on knowledge in all 3 registers (3 cells)
  and he_logos_v1 engages on truth in all 3 registers (3 cells).
  Prevents the lift claims being vacuously satisfied by a future
  engagement regression.

Lane evidence

  - 9 new orthogonality-tour tests pass.
  - core demo register-tour      → all_claims_supported: True
  - core demo anchor-lens-tour   → all_claims_supported: True
  - core demo orthogonality-tour → all_claims_supported: True
  - python -m core.cli eval cognition → byte-identical 100/100/91.7/100.
  - Full lane: 2745 passed / 4 skipped / 1 pre-existing failure
    (+9 over L1.4's 2736; the one failure remains
    test_all_preamble_explains_combined_run, unrelated).

No runtime / composer / loader / pack / schema changes.  Pure demo
consumer of existing telemetry contracts.
2026-05-19 20:33:33 -07:00
Shay
1feec74b1c feat(anchor_lens): ADR-0073d — L1.4 telemetry, CLI flag, tour demo
L1.4 closes the anchor-lens inside-out arc (L1.1→L1.4 mirroring
R1→R5).  Substantive axis is now operator-observable,
operator-driven, and demo-falsifiable — exactly what R5 did for
the register subsystem.

Telemetry extension
  - TurnEvent + ChatResponse gain anchor_lens_id +
    anchor_lens_mode_label (both default "" → pre-L1.4
    byte-identical).
  - serialize_turn_event surfaces both fields in every JSONL line.
  - Mode-label extracted via _ANCHOR_LENS_ANNOTATION_RE from the
    PRE-decoration surface (so register decoration cannot interfere
    with anchor-lens telemetry).  Composer remains the sole source
    of truth for engagement; the runtime helper is read-only.

Operator surface
  - core chat --anchor-lens <id> CLI flag threads into
    RuntimeConfig.anchor_lens_id.
  - Invalid id → AnchorLensError caught at cmd_chat and surfaced
    as _die("invalid --anchor-lens pack id: ...", code=2) before
    the REPL launches.
  - Composes with --register (both flags wire through
    _runtime_config_from_args).

Narrative demo
  - evals/anchor_lens_tour/run_tour.py walks 2 prompts × 3
    ratified lenses ({default_unanchored_v1, grc_logos_v1,
    he_logos_v1}).  Asserts four claims:
      * lens_ids_recorded_per_turn
      * trace_hashes_distinct_across_lenses (OPPOSITE of
        register-tour's identical-hash claim)
      * surface_propositions_distinct_across_lenses
      * no_substrate_glyph_leak (block-scoped Greek/Hebrew/
        Syriac/Arabic; stylistic punct allowed)
  - Exit code 0 iff all four hold.
  - Bundled into `core demo` choices + EPILOG.

Tests (30 new)
  - tests/test_anchor_lens_telemetry.py (16) — TurnEvent shape,
    serializer keys, runtime emits per lens / per engagement
    state, ChatResponse mirrors event, mode-label extractor unit.
  - tests/test_anchor_lens_cli.py (9) — _runtime_config_from_args
    threading, invalid id fail-fast, parser flag wiring, parser
    composes with --register.
  - tests/test_anchor_lens_tour_demo.py (9) — four seam claims
    pinned individually + all_claims_supported + per-cell
    anchor_lens_id + unanchored cells empty mode + engaged cells
    carry mode label.

Lane evidence
  - 30 new L1.4 tests pass.
  - core demo anchor-lens-tour --json → all_claims_supported: True.
  - core demo register-tour --json    → all_claims_supported: True.
    Both tours pass simultaneously — orthogonality CI-pinned.
  - python -m core.cli eval cognition → public 100/100/91.7/100
    byte-identical (lens=None / default_unanchored_v1).
  - Full lane: 2736 passed / 4 skipped / 1 pre-existing failure
    (+30 over L1.3's 2706; the one failure remains
    test_all_preamble_explains_combined_run, unrelated).

Live demo (canonical proof)
  P1: 'What is knowledge?'
    default_unanchored_v1  trace=17c9aabe…  mode=(none)
    grc_logos_v1           trace=0198ad4c…  mode=systematic
    he_logos_v1            trace=17c9aabe…  mode=(none)
  P2: 'What is truth?'
    default_unanchored_v1  trace=2557f3e8…  mode=(none)
    grc_logos_v1           trace=2557f3e8…  mode=(none)
    he_logos_v1            trace=ec8d84aa…  mode=covenant-verity

  Engagement is substrate-scoped: grc never touches truth, he
  never touches knowledge.  Trace hashes diverge exactly where the
  lens engages.

Trust boundaries
  - --anchor-lens flag does not bypass ratification; loader still
    enforces companion mastery report self-seal + ratify-time
    substrate-atom existence check (ADR-0073b/c).
  - Mode-label extraction is read-only regex parse; can't forge
    annotations the composer didn't emit.
  - Telemetry stays redact-safe — both fields are identifiers /
    mode labels, not content.  include_content=False emits them
    unconditionally.
  - No new mutation surface; pack files unchanged.

Closes the anchor-lens inside-out arc
  L1.1  content prerequisite                  ✓ (ADR-0073a)
  L1.2  class + loader + unanchored sentinel  ✓ (ADR-0073b)
  L1.3  first lenses + composer wiring        ✓ (ADR-0073c)
  L1.4  telemetry + CLI + tour demo           ✓ (this commit)

  Mirrors the R1→R5 register cadence exactly.  Both axes are now
  operator-observable, CI-falsifiable, audit-traceable, and
  composable via the orthogonality claim pinned in both tours.
2026-05-19 20:21:41 -07:00
Shay
b35bec6465 feat(anchor_lens): ADR-0073c — L1.3 first lenses + composer wiring
L1.3 of the anchor-lens inside-out rollout — first substantive
surface lift on the substantive axis.  Two ratified non-trivial
lenses engage on cognition-pack lemmas via the alignment graph,
appending [lens(<id>):<mode>] annotations to the existing
pack-grounded surface.

Two ratified lenses

  grc_logos_v1 (Greek substrate)
    primary_substrate         : "grc"
    semantic_domain_preferences: ["logos.episteme.systematic_knowledge"]
    cognitive_mode_label       : "systematic"
    Engages on en "knowledge" via grc-core-cog-021 (ἐπιστήμη) →
    en-core-cog-007 alignment edge.

  he_logos_v1 (Hebrew substrate)
    primary_substrate         : "he"
    semantic_domain_preferences: ["logos.aletheia.verity"]
    cognitive_mode_label       : "covenant-verity"
    Engages on en "truth" via he-core-cog-002 (אמת) →
    en-core-cog-002 alignment edge.

  Both ratified under method anchor_lens_lifts_proposition.

Engagement rule (single)

  1. Resolve en_lemma → entry_id (cognition pack).
  2. For each substrate pack matching lens.primary_substrate, load
     alignment.jsonl; find edges where target_id == entry_id.
  3. For each such substrate lemma, if any atom in its
     semantic_domains ∈ lens.semantic_domain_preferences → engage.
  4. No match → None (no annotation; byte-identical surface).

The pivot is shared semantic_domain atoms surfaced via the
alignment graph — exactly the language-neutral commitment from
ADR-0073.  Engagement never touches non-English surface text;
entry_ids and atom strings only.

Surface lift

  no-lens : "Knowledge is X. pack-grounded (en_core_cognition_v1)."
  lens-on : "Knowledge is X. pack-grounded (en_core_cognition_v1) [lens(grc_logos_v1):systematic]."

  Annotation between existing provenance and trailing period.
  Both metadata fields are ASCII-bounded ≤64 chars at the loader
  level, so the annotation can never carry non-ASCII.

Scope deliberately narrow

  L1.3 wiring restricted to pack_grounded_surface /
  build_pack_surface_candidate (DEFINITION/RECALL only).  Other
  composers (COMPARISON / CORRECTION / PROCEDURE / NARRATIVE /
  EXAMPLE / CAUSE / VERIFICATION) accept the anchor_lens kwarg via
  forward-compat default UNANCHORED but do not yet consume it.
  L1.3b or later broadens to those intent shapes.

Ratify gate widening

  Non-null lenses must:
    - have primary_substrate ∈ {grc, he, en}
    - have a non-empty cognitive_mode_label
    - every preferred atom must exist in at least one lemma of the
      named substrate (trust boundary: operators cannot ship a lens
      pointing at atoms not on disk).
  Method: anchor_lens_lifts_proposition.  Null lenses still ratify
  under byte_identity_null_lift (L1.2 method).

Seam allow-list widening

  Truth-path modules (cognition / trace / pipeline / intent /
  propagation / vault / algebra) still refused.  Composer-side
  imports from chat/pack_grounding.py now permitted — the same way
  ADR-0069's R2 widened the register seam.

New invariants pinned (3)

  tests/test_anchor_lens_engagement_unit.py (14 tests) — resolver
  returns mode label only on intended substrate × en lemma pair;
  case-insensitive; engagement None under null lens; synthetic
  lens with unmatched atom returns None; annotation is pure ASCII.

  tests/test_anchor_lens_lifts_proposition.py (17 tests) — grc
  engages on knowledge only, he engages on truth only,
  cross-lens isolation, three-way distinctness, replay determinism
  per (lens × prompt), register-tour seam holds within each lens
  scope (orthogonality CI-pinned, parametrized over 4 lens
  choices).

  tests/test_anchor_lens_no_glyph_leak.py (5 tests) — hard
  block-scoped gate: Greek (U+0370..03FF, U+1F00..1FFF), Hebrew
  (U+0590..05FF), Syriac, Arabic.  Stylistic punctuation
  (em-dash etc.) explicitly allowed; em-dash predates L1.3 by a
  wide margin and is not a substrate-leak risk.  Tested per-lens
  across every cognition case + direct lens-metadata ASCII check.

Lane evidence

  74 anchor-lens tests pass (37 from L1.2 + 37 new).
  python -m core.cli eval cognition → public 100/100/91.7/100
  byte-identical (lens=None / default_unanchored_v1).
  core demo register-tour --json → all_claims_supported: True
  (R5 seam still holds; L1.3 doesn't perturb presentation axis).
  Full lane: 2706 passed / 4 skipped / 1 pre-existing failure
  (+37 over L1.2's 2669; the one failure remains
  test_all_preamble_explains_combined_run, unrelated).

Files

  packs/anchor_lens/grc_logos_v1.json                        NEW
  packs/anchor_lens/grc_logos_v1.mastery_report.json         NEW
  packs/anchor_lens/he_logos_v1.json                         NEW
  packs/anchor_lens/he_logos_v1.mastery_report.json          NEW

  scripts/ratify_anchor_lens_packs.py                        EDIT
    LENS_IDS adds grc_logos_v1 / he_logos_v1; gate widened.

  chat/pack_grounding.py                                     EDIT
    _resolve_anchor_lens_mode, _maybe_append_anchor_lens_annotation,
    _substrate_lexicon_by_entry_id, _en_lemma_to_entry_id.
    build_pack_surface_candidate + pack_grounded_surface gain
    anchor_lens kwarg (default UNANCHORED).

  chat/runtime.py                                            EDIT
    Thread self.anchor_lens into pack_grounded_surface() call.

  tests/test_anchor_lens_pack_seam.py                        EDIT
    Doc-comment updated for L1.3 allow-list.

  tests/test_anchor_lens_*                                   NEW (3 files)

  docs/decisions/ADR-0073c-anchor-lens-composer-wiring.md    NEW
2026-05-19 20:06:02 -07:00
Shay
9b1b63b253 feat(anchor_lens): ADR-0073b — L1.2 class + loader + unanchored sentinel
L1.2 of the anchor-lens inside-out rollout — pack class, loader,
ratified sentinel pack, and runtime threading.  Mirrors the
ADR-0068 register-class pattern exactly.  No composer consumes the
lens yet — that's L1.3.

AnchorLens frozen dataclass (packs/anchor_lens/loader.py)
  - lens_id / version / description / display_name
  - primary_substrate ∈ {grc, he, en, none}
  - semantic_domain_preferences: tuple[str, ...] (ordered, ≤64 atoms
    of ≤64 chars each, no duplicates)
  - cognitive_mode_label: str (≤64 chars)
  - mastery_report_sha256
  - is_unanchored() / is_null_lens() predicates
  - unanchored() classmethod + module-level UNANCHORED singleton

Loader contract (mirror of packs/register/loader.py)
  - safe_pack_id path-traversal rejection
  - Schema validation + envelope bounds checks
  - Companion mastery report self-seal + report_sha256 verification
  - CORE_ALLOW_UNRATIFIED_ANCHOR_LENS=1 dev bypass
  - require_ratified default True
  - No truth-path imports (pinned by seam test)

default_unanchored_v1 ratified pack
  - Null lens: primary_substrate="none", empty preferences,
    empty cognitive_mode_label
  - Self-sealed at b3235072fdbb2219...
  - Ratification method: byte_identity_null_lift
  - scripts/ratify_anchor_lens_packs.py L1.2 gate accepts only
    null lenses; L1.3 will widen.  Idempotent.

RuntimeConfig threading
  - new field: anchor_lens_id: str | None = None
  - new constant: DEFAULT_ANCHOR_LENS = "default_unanchored_v1"
  - ChatRuntime.__init__ loads the lens (None → AnchorLens.
    unanchored(); otherwise load_anchor_lens(id)) and stores as
    self.anchor_lens + self.anchor_lens_id.  Invalid ids fail-fast
    at init via AnchorLensError, not at first turn.
  - No composer reads the attribute yet.

Tests pinned (37 total)
  - tests/test_anchor_lens_pack_loader.py (24) — load happy path,
    sentinel structural identity, invalid id rejection (traversal,
    empty, slashes, missing), ratification bypass paths, companion
    SHA mismatch, bounds (substrate / preferences / atoms / label /
    duplicates / capacity), field-missing, lens_id mismatch with
    filename, unsupported schema_version.
  - tests/test_anchor_lens_null_lift.py (4) — load-bearing L1.2
    invariant `anchor_lens_byte_identity_null_lift`: full public
    cognition lane byte-identical for surface, trace_hash, and
    aggregate metrics between anchor_lens_id=None and
    "default_unanchored_v1".
  - tests/test_anchor_lens_pack_seam.py (9) — AST refuses any
    `packs.anchor_lens` import from truth-path modules (cognition /
    trace / pipeline / intent / propagation / vault / algebra) AND
    refuses any truth-path import from the loader itself.

Lane evidence
  - All 37 anchor-lens tests pass.
  - python -m core.cli eval cognition → public 100/100/91.7/100
    byte-identical (lens loaded but no composer reads it).
  - core demo register-tour --json → all_claims_supported: True
    (R5 seam still holds; L1.2 doesn't perturb register).
  - Full lane: 2669 passed / 4 skipped / 1 pre-existing failure
    (+37 over L1.1's 2632; the one failure remains
    test_all_preamble_explains_combined_run, unrelated).

Trust boundaries (per CLAUDE.md / ADR-0051)
  - safe_pack_id path-traversal rejection at loader entry.
  - No dynamic imports.
  - Loader is read-only; mutation only via ratify script.
  - Seam test refuses any new anchor-lens import upstream of the
    realizer.  L1.3 will widen the allow-list to include composer
    files at the same time it adds composer behaviour — exactly the
    way the register seam was widened at R2.

What L1.2 deliberately does NOT do
  - No composer consumes the lens (that's L1.3).
  - No TurnEvent / ChatResponse telemetry fields (L1.4).
  - No `core chat --anchor-lens` CLI flag (L1.4).
  - No anchor-lens-tour demo (L1.4).
2026-05-19 19:46:34 -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
f673c0eb06 docs(adr): ADR-0073 — anchor lens substrate (Proposed)
Umbrella ADR for the substantive-variation axis that composes
orthogonally against register (ADR-0068..0072).  Drafted only;
status Proposed.  No code, no pack, no test landed.

Architecture summary
  - Anchor lens is the substantive axis: register varies surface text
    while keeping grounding_source / trace_hash byte-identical;
    anchor lens deliberately moves both because the proposition
    itself changes when the substrate changes.
  - Pivot is shared `semantic_domains` atoms (already on disk across
    grc / he / en cognition packs), not transliteration tables — the
    seam stays language-neutral so future substrates compose without
    touching anchor-lens code.
  - English compound phrasing only at the surface ("knowing-as-
    experience", "knowing-as-system"); Greek / Hebrew glyphs live in
    audit / provenance fields only.  L1.3 invariant
    `anchor_lens_no_glyph_leak` is a hard gate.

Four-phase rollout (mirrors R1–R5 cadence)
  L1.1  content phase — distinction-bearing lemma additions
        (ἐπιστήμη / σύνεσις / ἀγάπη-φιλία-ἔρως-στοργή / αἰών-χρόνος-
        καιρός; חסד / שלום / צδק) + alignment.jsonl on the cognition-
        tier packs.  No code.  Prerequisite for every later phase.
  L1.2  AnchorLens pack class + loader + `default_unanchored_v1`
        sentinel.  Null-lift CI invariant pinned.
  L1.3  First non-trivial lenses (`grc_logos_v1`, `he_logos_v1`)
        wired into chat/pack_grounding.py composers.  Proposition-
        lift invariant + glyph-leak gate pinned.
  L1.4  Telemetry (TurnEvent + ChatResponse gain anchor_lens_id),
        `core chat --anchor-lens` flag, `core demo anchor-lens-tour`
        asserting trace_hashes_distinct_across_lenses (opposite of
        register-tour's claim — both must hold).

Three honest gaps blocking L1.2+
  - Distinction-bearing lemmas absent from cognition packs.
  - No reviewed teaching corpus for non-English (cognition_chains,
    relations_chains, cross_pack_chains all en-only).
  - No realizer infrastructure for cross-lingual surface composition.

L1.1 (pure content) closes all three for the cognition tier.

Orthogonality claim — load-bearing
  register-tour    : per prompt, fix lens, vary register → trace_hash CONSTANT
  anchor-lens-tour : per prompt, fix register, vary lens → trace_hash DISTINCT
  Both must continue to hold; failure of either breaks the seam.
2026-05-19 19:13:01 -07:00
Shay
7f0bad3e20 feat(register): R5 — operator-visible register telemetry + tour demo
ADR-0072 ratified + implemented.  Closes the register subsystem
inside-out arc (R1 ADR-0068 → R5 ADR-0072): the presentation axis is
now operator-visible, CI-falsifiable, and audit-traceable.

Telemetry extension
  - TurnEvent + ChatResponse gain register_id + register_variant_id
    (12-char SHA-256 prefix of selected (opening, closing) pair;
    empty string for UNREGISTERED / no-decoration registers).
  - serialize_turn_event surfaces both fields in every audit JSONL
    line.  Pre-R5 callers stay byte-identical (defaults are "").

Decoration result widened
  - chat/register_variation.py: decorate_surface now returns
    DecorationResult(surface, opening, closing, variant_id).
  - decorate_surface_str alias preserves the pre-R5 string-only API
    for off-runtime callers.
  - chat/runtime.py updated at both call sites (stub + main).

Operator surface
  - core chat --register REGISTER_ID threads into
    RuntimeConfig.register_pack_id via _runtime_config_from_args.
  - Invalid id ⇒ RegisterPackError caught at cmd_chat and surfaced
    as a clean _die(...) before the REPL launches.

Narrative demo
  - evals/register_tour/run_tour.py walks 4 prompts × 3 ratified
    registers ({default_neutral_v1, terse_v1, convivial_v1}) and
    asserts three load-bearing seam claims:
      * all_grounding_sources_identical
      * all_trace_hashes_identical (ADR-0069 invariant C, falsifiable)
      * surfaces_vary_at_least_once (ADR-0071 seeded variation lift)
  - core demo register-tour exit code = 0 iff every claim holds.

Tests
  - tests/test_register_telemetry.py (6) — TurnEvent default,
    serializer keys, runtime emits register_id/variant_id for
    convivial/terse/unregistered, ChatResponse mirrors event fields.
  - tests/test_register_cli.py (7) — _runtime_config_from_args
    threading, invalid-id fail-fast, parser wires --register.
  - tests/test_register_tour_demo.py (7) — three seam claims pinned
    individually + all_claims_supported + per-cell register_id +
    variant_id discipline (empty for neutral/terse, non-empty for
    convivial).
  - tests/test_register_variation.py extended (4 new) — DecorationResult
    shape, decorate_surface_str alias, variant_id stability,
    bijection between non-trivial marker pairs and variant_ids.

Lane evidence
  - Full lane: 2632 passed / 4 skipped / 1 pre-existing failure
    (tests/test_cli_demo.py::test_all_preamble_explains_combined_run,
    unrelated to R5).
  - Cognition eval byte-identical: public 100 / 100 / 91.7 / 100.

Trust boundaries (per CLAUDE.md)
  - --register flag does not bypass ratification; loader validates the
    pack id through _find_pack and the ratify gate at load time.
  - variant_id is content-addressed; no raw markers leak into audit.
  - Telemetry stays redact-safe — register_id and variant_id are
    identifiers, not content, so include_content=False emits them
    unconditionally.
  - No new mutation surface; pack files on disk are not modified.
2026-05-19 19:03:07 -07:00
Shay
6207b5fd0e feat(register): R1–R4 register pack subsystem — deterministic surface variation
Introduces the presentation axis as a fourth pack class (sibling to identity /
safety / ethics), orthogonal to the truth path. Same input + same packs +
same register ⇒ bit-for-bit reproducible surface; varying any of the three ⇒
genuinely different output. No stochastic sampling.

ADR-0068 (R1): RegisterPack frozen dataclass, loader, ratify script, seam test.
  - default_neutral_v1 ratified as null register.

ADR-0069 (R2): realizer register parameter threaded through 9 composer entry
  points; RuntimeConfig.register_pack_id; three byte-identity invariants
  (A: None ≡ pre-R2 unregistered; B: None ≡ default_neutral_v1; C: trace_hash
  invariant under register). Amended to default-with-lint after 167-call-site
  scout: composers default to UNREGISTERED, AST lint enforces explicit
  register= at runtime call sites.

ADR-0070 (R3): terse_v1 register, first non-neutral pack. realizer_overrides
  schema with known-keys allow-list (disclosure_domain_count ∈ {1,2,3}).
  build_pack_surface_candidate reads override with fail-soft clamp. New
  invariant register_invariant_grounding asserts grounding_source +
  trace_hash byte-identical across {None, neutral, terse}.

ADR-0071 (R4): seeded surface variation via convivial_v1.
  chat/register_variation.py applies SHA-256-seeded marker selection from
  bounded discourse-marker buckets. ChatResponse.pre_decoration_surface routes
  truth-path surface to core/cognition/pipeline.py so trace_hash stays
  invariant under register (the load-bearing architectural fix — initially
  invariant C failed under convivial because decoration was leaking into
  trace_hash via response.surface). Empty-string marker entries now
  legitimate ("no marker this turn" is a valid seed pick). realizer_overrides
  schema widened with per_intent nested block (validated against IntentTag
  whitelist; wired but not exercised by convivial). Two new invariants:
  seeded_variation_replay_equivalence (fresh runtimes → byte-identical) and
  seeded_variation_turn_distinct (same prompt across turns → ≥2 distinct
  surfaces).

ADR-0072 (R5, draft): telemetry + operator surface — TurnEvent gains
  register_id and register_variant_id, core chat --register flag, core demo
  register-tour. Status: Proposed; not yet implemented.

Three ratified register packs ship: default_neutral_v1 (null), terse_v1
(disclosure_domain_count=1), convivial_v1 (3 openings × 3 closings).

Verification:
  - 84 register tests pass + 1 documented skip
  - Curated lanes green: smoke 67, cognition 120+1s, teaching 17, packs 6,
    runtime 19, algebra 132
  - Cognition eval byte-identical to pre-register baseline:
    public 100/100/91.7/100, holdout 100/100/83.3/100
  - Full lane: 2608 passed, 4 skipped, 1 failed (pre-existing
    test_cli_demo.py "Combined Demo" → "Run Every Demo" rename, unrelated)

Truth-path isolation: chat/register_variation.py is realizer-side; the seam
test (tests/test_register_pack_seam.py) refuses imports of packs.register
from intent classification, propagation, vault recall, trace hashing, and
algebra.
2026-05-19 16:52:36 -07:00
Shay
d5a6e81b33 feat(adr-0067): cross-pack teaching chains — Plan Phase 4 closed
ADR-0064 bound each teaching corpus 1:1 to a single ratified pack;
chains whose subject + object resolved to different packs were
dropped at load time. Phases 1–3 ratified the per-pack DAGs needed
to lift that constraint safely.

ADR-0067 introduces a deliberately narrow cross-pack chain shape.
Each entry carries explicit subject_pack_id and object_pack_id
fields, and the loader verifies per-chain residency. Same-pack
entries are rejected as corpus-misfilings (anti-leakage). The
cross-pack composer is the fall-through after the in-pack composer,
so the cognition lane stays byte-identical.

Files:
- chat/cross_pack_grounding.py — CrossPackChain + loader +
  single-chain composer + multi-chain enumerators
- teaching/cross_pack_chains/cross_pack_chains_v1.jsonl — 5 seed
  chains (family×identity, parent×understanding, family×memory,
  identity×family, understanding×parent)
- chat/runtime.py — fall-through wiring in CAUSE/VERIFICATION
- chat/narrative_surface.py, chat/example_surface.py — merge
  cross-pack chains, per-chain pack-residency helpers
- tests/test_cross_pack_chains.py — 31 tests covering loader,
  surface, multi-chain access, runtime integration, in-pack
  precedence
- tests/test_narrative_example_intents.py — corpus-tag assertions
  widened to allow cross-pack aggregation

Verification:
- 31 new tests pass
- Curated lanes: smoke 67 / cognition 121 / teaching 17 / packs 6 /
  runtime 19 — all green
- Cognition eval byte-identical (public 100/100/91.7/100, holdout
  100/100/83.3/100)
- Full lane: 2098 passed, 2 skipped, 0 failed in 2:30
2026-05-18 17:22:43 -07:00
Shay
ce8226e9a2 feat(adr-0066): NARRATIVE + EXAMPLE intents with multi-clause composers (Phase 3.3 + 3.4)
Two new intent shapes + composers turn the runtime's corpus
density into operator-visible articulation.  Both consult the
cross-corpus aggregator from ADR-0064; no new ratification needed.

P3.3 — chat/narrative_surface.py + IntentTag.NARRATIVE.

  Classifier patterns (registered BEFORE generic DEFINITION):
    ^tell\s+me\s+about\s+
    ^describe\s+
    ^what\s+(?:can|do)\s+you\s+(?:say|know)\s+about\s+

  narrative_grounded_surface(subject, max_clauses=4) walks every
  reviewed chain rooted on subject across all registered teaching
  corpora.  Dedupes by (connective, object) — cause + verification
  carrying the same predicate emit one clause, not two.  Sorts by
  (intent, connective, object) for replay stability.

  Surface format:
    "{X} — narrative-grounded ({corpus_ids}): {dX1}; {dX2}.
     {X} {conn1} {O1} ({dO1}); {X} {conn2} {O2} ({dO2}).
     No session evidence yet."

  Cross-corpus subjects (e.g. mother in relations_v2) emit
  narrative-grounded (relations_chains_v2) tag; cognition subjects
  emit cognition_chains_v1 tag.  Multi-corpus subjects (when
  applicable) emit composite "corpus_a + corpus_b" tag.

P3.4 — chat/example_surface.py + IntentTag.EXAMPLE.

  Classifier patterns:
    ^(?:give|show)\s+(?:me\s+)?an?\s+(?:example|instance)\s+of\s+
    ^example\s+of\s+

  example_grounded_surface(object_lemma, max_examples=3) walks chains
  where the lemma is the OBJECT — inverts the typical subject-keyed
  access pattern.  Dedupes by subject; sorts by (intent, subject,
  connective).

  Surface format:
    "{X} — example-grounded ({corpus_ids}): {dX1}.
     Example: {subj1} {conn1} {X}; {subj2} {conn2} {X}.
     No session evidence yet."

Cross-cutting:
  - Both intents added to _OOV_INTENT_TAGS — fall through to OOV
    invitation when subject is unknown (Phase 2 gradient discipline).
  - Both tagged grounding_source="teaching" (same provenance tier
    as the existing teaching_grounded_surface).
  - No prose generation, no new mutation surface.

Live verification:
  > Tell me about truth.
    [teaching] truth — narrative-grounded (cognition_chains_v1):
    cognition.truth; logos.core. truth grounds knowledge
    (cognition.knowledge); truth requires evidence (cognition.evidence).

  > Give me an example of knowledge.
    [teaching] knowledge — example-grounded (cognition_chains_v1):
    cognition.knowledge. Example: truth grounds knowledge;
    understanding requires knowledge; evidence grounds knowledge.

  > Tell me about mother.
    [teaching] mother — narrative-grounded (relations_chains_v2):
    kinship.parent.female. mother precedes daughter (kinship.child.female).

  > Describe photosynthesis.
    [oov] I haven't learned 'photosynthesis' yet (intent: narrative). ...

ADR-0066 (this commit completes the ADR).  30 new tests passed.
Full lane: 2067 passed, 2 skipped, 0 failed in 2:32.
2026-05-18 17:01:55 -07:00
Shay
ea298bdc28 feat(teaching): OOV signal flywheel — sink, aggregator, auto-promotion (Phase 2.3)
Mirrors the chain-gap pipeline (Phase 1.1+1.2) for vocabulary gaps:
the OOV invitation surface (P2.1) now emits structured signals that
operators can aggregate, rank, and auto-promote into reviewed
PackMutationProposal candidates — closing the OOV loop the same way
Phase 1 closed the chain loop.

Three new modules + two new CLI surfaces:

teaching/oov_sink.py.
  OOVCandidate dataclass mirroring teaching.discovery.DiscoveryCandidate.
  OOVBufferSink (in-memory) + OOVMonthlyFileSink (append-only JSONL
  under <root>/<YYYY>/<YYYY-MM>.jsonl — same layout as discovery sink
  so the aggregator reuses the file-walk machinery).
  hash_oov_candidate_id(token, intent, trace_hash) — deterministic
  32-char hex id matching DiscoveryCandidate's replay invariant.
  format_oov_candidate_jsonl — sorted-keys compact JSONL line.

teaching/oov_gaps.py.
  aggregate_oov_gaps(root, since, sample_limit) groups emitted
  candidates by token, tracks intent-shape union (a token asked under
  multiple intents is a stronger curriculum signal), splits
  boundary_clean from boundary_tainted counts, supports --since
  YYYY-MM filtering via the sink's file naming convention.
  Pure reader; never mutates the sink.  Deterministic ordering:
  (count desc, token asc).

teaching/oov_promotion.py.
  promote_oov_gaps(gaps, threshold, include_tainted, suggested_packs)
  lifts threshold-crossing tokens to OOVPromotion records.
  - boundary_clean_count gates promotion by default (tainted-only
    tokens may indicate the prompt hit a safety axis rather than a
    vocab gap).
  - --include-tainted flag for operator override.
  - threshold < 1 raises.
  - queue_id deterministic: ``oov:<token>@<threshold>`` — diffable
    across runs.
  - suggested_packs lists mounted packs but does NOT recommend one
    — domain inference is out of scope (would require a stochastic
    classifier).  Operator picks the destination.

Runtime wiring:
  ChatRuntime.attach_oov_sink(sink) mirrors attach_discovery_sink.
  Runtime emits one OOVCandidate JSONL line per turn whose
  grounding_source == "oov", no-op when no sink is attached.
  Intent classifier is now invoked when EITHER sink is attached
  (was: only discovery sink) — both downstream paths need it.

CLI:
  core teaching oov-gaps [--top N] [--since YYYY-MM] [--root PATH]
                          [--sample-limit N] [--json]
  core teaching oov-queue [--threshold N] [--include-tainted]
                          [--root PATH] [--since YYYY-MM] [--json]

ADR-0065 documents the full design (five-tier honesty gradient,
P2.1-P2.4 architecture).  README.md updated with the ADR-0065
index entry.

Verification:
  tests/test_oov_pipeline.py                      24 passed
  Operator workflow round-trip verified live:
    > rt.attach_oov_sink(sink); rt.chat("What is photosynthesis?")
    → sink receives:
      {"boundary_clean":true,"candidate_id":"f51bf8...",
       "intent":"definition","token":"photosynthesis","trigger":"unresolved_subject",
       "source_turn_trace":"","review_state":"unreviewed"}
    > core teaching oov-gaps --root /tmp/oov_demo
    → ranked table by count, intent-set per token
    > core teaching oov-queue --root /tmp/oov_demo --threshold 2
    → promoted tokens + suggested mounted packs

Full lane: 2005 passed, 2 skipped, 0 failed in 2:34 (xdist).
2026-05-18 16:42:26 -07:00
Shay
b5ba9b6d6f feat(adr-0064): cross-pack teaching chains + relations_chains_v1 seed (Phase 1.3+1.4)
ADR-0064 is the corpus-layer sibling of ADR-0063.  The teaching-grounded
surface composer was hardcoded to cognition_chains_v1, so kinship CAUSE/
VERIFICATION prompts fell through to the universal disclosure even though
en_core_relations_v1 was mounted on the live runtime (ADR-0063).

Architectural change in chat/teaching_grounding.py:

  - New TeachingCorpusSpec dataclass (corpus_id, path, pack_id).
  - TEACHING_CORPORA tuple registers every active corpus.  Each
    corpus is 1:1-bound to one lexicon pack — cross-domain triples
    deferred per docs/teaching_order.md §5.
  - _load_corpus(spec) loads one corpus with pack-residency scoped
    to its declared pack.
  - _all_chains_index() aggregates across all registered corpora
    (first-match-wins; cognition first preserves byte-identity).
  - _pack_for_corpus(corpus_id) → bound pack lexicon.
  - clear_teaching_caches() atomic cache invalidation.
  - TeachingChain gains corpus_id field → surface tag follows resolving corpus.

Wiring updates:

  - teaching_grounded_surface + teaching_grounded_surface_composed
    consult _all_chains_index; surface tag follows chain.corpus_id.
  - teaching/discovery.py gate uses chat.pack_resolver.is_resolvable
    (any mounted pack) + _all_chains_index (any registered corpus).
  - teaching/replay.py _swap_corpus_path rewrites the registry path
    + clears all teaching caches during the gate's transient phase.
    Active corpus bytes unchanged (replay invariant preserved).
  - evals/learning_loop/run_demo.py scene-5 swap mirrors the new
    pattern so the demo still grounds against transient corpora.

Back-compat preserved: _corpus_index, _CORPUS_PATH, TEACHING_CORPUS_ID
remain cognition-corpus-specific for audit/replay consumers.

Phase 1.4 — relations_chains_v1 seeded with 7 reviewed kinship chains:
  cause_parent_precedes_child
  cause_child_follows_parent
  cause_ancestor_precedes_descendant
  cause_descendant_follows_ancestor
  cause_family_grounds_parent
  verification_child_requires_parent
  verification_descendant_requires_ancestor

5 of 8 relations lemmas covered.  All connectives already humanised.
Strict pack-internal to en_core_relations_v1 (no cross-domain in v1).
Seed pattern matches cognition_chains_v1's original pre-ADR-0055 seed.

Live verification:
  > Why does parent exist?
  parent — teaching-grounded (relations_chains_v1):
  kinship.ascendant.direct; kinship.parent.
  parent precedes child (kinship.descendant.direct).
  grounding_source = teaching

Cognition eval byte-identical to pre-ADR baseline:
  public:  intent 100% / surface 100% / term 91.7% / closure 100%
  holdout: intent 100% / surface 100% / term 83.3% / closure 100%

Lanes green: smoke 67 / cognition 121 / teaching 17 / packs 6 /
runtime 19 / algebra 132 / full 1933 passed.
2026-05-18 16:04:20 -07:00
Shay
9f83b27a7c feat(adr-0063): cross-pack surface resolver — kinship lemmas ground on live path
ADR-0063 closes the ADR-0048/0050/0053/0061 hardcoded-cognition-pack
asymmetry. New chat/pack_resolver.py provides resolve_lemma(lemma,
pack_ids) → (resolving_pack_id, semantic_domains) across an ordered
tuple of mounted lexicon packs (first-match-wins, lru_cache per-pack).

Surface composers in chat/pack_grounding.py now consult the resolver
instead of a hardcoded en_core_cognition_v1. en_core_relations_v1
joins RuntimeConfig.input_packs defaults; kinship lemmas now ground
on the live path:

  > What is a parent?
  parent — pack-grounded (en_core_relations_v1):
  kinship.ascendant.direct; kinship.parent; biology.progenitor.
  No session evidence yet.

Cross-pack comparison (knowledge × parent) renders composite tag
(en_core_cognition_v1 × en_core_relations_v1). Cognition lane
remains byte-identical: cognition is resolved first and the surface
format for cognition lemmas is unchanged.

Cognition eval (byte-identical to pre-ADR baseline):
  public  → intent 100% / surface 100% / term 91.7% / closure 100%
  holdout → intent 100% / surface 100% / term 83.3% / closure 100%

Curated lanes green: smoke 67 / cognition 121 / teaching 17 /
packs 6 / runtime 19 / algebra 132.

New tests: test_pack_resolver.py (28) + test_cross_pack_grounding.py
(17). test_en_core_relations_v1_pack.py: default-input-packs guard
inverted. test_pack_grounding.py: two stale ADR-0048 tests rewritten
(premises invalidated by ADR-0052/0061; now use fully-out-of-pack
prompts).

chat/teaching_grounding.py UNCHANGED — cognition_chains_v1 corpus
stays cognition-only. Cross-pack teaching corpora are the natural
ADR-0064.
2026-05-18 15:00:58 -07:00
Shay
c492014815 feat(adr-0062): composed teaching-grounded surface (chain-of-chains)
Pre-ADR-0062, the teaching-grounded composer emitted exactly one
reviewed chain per surface — "light reveals truth" — even when the
corpus already contained an immediate follow-up "truth grounds
knowledge".  With 21 active chains after curriculum saturation v2,
many grounded prompts had a corpus-ratified follow-up the composer
silently dropped.

ADR-0062 adds the composed composer + an opt-in config flag:

  flag OFF (default):
    light — teaching-grounded (cognition_chains_v1): cognition.illumination;
    logos.core. light reveals truth (cognition.truth). No session evidence yet.

  flag ON:
    light — teaching-grounded (cognition_chains_v1): cognition.illumination;
    logos.core. light reveals truth (cognition.truth), which grounds
    knowledge (cognition.knowledge). No session evidence yet.

Follow-up resolution:
  - prefer cause; fall back to verification (deterministic preference)
  - cycle guard: 1-step cycles (A→B, B→A) blocked
  - pack-residency guard: follow-up's object must be pack-resident
  - bounded depth: v1 follows exactly one hop
  - degrades to single-chain BYTE-IDENTICALLY when no follow-up
    survives the guards (drop-in replacement)

Trust-boundary invariants preserved:
  - Every visible non-template token is lemma / pack-domain /
    humanize_predicate connective / template constant.  Only added
    template constant: ", which "
  - Deterministic: same chains → same surface bytes
  - Default-False flag pattern mirrors ADR-0047/0058
  - `versor_condition < 1e-6` invariant untouched (surface composition only)

Cognition lane null-drop invariant CI-pinned:
  Composed mode emits a strictly LONGER surface (extra follow-up
  clause); every expected_term passing flag-OFF must still pass flag-ON.
  Asserted in test_cognition_lane_metrics_unchanged_with_composed_flag
  for both public and holdout splits.  If a future change drops tokens,
  the test fails as a deliberate regression.

  public  flag OFF: intent 100% / surface 100% / term 91.7% / versor 100%
  public  flag ON : intent 100% / surface 100% / term 91.7% / versor 100% (identical)
  holdout flag OFF: intent 100% / surface 100% / term 83.3% / versor 100%
  holdout flag ON : intent 100% / surface 100% / term 83.3% / versor 100% (identical)

Live-prompt lift visible on ~12 of 21 active chains; the rest hit
cycle or pack-residency guards.  Saturation v2's clusters were
authored partly with composition in mind (thought→meaning→
understanding, inference→evidence→knowledge, etc.).

- core/config.py — `RuntimeConfig.composed_surface: bool = False`
- chat/teaching_grounding.py — `teaching_grounded_surface_composed`
  sibling to `teaching_grounded_surface`
- chat/runtime.py — dispatch branch in `_maybe_pack_grounded_surface`
  selects composed vs single-chain based on config flag
- tests/test_composed_surface.py — 11 tests pin: function-level
  (None on no chain / degrades when no follow-up / two-clause when
  follow-up exists / includes intermediate + final domains /
  deterministic / cycle guard / trust label preserved); runtime
  integration (default single-chain / flag-on composed / frozen
  config); cognition-lane null-drop invariant.

Lanes (regression): smoke 67 / cognition 121 / teaching 17 /
composed-surface 11 — all green.
2026-05-18 14:34:45 -07:00