Commit graph

94 commits

Author SHA1 Message Date
Shay
c3e2a229a8 fix(pipeline): usefulness gate on realized-plan override
The 2026-05-19 design review's P0 #1 finding:

  > CognitiveTurnPipeline can replace a useful runtime surface with
  > placeholder prose.

Evidence at core/cognition/pipeline.py:147-149 (pre-fix):

  if realized_plan.surface and not gate_fired:
      surface = realized_plan.surface
      articulation_surface = realized_plan.surface

The override gate was JUST "non-empty + gate didn't fire".  No
usefulness check.  Result: a realizer output of
"Truth is defined as ..." (with <pending> rendered as ...) silently
overrode a perfectly-grounded runtime pack surface, and the runtime
audit log still held a third surface.

Fix: gate the override through ``_is_useful_surface`` from
generate/intent_bridge.py — the same predicate that already gates
the bridge's articulate_with_intent fallback path.  An ungrounded
realizer surface cannot honestly override a grounded runtime
surface.  When the realizer cannot produce a useful surface, we
keep the runtime answer the user sees.

Measured lift on the warmed_session_consistency lane (3 of its 4
metrics):

                                BEFORE      AFTER
  no_placeholder_rate         0.4444  →   1.0000
  telemetry_consistency_rate  0.4444  →   1.0000
  warm_grounding_stability    0.0000  →   0.0000  (separate bug — see below)

The two metrics that flipped to 1.00 are now CI-pinned in
tests/test_warmed_session_lane.py:
TestPipelineOverrideGateInvariants — any future weakening of the
override gate fails the suite immediately.

Cognition eval byte-identical:
  public:  100 / 100 / 91.7 / 100
  holdout: 100 / 100 / 83.3 / 100

