Commit graph

236 commits

Author SHA1 Message Date
Shay
ec5d6f5ac7
feat(ADR-0161.1): core teaching queue list|show — read-only queue projection (#296)
* docs(math): ADR-0163 — path to GSM8K mastery via candidate-graph admissibility (proposed)

Audit reframes the math roadmap entirely.

State of main: every named math capability axis (G1..G5, S1) passes
at 100% with wrong=0 on its controlled lane.  binding_graph,
math_versor_arithmetic, math_symbolic_equivalence, math_parser,
math_candidate_parser, math_solver, math_verifier, math_realizer,
math_problem_graph — all landed.  The worktrees on disk are stale
forks.

State of GSM8K (50-case train sample): correct=0, refused=50, wrong=0.
Every refusal reason is identical: "candidate_graph: no admissible
candidate for statement: <STATEMENT>".

The reframe: the gap is NOT in operator algebra, NOT in binding graph
internals, NOT in symbolic equivalence.  The gap is in
generate/math_candidate_graph.py — the admissibility surface that
turns a natural-language statement into a candidate the downstream
pipeline can consume.  The capability axes pass at 100% because they
test statement shapes the candidate-graph already admits.  GSM8K
refuses at 100% because its statements span shapes the candidate-graph
has never been taught.

Six-phase plan to lift GSM8K under the thesis "decodes, not generates":

A. Refusal taxonomy (measure before building)
B. Exemplar corpora per shape category (≤20 statements each, ≤3 per round)
C. Contemplation runner ingests exemplars; emits DerivedRecognizer
   proposals
D. Operator ratifies through ADR-0161 HITL queue (no new surface)
E. Re-baseline GSM8K train sample.  Round 1 exit: correct ≥ 10, wrong = 0.
   Round 2: ≥ 25.  Round 3: ≥ 35.
F. Scale to public/v1 (200 cases, target correct ≥ 100), then
   holdout (measurement-only — never tune against).

Three non-negotiables:
- wrong = 0 at every phase.  Auto-rejected by replay gate, not by
  operator vigilance.
- No hand-rolled recognizers in generate/.  Every recognizer lands
  via contemplation → proposal → review corridor.
- Active corpus mutation only via accept_proposal.

Status: proposed.  Implementation lands as three PRs starting with
Phase A scaffolding.

Scope discipline: docs-only.  No code, no eval changes, no corpus
mutation.

* feat(ADR-0161.1): core teaching queue list|show — read-only queue projection

* fix(ADR-0161.1): restore gap-queue CLI + rename new commands to hitl-queue + R1..R5 refinements
2026-05-26 11:42:51 -07:00
Shay
8b3314f060
docs(math): ADR-0163 — path to GSM8K mastery via candidate-graph admissibility (proposed) (#294)
Audit reframes the math roadmap entirely.

State of main: every named math capability axis (G1..G5, S1) passes
at 100% with wrong=0 on its controlled lane.  binding_graph,
math_versor_arithmetic, math_symbolic_equivalence, math_parser,
math_candidate_parser, math_solver, math_verifier, math_realizer,
math_problem_graph — all landed.  The worktrees on disk are stale
forks.

State of GSM8K (50-case train sample): correct=0, refused=50, wrong=0.
Every refusal reason is identical: "candidate_graph: no admissible
candidate for statement: <STATEMENT>".

The reframe: the gap is NOT in operator algebra, NOT in binding graph
internals, NOT in symbolic equivalence.  The gap is in
generate/math_candidate_graph.py — the admissibility surface that
turns a natural-language statement into a candidate the downstream
pipeline can consume.  The capability axes pass at 100% because they
test statement shapes the candidate-graph already admits.  GSM8K
refuses at 100% because its statements span shapes the candidate-graph
has never been taught.

Six-phase plan to lift GSM8K under the thesis "decodes, not generates":

A. Refusal taxonomy (measure before building)
B. Exemplar corpora per shape category (≤20 statements each, ≤3 per round)
C. Contemplation runner ingests exemplars; emits DerivedRecognizer
   proposals
D. Operator ratifies through ADR-0161 HITL queue (no new surface)
E. Re-baseline GSM8K train sample.  Round 1 exit: correct ≥ 10, wrong = 0.
   Round 2: ≥ 25.  Round 3: ≥ 35.
F. Scale to public/v1 (200 cases, target correct ≥ 100), then
   holdout (measurement-only — never tune against).

Three non-negotiables:
- wrong = 0 at every phase.  Auto-rejected by replay gate, not by
  operator vigilance.
- No hand-rolled recognizers in generate/.  Every recognizer lands
  via contemplation → proposal → review corridor.
- Active corpus mutation only via accept_proposal.

Status: proposed.  Implementation lands as three PRs starting with
Phase A scaffolding.

Scope discipline: docs-only.  No code, no eval changes, no corpus
mutation.
2026-05-26 10:56:12 -07:00
Shay
8a24256ac5
docs(workbench): ADR-0162 — Workbench Design System v1 (proposed) (#293)
The design substrate that W-027..W-031 will inherit.  Pins tokens,
typography, motion, semantic state mapping, the StableJsonViewer
trust-surface invariants, empty/error/loading contracts, the
keyboard-first contract, the five-region shell, the v1 component map,
and an explicit no-go list — before any frontend code exists.

Headline decisions:

- Semantic tokens only.  `--color-surface-base`, not `--color-zinc-900`.
- Inter (UI) + JetBrains Mono (hash/JSON/trace), self-hosted.
- Badges bound 1:1 to ratified Python enums:
  EpistemicState (15), NormativeClearance (4), ReviewState (4),
  grounding source (6).  No aspirational badges; adding an enum
  value to the engine without a badge fails the test.
- Motion: reveals structure, not cognition.  Allowed set is small
  and tokenised; reduced-motion collapses everything to instant.
- StableJsonViewer ships six tested invariants (deterministic order,
  lossless strings, no semantic auto-format, copy-path as JSON
  Pointer, structural diff, large-doc / oversize safety).
- Every route ships empty / error / loading states from day one,
  each following an explicit contract.  No empty-empty, no
  "Thinking…", no indefinite shimmer.
- Five-region shell; routes may collapse the right inspector but
  not the top bar, left nav, or status footer.
- v1 must-ship component map is narrower than the vision; named
  follow-ups are anticipated but not committed.

No-go list is explicit: no chat-clone styling, no animated cognition
theater, no glassmorphism, no purple gradients, no accept buttons,
no dashboard soup, no color-only encoding.

Status: proposed.  Implementation lands in Branch 1
(workbench-ui/ scaffold + design tokens + StableJsonViewer +
badges + empty/error/loading + a /preview page) before W-027
starts.

Scope discipline: docs-only.  No code, no UI, no API changes.
2026-05-26 10:38:47 -07:00
Shay
0909ef2782
docs(L11): ADR-0161 — HITL async queue (proposed) (#291)
Answers all eight L11 sub-questions by selecting the narrowest
commitment compatible with existing ADR-0057 / 0151 / 0152 / 0155
machinery and the ratify-proposal workflow.

Headline decisions:

- Queue is a DERIVED VIEW over teaching/proposals/proposals.jsonl
  ∪ contemplation/runs/*.json.  No new persistence file.
- Queue identifier = proposal_id (deterministic over content per
  ADR-0151).  States: ADR-0057's existing alphabet.
- Three operator surfaces: GitHub PR (inspect-only, mobile),
  workflow_dispatch (accept|reject|withdraw, mobile),
  local CLI (audit-grade authority).  PR-merge admits; it does
  not ratify.
- Engine keeps serving turns while items are pending; pending
  proposals are observable but never active truth; proposal-on-
  proposal dependencies forbidden.
- Pending cap 256.  Dedup by deterministic proposal_id.  No
  wall-clock expiry — staleness is measured in proposals, not
  seconds.  Full queue emits a typed `queue_full` report instead
  of silently dropping.
- Only the repo owner ratifies; workflow path enforces an actor
  allow-list and fails closed.  Every transition records
  ratifier_kind, actor, commit_sha, workflow_run_id, review_date.

Five-step implementation plan included; each step is small,
self-contained, and ships its own ADR-compatibility test.

Status: proposed.  Closes W-009 once implementation lands.

Scope discipline: docs-only.  No code, no workflow changes, no
tests, no ADR ratification yet.  Pure prose contract.
2026-05-26 10:02:16 -07:00
Shay
f0892251af
docs(L11): scope HITL async queue
Docs-only L11 scope document. Smoke + verify pinned lane SHAs CI checks pass.
2026-05-26 08:22:50 -07:00
Shay
404e694824
docs(workbench): CORE Workbench v1 planning architecture (ADR-0160)
Docs-only planning branch. Smoke + verify pinned lane SHAs CI checks pass.
2026-05-26 08:22:38 -07:00
Shay
cc6c912f17
feat(W-025): contemplation quality eval lane (ADR-0159) (#286)
* feat(W-025): add contemplation quality eval lane

* feat(W-025): add contemplation quality eval lane

* feat(W-025): expose contemplation-quality generic eval runner

* feat(W-025): add contemplation-quality contract

* feat(W-025): add contemplation-quality invocation case

* feat(W-025): add contemplation-quality public invocation case

* feat(W-025): add ADR-0159 contemplation-quality eval lane

* fix(W-025): harden contemplation-quality malformed input handling
2026-05-25 20:38:52 -07:00
Shay
5045700484
feat(W-024): reboot_event audit trail entry (L10b.3, ADR-0158) (#284)
* feat(W-024): reboot_event audit trail entry (L10b.3, ADR-0158)

L10 scope §Sub-question 3: a reboot_event analog of TurnEvent, written
to the telemetry JSONL, lets future audit reconstruct when this engine
instance lost and regained its lifetime.

- serialize_reboot_event / format_reboot_event_jsonl in chat/telemetry.py
  emit type="reboot" with restored_turn_count, stored/current revisions,
  revision_matched, recognizers_count, candidates_count
- ChatRuntime._load_engine_state() buffers the JSONL line in
  _pending_reboot_payload (str|None); ChatRuntime.attach_telemetry_sink()
  flushes it exactly once when a sink is first attached
- Reboot event precedes all turn events in the session audit stream
- Pinned by 11 tests: serializer structure, determinism, revision_matched
  logic, runtime integration (emit-once, no-checkpoint, no-load-state,
  revision match, ordering)

Closes L10b: W-022 (atomic writes) + W-023 (revision warning) + W-024
together satisfy ADR-0146's atomic/observable/auditable checkpoint triad.

* fix(W-024): expose cached public git revision helper
2026-05-25 20:37:00 -07:00
Shay
fbff161a2e
feat(W-023): revision-mismatch warning on engine-state load (L10b.2, ADR-0157) (#283)
* feat(W-022): ratify-proposal workflow_dispatch for mobile ratification

Adds .github/workflows/ratify-proposal.yml — a manually triggered
workflow that lets the operator ratify engine-authored proposals from
the GitHub mobile app without needing terminal access.

Inputs: proposal_id (required), review_date (default: today UTC),
operator_note (optional).  Runs `core teaching review --accept`,
commits the updated corpus + proposal log to main, and posts a
job summary with the accepted chain_id.

Shared CONTEMPLATION_ENABLED kill switch disables the entire
learning-arc loop (contemplation + ratification) with one toggle.

ADR-0155 / ADR-0057

* feat(W-023): revision-mismatch warning on engine-state load (L10b.2, ADR-0157)

ADR-0146 §Risks line 127 specified that load_manifest() should compare
written_at_revision against the current git SHA and warn if they differ,
but never refuse to load (reboot is recovery, not control flow).

- EngineStateStore.load_manifest() emits RuntimeWarning when stored and
  current revisions are both known and do not match
- Suppresses warning when either side is "unknown" (offline/packaged builds)
- Always returns the manifest; no state is cleared or rejected
- Pinned by 8 tests covering match, mismatch, unknown suppression, and
  missing/empty manifest edge cases

ADR-0156 §Out of scope closes; L10b.3 (reboot_event audit entry, W-024) remains.
2026-05-25 19:56:07 -07:00
Shay
2c49b05acc
feat(W-022): atomic engine-state checkpoint writes (L10b.1, ADR-0156) (#280)
ADR-0146 specified write-temp+rename for the engine-state
checkpoint to prevent corruption on mid-write process termination.
The W-008 implementation used Path.write_text directly, which
truncates the target before writing — SIGINT/SIGKILL between
truncate and write left a partial / empty file, breaking reboot
recovery (or worse, silently restoring half-state).

- engine_state._atomic_write_text: NamedTemporaryFile in target dir,
  flush + fsync, os.replace (atomic same-FS rename), best-effort
  cleanup of temp on failure
- All three EngineStateStore.save_* methods route through the helper
- Content bytes unchanged → round-trip regression guard passes

Pinned by tests/test_adr_0156_atomic_checkpoint.py (9 tests):
atomic create / overwrite / parent-mkdir, failed-replace preserves
prior target, failed-replace cleans temp, temp lives in target dir
(same-FS atomicity requirement), store-level failure preservation,
round-trip content regression guard.

CLI lanes: smoke (67) + cognition (120+1 skip) green.

Out of scope (next L10b chunks): reboot_event audit entry (W-024),
revision-mismatch warning on load (W-023), parent-dir fsync, cross-
process locking.
2026-05-25 19:41:11 -07:00
Shay
89387fc0ff
feat(W-021): CI contemplation runner with HITL PR gate (ADR-0155) (#279)
Adds a scheduled GitHub Actions workflow that runs
`core demo learning-arc --json`, writes the report to
contemplation/runs/<stamp>.json, and opens a PR against main.
Operator review on the PR is the ratification gate — preserves the
HITL invariant from ADR-0150/0152.

Workflow stays disabled until repo variable CONTEMPLATION_ENABLED
is set to "true" (soft kill switch in repo settings).  Default
cadence is nightly; ADR includes a budget table for the 3000
Linux minutes/month available on GitHub Pro.

CI never:
- commits to main directly
- mutates corpora/ or packs/
- ratifies proposals
- registers recognizers

CI only writes a report file under contemplation/runs/ and proposes
the diff via PR.  Determinism check (first-run verification): local
+ CI runs at same SHA must byte-match on proposal_id / trace_hash.

Out of scope (noted in ADR): persisted engine_state across CI runs,
auto-merge, cross-runner determinism, recognizer growth from CI
synthetic traffic.

To enable:
1. Repo Settings → Variables → CONTEMPLATION_ENABLED=true
2. Actions → contemplation → Run workflow
3. Review the resulting PR before merging
2026-05-25 18:47:06 -07:00
Shay
34baf60b35
feat(W-020b): DerivedRecognizer producer wiring (ADR-0154) (#278)
W-007/ADR-0149 wired the consumer side of the recognizer registry
(first_admitted_recognizer → graph derivation, opt-in via
recognition_grounded_graph). The producer side — capturing
(tokens, bundle) from admitted turns so derive_recognizer at
checkpoint can anti-unify them — had no production caller.
record_recognition_example existed but was only invoked by tests,
so _pending_recognizer_examples stayed empty in live sessions and
the registry could never grow from traffic.

Observed: 103-turn session wrote recognizers.jsonl empty even with
recognition running.

- CognitiveTurnPipeline.run calls runtime.record_recognition_example
  at the admitted-recognition boundary
- Producer fires unconditionally; consumer (derive_recognizer at
  checkpoint) stays opt-in behind the same flag — flipping it later
  is no longer a cold start
- hasattr guard keeps the pipeline tolerant of non-ChatRuntime
  runtimes

Validated: tests/test_adr_0154_recognizer_producer_wiring.py (5
tests covering admit/refuse, flag-off producer, end-to-end loop,
accumulation); core test --suite cognition/smoke + recognition
phase 1/2/refusal-propagation all green.

Out of scope: bootstrap of the first recognizer from operator
review (substrate-liveness audit scope); bounded growth of the
producer queue when consumer flag stays off (future LRU cap).
2026-05-25 18:44:12 -07:00
Shay
5e6a16d473
feat(W-020a): TurnEvent.trace_hash back-stamp (ADR-0153) (#277)
TurnEvent had no trace_hash field, so teaching/discovery._trace_hash
always returned "" via getattr default. Every persisted DiscoveryCandidate
had source_turn_trace="" — provenance gap observed in a real 103-turn
session.

- Add trace_hash: str = "" to TurnEvent
- runtime.finalize_turn_trace_hash back-stamps last TurnEvent and
  unstamped tail of _pending_candidates, then re-persists
- CognitiveTurnPipeline.process calls finalize_turn_trace_hash after
  compute_trace_hash, before constructing CognitiveTurnResult

Invariants: empty hash is a no-op; back-walk halts at first already-
stamped candidate (no overwrite of prior turns); trace_hash bytes are
unchanged for any given turn.

Validated: tests/test_adr_0153_trace_hash_backstamp.py (6 tests),
core test --suite cognition/smoke/runtime/teaching all green.

Out of scope: OOV candidate trace_hash (same root cause, line-streamed
sink requires different fix); telemetry-sink trace_hash exposure.
2026-05-25 18:42:35 -07:00
Shay
e7e28a2fd5
feat(W-019): learning-arc demo — engine-authored proposal from contemplation (ADR-0152) (#276)
Two-session arc where engine derives connective+object from corpus
decomposition; operator ratifies rather than authors. Distinguishes
from learning-loop (operator-authored) and directly exercises W-018
checkpoint contemplation and W-017 auto-proposal provenance path.
2026-05-25 13:03:10 -07:00
Shay
df6c9a3206
feat(W-017): load-time auto-proposal pipeline from enriched candidates (ADR-0151) (#275)
Wires contemplation-enriched DiscoveryCandidates into the ADR-0057 proposal
gate at _load_engine_state(). Proposals land in ProposalLog with
source.kind="contemplation"; operator ratification via existing
core teaching review path unchanged.
2026-05-25 12:46:10 -07:00
Shay
81718a0952
feat(W-007): wire DerivedRecognizer registry into CognitiveTurnPipeline (ADR-0149) (#274)
- RecognizerRegistry.first_admitted() — deterministic first-registered selection
- CognitiveTurnPipeline consults runtime registry when no recognizer explicitly passed
- ChatRuntime gains _pending_recognizer_examples + record_recognition_example()
- checkpoint_engine_state() derives and registers recognizer from accumulated examples
- RuntimeConfig.recognition_grounded_graph gate (already existed) controls wiring
- ADR-0149 decision record
2026-05-25 12:24:48 -07:00
Shay
5152719613
feat(W-018): autonomous inter-session contemplation at checkpoint (ADR-0150) (#273) 2026-05-25 12:24:39 -07:00
Shay
0b940674c0
feat(W-003): wire VaultPromotionPolicy into turn boundary (ADR-0148) (#272)
* feat(W-003): wire VaultPromotionPolicy into turn boundary (ADR-0148)

VaultPromotionPolicy had zero callers; vault entries never crystallized
from SPECULATIVE to COHERENT.  This PR wires the policy at the turn
boundary so settled entries can promote automatically.

Changes:
- core/config.py: add vault_promotion_enabled flag (default False, null-drop)
- vault/store.py: add promote_eligible_entries(policy) — metadata-only scan,
  versors unchanged, _matrix_cache not invalidated
- session/context.py: persist energy_raw/energy_class/coherence_residual in
  vault payload inside finalize_turn so the policy has data to decide on
- chat/runtime.py: call promote_eligible_entries after each finalize_turn,
  gated on vault_promotion_enabled; import VaultPromotionPolicy
- docs/decisions/ADR-0148-vault-promotion-policy-wiring.md: decision record
- tests/test_adr_0148_vault_promotion.py: 6 tests, all green

Unlocks W-007 (DerivedRecognizer derivation from COHERENT vault entries).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* fix(W-003): resolve Pyright errors on vault promotion wiring

- vault/store.py: add TYPE_CHECKING guard to import VaultPromotionPolicy
  only at type-check time, avoiding circular import at runtime while
  making the name resolvable to Pyright.
- session/context.py:262: suppress union-attr false positive — self.state
  is guarded non-None by the raise at line 256 when input_versor is also
  None, but Pyright cannot narrow through the nested ternary structure.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 11:57:00 -07:00
Shay
9bbdcc96aa
feat(W-008): L10 Shape B hybrid engine-state persistence (#271)
* ci: re-trigger full-pytest

* docs: ADR-0146 — L10 Shape B hybrid engine-state persistence

* feat(W-008): Shape B engine-state persistence spike (ADR-0146)

* fix(W-008): eval isolation + env-var path + empty-manifest guard

- evals/run_cognition_eval.py: all ChatRuntime() calls pass no_load_state=True
  so parallel eval workers never touch engine_state/ checkpoints
- engine_state/__init__.py: honour CORE_ENGINE_STATE_DIR env var (ADR-0146 spec)
- engine_state/__init__.py: load_manifest() skips empty file instead of crashing
  (defensive against partial writes in concurrent contexts)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 11:45:54 -07:00
Shay
f1d6c49814
[codex] Implement energy-modulated vault surface (#269)
* Implement energy-modulated vault surface

* docs/tests: add ADR-0145 and test suite for energy-modulated vault readback

Adds the decision record and 9 tests pinning the W-005 contract:
- energy_modulated_surface() prefix table (E0–E4)
- pack-grounded paths carry no recall_energy_class
- vault-grounded paths carry recall_energy_class=E2 and prefixed surface

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

* ci: retrigger after 30m timeout

* ci: raise full-pytest timeout-minutes 30→45

* fix(ci): skip showcase runtime budget on slow CI runners (CORE_SHOWCASE_SKIP_BUDGET)

---------

Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-25 11:33:32 -07:00
Shay
9b1c94704c
feat(protocol): ADR-0140 CORE Trace Protocol v0 (#259)
* feat(protocol): add protocol package exports

* feat(protocol): add canonical serialization and hashing

* feat(protocol): add CTP typed records

* feat(protocol): add CTP envelope

* feat(protocol): add CTP event constructors

* feat(protocol): export CTP constructors

* feat(protocol): add JSONL event IO

* feat(protocol): add CTP replay verification

* feat(protocol): export JSONL and replay helpers

* test(protocol): pin CTP canonical replay contracts

* docs(protocol): ratify CORE Trace Protocol v0
2026-05-25 06:08:51 -07:00
Shay
d52bff56e7
chore(phase2): close W-006/W-010/W-014 — cleanup + doc amendments (#264)
W-006 (operator decision: delete):
- Remove dormant packs/en/el/grc/he/readback_rules.py (4 files, 0 live
  production callers). generate/realizer.py superseded the per-language
  readback path; per [[feedback-cleanup-as-you-find]], superseded code
  is removed rather than preserved.
- Remove _gate_readback from packs/common/validator.py and drop it from
  the validate_pack_dir gate sequence. Add language to the report dict
  so the param remains non-vacuous.

W-010 (operator decision: intentional token-level):
- Amend ADR-0143 with "Vocabulary isolation is intentional" section.
  Token-level anti-unification derives its own structural vocabulary;
  importing VocabManifold adds no information at that level. Confirmed
  intentional by operator review 2026-05-25.

W-014 (operator decision: evals-only):
- Add deployment-scope note to core/cognition/provenance.py docstring:
  evals-only infrastructure, no live runtime caller. Confirmed
  evals-only by operator review 2026-05-25.
2026-05-25 06:05:17 -07:00
Shay
3aade62192
docs(runtime): scope L10 — runtime model for forever-running CORE (#236)
Names the missing prerequisite that recognizer-storage v2 and
substrate-liveness-audit v2 both flagged: the process shape in which
the engine accumulates capability over its lifetime, survives reboot
as recovery, and presents a narrow async HITL ratification entrypoint.

Cross-reference discipline applied up-front (per
feedback-adr-cross-reference-discipline memory entry — fourth
iteration; this time grep BEFORE draft). Existing ADRs identified
as load-bearing: ADR-0040 (telemetry sink, persistent audit trail),
ADR-0041/0042 (operator surface + audit-tour), ADR-0055 (four-tier
memory: T1 session vault → T4 ratified packs; explicitly names
"what survives across all sessions and reboots"), ADR-0056/0080
(contemplation loop), ADR-0057 (proposal review machinery this
scope must build on), ADR-0014 (vault promotion gate, currently
dormant — L2 audit will verify), ADR-0027/0029/0033 (identity/
safety/ethics packs, currently startup-loaded).

Current state honestly mapped: every entry is a one-shot CLI
command via argparse in core/cli.py; ChatRuntime is per-invocation;
no long-lived process exists.

Four sub-questions framed:
1. Process shape — long-lived daemon vs. hybrid (state externalized
   + restored) vs. one-shot CLI with audit-trail-as-lifetime.
2. State partitioning — session-state (ephemeral) / engine-state
   (live, persistent across reboot) / substrate-state (cold,
   persistent).
3. Reboot recovery — what verifies, what reloads vs. rederives, what
   records.
4. HITL async entrypoint — queue shape, backpressure, operator
   interaction model.

Cross-references shelved project-engine-identity-candidate (DNA-
analog EngineIdentity) as potential primitive if sub-question 3
demands cross-reboot identity verification. Does NOT un-shelve it;
flags trigger.

Explicit rejections: database persistence (per ADR-0055 north-star);
network primary entrypoint (per user-circumstances memory entry,
always-on-internet unsafe to assume); multi-tenant; re-architecting
ChatRuntime.

Constraints inherited from CLAUDE.md: deterministic replay, no
hidden state, HITL is narrow entrypoint, reboot is recovery not
control flow, append-only artifacts stay append-only, no drift
repair / hot-path normalization.

This is a scope, not a decision. Spike/ADR decides; audit findings
(L4-L9) inform.
2026-05-24 16:27:27 -07:00
Shay
573ef98aba
docs(audit): scope substrate liveness audit (system-of-systems closure) (#234)
* docs(audit): scope substrate liveness audit (system-of-systems closure)

The recognizer-storage v1→v2 revision surfaced a pattern: CORE
contains ~140 ADRs, many marked Implemented, but several have
spec-in-code that nothing live calls (e.g., VaultPromotionPolicy in
core/physics/learning.py — imported by no module outside its package).
The engine today executes a subset of its own design.

Per the operator's system-of-systems framing (human body / universe /
ecosystem: subsystems achieve closure together; a half-built layer
degrades the whole organism silently): this scope defines a layered
audit that walks from the foundation outward to identify, per ADR
and per module, which subsystems are closed (designed + wired +
exercised + cross-layer consistent), which are partial, and which
are open.

The audit method is mechanical: grep + caller-trace + end-to-end test
verification + cross-layer contract check. Two reviewers running the
audit should produce identical verdicts. No refactoring, no new ADRs,
no subjective judgment — just evidence.

The output is two artifacts: a closure registry (per-layer, per-ADR
verdicts with evidence) and a ratchet plan (wiring sequence in
dependency order). Both append-only / revisable; both committed to
the repo as audit artifacts.

First-pass layering (L0 algebra primitives → L11 forever-running
engine, with L10 runtime model named as the missing prerequisite)
is a hypothesis the audit will refine. Layers L0–L3 are expected to
be closed (foundation); L4–L9 are expected to be partial; L10–L11
are explicitly open and depend on the audit + the runtime-model
scope.

Applies feedback-adr-cross-reference-discipline (the memory entry
this revision flagged): explicit cross-references to ADR-0006/0014/
0055/0056/0057/0142/0143/0144 and the existing scope docs.

This is a scope, not an audit. Audit deliverables (registry, ratchet)
are separate work.

* docs(audit): revise substrate-liveness-audit scope to v2 (self-review fixes)

Self-review surfaced two HIGH, three MEDIUM gaps in v1. Notably,
v1 of the scope that creates cross-reference discipline still
committed the documented mistake — third consecutive iteration of
the same failure mode in one session (recognizer-storage v1
substrate overclaim → recognizer-storage v2 drop-off invention →
audit-scope v1 ADR range mis-grouping). New "Self-review
acknowledgment" section records the pattern's durability and
states the structural mitigation: the audit's mechanical
deliverables make the discipline impossible to skip silently,
which is more rigorous than the memory entry alone.

HIGH-1 — ADR range mis-grouping. v1 layering table listed
"ADR-0055..0064" as L7 (teaching loop); verification showed
ADR-0058-0064 are predominantly L6 (surface composition,
correction telemetry, cross-pack resolution). Fixed L7 to cite
only ADR-0057; added explicit note that ADR-range citations
are starting points and the audit's first act per layer is
re-enumeration.

HIGH-2 — Audit tractability buried in risks. ~140 ADRs requires
structural handling, not just a risk warning. Promoted "per-layer
commits + per-layer handoff to subagents + progress tracking in
registry + optional per-layer file splitting" to a first-class
Step 0 in the audit method. The audit is explicitly framed as the
archetypal parallel-agent handoff candidate.

MEDIUM-1 — Expected-status column anchored the auditor. v1's
table had my predictions ("Closed (foundation)", "Live but
session-bounded"). Removed; replaced with a "Where to look first"
column. Explicit note: "No expected-status column intentionally
— predictions are the failure mode this scope was meant to
prevent."

MEDIUM-2 — "End-to-end test" criterion maps awkwardly onto CORE's
suite-lane organization. Reframed Step 4 to "Identify the
exercising suite lane" with concrete `core test --suite {…}` /
`core eval …` invocations. A module whose only test coverage is
in `tests/` files not reached by any suite lane is a closure gap.

MEDIUM-3 — Cross-layer contract check was hand-wavy. Made
Step 5 explicitly two-pass: mechanical (grep for at least one
consumer per exposed field/method) carries full verdict authority;
judgment-required semantic mismatches are flagged for operator
review rather than verdicted mechanically.

LOW fixes: softened "two reviewers identical" claim; L10/L11
explicitly marked not-audit-targets; per-layer file splitting
flagged as auditor's choice; closure-criteria item 4 wording
aligned with new Step 4.

Frontmatter status bumped to "Draft v2"; date line records
revision provenance.
2026-05-24 16:02:10 -07:00
Shay
6fad87b4d7
docs(recognition): scope recognizer storage against existing thermodynamic substrate (#232)
* docs(recognition): scope recognizer storage against existing thermodynamic substrate

Two changes:

1. New scope: docs/decisions/recognizer-storage-scope.md (draft v1).
   Reframes the recognizer-storage question against ADR-0006 (field
   energy operator) and ADR-0014 (vault promotion policy) — the
   thawed ↔ crystallized lattice already implemented under
   core/physics/{energy,learning}.py. The three-candidate framing
   (pack / vault / substrate) was drafted without acknowledging this
   substrate; once it's in view, the storage question collapses to:
   how does a derived recognizer participate in the existing
   excitation / cooling / coherence-settling / promotion / re-thaw
   dynamics, and what extension is needed for HITL-gated drop-off.

   Names three measurements that need definition (recognizer
   excitation, coherence residual, promotion criteria), one sibling
   ADR (drop-off / deprecation), and the forever-running runtime
   principle. Explicitly rejects pack-as-recognizer-container,
   vault-without-substrate-reframe, per-session re-derivation, and
   approximate match.

2. Amendment to docs/decisions/teaching-derived-recognition-scope.md.
   Appends a "Connection to existing thermodynamic substrate" section
   acknowledging the three-candidate omission, citing ADR-0006/0014,
   and pointing forward to the recognizer-storage scope. The original
   framing is preserved for history.

Neither doc proposes a decision. Both define the question.

Process note: the omission this corrects motivated saving a project
memory (feedback-adr-cross-reference-discipline) to prevent
independent reinvention in future ADR work.

* docs(recognition): revise recognizer-storage scope to v2 (self-review fixes)

Self-review surfaced two HIGH and two MEDIUM gaps in v1.

HIGH-1 — Substrate liveness overclaim. v1 described the entire
field-energy + vault-promotion lattice as live. Verified: only the
energy half is wired (FieldEnergyOperator called by ingest/gate.py,
field/propagate.py, language_packs/compiler.py); core/physics/learning.py
(VaultPromotionPolicy) is imported by no module outside core/physics/.
Added "Substrate liveness audit" subsection that honestly accounts for
which pieces are live vs. dormant, and explicitly states that the
recognizer-storage ADR must deliver both wiring the dormant promotion
path AND extending it for recognizers as content type.

HIGH-2 — Meta-irony: v1's drop-off section invented a HITL ratification
path without cross-referencing ADR-0057's existing teaching-chain
review/replay/append-only-log machinery — exactly the failure mode the
new feedback-adr-cross-reference-discipline memory was meant to prevent.
Added explicit cross-reference: drop-off reuses ADR-0057's review-and-log
plumbing; load-bearing originality is the recency-driven trigger and
the (non-replay-equivalence) gate. Plus HITL latency named as a
load-bearing architectural constraint, not just queue plumbing.

MEDIUM-1 — "Forever-running runtime" was framed as an assumption. Honest
status: current runtime is session-bounded (core chat is a CLI; each
invocation builds a fresh ChatRuntime; no long-lived process). Reframed
as a prerequisite (own scope, gates this one), not an assumption.

MEDIUM-2 — "Substrate-resident destination" was named but never sketched,
making the IOU concrete-free. Added a one-paragraph sketch (recognizer
as versor; recognizer as null-cone region) to keep the destination
honest. Explicitly illustrative, not committed.

LOW corrections inline: recognizer-excitation temporal-direction note;
0.05 residual threshold marked as default; cold-path latency reframed
as a general vault concern recognizers inherit rather than introduce.

Frontmatter status bumped to "Draft v2"; date line records revision
provenance.
2026-05-24 15:35:10 -07:00
Shay
d230eaa222
docs(recognition): scope teaching-derived recognition (v2, prerequisite-only) (#228)
566-line scope document defining the next recognition phase after
ADR-0144's epistemic carrier. Not a decision — defines the question
the follow-up ADR must answer.

v2 reframes from v1:
- feature-bundle outputs whose type emerges from lifted features (not
  pre-decided proposition categories)
- evidence-bound lifts with span pointers + contradiction detection
  for adversarial robustness
- multi-resolution decoding (chunked-first / word-by-word fallback)

Companion to docs/decisions/proposition-graph-scope.md (shipped with
ADR-0144). Anchored to the decoding-not-generating thesis.
2026-05-24 14:27:54 -07:00
Shay
87b0eda345
feat(recognition): ADR-0144 — EpistemicGraph carrier + pipeline integration (#227)
Implements the PropositionGraph epistemic carrier (ADR-0144):

recognition/carrier.py — EpistemicTransition, EpistemicNode, EpistemicGraph.
  Frozen, JSON-serializable, byte-deterministic. EpistemicNode wraps a
  RecognitionOutcome with an append-only provenance chain; epistemic_state
  property tracks last transition's to_state or outcome.state when empty.

recognition/connector.py — epistemic_node_to_graph_node(). Maps an admitted
  EpistemicNode's FeatureBundle (agent/relation/count/unit) to a GraphNode
  for the generation-side articulation planner.

CognitiveTurnPipeline gains a recognizer: DerivedRecognizer | None param
  (default None — all existing callers unaffected). When attached, run()
  calls recognize() at the top of every turn and wraps admitted outcomes in
  an EpistemicGraph. CognitiveTurnResult.epistemic_graph carries it.

RuntimeConfig.recognition_grounded_graph: bool = False — opt-in flag that
  replaces the intent-derived PropositionGraph with one derived from the
  admitted EpistemicNode via the connector.

RatificationOutcome gains three specific PASSTHROUGH sub-values
  (PASSTHROUGH_NO_FIELD / NO_VOCAB / NO_VERSOR) for _ratify_intent
  observability (ADR-0142 debt 1). All normalise to "passthrough" before
  trace_hash so pre-ADR-0144 hashes are byte-identical.

24/24 acceptance tests pass; 67/67 smoke tests pass; no regressions.
2026-05-24 13:39:01 -07:00
Shay
5f3b7e316d
feat(recognition): output contract + ADR-0143 (#225)
Adds recognition/outcome.py: RecognitionOutcome, FeatureBundle,
BoundFeature, EvidenceSpan, NegativeEvidence, the three typed refusal
classes (ShapeRefusal, FeatureEvidenceRefusal, FeatureConsistencyRefusal),
and RecognitionProvenance. Frozen dataclasses, JSON-serializable,
byte-deterministic invariants enforced in __post_init__.

ADR-0143 commits to Mechanism D (multi-resolution anti-unification over
token sequences) and defines the two-phase acceptance test.
2026-05-24 12:37:25 -07:00
Shay
35c8a1c56b
feat(epistemic): populate normative_detail on TurnEvent and ChatResponse (#223)
* feat(epistemic): populate normative_detail on TurnEvent and ChatResponse

Adds normative_detail_from_verdicts() to core.epistemic_state and wires
it into both the stub and main ChatResponse/TurnEvent construction sites.
The field carries a sorted comma-separated list of violated boundary or
commitment IDs when normative clearance is VIOLATED or SUPPRESSED; empty
string otherwise.

* docs(ADR-0142): ratify epistemic state taxonomy — 14-state vocabulary + normative clearance axis

Formalises the six-subsystem Framing 1 audit findings into a first-class
decision. Accepts the 14-state taxonomy and companion 4-value normative
clearance axis. Documents Phase 3 deliverables already landed and defers
structured provenance + cross-subsystem transition machinery to ADR-0144.
2026-05-24 11:56:34 -07:00
Shay
03adedde01
docs(epistemic-scope): Framing 1 audit complete across all six subsystems (#221)
* feat(epistemic): add first-class state enums

* feat(epistemic): tag TurnEvent with state axes

* feat(epistemic): serialize turn state axes

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

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

* feat(epistemic): expose vault status mapping

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

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

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

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

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

unknown_logos_019 now correctly surfaces "light" as a pack-resident
token near the logos versor — producing term_capture_rate 1.0 on both
main and Phase 3. The 0.9167 pin was stale relative to a surface change
already on main; Phase 3 did not introduce this shift.

* docs(epistemic-scope): mark Framing 1 audit complete across all six subsystems

Teaching pipeline (47 pts, 0 new states), cognition pipeline (42 pts,
0 new states, 1 EPISTEMIC_STATE_NEEDED placeholder), and chat runtime
(47 pts, 0 new states, 6 provenance gaps) audits complete. Taxonomy
confirmed stable; remaining work is implementation debt and provenance,
not taxonomy extension.
2026-05-24 11:42:30 -07:00
Shay
c186ac858d
Merge pull request #218 from AssetOverflow/docs/pedagogy-review-and-teaching-backlog
docs(epistemic-state-taxonomy): ratify INFERRED + normative clearance axis; close four-subsystem audit
2026-05-24 10:14:03 -07:00
Shay
2ba137031e docs(epistemic-state-taxonomy): ratify INFERRED + normative clearance axis; close four-subsystem audit
Vault, language-packs, and runtime-packs audits complete. Findings:

- Ratifies INFERRED (14th epistemic state): derived from DECODED
  primitives by ratified deterministic rule; composite never curated.
  Grounded by language_packs composition rules (per/square/cubic unit
  synthesis). Sits between DECODED and UNVERIFIED-POSSIBLE in the
  epistemic progression.

- Ratifies normative clearance axis as orthogonal companion to the
  epistemic axis. Safety/ethics verdicts are not epistemic states;
  they answer a different question (normative compliance vs. truth-
  value). Four clearance states: CLEARED, VIOLATED, UNASSESSABLE,
  SUPPRESSED. Every proposition in ChatResponse/TurnEvent carries
  both an epistemic_state and a normative_clearance tag.

- Closes open question 5 (identity/safety/ethics interaction):
  identity grounds the epistemic axis; safety/ethics live on the
  normative axis; they coexist without collapsing.

- Updates RecognitionOutcome shape with both axes.

- Marks all four subsystem audits complete in Framing 1 block;
  documents vault implementation debt (_status_admits conflation)
  and deferred candidate (COMPOSED_RECOGNITION).

- Records four Phase 2 implementation bugs in summary:
  evidence.py empty-pairs silent-FALSIFIED, runner DECODED-
  UNARTICULATED misclassification, domain_contract present=False
  inconsistency, _status_admits FALSIFIED/SPECULATIVE conflation.

Taxonomy is now stable. Phase 2 (bug fixes) and Phase 3 (first-class
state tagging in ChatResponse/TurnEvent) are the remaining work.
2026-05-24 10:13:42 -07:00
Shay
04e317245c
Merge pull request #217 from AssetOverflow/docs/pedagogy-review-and-teaching-backlog
docs(epistemic-state-taxonomy): extend starter taxonomy with 4 audit-derived states
2026-05-24 10:00:46 -07:00
Shay
9538c304ee docs(epistemic-state-taxonomy): extend starter taxonomy with 4 audit-derived states
Math-subsystem audit (40 decision points, 9 files) confirmed the 9-state
starter taxonomy was too coarse. Ratifies four new states surfaced as
EPISTEMIC_STATE_NEEDED gaps:

- EVIDENCED-INCOMPLETE: lift completed for a sub-span; proposition
  structurally partial but not contradicted (grounded by orphan-rate
  refusal in math parser)
- DECODED-UNARTICULATED: trace is replay-equal; surface realization
  path broke — exposes a runner misclassification where RealizerError
  on a verified trace is currently outcome="wrong"
- SCOPE_BOUNDARY: engine recognized the proposition type; refuses
  because the type is outside the current capability envelope, not
  because the lift failed
- COMPUTATIONALLY_BOUNDED: search budget exhausted before answers were
  enumerated; distinct from AMBIGUOUS (no answers compared) and
  UNDETERMINED (text likely has structure)

Also: updates primitives mapping table, marks Framing 1 audit complete
for math subsystem, closes the "too coarse" risk item (9 → 13 states),
and records the runner misclassification as a load-bearing correctness
issue for the next runner revision.
2026-05-24 09:58:39 -07:00
Shay
34cc345d7e
feat(ADR-0141): multiply as CGA dilator versor (positive non-zero) (#216)
* feat(ADR-0141): multiply as CGA dilator versor (positive non-zero)

Adds `multiply(scale)` to `generate/math_versor_arithmetic.py` as the
standard CGA dilator for multiplicative scaling along e1, restricted to
`scale > 0`.  All ten ADR-0141 assertion families pass.

Preliminary measurement confirmed:
  N = n_o ∧ n_inf: component -1 at index 15 (blade (3,4) = e4∧e5)
  N² = +1.0 (pure scalar) → closed-form D_s = cosh(α/2) + sinh(α/2)·N
  n_o · n_inf = -1;  n_o² = n_inf² = 0

Because N² = +1, the cosh/sinh expansion is exact in float64 and
D_s · ~D_s = cosh² − sinh² = 1 holds to machine epsilon.

The sandwich D_s·X·~D_s produces a null point with n_inf normalization
1/s.  `decode_quantity` is updated to divide by that factor, recovering
value · s.  For translator outputs (normalization = 1) the result is
identical to the previous direct e1 read; all 152 prior add/subtract
tests pass unchanged.

`embed_quantity` is updated to embed directly in float64, eliminating
float32 quantization error for values like 0.01 (float32(0.01) ≠ 0.01);
all prior test-case values were exactly representable in float32.

* docs(ADR-0141): add decision document for multiply-as-dilator spike

The ADR doc was drafted in a separate branch and not present when the
implementation worktree was created from origin/main. Adding it now so
the decision record lands on main with the implementation it specifies.

Content unchanged from the draft — same spec the implementation already
satisfies (10 assertion families, fixed test cases, falsification
discipline, deferred scope for negative / zero / divide / Rate).

No code or test changes in this commit.
2026-05-24 09:09:53 -07:00
Shay
622919019d
feat(ADR-0140): subtract as inverse translator + additive group closure (#215)
Extends generate/math_versor_arithmetic.py with one new function:

    def subtract(addend: float) -> np.ndarray:
        return translator(-float(addend))

Single-line delegate to translator(); no new algebra.

Adds tests/test_arithmetic_subtract_and_group.py covering all nine
ADR-0140 acceptance families:

  Families 1-6 (ADR-0139 families applied to subtract):
    1. Embedding well-formedness — null cone preserved for subtract cases
    2. Translator-of-negative well-formedness — versor_condition < 1e-6
    3. Closure — sandwich result stays on null cone
    4. Arithmetic correctness — decoded value == a − b within 1e-9
    5. Replay determinism — byte-identical across runs
    6. Composability — subtract(c) ∘ subtract(b) decodes to a − b − c

  New group-property families (structural verification of ADR-0139 claim):
    7. Inverse composition — T_{-b} * T_b = identity (max residual: 0.000e+00)
    8. Round-trip closure — versor_apply(T_{-b}, versor_apply(T_b, X)) → (a, u)
    9a. Sum composition — T_a * T_b = T_{a+b} (max residual: 0.000e+00)
    9b. Commutativity — T_a * T_b byte-equals T_b * T_a (all 10 cases)

All 96 tests pass. Group residuals are exactly 0.0 in float64.
The additive subgroup of Cl(4,1) translators along e1 is abelian and
closed; ADR-0139's algebraic claim holds at the group level.
2026-05-24 08:34:35 -07:00
Shay
589297b79a
feat(ADR-0139): arithmetic-as-versor spike — add closes exactly in Cl(4,1) (#212)
First step of the Engine A lift program (CLAUDE.md commits the project to a
single deterministic cognitive engine; Engine B / math pipeline was always
intentional scaffolding per math_solver.py:24). Proves the load-bearing
unknown: one arithmetic operation can be represented as a closed versor at
the required tolerance, with no new normalization and no weakened invariant.

Scope (frozen by ADR-0139):
- One operation: add
- Single-axis embedding: quantities on e1 axis
- No graph wiring, no pipeline integration, no GSM8K case routed
- Unit carried as caller metadata

Construction:
- embed_quantity(v, u) = embed_point([v, 0, 0])  (existing CGA primitive)
- translator(b)         = 1 - 0.5 * (b*e1 * n_inf)   (textbook CGA translator)
- decode_quantity(F, u) = (F[1], u)                  (e1 coordinate)

Measured values (all 11 fixed cases + composability):

      a         b      vcond(T)         |<R,R>|     decode_err
    0.0       0.0     0.000e+00       0.000e+00      0.000e+00
    0.0       1.0     0.000e+00       0.000e+00      0.000e+00
    1.0       0.0     0.000e+00       0.000e+00      0.000e+00
    3.0       4.0     0.000e+00       0.000e+00      0.000e+00
    7.0      -3.0     0.000e+00       0.000e+00      0.000e+00
   0.25      0.75     0.000e+00       0.000e+00      0.000e+00
    1.5       2.5     0.000e+00       0.000e+00      0.000e+00
   -5.0       5.0     0.000e+00       0.000e+00      0.000e+00
   -2.0      -3.0     0.000e+00       0.000e+00      0.000e+00
  100.0       1.0     0.000e+00       0.000e+00      0.000e+00
    1.0     100.0     0.000e+00       0.000e+00      0.000e+00
  compose (2, 3, 5) → 10:   |<R2,R2>| = 0.000e+00, decode_err = 0.000e+00

Every residual is exactly 0.0 in float64. The construction is algebraically
closed: T_t * reverse(T_t) = 1 - 0.25*B^2 where B = t*n_inf, and B^2 = 0
because (e14)^2 + (e15)^2 = -1 + 1 and cross-terms cancel. No machine-epsilon
drift accumulates because the relevant cancellation happens at the algebraic
level before float arithmetic.

ADR-0139 acceptance items 1-6 (one parametrized test family each):
  1. Embedding well-formedness   — test_family1_embedding_is_null         (11 cases)
  2. Translator well-formedness  — test_family2_translator_unit_versor    (11 cases)
  3. Closure                     — test_family3_sandwich_preserves_null   (11 cases)
  4. Arithmetic correctness      — test_family4_decode_matches_sum        (11 cases)
  5. Replay determinism          — test_family5_replay_byte_identical     (11 cases)
  6. Composability               — test_family6_two_translators_compose   (1 case)
  Total: 56 tests, all passing.

Lift program decision: proceeds. Follow-on ADRs (subtract, multiply, Rate,
compare, MathProblemGraph → PropositionGraph, pipeline integration, first
GSM8K case end-to-end through Engine A) are now justified by a concrete
algebraic foundation rather than design speculation.

Out of scope per ADR-0139:
- No modifications to algebra/, core/cognition/, chat/, math_solver.py,
  math_verifier.py, math_realizer.py, math_candidate_parser.py
- No GSM8K runner changes
- No pack changes
- Engine B continues serving GSM8K unchanged; the 3/50 admission set is
  preserved

CLI lanes intentionally not run — main has known test-rot orthogonal to
this PR. The 56 new tests are self-contained and the diff touches only
three new files.
2026-05-24 06:57:39 -07:00
Shay
0685c87e54
docs(ADR-0138): comparative-reference layer — design only (#209)
A small typed linguistic layer (FractionOperand, ComparativeOperand,
QuantityReference, ReferenceTarget) replacing the closed-and-deferred
ADR-0137 with a shape-driven design. Justified by the rescan-v3 ledger
showing 7 cases under fraction_operand + compound_comparative that
share a common deep structure (value relative to literal or
prior-sentence anchor) with multiple surface shapes.

Key commitments:
- Three typed phrase structures handle the closed surface set
  (frac/percent of X, N times as much/greater than X, frac more/less
  than X), where X is either a sentence-internal literal or a
  QuantityReference to a prior grounded quantity.
- Binding pass is minimal: sentence-order lookup of QuantityReference
  against prior grounded candidates with unique-match gating. No
  generic DeferredCandidate apparatus.
- Pinned English convention: "N times greater" = N+1 multiplier
  (documented + tested). Other reading requires explicit revisit.
- Supersedes ADR-0137 in scope; the deferral note's reopen criterion
  is met by this lighter, more specific design.
- S.1/S.2 short-circuits stay as joins of grounded candidates;
  this ADR does not subsume them.

Forcing case for cross-sentence binding: gsm8k-0029 ("three times
greater than the cost of the mouse" + "the mouse cost $16"). Clean,
unambiguous, two-sentence binding.

Self-correction caught before push: gsm8k-0010 was initially framed
as the forcing case, but its expected dataset answer requires a
non-standard English parse of "1/4 more than X" (the natural reading
gives 17; the dataset says 9, requiring X/4 + 7). Per non-negotiable
#2 the binding pass MUST refuse on this; 0010 is reclassified from
"expected admission" to "negative probe." 0029 takes the forcing-case
slot.

Honest expected admission delta: +1 to +2 (0005, 0029 high
confidence; 0041, 0043 partial credit only).

In scope: in the same PR family, fix the issue gsm8k-0029 surfaces by
implementing the binding pass. Out of scope: inverse arithmetic
(0004), multi-term aggregate references (0036), pronoun anchoring,
self-referential cycles.
2026-05-23 22:35:29 -07:00
Shay
2342564883
feat(ADR-0136.S.4): novel-initial-form parser extension + rescan v4 (#210)
S.4 extends initial-state parsing with two closed subject-slot widenings:
- Indefinite-article: `A <noun> has N <unit>` (gsm8k-0046 sentence 1)
- Prepositional-prefix existential: `In a <place>, there are N <unit>...`
  (gsm8k-0038 sentence 1)

Design choice: sibling regexes (_INITIAL_HAS_INDEF_RE,
_INITIAL_THERE_ARE_PREFIX_RE) rather than widening the global _ENTITY
pattern — preserves existing behavior across all other initial-state
extractors (cascade-safety).

Per the S.x corridor discipline: no new short-circuit; new candidates
flow through extract_initial_candidates and the existing graph machinery.
No solver/graph/verifier changes.

Honest delta:
- Direct admissions: 0 (admission set unchanged at {0014, 0018, 0042})
- Barrier shifts: +2 (gsm8k-0038: novel_initial_form → compound_comparative;
  gsm8k-0046: novel_initial_form → fraction_operand)
- wrong == 0 on every lane

Bundled with this PR for ledger currency:

1. tests/test_rescan_v3_invariants.py refactored to read frozen on-disk
   v3 artifacts only (no more re-running build_rescan against live
   parser). The previous design tied a historical snapshot to live code
   and broke the moment any new phase landed.

2. rescan_v4.py + refusal_rescan_v4.json + refusal_taxonomy_v4.json +
   tests/test_rescan_v4_invariants.py — the current live snapshot.
   Shifts: exactly 2 (0038, 0046). Same pattern as v3.

Sonnet wrote: S.4 parser/axis-lane/tests/ADR.
Opus wrote: rescan_v4.py + v3 test refactor + bundling.

Files:
- generate/math_candidate_parser.py (+142 lines)
- evals/math_capability_axes/S4_novel_initial_form/v1/ (20-case lane)
- tests/test_adr_0136_S4_novel_initial_form.py (40 tests)
- docs/decisions/ADR-0136.S.4-novel-initial-form.md
- evals/gsm8k_math/train_sample/v1/{rescan_v4.py, *_v4.json}
- tests/test_rescan_v4_invariants.py (8 tests)
- tests/test_rescan_v3_invariants.py (refactored to artifact-only)
2026-05-23 22:34:51 -07:00
Shay
a7feda3c19
audit(ADR-0136.S.3): refusal rescan v3 — exactly 1 barrier shift (gsm8k-0010) (#208)
Re-runs parse_and_solve on the 50-case GSM8K train sample on current
main (post-S.3) and compares to v2. Result: admitted=3/50 (unchanged),
wrong=0, exactly 1 barrier shifted v2→v3.

Shift: gsm8k-0010 (compound_statement → fraction_operand). S.3's
_INIT_MUTATION_RE resolves "Yun had 20 paperclips initially, but then
lost 12" to InitialPossession(Yun, 8, paperclips). First refusal moved
to sentence 2: "Marion has 1/4 more than what Yun currently has, plus
7" — needs fraction-operand + coreference-quantity + comparative-additive
arithmetic.

Top blockers (v3):
  compound_statement   5  (was 6)
  novel_initial_form   5  (unchanged)
  fraction_operand     4  (was 3 — gsm8k-0010 moved here)
  novel_initial_verb   4  (unchanged)

Artifacts:
- evals/gsm8k_math/train_sample/v1/rescan_v3.py
- evals/gsm8k_math/train_sample/v1/refusal_rescan_v3.json
- evals/gsm8k_math/train_sample/v1/refusal_taxonomy_v3.json
- docs/decisions/ADR-0136.S3-post-rescan.md
- tests/test_rescan_v3_invariants.py (7 tests; determinism + admission
  set unchanged + exactly-one-shift + 0010-specific shift assertions)
2026-05-23 22:05:16 -07:00
Shay
b448657c15
feat(ADR-0136.S.3): compound initial-mutation extractor — one shape, gsm8k-0010 barrier shift, wrong==0 (#207)
Closed-verb init-mutation extractor for "Entity had N unit, but then
verb M" canonical compound form. Produces derived InitialPossession
(N ± M) through existing graph machinery (no short-circuit).

Admission delta: 0 (gsm8k-0010 sentence 1 now extracts but sentence 2
fraction_operand blocks). Barrier shifted: 1 case (0010: compound_statement
→ fraction_operand). Axis lane: 24/24 pass, wrong=0. S.1 lane: unchanged.
GSM8K admission set: {0014, 0018, 0042} unchanged.
2026-05-23 21:58:55 -07:00
Shay
6e11145282
docs(ADR-0136.S.2): record ADR-0137 attempt + deferral (#206)
PR #204 (ADR-0137 retrospective binding, rescoped to subsumption-only)
was drafted then closed without merging. The deferral reason is
structural: the DeferredCandidate/BindingProof apparatus is the right
shape for true retrospective binding (first-pass candidate has open
slots awaiting later evidence), but re-examination showed neither S.1
nor S.2 short-circuit fits that definition — both join two fully
grounded candidates. With no kind that has open slots in the v2
ledger, the machinery would degenerate to a join everywhere, which is
speculative infrastructure ahead of a forcing function (CLAUDE.md
warns against this pattern).

Update appended to the post-rescan notes so the deferral lives in the
repo, not just in a closed PR thread. Includes a reopen criterion:
ADR-0137 (or successor) may return when a case appears whose first-pass
candidate has genuinely open slots only later-sentence evidence can
close. Until then the unlock vehicle is S.x.

The S.1/S.2 short-circuits remain as tactical bridges; the canonical
runner staleness is better fixed in the runner than in a binding
layer.
2026-05-23 21:55:48 -07:00
Shay
684481910b
audit(ADR-0136.S.2): refusal rescan v2 — barrier-shift ledger, subsumption directive pinned (#205)
Measurement-only branch. Re-runs parse_and_solve on all 50 GSM8K train-sample
cases against the current parser (post-S.1/S.2) and produces a barrier-shift
ledger comparing v1 taxonomy to current behavior.

Results: admitted=3/50 (0014, 0018, 0042), wrong=0, barrier_shifted=27/50.
Context-filler dominance collapsed from 23→3 cases; compound_statement (6)
and novel_initial_form (5) are now the largest buckets.

Subsumption directive pinned: ADR-0137 SHALL re-derive all short-circuit
admissions as (DeferredCandidate, evidence, BindingProof) triples.
2026-05-23 21:43:25 -07:00
Shay
e7a1ffb72e
feat(ADR-0136.S.2): conditional-op question — gsm8k-0042 admits, wrong==0 (#203)
Adds CandidateConditionalOpQuestion + extractor for the closed shape:
  "If <Entity> <verb> <N> <unit>, how many <unit2> does <Entity2> <aux> [<qualifier>]?"

In parse_and_solve, when the question yields exactly one such candidate
and exactly one matching InitialPossession exists by (entity, unit) across
all statement sentences, computes initial_value ± operand (verb polarity)
and emits when answer >= 0; refuses otherwise. Structurally identical to
S.1 capacity/earnings short-circuits.

GSM8K probe: 2/50 → 3/50 (+0042, answer=30.0), wrong stays 0.

- generate/math_candidate_parser.py: _COND_SUBTRACT_VERBS / _COND_ADD_VERBS
  closed sets; _COND_OP_Q_RE; extract_conditional_op_question_candidates
- generate/math_candidate_graph.py: short-circuit after earnings path
- tests/test_adr_0136_S2_conditional_op.py: 25 tests (extractor unit tests,
  end-to-end short-circuit, B3 + S.1 regression guards, post-S.2 honest
  admission count)
- docs/decisions/ADR-0136.S.2-conditional-op-question.md
2026-05-23 21:20:52 -07:00
Shay
52f2bf6f4c
feat(ADR-0136.S.1): rate/event statement parsing — capacity + earnings shapes, axis lane 20/20, wrong==0, gsm8k-0014 admits (#201)
* docs(ADR-0136.S.0): refusal taxonomy + S.1 brief for rate/event statement corridor

Taxonomy: deterministic classification of all 50 GSM8K train-sample refused cases
into primary + secondary barriers. Key findings:

  context_filler (primary): 23/50 — legitimately refuses; not parser gaps
  compound_statement:         5/50 — two ops in one sentence
  rate/capacity class:        4/50 — direct S.1 targets
  distributive_multiply:      1/50 primary, 5/50 secondary
  long-tail (diverse):       17/50

Honest S.1 ceiling: 0/50 → ≤4/50 admission. gsm8k-0014 ('Bob can shuck 10
oysters in 5 minutes') is the only case with capacity_rate as sole barrier.

Ships:
- evals/gsm8k_math/train_sample/v1/refusal_taxonomy.json (schema v1, 50 records)
- docs/briefs/parallel-2026-05-23/L17-ADR-0136-S1-rate-event-statements.md
- full briefs archive (parallel-2026-05-23)

No implementation changes. Taxonomy and brief only.

* feat(ADR-0136.S.1): rate/event statement parsing — capacity + earnings shapes, axis lane 20/20, wrong==0, gsm8k-0014 admits

Two closed statement shapes added to candidate parser and graph:

Shape A (capacity-rate): "<Actor> can <verb> N <unit> in M <time-unit>"
  - 13 closed verbs (shuck/pick/pack/make/produce/type/read/write/paint/run/score/answer/complete)
  - Pronoun question form (he/she/they/it) accepted
  - Time-unit conversion (second/minute/hour/day)

Shape B (earnings-rate): "<Actor> <verb> $N per/an/a <time-unit>"
  - 5 closed verbs (make/earn/receive/get/charge)
  - Currency: $ only, 0-2 decimal places
  - Per-token alternation: per/a/an/for each/every

Short-circuit paths in parse_and_solve run before the Cartesian product,
computing rate_per_sec × T_seconds directly. Actor mismatch → refusal
(not wrong). Answer ≤ 0 → fall through to refusal.

GSM8K honest delta: 0/50 → 1/50 (gsm8k-0014: answer=240.0, correct).
23 context-filler cases correctly remain refused.
Axis lane: 20/20 pass, wrong=0.
B3 bounded-grammar lane: unchanged (wrong=0).
35 new tests including B3 regression guard and GSM8K admitted_wrong=0 rail.
2026-05-23 20:36:01 -07:00
Shay
1a288ad017
docs(ADR-0136): statement-layer corridor, S.0 taxonomy, 2026-05-23 brief archive (#200)
* docs(ADR-0136): statement-layer corridor + S.0 taxonomy + resolve math-file conflicts

- Add ADR-0136-statement-layer-corridor.md: corridor overview, S.0-S.4 phase table,
  taxonomy summary (23 context-filler, 4 rate-class, 5 compound, 17 long-tail),
  standing invariants (admitted_wrong==0, context-filler safety rail, honest delta).
- Restore generate/math_*.py to HEAD (main) — stash-pop conflict markers were
  cosmetic comment rewording; upstream version is authoritative.
- Stage docs/reviewers.yaml math_expert_claims addition (already authored).

* docs(briefs): archive all 2026-05-23 parallel-dispatch briefs (L1–L17 + README)
2026-05-23 20:25:23 -07:00
Shay
beafeb5ad2 docs(ADR-0136): statement-layer corridor + S.0 taxonomy + resolve math-file conflicts
- Add ADR-0136-statement-layer-corridor.md: corridor overview, S.0-S.4 phase table,
  taxonomy summary (23 context-filler, 4 rate-class, 5 compound, 17 long-tail),
  standing invariants (admitted_wrong==0, context-filler safety rail, honest delta).
- Restore generate/math_*.py to HEAD (main) — stash-pop conflict markers were
  cosmetic comment rewording; upstream version is authoritative.
- Stage docs/reviewers.yaml math_expert_claims addition (already authored).
2026-05-23 20:21:32 -07:00
Shay
a32ffdc525
docs(ADR-0131.5): retire GSM8K probe as per-iteration gate after G.x completion (#199)
All five G.x capability axes shipped (G.1–G.5); admission remained 0/50
throughout — expected, because the 50 sample cases fail at the statement
layer (rate verbs, implicit entities, multi-step ops), orthogonal to the
question-layer and initial-state-verb work the G.x axes targeted.

Decisions:
- Probe retired as a per-iteration gate (signal-to-noise ≈ 0 at 0/50)
- `admitted_wrong == 0` invariant preserved as a standing architectural
  invariant, CI-pinned by existing test + per-axis regression guards
- Re-activation criterion named: statement-layer expansion that
  plausibly admits ≥1 new case (Δadmission_rate ≥ 0.02)
- Probe runner + report stay in-repo for future statement-layer push

Docs-only. No test, parser, solver, or eval changes.
2026-05-23 19:49:45 -07:00
Shay
7f67cea400
feat(ADR-0131.G.5): aggregate answer composition — combined/together cues wired, axis lane 20/20, wrong==0 (#197)
Closes the vocabulary gap: `combined` and `together` added to `_Q_TOTAL_RE`
and `_Q_ENTITY_RE` tail alternations. Both map to `entity=None` semantics;
the solver's existing sum path is unchanged.

Ships:
- Parser one-line regex extension (`generate/math_candidate_parser.py`)
- 20-case curated axis lane (`G5_aggregate/v1/`) — 5 shapes × 4 cues
- Runner + byte-equal report (20/20 pass, wrong=0)
- 25 tests covering cue vocab, 2/3-entity sums, degenerate aggregate,
  refusals, byte-equality, B3 regression guard, GSM8K safety rail
- ADR-0131.G.5

No admission movement on GSM8K probe (statement-parse bottleneck unchanged).
2026-05-23 19:42:55 -07:00
Shay
657c74102b
fix(ADR-0131.G.2): rebase + mastery hardening — quarter/third fraction anchors, gate regex, boundary refusals (#196)
Rebases onto current main (dec98ea, post-G.1/G.3.1/G.4/promotion).

Parser:
- Extend _COMPARE_MULT_ANCHOR_RE anchor alternation to include 'quarter'
  and 'third'; add optional 'a\s+' article prefix so "a quarter as many"
  and "a third as many" parse. Both anchors are in COMPARE_MULTIPLICATIVE_ANCHORS
  and the round-trip factor-divisor table ("quarter":4, "third":3), so
  round-trip checks pass. quarter→0.25 (exact), third→1/3 (float).
- Add _ANCHOR_TO_FACTOR entries for quarter and third.

Gate regex (test_adr_0131_G2_comparatives.py):
- Widen _COMPARATIVE_STATEMENT_PATTERNS multiplicative pattern from
  '\d+\s+times' to '\w+\s+times' to match word-number forms ("four times")
  that would be missed by the digit-only pattern if a future GSM8K case
  contains one in a still-refused statement.

Cases (31 total, was 24):
- G2-mul-frac-005/006: two 'quarter' cases (fraction direction now has
  half×4 + quarter×2 + third×1 = 7 cases, was 4 all-half).
- G2-mul-frac-007: 'third' case.
- G2-refuse-006: hyphenated 'one-third' pins the closed-anchor boundary.
- G2-refuse-007: 'double as many' pins the deferred grammar shape.

Tests (25, was 21):
- Add quarter and third parametric entries to test_multiplicative_direction_admits.
- Add one-third and double-as-many refusal params to test_refusal_cases.
- Add quarter/third to test_direction_literals_closed_set.
- Update test_runner_per_category_minima comment to reflect new counts.

ADR: document quarter/third admission, updated case table, deferred list.
report.json: refreshed to 31 cases, wrong==0 preserved.
2026-05-23 19:28:09 -07:00