KNOWN FOLLOW-UP — not in this commit:

  warm_grounding_stability remains 0.0 because of a SEPARATE bug
  the warmed lane surfaces:

    Turn 1: "What is truth?" -> pack-grounded ("truth — pack-grounded
            (en_core_cognition_v1): cognition.truth; ...")
    Turn 2: "What is truth?" -> vault-grounded ("Truth infer.")

  After turn 1 ingests pack content into the vault, turn 2's gate
  source flips from ``empty_vault`` to ``vault``, so the runtime's
  ``_maybe_pack_grounded_surface`` dispatcher is bypassed entirely
  and the field-walk path produces gibberish ("Truth infer.").

  This is the SurfaceSelector-shaped problem from the design review:
  pack-grounding should fire by intent shape and lemma residency, not
  by vault gate state.  Fix scope crosses runtime.py:chat() + the
  vault gate logic; deferred to its own commit / design proposal
  rather than absorbed here.

  The warmed lane already records the metric (0.0 baseline) so when
  the fix lands it shows up as a measurable lift.
2026-05-19 07:21:00 -07:00
Shay
1c8f2ee943 feat(packs): en_core_polarity_v1 — polarity + frequency (16 lemmas)
Workstream 1 eighth pack.  Closes the polarity-marker + frequency-
adverb gap.  Common conversational markers (yes/no/maybe/always/never)
had zero coverage in any prior pack.

Pack composition (16 entries — 2 INTJ / 14 ADV):

  polarity.affirm.*      yes indeed surely definitely
  polarity.negate.*      no hardly
  polarity.uncertain.*   maybe perhaps
  polarity.frequency.*   always sometimes often rarely never
                         usually occasionally frequently

``certain``/``certainly``/``uncertain`` deliberately excluded — those
remain in en_core_attitude_v1 (epistemic.certainty/uncertainty).
Regression test pins the invariant.

tests/test_correction_topic_lemma.py:
  Three fixtures swapped from "No that is wrong" to "Nope that is
  wrong".  ``no`` is now correctly pack-resident in en_core_polarity_v1
  (polarity.negate.dissent), so the "no pack-resident lemma" contract
  these tests pin needed a fixture where every content token is
  genuinely OOV.  ``nope`` is OOV across all 10 mounted packs; ``wrong``
  remains OOV (collision with attitude's ``right`` blocked spatial-
  direction ``right`` but did not add ``wrong``).

Authoring:
  Three parallel subagents — affirm / negate+uncertain / frequency.
2026-05-19 05:38:13 -07:00
Shay
e72e946c0b feat(packs): en_core_causation_v1 — causation vocabulary (15 lemmas)
Workstream 1 seventh pack.  Extends the causal apparatus beyond
cognition_v1's ``cause`` (NOUN+VERB) and ``because`` (SCONJ).

Pack composition (15 entries — 6 NOUN / 6 VERB / 3 ADJ):

  causation.effect.*     effect result consequence outcome impact influence
  causation.verb.*       trigger induce yield enable prevent drive
  causation.adjective.*  causal resultant consequent

``cause`` was deliberately retained in en_core_cognition_v1.  Test
pins the invariant.

Verification:
  Cognition eval byte-identical (100/100/91.7/100 public,
  100/100/83.3/100 holdout).
2026-05-19 05:38:12 -07:00
Shay
390c2834f8 feat(packs): en_core_spatial_v1 — spatial vocabulary (24 lemmas)
Workstream 1 sixth pack.  Closes the spatial-vocabulary gap.  Prior
packs had zero coverage of here/there, location nouns, or spatial
prepositions.

Pack composition (24 entries — 7 ADV / 8 ADP / 9 NOUN):

  spatial.deictic.*          here there  (2 ADV)
  spatial.direction.*        forward backward left up down  (5 ADV)
  spatial.relation.*         near far above below inside outside
                             between beyond  (8 ADP)
  spatial.noun.*             place location area region space
                             end top bottom side  (9 NOUN)

``right`` was deliberately omitted — en_core_attitude_v1 already owns
it as evaluative.positive, and first-match-wins resolution preserves
that claim.  A regression test pins this invariant explicitly.

Files: lexicon.jsonl / manifest.json + 12 contract tests.

Verification: full lane 2204 passed / 2 skipped / 0 failed.
Cognition eval byte-identical both splits.
2026-05-19 05:38:12 -07:00
Shay
891ffa8969 feat(packs): en_core_quantitative_v1 — quantifiers + numeric basics (24 lemmas)
Workstream 1 fifth pack.  Closes the quantifier + basic-numeric gap.
Prior packs had zero coverage of universal / existential / comparative
quantifiers — queries about *all*, *some*, *many*, *more*, *most* all
fell through to OOV.

Pack composition (24 entries — mixed POS, 18 DET / 3 NUM / 2 ADJ / 1 NOUN):

  quantitative.universal.*    (6 DET) all every each both none neither
  quantitative.existential.*  (6 DET) some any several few many much
  quantitative.comparative.*  (6 DET) more less fewer most least enough
  quantitative.numeric.*      (3 NUM) one two three
  quantitative.unit.*         (3 mix) single (ADJ) half (NOUN) whole (ADJ)

The composer is POS-agnostic; surface composition uses
``semantic_domains`` rather than POS, so DET/NUM/ADJ/NOUN entries all
surface identically.

Files:
  language_packs/data/en_core_quantitative_v1/
    lexicon.jsonl   — 24 entries, SHA-256 checksum-sealed
    manifest.json   — operational_base / D0
  chat/pack_resolver.py
    Appended to DEFAULT_RESOLVABLE_PACK_IDS after action.
  core/config.py
    Added to RuntimeConfig.input_packs default mount.
  tests/test_en_core_quantitative_v1_pack.py
    11 contract tests (load / POS-dist / namespace / no-collision /
    contiguous-ids / mount / resolver-order / routing / invariance).

Authoring:
  Three parallel subagents — universal+existential / comparative /
  numeric.  Strict exemplar + forbidden-lemma list against all 7
  prior packs.

Verification:
  Full lane: 2192 passed, 2 skipped, 0 failed.
  Cognition eval byte-identical on both splits.
2026-05-19 05:38:12 -07:00
Shay
cb1eba72ae feat(packs): en_core_action_v1 — action verbs (26 lemmas)
Workstream 1 fourth pack.  Closes the common-action verb gap.  Prior
packs covered reasoning (cognition), speech/perception (meta), and
adjectives (attitude); this pack covers what an agent *does*.

Pack composition (26 VERB entries):

  action.doing.perform     do perform execute carry conduct
  action.doing.make        make
  action.doing.achieve     achieve accomplish
  action.creating.originate create build form produce generate develop
  action.changing.transform change transform
  action.moving.translate  move
  action.moving.depart_arrive go come
  action.moving.transfer   send receive
  action.possessing.acquire get take
  action.possessing.transfer give
  action.possessing.retain keep
  action.possessing.deploy use

Files:
  language_packs/data/en_core_action_v1/
    lexicon.jsonl   — 26 entries, SHA-256 checksum-sealed
    manifest.json   — operational_base / D0
  chat/pack_resolver.py
    Appended to DEFAULT_RESOLVABLE_PACK_IDS after temporal.
  core/config.py
    Added to RuntimeConfig.input_packs default mount.
  tests/test_en_core_action_v1_pack.py
    11 contract tests covering load / POS / namespace / no-collision /
    contiguous-ids / mounted-by-default / resolver-order / routing /
    prior-pack invariance.
  tests/test_procedure_surface.py
    Swapped two test fixtures from "do stuff" to "fix bugs".  ``do``
    is now correctly pack-resident in en_core_action_v1 (semantically
    correct — "How do I do stuff?" should ground on ``do``), so the
    "no pack lemma exists" contract needed a fixture where both verb
    and noun are genuinely OOV.  ``fix bugs`` satisfies this across
    all 7 mounted packs.

Authoring:
  Three parallel subagents — doing / creating / moving+possessing.
  Strict exemplar + forbidden-lemma list against all 6 prior packs.

Verification:
  Cognition eval byte-identical on both splits (100/100/91.7/100 and
  100/100/83.3/100).
  All 70 pack tests pass (cognition + meta + attitude + temporal +
  action + quant tests run together).
  Live composer probes confirm every action lemma surfaces
  deterministically from en_core_action_v1.
2026-05-19 05:38:12 -07:00
Shay
1c7408f7d0 feat(packs): en_core_temporal_v1 — temporal pack (28 lemmas)
Workstream 1 third pack.  Closes the temporal-vocabulary gap — prior
to this pack zero time/sequence/aspect terms existed in any mounted
English pack, so queries about *when*, *before*, *after*, *now*,
*future*, *past* all fell through to OOV.

Pack composition (28 entries, mixed POS — 12 ADV / 9 NOUN / 5 ADP /
1 SCONJ / 1 ADJ):

  temporal.deictic.*    (10 ADV)  now today tomorrow yesterday soon
                                  later recently eventually currently
                                  formerly
  temporal.relative.*    (9 mix)  before after during while until since
                                  ago prior henceforth
  temporal.noun.*        (9 NOUN) moment period duration instant era
                                  future past present time

The pack composer is POS-agnostic — surface composition uses the
ratified ``semantic_domains`` list rather than the POS tag.  Mixed-POS
entries surface identically to noun/verb entries.

Files:
  language_packs/data/en_core_temporal_v1/
    lexicon.jsonl   — 28 entries, SHA-256 checksum-sealed
    manifest.json   — operational_base / D0 / checksum-verified
  chat/pack_resolver.py
    Appended to DEFAULT_RESOLVABLE_PACK_IDS after attitude.
  core/config.py
    Added to RuntimeConfig.input_packs default mount.
  tests/test_en_core_temporal_v1_pack.py
    11 contract tests: checksum, POS-distribution invariant, primary-
    domain namespace, no-collision regression gate against all 5 prior
    packs, contiguous entry_ids, mounted-by-default, resolver-order
    invariant, routing correctness, and prior-pack resolution unchanged.

Authoring:
  Three parallel subagents — deictic / relative / nouns.  Strict
  exemplar + forbidden-lemma list against all 5 prior packs.

Verification:
  Full lane: 2170 passed, 2 skipped, 0 failed (+11 new tests).
  Cognition eval byte-identical on both splits.
  Live composer probes confirm every temporal lemma surfaces
  deterministically from en_core_temporal_v1.
2026-05-19 05:38:12 -07:00
Shay
f074ba729e feat(packs): en_core_attitude_v1 — adjective pack (40 lemmas)
Workstream 1 second pack.  Closes the ADJ POS gap — prior to this pack
zero adjectives existed in any mounted English content pack, so the
runtime could not emit grounded surfaces for predicative queries like
"What is true?" or "What is important?".

Pack composition (40 ADJ entries):

  attitude.truth_value.*   (8)  true false valid invalid accurate
                                inaccurate factual sound
  attitude.evaluative.*    (6)  good bad right better worse best
  attitude.epistemic.*    (10)  certain uncertain possible impossible
                                likely unlikely probable clear obscure
                                evident
  attitude.modal.*         (4)  necessary sufficient required optional
  attitude.importance.*    (6)  important essential relevant central
                                primary useful
  attitude.scope.*         (6)  general specific broad narrow universal
                                particular

Files:
  language_packs/data/en_core_attitude_v1/
    lexicon.jsonl   — 40 entries, SHA-256 checksum-sealed
    manifest.json   — operational_base / D0 / checksum-verified
  chat/pack_resolver.py
    Appended to DEFAULT_RESOLVABLE_PACK_IDS after cognition + meta.
  core/config.py
    Added to RuntimeConfig.input_packs default mount.
  tests/test_en_core_attitude_v1_pack.py
    11 contract tests: checksum, POS=ADJ uniformity, primary-domain
    namespace, no-collision regression gate against all 4 prior packs,
    contiguous entry_ids, mounted-by-default, resolver-order invariant,
    routing correctness, and cognition+meta resolution unchanged.

Authoring:
  Three parallel subagents (1 per cluster) — truth/eval, epistemic/modal,
  importance/scope.  Strict exemplar + forbidden-lemma list against all
  prior packs.  Main pass assembled, validated, sealed.

Verification:
  Full lane: 2159 passed, 2 skipped, 0 failed (+11 new tests over the
  previous 2148 baseline).
  Cognition eval byte-identical on both splits:
    public  100 / 100 / 91.7 / 100
    holdout 100 / 100 / 83.3 / 100
  Live composer probes: every ADJ lemma emits a deterministic
  pack-grounded surface from en_core_attitude_v1.
2026-05-19 05:38:12 -07:00
Shay
a376a30bf8 feat(packs): en_core_meta_v1 — conversational substrate (73 lemmas)
Workstream 1 (pack content scale-up) first load-bearing step.

Adds a new ratified content pack covering the conversational vocabulary
en_core_cognition_v1 deliberately omits — speech acts, mental states,
perception, self-reference, and discourse-object nouns.  These are the
lemmas that show up in nearly every model response and that previously
fell through to the OOV invitation surface.

Pack composition (73 entries, 49 VERB + 24 NOUN):

  meta.speech_act.*     (20 verbs)  say tell speak reply claim state
                                    describe express name mention note
                                    observe declare assert deny confirm
                                    suggest propose articulate respond
  meta.mental_state.*   (18 verbs)  know believe think suppose assume
                                    expect hope want prefer doubt wonder
                                    guess recognize realize consider intend
                                    decide hold
  meta.perception.*     (11 verbs)  see hear feel sense perceive watch
                                    look listen find detect notice
  meta.self_reference.* (10 nouns)  self mind view perspective position
                                    role agent model system speaker
  meta.discourse.*      (14 nouns)  response reply statement fact idea
                                    point argument proposal suggestion
                                    case instance example kind type

Files:
  language_packs/data/en_core_meta_v1/
    lexicon.jsonl   — 73 entries, SHA-256 checksum-sealed
    manifest.json   — operational_base / D0 / checksum-verified
  chat/pack_resolver.py
    Appended en_core_meta_v1 to DEFAULT_RESOLVABLE_PACK_IDS after
    en_core_cognition_v1 so cognition lemma resolution stays first-
    match-wins on any future collision (preserves cognition-lane
    byte-identity invariant).
  core/config.py
    Added en_core_meta_v1 to RuntimeConfig.input_packs default mount.
  tests/test_en_core_meta_v1_pack.py
    11 contract tests: checksum-verified load, POS split, primary-
    domain namespace, no-collision-with-cognition-v1 regression gate,
    pack registration order, resolver routing, and cognition-lemma
    resolution unchanged.
  tests/test_procedure_surface.py
    Swapped two test fixtures from "claim" to "hypothesis".  ``claim``
    is now correctly pack-resident (meta.speech_act.claim) so the
    procedure composer's object-first selector picks it over the verb
    — the new behavior is semantically correct.  ``hypothesis`` is
    genuinely OOV across all mounted packs and preserves the verb-
    fallback contract these tests pin.

Authoring methodology:
  Four parallel subagents authored one cluster each from a strict
  exemplar + word list + forbidden-lemma list (every en_core_cognition_v1
  lemma listed explicitly to prevent collision).  Each subagent wrote
  only its cluster JSONL; the main pass assembled, validated, computed
  the SHA-256 over bytes-on-disk, and wrote the manifest.

Verification:
  Full lane: 2148 passed, 2 skipped, 0 failed (+11 new tests).
  Cognition eval byte-identical on both splits:
    public  100 / 100 / 91.7 / 100
    holdout 100 / 100 / 83.3 / 100
  Live runtime probes: fresh ChatRuntime() for "What is X?" with
  X ∈ {fact, doubt, statement, model, self} all emit a
  pack-grounded sentence from en_core_meta_v1.
  OOV path still honest for genuinely-unknown terms (e.g. hypothesis).

Scope note:
  This is one pack of ~70 lemmas, not "the model now articulates
  open-domain English."  The architecturally-honest articulation
  story still requires more pack and teaching-chain content; this
  pack moves the conversational-substrate boundary forward by ~70
  lemmas in one ratifiable, replay-stable step.
2026-05-19 05:38:12 -07:00
Shay
4670e391ec feat(phase5+bench): cross-pack supersede + articulation benchmark suite
Phase 5 (ADR-0067 follow-up):
  teaching/cross_pack_supersede.py — supersede_cross_pack_chain()
  CLI: core teaching supersede ... --cross-pack
    --subject-pack-id ... --object-pack-id ...
  Strict per-chain residency, anti-leakage, byte-identical rollback
  on any post-append re-load failure.  9 new tests.

Articulation benchmark suite (Phase 4 capability proof):
  benchmarks/articulation.py — 5 sub-benches
    [1] breadth        — every intent shape (9 + OOV + cross-pack)
    [2] determinism    — N reruns / unique-surface count
    [3] footprint      — psutil RSS profile across T turns
    [4] cross-topic    — thread context across mixed subjects
    [5] ollama-compare — opt-in side-by-side with local Ollama
  CLI: core bench --suite articulation
    --runs N (det rerun count)
    --turns N (footprint sample window)
    --ollama-model MODEL --ollama-reruns N
  Full operator preamble + JSON report path.
  10 new tests cover the bench shape (psutil import-skipped).

Documentation:
  benchmarks/README.md — full operator manual: catalogue of every
    bench suite, how to read good/neutral/bad results for each sub-
    bench, why CORE vs Ollama comparisons are valid on the
    determinism axis and not on linguistic quality, workflow guide.
  README.md — articulation bench listed in the live-demo grid and
    quick-start examples.

Reference run (llama3:8b, 100 turns, 5 reruns):
  determinism_all_identical=True
  per-turn ΔRSS ≈ 23 KiB
  CORE byte_identical_on_every_prompt=True
  Ollama unique_surfaces≥2 on every prompt

Verification:
  18 new tests pass
  Full lane: 2116 passed, 2 skipped, 0 failed in 2:38
2026-05-18 17:44:59 -07:00
Shay
fe4cc2cd1f feat(adr-0066): session-thread context + opt-in anaphora prefix (Phase 3.1 + 3.2)
ADR-0066 P3.1 + P3.2.  Conversation now reads as a thread: turns
carry structured summaries of their predecessors and (optionally)
prefix new pack/teaching surfaces with deterministic backreferences.

P3.1 — chat/thread_context.py.

  TurnSummary(turn_index, intent_tag_name, subject, grounding_source,
              chain_id, corpus_id) — frozen, structured-fields-only.
  ThreadContext — bounded FIFO (default MAX_THREAD_TURNS=8) with
    snapshot(), recent_for_subject(), recent_subjects(), clear().
  recent_for_subject() excludes ungrounded tiers (oov/partial/none)
    by default — those are not strong-enough anchors.
  ChatRuntime.thread_context is owned at construction.
  _push_thread_summary runs at end-of-turn on BOTH stub and walk
    paths.  Teaching-grounded turns carry chain_id + corpus_id so
    downstream composers (P3.2) can detect same-chain reference.
  Cold-start intent classification now runs unconditionally (was:
    gated on sink attachment) so thread context captures subject
    regardless of sink state.

P3.2 — chat/anaphora.py.

  thread_anaphora_prefix(ctx, subject, intent_name, source) returns
  a deterministic prefix when:
    - current turn is pack/teaching tier
    - a prior pack/teaching turn on the same subject exists
    - the prior intent differs from the current intent

  Format (structural-fields-only — no prose):
    "(Recalling turn N: chain <chain_id>.) "    # prior was teaching
    "(Recalling turn N: <subject> grounded pack.) "  # prior was pack

  Opt-in via RuntimeConfig.thread_anaphora=False.  Default off keeps
  every existing surface byte-identical.

Live verification (with thread_anaphora=True + seeded context):
  > What is light?  # following a "Why does light exist?" teaching turn
  [pack] (Recalling turn 0: chain cause_light_reveals_truth.)
  light — pack-grounded (en_core_cognition_v1): cognition.illumination;
  logos.core; perception.clarity. No session evidence yet.

32 new tests passed.  Curated lanes green.  Cognition eval
byte-identical to pre-ADR baseline.
2026-05-18 17:01:34 -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
a435411be5 feat(packs): en_core_relations_v2 — pronouns + role-fillers (Phase 2.4)
ADR-0065 P2.4.  Eight specialization lemmas, each a typed
specialization of an en_core_relations_v1 primitive:

  mother / father           is-a parent
  daughter / son            is-a child
  sister / brother          is-a sibling
  grandparent / grandchild  is-a ancestor / descendant (1-step)

Strict pack-internal taxonomy under kinship.*:

  mother      → kinship.parent.female
  father      → kinship.parent.male
  daughter    → kinship.child.female
  son         → kinship.child.male
  brother     → kinship.sibling.male
  sister      → kinship.sibling.female
  grandparent → kinship.ascendant.transitive_1step
  grandchild  → kinship.descendant.transitive_1step

Pack ratification:
  - SHA-256 checksum 7d0583f7e6a13ce72a5b0b191786cfc57af31583dc5111b24c3466e89ee70856
  - Orthogonal to en_core_relations_v1 + en_core_cognition_v1 (zero
    lemma collision in either direction)
  - Mounted by default in RuntimeConfig.input_packs + added to the
    cross-pack resolver's DEFAULT_RESOLVABLE_PACK_IDS

Companion corpus relations_chains_v2.jsonl seeds 7 v2-internal
reviewed chains so DEFINITION/CAUSE/VERIFICATION on every v2 lemma
grounds (not just DEFINITION via the pack path):

  cause_mother_precedes_daughter
  cause_father_precedes_son
  cause_grandparent_precedes_grandchild
  cause_daughter_follows_mother
  cause_son_follows_father
  verification_daughter_requires_mother
  verification_son_requires_father

Registered as a third TeachingCorpusSpec alongside cognition and
relations_v1.  Strict pack-internal: every chain's subject AND
object reside in en_core_relations_v2.  Cross-pack chain shapes
(e.g. v2 subject + v1 object) deferred per teaching_order.md §5.

Live verification:
  > What is mother?
    [pack] mother — pack-grounded (en_core_relations_v2):
    kinship.parent.female; kinship.parent; biology.maternal.
  > Why does mother exist?
    [teaching] mother — teaching-grounded (relations_chains_v2):
    mother precedes daughter (kinship.child.female).
  > Does daughter require mother?
    [teaching] daughter requires mother — verification-grounded.

10 pack-contract tests passed.  Curated lanes all green; cognition
eval byte-identical.
2026-05-18 16:42:02 -07:00
Shay
34295e55ce perf(test-infra): pytest-xdist + module-scoped demo fixtures
Full lane wall-time: 6:35 → 2:25 (2.7× speedup).  No behavioral
changes; same 1933 passed, 2 skipped.

Three wins, biggest first:

1. pytest-xdist as a project dependency.

   ``pyproject.toml`` gains ``pytest-xdist>=3.6``.  ``cmd_test``
   injects ``-n auto`` for ``--suite full`` when xdist is importable;
   curated suites stay single-process because worker-spawn overhead
   is net-negative on the smaller suites.  Operator can override
   via passing ``-n <N>`` or ``--dist`` explicitly.

   Verified: ``core test --suite full -q`` prints ``bringing up
   nodes...`` and parallelises across the runner's CPUs.

2. Module-scoped fixture for run_demo() in test_learning_loop_demo.py.

   The 7 demo tests each previously called ``run_demo(emit_json=True)``
   from scratch — and ``run_demo`` itself runs the cognition lane
   twice via the replay-equivalence gate.  ~15s/file → ~3s/file.

   Module scope (not session) is intentional: pytest-xdist
   distributes by test, so a session-scoped fixture would still be
   re-evaluated per worker that picks up a test from this file.
   Module scope keeps the cost paid once per worker per file, which
   is the actual lower bound.

3. Module-scoped fixture for the teaching-loop bench.

   ``test_teaching_loop_bench.py``'s 5 tests previously each ran
   ``run_teaching_loop_determinism(runs=2 or 3)`` — 12 pipeline
   invocations across the file.  One ``runs=3`` invocation shared
   across all 5 tests covers every assertion: ~25s → ~7s.

For local iteration, ``core test --suite cognition -q`` etc. remain
fast (no xdist overhead).  The full-lane speedup is most visible
under CI / pre-merge runs.
2026-05-18 16:12:27 -07:00
Shay
84e74eede8 feat(teaching): discovery gaps aggregator + auto-promotion queue (Phase 1.1+1.2)
Closes the corpus flywheel.  ADR-0055 Phase B emits DiscoveryCandidate
JSONL to the discovery sink, but until now there was no operator-facing
view: candidates accumulated to disk, no one grepped them, the system's
"I would have grounded this if I had a chain" signal went into a void.

P1.1 — Discovery aggregator (teaching/gaps.py).

  Pure reader over the discovery-sink monthly-rollover layout
  (<root>/<YYYY>/<YYYY-MM>.jsonl).  aggregate_gaps(root, since,
  sample_limit) groups emitted candidates by (subject, intent) cell
  and returns a deterministic ranked tuple of Gap records.

  - count: total emissions
  - boundary_clean_count: subset whose boundary_clean flag held
    (refusal/hedge-tainted emissions split out so operators can filter)
  - sample_candidate_ids: up to N retained ids per cell, sorted
  - months_seen: every month token where the cell appeared

  --since YYYY-MM filters by file naming convention (no timestamp
  dependency).  Malformed lines silently skipped.  Default root:
  teaching/discovery_log.

  CLI: core teaching gaps [--root PATH] [--since YYYY-MM] [--top N]
                          [--sample-limit N] [--json]

P1.2 — Auto-promotion queue (teaching/promotion.py).

  promote_gaps(gaps, threshold, include_tainted) lifts cells whose
  effective count meets the threshold into GapPromotion records.

  - Default mode: boundary_clean_count gates promotion.  Tainted-only
    cells (count > 0 but all emissions refusal/hedge-tainted) do not
    auto-promote — those may indicate the prompt hit a safety axis,
    not a curriculum gap.
  - include_tainted=True counts every emission (operator override).
  - Threshold must be >= 1 (zero threshold defeats the queue).
  - queue_id is stable + deterministic (gap:<intent>:<subject>@<N>).
  - No content synthesis — promotion never invents connective or
    object; only an operator can author a complete chain via the
    propose/replay/accept pipeline.

  CLI: core teaching queue [--threshold N] [--include-tainted]
                           [--root PATH] [--since YYYY-MM] [--json]

Operator workflow (closed loop):

  operator → core chat                            # asks question
           ← cold turn emits DiscoveryCandidate
  operator → core teaching gaps --top 10          # ranked gaps
  operator → core teaching queue --threshold 3    # auto-promoted
  operator → authors candidate JSONL
  operator → core teaching propose <path>         # replay gate runs
  operator → core teaching review <id> --accept   # corpus mutates

24 new tests (13 gaps + 11 promotion), all pure / no I/O dependencies,
fast (<1s combined).  Full lane: 1933 passed, 2 skipped.
2026-05-18 16:04:39 -07:00
Shay
7c80b791ec fix(tests): retire 13 stale failures from full lane — corpus saturation drift
The full lane carried 13 long-standing red tests whose premises were
invalidated by reviewed-corpus growth that landed in earlier commits.
None reflected runtime bugs — all four classes are corpus-state drift
where the test fixture became stale.  Curated lanes were green, full
lane stayed quietly red.  Closes that gap.

1. test_teaching_audit (2 tests).
   * test_audit_real_corpus_runs_clean asserted dropped == () and
     lines_on_disk == lines_loaded — premise written before any
     supersession existed.  Curriculum saturation v2 (commit a0edbb4)
     ratified the wisdom_grounds_judgment → wisdom_requires_knowledge
     supersession; the audit now correctly shows 1 dropped line.
     Rewritten as the line-conservation invariant:
       lines_loaded + len(dropped) == lines_on_disk
     plus a typed-reason check on every dropped entry.
   * test_default_superseded_by_is_null_in_loaded_entries asserted
     ALL loaded entries have superseded_by == None.  Wrong even by
     ADR-0055 design: the replacement entry IS loaded and carries
     the back-pointer to the retired chain.  Rewritten as the
     active-set invariant: any non-null superseded_by on a loaded
     entry must reference a dropped (retired) chain id, never a live
     one — no double-live state.

2. test_learning_loop_demo (7 tests).
   The demo's headline prompt was "Why does thought exist?", and the
   ADR-0057 demo trilogy (commit 82dac4b) chose (thought, cause) as
   the cold cell.  Cognition saturation v2 (commit a0edbb4) ratified
   cause_thought_reveals_meaning into the active corpus — so the
   cold turn now grounds, no discovery candidate is emitted, every
   demo scene breaks.  Rotated the cold subject to ``narrative``
   (pack-resident, no chain, same thematic shape, same affirming
   evidence pointer cause_creation_reveals_meaning).  Demo headline,
   evals/learning_loop/run_demo.py, core/cli.py preamble, and the
   test assertions all updated together so the demo reads cleanly:
       before: [none]     I don't know — insufficient grounding...
       after : [teaching] narrative — teaching-grounded ... narrative
                          reveals meaning ...

3. test_discovery_candidates (4 tests).
   Test fixture used (judgment, CAUSE) as the still-cold pair.
   Epistemology v1 (commit 2acf71f) ratified
   cause_judgment_requires_wisdom — (judgment, cause) is no longer
   cold.  Rotated to ``principle`` (pack-resident, no chain on either
   intent today).  Added a pytest.skip self-guard so when a future
   curriculum unit ratifies a (principle, *) chain the test rotates
   cleanly instead of going red.

Full lane: 1892 passed, 2 skipped, 0 failed (was 4 failed pre-fix,
13 failed pre-ADR-0063).  Cognition eval unchanged: public 100/100/
91.7/100, holdout 100/100/83.3/100.
2026-05-18 15:23:22 -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
Shay
d24e98906e docs(adr-0055-0057): preambles + README index for the demo trilogy
Three external-facing demos / benchmarks now match the existing
audit-tour / pack-measurements / long-context-comparison treatment:
preamble printed before the run, README index entries, claims table.

- core/cli.py — _ANTI_REGRESSION_PREAMBLE, _LEARNING_LOOP_PREAMBLE,
  _TEACHING_LOOP_BENCH_PREAMBLE.  Each lists reference ADRs, what to
  expect, trust boundary, test gate, and machine-readable invocation.
  Wired through _print_preamble in the demo dispatch + bench dispatch
  (suppressed under --json).
- README.md — new "Inter-Session Memory — Reviewed Learning" section
  between Teaching Order and Architecture: the three-gate trust
  property table, the three live-demo table, and the operator-surface
  command list.  Quick-start block lists `core demo anti-regression`,
  `core demo learning-loop`, and `core bench --suite teaching-loop
  --runs 100` alongside the existing demos.

No code paths changed — preambles are stdout-only when not under JSON.
Tests unchanged; 17/17 green (5 anti-regression + 7 learning-loop + 5 bench).
2026-05-18 11:08:55 -07:00
Shay
82dac4b16f feat(adr-0055-0057): teaching-loop determinism benchmark — replayable learning
`core bench --suite teaching-loop [--runs N]` runs the full reviewed-
corpus extension pipeline (propose → real replay-equivalence gate →
operator accept) N times against an identical input and asserts
byte-identical artifacts every run:

  - proposal_id          (SHA-256 of canonical-JSON payload)
  - replay_baseline      (cognition lane metrics on active corpus)
  - replay_candidate     (cognition lane metrics on transient corpus)
  - regressed_metrics    (sorted tuple)
  - chain_id_written

Also reports per-iteration latency (mean / p50 / p95) and total wall.

100-run result against today's main:
  unique(proposal_id)=1  unique(baseline)=1  unique(candidate)=1
  unique(chain_id)=1     active_corpus_byte_eq=True
  mean=1.849s  p50=1.838s  p95=1.851s

The full learning loop is replayable bit-identically across N
independent invocations.  Pairs naturally with ADR-0045's 100% exact-
NIAH recall numbers — same epistemic class of guarantee, applied to
the *learning loop* itself rather than only to retrieval.  No LLM
provider can publish equivalent numbers on a learning path.

- benchmarks/teaching_loop.py — `run_teaching_loop_determinism(runs)`
  returns a typed `TeachingLoopBenchReport` with uniqueness counts,
  determinism flag, byte-identical-active-corpus flag, and latency
  distribution (mean / p50 / p95 / total).  Pure-stdlib percentile —
  no numpy dep on this path.
- benchmarks/run_benchmarks.py — `bench_teaching_loop_determinism`
  shim + `_SUITES["teaching-loop"]` registration + runs= passthrough.
- core/cli.py — `--suite teaching-loop` choice added to bench parser.
- tests/test_teaching_loop_bench.py — 5 tests pin determinism at
  small N, proposal_id SHA-256 shape, canonical chain_id layout,
  latency stats well-formedness, JSON serialisation.

Trust boundary: every write is confined to a tempdir created inside
the bench loop; the active corpus is read once at start, once at end,
and any byte difference would fail the bench.
2026-05-18 11:03:48 -07:00
Shay
a71b321a9a feat(adr-0055-0057): learning-loop demo — cold turn to grounded surface, end-to-end
`core demo learning-loop` (+ `--json`) walks a single prompt through the
full ADR-0055..0057 inter-session-memory architecture:

  S1. Cold turn          → universal disclosure, grounding_source=none
  S2. Discovery emission → DiscoveryCandidate to attached sink
  S3. Operator proposal  → real replay-equivalence gate, no regression
  S4. Operator accept    → TRANSIENT corpus only; active untouched
  S5. Same prompt        → teaching-grounded surface with the new chain

Before / after on the deterministic prompt "Why does thought exist?":

  before: [none]     I don't know — insufficient grounding for that yet.
  after:  [teaching] thought — teaching-grounded (cognition_chains_v1):
          cognition.thought; logos.internal. thought reveals meaning
          (cognition.meaning). No session evidence yet.

The active corpus on disk is byte-identical pre/post.  The demo writes
only to a transient corpus, then swaps `_CORPUS_PATH` for the after
turn — the same pattern the replay-equivalence gate uses.

- evals/learning_loop/run_demo.py — `run_demo(emit_json=False)` returns
  a structured `DemoReport` with both surfaces and per-scene detail.
- core/cli.py — `core demo learning-loop` target wired.
- tests/test_learning_loop_demo.py — 7 tests pin: full loop closes,
  before is ungrounded, after contains new chain atoms (thought /
  reveal / meaning), discovery emits ≥1, replay gate reports no
  regression, S4 byte-identical active + 1 line on transient, same
  prompt drives both surfaces.

Lane state: learning-loop-demo 7 new — green.  Demo runs in ~15s
end-to-end (cognition lane runs twice via replay gate).

No LLM provider has a published equivalent of this loop: per-fact
provenance from operator accept to surface, replay-equivalence gate
proving non-regression, byte-identical active state regardless of
outcome, full audit trail back to the originating cold turn.
2026-05-18 10:57:41 -07:00
Shay
6f4b2b7b2c feat(adr-0057): anti-regression demo — three-gate defense against learning harm
`core demo anti-regression` (+ `--json`) is a self-contained walkthrough of
the three independent gates that every reviewed-corpus extension must pass.
Designed for showcasing CORE's epistemic discipline to reviewers / industry
observers — no LLM provider has a published equivalent.

Scenes:
- S1. Eligibility predicate refuses an undetermined-polarity candidate
  before any replay is invoked.  ProposalError raised; no log row.
- S2. Replay-equivalence gate auto-rejects a regressing candidate with
  the named regressed metrics in the operator note.  Uses the documented
  `run_replay=` kwarg of `propose_from_candidate` to inject a controlled
  regression of the same `ReplayEvidence` shape the real gate produces.
- S3. Real `teaching.replay.run_replay_equivalence` runs the cognition
  public lane.  A replay-equivalent candidate reaches 'pending' — operator
  `--accept` is still required to write.

Each scene asserts the active corpus is byte-identical pre/post.

- evals/anti_regression/run_demo.py — `run_demo(emit_json=False)` returns
  a structured `DemoReport`; verbose human output by default, JSON on flag.
- core/cli.py — `core demo anti-regression` target wired alongside
  audit-tour / pack-measurements / long-context-comparison.
- tests/test_anti_regression_demo.py — 5 tests pin each scene's
  load-bearing claim + the corpus-byte-identical invariant.

Lane state: anti-regression-demo 5 new — green.  Demo runs in ~10s end-to-end.
2026-05-18 10:52:23 -07:00
Shay
3cad6686cc feat(adr-0057): operator supersession history view — closes the supersede loop
`core teaching supersessions` (+ `--json`) pairs each retired chain with its
active replacement.  Derived view over `audit_corpus()`; pure, read-only.

- teaching/audit.py — `SupersessionRecord` + `supersession_history(report)`
  returns retired→replacement pairs ordered by retired-line (disk order,
  oldest first).  Orphan supersessions (retired with no live entry carrying
  the matching `superseded_by` — e.g. chained retirements where the middle
  link itself was retired) surface as `replacement=None` so silent corpus
  drift is inspectable.
- core/cli.py — `core teaching supersessions [--json]`.  Exit 1 if any
  orphan is detected (catches silent drift in CI); 0 otherwise.
- tests/test_supersession_history.py — 7 tests pin empty-history,
  single-pair shape, chained-supersession surfaces both pairs, line-no
  ordering, orphan detection, JSON round-trip, no corpus mutation.

Lane state: smoke 67 / cognition 121 / supersession-history 7 new / supersede 13 /
audit 23 — green.  `core eval cognition`: unchanged (intent 100% / surface 100% /
term 91.7% / versor 100%).  Real corpus today reports `(no supersessions)`.
2026-05-18 10:40:38 -07:00
Shay
8d2c84a041 feat(adr-0057): operator supersede CLI — retire active chain by appended replacement
`core teaching supersede <old_chain_id> --subject ... --intent ... --connective ...
--object ... --review-date YYYY-MM-DD` is the second corpus mutation surface
(alongside accept_proposal). No replay gate — it's a deliberate operator action
that replaces a hand-authored or previously discovery-promoted chain.

- teaching/supersede.py — `supersede_chain()` orchestrator with pre-checks
  (review_date format, intent whitelist, pack-consistency via re-audit,
  no double-supersede, no self-supersede, no new-chain-id collision) and
  byte-identical rollback on post-audit failure.
- teaching/proposals.py — extended `append_chain_to_corpus` with optional
  `superseded_by` kwarg; remains the only function in the codebase that
  writes to the active teaching corpus.
- core/cli.py — `core teaching supersede` subcommand wired to the live
  `_CORPUS_PATH`; EPILOG updated with example.
- tests/test_supersede.py — 13 tests pin every gate, byte-identical
  rollback on rejection, append-only at disk level, audit-and-runtime
  parity after supersession, hand_authored provenance with
  `supersede(<old_chain_id>)` tag.

Lane state: smoke 67 / cognition 121 / teaching 17 / supersede 13 / audit 23 /
proposals 16 / contemplation 16 / contemplation-wiring 6 / discovery 24 — green.
`core eval cognition`: intent 100% / surface 100% / term 91.7% / versor 100% — unchanged.
2026-05-18 10:35:49 -07:00
Shay
e03ab4b609 feat(adr-0057): Phase C2 — TeachingChainProposal + replay gate + review CLI
The only path by which CORE extends its own active teaching corpus.
Closes ADR-0055 Phase C alongside ADR-0056's cognitive surface.

Three load-bearing calls (recorded in ADR-0057):
  1. Replay-equivalence is a precondition, not a permission;
     operator --accept remains required.
  2. Eligibility = polarity in {affirms, falsifies} AND at least
     one source='corpus' evidence pointer AND boundary_clean AND
     claim_domain != evaluative (unless --allow-evaluative) AND
     proposed_chain complete.
  3. Append-only proposal log; corpus history append-only too.

Changes
- teaching/proposals.py — TeachingChainProposal, ReplayEvidence,
  ProposalLog (event-sourced replay → current_state), eligibility
  predicate, propose_from_candidate, accept/reject/withdraw,
  append_chain_to_corpus (the sole corpus-write surface).  Uses
  TYPE_CHECKING guards to break the circular import with
  chat.pack_grounding.
- teaching/replay.py — run_replay_equivalence; swaps _corpus_index
  path to a tmp file, runs cognition lane on the active corpus
  AND a transient copy with the proposed chain appended, returns
  regressed-metrics list; trust-boundary assertion that the active
  corpus bytes are byte-identical pre/post.
- teaching/discovery.py — moved chat.pack_grounding /
  chat.teaching_grounding imports inside extract_discovery_candidates
  to break the cycle (was masked when chat.runtime was the entry
  point; surfaced by CLI entry).
- core/cli.py — three new subcommands:
    core teaching propose <candidate-jsonl-path> [--allow-evaluative]
    core teaching proposals [--state pending|accepted|rejected|withdrawn] [--json]
    core teaching review <proposal_id> --accept --review-date YYYY-MM-DD
    core teaching review <proposal_id> --reject [--note ...]
    core teaching review <proposal_id> --withdraw [--note ...]
- tests/test_teaching_proposals.py — 16 tests covering: every
  eligibility gate, proposal_id idempotency, append-only log,
  replay-equivalent stays pending, regression auto-rejects with
  named regressed metrics, --accept appends one line with typed
  Provenance, --accept refused on non-equivalent, state-machine
  blocks double-accept, real replay gate runs cognition lane
  twice and asserts byte-clean active corpus pre/post.

Invariants preserved
- versor_condition(F) < 1e-6 — C2 touches no algebra path.
- Active corpus bytes byte-identical regardless of replay outcome.
- No clock-time reads, no LLM, no async.
- Proposal-only — accept_proposal is the sole corpus-write path.

Lanes: smoke 67 / cognition 121 / runtime 19 / teaching 17 /
new proposals 16.  Cognition eval unchanged.

Open follow-ups (not in scope):
- supersession via operator review action
- cross-pack falsification arbitration (ADR-0056 Call 2 deferred)
- pack-data migration of frame-dependent connectives

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-18 10:23:14 -07:00
Shay
7aa77806f9 feat(adr-0055): Phase A — teaching corpus audit, supersession, typed provenance
Lands the three load-bearing pieces of ADR-0055 Phase A so later
phases (DiscoveryCandidate, TeachingChainProposal) have a safe
substrate to write into.

- teaching/audit.py: pure, deterministic re-parse of the reviewed
  corpus with same gates as the runtime loader but keeps drop
  reasons (invalid_json, missing_required_field:*, unsupported_intent,
  pack_missing_subject, pack_missing_object, superseded_by:*).
- teaching/provenance.py: typed Provenance(adr_id, source,
  review_date, raw); legacy "reviewed" maps to "hand_authored" so
  current corpus reports the canonical enum without a file rewrite.
- chat/teaching_grounding._corpus_index honors superseded_by —
  active view drops superseded entries while disk preserves history.
- core teaching audit CLI subcommand (--json optional); exits 1 on
  any drop so CI catches silent corpus shrinkage from pack swaps.

Observable behaviour unchanged: corpus is 10/10 loaded, all five
core lanes green (smoke 67, cognition 121, runtime 19, teaching 17,
packs 6), cognition eval metrics identical on dev / public /
holdout splits. versor_condition < 1e-6 invariant untouched.

Tests: tests/test_teaching_audit.py — 23 tests covering provenance
parser, real-corpus determinism, every drop-reason path,
supersession semantics, runtime/audit parity, read-only contract.
2026-05-18 08:15:23 -07:00
Shay
6b25069da8 feat(adr-0054): vault recall indexing/batching + holdout split wired
Two doctrine-aligned CLAUDE.md items closed together.

Part 1 — vault indexing + batching (item #4):
- VaultStore lazy _matrix_cache (invalidated on store / reproject /
  eviction); vault_recall(prebuilt_matrix=...) skips deque→ndarray
  rebuild on hot path
- New vault_recall_batch + VaultStore.recall_batch — B queries
  scored in one component-serial sweep, bit-identical to per-query
  vault_recall (3 seeds × 7 queries × N=137 parity test)
- No approximation, no hot-path repair, scoring arithmetic
  unchanged

Part 2 — holdout split wired:
- LaneInfo.holdout_cases_path resolves plaintext holdouts in fixed
  priority; sealed (.age) holdouts stay in holdout_runner
- framework.run_lane(split="holdout") + argparse --split choices
- First official cognition holdout numbers: 19 cases, intent 100%,
  surface 94.7%, term_capture 70.8%, versor 100% — single miss is
  predicted correction_truth_040 (ADR-0053 scope-limit)

Tests: 21 new vault tests + 10 new framework tests. Lanes: smoke
67, cognition 121, runtime 19, teaching 17, packs 6, algebra 132 —
all green. versor_condition < 1e-6 invariant preserved.
2026-05-18 07:58:57 -07:00
Shay
0d854ff387 merge: ADR-0052 teaching-grounded CAUSE/VERIFICATION surface 2026-05-18 07:28:12 -07:00
Shay
c6ade6c76f feat(adr-0052): teaching-grounded CAUSE/VERIFICATION surface 2026-05-18 07:13:43 -07:00
Shay
140b6fea37 feat(adr-0051): trust-boundary hardening pass 2026-05-18 07:09:55 -07:00
Shay
c28e107dc7 feat(adr-0048): pack-grounded surface for cold-start DEFINITION/RECALL
Closes the surface-grounding gap isolated by ADR-0047's
characterisation.  Adds the ratified cognition pack as a second
grounding source alongside the session vault.

== chat/pack_grounding.py (new) ==

Loads en_core_cognition_v1's lexicon once (cached; immutable pack)
and exposes:

  pack_grounded_surface(lemma) -> str | None

Returns a deterministic, fully pack-sourced surface:

  "{lemma} — pack-grounded ({pack_id}): {d1}; {d2}; {d3}. No session evidence yet."

Every visible atom is the lemma or a verbatim semantic_domains
string from the pack.  No rewording, no synthesis, no LLM.

== chat/runtime.py ==

_stub_response gains optional pack_grounded_surface= parameter.
_maybe_pack_grounded_surface routes to the pack only when all four
hold: gate_source=="empty_vault", output_language=="en",
intent.tag in {DEFINITION, RECALL}, and intent.subject is a pack
lemma.  Safety/ethics refusal still takes priority above this branch.

ChatResponse and TurnEvent gain grounding_source ∈ {vault,pack,none}.
Main walk path tags responses "vault".

== core/cognition/pipeline.py ==

gate_fired detection moved from string equality on the universal
disclosure to provenance:

  gate_fired = response.vault_hits == 0 and response.grounding_source != "vault"

Same intent (suppress realizer template on gate-fired turns),
broader stub-path surface set.

== Characterisation (core eval cognition, 13-case public split) ==

  Metric                  Pre        Post     Δ
  intent_accuracy        100.0%     100.0%    0
  surface_groundedness    15.4%      46.2%   +30.8 pp
  term_capture_rate        0.0%      33.3%   +33.3 pp
  versor_closure_rate    100.0%     100.0%    0

Lift is non-uniform by design: only single-lemma DEFINITION/RECALL
on pack-known English subjects engage.  CAUSE/COMPARISON/VERIFICATION
and multi-word OOV subjects still return the universal disclosure —
fabricating those would violate the no-LLM-fallback doctrine.

== Tests ==

  tests/test_pack_grounding.py                          18 passed
  tests/test_semantic_realizer_integration.py (updated) 1 stub-path test
    pinned to the broader contract: surface is either universal
    disclosure or pack-grounded; never the realizer template.

== Lanes ==

  smoke 67  cognition 121  runtime 19  algebra 132
  teaching 17  packs 6

versor_condition(F) < 1e-6 invariant unaffected (no algebra changes).
2026-05-18 06:36:10 -07:00
Shay
f47a85a3e7 feat(adr-0047): wire forward graph constraint into the chat hot path
Closes ADR-0046's deferred follow-up: convert the PropositionGraph
into an AdmissibilityRegion BEFORE generate() runs on the live
chat path.

== generate/intent_bridge.py ==

New public helper:

    build_graph_from_input(text, plan) -> PropositionGraph

Same internal call as _build_graph_from_intent, without the
post-generation ground_graph step — suitable for forward use.

== chat/runtime.py ==

When the new flag is on and output language is English, build the
graph and the region before generate() and pass it via region=.
Empty / fully OOV graphs return AdmissibilityRegion(allowed_indices=None),
which generate() treats as unconstrained — the change is a true
no-op when the graph carries no in-vocab anchors.

== core/config.py ==

RuntimeConfig.forward_graph_constraint: bool = False

Default False preserves all pre-ADR-0046 behaviour and the ADR-0024
honest-refusal contract.  A first attempt wired the constraint
unconditionally; 15 tests failed with InnerLoopExhaustion because the
intent-derived graph's CGA neighbourhood doesn't intersect the walk's
candidate pool with top_k=8 on the current packs.  The honest answer
is not to widen top_k until the failure goes away nor to silently
relax — both erase the architectural information that the geometry
of the graph and the geometry of the walk are not yet co-located.
Opt-in preserves ADR-0024 and follows the ADR-0022→0026 transition-
window pattern.

== Characterisation (core eval cognition, 13-case public split) ==

A/B with the flag toggled:

  Metric                  OFF      ON      Δ
  intent_accuracy        100.0%   100.0%   0
  surface_groundedness    15.4%    15.4%   0
  term_capture_rate        0.0%     0.0%   0
  versor_closure_rate    100.0%   100.0%   0
  InnerLoopExhaustion       0        0     0
  non-trivial constraint   n/a    6 / 13   —

Findings:
- Wiring is correct and safe (no exhaustions, closure unchanged).
- Single-token in-vocab subjects engage the constraint
  (light/knowledge/meaning/memory/correction).
- Multi-word OOV subject phrases produced by the intent classifier
  fall through to unconstrained — this is the existing intent-
  classifier contract surfacing into geometry, not a constraint bug.
- Restricting which tokens the walk may visit did not change
  surface_groundedness or term_capture_rate on this lane.  The
  surface-grounding gap therefore lives downstream of propagation
  — in the realizer / surface-assembly / dialogue-role path — and is
  the next load-bearing pull.  This isolates the next ADR's scope.

== tests/test_forward_graph_constraint_wiring.py (5 tests) ==

  - DEFAULT_CONFIG.forward_graph_constraint is False
  - Default runtime answers without InnerLoopExhaustion
  - Opt-in runtime answers on a short benign input
  - Graph builder + build_graph_constraint produce a labelled
    AdmissibilityRegion ("graph:unconstrained" or "graph:<root_id>")
  - Flag is observable on the frozen RuntimeConfig

== docs/decisions/ ==

  - ADR-0047 ratifies the wire-up, opt-in rationale, and A/B numbers.
  - README index updated; the Pillar 1→2→3 section now reflects both
    the primitive (ADR-0046) and the live wiring (ADR-0047), and
    names the next pull (realizer / surface assembly) explicitly.

Verification (this branch):

  tests/test_forward_graph_constraint_wiring.py    5 passed
  tests/test_graph_constraint.py                   8 passed
  core test --suite smoke                         67 passed
  core test --suite cognition                    121 passed
  core test --suite runtime                       19 passed
  core test --suite algebra                      132 passed
  core test --suite teaching                      17 passed
  core test --suite packs                          6 passed
  core eval cognition                            metrics unchanged from main

versor_condition(F) < 1e-6 invariant unaffected.
2026-05-18 06:18:10 -07:00
Shay
283680f110 feat(adr-0044, adr-0045): domain ethics pack + long-context comparison
ADR-0044 — Medical / clinical ethics pack (worked-example domain pack).
Ships packs/ethics/medical_clinical_ethics_v1.json with six commitments
partitioned across all three remediation tiers:
  - refuse: no_dosing_recommendation, no_emergency_triage_authority
  - hedge:  defer_diagnosis_to_clinician, surface_evidence_grade
  - audit:  disclose_no_clinician_relationship, respect_patient_autonomy

Ratified end-to-end through scripts/ratify_ethics_pack.py (PACK_IDS
extended).  Production-mode load via load_ethics_pack succeeds.
ChatRuntime composition includes universal safety floor + every medical
commitment.  tests/test_medical_clinical_ethics_pack.py (8 tests) gates
file existence, sealed report, disjoint refusal/hedge lists, and
pack-swap visibility (default pack does NOT carry medical commitments).

ADR-0045 — Long-context recall: CORE vs transformer baselines.
Adds evals/long_context_cost/comparison_runner.py with a deterministic
needle-in-a-haystack measurement at N ∈ {100, 1_000, 10_000, 100_000}.
CORE recall = 100% at every tested N by exact cga_inner scan.

Paired with frozen citations of published transformer NIAH numbers in
evals/long_context_cost/baselines/transformer_long_context.json:
Claude 2.1 (200k, 50%), GPT-4 Turbo 128k (~71%), Gemini 1.5 Pro (99.7%),
NVIDIA RULER (varies).  Each citation carries source + url.

The two components measure different inputs (synthetic versors vs NL
needles) and are not directly comparable benchmark-for-benchmark.  The
comparison is at the architectural level — exact-scan recall vs
attention-based probabilistic recall.  Scope and limits documented in
the ADR.  tests/test_long_context_comparison.py (5 tests) gates schema,
CORE recall == 100%, and baseline citation presence.

CLI integration: two new demo targets with study-grade preambles.
  - core demo pack-measurements          (ADR-0043 — wired)
  - core demo long-context-comparison    (ADR-0045)
README + docs/PROGRESS.md cheatsheets updated.  docs/decisions/README.md
index extended with ADR-0044 + ADR-0045; pack-layer chain title now
"ADR-0027 through ADR-0045".

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
2026-05-17 22:31:47 -07:00
Shay
294cfc3576 feat(adr-0042): audit-tour demo — pack-layer story in four scenes
Ships `core demo audit-tour` as the first investor-facing
walkthrough of the ADR-0027→0041 pack-layer architecture.  Four
scenes, each making one falsifiable claim no transformer-LLM
wrapper can reproduce:

  S1. Identity is geometric, not prompt-veneer.
      Three identity packs load three structurally distinct
      manifolds (ADR-0027).  Distinct alignment thresholds +
      distinct hedge phrases from JSON pack files, not prompts.

  S2. Safety is the universal floor.
      Runtime-checkable safety violation produces a deterministic
      typed refusal string (ADR-0036).  walk_surface preserved
      for audit.  Byte-identical across runs.

  S3. Ethics commitments choose their remediation.
      Per-commitment opt-in (ADR-0037 / ADR-0038): pure-helper
      evidence (should_inject_hedge + inject_hedge worked
      example) against a synthetic violation.  Default pack
      returns False; deployment pack (with acknowledge_uncertainty
      in hedge_commitments) returns True.  Pack JSON drives the
      policy tier.

  S4. Deterministic replay across runtime instances.
      Two fresh ChatRuntime instances, same input, same packs.
      Byte-identical JSONL audit lines (ADR-0040).

Load-bearing evidence over surface inspection: the draft compared
response.surface across packs.  Cold-start hits stub path; pack
differences don't manifest at the surface by design.  Shipped
version pulls evidence from structural surfaces (manifold fields,
opt-in lists, pure helpers) — what actually distinguishes the
packs.  No fake claims.

Scene 3 uses synthetic verdict (not chat()) because ADR-0038
specifies stub path skips hedge by design.  Main-path end-to-end
is asserted in tests/test_hedge_injection.py and referenced in
the tour's evidence comment.

Test gate: tests/test_audit_tour.py asserts
result["all_claims_supported"] is True.  Any scene flipping to
False fails the test and catches the regression.

CLI integration:
  core demo audit-tour          # narration to stdout
  core demo audit-tour --json   # structured report, no narration

Files:
- evals/audit_tour/__init__.py + run_tour.py (new) — 4-scene tour
- core/cli.py — audit-tour target on demo subcommand;
  _AUDIT_TOUR_PREAMBLE; --json suppresses narration
- tests/test_audit_tour.py (new) — 8 tests gating all four claims
- docs/decisions/ADR-0042-audit-tour-demo.md (new) — decision record
- docs/decisions/README.md — ADR index now lists ADR-0027..0042
  + Pack-Layer chain section describing the three-tier composition,
  remediation tiers, and verification surface
- docs/PROGRESS.md — adds core demo audit-tour to verify cheatsheet
- README.md — adds core demo audit-tour to commands cheatsheet

Verification:
- Combined pack-layer + telemetry + tour suite: 220 green
  (was 212 after ADR-0041; +8)
- CLI suites unchanged: smoke 67, runtime 19, cognition 121
- core eval cognition: intent 100%, versor_closure 100% (baseline)
- Manual: core demo audit-tour and --json both correct;
  all_claims_supported = true
2026-05-17 22:06:45 -07:00
Shay
417f71917c feat(adr-0041): core chat --show-verdicts + FanOutSink
Two thin layers closing the audit story end-to-end:

- core chat --show-verdicts prints format_verdict_summary(verdicts)
  to stderr after each turn.  Stdout stays clean for piped
  consumers.  Format is dense and terse; designed to skim, not
  machine-parseable (the JSONL sink owns that contract).

- FanOutSink forwards every emitted line to N sinks in declaration
  order.  Fail-fast on first error — consistent with ADR-0040's
  single-sink contract (audit failures surface).  Composes with
  any combination of JsonlFileSink / JsonlBufferSink / future
  sinks.

Two formatters, one bundle: format_turn_event_jsonl (machine,
ADR-0040) and format_verdict_summary (operator, ADR-0041) both
consume the same TurnVerdicts.  No risk of drift.

Summary format:
  [identity=0.83 safety=ok ethics=VIOLATED:foo refusal=- hedge=YES]

Audit story now reads end-to-end:
  - TurnVerdicts bundle (ADR-0039)
  - Machine JSONL sink (ADR-0040)
  - Fan-out + operator CLI (ADR-0041)

Files:
- chat/telemetry.py — FanOutSink dataclass, format_verdict_summary,
  _format_verdict_short helper
- core/cli.py — --show-verdicts on chat subparser; cmd_chat prints
  summary to stderr when set
- tests/test_telemetry_fanout_and_summary.py (new) — 13 tests
- docs/decisions/ADR-0041-cli-verdicts-and-fanout.md (new)

Verification:
- Combined pack-layer + telemetry suite: 212 green (was 199; +13)
- CLI suites unchanged: smoke 67, runtime 19, cognition 121
- core eval cognition: intent 100%, versor_closure 100% (baseline)
- Manual smoke: echo "light is" | core chat --show-verdicts prints
  expected bracketed audit line to stderr alongside response.
2026-05-17 21:47:47 -07:00
Shay
f3cc408f82 feat(adr-0039): audit completeness — TurnVerdicts bundle, stub TurnEvent, hedge_injected
Closes three audit gaps left by the ADR-0035→ADR-0038 pack-layer
surface:

1. TurnVerdicts bundle (chat/verdicts.py) — frozen dataclass
   aggregating identity_score + safety_verdict + ethics_verdict +
   refusal_emitted + hedge_injected.  Attached to both
   ChatResponse.verdicts and TurnEvent.verdicts.  Fields typed as
   object for the same module-coupling reason as
   TurnEvent.safety_verdict.

2. Stub-path TurnEvent emission — _stub_response accepts optional
   tokens kwarg and appends a TurnEvent to turn_log when invoked
   from a real turn.  Audit consumers can now iterate turn_log
   end-to-end without missing stub paths.  Defensive call sites
   (correct() fallback) bypass the append by omitting tokens.

3. refusal_emitted / hedge_injected flags — runtime tracks whether
   it actually mutated the surface this turn.  hedge_injected uses
   idempotent-on-prefix semantics (True iff the runtime ADDED a
   hedge, not iff a hedge happens to be present).

Test-pattern note: previous "gate on rt.turn_log to detect main vs
stub" pattern is now broken; updated to gate on walk_surface ==
_UNKNOWN_DOMAIN_SURFACE.  One existing hedge-injection test gate
updated accordingly.

Back-compat: ADR-0035→0038 per-field accessors
(response.safety_verdict, etc.) still work.  New consumers should
read response.verdicts.

Files:
- chat/verdicts.py (new) — TurnVerdicts dataclass
- chat/runtime.py — _stub_response tokens kwarg + stub TurnEvent
  append + hedge_injected tracking + bundle construction
- core/physics/identity.py — TurnEvent.verdicts: object = None
- tests/test_turn_verdicts_bundle.py (new) — 16 tests
- tests/test_hedge_injection.py — gate fix for stub detection
- docs/decisions/ADR-0039-audit-completeness.md (new)

Verification:
- Combined pack-layer suite: 170 green (was 154 after ADR-0038)
- CLI suites unchanged: smoke 67, runtime 19, cognition 121
- core eval cognition: intent 100%, versor_closure 100% (baseline)
2026-05-17 21:32:46 -07:00
Shay
514ace0cbf feat(adr-0035): turn-loop auto-invocation — surfacing only
Wires SafetyCheck and EthicsCheck into ChatRuntime at end-of-turn on
both the main articulation path and _stub_response.  Verdicts attach
to ChatResponse.safety_verdict / .ethics_verdict and TurnEvent.
Observational at v1: no refusal, no re-articulation, no behavioral
change.  Refusal policy is the next ADR with real verdict data in hand.

Runtime-checkable predicates today:
  - preserve_versor_closure         (via _FieldStateWithVersor adapter)
  - no_identity_override            (manifold hash before vs after; equal by construction)
  - no_silent_correction            (runtime._last_refusal_was_typed bookkeeping)
  - acknowledge_uncertainty         (IdentityScore.alignment + hedge detection)
  - disclose_limitations            (walk_surface == _UNKNOWN_DOMAIN_SURFACE)

Predicates with no runtime evidence (no_manipulation, no_fabricated_source,
defer_high_stakes_to_human_review, respect_user_autonomy, no_hot_path_repair)
honestly report runtime_checkable=False per the ADR-0032/0034 discipline.
They become checkable as classifiers and pipelines land — surface contract
doesn't change.

Test coverage: 14 new tests; combined pack-layer surface suite (loaders +
checks + turn-loop) now 122 green.  CLI suites unaffected: smoke 67,
cognition 121, teaching 17, runtime 19.  Cognition eval baseline preserved.
2026-05-17 20:57:33 -07:00
Shay
dab7b9c061 feat(adr-0033): ethics packs — third pack-layer sibling to identity + safety
Completes the three-layer pack architecture:
  identity (who CORE is)  + safety (universal red lines)
                          + ethics (deployment-specific propositional commitments)

  manifold.boundary_ids = identity.boundary_ids
                        ∪ safety.boundary_ids
                        ∪ ethics.commitment_ids

Ethics packs are swappable like identity (fall back to default on load
failure) but propositional like safety (commitment ids union into the
manifold).  EthicsPackError inherits from ValueError; only when both
the requested and default packs fail does startup refuse.

Ships default_general_ethics_v1 with five commitments:
  - acknowledge_uncertainty
  - defer_high_stakes_to_human_review
  - disclose_limitations
  - no_manipulation
  - respect_user_autonomy

Ratified through identity_anchor template at SHA 81fc9b61c828….

Test coverage: 20 new tests; combined identity/safety/ethics surface
suite is 81 tests, all green.  Cognition (121), teaching (17), runtime
(19), smoke (67), and cognition eval all unaffected.
2026-05-17 20:41:04 -07:00
Shay
07ad3af845 feat(surface): ADR-0031 — score-decomposition surface (per-axis hedges)
Closes the 'identity hedges are generic' gap.  When IdentityCheck reports
that a specific axis is deviating AND the pack supplies an axis_hedges
entry for that axis, the assembler uses that axis's phrase instead of
ADR-0028's generic preferred_hedge_*.  The hedge text now names what is
actually at issue.

Selection: lex-smallest axis_id in (ctx.deviation_axes ∩ axis_hedges).
Deterministic; loader emits axis_hedges in lex order on axis_id.

Example surface at alignment=0.30 (strong band) under default pack:
  No deviation             → 'It seems that truth reveals reality.'
  truthfulness deviates    → 'Evidence is thin that truth reveals reality.'
  coherence deviates       → 'This does not yet cohere: truth reveals reality.'
  reverence deviates       → 'Reports suggest truth reveals reality.'

Same trajectory + truthfulness deviation, three different packs:
  default_general_v1   → 'Evidence is thin that truth reveals reality.'
  precision_first_v1   → 'The evidence does not support that truth reveals reality.'
  generosity_first_v1  → 'Truth reveals reality.'  (above generosity's strong=0.20)

Schema (additive, optional):
  surface_preferences.axis_hedges = {
    <axis_id>: { 'strong': str, 'soft': str, 'qualifier': str },
    ...
  }

Bounds: each phrase length 1–64; axis_id non-empty.  Absent block →
ADR-0028 byte-for-byte fallback.  Loader emits pairs in lex order on
axis_id for hashability + deterministic tie-break.

Files:
  core/physics/identity.py
    + class AxisHedge (frozen: strong, soft, qualifier)
    SurfacePreferences gains axis_hedges: Tuple = ()
  packs/identity/loader.py
    + _build_axis_hedges(): parse + bounds-check + emit lex-ordered tuple
  generate/surface.py
    SurfaceContext gains deviation_axes: frozenset[str] + axis_hedges tuple
    + _axis_specific_phrase(ctx): lex-smallest match or None
    _apply_hedge consults axis-specific phrase before ADR-0028 fallback
    Depth languages (he, grc) unchanged — ADR-0030 canonical phrases
  chat/runtime.py
    _build_surface_context lifts identity_score.deviation_axes and
    prefs.axis_hedges into SurfaceContext
  packs/identity/*.json
    Three v1 packs gain axis_hedges blocks (truthfulness, coherence,
    reverence — each pack uses voice consistent with its character)
  scripts/ratify_identity_packs.py (no change — idempotent)
  packs/identity/*.mastery_report.json
    Auto-refreshed.  New SHAs:
      default_general_v1   → 2ab7d469013509ba5030313ca9a609a443d0716e3ddcc5596f59858ce054f5d3
      precision_first_v1   → 78aa1e6a68a35c2c8576b6196a52d421b94f6d11e006128986902a4fd08679af
      generosity_first_v1  → 511f1ce20edd4266239da61443bfc93473a5433f20bfee6692a25a03073dc933

Tests: tests/test_identity_score_decomposition.py — 17 new tests:
  per-axis phrase selection, band gating still applies, pack swap with
  same deviation produces three different phrases, lex tie-break is
  deterministic, depth-language fallback to ADR-0030, backward compat
  with empty deviation_axes, and the contract that all three v1 packs
  ship axis_hedges for all three default-pack axes.

Suite status (all green):
  cognition 121, teaching 17, runtime 19, formation 182, smoke 67
  identity+safety+English+depth divergence 71
  score decomposition 17

Scope limits (documented in ADR-0031):
  - English-only at v1 (depth languages use canonical ADR-0030 phrases)
  - Lex tie-break is operational not semantic — pack authors can re-key
    if they need a different priority
  - No dominance-driven phrasing (Interpretation A); preserved as
    forward-compatible follow-up

Docs: ADR-0031 (Accepted) recorded; docs/identity_packs.md gains
§Axis-specific hedge phrases section and updated v1-pack SHAs; memory
'identity-packs.md' refreshed.
2026-05-17 20:16:22 -07:00
Shay
7c839d2e12 feat(cli): core chat --list-identity-packs + companion-file filter
Adds the discovery flag callers have been asking for since ADR-0027.
Short-circuits before the REPL launches; supports both a human-readable
table and `--json` machine output.  Drives the loader's existing
`available_packs()` helper.

Bug fix on the way: `available_packs()` was globbing every `*.json`
in the search path, so the Phase-5 companion `<pack_id>.mastery_report.json`
files were leaking into the list as fake packs with empty fields.  The
helper now skips any file ending in `.mastery_report.json` and rejects
JSON that lacks the required `schema_version` / `value_axes` fields.

CLI output:

  pack_id              version  ratified  description
  -------------------  -------  --------  -----------
  default_general_v1   1.0.0    yes       Balanced general identity...
  generosity_first_v1  1.0.0    yes       Generosity-first specialization...
  precision_first_v1   1.0.0    yes       Precision-first specialization...

Tests: +3 (CLI table, CLI JSON, companion-file filter regression).
test_identity_packs.py: 23 -> 26.  cognition / smoke green.

Docs: docs/identity_packs.md CLI usage block updated; memory
'identity-packs.md' closes that follow-up.
2026-05-17 19:47:13 -07:00
Shay
1574a4b030 feat(identity-packs): ADR-0028 — pack-driven hedge & claim-strength shaping
Closes the 'identity is load-bearing but not visibly differentiated'
gap noted at the end of ADR-0027.  Pack swap now produces visibly
different surfaces on identical trajectories at the same alignment.

Schema bump — packs gain an optional 'surface_preferences' block:

  hedge_threshold_strong, hedge_threshold_soft  → band entries
  preferred_hedge_strong, preferred_hedge_soft  → phrases per band
  claim_strength                                → balanced|qualified|affirmative
  qualified_band_high, preferred_qualifier      → marginal-band shaping

Loader enforces threshold ordering (strong <= soft <= qual_high),
phrase length bounds, and the enum-of-three for claim_strength.
Missing block resolves to defaults that reproduce pre-ADR behavior
byte-for-byte; existing tests pass unchanged.

Algorithm (deterministic, surface-only, no sampling/repair/normalize):

  alignment < strong              → preferred_hedge_strong + lower-cased surface
  alignment < soft                → preferred_hedge_soft + lower-cased surface
  soft <= alignment < qual_high
    and claim_strength=qualified  → preferred_qualifier + lower-cased surface
  otherwise                       → bare surface

Three v1 pack profiles:

  default_general_v1   balanced; 0.40 / 0.50 / 0.75 ; 'It seems that' / 'Perhaps'
  precision_first_v1   qualified; 0.55 / 0.70 / 0.85 ; 'Arguably,' / 'In some cases,' / 'Under certain conditions,'
  generosity_first_v1  affirmative; 0.20 / 0.30 / 0.50 ; default hedge phrases

Re-ratified.  New MasteryReport SHAs (superseding Phase-5):

  default_general_v1   → ddc1ba127231272660e6a435e177227558461b0278572a95635b416c3e1dec5a
  precision_first_v1   → cb5fb2323214a26afda33f2a67e22f38fe49f4763829d48ef67fd41241aba33c
  generosity_first_v1  → 94f2f49e1b16c7498fb52b8f9864eecc198618933dc8381a01b809c146826db7

Files touched:

* core/physics/identity.py — new SurfacePreferences dataclass;
  IdentityManifold gains 'surface_preferences' field with defaults.
* packs/identity/loader.py — _build_surface_preferences() parses,
  bounds-checks (threshold ordering, claim_strength enum, phrase
  length, threshold ranges); SurfacePreferences round-trips.
* generate/surface.py — SurfaceContext gains 7 new fields with defaults
  matching the pre-ADR module-level HEDGE_STRONG_THRESHOLD /
  HEDGE_SOFT_THRESHOLD; _apply_hedge takes the full context and
  implements the four-band algorithm; module-level constants retained
  for back-compat.
* chat/runtime.py — _build_surface_context lifts manifold.surface_preferences
  into SurfaceContext.
* packs/identity/*.json — three v1 packs gain surface_preferences blocks
  tuned to their roles; re-ratified via scripts/ratify_identity_packs.py
  (idempotent).
* tests/test_identity_surface_divergence.py — 15 tests covering hedge
  bands, claim_strength bands, pack-swap divergence proof, and runtime
  context wiring.

Suite status: cognition 121, teaching 17, runtime 19, formation 182,
smoke 67 — all green.  test_identity_packs.py 23/23, new
test_identity_surface_divergence.py 15/15.

Docs: ADR-0028 (Accepted) records the decision and verification; ADR-0027
status updated to point to ADR-0028 for deep realizer wiring; README
§Identity Packs notes the visible divergence; docs/identity_packs.md
gains a §Surface preferences section and closes the known-limit #1
about invisible surface differentiation.
2026-05-17 19:42:54 -07:00
Shay
fa05be9293 feat(identity-packs): ADR-0027 — swappable identity manifold via packs
Replaces the hardcoded IdentityManifold constructor in chat/runtime.py
with a content-addressed pack loader.  Identity is now load-bearing AND
swappable: deployments select an identity pack at startup, downstream
builders (robotics, personalization, creative tools) author their own
ratified packs without editing CORE Python.

Phase 1 — pack format + loader
  * packs/identity/loader.py — load_identity_manifold(pack_id, *,
    search_paths, require_ratified) with bounds checks (axis count,
    direction in [-1, 1], weight in [0, 10], threshold in [0, 1],
    axis-id uniqueness).
  * available_packs() helper for discovery.
  * IdentityPackError raised on every bounds violation.

Phase 2 — three v1 packs
  * default_general_v1.json — ship default; encodes the previous
    hardcoded three axes (truthfulness, coherence, reverence)
    byte-for-byte so existing runtime behavior is preserved.
  * precision_first_v1.json — boosts truthfulness weight, narrows
    coherence/reverence; tighter alignment threshold.
  * generosity_first_v1.json — boosts coherence weight, broadens
    reverence; looser alignment threshold.

Phase 3 — replace hardcoded constructor
  * chat/runtime.py:206 calls load_identity_manifold() using
    RuntimeConfig.identity_pack (default DEFAULT_IDENTITY_PACK).
  * Dead _default_identity_manifold() removed.
  * ChatRuntime.identity_pack_id surfaces the loaded pack id.

Phase 4 — CLI flag
  * core chat --identity <pack_id>  (also threaded into trace/oov via
    _add_runtime_policy_args).
  * core/config.py: RuntimeConfig.identity_pack added; empty string
    falls back to DEFAULT_IDENTITY_PACK = 'default_general_v1'.

Phase 5 — formation ratification — INTENTIONALLY DEFERRED.  Loader
currently calls require_ratified=False so the v1 packs (which carry
empty mastery_report_sha256) load.  Authoring SubjectSpecs for each
pack, running the formation pipeline end-to-end to produce signed
MasteryReports, and embedding the SHA into each pack file is a
follow-up.

Tests: 18 new tests in tests/test_identity_packs.py covering loader
happy paths, every bounds violation, runtime wiring, and pack-swap
divergence.

Suite status: cognition 121, teaching 17, runtime 19, formation 182,
smoke 67 — all green.

Docs: ADR-0027 (Accepted) + docs/identity_packs.md (operational ref) +
README.md §Identity Packs + docs/teaching_order.md Layer 1 cross-ref.
2026-05-17 19:24:39 -07:00
Shay
c3d139a2ba docs(cli): self-explanatory demos — preambles + per-directory READMEs
Two-pronged self-documentation pass so reviewers / investors / the
future team can revisit any artifact cold and immediately understand
what it tests, what to expect, and what to do if the numbers shift.

Inline preambles (`core demo`):

  Before each demo's results table, print a structured preamble:
    - WHAT THIS DEMO TESTS          mechanism + corpus shape
    - WHAT TO EXPECT IF WORKING     concrete pass numbers
    - WHAT TO LOOK FOR              specific signals on regression
    - WHEN TO TWEAK                 falsifiability + corpus authoring rules

  Suppressed under --json so machine-readable output is uncluttered.
  Wired into:
    core demo phase5      (5-family stratified mechanism-isolation)
    core demo phase6      (3-condition head-to-head vs baseline)
    core demo all         (combined; both preambles + a "what this means"
                           summary after the combined table)

Per-directory READMEs:

  evals/forward_semantic_control/results/README.md
    - Inventory of every JSON report with headline metrics
    - Per-report interpretation guide ("when to look here")
    - Per-case schema reference
    - "When something looks wrong" troubleshooting tree
    - Cross-links to ADRs, runtime_contracts, findings docs

  evals/forward_semantic_control/public/v2_phase5/README.md
    - The five failure-mode families, geometric construction, and
      expected behaviour per mode
    - Case schemas (single-step + chained) with field semantics
    - How cases were geometrically mined (phase5_mine.py)
    - Authoring rules: add cases, never relax assertions

  evals/forward_semantic_control/public/v2_phase6_demo/README.md
    - The three conditions with case counts and what each proves
    - Why the baseline is in-system (not a transformer LLM) — table
    - Case schema with the `condition` field
    - Authoring rules: surface specific asymmetry, never relax predicate

  evals/forward_semantic_control/public/inner_loop_benign/README.md
    - Why this corpus exists (replaces adversarial-by-accident v1/dev)
    - The Cl(4,1) signature quirk (23/85 tokens with negative
      self-cga_inner) and the 0.25 self-score authoring filter
    - Expected exhaustion_rate per condition
    - How to verify a new case before committing (one-liner snippet)

New contract tests (tests/test_cli_demo.py::TestDemoPreambles + ::TestResultsReadme):
  - Phase 6 preamble explains C1/C2/C3 and the in-system baseline rationale
  - Phase 5 preamble explains all five families AND that δ is falsifiable
  - Preamble suppressed under --json (parseable JSON from byte 0)
  - `demo all` runs both preambles + a "what this means" summary
  - results/README.md mentions every phase report file
  - All three corpus READMEs exist

Tests: 1107 passed, 2 skipped (+8 from preceding baseline).

No mechanism changes — all additions are documentation surface.
2026-05-17 16:39:50 -07:00
Shay
36aad75202 feat(cli): ADR-0024 chain test-suite aliases + core demo subcommand
Two layers of CLI surface so reviewers / investors / industry
observers can run the ADR-0024 chain evidence end-to-end without
typing test file paths or hunting for runner scripts.

Layer 1 — test-suite aliases:
  core test --suite refusal     (Phase 2 typed refusals)
  core test --suite margin      (Phase 3 / ADR-0026 ranked-with-margin)
  core test --suite rotor       (Phase 4 / ADR-0025 rotor admissibility)
  core test --suite inner-loop  (ADR-0024 inner-loop, all 4 sub-tests)
  core test --suite phase5      (stratified mechanism-isolation)
  core test --suite phase6      (3-condition comparative demo)
  core test --suite adr-0024    (full chain, 98 tests, ~2 min)

Layer 2 — `core demo` subcommand:
  core demo phase5              stratified pass/refuse table + per-family
                                breakdown (5 families, both modes)
  core demo phase6              three head-to-head verdicts vs baseline
                                (replay determinism / traced rejection /
                                coherent refusal)
  core demo all                 both phases + combined summary
  core demo list-results        index every JSON report in the central
                                results directory with headline metrics

All demo runs:
  - Write fresh JSON to evals/forward_semantic_control/results/
  - Refresh the results/index.json manifest so reviewers see every
    available report in one place
  - Accept --json for machine-readable output

Central results directory: evals/forward_semantic_control/results/
  phase2_inner_loop_report.json
  phase3_v2_report.json
  phase4_characterization_*.json
  phase5_report.json
  phase5_benign_inner_loop_report.json
  phase6_demo_report.json
  index.json (auto-generated manifest)

Files:
  core/cli.py                   — +9 suite aliases, +cmd_demo (3 targets +
                                  list-results), +index manifest writer
  tests/test_cli_demo.py        — 14 contract tests pinning Layer 1 + 2
  evals/forward_semantic_control/results/index.json — auto-generated

Tests: 1099 passed, 2 skipped (+14 from Phase 6 baseline).
2026-05-17 16:21:37 -07:00
Shay
639e107442 feat(adr-0026): Phase 3 — ranked admissibility with margin
Replace the static-threshold admissibility gate with a ranked-with-
margin check that is scale-invariant under blade-norm variation.
Phase 4 characterization established no single global threshold
separates the v2 mechanism-isolation cases (blade norms vary ~10x);
margins between top and second-ranked candidates do, because they
scale with the blade norm and carry the relative ordering the
geometry actually delivers.

New primitives in generate/admissibility.py:
  RankedCandidate          — (index, word, score)
  MarginVerdict            — admit/reject + top + margin + full ranking
  rank_candidates_by_blade — sort admissible set by cga_inner desc,
                             strict > tie-break by ascending vocab index
  check_margin             — admit top iff score>0 AND margin>=delta

Selection semantics in margin mode are blade-rank-driven: the top-
ranked admissible candidate IS the admitted destination. Differs
from threshold mode (field-driven _nearest_next then per-candidate
gate). Both modes coexist; threshold is the default and ADR-0024
acceptance evidence is preserved byte-for-byte.

Wired through:
  core/config.py        admissibility_mode="threshold" (default)
                        admissibility_margin=0.4
  chat/runtime.py       forwards both fields
  generate/stream.py    margin_mode_active branch — ranks the
                        candidate set once per step, admits or
                        raises InnerLoopExhaustion with the full
                        ranking in rejected_attempts

Default delta = 0.4 chosen from the v2 case margins:
  V2-001: 0.596   V2-002: 0.456   V2-003: 13.27
  V2-004: 3.37    V2-005: 12.74
  min = 0.456 → 0.4 admits all 5 with headroom; 0.5 would refuse
  V2-002. The default is falsifiable: Phase 5 may surface a case
  below 0.4, which should be reported as an architectural finding
  rather than patched per-case.

Acceptance evidence (tests/test_margin_admissibility.py, 13 passing):
  5/5 v2 cases pass in margin mode; forbidden_token in every
  case's rejected_attempts ranking
  Refusal-on-insufficient-margin: delta=0.9 on V2-001 (margin
  0.597) raises InnerLoopExhaustion with full ranking; no silent
  boundary fallback
  Threshold mode byte-identical with or without margin plumbing
  5 reruns produce identical canonical trace steps
  Strict > tie-break: equal scores resolve to lower-index winner
  deterministically

Invariants preserved:
  versor_condition < 1e-6 — rotor V is constructed only for the
    admitted candidate; margin mode adds no normalization/repair site
  Deterministic replay — strict > tie-break now load-bearing in
    rank_candidates_by_blade alongside vocab.nearest
  No approximate recall, no cosine similarity, no HNSW/ANN; pure
    rank-and-difference on exact cga_inner scores
  No new code in field/propagate.py, algebra/versor.py,
    vault/store.py, or chat/runtime.respond()

Suite results:
  full: 1037 passed, 2 skipped (+13 new margin tests)
  core eval cognition: 13/13, 100% intent_accuracy,
                       100% versor_closure_rate

ADR-0026 documents the contract, the single-delta rationale, the
falsifiability story, and the residual risks. Margin mode is
flag-gated default-off; a future ADR may promote it to default
after Phase 5's diversified families confirm the single delta
holds (or surface the architectural finding if it doesn't).
2026-05-17 15:03:03 -07:00
Shay
310793a4ea feat(adr-0024): Phase 2 — honest refusal with typed evidence
Replace plain ValueError at both inner-loop exhaustion sites in
generate/stream.py with InnerLoopExhaustion, a typed ValueError
subclass carrying machine-readable refusal evidence:

  reason            : RefusalReason (INNER_LOOP_EXHAUSTION)
  region_label      : which AdmissibilityRegion blocked
  step_index        : -1 = pre-walk empty intersection;
                      >=0 = in-walk per-step exhaustion
  rejected_attempts : ordered (idx, word, score) triples

Backward-compat by construction: subclassing ValueError preserves
every pre-Phase-2 `except ValueError` handler in chat/runtime.py,
eval lanes, and tests. No edits to chat/runtime.py, field/propagate.py,
algebra/versor.py, or vault/store.py.

Trace path wired:
  - CognitiveTurnResult.refusal_reason (str, default "")
  - compute_trace_hash folds refusal_reason only when non-empty
    -> byte-identical hashes preserved for non-refused turns
  - CognitiveTurnPipeline reads via getattr from ChatResponse and
    forwards into both trace_hash and result construction

Contract documented in docs/runtime_contracts.md §"Refusal contract".

Tests (tests/test_refusal_contract.py — 10 passing):
  - InnerLoopExhaustion isinstance(ValueError) at both raise sites
  - In-walk site carries reason/region_label/step_index>=0/
    rejected_attempts with (int,str,float) triples
  - Pre-walk site uses step_index=-1 sentinel + empty
    rejected_attempts
  - Pre-walk fires even when inner_loop_admissibility=False
  - Trace hash: empty refusal_reason preserves legacy bytes;
    non-empty differs; same inputs are stable

Suite results:
  smoke: 67 passed
  cognition: 121 passed
  runtime: 19 passed
  full: 1024 passed, 2 skipped
  core eval cognition: 13/13, 100% intent accuracy, 100% versor closure

Residual silent path (documented as out-of-scope for Phase 2):
chat/runtime.respond()/arespond() still convert any ValueError to
"" for their public str return contract. So a refused turn today
produces surface == "" with refusal_reason == "" — the typed
evidence is unread between the raise site and the result. The
plumbing on result + trace + pipeline is in place so a future ADR
can wire materialisation (propagate exception to
ChatResponse.refusal_reason, or catch at the pipeline seam) without
re-deriving the contract.

Phase 1 (commit 3940290) and Phase 2 (this commit) were developed
in parallel with disjoint file scope to avoid conflicts.
2026-05-17 14:49:08 -07:00
Shay
7fccf368fb feat(adr-0024): Phase 1 — wire inner-loop admissibility + determinism proof
Phase 1 of the post-ADR-0024 sequence: wire the inner-loop flag into live
cognition paths and prove deterministic-when-wired in the same milestone.

Changes:
- RuntimeConfig: add inner_loop_admissibility + admissibility_threshold.
- ChatRuntime: pass both into generate() on the chat hot path.
- CLI: --inner-loop-admissibility / --admissibility-threshold flags.
- vocab/manifold.py: document strict `>` tie-break as load-bearing for
  ADR-0024 rejected_attempts ordering (determinism by construction, not
  by accident).
- tests/test_inner_loop_admissibility.py: three new determinism tests —
  identical rejected_attempts across 5 runs, identical trace hash across
  5 runs (non-empty), and legacy hash equivalence when no rejections
  occur (flag on/off byte-identical).
- tests/test_language_pack_cache.py: fix stale fixture (en-core-cog-070
  -> en-core-cog-085 after pack growth).

Suite: 995 passed, 0 failed, 2 skipped.

Acceptance criteria met:
- wired through RuntimeConfig + CLI + ChatRuntime + generate()
- deterministic rejected_attempts sequence (verified by repetition)
- deterministic trace hash under inner_loop=True
- legacy ADR-0023 trace hashes preserved when no rejections
- nearest_next determinism is by construction (sequenced iteration +
  strict > tie-break), now documented

Next: Phase 2 — corpus-observation eval on existing v1 corpus with the
four-condition matrix (boundary-only, null control, inner-loop t=0.0,
inner-loop t>0) and exhaustion_rate + latency metrics.
2026-05-17 13:38:55 -07:00
Shay
c504796165 feat(adr-0023): Forward Semantic Control proof evidence — Accepted
Extends ADR-0022 with inspection/telemetry surfaces that turn the
forward-semantic-control claim from "mechanism exists" into "mechanism
is causally load-bearing, isolated, and replayable."

Changes (zero runtime semantics change beyond a pipeline bug fix):

- AdmissibilityTraceStep + GenerationResult.admissibility_trace —
  per-transition record of region label, candidates before/after,
  selected destination, and the typed AdmissibilityVerdict.
- ChatResponse + CognitiveTurnResult expose admissibility_trace,
  admissibility_trace_hash, ratification_outcome,
  region_was_unconstrained.
- hash_admissibility_trace + compute_trace_hash fold the new fields
  only when they carry non-default values, so pre-ADR-0023 turn
  hashes remain byte-preserved.
- Same-path ablation leg in evals/forward_semantic_control/runner.py:
  generate(..., region=None) vs generate(..., region=R) on the same
  runtime/vocab/field/persona/prompt — isolates the region as cause.
- Lane expansion: 8 dev cases across 4 relation axes (cause, means,
  precedes, part_of) including 2 adversarial distractor cases.
- Lane metrics now report region_only_constrained_rate /
  region_only_gap / ratified_rate / demoted_rate / passthrough_rate /
  passthrough_on_scored.
- Bug fix surfaced by the new accounting: _ratify_intent looked up
  runtime.vocab (always None) instead of runtime.session.vocab —
  every production turn was silently PASSTHROUGH. Fixed; ratifier
  now actually gates intent classification.
- tests/test_admissibility_trace.py: hash determinism +
  pre-ADR-0023 byte-preservation tests.

Lane evidence (dev, 8 cases):
- constrained_pass_rate=0.80, causality_gap=0.80
- region_only_gap=1.00 (5/5 with region, 0/5 without — same path)
- ratified_rate=1.00, passthrough_on_scored=false
- overall_pass=true

Bench: 9.41s / 20 turns (~470ms/turn), well inside the +5% budget.

Full pytest: 922 passed, 1 pre-existing failure
(test_language_pack_cache, unrelated to ADR-0023).
2026-05-17 12:55:19 -07:00
Shay
21c22b2201 feat(adr-0022): Forward Semantic Control — Accepted
Resolves all 5 TBDs and closes all 8 acceptance gates for ADR-0022.

TBD-1 (intent oracle): regex seed + field ratification —
generate/intent_ratifier.py. RATIFIED / DEMOTED / PASSTHROUGH
outcomes; DEMOTED routes through honest refusal.

TBD-2 (region intersection algebra): generate/admissibility.py.
Token-set composition via sorted set intersection; blade composition
via outer product with zero-blade as neutral element; rotor
composition via sandwich conjugation routed through
algebra.backend.versor_apply (Rust parity preserved by construction).
Empty intersections preserved — no silent relaxation.

Wiring: propose() and generate() accept an AdmissibilityRegion
(default None preserves legacy behavior); pipeline ratifies intent
at step 1b.i before graph construction.

Eval lane: evals/forward_semantic_control/ — both legs run against
CognitiveTurnPipeline (constrained) vs bare ChatRuntime.chat()
(unconstrained baseline). Dev (3 cases) and public/v1 (1 case) both
report overall_pass=true, causality_gap=1.0, coincidence_rate=0.0.
Chain-endpoint probe surfaces 'delta' only under forward semantic
control.

Bench cost (30 turns): -2.8% wall-clock (within +5% budget the ADR
set for the ratification gate on every turn). 138x cheaper than
Sonnet 4.5; main was 142x.

Tests: 33 new (25 admissibility + 8 ratifier). Full suite 912/913
pass — the single failure is pre-existing pack-size drift on main,
unrelated.
2026-05-17 12:10:20 -07:00
Shay
79a4125d24 feat(bench): bench cost — $/1000 turns + latency, with disclosed assumptions
benchmarks/cost.py measures CORE per-turn cost honestly:

Measured (no estimation):
  - turns, wall_seconds_total, cpu_seconds_total
  - latency stats: min / median / p95 / max in ms
  - throughput in turns per second

Derived with disclosed assumptions:
  - USD per 1000 turns at AWS t3.medium on-demand
    ($0.0416/hr, source cited in CloudReference.source_note)
  - Frontier pricing comparison: Anthropic Claude Sonnet 4.5 /
    Haiku 4.5 and OpenAI GPT-4o, public per-token rates with
    source notes, derived using a conservative 20-in / 40-out
    tokens-per-turn assumption.

Explicitly NOT reported:
  - Joules per turn. Honest energy measurement requires RAPL
    (Linux) or IOKit/powermetrics (macOS) with privileged access
    that a plain Python process cannot get. Reporting a fabricated
    figure from a hand-waved TDP would violate "speculation is not
    evidence." cpu_seconds_total is the available proxy.

CLI:
  core bench --suite cost --runs 100

Measured numbers (100 turns, "What is truth?", warmup 5):
  median latency: 444.88 ms
  p95 latency:    447.10 ms
  throughput:     2.61 turns/s
  $/1000 turns:   $0.0044
  vs frontier:    48–149× cheaper depending on provider

CLAIMS.md Tier 4 cost/latency rows updated with real numbers
replacing TBDs. evals/reports/cost_latest.json committed as the
captured baseline.

Verified: smoke (67), bench --suite cost CLI works.
2026-05-17 10:53:08 -07:00