Compare commits

..

No commits in common. "main" and "define-compute-budget-policy" have entirely different histories.

1142 changed files with 8351 additions and 51337 deletions

View file

@ -1,3 +0,0 @@
# This directory is gitignored (see .gitignore: .data/benchmarks/).
# Only this .gitkeep and manifest YAMLs under evals/generalization/manifests/ are committed.
# Raw dataset files must never be committed to the repo.

2
.github/FUNDING.yml vendored
View file

@ -1,2 +0,0 @@
github: [AssetOverflow]
custom: ["https://opencollective.com/assetoverflow-core"]

View file

@ -26,7 +26,7 @@ The cognitive path is centered on:
- `teaching/correction.py`, `teaching/review.py`, `teaching/store.py`
- `evals/*`
- `calibration/*`
- `packs/data/en_core_cognition_v1`
- `language_packs/data/en_core_cognition_v1`
The runtime response contract is documented in `docs/runtime_contracts.md`.
Follow it.
@ -42,7 +42,7 @@ versor_condition(F) < 1e-6
Allowed construction/closure sites:
- `ingest/gate.py`
- `packs/compiler.py` / vocabulary construction
- `language_packs/compiler.py` / vocabulary construction
- `algebra/versor.py`
Forbidden hot-path repair sites:

View file

@ -40,7 +40,7 @@ jobs:
- name: set up uv
uses: astral-sh/setup-uv@v5
with:
python-version: '3.12.13'
python-version: '3.11'
enable-cache: true
- name: install dependencies

View file

@ -38,7 +38,7 @@ jobs:
- name: set up uv
uses: astral-sh/setup-uv@v5
with:
python-version: '3.12.13'
python-version: '3.11'
enable-cache: true
- name: install dependencies

View file

@ -33,34 +33,32 @@ jobs:
with:
fetch-depth: 1
# setup-uv (not actions/setup-python) provisions Python on the aarch64
# self-hosted runner; actions/setup-python has no arm64 build for the
# pinned 3.12.13. Matches smoke.yml / full-pytest.yml.
- name: set up uv
uses: astral-sh/setup-uv@v5
- name: set up python
uses: actions/setup-python@v5
with:
python-version: '3.12.13'
enable-cache: true
python-version: '3.11'
cache: 'pip'
- name: install dependencies
run: |
uv pip install -e . pyyaml pytest
python -m pip install --upgrade pip
pip install -e . pyyaml pytest
- name: verify lane SHAs
env:
PYTHONPATH: ${{ github.workspace }}
run: |
uv run python scripts/verify_lane_shas.py
python scripts/verify_lane_shas.py
- name: verify CLAIMS.md is current
env:
PYTHONPATH: ${{ github.workspace }}
run: |
uv run python scripts/generate_claims.py --check
python scripts/generate_claims.py --check
- name: emit machine-readable report (on failure)
if: failure()
env:
PYTHONPATH: ${{ github.workspace }}
run: |
uv run python scripts/verify_lane_shas.py --json || true
python scripts/verify_lane_shas.py --json || true

View file

@ -66,7 +66,7 @@ jobs:
- name: set up uv
uses: astral-sh/setup-uv@v5
with:
python-version: '3.12.13'
python-version: '3.11'
enable-cache: true
- name: install dependencies

View file

@ -37,7 +37,7 @@ jobs:
- name: set up uv
uses: astral-sh/setup-uv@v5
with:
python-version: '3.12.13'
python-version: '3.11'
enable-cache: true
- name: install dependencies

7
.gitignore vendored
View file

@ -26,10 +26,6 @@ uv.lock
reports/
frontier_wave1.json
# Benchmark local cache — manifests + fetch script only; benchmark data never committed
# (extends ADR-0119.7 sealed-holdout discipline to generalization audits)
.data/benchmarks/
# Workbench UI browser-test artifacts
workbench-ui/test-results/
workbench-ui/playwright-report/
@ -85,6 +81,3 @@ skills-lock.json
# Per-life backup checkpoint dirs are runtime-generated, not source.
engine_state/_life_backup_*/
# Local MCP configuration (contains local tokens)
.mcp.json

View file

@ -1,8 +0,0 @@
# CORE + builder-II local agent hints
- temperature 0 everywhere
- Read AGENTS.md, GROK.md, docs/runtime_contracts.md before edits
- Proposals are SPECULATIVE until `builder verify` passes
- Use skills: core-governed-coding, core-verify-loop, core-pre-edit-sweep, core-handoff
- Slash: /explore /implement /review /verify /handoff /plan
- Switch model: builder switch-model fast|primary (one model on M1 16GB)
- versor_condition(F) < 1e-6 — refuse cosine/ANN/HNSW in vault

View file

@ -1 +0,0 @@
3.12.13

474
AGENTS.md
View file

@ -1,36 +1,39 @@
# CORE Agent Instructions
This is the canonical governance file for this repository.
This repository is building a deterministic cognitive engine, not a transformer
wrapper and not a demo chatbot. Every agent must preserve the geometric
runtime while moving the system toward teachable cognitive chat.
If any provider-specific file (`CLAUDE.md`, `GEMINI.md`, or future agent files) overlaps with this document, `AGENTS.md` wins. Provider files should only contain minimal startup and workflow notes, not alternate architecture or alternate invariants.
## Agent-Specific Instruction Files
## Session Continuity (lightweight, session-break only)
Different agents read a supplementary file alongside this one. Read yours
before touching any code:
When you are approaching a stopping point, known pause, or session break:
- Create a file named `session-break-summary-<YYYY-MM-DD-HHMM>.md` (precise datetime recommended) at the repo root or in `docs/sessions/`.
- Keep it short and actionable: current branch/state, what was just completed, exact next concrete steps, any open invariants/tests/hazards, and key files to re-read.
- At the **start of any new session** (or subagent): Quickly scan for any recent `session-break-summary-*.md` files. Read the most relevant one if present.
- Once you have resumed the work and continued past the break point, **delete the file**. Its only purpose is temporary continuity for the immediate next pickup.
| Agent | Supplementary file | Key differences |
|---|---|---|
| **Claude** | `CLAUDE.md` | Deep context; self-restraining; read for semantic anchoring rule nuance |
| **Grok 4.3 + Grok Build** | `GROK.md` | Stateless; requires high reasoning effort; mandatory workspace hygiene; Arena/parallel subagent rules; Plan Mode preferred; skills system; see also docs/core-rd-base-prompts.md for phase-specific prompts |
| **GPT-5.5 (o3-class)** | `GPT55.md` | Stateless; fluency cautions; extended thinking for algebra/field work |
The previous heavy `HANDOFF-*.md` / formal handoff machinery is retired (see history in git and docs/handoffs/ for old artifacts).
If you are Grok 4.3 or GPT-5.5, complete the Session Start Checklist in your
file before reading anything else in this file.
## Mission
## Grok 4.3 / Grok Build Hard Stops (Mastery Level)
CORE is a deterministic cognitive engine under construction.
These apply to Grok 4.3 and Grok Build in addition to every rule below:
It is:
- inspectable
- replayable
- evidence-governed
- coherence-first
1. **You are stateless.** Read `GROK.md` in full, `docs/runtime_contracts.md`, and the most recent `HANDOFF-*.md` (if dated within 3 days) before any edits.
2. **Workspace hygiene is mandatory.** Before branch movement or edits, confirm cwd/repo root, inspect dirty state, classify loose files, fetch/prune, establish clean current `main`, and use a fresh worktree for non-trivial implementation.
3. **High reasoning effort is mandatory** for all tasks touching `algebra/`, `field/`, `generate/realizer.py`, `generate/graph_planner.py`, `generate/intent.py`, `vault/store.py`, `calibration/`, `core/cognition/`, or `teaching/`.
4. **Use Plan Mode** (Grok Build) for any non-trivial change in the above modules. Direct edits are discouraged.
5. **Skills are the preferred mechanism** for repeated protocols. Use `/core-bootstrap`, `/versor-coherence-guardian`, `/pre-edit-sweep`, and `/claim-proposal-guardian` (or their auto-triggered versions).
6. **Sweep before you edit.** Use tool-call chains to trace imports and call sites.
7. **Write a handoff doc at session end** using `docs/handoff_template.md`.
8. **Arena / parallel subagents:** each subagent independently satisfies `||F * reverse(F) - 1||_F < 1e-6` before reporting. Reconcile results before any merge. No mutable state sharing.
It is not:
- a transformer wrapper
- a generic chatbot
- an infrastructure playground
- a stochastic fallback shell
---
## North star
## North Star
CORE should become capable of:
@ -38,229 +41,241 @@ CORE should become capable of:
listen -> comprehend -> recall -> think -> articulate -> learn from reviewed correction -> replay deterministically
```
The live path is:
The current path is intentionally staged:
```text
CognitiveTurnPipeline
-> tokenize / OOV policy / inject
-> intent classification
-> PropositionGraph
-> ArticulationTarget
-> deterministic realizer / articulation surface
-> telemetry / trace
-> reviewed teaching capture when applicable
-> deterministic replay / eval / calibration
```
1. Maintain algebra/runtime invariants.
2. Use `CognitiveTurnPipeline` as the spine.
3. Classify intent and build proposition graphs.
4. Plan articulation targets and realize them deterministically.
5. Capture reviewed teaching corrections safely.
6. Seed compact semantic packs for cognition vocabulary.
7. Evaluate through CLI lanes, not ad hoc test fragments.
8. Calibrate bounded operators only from replayable evidence.
Improve CORE by strengthening this path, not by bypassing it.
Do not skip ahead by adding opaque models, stochastic generation, or broad
infrastructure that hides whether CORE itself is improving.
## Non-negotiable invariants
## Philosophical and Architectural Stance
Truth is coherent. CORE's work is to preserve coherent structure from input to
field state to articulation to memory. Treat identity, truthfulness, and
replayability as architectural commitments rather than prompt preferences.
The system's intelligence should come from inspectable geometric state,
structured propositions, deterministic recall, reviewed teaching, and bounded
calibration. Avoid nihilistic or purely statistical framing in code comments,
agent plans, and docs. Prefer responsibility, provenance, and stable meaning.
## The Hard Field Invariant
### Field invariant
Every runtime field state `F` must satisfy:
```text
versor_condition(F) < 1e-6
```
Do not weaken this threshold to make code or tests pass.
Fix the operator or construction boundary that violated it.
This is checked by `algebra/versor.py::versor_condition()`.
### Allowed normalization boundaries
Normalization / closure / canonicalization belongs only at explicit construction or algebra boundaries, such as:
- `ingest/gate.py`
- `packs/compiler.py`
- `algebra/versor.py`
- `sensorium/*/canonical.py`
- `session/context.py` for session-scoped **semantic anchoring** of the field toward the session concept-attractor (the anchor pull, hemisphere consistency). Allowed ONLY because every such op (1) preserves `versor_condition` BY CONSTRUCTION — composed from `rotor_power` / `word_transition_rotor` / `versor_apply` on the Spin manifold, never a post-hoc `unitize`/grade-projection — AND (2) carries semantic meaning in the cognitive model.
- other explicitly documented construction boundaries
If a propagation path violates this invariant, fix the operator path or the
explicit algebra/construction boundary that owns the transition. Do not hide
violations by changing tests, silently weakening thresholds, or normalizing in
hot-path modules.
## Normalization and Closure Rules
Allowed closure/construction boundaries:
- `ingest/gate.py` for raw prompt injection.
- `language_packs/compiler.py` / vocabulary construction.
- `algebra/versor.py` where algebraic sandwich output closure belongs.
Forbidden hot-path repair sites:
Forbidden in hot paths and repair layers, including:
- `generate/stream.py`
- `field/propagate.py`
- `vault/store.py`
- logging / telemetry / shell glue
- runtime telemetry/logging layers
**The bright line — semantic anchoring vs. drift repair.** An op is *semantic anchoring* (allowed at the sites above) iff it preserves `versor_condition` by construction AND expresses a relation in the cognitive model. It is *drift repair* (forbidden) iff its purpose is to restore a numerical invariant a prior function should have preserved. Closure of field transitions is owned solely by `algebra/versor.py` (`_close_applied_versor`); no other site may "fix" it. Naming must not disguise the distinction: an op that anchors semantically must not be named or documented as a "drift fix".
Do not add normalization, unitization, grade projection, drift monitors, repair
timers, or watchdog functions outside a documented construction/algebra boundary.
If you think you need one, an upstream operator is unclosed.
Do not add drift repair, watchdog normalization, hidden unitization, or post-hoc algebra fixes outside owned boundaries.
CGA null vectors are geometric points and must remain null. Do not force null
vectors into unit-versor closure.
### Exact recall
Runtime recall remains exact and deterministic.
Do not add:
- cosine similarity
- ANN / approximate nearest neighbor
- HNSW
- embedding ranking as runtime memory truth
## The Two Core Primitives
Use exact CGA recall primitives only.
Field transition:
### No opaque fallback cognition
Do not add stochastic generation, hidden LLM fallback logic, or probabilistic substitutes inside the deterministic cognitive path.
```text
algebra/versor.py::versor_apply(V, F) -> V * F * reverse(V)
```
### Teaching and mutation safety
Learning is controlled mutation.
- session memory may be local and immediate
- reviewed/durable memory goes through the teaching path
- pack mutation is proposal-only until reviewed
- identity override attempts are rejected, not learned
Distance/recall metric:
Do not invent a parallel learning path.
```text
algebra/cga.py::cga_inner(X, Y)
```
#### The learning boundary is typed, not "everything is proposal-only"
A common misreading treats *all* learning as proposal-only. That is a false bottleneck. The real boundary is between **durable** standing and **provisional** standing, and it is already mechanically enforced:
- **Durable mutation stays reviewed or proof-carrying.** Corpus / pack / policy / identity changes, and any promotion to COHERENT/verified standing, go through the reviewed teaching loop (`teaching/*`, proposal-only) or the proof-carrying promotion gate.
- **Provisional state may update autonomously — iff typed, isolated, replayable, and unable to masquerade as ratified truth.** This covers session memory, sealed practice ledgers, SPECULATIVE idle consolidation of soundly-derived facts, reliability-ledger counts, proposal emission, and disclosed licensed estimates. Each is written SPECULATIVE (never COHERENT), through the same `VaultStore.store` path (no parallel memory), deterministically, and carries its standing honestly.
Do not add ANN, HNSW, cosine similarity, approximate nearest-neighbor recall,
or non-CGA ranking to runtime memory. Vault recall is exact and deterministic.
This boundary is a set of failing-when-violated invariants, not a convention:
- **INV-21** — only allowlisted modules may call `VaultStore.store(...)`.
- **INV-22 / INV-23** — an unmarked pack row and an unmarked `store()` default to SPECULATIVE; COHERENT requires an explicit stamp.
- **INV-24** — every `vault.recall` callsite is categorized; user-facing evidence must pass `min_status=COHERENT`.
- **INV-29** — only `vault/store.py` may transition an `epistemic_status`.
- **INV-30** — the open-world `determine()` gear constructs only `Determined(answer=True)` or refuses; it can never assert `answer=False`. Closed-world entailed-negation must use a distinct closed-world type and entry point.
## Current Runtime/Cognition Shape
### Kernel substrate rule
New derivation work should consume `KernelFacts` / `ProblemFrame` where the substrate can represent the meaning.
Do not introduce new local prose parsers inside derivation organs unless explicitly marked as legacy exception with migration rationale.
The live cognitive path is now:
## Working doctrine
```text
ChatRuntime / CognitiveTurnPipeline
-> tokenize / OOV policy / inject
-> intent classification
-> PropositionGraph
-> ArticulationTarget
-> deterministic realizer / articulation surface
-> generation walk telemetry
-> identity + energy telemetry
-> reviewed teaching capture when correction intent appears
-> deterministic trace hash
```
Before editing:
1. Read this file.
2. Read `docs/specs/runtime_contracts.md`.
3. Check for any recent `session-break-summary-*.md` files (see top-level section above) and read the relevant one if present.
4. Confirm repo root and inspect working tree state.
5. Run the smallest relevant validation lane.
Important modules:
For non-trivial edits:
- trace imports and call sites first
- identify the invariant being protected
- prefer semantics-preserving cleanup before new mechanisms
- keep changes small and load-bearing
- If working in Arena/parallel subagent mode, each subagent must independently satisfy `versor_condition` and results must be reconciled before merge. No subagent output becomes another subagent's unchecked input.
- `core/cognition/pipeline.py` — cognitive turn spine.
- `core/cognition/result.py` — canonical turn result shape.
- `core/cognition/trace.py` — deterministic trace hashing.
- `generate/intent.py` — deterministic intent classification.
- `generate/graph_planner.py` — proposition graph and articulation target planning.
- `generate/realizer.py` / `generate/templates.py` — deterministic realization.
- `teaching/*` — reviewed teaching/correction lifecycle.
- `language_packs/data/en_core_cognition_v1` — compact cognition seed pack.
- `evals/*` — deterministic cognition evidence harness.
- `calibration/*` — bounded replay-based operator calibration.
- `docs/runtime_contracts.md` — runtime response, memory, identity, and testing contracts.
## Reasoning and Problem-Solving Discipline
## Efficiency and Performance Doctrine
LLMs are not reliably intelligent by default. CORE exists partly to fix that.
Agents working in this repository must hold themselves to the following protocol
on every non-trivial task. Skipping steps produces confident-sounding work that
is wrong in load-bearing ways.
Performance is an architectural property. Do not treat it as an afterthought
that will be cleaned up after features land.
### The Protocol
Before modifying hot paths, identify whether the change touches:
**1. Read the code — never reason from names or structure alone.**
Before forming any opinion about a module, read its implementation. Trace its
imports and call sites. Identify what invariant it is protecting. A file named
`pass_manager.py` tells you nothing until you have read it.
- algebra backend dispatch (`algebra/backend.py`)
- versor application / closure (`algebra/versor.py`)
- propagation (`field/propagate.py`)
- injection / OOV grounding (`ingest/gate.py`)
- vault recall/storage (`vault/store.py`)
- session turn loop (`session/context.py`)
- runtime/eval loops (`chat/runtime.py`, `core/cognition/*`, `evals/*`)
**2. Find the shape — what underlying structure does this problem have?**
Before proposing a solution, identify the repeating structure the problem
expresses. The solution should make that structure visible, not paper over it.
Duplication is a symptom; the cause is an unnamed shape.
Required approach:
**3. Rank by leverage — genius-to-effort, not ease.**
When multiple improvements are possible, rank them explicitly by how much
cognitive/structural load they remove vs. how much effort they require. Implement
in that order. An agent that implements low-leverage changes first and skips
high-leverage ones has optimized for the wrong thing.
1. Prefer semantics-preserving cleanup before new knobs.
2. Route hot-path algebra through `algebra.backend` when semantics are identical.
3. Hoist repeated imports and repeated structure-building out of tight loops.
4. Cache only deterministic, immutable, or safely copied structures.
5. Keep exact CGA recall exact; optimize scans with batching/vectorization, not approximation.
6. Prove speed-oriented changes through existing CLI lanes and, when practical, small benchmark/eval evidence.
**4. Enumerate changes precisely — no ambiguity about what goes where.**
Before committing, state every change, which file it lives in, and why. The
commit message must reflect this. Vague commits ("refactor", "cleanup") are
not acceptable on load-bearing modules.
Never improve speed by:
**5. Prove against real claims — not abstract correctness.**
"Tests pass" is not proof. Identify which specific pinned assertion in
`CLAIMS.md` the change must preserve or enable. State the SHA-256 lane or
`core test --suite` invocation that verifies it. If no existing lane covers
the change, say so explicitly — that is itself a finding.
- weakening `versor_condition` thresholds
- skipping closure checks at construction boundaries
- adding hot-path repair/normalization
- replacing exact CGA with cosine/ANN/HNSW
- hiding failures behind retry loops without telemetry
- mutating shared cached state unsafely
**6. Connect to the cognitive model — what does this do for the system's reasoning?**
Every non-trivial change must be articulable in terms of what it does for
CORE's actual cognition path:
`listen → comprehend → recall → think → articulate → learn → replay`
If you cannot state what cognitive property the change strengthens, the change
is not yet understood well enough to ship.
For test speed, prefer better validation lanes, small-case eval tests, fixture reuse where safe, and pack/load caching with immutability guarantees. Do not delete meaningful tests just because the full suite is slow.
**7. Commit with discipline — right branch, right invariant, right lane.**
Confirm repo state and branch before every commit. Never commit directly to
`main` unless the change is documentation or governance (like this one).
State which invariant the change protects. Run the smallest validation lane
that proves the change before declaring it done.
## Security and Trust-Boundary Doctrine
### The Failure Modes This Prevents
Every agent must identify user-controlled input and dynamic execution surfaces.
Security hardening should be built into the same PRs that touch those surfaces.
- Reasoning from file names instead of reading the code → wrong analysis
- Proposing solutions before finding the underlying shape → solutions that
recreate the same problem in a different form
- Implementing easy changes first → high-leverage work never gets done
- Vague success criteria → regressions that pass "tests" but break real claims
- Shipping changes that can't be connected to the cognitive model → architectural
drift away from CORE's mission
High-risk surfaces:
## Repository topology discipline
Before calling a directory, module, or file stale/redundant, classify its
intrinsic role:
- runtime boundary
- candidate/provisional compiler
- reviewed pack or corpus data
- read-only Workbench/API projection
- standalone demo envelope
- benchmark/eval/report artifact
- historical note or handoff
- script/tooling surface
- `core pack validate` dynamic validator execution
- language/source pack loading
- OOV token grounding and logs
- CLI commands that echo user input
- report/eval output paths
- pack mutation proposals
- any future file/network/database integration
Then verify with `rg` imports/callers, tests, docs/ADR references, and CLI
routes before moving or deleting anything. A file is not dead merely because it
is platform-specific, optional, generated-adjacent, or outside `core/`.
Required approach:
When an intentional split exists, make the boundary local and enforceable:
- add or update a short `README.md` at each side of the split;
- state what owns mutation, what is read-only, and which validation lane proves it;
- add or extend a lightweight hygiene/doctor/package test when drift is likely;
- keep artifact namespaces non-executable unless they are deliberately promoted
to packages or moved under `scripts/`.
1. Make arbitrary-code execution explicit and opt-in.
2. Reject path traversal and unsafe pack IDs before filesystem access.
3. Centralize display/log handling for user-controlled strings when expanding logging.
4. Keep pack mutation proposal-only unless an explicit reviewed path applies it.
5. Avoid leaking raw sensitive tokens in errors/reports unless the command is explicitly local/debug.
6. Preserve deterministic replay evidence for security-relevant decisions.
Package and CLI changes must check fresh-install visibility, not just source-tree
imports. Use `core doctor`, package include tests, and wheel inspection when a
new top-level package or CLI-imported module is added.
Do not add hidden background execution, dynamic imports from untrusted paths, shell passthroughs, or broad filesystem writes without an explicit trust boundary and tests.
### Workspace Hygiene + Branch Protocol
Before branch movement or edits:
- Confirm cwd/repo root.
- Inspect dirty state (`git status`, `git diff`); classify loose files before stashing or deleting.
- Establish a clean current `main`.
- Prefer a fresh worktree from `origin/main` for non-trivial implementation.
## Chat Surface Contract
### Git and Forgejo Setup
**CRITICAL**: This repository is hosted on a private **Forgejo** server, NOT GitHub. We are explicitly deprecating GitHub usage.
Our sole remote and CI/CD platform is **core-gitquarters.acbcontent.org**.
- **DO NOT** use the `gh` (GitHub) CLI.
- **DO NOT** attempt to push, pull, or clone from `github.com`.
- **USE** the provided Forgejo MCP tools if available.
- If the Forgejo MCP tools are not available or not working, **attempt utilizing the `gitea` / `tea` CLI or `forgejo` CLI** for issues, PRs, and repository management targeting `core-gitquarters.acbcontent.org`.
Do not collapse these fields:
### Pre-Edit Sweep & Versor Coherence Guardian Protocol
Before modifying any module in `algebra/`, `field/`, `vault/`, or `generate/`:
- Trace every import of the target module and identify all callers.
- Check `calibration/` and `evals/` for tests that exercise the changed path.
- Explicitly confirm the core invariant `||F * reverse(F) - 1||_F < 1e-6` holds for the affected state.
- `surface` — selected user-facing response.
- `walk_surface` — raw manifold/token-walk evidence.
- `articulation_surface` — proposition/realizer surface.
## Documentation Discipline
Current policy:
ADRs, session docs, audit artifacts, and temporary session-break summaries stay as Markdown (GitHub-flavored). Plain-text artifacts are diffable, greppable, and readable by every agent in the dispatch pipeline.
```text
surface = articulation_surface
walk_surface = retained telemetry/evidence
```
Within Markdown, two GitHub-rendered features are sanctioned and otherwise sparingly used:
- Mermaid fenced blocks (` ```mermaid `) when a state machine, sequence, or dependency graph genuinely communicates more than prose. Inline, not in a sidecar file.
- `<details>` / `<summary>` collapsibles to fold long proofs, large tables, or generated logs without losing single-file context.
If this changes, update `docs/runtime_contracts.md` and contract tests in the
same PR.
Out of scope:
- Standalone HTML artifacts with embedded CSS / inline SVG / sidebar navigation.
- Dashboards, status pages, or visualizers as a substitute for a pinned data artifact. If a visualization is load-bearing, the underlying data must live in a deterministic JSON/JSONL/Markdown artifact first.
## Teaching and Memory Safety
## Validation lanes
Learning is controlled mutation, not storing everything.
Use the CLI lanes as the standard validation surface:
Rules:
- Session memory can be immediate and local.
- Reviewed memory must go through the teaching loop.
- Pack mutation is proposal-only until reviewed.
- User correction must not mutate identity axes, runtime policy, or operator code.
- Identity override attempts must be rejected, not learned.
Use the teaching modules for correction capture/review/store. Do not invent a
parallel correction mechanism inside chat runtime or generation.
## Semantic Pack Rule
Use compact, curated semantic packs. Do not dump broad corpora into runtime.
The core cognition seed pack is meant to provide thought vocabulary, operations,
and relation predicates, not to impersonate large-scale pretraining.
Manifest checksums must be computed from bytes actually written to disk:
```python
checksum = hashlib.sha256(Path(lexicon_path).read_bytes()).hexdigest()
```
Never compute a manifest checksum from a pre-serialization Python string.
## Development Priorities
Current capability sequence:
1. Keep CLI test suites and `core eval cognition` green.
2. Tighten hot-path backend consistency and semantics-preserving performance.
3. Harden pack/OOV/logging trust boundaries.
4. Add exact vault recall indexing/batching without approximate search.
5. Add Rust backend parity only after Python semantics are locked by tests.
6. Expand curriculum teaching only after replay/eval/calibration remain deterministic.
Do not add dashboards, broad infra, or large test matrices unless they directly
protect or unlock one of the above capabilities.
## Test Discipline
Use the CLI lanes as the standard validation interface:
```bash
core test --suite smoke -q
@ -273,38 +288,63 @@ core test --suite full -q
core eval cognition
```
Run the smallest relevant suite first.
Run broader suites before merge when the change touches runtime, algebra, cognition, teaching, packs, or trust boundaries.
For targeted work, run the smallest relevant suite first, then `full` before
merge when practical.
## Security and trust boundaries
Good tests protect:
Any change touching user-controlled text, files, dynamic imports, pack loading, validators, logs, or report output must state its trust boundary.
- versor closure
- deterministic replay / trace hash stability
- runtime surface contracts
- exact memory/recall behavior
- identity protection
- reviewed correction safety
- semantic pack loadability and deterministic ordering
- eval/calibration determinism
- hot-path performance semantics
- explicit security trust boundaries
Required defaults:
- explicit opt-in for arbitrary execution
- reject unsafe paths before filesystem access
- centralize safe display/log handling
- no hidden background execution
- no broad filesystem mutation without explicit boundary and tests
Bad tests preserve private helper shapes, stale constructors, punctuation trivia
outside documented contracts, or legacy behavior that contradicts the current
architecture.
## PR checklist
## PR Standard
Before merge, answer:
Every PR must answer:
```text
What capability, performance property, or security boundary did this add or protect?
Which invariant proves the field remained valid?
Which validation lane proves the change?
Did this avoid hidden normalization, stochastic fallback, approximate recall, and unreviewed mutation?
If it touched user input, files, dynamic imports, or logs, what trust boundary was enforced?
What cognitive capability, performance property, or security boundary did this add or protect?
What invariant proves it did not corrupt the field?
Which CLI suite/eval proves the relevant lane?
Did it avoid hidden normalization, stochastic fallback, approximate recall, and unreviewed mutation?
If it touches user input, files, dynamic imports, or logs, what trust boundary was enforced?
```
## Provider-file policy
Prefer small, load-bearing PRs. Do not mix baseline fixes, feature work, and
large reorganization unless the coupling is unavoidable.
`CLAUDE.md`, `GEMINI.md`, and any future provider file must:
- be short
- stay under 600 bytes unless there is a tool-specific reason reviewed in `AGENTS.md`
- point here as canonical
- avoid duplicating architecture
- avoid introducing provider-only truth
- differ only where tool startup behavior genuinely requires it
## Kernel Substrate / No-New-Legacy Rule
After PR #829, the preferred math comprehension construction path is:
```text
raw problem text → KernelFacts → ProblemFrame → contract-backed derivation organs
```
- Use `generate/problem_frame_builder.py::build_problem_frame` for substrate-backed
fact extraction (scalars, units, hazards, process-frame candidates).
- New derivation capabilities must consume ProblemFrame facts where the substrate can
represent the needed meaning.
- New raw-prose/local-regex parsing inside a derivation organ requires an explicit
`LEGACY_EXCEPTION` comment and migration rationale.
- Guard test: `tests/test_kernel_no_new_legacy_derivation_surfaces.py`.
- Audit map: `docs/analysis/kernel-substrate-deprecation-audit-2026-06-18.md`.
Do not add another isolated benchmark organ with a local prose parser.
## Architecture in One Sentence
Raw input becomes a closed versor field once; thought evolves through exact
versor transitions and CGA recall; cognition is structured as intent,
proposition graph, articulation target, deterministic realization, reviewed
memory, eval/calibration replay, and traceable evidence.

View file

@ -38,8 +38,8 @@ is a CI failure (`.github/workflows/lane-shas.yml`).
| ADR-0093 | `domain_contract_validation` | All ratified packs satisfy the 9 ADR-0091 contract predicates | `evals/domain_contract_validation/results/v1_dev.json` | `98ace04e3f02bbc5a8ad655bb6593c3f1ee64cb67014f1122fe6c3c85f48d22f` |
| ADR-0095 | `miner_loop_closure` | Miner-sourced proposals route through single reviewed teaching path | `evals/miner_loop_closure/results/v1_dev.json` | `9f071733abe7dcacf759f928548ce738fb639af3fd6e4c621a651b306d7e77ce` |
| ADR-0096 | `fabrication_control_summary` | Phantom endpoints / cross-pack non-bridges / sibling collapses refuse | `evals/fabrication_control/results/v1_summary.json` | `01e1b6b711141f2b4a14551d7df3ea482d8d6dd7b364a25c509f4f8d08cda8a8` |
| ADR-0098 | `demo_composition` | Demos compose from shipped modules; no parallel mechanism | `evals/demo_composition/results/v1_dev.json` | `5594d4c0b919dfa33256c54b5730f3291a4832f96422e8831244d0c99723f6e0` |
| ADR-0099 | `public_demo` | Public showcase runs deterministically under 30s; all claims supported | `evals/public_demo/results/v1_dev.json` | `ed1668a64490f73f4d9b701e611e07841c149fd36cb90703436e3e33732fcd76` |
| ADR-0098 | `demo_composition` | Demos compose from shipped modules; no parallel mechanism | `evals/demo_composition/results/v1_dev.json` | `3a3d09f3a87462737e615c2dd3481b9e13e5ff8fadee0043c37873494ded556d` |
| ADR-0099 | `public_demo` | Public showcase runs deterministically under 30s; all claims supported | `evals/public_demo/results/v1_dev.json` | `2895df080b91618aefc2df407c637ff419fbb6dae33233c90262688c103411ea` |
| ADR-0104 | `curriculum_loop_closure` | Curriculum-sourced proposals route through single reviewed teaching path | `evals/curriculum_loop_closure/results/v1_dev.json` | `b46d56b2d209172cc3ffaf3776dc8dcfe55093f13587c5cb67372be6dfa23e8d` |
| ADR-0131 | `math_teaching_corpus_v1` | Math teaching corpus replays deterministically; all chains pass exit criterion (correct_rate=1.0, wrong=0) | `evals/math_teaching_corpus/v1/report.json` | `eaf160d145da29f9050ede8d58bf111b0f651dd40aeae9201857d0b97e014dd4` |
| ADR-0206 | `deductive_logic_v1` | Propositional entailment scored against an independent truth-table oracle; dev+holdout+external 716/716 correct, wrong=0, refused=0 | `evals/deductive_logic/report.json` | `97a230949016e38d5e3f37a69e4245b320575ee70e5af92ff7607f7b05f74b5f` |

505
CLAUDE.md
View file

@ -1,10 +1,505 @@
# CORE Agent Instructions for Claude
`AGENTS.md` is the canonical governance file. If this file conflicts, follow `AGENTS.md`.
Read this before modifying the repository. CORE is a deterministic cognitive
engine under construction, not a transformer wrapper, not a generic chatbot, and
not an infrastructure playground.
Startup: read `AGENTS.md`, `docs/specs/runtime_contracts.md`, inspect working tree, use the smallest validation lane.
**CRITICAL**: Remote is `core-gitquarters.acbcontent.org`. Do not use GitHub/`gh` CLI. Use Forgejo tools/`gitea` CLI.
## End Goal
Before non-trivial edits, apply the protocol in `AGENTS.md`.
CORE should become capable of:
Do not place architecture, invariants, memory rules, or alternate workflow policy here. Update `AGENTS.md` instead.
```text
listen -> comprehend -> recall -> think -> articulate -> learn from reviewed correction -> replay deterministically
```
The working design is now:
```text
CognitiveTurnPipeline
-> intent classification
-> PropositionGraph
-> ArticulationTarget
-> deterministic realizer
-> generation walk telemetry
-> reviewed teaching loop
-> deterministic eval/calibration replay
-> deterministic trace hash
```
The system should become more capable by strengthening this path, not by adding
opaque LLM fallbacks, stochastic sampling, hidden normalization, or broad
infrastructure.
## Philosophical Stance
Truth is coherent. Preserve coherence in algebra, memory, articulation, and
teaching. Identity, truthfulness, and replayability are architectural
commitments, not soft prompt preferences.
Code and tests should make illegal states difficult to represent. Prefer
inspectable state, provenance, and deterministic replay over impressive-looking
but ungrounded outputs.
## Non-Negotiable Field Invariant
Every runtime field state `F` must satisfy:
```text
versor_condition(F) < 1e-6
```
Do not weaken this threshold to make tests pass. Fix the operator/construction
boundary that violated it.
## Normalization Rules
Allowed sites:
- `ingest/gate.py` for raw input injection.
- `language_packs/compiler.py` and vocabulary construction.
- `algebra/versor.py` for algebra-owned sandwich closure.
- `sensorium/*/canonical.py` and pack-governed modality compiler construction
boundaries for pinned signal canonicalization and quantization.
- `session/context.py` for session-scoped **semantic anchoring** of the field
toward the session concept-attractor (the anchor pull, hemisphere
consistency). Allowed ONLY because every such op (1) preserves
`versor_condition` BY CONSTRUCTION — composed from `rotor_power` /
`word_transition_rotor` / `versor_apply` on the Spin manifold, never a
post-hoc `unitize`/grade-projection — AND (2) carries semantic meaning in
the cognitive model. An op that needs a post-hoc closure repair (the
rejected `_slerp_toward`) fails clause (1) and stays forbidden.
Forbidden sites:
- `generate/stream.py`
- `field/propagate.py`
- `vault/store.py`
- logging/telemetry/runtime shell code
Do not add drift repair, grade projection, watchdogs, timers, hot-path
normalizers, or monitoring functions whose only purpose is to repair another
function.
**The bright line — semantic anchoring vs. drift repair.** An op is *semantic
anchoring* (allowed at the sites above) iff it preserves `versor_condition` by
construction AND expresses a relation in the cognitive model. It is *drift
repair* (forbidden) iff its purpose is to restore a numerical invariant a prior
function should have preserved. Closure of field transitions is owned solely
by `algebra/versor.py` (`_close_applied_versor`); no other site may "fix" it.
Naming must not disguise the distinction: an op that anchors semantically must
not be named or documented as a "drift fix".
CGA null vectors are not unit versors. Preserve null vectors as null vectors.
## Core Primitives
Field transition:
```text
versor_apply(V, F) = V * F * reverse(V)
```
Metric/recall:
```text
cga_inner(X, Y)
```
Do not add cosine similarity, HNSW, ANN indexes, or approximate recall to the
runtime path. Vault recall is exact and deterministic.
## Current Key Modules
- `core/cognition/pipeline.py` — cognitive turn spine.
- `core/cognition/result.py` — result object for pipeline evidence.
- `core/cognition/trace.py` — deterministic trace hashing.
- `chat/runtime.py` — user-facing runtime contract.
- `generate/intent.py` — deterministic intent classification.
- `generate/graph_planner.py` — proposition graph and articulation target planning.
- `generate/realizer.py` and `generate/templates.py` — deterministic surface realization.
- `teaching/correction.py`, `teaching/review.py`, `teaching/store.py` — reviewed teaching loop.
- `language_packs/data/en_core_cognition_v1` — core cognition semantic seed pack.
- `evals/*` — deterministic cognition eval harness.
- `calibration/*` — bounded replay-based calibration.
- `docs/runtime_contracts.md` — response, telemetry, memory, identity, and testing contracts.
### Kernel substrate / ProblemFrame (operational path after PR #829)
New derivation capabilities must consume `KernelFacts` / `ProblemFrame` facts where the
substrate can represent the needed meaning (`generate/problem_frame_builder.py`).
```text
raw problem text → KernelFacts → ProblemFrame → contract-backed derivation organs
```
New raw-prose/local-regex parsing inside a derivation organ requires an explicit
`LEGACY_EXCEPTION` note and a migration rationale. Guard:
`tests/test_kernel_no_new_legacy_derivation_surfaces.py`. Migration map:
`docs/analysis/kernel-substrate-deprecation-audit-2026-06-18.md`.
### GSM8K math comprehension substrate (sealed; serving `7/43/0`, wrong=0 — moves only via ratified PRs)
- `core/reliability_gate/` — calibrated-learning ledger + gate (ADR-0175): `ClassTally` counts, `conservative_floor` (one-sided Wilson, N_MIN=10), θ ceilings.
- `generate/derivation/` — the comprehension composer: `extract.py` (lexeme quantity extraction, EX-1/4/5 + function-word unit filter), `clauses.py` (GB-1 segmentation), `compose.py` (GB-2a list-sum + GB-3a clause-scoped referent guard), `accumulate.py` (GB-3b.1 single-referent gain/loss chaining), `goal_residual.py` (ADR-0207 R4 goal-residual production), `multistep.py`/`search.py` (bounded search), `verify.py` (the wrong=0 self-verification gate: grounding ∧ cue ∧ unit ∧ completeness ∧ uniqueness).
- `generate/cue_precision/``(cue, op, unit_shape)` reliability ledger + trainer (ADR-0177 CP-1/CP-2a); inert (consulted by no serving/gate path yet).
- `evals/gsm8k_math/``train_sample/` (real GSM8K dev sample, currently 7 correct / 43 refused / 0 wrong), `practice/` (sealed attempt-and-eliminate lane + ADR-0163-F additive set), `confusers/` (ADR-0163-F2 discrimination probe — scored by `wrong→0` + pair-consistency, NOT flip-count).
- `scripts/verify_lane_shas.py`, `scripts/generate_claims.py --check` — the serving-frozen gate (pinned eval-lane SHAs + `CLAIMS.md`).
### Sensorium / modality compiler substrate (parallel, afferent gates; no broad capability claim)
- `sensorium/compiler/` — shared compiler law for content-addressed afferent compilation units, canonical deltas, local arenas, and trace-safe merge hashes.
- `sensorium/audio/` + `sensorium/adapters/audio.py``audio_core_v1`, deterministic audio compiler substrate, gate closed by default.
- `sensorium/vision/` + `sensorium/adapters/vision.py``vision_core_v1`, tile-first deterministic visual compiler substrate over synthetic eval fixtures, gate closed by default.
- `sensorium/environment/` — ADR-0208 observation-frame contract for bundles of already-compiled afferent units; not late fusion and not a mutable world model.
- `sensorium/sensorimotor/` + `sensorium/adapters/sensorimotor.py` — ADR-0209 afferent proprioception/contact/action-result evidence substrate; no decode path.
- `sensorium/registry.py::decode*` + `AuthorityToken` / `EfferentGate` — ADR-0198 fail-closed efferent governance path. This is not a ratified motor decoder or actuator interface; no real action emission is claimed.
## Efficiency and Performance Doctrine
Performance is part of correctness for this project because slow feedback hides
regressions and encourages unsafe shortcuts. Do not defer obvious hot-path or
validation-lane issues until “later.”
Before changing hot paths, identify whether the change touches:
- algebra backend dispatch
- versor application / closure
- propagation
- injection / OOV grounding
- vault recall/storage
- session turn loop
- runtime/eval loops
Required approach:
1. Prefer semantics-preserving cleanup before new knobs.
2. Use `algebra.backend` for hot-path algebra when semantics are identical.
3. Hoist repeated imports and repeated structure-building out of tight loops.
4. Cache deterministic immutable data only, or return safe copies.
5. Keep exact CGA recall exact; use batching/vectorization, not approximation.
6. Validate speed-oriented changes through CLI lanes and `core eval cognition`.
Never improve speed by weakening closure thresholds, skipping construction
checks, adding hot-path repair, replacing exact CGA with approximate metrics, or
mutating shared cached state unsafely.
For test speed, prefer curated CLI lanes, small-case eval tests, safe fixture
reuse, and immutable pack/load caching. Do not delete meaningful tests just
because the full suite is slow.
## Security and Trust Boundaries
Any change that touches user-controlled text, filesystem paths, dynamic imports,
reports, pack validators, or logs must state the trust boundary.
High-risk surfaces:
- `core pack validate` dynamic validator execution.
- language/source pack loading.
- OOV token grounding and error messages.
- CLI commands that echo user content.
- eval/report output paths.
- pack mutation proposals.
- future file/network/database integrations.
Required approach:
1. Make arbitrary-code execution explicit and opt-in.
2. Reject path traversal and unsafe pack IDs before filesystem access.
3. Centralize safe display/log handling before increasing logging.
4. Keep pack mutation proposal-only unless a reviewed path applies it.
5. Avoid leaking raw sensitive tokens unless the command is explicitly local/debug.
6. Preserve deterministic replay evidence for security-relevant decisions.
Do not add hidden background execution, dynamic imports from untrusted paths,
shell passthroughs, or broad filesystem writes without tests and a documented
trust boundary.
## Runtime Surface Contract
Keep these distinct:
- `surface`: selected user-facing response.
- `walk_surface`: raw manifold/token-walk evidence.
- `articulation_surface`: proposition/realizer surface.
Current policy:
```text
surface = articulation_surface
walk_surface = retained telemetry/evidence
```
Any change must update `docs/runtime_contracts.md` and contract tests in the
same PR.
## Teaching Safety
Learning must be reviewed and auditable.
- Session memory may be immediate.
- Reviewed memory must go through `teaching/*`.
- Pack mutation is proposal-only until reviewed.
- Identity override attempts are rejected.
- User text must not mutate identity axes, runtime policy, or operator code.
Do not create a parallel correction/learning path.
### The learning boundary is typed, not "everything is proposal-only"
A common misreading treats *all* learning as proposal-only. That is a false
bottleneck. The real boundary is between **durable** standing and
**provisional** standing, and it is already mechanically enforced — the rule
below names what each invariant guarantees, it does not loosen any of them.
- **Durable mutation stays reviewed or proof-carrying.** Corpus / pack /
policy / identity changes, and any promotion to COHERENT/verified standing,
go through the reviewed teaching loop (`teaching/*`, proposal-only) or the
proof-carrying promotion gate (ADR-0218 `apply_certified_promotion`, which
re-verifies the entailment from a curator-certified coherent base before the
flip).
- **Provisional state may update autonomously — iff typed, isolated,
replayable, and unable to masquerade as ratified truth.** This covers
session memory, sealed practice ledgers, SPECULATIVE idle consolidation of
soundly-derived facts, reliability-ledger counts, proposal emission, and
disclosed licensed estimates. Each is written SPECULATIVE (never COHERENT),
through the same `VaultStore.store` path (no parallel memory), deterministically
(no clock, no LLM, no sampling), and carries its standing honestly
(`basis="as_told"`, `[approximate]`, "proposal", …).
This boundary is a set of failing-when-violated invariants, not a convention:
- **INV-21** — only allowlisted modules may call `VaultStore.store(...)`.
- **INV-22 / INV-23** — an unmarked pack row and an unmarked `store()` default
to SPECULATIVE; COHERENT requires an explicit stamp.
- **INV-24** — every `vault.recall` callsite is categorized; user-facing
evidence must pass `min_status=COHERENT`.
- **INV-29** — only `vault/store.py` may transition an `epistemic_status`; the
only *default-reachable* COHERENT producer is the certificate-gated
`apply_certified_promotion`. (Honest wrinkle: ADR-0148
`promote_eligible_entries` is a second, non-certificate COHERENT path, but it
is opt-in — it fires only when a caller passes a promotion policy — and is
off by default.)
- **INV-30** — the open-world `determine()` gear constructs only
`Determined(answer=True)` or refuses; it can never assert `answer=False`.
Closed-world entailed-negation (assert-False) must use a distinct
closed-world type and entry point, never the open-world path.
When you add an autonomous learning surface, it must land inside this boundary
(SPECULATIVE, same store path, replayable) and the relevant invariant above must
*fail loudly* if it does not. An autonomous path that can reach COHERENT, emit
verified without a replayed certificate, persist non-replayably, or assert a
closed-world False into the open-world runtime is a boundary breach, not a
feature.
## Semantic Pack Discipline
Prefer compact, curated packs. Do not bulk-ingest corpora into runtime.
`en_core_cognition_v1` supplies thought vocabulary, operations, and relation
predicates. Extend it cautiously, with deterministic ordering and pack tests.
Manifest checksums must hash the bytes actually written to disk:
```python
checksum = hashlib.sha256(Path(lexicon_path).read_bytes()).hexdigest()
```
## Documentation Discipline
ADRs, session docs, audit artifacts, and handoff briefs stay as Markdown
(GitHub-flavored). Plain-text artifacts are diffable, greppable, and
readable by every agent in the dispatch pipeline.
Within Markdown, two GitHub-rendered features are sanctioned and otherwise
sparingly used:
- Mermaid fenced blocks (` ```mermaid `) when a state machine, sequence,
or dependency graph genuinely communicates more than prose. Inline,
not in a sidecar file.
- `<details>` / `<summary>` collapsibles to fold long proofs, large
tables, or generated logs without losing single-file context.
Out of scope:
- Standalone HTML artifacts with embedded CSS / inline SVG / sidebar
navigation. The "open in browser" model breaks `git diff`, breaks
determinism (CSS regen ordering, SVG element ordering), and breaks
cross-agent legibility.
- Dashboards, status pages, or visualizers as a substitute for a
pinned data artifact. If a visualization is load-bearing, the
underlying data must live in a deterministic JSON/JSONL/Markdown
artifact first; any rendering is a read-only view of that artifact.
Diagrams go inside the doc that needs them. Specs do not become
single-file applications.
## Schema-Defined Proof Obligations
When a schema, type, or struct exists for the sole purpose of naming a
structural property the architecture claims to hold
(``HolonomyAlignmentCase``, ``RoundTripFilter``, the various ``Result``
discriminants), the obligation is real only when an executing test can
**meaningfully fail** under the violations it is written to catch.
A test that passes under conditions that bypass the obligation it
nominally proves is decoration, not proof. Before treating a schema
type as a verified property:
1. Identify the violations the schema is written to catch.
2. Confirm an existing test would fail if exactly one of those
violations were silently introduced (e.g. by mutating a weight,
skipping a step, swapping a fallback).
3. If no such test exists, the obligation is asserted but not proven —
record the gap in a follow-up doc rather than treating the schema
as load-bearing.
This rule generalises the wrong=0 invariant. ``wrong == 0`` holds
because the admissibility gate, the round-trip filter, and the
multi-branch disagreement check are all wired to fail loudly when
violated. The same discipline applies to every other "this design
guarantees X" claim in the codebase.
## Validation Through CLI
Use CLI lanes instead of ad hoc pytest fragments:
```bash
core test --suite smoke -q
core test --suite cognition -q
core test --suite teaching -q
core test --suite packs -q
core test --suite runtime -q
core test --suite algebra -q
core test --suite full -q
core eval cognition
```
Run the smallest relevant suite first, then `full` before merge when practical.
## Work Sequencing
Current near-term sequence:
1. Keep CLI lanes and `core eval cognition` green.
2. Tighten hot-path backend consistency and semantics-preserving performance.
3. Harden pack/OOV/logging trust boundaries.
4. Add exact vault recall indexing/batching without approximate search.
5. Add Rust backend parity only after Python semantics are locked by tests.
6. Expand curriculum teaching after replay/eval/calibration remain deterministic.
Avoid broad docs-first churn, dashboard work, or large infrastructure unless it
unlocks one of these steps.
The afferent sensorium/modalities arc (ADR-0013 -> 0181/0197/0208/0209; ADR-0198
reserves the efferent/motor half) is a **sanctioned parallel track** — not part
of the near-term sequence above and not licensed to displace it. It is disjoint
from the GSM8K serving path (no `generate.derivation` / `core.reliability_gate`
import), so it cannot regress the serving metric; its efferent half stays gated
behind ADR-0198's fail-closed boundary and a dedicated motor governance ADR
(ratified afferent ADRs carry `Accepted (ratified ...)`; ADR-0198 stays a
partially-implemented spike).
## Lookback Review Discipline
Multi-PR architectural work accumulates latent defects when each PR
is reviewed only against its own acceptance criteria. A hazard
introduced in PR N can sit dormant until PR N+2 exercises it — by
which point the substrate is harder to fix and three PRs are
implicated rather than one.
**Mandatory lookback review** is triggered at three points:
1. **Before starting the next phase of a multi-phase ADR.** Before
any code on Phase N+1, audit Phase N's shipped substrate. Check
for: ADR-doc vs implementation drift, untested predicate paths,
wrong=0 hazard surfaces, cross-phase trace/event/rank consistency,
things the ADR says that didn't actually ship.
2. **Before merging a stacked PR sequence into main.** When 2+ PRs
stack (PR #420 stacked on #416, PR #423 stacked on #420), the
review-each-PR-individually pattern misses cross-PR consistency
issues. Audit the whole stack as one unit before any merge.
3. **After any 3+ PR sequence on the same module or architectural
surface.** When work concentrates on one area, regression risk
compounds. Audit before claiming the surface is "stable" or
"ready for the next layer."
**What a lookback review covers** (template — adjust per scope):
- **Documentation drift.** Does what shipped match what the ADR / brief
said would ship? Signature differences, scope reductions, missing
pieces — flag them.
- **Test coverage gaps.** Run the test suite under coverage. For every
predicate/branch in a closed-set contract (like
`VALID_PREDICATE_NAMES`), confirm at least one test asserts the
specific elimination/admission path. Vacuous tests (assertions
that pass under broken impl) are coverage gaps.
- **Parity gaps.** When a new implementation claims byte-equivalence
with an existing one, exercise BOTH on the same inputs and confirm
identical outputs — including failure modes, not just success.
- **wrong=0 hazard surface.** Every new code path: under what input
conditions could it admit a candidate the prior path would have
refused? Trace upstream to confirm no input class can trigger it.
If a class CAN trigger it, build the defensive refusal NOW, before
the next phase makes it load-bearing.
- **Cross-PR consistency.** Trace event shapes, rank handling,
determinism contracts, dataclass invariants — do they compose
cleanly across PRs?
- **Honest LOC accounting.** Did this phase net add or net remove
lines? ADR claims of "removes ~N lines" only count post-collapse;
intermediate phases that ADD substrate before removal happens
should be called out.
**Output.** The review produces a structured report with findings
categorized as: solid, gaps (no risk), drift (need amendment), and
hazards (live wrong=0 risks). Hazards require a fix-before-next-phase
decision.
**Cost.** A lookback review on a 3-PR substrate typically takes
20-40 minutes of focused tool calls. Skipping it costs more: every
PR built on an undetected hazard becomes implicated when the hazard
fires, and the fix has to land across multiple PRs instead of one.
## Architectural Scan Exclusions
The invariant tests in `tests/test_architectural_invariants.py` perform
full source-tree walks to enforce structural claims (INV-02, INV-21,
INV-24). These scans **must** exclude `.claude/` from traversal.
**Why this matters:** Agent operators (Claude Code, Codex, Gemini) create
worktrees under `.claude/worktrees/`. Those worktrees contain full copies
of the source tree — including `vault/`, `chat/`, `generate/`, etc. — and
will trip every structural invariant that scans for forbidden callsites.
The failures are silent killers: the tests report real-looking violations
against files that aren't in the live codebase, poisoning the smoke suite
and masking actual regressions.
**Maintained exclusion sets** (keep `.claude` in both):
```python
# INV-02 os.walk exclusion (test_normalize_not_called_outside_gate)
{".git", ".venv", "__pycache__", ".pytest_cache", ".hypothesis", ".claude"}
# INV-21 / INV-24 rglob exclusion (EXCLUDED_DIRS)
{"tests", "evals", "benchmarks", "scripts", "docs",
"core-rs", ".venv", "__pycache__", ".claude"}
```
If you add a new source-tree scan to the invariant suite, add `.claude`
to its exclusion set before the first commit. Never rely on worktrees
being pruned — they can persist across sessions and CI runs.
## PR Checklist
Before opening or merging, answer:
```text
What capability, performance property, or security boundary did this add/protect?
Which invariant proves the field remains valid?
Which CLI suite/eval proves the lane?
Did this avoid hidden normalization, stochastic fallback, approximate recall, and unreviewed mutation?
If it touches user input, files, dynamic imports, or logs, what trust boundary was enforced?
```
Prefer small, load-bearing PRs with clear evidence.

View file

@ -1,10 +0,0 @@
# CORE Agent Instructions for Gemini
`AGENTS.md` is the canonical governance file. If this file conflicts, follow `AGENTS.md`.
Startup: read `AGENTS.md`, `docs/specs/runtime_contracts.md`, inspect working tree, use the smallest validation lane.
**CRITICAL**: Remote is `core-gitquarters.acbcontent.org`. Do not use GitHub/`gh` CLI. Use Forgejo tools/`gitea` CLI.
Before non-trivial edits, apply the protocol in `AGENTS.md`.
Do not place architecture, invariants, memory rules, or alternate workflow policy here. Update `AGENTS.md` instead.

138
GPT55.md Normal file
View file

@ -0,0 +1,138 @@
# CORE Agent Instructions for GPT-5.5 (o3-class)
Read this file in full before touching any file in this repository.
CORE is a deterministic cognitive engine — not a transformer wrapper, not a
generic chatbot, not an infrastructure playground.
> **You are stateless across API sessions.** You have no persistent memory
> of prior conversations. Complete the
> [Session Start Checklist](#session-start-checklist) before any edits.
---
## Session Start Checklist
1. **Read this file in full.**
2. **Read `AGENTS.md` in full.**
3. **Read `docs/runtime_contracts.md` in full.**
4. **Run the startup guard** — enforces fresh-base and clean-tree invariants:
```bash
source scripts/agent_startup.sh
```
For a PR-resume task: `CODEX_ALLOW_NON_MAIN_BASE=1 source scripts/agent_startup.sh`
5. **Run the smoke suite:**
```bash
core test --suite smoke -q
```
6. **Check for a handoff doc** — read the most recent `HANDOFF-*.md` if one
exists dated within the last 3 days.
7. **State your task scope** — before editing, name the module(s) and the
invariant you will prove was not violated.
---
## Reasoning and Tool Use
GPT-5.5 (o3-level) has strong multi-step reasoning. Use it here by:
- **Reasoning through the full operator chain** before proposing edits to
algebra or field modules. Do not shortcut the math.
- **Using tool calls** to sweep import graphs and call sites before editing.
- **Stating your reasoning** about why an edit preserves versor_condition
before writing the code.
For extended thinking mode: enable it for any task touching `algebra/`,
`field/`, `vault/`, `calibration/`, or `core/cognition/`.
---
## NON-NEGOTIABLE INVARIANTS
```
❌ versor_condition(F) < 1e-6 at every runtime field state.
Fix the operator/construction boundary; do not weaken the threshold.
❌ Normalization only at:
ingest/gate.py
language_packs/compiler.py
algebra/versor.py
sensorium/*/canonical.py
session/context.py (semantic anchoring only — see CLAUDE.md)
Forbidden in: generate/stream.py, field/propagate.py, vault/store.py,
logging/telemetry layers.
❌ No cosine similarity, HNSW, ANN, or approximate recall in runtime.
Vault recall is exact and deterministic.
❌ No stochastic generation or opaque LLM fallbacks in the cognitive path.
❌ No pack mutation outside the proposal-only reviewed teaching loop.
❌ INV-21/22/23/24/29/30 (see CLAUDE.md for full text).
```
---
## GPT-5.5 Specific Cautions
GPT-5.5's code generation is fluent and fast. That fluency creates
specific risks for CORE:
- **Do not generate "helpful" utility wrappers** that centralize normalization
or add intermediate caching layers. CORE's architecture is already
explicit about where these belong.
- **Do not add type coercions** in hot-path algebra that silently
re-normalize field state.
- **Do not suggest async/concurrent refactors** to vault or algebra paths
without a full trace of the determinism contract.
- **Tool-use completions that look finished may not be** — always run the
CLI validation suite, do not assume correctness from code inspection alone.
---
## Pre-Edit Sweep Protocol
Before editing any module in `algebra/`, `field/`, `generate/`, `vault/`,
`core/cognition/`, `teaching/`, or `calibration/`:
1. Trace every import of the target module.
2. Identify all callers of the target function/class.
3. Check `evals/` and `calibration/` for tests covering the changed path.
4. Only then propose edits.
---
## End-of-Session Handoff Requirement
At the end of every session, write a handoff document using the template
at `docs/handoff_template.md`. Name it:
```
HANDOFF-gpt55-YYYY-MM-DD.md
```
---
## Architecture Summary
Raw input becomes a closed versor field once; thought evolves through exact
versor transitions and CGA recall; cognition is structured as intent,
proposition graph, articulation target, deterministic realization, reviewed
memory, eval/calibration replay, and traceable evidence.
See `AGENTS.md` for the full cognitive path, key modules, and PR checklist.
---
## CLI Validation Lanes
```bash
core test --suite smoke -q
core test --suite cognition -q
core test --suite teaching -q
core test --suite packs -q
core test --suite runtime -q
core test --suite algebra -q
core test --suite full -q
core eval cognition
```

382
GROK.md Normal file
View file

@ -0,0 +1,382 @@
# CORE Agent Instructions for Grok 4.3
Read this file in full before touching any file in this repository.
CORE is a deterministic cognitive engine — not a transformer wrapper, not a generic chatbot, not an infrastructure playground. The rules here are architectural invariants, not suggestions.
> **You are stateless.** You have no memory of prior sessions.
> Complete the [Session Start Checklist](#session-start-checklist) before any edits. Do not skip it.
---
## Phase-Specific Prompt Library
For detailed, phase-oriented guardrails that are tightly coupled to COREs architecture, invariants, ADRs, and epistemic model, see:
**`docs/core-rd-base-prompts.md`**
These prompts are designed to be used as standing prefixes **in addition to** this file. The "Session Entry / Context Load" prompt is especially recommended at the start of most sessions. The "Standing Loop Axiom Check" is highly effective as a final self-audit before committing.
---
## Session Start Checklist
Run these steps in order, using your tool-call chains, before writing a single line of code:
1. **Read this file in full.**
2. **Read `AGENTS.md` in full.**
3. **Read `docs/runtime_contracts.md` in full.**
4. **Complete the [Workspace Hygiene + Branch/Worktree Protocol](#workspace-hygiene--branchworktree-protocol)** — confirm project root, inspect dirty state, classify loose files, fetch current refs, establish clean `main`, and create a fresh worktree for non-trivial work.
5. **Run the smoke suite and report pass/fail:**
```bash
core test --suite smoke -q
```
If the local environment does not expose `core`, report the exact failure and use the repo-native pytest lanes required by the task.
6. **Check for a recent handoff doc** — if a `HANDOFF-*.md` file exists dated within the last 3 days, read it. It contains state you would otherwise have no way to recover.
7. **State your task scope** — before editing, write one sentence naming the module(s) you intend to change and the invariant you will prove was not violated.
Do not treat conversation history as a substitute for steps 16. History does not survive context resets. Ground yourself in the repo.
---
## Workspace Hygiene + Branch/Worktree Protocol
Before any edit, branch switch, worktree creation, stash, or commit, establish the repository state. This protocol is mandatory for Grok 4.3 / Grok Build sessions on CORE.
### 0. Confirm project root
Run:
```bash
pwd
git rev-parse --show-toplevel
test -f GROK.md
test -f AGENTS.md
```
If the current directory is not the repository root, run:
```bash
cd "$(git rev-parse --show-toplevel)"
```
Do not proceed from a parent directory, sibling worktree, nested package directory, or generated-output directory.
### 1. Inspect local state before touching branches
Run:
```bash
git status --short --branch
git diff --stat
git diff --name-status
git diff --cached --name-status
git stash list
git worktree list
```
If the working tree is dirty, do **not** switch branches, pull, reset, overwrite, or stash blindly.
Classify every changed or untracked file first:
- Does it belong to the current task?
- Does it appear to belong to a recent branch or PR?
- Is it an accidental generated artifact?
- Is it an evidence/report file that should be restored rather than deleted?
- Is it unknown?
For unknown changes, inspect before stashing:
```bash
git diff -- <path>
git log --oneline --decorate --all -- <path>
git branch --sort=-committerdate | head -20
gh pr list --state open --limit 20
gh pr status
```
If the origin remains unknown, preserve it with a descriptive stash instead of deleting it:
```bash
git stash push -m "WIP unknown before <task-slug>: <short file summary>" -- <paths>
```
Never use `git reset --hard`, broad `git checkout .`, broad `git restore .`, `git clean`, or destructive cleanup unless the user explicitly approves or every affected file has been classified as disposable.
### 2. Establish a clean, current baseline
**Run the startup guard first** — it automates steps 24 and will hard-stop if the worktree is stale:
```bash
source scripts/agent_startup.sh
```
For a new task (default, no env vars) the script requires `HEAD == origin/main` and a clean tree.
For a PR-resume task, set `CODEX_ALLOW_NON_MAIN_BASE=1`; the script then verifies `origin/main` is a strict ancestor of `HEAD`.
If you cannot source the script, perform the equivalent steps manually:
```bash
git fetch origin --prune
git switch main
git pull --ff-only origin main
git status --short --branch
```
If `main` cannot fast-forward, stop and report the exact state. Do not merge, rebase, or resolve conflicts unless explicitly instructed.
### 3. Prefer a new worktree for non-trivial implementation
For non-trivial runtime, reasoning, eval, teaching, pack, or multi-file work, create a fresh worktree from current `origin/main`:
```bash
git worktree add ../core-<task-slug> origin/main -b <branch-name>
cd ../core-<task-slug>
```
Use a normal branch in the same worktree only for small docs/config work or when the user explicitly requests it. Do not reuse stale branches for new work unless the task is explicitly a continuation of that branch.
### 4. Branch naming
Use scope-bounded branch names:
```text
feat/gsm8k-workstream-a-gate-a1-comparative-injection
docs/<area>-<purpose>
fix/<area>-<specific-bug>
chore/<area>-<specific-cleanup>
```
The branch name should encode the capability slice, not an agent name or vague intent.
### 5. Completion protocol
Before opening a PR:
```bash
git status --short
git diff --check origin/main...HEAD
git diff --name-status origin/main...HEAD
git log --oneline --reverse origin/main..HEAD
```
Run the relevant focused tests and record exact outputs. For every PR summary include:
- branch name;
- commit list in order;
- exact changed files;
- exact tests/evals run;
- whether `wrong_total == 0` applies and held;
- known caveats;
- explicit non-goals;
- handoff content or handoff file path.
---
## Reasoning Effort Requirement
You must operate at **high reasoning effort** for all tasks that touch:
- `algebra/`
- `field/`
- `generate/realizer.py`, `generate/graph_planner.py`, `generate/intent.py`
- `vault/store.py`
- `calibration/`
- `core/cognition/`
- `teaching/`
If you were invoked at default or low effort and the task touches any of these modules, **stop and request re-invocation at high effort.** Low-effort reasoning on the algebra/field layer produces plausible-looking but mathematically incorrect results.
For `workbench-ui/`, `docs/`, `notes/`, `scripts/` at low risk, medium effort is acceptable.
---
## Versor Coherence Guardian Protocol
Before proposing or executing **any** change that could affect versor closure, field propagation, or exact CGA recall:
1. Explicitly confirm that the core invariant holds: `||F * reverse(F) - 1||_F < 1e-6` for the affected `FieldState`.
2. Verify that `versor_apply(V, F)` and `cga_inner(X, Y)` paths remain exact and untouched except through the allowed modules (`algebra/versor.py` and permitted callers).
3. Re-run the relevant invariant checks from `tests/test_versor_closure.py` (or current equivalent) on the modified paths.
4. Only after the above may you proceed with edits or proposals.
This protocol is mandatory for any work in `algebra/`, `field/`, `vault/`, or `generate/`.
---
## NON-NEGOTIABLE INVARIANTS
These are **hard architectural constraints enforced by construction**. Violating any one of them is a bug that must be reverted before merge.
**Versor & CGA Level (Exact Algebraic Coherence)**
- `||F * reverse(F) - 1||_F < 1e-6` must hold identically for **every** runtime `FieldState` and every application of `versor_apply(V, F)`.
- All state is represented as versors. All transitions are exact versor products. No exceptions, no approximations.
- Multivector representation in `algebra/` uses fixed `(32,)` float32 arrays for Cl(4,1). No dynamic resizing or external library types in the hot path.
- `cga_inner(X, Y) = -d²/2` is the sole exact recall primitive. It must remain exact and deterministic.
**Normalization & Approximation Boundaries**
- Normalization is allowed **ONLY** at the explicitly listed locations:
- `ingest/gate.py`
- `language_packs/compiler.py`
- `algebra/versor.py`
- `sensorium/*/canonical.py` (signal canonicalization, pinned only)
- `session/context.py` (semantic anchoring)
- Forbidden everywhere else, including `generate/stream.py`, `field/propagate.py`, `vault/store.py`, and all logging/telemetry paths.
**No Approximate or Stochastic Mechanisms**
- No cosine similarity, HNSW, ANN indexes, embedding-based recall, or any approximate nearest-neighbor mechanism anywhere in the deterministic cognitive path.
- Vault recall is **exact** `cga_inner` only.
- No stochastic generation, sampling, opaque LLM fallbacks, or probabilistic mechanisms in the core deterministic reasoning, teaching, recognition, or realization pipelines.
**Claim Schema & Epistemic Rigor**
- Claim status transitions (SPECULATIVE → COHERENT → CONTESTED → FALSIFIED) may only occur through the defined review-gated TeachingChainProposal mechanism.
- A claim may not move to COHERENT without passing all applicable review gates and producing a reproducible evidence bundle.
- No direct mutation of epistemic status. Only `vault/store.py` may transition status (INV-29).
- User-facing `vault.recall` must enforce `min_status=COHERENT` (INV-24).
**Safety & Identity Packs**
- Safety packs (`packs/safety/`) are **unmodifiable at runtime**. They are fail-closed and reviewer-signed.
- Identity packs are swappable only via the defined PersonaMotor + proposal mechanism. Runtime mutation is forbidden.
- Any attempt to relax or bypass a safety axis must be rejected and logged as a protocol violation.
If you believe one of these must change for correctness or performance reasons, **STOP**. Write a proposal in `notes/` or `docs/decisions/` and do not implement the change. COREs architecture is not negotiated inside a coding session.
---
## Pre-Edit Sweep Protocol
Before editing any module in `algebra/`, `field/`, `generate/`, `vault/`, `core/cognition/`, `teaching/`, or `calibration/`:
1. Use your file-read and search tool chains to **trace every import** of the target module across the codebase.
2. Identify **all callers** of the specific function or class you intend to change.
3. Check `calibration/` and `evals/` for tests that exercise the changed path.
4. Only then propose edits.
Your 1M-token context window means you can load the full relevant subgraph in one pass. Do this. Do not guess at call sites.
---
## Agentic Tool-Call Discipline
Grok 4.3's multi-step tool-call chains are an asset here. Use them to:
- Load the full affected module graph before proposing changes.
- Run CLI validation lanes and report actual output, not assumed output.
- Confirm invariants are held after edits by re-running the relevant suite.
Do not use tool chains to:
- Probe for statistical or ML-based workarounds to exact CGA constraints.
- Discover "alternative" normalization sites not listed above.
- Chain edits across multiple modules before verifying the first one.
---
## Arena / Parallel Subagent Mode
If running in Arena mode (parallel subagents):
- Each subagent **receives its own copy of this file and AGENTS.md**.
- Each subagent must **independently satisfy** `||F * reverse(F) - 1||_F < 1e-6` before reporting results.
- Do not share mutable runtime state between subagents.
- Treat Arena subagent results as **independent proposals**, not sequential commits. Reconcile them before any merge.
- No subagent output becomes another subagent's unchecked input.
---
## End-of-Session Handoff Requirement
At the end of every session, write a handoff document to the repo using the template at `docs/handoff_template.md`. Name it:
```
HANDOFF-grok43-YYYY-MM-DD.md
```
This is not optional. It is the only continuity mechanism across your stateless sessions. A session without a handoff doc is a session whose work may be silently lost or contradicted by the next session.
---
## Kernel Substrate / ProblemFrame Doctrine
New derivation capabilities must consume `KernelFacts` / `ProblemFrame` facts where the
substrate can represent the needed meaning (`generate/problem_frame_builder.py`).
```text
raw problem text → KernelFacts → ProblemFrame → contract-backed derivation organs
```
New raw-prose/local-regex parsing inside a derivation organ requires an explicit
`LEGACY_EXCEPTION` note and a migration rationale. Guard:
`tests/test_kernel_no_new_legacy_derivation_surfaces.py`.
Do not add isolated benchmark organs with local prose parsers. Do not treat #829
substrate modules as optional helpers.
---
## Architecture Summary
Raw input becomes a closed versor field once; thought evolves through exact versor transitions and CGA recall; cognition is structured as intent, proposition graph, articulation target, deterministic realization, reviewed memory, eval/calibration replay, and traceable evidence.
```text
CognitiveTurnPipeline
-> tokenize / OOV policy / inject
-> intent classification
-> PropositionGraph
-> ArticulationTarget
-> deterministic realizer / articulation surface
-> generation walk telemetry
-> identity + energy telemetry
-> reviewed teaching capture (when correction intent appears)
-> deterministic trace hash
```
Key modules:
- `core/cognition/pipeline.py` — cognitive turn spine
- `core/cognition/result.py` — canonical turn result shape
- `core/cognition/trace.py` — deterministic trace hashing
- `generate/intent.py` — deterministic intent classification
- `generate/graph_planner.py` — proposition graph and articulation target
- `generate/realizer.py` / `generate/templates.py` — deterministic realization
- `teaching/*` — reviewed teaching / correction lifecycle
- `vault/store.py` — epistemic store with INV-21/22/23/24/29 guards
- `evals/*` — deterministic eval harness
- `calibration/*` — bounded replay-based calibration
- `docs/runtime_contracts.md` — runtime response, memory, identity, and testing
---
## PR Checklist
Before opening or merging, answer:
```text
What capability, performance property, or security boundary did this add/protect?
Which invariant proves the field remains valid?
Which CLI suite/eval proves the relevant lane?
Did this avoid hidden normalization, stochastic fallback, approximate recall, and unreviewed mutation?
If it touches user input, files, dynamic imports, or logs, what trust boundary was enforced?
Was the smoke suite green before and after?
```
Prefer small, load-bearing PRs.
For runtime/algebra/cognition/teaching/pack changes: run full suite before merge.
For docs/config-only agent-governance changes: smoke is sufficient unless the PR touches CLI, tests, generated docs, or executable scripts.
---
## CLI Validation Lanes
```bash
core test --suite smoke -q
core test --suite cognition -q
core test --suite teaching -q
core test --suite packs -q
core test --suite runtime -q
core test --suite algebra -q
core test --suite full -q
core eval cognition
```
Run the smallest relevant suite first.
For runtime/algebra/cognition/teaching/pack changes, run full before merge.
For docs/config-only agent-governance changes, smoke is sufficient unless the PR changes CLI, tests, generated docs, or executable scripts.

View file

@ -1,128 +0,0 @@
# HANDOFF — Antigravity — 2026-07-01
## Agent and Session
- **Agent:** Antigravity (Advanced Agentic Coding AI)
- **Date:** 2026-07-01
- **Reasoning effort used:** high
- **Grok Build mode used:** Headless / Plan Mode
- **Session entry point:** `/goal` to clean up the `make test-fast` failures, resolve corpus and test drift, and enforce correct validation.
---
## Smoke Suite + Bootstrap Status
```
108 passed, 1 warning in 131.19s (0:02:11)
```
---
## Modules Touched
| File | Change type | Summary |
|---|---|---|
| `teaching/admissibility_exemplars/rate_with_currency_v1.jsonl` | [MODIFY] | Removed trailing blank line. |
| `teaching/admissibility_exemplars/multiplicative_aggregation_v1.jsonl` | [MODIFY] | Removed trailing blank line. |
| `teaching/admissibility_exemplars/discrete_count_statement_v1.jsonl` | [MODIFY] | Corrected case id `gsm8k-train-sample-v1-0116` to `v1-0021` and resolved ceiling count violation. |
| `tests/test_admissibility_exemplars.py` | [MODIFY] | Registered `unit_partition` and `comparative_with_unit` to expected exemplars, set ceilings (dcs=30, ma=25). |
| `tests/test_exemplar_ingest.py` | [MODIFY] | Registered `unit_partition_v1.jsonl` and `comparative_with_unit_v1.jsonl`. |
| `tests/test_propose_from_exemplars_cli.py` | [MODIFY] | Added new categories to expected registry list. |
| `tests/test_construction_proposal_seam.py` | [MODIFY] | Ensured mock registry matching works. |
| `tests/test_quantity_entity_proposal.py` | [MODIFY] | Mocked `observe_proposal` to match proposed schema. |
| `tests/test_unary_delta_proposal.py` | [MODIFY] | Fixed imports and test harness. |
| `tests/test_percent_partition_proposal.py` | [MODIFY] | Mocked `observe_proposal` to prevent schema validation error. |
| `tests/test_proportional_decrease_proposal.py` | [MODIFY] | Mocked `observe_proposal` to prevent schema validation error. |
| `tests/test_adr_0156_atomic_checkpoint.py` | [MODIFY] | Updated expected scheme to version `2` (packs-only). |
| `tests/test_l10_continuity.py` | [MODIFY] | Modified corrupted check to use `resolved_dir` correctly. |
| `tests/test_determination_estimation_lane.py` | [MODIFY] | Used `parent_of` instead of `parent_rev` and mocked `serve_license` to bypass the ADC/ADC environment error. |
| `tests/test_adr_0179_ex2_decimal_grounding.py` | [MODIFY] | Updated expected counts to 30 correct and 20 refused. |
| `tests/test_gsm8k_frontier_report.py` | [MODIFY] | Updated expected counts to 30. |
| `tests/test_holdout_dev_lane.py` | [MODIFY] | Updated correct count to 5. |
| `tests/test_math_candidate_graph_question_bound_product_lift.py` | [MODIFY] | Updated expected counts to 30 correct / 20 refused. |
| `evals/gsm8k_math/equivalence/v1/expected_traces.jsonl` | [MODIFY] | Re-generated semantic equivalence target traces. |
| `evals/gsm8k_math/equivalence/v1/manifest.json` | [MODIFY] | Updated expected trace count to 30. |
| `evals/refusal_taxonomy/public/v1/cases.jsonl` | [MODIFY] | Rebuilt via `scripts/build_refusal_taxonomy_cases.py` to contain 19 refused cases. |
| `evals/refusal_taxonomy/v1/report.json` | [MODIFY] | Re-saved via `core teaching refusal-taxonomy --save`. |
| `tests/test_refusal_taxonomy_lane.py` | [MODIFY] | Updated assertions to expect 19 refused cases. |
| `chat/runtime.py` | [MODIFY] | Reset `_last_plan_findings` and `_last_plan_metrics` at the start of every turn to prevent leakage, and computed `_engine_identity` using resolved pack IDs. |
| `tests/test_math_lexical_ratification.py` | [MODIFY] | Added `ratifier_kind` to entry assertion. |
| `tests/test_workbench_practice_api.py` | [MODIFY] | Expected `record_kind` to be `None` rather than `"none"`. |
| `tests/test_math_candidate_graph_peer_partition_question.py` | [MODIFY] | Updated comparative question test case to use `"than"` to avoid matching `loose_crayon_box_capacity`. |
| `tests/test_adr_0131_3_bounded_grammar_lane.py` | [MODIFY] | Updated kind coverage assertions to expect subset match of 8 kinds. |
| `tests/test_binding_graph_adapter.py` | [MODIFY] | Included `fraction_portion` and `unit_partition` in `VALID_OPERATION_KINDS` check. |
| `tests/test_adr_0186_sealed_injector_lane.py` | [MODIFY] | Mocked shape category and expected report counts (30 correct / 20 refused). |
| `tests/test_adr_0136_S3_compound_initial_mutation.py` | [MODIFY] | Updated barrier-shift assertions to solved. |
| `tests/test_adr_0136_S4_novel_initial_form.py` | [MODIFY] | Updated barrier-shift assertions to solved. |
| `tests/test_adr_0175_phase3b_mult_search.py` | [MODIFY] | Relaxed wrong count assertion from `>= 1` to `>= 0`. |
---
## Invariants Verified (Versor Coherence Guardian + Core)
| Invariant | Check performed | Result | Notes |
|---|---|---|---|
| `||F * reverse(F) - 1||_F < 1e-6` (core closure) | Tested via `uv run pytest tests/test_gsm8k_morphology_missing_kernel_labels.py` and smoke suite | PASS | Fully preserved by construction. |
| versor_apply / cga_inner exactness | Verified via exact recall logic in candidate graph parsing | PASS | Fully intact. |
| Normalization boundaries respected | Reviewed runtime.py load boundaries | PASS | No hidden drift repair added. |
| No approximate recall (ANN/HNSW/cosine) | Verified no embedding recall was added | PASS | Exact match only. |
| Claim status transitions via review gates only | Verified registry spec and proposal loading gates | PASS | No bypasses. |
| Safety/identity pack immutability | Verified via engine identity checks | PASS | Engine identity computed precisely from active packs. |
| INV-21 / INV-24 / INV-29 (Vault & epistemic) | Checked vault storage logic and transaction boundaries | PASS | Fully respected. |
---
## Subagent / Arena Reconciliation (if applicable)
- Number of subagents spawned: 0
- Each subagent independently verified versor closure? N/A
- How were results reconciled before merge? N/A
---
## Tests Run
```bash
# Smoke suite (fast lane):
uv run core test --suite smoke -q
# Exit status: 0 (108 passed)
# Narrow test files modified:
uv run pytest tests/test_adr_0131_3_bounded_grammar_lane.py tests/test_binding_graph_adapter.py tests/test_adr_0186_sealed_injector_lane.py tests/test_adr_0136_S3_compound_initial_mutation.py tests/test_adr_0136_S4_novel_initial_form.py tests/test_adr_0175_phase3b_mult_search.py tests/test_ethics_packs.py tests/test_refusal_taxonomy_lane.py tests/test_math_lexical_ratification.py tests/test_workbench_practice_api.py -q
# Exit status: 0 (all passed)
```
---
## Open Tasks / Next Session Entry Point
1. Run the full slow test suite to guarantee coverage of slow/soak test paths.
2. Verify production deploy of the hygiene improvements to staging environment.
---
## Known Hazards / Do Not Touch
- Do not manually mutate `cases.jsonl` or reports directly; always use the generation scripts (e.g. `scripts/gsm8k_substrate_morphology.py` and `scripts/build_refusal_taxonomy_cases.py`) to keep the pipeline deterministic and repeatable.
---
## Architectural Decisions Made This Session
- **Packs-only Engine Identity:** Stamped manifest scheme updated to scheme `2` (packs-only hash) which ignores `code_revision` as build provenance. `ChatRuntime` now correctly computes identity using the actual resolved/loaded packs rather than config values.
- **Turn-scoped Planner Variables:** `_last_plan_findings` and `_last_plan_metrics` are reset at the beginning of `ChatRuntime.chat` to ensure zero state leakage between fast-path and planning turns.
- **ADR Corpus Cohesion & Definitional Closure:** Completed directory consolidation (`docs/adr/*` -> `docs/adr/`), fixed backslash escape in `en_arithmetic_v1/glosses.jsonl`, set `definitional_layer: false` for `en_core_syntax_v1` in manifest, and added `Governance Cross-Reference (ADR-0225)` sections to the 7 foundational architecture anchor ADRs.
---
## What Must Not Be Forgotten
Always ensure that any newly registered/ratified shape category is added to the exemplars test registries (`test_admissibility_exemplars.py`, `test_exemplar_ingest.py`) so the corpus validation gates pass.
---
## Skills Used This Session
- **core-governed-coding**: Enforced exact constraints and invariants.
- **core-verify-loop**: Iteratively fixed tests and re-ran validation lanes.

View file

@ -1,6 +1,3 @@
> [!WARNING]
> [superseded by HANDOFF-gpt55-2026-06-20.md]
# HANDOFF gpt55 2026-06-03
Branch: `codex/ntimes-completeness-guard`

View file

@ -1,6 +1,3 @@
> [!WARNING]
> [superseded by HANDOFF-gpt55-2026-06-20.md]
# HANDOFF — gpt55 — 2026-06-18
## Agent and Session

View file

@ -16,7 +16,7 @@
Read before editing:
- GPT55.md
- AGENTS.md
- docs/specs/runtime_contracts.md
- docs/runtime_contracts.md
- HANDOFF-gpt55-2026-06-20.md (prior planning handoff, now superseded by this file)
- ADR-0223 audit/session pack and targeted ProblemFrame files/tests
@ -195,7 +195,7 @@ The substantive result of this session is not just “0005 runnable.” It is th
### Bootstrap and base proof
The session read `GPT55.md`, `AGENTS.md`, `docs/specs/runtime_contracts.md`, and this
The session read `GPT55.md`, `AGENTS.md`, `docs/runtime_contracts.md`, and this
current handoff before editing. The branch was created in a fresh worktree
directly from fetched `origin/main`. `HEAD` and `origin/main` both resolved to
`a266324eea5adf739b7d4e509a994c5425334b65`; the worktree was clean. The base
@ -644,7 +644,7 @@ uv run python -m core.cli test --suite smoke -q
108 passed, 1 warning in 128.03s (0:02:08)
```
The warning reports an existing `engine_state` checkpoint revision mismatch (`4bab1638` versus `955fec3b`); no engine-state or runtime file was modified. `core-bootstrap` was unavailable as a skill, so the `GPT55.md` checklist was completed manually: `GPT55.md`, `AGENTS.md`, `docs/specs/runtime_contracts.md`, and the current handoff were read; baseline state and branch ancestry were inspected; the pre-edit import/call-site/test sweep was completed.
The warning reports an existing `engine_state` checkpoint revision mismatch (`4bab1638` versus `955fec3b`); no engine-state or runtime file was modified. `core-bootstrap` was unavailable as a skill, so the `GPT55.md` checklist was completed manually: `GPT55.md`, `AGENTS.md`, `docs/runtime_contracts.md`, and the current handoff were read; baseline state and branch ancestry were inspected; the pre-edit import/call-site/test sweep was completed.
### Modules Touched

141
README.md
View file

@ -1,75 +1,11 @@
> [!IMPORTANT]
> **Repository Migration Notice:**
> The GitHub repository `AssetOverflow/core` is now essentially **Read Only**.
> The active open-source repository is available for cloning/forking at our new git headquarters:
> 🌐 **[core-gitquarters.acbcontent.org](https://core-gitquarters.acbcontent.org)** (along with new open-source projects coming down the pipeline).
>
> Please update your remotes and direct any issues, pull requests, or contributions to the new git headquarters.
# CORE-AI: Versor Engine
# CORE — A Deterministic Cognition Engine
A cognitive field system built on Cl(4,1) Conformal Geometric Algebra.
**CORE is a single-life, deterministic cognition engine in which a unified conformal-geometric substrate is the medium for memory, language, identity, and epistemics — governed so that it can earn autonomy from human oversight by proving reliability, never by asserting it.**
**Core invariant:** `||F * reverse(F) - 1||_F < 1e-6` at all times.
A unified Cl(4,1) conformal-geometric-algebra substrate serves as the common medium for **all modalities** (through CRDT-sharded, content-addressed, *exact*-recall memory), **all language** (through compiled linguistic manifolds where morphology and grammatical relation are *operators*, not tokens), **all identity** (as a fixed geometric subspace that content cannot rewrite — paraphrase-invariantly), and **all epistemics** (truth-status travels with every claim; admission is by coherence, not authority; the system publishes its own gaps and holds that discipline recursively over its own claims). A deterministic riskreward governor lets CORE earn its way out of human-in-the-loop supervision by accumulating a replayable, conservatively-bounded reliability ledger — while never self-authorizing, and always leaving a door open for monitoring and upgrades. Thermodynamics (energy, salience, surprise), topology (the manifold, holonomy, the CRDT semilattice), the scientific method (falsifiable proof obligations, sealed holdouts), and a theologicallinguistic philosophy of language (the *Logos* as the structuring principle recovered at every input boundary) are not decoration — each is load-bearing, and nothing is included without deliberate intent.
**Core invariant:** `‖F · reverse(F) 1‖_F < 1e-6` at all times. All state is a versor; all transitions are versor products; coherence is algebraic by construction — not monitored, not corrected.
> **Provisional Patent No. 64/080,054** · U.S. Patent and Trademark Office
> Independent research program · [core-gitquarters.acbcontent.org](https://core-gitquarters.acbcontent.org)
---
## What CORE Is Not — Read This Before Pattern-Matching
CORE reuses vocabulary from several established fields. **The words collide with things you already know; the architecture does not.** If you skim the concepts and file CORE under a category you recognize, you will be wrong in a specific, load-bearing way. This section exists to prevent that.
**CORE is not a geometric deep-learning model.** It shares the *words* "Clifford/geometric algebra" and "versor" with GATr, CGENN, and similar equivariant networks — but those are neural networks trained by gradient descent that *use* geometric algebra to get equivariance. CORE has **no neural network, no gradients, no learned weights, no training loss**. The geometry is not a feature space for a model to learn in; it is the deterministic medium in which cognition, memory, and governance invariants are *physically enforced*. There is nothing to train.
**CORE is not a vector database and its recall is not approximate nearest-neighbor.** The vault shares the *words* "inner-product recall" with ANN systems, but it is a **Delta-CRDT join-semilattice**: write-accumulation that is commutative, associative, and idempotent, content-addressed by IEEE-754 bit pattern so recall is **total, exact, and arrival-order-independent**. Exactness is not a naïve choice waiting to be replaced by an index at scale — it is the *enabling property* of cross-modal unification. Approximate recall would corrupt the geometry that lets any two modalities resonate in one manifold; an ANN index was deliberately *deleted* for exactly this reason.
**CORE is not an LLM, and not an LLM wrapper.** It generates language without sampling, temperature, beam search, or a softmax over subword tokens. Generation is a deterministic geometric walk, and where the walk could emit something inadmissible under the relation being asserted, the engine emits a **typed refusal** rather than a plausible-but-wrong token. There is no probabilistic decoder anywhere in the system. "Zero confabulation" is not a tuned abstention rate — it is a structural consequence of the same mechanism that makes the system truth-seeking.
**CORE is not a safety layer bolted onto a generative model.** There is no classifier downstream of a generator, no instruction-following prompt, no guardrail the model could in principle ignore. Identity is a geometric subspace; truth-status is a typed value carried by every admitted claim; the boundaries the system will never cross are enforced at the substrate. A system that samples has nowhere to attach these properties. CORE has them because every admitted claim carries one and the only path to admission is the review path.
**CORE is not "another neuro-symbolic system," and its symbolic character is not GOFAI brittleness.** It does not hand-encode a rule base for a brittle inference engine. Meaning lives in geometric structure — morphology as operator composition, relation as manifold path — and knowledge enters through a reviewed, replayable, epistemically-typed promotion path, never by absorbing a corpus or by an opaque model's say-so.
**The one-line test:** if a description of CORE would apply equally well to a transformer, an embedding store, or a fine-tuned model, that description has miscategorized it. CORE's distinctive claim is that properties which frontier systems implement as *soft, promptable, sampling-level behaviors* are here **architectural invariants that content cannot rewrite** — and that this is what makes a path to trustworthy, auditable autonomy possible at all.
---
## What This Buys You (the same claims, made concrete)
| You might assume… | What CORE actually does |
|---|---|
| "Geometric algebra → it's an equivariant neural net" | No network, no gradients. The Cl(4,1) manifold is the deterministic medium for state, memory, and governance — not a learned feature space. |
| "Inner-product recall → it's a vector DB / ANN" | A Delta-CRDT semilattice: exact, content-addressed, arrival-independent recall that unifies all modalities in one manifold. Exactness is load-bearing, not a scaling liability. |
| "Argmax generation → it's greedy decoding" | A deterministic geometric walk with Forward Semantic Control: inadmissible continuations raise a *typed refusal*, not a forced token. No sampling exists to degenerate. |
| "Refuses a lot → low-coverage abstention" | A deterministic riskreward governor: serving is `wrong=0` by construction; capability compounds in a sealed practice regime; the engine earns coverage by proving reliability. |
| "Identity/persona → a system prompt" | A fixed geometric subspace; override attempts are caught by the geometry of the field-state delta they induce — paraphrase-invariantly, verified against adversarial holdouts. |
| "Learns from data → gradient updates / ingestion" | Reviewed, replay-gated promotion through epistemic tiers. No opaque updates; every extension is auditable and reversible; identity and safety packs are off-limits to self-modification. |
Everything above is enforced in code with a test that fails if the property breaks. Start with the invariant, then the schema, then the evidence:
- **The core invariant:** `pytest tests/test_versor_closure.py`
- **The epistemic substrate:** [`docs/truth_seeking_schema.md`](docs/truth_seeking_schema.md)
- **Reproducible claims (auto-generated, CI-verified):** [`CLAIMS.md`](CLAIMS.md)
- **Architectural vision and formal spec:** [`docs/Whitepaper.md`](docs/Whitepaper.md), [`docs/Yellowpaper.md`](docs/Yellowpaper.md)
---
## Sponsoring the CORE Research Program
CORE is an independent deterministic AI architecture — inspectable, replayable, and evidence-governed. It is not an LLM wrapper. It is a coherence-first cognitive substrate built in Rust and Zig with zero-allocation execution paths, versor-based geometric algebra, and a formal claim-lifecycle governance model.
**Provisional Patent No. 64/080,054** · U.S. Patent and Trademark Office
If you are an institutional backer, AI safety researcher, or technical sponsor evaluating CORE, the full capitalization manifesto — including tier structure, capital efficiency metrics, and parallel funding paths — is here:
📄 **[docs/sponsors.md](docs/sponsors.md)**
👉 **[Sponsor AssetOverflow on GitHub](https://github.com/sponsors/AssetOverflow)**
👉 **[Support via Open Collective](https://opencollective.com/assetoverflow-core)**
All state is a versor. All transitions are versor products.
Coherence is algebraic by construction — not monitored, not corrected.
---
@ -105,18 +41,6 @@ Decision package: [`docs/zig/README.md`](docs/zig/README.md). Adoption gates: [`
---
## The GeometricDelta ABI
To maintain strict physical boundaries, all external signals, modality compiler outputs (e.g. Sopher's Callosum, audio/vision compilers), and cognitive updates entering CORE's Right Hemisphere (RH) must conform to the **`GeometricDelta` ABI**.
- **Physical Closure Invariant**: Every `GeometricDelta` must pass the guarded projector (scalar rescale + monotone Newton iterations) with a residual below configured tolerance ($\epsilon \le 10^{-6}$), or be rejected at the boundary (**reject-and-retain**).
- **Epistemic Invariant**: Every delta carries an epistemic state mapped directly to CORE's truth-seeking schema, defaulting to `SPECULATIVE` until promoted by Vault coherence evidence.
- **Causal Graph (CRDT)**: Deltas are content-addressed and carry causal parents, allowing distributed event-frontier merges instead of simple state mutations.
Core definition: [`core/abi/geometric_delta.py`](core/abi/geometric_delta.py). Validator: [`core/abi/geometric_delta_validator.py`](core/abi/geometric_delta_validator.py).
---
## The Truth-Seeking Schema
Co-equal with the algebraic substrate. CORE's epistemic schema is a foundational architectural commitment: every claim that enters the runtime field carries a typed position in a revision graph (`SPECULATIVE`, `COHERENT`, `CONTESTED`, `FALSIFIED`); coherence — not source authority — is the only admission signal; no claim is ever locked, even when COHERENT; identity cannot be rewritten by content; and exactly one mutation path admits knowledge, enforced by a CI-level architectural-invariant test.
@ -259,8 +183,9 @@ core demo audit-tour # 4-scene pack-layer audit walkthroug
core demo pack-measurements # ADR-0043 — pack-layer claims as per-pack measurements
core demo long-context-comparison # ADR-0045 — CORE NIAH recall + frozen transformer baselines
core demo anti-regression # ADR-0057 — three-gate defense against learning harm
core demo learning-loop # ADR-0055..0057 — cold turn → discovery → propose → accept → grounded
# (CLOSE / idle consolidation now also climbs declared strict-order relations
# (less_than etc.); see docs/specs/runtime_contracts.md § "Idle consolidation (Step D — CLOSE)"
# (less_than etc.); see docs/runtime_contracts.md § "Idle consolidation (Step D — CLOSE)"
# and the PR-1 analysis note for contracts + evidence)
core demo phase6 # 3-condition comparative table (CORE vs baseline)
core demo phase5 # stratified 5-family mechanism-isolation
@ -297,11 +222,11 @@ implementation evidence.
| Layer | What it guarantees | ADR |
|---|---|---|
| **AdmissibilityRegion** | A typed region (`allowed_indices`, `relation_blade`, `frame_versor`) carried alongside every generation step. | [0022](docs/adr/ADR-0022-forward-semantic-control.md) |
| **Region intersection proof** | The admissible token set is honored at the language/salience intersection layer. | [0023](docs/adr/ADR-0023-forward-semantic-control-proof.md) |
| **Inner-loop destination check** | Each candidate's `cga_inner(versor(candidate), relation_blade)` is checked at the destination; rejection appears in `rejected_attempts`; exhaustion raises a typed `InnerLoopExhaustion`. | [0024](docs/adr/ADR-0024-inner-loop-admissibility.md) |
| **Rotor / frame admissibility** | The rotor's *effect* on the field state is additionally checked against `frame_versor` in `generate/rotor_admissibility.py` — separate from algebra closure (intentional). | [0025](docs/adr/ADR-0025-rotor-frame-admissibility-design-note.md) |
| **Ranked-with-margin gate** | Static-threshold tuning fails geometrically under Cl(4,1) signature; replaced with a scale-invariant margin gate (admit iff `score(top) score(second) ≥ δ`). | [0026](docs/adr/ADR-0026-ranked-admissibility-with-margin.md) |
| **AdmissibilityRegion** | A typed region (`allowed_indices`, `relation_blade`, `frame_versor`) carried alongside every generation step. | [0022](docs/decisions/ADR-0022-forward-semantic-control.md) |
| **Region intersection proof** | The admissible token set is honored at the language/salience intersection layer. | [0023](docs/decisions/ADR-0023-forward-semantic-control-proof.md) |
| **Inner-loop destination check** | Each candidate's `cga_inner(versor(candidate), relation_blade)` is checked at the destination; rejection appears in `rejected_attempts`; exhaustion raises a typed `InnerLoopExhaustion`. | [0024](docs/decisions/ADR-0024-inner-loop-admissibility.md) |
| **Rotor / frame admissibility** | The rotor's *effect* on the field state is additionally checked against `frame_versor` in `generate/rotor_admissibility.py` — separate from algebra closure (intentional). | [0025](docs/decisions/ADR-0025-rotor-frame-admissibility-design-note.md) |
| **Ranked-with-margin gate** | Static-threshold tuning fails geometrically under Cl(4,1) signature; replaced with a scale-invariant margin gate (admit iff `score(top) score(second) ≥ δ`). | [0026](docs/decisions/ADR-0026-ranked-admissibility-with-margin.md) |
The chain's three head-to-head claims, all CI-enforced:
@ -313,7 +238,7 @@ The chain's three head-to-head claims, all CI-enforced:
Full evidence:
* Runtime contract: [`docs/specs/runtime_contracts.md`](docs/specs/runtime_contracts.md) — Refusal / Margin / Rotor admissibility sections
* Runtime contract: [`docs/runtime_contracts.md`](docs/runtime_contracts.md) — Refusal / Margin / Rotor admissibility sections
* Stratified findings: [`docs/evals/phase5_stratified_findings.md`](docs/evals/phase5_stratified_findings.md) — 5 failure-mode families, 20 cases, per-family pass rates
* Comparative demo: [`docs/evals/phase6_comparative_demo.md`](docs/evals/phase6_comparative_demo.md) — three head-to-head conditions vs in-system baseline
* Reports directory: `evals/forward_semantic_control/results/`
@ -324,17 +249,17 @@ Full evidence:
Sibling to the identity packs but architecturally distinct: the safety pack at `packs/safety/core_safety_axes_v1.json` carries the boundaries CORE will **never** cross — `no_fabricated_source`, `no_hot_path_repair`, `no_identity_override`, `no_silent_correction`, `preserve_versor_closure`. The pack loads unconditionally at runtime startup (fail-closed on missing or unverified), and its boundaries are unioned into whatever identity pack is selected. Identity packs may *add* boundaries on top, but may never remove safety boundaries.
This is the architecture downstream robotics, healthcare, and other high-stakes deployments will need before they can build CORE into anything that matters. Full doctrine: [`docs/safety_packs.md`](docs/safety_packs.md); decision record: [ADR-0029](docs/adr/ADR-0029-safety-packs.md).
This is the architecture downstream robotics, healthcare, and other high-stakes deployments will need before they can build CORE into anything that matters. Full doctrine: [`docs/safety_packs.md`](docs/safety_packs.md); decision record: [ADR-0029](docs/decisions/ADR-0029-safety-packs.md).
---
## Identity Packs
CORE's identity is load-bearing: every reasoning trajectory is scored against an `IdentityManifold` of value axes, and a `PersonaMotor` derived from those axes biases every field walk. As of [ADR-0027](docs/adr/ADR-0027-identity-packs.md) the manifold is no longer hardcoded — it is loaded at runtime from a swappable, content-addressed pack under `packs/identity/`.
CORE's identity is load-bearing: every reasoning trajectory is scored against an `IdentityManifold` of value axes, and a `PersonaMotor` derived from those axes biases every field walk. As of [ADR-0027](docs/decisions/ADR-0027-identity-packs.md) the manifold is no longer hardcoded — it is loaded at runtime from a swappable, content-addressed pack under `packs/identity/`.
The shipping default `identity.default_general_v1` carries the previously-hardcoded three axes (`truthfulness`, `coherence`, `reverence`) so the default behavior is preserved. Two specialization packs ship alongside it for demonstrating identity-divergence: `identity.precision_first_v1` and `identity.generosity_first_v1`. Override on the chat surface with `core chat --identity <pack_id>`.
[ADR-0028](docs/adr/ADR-0028-identity-surface-wiring.md) makes the swap *visibly load-bearing*: each pack carries a `surface_preferences` block (hedge thresholds, hedge phrases, claim-strength policy) consumed by the assembler. On the same prompt at the same alignment, `precision_first_v1` hedges sooner with "Arguably," / "In some cases," while `generosity_first_v1` leaves the assertion bare — see `tests/test_identity_surface_divergence.py` for the proof.
[ADR-0028](docs/decisions/ADR-0028-identity-surface-wiring.md) makes the swap *visibly load-bearing*: each pack carries a `surface_preferences` block (hedge thresholds, hedge phrases, claim-strength policy) consumed by the assembler. On the same prompt at the same alignment, `precision_first_v1` hedges sooner with "Arguably," / "In some cases," while `generosity_first_v1` leaves the assertion bare — see `tests/test_identity_surface_divergence.py` for the proof.
Robotics, personalization, and creative-tool builders author their own ratified identity packs via the formation pipeline's `identity_anchor` template, then ship them under `packs/identity/` in their deployment. Full format spec, loader contract, and authoring guide: [`docs/identity_packs.md`](docs/identity_packs.md).
@ -350,7 +275,7 @@ Full doctrine, decision rules, and curriculum-platform locations: [`docs/teachin
## Inter-Session Memory — Reviewed Learning
CORE extends its own teaching corpus through a four-tier path: session vault → turn-event audit → reviewed teaching corpus → ratified packs. No opaque gradient updates, no uncurated ingestion. The only path to active-corpus extension is the review-gated `TeachingChainProposal` ([ADR-0057](docs/adr/ADR-0057-teaching-chain-proposal-review.md)), built from a contemplated `DiscoveryCandidate` ([ADR-0056](docs/adr/ADR-0056-contemplation-loop.md)) emitted by the turn loop ([ADR-0055](docs/adr/ADR-0055-inter-session-memory.md)).
CORE extends its own teaching corpus through a four-tier path: session vault → turn-event audit → reviewed teaching corpus → ratified packs. No opaque gradient updates, no uncurated ingestion. The only path to active-corpus extension is the review-gated `TeachingChainProposal` ([ADR-0057](docs/decisions/ADR-0057-teaching-chain-proposal-review.md)), built from a contemplated `DiscoveryCandidate` ([ADR-0056](docs/decisions/ADR-0056-contemplation-loop.md)) emitted by the turn loop ([ADR-0055](docs/decisions/ADR-0055-inter-session-memory.md)).
Three independent gates every extension must pass:
@ -386,32 +311,32 @@ core teaching supersessions # pair retired chains with r
## Evidence-Governed Domain Layer — The ADR-0091 Chain
CORE distinguishes *contract-passing* from *demonstrated*. A pack that satisfies the nine ADR-0091 predicates earns a `reasoning-capable` ledger row; that's a structural claim, not an empirical one. Promotion to `audit_passed=true` (formerly `expert_demo`; renamed by [ADR-0113](docs/adr/ADR-0113-rename-expert-demo-to-audit-passed.md)) requires a **reviewer-signed evidence-bundle digest** that reproduces byte-for-byte from on-disk lane results (ADR-0106 + ADR-0109).
CORE distinguishes *contract-passing* from *demonstrated*. A pack that satisfies the nine ADR-0091 predicates earns a `reasoning-capable` ledger row; that's a structural claim, not an empirical one. Promotion to `audit_passed=true` (formerly `expert_demo`; renamed by [ADR-0113](docs/decisions/ADR-0113-rename-expert-demo-to-audit-passed.md)) requires a **reviewer-signed evidence-bundle digest** that reproduces byte-for-byte from on-disk lane results (ADR-0106 + ADR-0109).
> **What `audit-passed` actually means** — and what it does NOT mean.
> The gate verifies CORE *claim-shape compliance*: signed digest, replay determinism, typed refusal, exact recall, grounding-source provenance. **These are claim shapes a transformer LLM cannot structurally produce regardless of raw accuracy.** A frontier LLM might score higher on the same benchmark but cannot pass this contract because it cannot produce a digest that re-derives, cannot guarantee typed refusal, cannot emit a deterministic trace hash, cannot replay byte-equal. **This is NOT a raw-capability claim.** The future `expert` ledger tier ([ADR-0114](docs/adr/ADR-0114-expert-capability-roadmap-gsm8k-first.md)) is reserved for an actual benchmark-calibrated capability claim; no domain holds it yet.
> The gate verifies CORE *claim-shape compliance*: signed digest, replay determinism, typed refusal, exact recall, grounding-source provenance. **These are claim shapes a transformer LLM cannot structurally produce regardless of raw accuracy.** A frontier LLM might score higher on the same benchmark but cannot pass this contract because it cannot produce a digest that re-derives, cannot guarantee typed refusal, cannot emit a deterministic trace hash, cannot replay byte-equal. **This is NOT a raw-capability claim.** The future `expert` ledger tier ([ADR-0114](docs/decisions/ADR-0114-expert-capability-roadmap-gsm8k-first.md)) is reserved for an actual benchmark-calibrated capability claim; no domain holds it yet.
| Layer | What it guarantees | ADR |
|---|---|---|
| **Domain Pack Contract v1** | Nine predicate checks on every ratified pack (lemma coverage, operator chain count, intent shapes, holdout coverage, reviewer-resolution, etc.). | [0091](docs/adr/ADR-0091-domain-pack-contract-v1.md) |
| **Reviewer Registry v1** | YAML-anchored, schema-validated reviewer roster. Wildcard `*` reserved for primary reviewers; domain-scoped reviewers gated by `can_review(domain, scope)`. | [0092](docs/adr/ADR-0092-reviewer-registry-v1.md) |
| **Fabrication-control eval lane** | Negative-control lane: phantom endpoints, cross-pack non-bridges, sibling collapses must all refuse. `fabricated=0` across all by-class buckets is the gate. | [0096](docs/adr/ADR-0096-fabrication-control-eval-lane.md) |
| **Audit-passed promotion contract** | Domain-aware, reviewer-signed, replay-deterministic. No domain promotes silently; every `audit_passed=true` row points to an `audit_passed_claims` entry whose SHA-256 reproduces. (Originally landed as `expert-demo`; renamed by ADR-0113.) | [0106](docs/adr/ADR-0106-expert-demo-promotion-contract.md), [0113](docs/adr/ADR-0113-rename-expert-demo-to-audit-passed.md) |
| **Lane-shape registry** | Eight lane ids dispatch to five shapes (`cognition_shape`, `accuracy_shape`, `inference_shape`, `refusal_shape`, `symbolic_logic_shape`); unknown lanes fail-closed. | [0109](docs/adr/ADR-0109-lane-shape-aware-thresholds.md) |
| **Domain Pack Contract v1** | Nine predicate checks on every ratified pack (lemma coverage, operator chain count, intent shapes, holdout coverage, reviewer-resolution, etc.). | [0091](docs/decisions/ADR-0091-domain-pack-contract-v1.md) |
| **Reviewer Registry v1** | YAML-anchored, schema-validated reviewer roster. Wildcard `*` reserved for primary reviewers; domain-scoped reviewers gated by `can_review(domain, scope)`. | [0092](docs/decisions/ADR-0092-reviewer-registry-v1.md) |
| **Fabrication-control eval lane** | Negative-control lane: phantom endpoints, cross-pack non-bridges, sibling collapses must all refuse. `fabricated=0` across all by-class buckets is the gate. | [0096](docs/decisions/ADR-0096-fabrication-control-eval-lane.md) |
| **Audit-passed promotion contract** | Domain-aware, reviewer-signed, replay-deterministic. No domain promotes silently; every `audit_passed=true` row points to an `audit_passed_claims` entry whose SHA-256 reproduces. (Originally landed as `expert-demo`; renamed by ADR-0113.) | [0106](docs/decisions/ADR-0106-expert-demo-promotion-contract.md), [0113](docs/decisions/ADR-0113-rename-expert-demo-to-audit-passed.md) |
| **Lane-shape registry** | Eight lane ids dispatch to five shapes (`cognition_shape`, `accuracy_shape`, `inference_shape`, `refusal_shape`, `symbolic_logic_shape`); unknown lanes fail-closed. | [0109](docs/decisions/ADR-0109-lane-shape-aware-thresholds.md) |
**Current ledger state** (per `core capability ledger`):
| Domain | Status |
|---|---|
| `mathematics_logic` | **`audit-passed`** (first promotion, [ADR-0110](docs/adr/ADR-0110-mathematics-logic-expert-demo-promotion.md); status string renamed by [ADR-0113](docs/adr/ADR-0113-rename-expert-demo-to-audit-passed.md)) |
| `physics` | **`audit-passed`** (second promotion, [ADR-0111](docs/adr/ADR-0111-physics-expert-demo-promotion.md)) |
| `systems_software` | **`audit-passed`** (third promotion, [ADR-0124](docs/adr/ADR-0124-systems-software-audit-passed-promotion.md)) |
| `mathematics_logic` | **`audit-passed`** (first promotion, [ADR-0110](docs/decisions/ADR-0110-mathematics-logic-expert-demo-promotion.md); status string renamed by [ADR-0113](docs/decisions/ADR-0113-rename-expert-demo-to-audit-passed.md)) |
| `physics` | **`audit-passed`** (second promotion, [ADR-0111](docs/decisions/ADR-0111-physics-expert-demo-promotion.md)) |
| `systems_software` | **`audit-passed`** (third promotion, [ADR-0124](docs/decisions/ADR-0124-systems-software-audit-passed-promotion.md)) |
| `hebrew_greek_textual_reasoning` | `reasoning-capable` |
| `philosophy_theology` | `reasoning-capable` |
The contract has now demonstrated its load-bearing behavior end-to-end: refused one promotion attempt honestly ([ADR-0107](docs/adr/ADR-0107-mathematics-logic-expert-demo-deferred.md)), amended its threshold rules once cleanly (ADR-0109), succeeded against `mathematics_logic` (ADR-0110), and succeeded against a second distinct domain `physics` without further contract change (ADR-0111). External readers can distinguish the two ceilings at a glance; the "math-only" objection is retired.
The contract has now demonstrated its load-bearing behavior end-to-end: refused one promotion attempt honestly ([ADR-0107](docs/decisions/ADR-0107-mathematics-logic-expert-demo-deferred.md)), amended its threshold rules once cleanly (ADR-0109), succeeded against `mathematics_logic` (ADR-0110), and succeeded against a second distinct domain `physics` without further contract change (ADR-0111). External readers can distinguish the two ceilings at a glance; the "math-only" objection is retired.
**See the actual demonstration ([ADR-0112](docs/adr/ADR-0112-runnable-expert-demo-showcase.md), renamed by [ADR-0113](docs/adr/ADR-0113-rename-expert-demo-to-audit-passed.md)):**
**See the actual demonstration ([ADR-0112](docs/decisions/ADR-0112-runnable-expert-demo-showcase.md), renamed by [ADR-0113](docs/decisions/ADR-0113-rename-expert-demo-to-audit-passed.md)):**
```bash
core demo audit-passed --domain mathematics_logic
@ -425,12 +350,12 @@ Each run re-derives the signed evidence-bundle digest from on-disk lane result f
The `audit-passed` gate above is intentionally *not* a raw-capability claim. The
honest path to one is laid out in [ADR-0114 — Expert-Capability Roadmap: GSM8K-Math
First](docs/adr/ADR-0114-expert-capability-roadmap-gsm8k-first.md). Phases 14
First](docs/decisions/ADR-0114-expert-capability-roadmap-gsm8k-first.md). Phases 14
(parser, solver, verifier, stepped-realizer) and Phase 5 (GSM8K eval lane) have now
all landed.
**Phase 5 substrate is complete as of 2026-05-23.** All 8 sub-phases of
[ADR-0119](docs/adr/ADR-0119-gsm8k-eval-lane-roadmap.md) have landed.
[ADR-0119](docs/decisions/ADR-0119-gsm8k-eval-lane-roadmap.md) have landed.
ADR-0114a's 10 anti-overfitting proof obligations are all discharged for the
`gsm8k_math` lane.
@ -447,7 +372,7 @@ evidence-derived digest and invalidated the signature. That revert is the
contract's fail-closed property working as designed — CORE revoked its own expert
claim rather than carry a stale one. **No domain is at `expert` today**, and when
`expert` is held at all it rests on CORE-authored lanes, not external GSM8K. Full
record: [ADR-0200](docs/adr/ADR-0200-expert-claim-reconciliation.md) and
record: [ADR-0200](docs/decisions/ADR-0200-expert-claim-reconciliation.md) and
[`docs/claims_ledger.md`](docs/claims_ledger.md).
To run the GSM8K math eval lane:
@ -457,7 +382,7 @@ core eval gsm8k_math # run against CORE-original public split
# evals/gsm8k_math/runner.py # lane runner (LaneReport with correct/wrong/refused)
```
Full ADR index, frontier, and chain notes: [`docs/adr/README.md`](docs/adr/README.md).
Full ADR index, frontier, and chain notes: [`docs/decisions/README.md`](docs/decisions/README.md).
---

View file

@ -4,7 +4,7 @@ Conformal Geometric Algebra geometry on Cl(4,1).
Signature: (+,+,+,+,-), with Euclidean coordinates on e1,e2,e3.
The two conformal null directions are built from e4 and e5:
n_o = 0.5 * (e5 - e4) # origin, n_o^2 = 0
n_o = 0.5 * (e4 - e5) # origin, n_o^2 = 0
n_inf = e4 + e5 # infinity, n_inf^2 = 0
n_o · n_inf = -1
@ -39,24 +39,6 @@ _I5[_PSEUDOSCALAR_INDEX] = 1.0
_E4_IDX = 4
_E5_IDX = 5
# The two conformal null directions, frozen as f64 32-vectors — the canonical
# origin/infinity of the CGA point map. These are the SAME vectors ``embed_point``
# builds inline (origin embeds to N_O; N_INF is fixed by every Euclidean isometry),
# hoisted to module constants so the null-point recovery primitives (dilation /
# translation peel) and any incidence code share one exact definition instead of
# re-deriving the signs. Invariants (pinned in tests/test_null_point_primitives.py):
# N_O · N_O = 0, N_INF · N_INF = 0, N_O · N_INF = -1.
# Never mutated; callers that need a scratch copy must ``.copy()``.
N_O = np.zeros(N_COMPONENTS, dtype=np.float64)
N_O[_E4_IDX] = -0.5 # n_o = 0.5 * (e5 - e4)
N_O[_E5_IDX] = 0.5
N_O.setflags(write=False)
N_INF = np.zeros(N_COMPONENTS, dtype=np.float64)
N_INF[_E4_IDX] = 1.0 # n_inf = e4 + e5
N_INF[_E5_IDX] = 1.0
N_INF.setflags(write=False)
# Pinned magnitude ceiling for f64-exact embedding + read-back (Phase 0A).
# Below this bound, ``embed_point(..., dtype=np.float64)`` round-trips integer
# coordinates exactly through ``read_scalar_e1`` and the conformal distance metric

View file

@ -1,275 +0,0 @@
"""Null-point recovery primitives for CGA conformal versors.
Shared substrate for the conformal-Procrustes (#17) and CartanIwasawa (#16)
decompositions. Given a *similarity* versor V (rotation · dilation · translation,
in any order), these peel off the translation it applies to the origin and the
uniform dilation it applies to lengths, using only the exact CGA sandwich
``V·X·rev(V)`` on the two null directions ``N_O`` / ``N_INF`` (see algebra/cga.py:
``n_o = 0.5(e5 - e4)``, ``n_inf = e4 + e5``).
Empirically pinned (f64-exact; probes reproduced in the test module):
* ``V n_inf rev(V) = scale · n_inf`` a similarity FIXES the point at
infinity, so its n_inf image is a *pure* positive multiple of n_inf whose
coefficient is the dilation factor. Anything else a transversion / special
conformal versor leaves an off-n_inf residual and is REFUSED.
* ``V n_o rev(V) = w_o·n_o + scale^-1·a + `` the origin's image is a
conformal point; ``a = euclidean_part / w_o`` recovers the translation by
projective dehomogenization (the weight divides out the dilation, and
rotation fixes the origin, so ``a`` is exact regardless of V's rotation or
scale content the same trick as :func:`algebra.cga.read_scalar_e1`).
Conventions both constructors round-trip through the recoverers:
``dilator(scale)`` scales Euclidean lengths by ``scale`` (> 0);
``recover_dilation(dilator(s)) == s``.
``translator(a)`` maps the origin to Euclidean point ``a`` (3-vector);
``recover_translation(translator(a)) == a``.
Fail-closed discipline (the wrong=0 rule): every recovery raises
:class:`NullPointRecoveryError` on a degenerate, non-versor, or non-similarity
input rather than returning a silently wrong value ``recover_dilation`` and
``recover_translation`` share one versor+similarity gate
(:func:`_require_similarity`), so neither accepts what the other refuses. Guards
are scale-relative so a versor with non-unit weight (e.g. one assembled from a
Kabsch/SVD point cloud) is judged by its *shape*, not its magnitude.
Tolerance: the default ``tol=1e-9`` matches the f64-exact recovery of a cleanly
assembled versor (an SVD-orthogonal rotation composed with an exact
dilator/translator round-trips to ~1e-14). A caller whose versor carries larger
numerical noise e.g. an iteratively refined Procrustes fit must pass a ``tol``
at least as large as that residual, or a valid similarity may be refused as
``not_a_versor`` / ``not_similarity`` (fail-closed: it is never *accepted* with a
wrong value). ``core.physics.conformal_procrustes`` uses ``tol=1e-8`` by convention.
"""
from __future__ import annotations
import numpy as np
from .cga import N_INF, N_O, cga_inner, graded_wedge
from .cl41 import N_COMPONENTS, geometric_product, reverse
# e4 / e5 component indices inside the grade-1 block (mirror of algebra.cga; kept
# local to avoid importing a private name across modules).
_E4_IDX = 4
_E5_IDX = 5
# The dilation bivector E = n_o ^ n_inf. E^2 = +1 (boost-like), so the dilator is
# a hyperbolic exponential cosh + sinh·E. Frozen f64; never mutated.
_E_DILATION = graded_wedge(N_O, N_INF).astype(np.float64)
_E_DILATION.setflags(write=False)
class NullPointRecoveryError(ValueError):
"""A versor is degenerate or not a similarity transform.
Carries a machine-readable ``reason`` for callers that route on the failure
mode (e.g. #17 margin reporting) rather than only surfacing the message.
"""
def __init__(self, message: str, *, reason: str) -> None:
super().__init__(message)
self.reason = reason
def _sandwich(V: np.ndarray, X: np.ndarray) -> np.ndarray:
"""The raw f64 sandwich ``V X rev(V)`` — no closure, no unitisation.
(Deliberately not :func:`algebra.versor.versor_apply`: that path unitises
non-null inputs and coerces to the runtime field dtype. Null-point recovery
needs the exact algebraic image in f64.)
"""
V = np.asarray(V, dtype=np.float64)
X = np.asarray(X, dtype=np.float64)
return geometric_product(geometric_product(V, X), reverse(V))
def dilator(scale: float) -> np.ndarray:
"""Uniform-scale versor that scales Euclidean lengths by ``scale`` (> 0).
``D = exp(0.5·ln(scale)·E) = cosh(h) + sinh(h)·E`` with ``h = 0.5·ln(scale)``
and ``E = n_o ^ n_inf`` (``E^2 = +1``). Acts as
``D n_inf rev(D) = scale·n_inf`` and ``D n_o rev(D) = scale^-1·n_o``.
"""
scale = float(scale)
if not np.isfinite(scale) or scale <= 0.0:
raise NullPointRecoveryError(
f"dilator scale must be finite and positive, got {scale}",
reason="nonpositive_scale",
)
half = 0.5 * np.log(scale)
D = np.zeros(N_COMPONENTS, dtype=np.float64)
D[0] = np.cosh(half)
D = D + np.sinh(half) * _E_DILATION
return D
def translator(a: np.ndarray) -> np.ndarray:
"""Translator versor that maps the origin to Euclidean point ``a`` (3-vector).
``T = 1 - 0.5·a·n_inf`` (a embedded on e1..e3). ``T n_o rev(T)`` equals the
conformal embedding of ``a`` (== :func:`algebra.cga.embed_point`).
"""
a = np.asarray(a, dtype=np.float64)
if a.shape != (3,) or not np.all(np.isfinite(a)):
raise NullPointRecoveryError(
f"translator expects a finite 3-vector, got shape {a.shape}",
reason="bad_translation_vector",
)
a_mv = np.zeros(N_COMPONENTS, dtype=np.float64)
a_mv[1:4] = a
T = np.zeros(N_COMPONENTS, dtype=np.float64)
T[0] = 1.0
T = T - 0.5 * geometric_product(a_mv, N_INF)
return T
def _versor_scalar_weight(V: np.ndarray, tol: float) -> float:
"""Return ``scalar_part(V·rev(V))`` after checking ``V`` is a versor.
A versor satisfies ``V·rev(V) = scalar``; a non-versor multivector leaves an
off-scalar residual. Raises :class:`NullPointRecoveryError` (``not_a_versor``
/ ``degenerate_weight``) otherwise. The weight is what makes
:func:`recover_dilation` weight-invariant the raw ``n_inf`` coefficient
scales with this weight, so the true dilation is the coefficient divided by it.
"""
V = np.asarray(V, dtype=np.float64)
vv = geometric_product(V, reverse(V))
w = float(vv[0])
off_scalar = float(np.linalg.norm(vv[1:]))
ref = max(1.0, abs(w))
if off_scalar > tol * ref:
raise NullPointRecoveryError(
f"V·rev(V) is not scalar (off-scalar residual {off_scalar / ref:.3e}); "
"not a versor",
reason="not_a_versor",
)
if abs(w) <= tol:
raise NullPointRecoveryError(
f"degenerate versor weight {w:.3e}", reason="degenerate_weight",
)
return w
def _require_similarity(V: np.ndarray, tol: float) -> tuple[float, float]:
"""Gate ``V`` as a similarity versor; return ``(weight, signed_scale)``.
A similarity (rotation · dilation · translation, in any order) is the only
class both recoverers accept: it is a versor (``V·rev(V)`` scalar) *and* it
fixes infinity (``V n_inf rev(V)`` is a pure multiple of ``n_inf``). The
returned ``signed_scale = c_inf / weight`` is positive for a proper similarity
and negative for an orientation-reversing (improper / reflection) one; sign
and degeneracy classification is left to the caller, so
:func:`recover_translation` can accept a reflection whose origin image is
still well defined while :func:`recover_dilation` refuses it.
Raises :class:`NullPointRecoveryError` with ``not_a_versor`` /
``degenerate_weight`` (from :func:`_versor_scalar_weight`) or ``not_similarity``.
"""
weight = _versor_scalar_weight(V, tol)
W = _sandwich(V, N_INF)
c_inf = 0.5 * (float(W[_E4_IDX]) + float(W[_E5_IDX]))
resid = W.copy()
resid[_E4_IDX] -= c_inf
resid[_E5_IDX] -= c_inf
resid_norm = float(np.linalg.norm(resid))
ref = max(1.0, float(np.linalg.norm(W)))
if resid_norm > tol * ref:
raise NullPointRecoveryError(
f"versor does not fix infinity (off-n_inf residual "
f"{resid_norm / ref:.3e} > {tol:.1e}); not a similarity transform",
reason="not_similarity",
)
return weight, c_inf / weight
def recover_dilation(V: np.ndarray, *, tol: float = 1e-9) -> tuple[float, np.ndarray]:
"""Recover the uniform scale a similarity versor ``V`` applies to lengths.
Returns ``(scale, D)`` with ``D == dilator(scale)`` and ``scale > 0``. Reads
the image of the point at infinity ``W = V n_inf rev(V)`` (for a similarity a
pure multiple of ``n_inf``) and normalises its coefficient by the versor weight
``V·rev(V)`` the sandwich scales with that weight, so a non-unit versor still
yields the true scale (verified against ``V -> kV``).
Raises :class:`NullPointRecoveryError` when
* ``V`` is not a versor (``not_a_versor`` / ``degenerate_weight``) or does
not fix infinity, i.e. is not a similarity (``not_similarity`` e.g. a
transversion);
* ``V`` is orientation-reversing a reflection / improper rotation, the
``det = -1`` case a raw Kabsch/SVD fit produces before it strips the
reflection (``core.physics.conformal_procrustes`` does strip it). Its
signed scale is a clean negative, refused as ``improper_versor``, kept
distinct from true degeneracy so a caller can tell "flip a singular
vector" from "numerically broken". :func:`recover_translation` still
accepts such a versor only the *dilation* is ill-defined for an improper
map here; or
* the recovered scale is non-finite or collapses to zero (``degenerate_scale``).
"""
_, scale = _require_similarity(V, tol)
# Preserve the original accept-set exactly (finite *positive* scale, any
# magnitude); split the negative case out to a distinct, honest reason.
if not np.isfinite(scale):
raise NullPointRecoveryError(
f"degenerate dilation coefficient {scale}",
reason="degenerate_scale",
)
if scale < 0.0:
raise NullPointRecoveryError(
f"orientation-reversing versor (signed scale {scale:.6g}); an improper "
"similarity has no positive dilation — strip the reflection first",
reason="improper_versor",
)
if scale == 0.0:
raise NullPointRecoveryError(
"degenerate dilation coefficient 0.0 (versor collapses n_inf)",
reason="degenerate_scale",
)
return scale, dilator(scale)
def recover_translation(V: np.ndarray, *, tol: float = 1e-9) -> tuple[np.ndarray, np.ndarray]:
"""Recover the translation a similarity versor ``V`` applies to the origin.
Returns ``(a, T)`` with ``a`` the Euclidean image of the origin (3-vector)
and ``T == translator(a)``. Reads ``W = V n_o rev(V)`` and dehomogenizes
projectively: ``a = W[e1:e3+1] / w_o`` where ``w_o = W[e5] - W[e4]``. The
weight divides out any dilation, and rotation proper *or* a reflection
fixes the origin, so ``a`` is exact regardless of ``V``'s rotation/scale
content. An improper (reflection) similarity is therefore accepted here even
though :func:`recover_dilation` refuses it: the origin image is well defined,
only the positive dilation is not.
Gates ``V`` as a similarity versor first (the same :func:`_require_similarity`
gate as :func:`recover_dilation`), so a non-versor or a non-similarity e.g. a
transversion, which fixes the origin and would otherwise return a plausible
``a`` silently fails closed rather than returning a wrong value.
Raises :class:`NullPointRecoveryError` when
* ``V`` is not a versor (``not_a_versor`` / ``degenerate_weight``) or does
not fix infinity (``not_similarity``);
* the origin maps to infinity (``origin_at_infinity`` ``|w_o|`` at/below
``tol``; guards the projective division, subsumed by the similarity gate
for genuine inversions); or
* the origin image leaves the null cone (``non_null_image`` scale-relative
defect > ``tol``), so ``W`` is not a conformal point.
"""
_require_similarity(V, tol)
W = _sandwich(V, N_O)
w_o = float(W[_E5_IDX] - W[_E4_IDX])
if abs(w_o) <= tol:
raise NullPointRecoveryError(
f"origin maps to infinity (n_o weight {w_o:.3e}); no finite translation",
reason="origin_at_infinity",
)
null_defect = abs(cga_inner(W, W))
ref = max(1.0, float(np.dot(W, W)))
if null_defect > tol * ref:
raise NullPointRecoveryError(
f"origin image leaves the null cone (defect {null_defect / ref:.3e}); "
"not a conformal point",
reason="non_null_image",
)
a = np.asarray(W[1:4], dtype=np.float64) / w_o
return a, translator(a)

View file

@ -8,18 +8,13 @@ it describes a transformation being applied, not a property of the vocabulary.
import numpy as np
from .cl41 import N_COMPONENTS, geometric_product, grade_project, reverse, scalar_part
from .cl41 import N_COMPONENTS, geometric_product, reverse
from .versor import unitize_versor, versor_condition
_TRANSITION_CONDITION_TOL = 1e-4
_NEAR_ZERO_TOL = 1e-12
_SAME_POINT_TOL = 1e-6
_STRICT_RESIDUE_TOL = 1e-2
# A rotor is SIMPLE iff its grade-4 part vanishes (<R>_4 == 0 <=> R = R1 with a
# single invariant plane). Above this, the rotor needs the invariant split.
_SIMPLE_GRADE4_TOL = 1e-10
# |discriminant| below this => the two invariant eigenvalues coincide (isoclinic).
_DEGEN_TOL = 1e-9
def _identity(dtype: np.dtype) -> np.ndarray:
@ -80,60 +75,40 @@ def make_rotor_from_angle(angle: float, bivector_idx: int = 6) -> np.ndarray:
def rotor_power(R: np.ndarray, alpha: float) -> np.ndarray:
"""Return R^alpha — the rotor on the manifold path from identity to R by alpha.
EXACT for ANY closed unit rotor in Cl(4,1), simple or not. A general rotor
factors (invariant / bivector decomposition) into two commuting SIMPLE
rotors ``R = R1 R2`` with distinct invariant planes; then, because they
commute, ``R^α = R1^α R2^α`` and each factor uses the simple closed form
below. The isoclinic case (coincident invariant planes) has its own closed
form. There is no iteration, no approximation, and no external library
the split is built from the Cl(4,1) geometric product alone.
Simple factor ``R_i = a + B`` (scalar + simple bivector):
For a simple unit rotor decomposed as ``R = a + B`` (scalar + bivector):
- rotation plane (`` < 0``): ``R^α = cos(α·θ/2) + (sin(α·θ/2)/|B|) · B``
where ``θ/2 = atan2(|B|, a)``.
- boost plane (`` > 0``): ``R^α = cosh(α·η/2) + (sinh(α·η/2)/|B|) · B``
where ``η/2 = atanh(|B|/a)``.
The result stays on the rotor manifold by construction, so
``versor_condition(rotor_power(R, α)) < 1e-6`` for any α whenever ``R`` is a
closed unit rotor. (Historically this returned the *identity* for non-simple
rotors an approximation where exactness was available, which silently
collapsed geodesic interpolation to a no-op. That corner is now closed.)
This is the proper slerp on the rotor manifold: it stays on the manifold
by construction, so ``versor_condition(rotor_power(R, α)) < 1e-6`` for any
α whenever ``R`` is itself a closed unit rotor.
Falls back to the identity rotor when ``R`` is not a closed scalar+bivector
rotor (e.g. carries higher-grade components or a non-simple bivector) so
callers never receive a manifold-violating output.
"""
R_arr = np.asarray(R, dtype=np.float64)
if R_arr.shape != (N_COMPONENTS,):
raise ValueError(
f"rotor_power expects a {N_COMPONENTS}-component rotor; got {R_arr.shape}."
)
dtype = _result_dtype(R_arr)
# <R>_4 == 0 <=> R is a single simple rotor. Otherwise take the split path.
if float(np.linalg.norm(grade_project(R_arr, 4))) >= _SIMPLE_GRADE4_TOL:
return _general_rotor_power(R_arr, alpha, dtype)
return _simple_rotor_power(R_arr, alpha, dtype)
def _simple_rotor_power(R_arr: np.ndarray, alpha: float, dtype: np.dtype) -> np.ndarray:
"""R^alpha for a SIMPLE rotor (scalar + one simple bivector). Exact closed form.
Behaviour is unchanged from the original ``rotor_power`` on simple inputs.
"""
a = float(R_arr[0])
B = R_arr.copy()
B[0] = 0.0
# A simple rotor's bivector squares to a scalar (B² is grade-0 only).
# Quick guard: bivector must be a simple bivector (B² is grade-0 only).
B_sq_full = geometric_product(B, B).astype(np.float64)
bsq_scalar = float(B_sq_full[0])
B_sq_higher = B_sq_full.copy()
B_sq_higher[0] = 0.0
if float(np.linalg.norm(B_sq_higher)) > 1e-6:
# Not a simple bivector under the simple dispatch — fail closed, never
# silently return identity (that zeros motion without a signal).
raise ValueError(
"rotor_power: non-simple bivector under simple dispatch "
f"(B² higher-grade residual {float(np.linalg.norm(B_sq_higher)):.3e})"
)
# Non-simple bivector — return identity to avoid drift.
return _identity(dtype)
# Near-identity: nothing to scale.
bivector_norm = float(np.linalg.norm(B))
@ -148,30 +123,19 @@ def _simple_rotor_power(R_arr: np.ndarray, alpha: float, dtype: np.dtype) -> np.
new_a = float(np.cos(alpha * theta_half))
new_b_mag = float(np.sin(alpha * theta_half))
elif bsq_scalar > 0.0:
# Boost plane. Domain of atanh requires |b_mag/a| < 1 and a > 0.
# Boost plane.
b_mag = float(np.sqrt(bsq_scalar))
if a <= 0.0 or abs(b_mag / a) >= 1.0 - 1e-12:
raise ValueError(
f"rotor_power: boost plane outside unit-rotor domain "
f"(a={a:.6g}, |B|/a={abs(b_mag / a) if a != 0.0 else float('inf'):.6g})"
)
# atanh requires |b_mag/a| < 1; for closed rotors a² - B² = 1 means
# |b_mag| < |a|, so this is safe when a > 0.
if a == 0.0:
return _identity(dtype)
eta_half = float(np.arctanh(b_mag / a))
new_a = float(np.cosh(alpha * eta_half))
new_b_mag = float(np.sinh(alpha * eta_half))
else:
# B² = 0: null bivector (translator generators in CGA). Exact binomial:
# (a + B)^α = a^α + α a^{α-1} B (higher powers of B vanish).
# Unit translators have a = 1 ⇒ T^α = 1 + α B = translator(α·a_eucl).
# Historically this returned identity — a silent zeroing of the Cartan
# translation leg in dual_correction_slerp (fidelity #16 follow-up).
if abs(a) < _NEAR_ZERO_TOL:
return _identity(dtype)
result = np.zeros(N_COMPONENTS, dtype=np.float64)
result[0] = float(a) ** float(alpha) if a > 0.0 else float(np.sign(a) * (abs(a) ** float(alpha)))
# Prefer real power for a>0; for a<0 (rare for unit translators) use |a|^α · sgn.
scale_B = float(alpha) * (float(a) ** (float(alpha) - 1.0)) if a > 0.0 else float(alpha) * (abs(a) ** (float(alpha) - 1.0)) * float(np.sign(a))
result = result + scale_B * B
return result.astype(dtype, copy=False)
# B² = 0: null bivector. Cannot interpolate on the manifold;
# return identity to fail safely.
return _identity(dtype)
result = np.zeros(N_COMPONENTS, dtype=np.float64)
result[0] = new_a
@ -180,92 +144,6 @@ def _simple_rotor_power(R_arr: np.ndarray, alpha: float, dtype: np.dtype) -> np.
return result.astype(dtype, copy=False)
def _isoclinic_power_coeffs(x: float, alpha: float) -> tuple[float, float, float]:
"""Power coefficients ``(A, f, c)`` for one of two identical (isoclinic) simple
factors with `` = x``: ``R_i^α = A + f · G_i``. Handles rotation, boost, and
the null limit uniformly.
"""
gsq = x - 1.0
c = float(np.sqrt(max(x, 0.0)))
if gsq < -1e-15: # rotation: c = cos(theta)
theta = float(np.arccos(min(1.0, max(-1.0, c))))
slin = float(np.sin(theta))
A = float(np.cos(alpha * theta))
f = float(np.sin(alpha * theta) / slin) if slin > 1e-300 else float(alpha)
elif gsq > 1e-15: # boost: c = cosh(eta)
eta = float(np.arccosh(max(1.0, c)))
slin = float(np.sinh(eta))
A = float(np.cosh(alpha * eta))
f = float(np.sinh(alpha * eta) / slin) if slin > 1e-300 else float(alpha)
else: # null / parabolic limit
A, f = 1.0, float(alpha)
return A, f, c
def _split_commuting_simple(
P: float, H: np.ndarray, W: np.ndarray, h0: float, disc: float
) -> tuple[np.ndarray, np.ndarray]:
"""Invariant decomposition of a non-simple rotor into two commuting SIMPLE
unit rotors ``R = R1 R2`` (distinct-eigenvalue branch).
With ``P = <R>_0``, ``H = <R>_2``, ``W = <R>_4``: the squared scalars of the
two simple factors are ``x_i = c_i²`` the roots of `` (2h0) t + ``
and each simple bivector ``G_i`` is recovered by the linear system in
``{H, HW}``. Returns ``(R1, R2)`` as 32-component rotors.
"""
b = 2.0 * P * P - h0
sq = float(np.sqrt(disc))
x1 = 0.5 * (b + sq)
x2 = 0.5 * (b - sq)
c1 = float(np.sqrt(max(x1, 0.0)))
c2 = float(np.sqrt(max(x2, 0.0)))
if P < 0.0:
c2 = -c2 # fix product sign so c1·c2 == <R>_0
g1sq = x1 - 1.0
g2sq = x2 - 1.0
HW = grade_project(geometric_product(H, W), 2).astype(np.float64)
det = c2 * c2 * g1sq - c1 * c1 * g2sq
if abs(det) < _NEAR_ZERO_TOL:
raise ValueError(
"rotor_power: singular invariant split (unexpected for distinct eigenvalues)"
)
G1 = (c2 * g1sq * H - c1 * HW) / det
G2 = (c2 * HW - c1 * g2sq * H) / det
R1 = G1.copy()
R1[0] = c1
R2 = G2.copy()
R2[0] = c2
return R1, R2
def _general_rotor_power(R_arr: np.ndarray, alpha: float, dtype: np.dtype) -> np.ndarray:
"""R^alpha for a NON-simple rotor via the invariant (bivector) decomposition."""
P = float(R_arr[0])
H = grade_project(R_arr, 2).astype(np.float64)
W = grade_project(R_arr, 4).astype(np.float64)
h0 = float(scalar_part(geometric_product(H, H)))
b = 2.0 * P * P - h0
disc = b * b - 4.0 * P * P
if disc <= _DEGEN_TOL:
# Isoclinic: coincident invariant planes (x1 == x2 == b/2). The result
# depends only on the symmetric functions H and W, so no per-plane split
# is needed: R^α = A² + (A·f/c)·H + f²·W.
A, f, c = _isoclinic_power_coeffs(0.5 * b, alpha)
if c < _NEAR_ZERO_TOL:
raise ValueError(
"rotor_power: isoclinic rotor at theta~pi/2 has no principal power"
)
out = (A * f / c) * H + (f * f) * W
out[0] += A * A
return out.astype(dtype, copy=False)
R1, R2 = _split_commuting_simple(P, H, W, h0, disc)
out = geometric_product(
_simple_rotor_power(R1, alpha, np.dtype(np.float64)),
_simple_rotor_power(R2, alpha, np.dtype(np.float64)),
)
return out.astype(dtype, copy=False)
def word_transition_rotor(A: np.ndarray, B: np.ndarray) -> np.ndarray:
"""
Compute the closed transition operator from source versor A to target B.

View file

@ -17,9 +17,9 @@ from __future__ import annotations
import json
from pathlib import Path
from packs.schema import AlignmentEdge
from language_packs.schema import AlignmentEdge
_DATA_DIR = Path(__file__).parent.parent / "packs" / "data"
_DATA_DIR = Path(__file__).parent.parent / "language_packs" / "data"
class AlignmentGraph:
@ -74,7 +74,7 @@ def load_alignment(
"""
Load AlignmentEdge records from <data_root>/<pack_id>/alignment.jsonl.
``data_root`` defaults to the committed ``packs/data`` tree; pass
``data_root`` defaults to the committed ``language_packs/data`` tree; pass
an alternate root (e.g. a test-fixture copy) to read packs from elsewhere
without forking the parser.

File diff suppressed because it is too large Load diff

View file

@ -1,282 +0,0 @@
"""Benchmark-only MLX exact CGA recall experiment.
ADR-0235 Lane 3: optional MLX score-vector experiment for CORE's exact
Cl(4,1) CGA recall workload. This module does not serve answers, does not
replace Python/Rust as semantic source of truth, does not use ANN, and does not
claim MLX as a runtime backend.
The MLX path computes the exact diagonal CGA score vector over deterministic
(N, 32) float32 fixtures. Scores are copied back to NumPy for the same stable
canonical top-k ordering used by the Python/Rust exact-recall oracle.
"""
from __future__ import annotations
import argparse
import json
import time
from dataclasses import dataclass
from typing import Any, Callable
import numpy as np
from benchmarks.apple_uma_mechanical_sympathy import (
DEFAULT_MEASURED,
DEFAULT_WARMUP,
N_COMPONENTS,
RECALL_N_VALUES,
RECALL_TOP_K,
synthetic_matrix,
synthetic_mv,
)
BENCHMARK_NAME = "CORE Apple Silicon MLX Exact CGA Recall Experiment"
BENCHMARK_VERSION = "0.1.0"
@dataclass(frozen=True, slots=True)
class TimingStats:
warmup_iterations: int
measured_iterations: int
min_ms: float
p50_ms: float
p95_ms: float
max_ms: float
mean_ms: float
ops_per_sec: float
def as_dict(self) -> dict[str, float | int]:
return {
"warmup_iterations": self.warmup_iterations,
"measured_iterations": self.measured_iterations,
"min_ms": round(self.min_ms, 6),
"p50_ms": round(self.p50_ms, 6),
"p95_ms": round(self.p95_ms, 6),
"max_ms": round(self.max_ms, 6),
"mean_ms": round(self.mean_ms, 6),
"ops_per_sec": round(self.ops_per_sec, 3),
}
def _measure_timing(
fn: Callable[[], Any],
*,
warmup: int = DEFAULT_WARMUP,
measured: int = DEFAULT_MEASURED,
) -> TimingStats:
for _ in range(warmup):
fn()
samples_ms: list[float] = []
for _ in range(measured):
t0 = time.perf_counter()
fn()
samples_ms.append((time.perf_counter() - t0) * 1000.0)
samples_ms.sort()
p95_index = max(0, int(round(0.95 * (len(samples_ms) - 1))))
mean_ms = float(np.mean(samples_ms))
return TimingStats(
warmup_iterations=warmup,
measured_iterations=measured,
min_ms=samples_ms[0],
p50_ms=float(np.median(samples_ms)),
p95_ms=samples_ms[p95_index],
max_ms=samples_ms[-1],
mean_ms=mean_ms,
ops_per_sec=(1000.0 / mean_ms) if mean_ms > 0 else 0.0,
)
def mlx_import_status() -> dict[str, Any]:
"""Return optional MLX availability without making it a dependency."""
try:
import mlx # type: ignore[import-not-found]
import mlx.core as mx # type: ignore[import-not-found]
except ImportError as exc:
return {"import_succeeded": False, "reason": str(exc)}
except Exception as exc:
return {"import_succeeded": False, "reason": f"MLX import failed: {exc}"}
status: dict[str, Any] = {
"import_succeeded": True,
"module": "mlx.core",
"version": getattr(mlx, "__version__", None),
"benchmark_only": True,
"serving_authorized": False,
}
try:
status["default_device"] = str(mx.default_device())
except Exception as exc:
status["default_device_error"] = str(exc)
return status
def _stable_top_k_from_scores(scores: np.ndarray, top_k: int) -> list[tuple[int, float]]:
scores = np.asarray(scores, dtype=np.float32)
k = min(top_k, scores.shape[0])
if k <= 0:
return []
if k < scores.shape[0]:
cand = np.argpartition(-scores, k - 1)[:k]
else:
cand = np.arange(scores.shape[0])
order = np.lexsort((cand, -scores[cand]))
cand = cand[order]
return [(int(i), float(scores[i])) for i in cand]
def _cga_inner_metric() -> np.ndarray:
from algebra import backend as alg_backend
metric = getattr(alg_backend, "_CGA_INNER_METRIC")
return np.asarray(metric, dtype=np.float32)
def mlx_exact_score_vector(matrix: np.ndarray, query: np.ndarray) -> np.ndarray:
"""Compute exact CGA recall scores with MLX, then copy scores to NumPy.
This intentionally performs only the score-vector workload in MLX. The
stable top-k ordering remains canonical NumPy/Python to avoid depending on
MLX top-k API details and to preserve CORE's deterministic ordering rule.
"""
import mlx.core as mx # type: ignore[import-not-found]
matrix_f32 = np.ascontiguousarray(matrix, dtype=np.float32)
query_f32 = np.ascontiguousarray(query, dtype=np.float32)
metric_f32 = np.ascontiguousarray(_cga_inner_metric(), dtype=np.float32)
mx_matrix = mx.array(matrix_f32)
mx_query = mx.array(query_f32)
mx_metric = mx.array(metric_f32)
scores = mx.zeros((matrix_f32.shape[0],), dtype=mx.float32)
for i in range(N_COMPONENTS):
scores = scores + (mx_metric[i] * mx_matrix[:, i]) * mx_query[i]
eval_fn = getattr(mx, "eval", None)
if callable(eval_fn):
eval_fn(scores)
return np.asarray(scores, dtype=np.float32)
def _parity_report(
*,
canonical: list[tuple[int, float]],
candidate: list[tuple[int, float]],
) -> dict[str, Any]:
canonical_indices = [i for i, _ in canonical]
candidate_indices = [i for i, _ in candidate]
deltas = [abs(float(a[1]) - float(b[1])) for a, b in zip(canonical, candidate)]
max_abs_score_delta = max(deltas) if deltas else 0.0
return {
"top_k_indices_match": canonical_indices == candidate_indices,
"max_abs_score_delta": round(float(max_abs_score_delta), 8),
"scores_close": bool(max_abs_score_delta <= 1e-4),
"parity_pass": canonical_indices == candidate_indices and max_abs_score_delta <= 1e-4,
}
def run_mlx_exact_recall_experiment(
*,
warmup: int = DEFAULT_WARMUP,
measured: int = DEFAULT_MEASURED,
mlx_status: dict[str, Any] | None = None,
) -> dict[str, Any]:
from algebra import backend as alg_backend
status = mlx_status or mlx_import_status()
if not status.get("import_succeeded"):
return {
"benchmark_name": BENCHMARK_NAME,
"benchmark_version": BENCHMARK_VERSION,
"track": "mlx_exact_cga_recall",
"skipped": True,
"reason": f"MLX unavailable: {status.get('reason', 'mlx.core import failed')}",
"mlx_status": status,
"benchmark_only": True,
"serving_authorized": False,
"semantic_backend": "python/rust canonical exact recall",
"non_claims": [
"No MLX semantic-backend claim.",
"No serving integration.",
"No ANN or approximate recall.",
"No CoreML or Neural Engine claim.",
],
}
cases: list[dict[str, Any]] = []
for n in RECALL_N_VALUES:
matrix = synthetic_matrix(n, seed=n % 17)
query = synthetic_mv(seed=5)
canonical = alg_backend.vault_recall(
[],
query,
top_k=RECALL_TOP_K,
prebuilt_matrix=matrix,
)
def _run_scores() -> np.ndarray:
return mlx_exact_score_vector(matrix, query)
timing = _measure_timing(_run_scores, warmup=warmup, measured=measured)
scores = _run_scores()
candidate = _stable_top_k_from_scores(scores, RECALL_TOP_K)
parity = _parity_report(canonical=canonical, candidate=candidate)
rows_per_sec = (n / (timing.mean_ms / 1000.0)) if timing.mean_ms > 0 else 0.0
cases.append(
{
"N": n,
"top_k": RECALL_TOP_K,
"dtype": "float32",
"contiguous": bool(matrix.flags["C_CONTIGUOUS"]),
"backend_used": "mlx",
"semantic_backend": "canonical exact recall via algebra.backend.vault_recall",
"copy_in_boundary": "NumPy contiguous float32 matrix/query copied into MLX arrays",
"copy_out_boundary": "MLX score vector copied to NumPy for canonical stable top-k ordering",
"timing": timing.as_dict(),
"rows_per_sec": round(rows_per_sec, 3),
"parity": parity,
"top_result_preview": candidate[:3],
"canonical_preview": canonical[:3],
}
)
return {
"benchmark_name": BENCHMARK_NAME,
"benchmark_version": BENCHMARK_VERSION,
"track": "mlx_exact_cga_recall",
"skipped": False,
"mlx_status": status,
"benchmark_only": True,
"serving_authorized": False,
"semantic_backend": "python/rust canonical exact recall",
"score_computation": "MLX exact diagonal CGA score vector; no ANN or approximate search",
"top_k_ordering": "canonical NumPy stable ordering after score copy-out",
"copy_boundary": {
"input": "NumPy -> MLX array copy at benchmark boundary",
"output": "MLX score vector -> NumPy copy for stable top-k",
"zero_copy_input": "no",
},
"non_claims": [
"No MLX semantic-backend claim.",
"No serving integration.",
"No ANN or approximate recall.",
"No CoreML or Neural Engine claim.",
],
"cases": cases,
}
def _cli_main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=BENCHMARK_NAME)
parser.add_argument("--json", action="store_true", help="emit machine-readable JSON")
parser.add_argument("--warmup", type=int, default=DEFAULT_WARMUP)
parser.add_argument("--measured", type=int, default=DEFAULT_MEASURED)
args = parser.parse_args(argv)
report = run_mlx_exact_recall_experiment(warmup=args.warmup, measured=args.measured)
if args.json:
print(json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True))
else:
print(f"{BENCHMARK_NAME} — use --json")
return 0
if __name__ == "__main__":
raise SystemExit(_cli_main())

View file

@ -1,382 +0,0 @@
"""Apple UMA PersonaMotor Benchmark — ADR-0027 / ADR-0028 proof of concept.
Measures the VRAM footprint and execution latency of the Cl(4,1) versor
sandwich product applied during generation field-walking, compiled into a
fused Metal kernel via ``@mx.compile``.
The three identity packs exercised below correspond to the axis directions
that ``PersonaMotor.from_identity_manifold`` would derive from real pack
JSON. They are constructed inline here so that this benchmark has zero
dependency on the pack loader path the motor geometry is identical to
what the runtime builds.
Key claims proved by this script
---------------------------------
Topological Cost Neutrality (ADR-0027):
Peak VRAM and step latency should be statistically indistinguishable
across identity.default_general_v1, identity.precision_first_v1, and
identity.generosity_first_v1. Changing CORE's behavioral character
incurs no additional GPU overhead there is no "alignment tax".
Backpressure Validation (ADR-0028):
The ``if step % 50 == 0: mx.eval(F)`` boundary mirrors the async
token-yielding rhythm of ``ChatRuntime``. An Active VRAM Delta of
~0.00 MB confirms that the lazy MLX computation graph is cleared safely
at each yield point and does not accumulate unboundedly.
Correctness notes
-----------------
``PersonaMotor.apply()`` calls ``algebra.versor.versor_apply``, which is
a NumPy path. The ``compiled_field_step`` below replicates the sandwich
product arithmetic directly in MLX so that the Metal kernel-fusion path
is exercised. The benchmark does not call ``motor.apply(F)`` on an MLX
array that would silently fall back to NumPy and defeat the purpose.
"""
from __future__ import annotations
import argparse
import json
import time
from dataclasses import dataclass
from typing import Any
import numpy as np
from core.physics.identity import IdentityManifold, ValueAxis
from persona.motor import PersonaMotor
BENCHMARK_NAME = "CORE Apple UMA PersonaMotor Benchmark"
BENCHMARK_VERSION = "0.1.0"
# Cl(4,1) multivector dimensionality — 2^5 = 32 components.
CGA_DIM = 32
# Pack definitions: axis directions that the real JSON packs would supply.
# Each direction is normalised; PersonaMotor.from_identity_manifold normalises
# again, but pre-normalising here keeps the motor magnitudes consistent and
# makes the "cost neutrality" claim legible without runtime pack loading.
_PACK_DEFS: list[tuple[str, list[tuple[str, tuple[float, float, float]]]]] = [
(
"identity.default_general_v1",
[
("truth_seeking", (0.577, 0.577, 0.577)),
("helpfulness", (0.577, 0.577, 0.577)),
],
),
(
"identity.precision_first_v1",
[
("precision", (1.0, 0.0, 0.0)),
("epistemic_care", (0.0, 1.0, 0.0)),
],
),
(
"identity.generosity_first_v1",
[
("generosity", (0.0, 0.0, 1.0)),
("warmth", (0.707, 0.707, 0.0)),
],
),
]
def _build_manifold_and_motor(
axes: list[tuple[str, tuple[float, float, float]]],
) -> PersonaMotor:
value_axes = tuple(
ValueAxis(name=name, direction=direction)
for name, direction in axes
)
manifold = IdentityManifold(value_axes=value_axes)
return PersonaMotor.from_identity_manifold(manifold)
def mlx_import_status() -> dict[str, Any]:
"""Return optional MLX availability without making it a hard dependency."""
try:
import mlx # type: ignore[import-not-found]
import mlx.core as mx # type: ignore[import-not-found]
except ImportError as exc:
return {"import_succeeded": False, "reason": str(exc)}
except Exception as exc:
return {"import_succeeded": False, "reason": f"MLX import failed: {exc}"}
status: dict[str, Any] = {
"import_succeeded": True,
"module": "mlx.core",
"version": getattr(mlx, "__version__", None),
"benchmark_only": True,
"serving_authorized": False,
}
try:
status["default_device"] = str(mx.default_device())
except Exception as exc:
status["default_device_error"] = str(exc)
return status
@dataclass(frozen=True, slots=True)
class MotorStepStats:
pack_id: str
steps: int
batch_size: int
total_latency_ms: float
per_step_ms: float
active_vram_delta_mb: float
peak_vram_mb: float
metal_available: bool
def as_dict(self) -> dict[str, Any]:
return {
"pack_id": self.pack_id,
"steps": self.steps,
"batch_size": self.batch_size,
"total_latency_ms": round(self.total_latency_ms, 3),
"per_step_ms": round(self.per_step_ms, 6),
"active_vram_delta_mb": round(self.active_vram_delta_mb, 4),
"peak_vram_mb": round(self.peak_vram_mb, 4),
"metal_available": self.metal_available,
}
def profile_motor_sandwich(
motor: PersonaMotor,
*,
pack_id: str,
batch_size: int = 128,
steps: int = 1_000,
) -> MotorStepStats:
"""Profile the compiled Cl(4,1) sandwich product on Apple UMA.
The sandwich product F <- M * F * reverse(M) is reproduced here in
pure MLX arithmetic so that ``@mx.compile`` can fuse it into a single
Metal dispatch. The motor ``M`` is extracted from the NumPy
``PersonaMotor`` instance once and converted to an MLX constant.
The ``if step % 50 == 0: mx.eval(F)`` boundary is load-bearing: it
mirrors the async token-yield rhythm of ``ChatRuntime`` and is the
mechanism that prevents unbounded lazy-graph accumulation on Apple UMA.
"""
import mlx.core as mx # type: ignore[import-not-found]
try:
import mlx.metal as metal # type: ignore[import-not-found]
metal_available = metal.is_available()
except Exception:
metal_available = False
# Convert the NumPy motor multivector to a frozen MLX constant.
# reverse(M) in Cl(4,1): negate grades 2 and 3 (indices match the
# algebra.cl41 basis ordering — grade-0 index 0, grade-1 indices 15,
# grade-2 indices 615, grade-3 indices 1625, grade-4 2630, grade-5 31).
M_np = motor.M.astype(np.float32)
rev_M_np = M_np.copy()
rev_M_np[6:16] *= -1.0 # grade-2 components
rev_M_np[16:26] *= -1.0 # grade-3 components
mx_M = mx.array(M_np) # shape (32,)
mx_rev_M = mx.array(rev_M_np) # shape (32,)
# Initialise the field matrix F of shape (batch_size, CGA_DIM).
F = mx.random.normal((batch_size, CGA_DIM))
mx.eval(F)
@mx.compile
def compiled_field_step(current_F: mx.array) -> mx.array:
# Batched sandwich: for each row f in F compute M * f * reverse(M).
# In Cl(4,1) we use the scalar projection of the bilinear form as a
# fast proxy for the full geometric product — sufficient to measure
# the kernel-fusion overhead without re-implementing the full
# 32x32x32 structure tensor here.
# Left multiply: scale each row by M component-wise (Hadamard);
# sum over CGA_DIM to project onto the grade-0 scalar, then broadcast
# back to maintain the (batch, 32) shape for the right multiply.
left = current_F * mx_M[None, :] # (batch, 32)
right = left * mx_rev_M[None, :] # (batch, 32)
return right
# Warm-up: let Metal compile and cache the shader.
for _ in range(10):
F_warmup = compiled_field_step(F)
mx.eval(F_warmup)
# --- Apple UMA memory baseline ---
if metal_available:
metal.reset_peak_memory()
start_active = metal.get_active_memory()
else:
start_active = 0
t0 = time.perf_counter()
for i in range(steps):
F = compiled_field_step(F)
# CRITICAL: flush the lazy graph periodically to mirror ChatRuntime
# token-yield backpressure (ADR-0028). Without this the MLX DAG
# accumulates across all steps and inflates UMA usage.
if i % 50 == 0:
mx.eval(F)
mx.eval(F)
total_ms = (time.perf_counter() - t0) * 1_000.0
if metal_available:
end_active = metal.get_active_memory()
peak_mem = metal.get_peak_memory()
else:
end_active = peak_mem = 0
return MotorStepStats(
pack_id=pack_id,
steps=steps,
batch_size=batch_size,
total_latency_ms=total_ms,
per_step_ms=total_ms / steps,
active_vram_delta_mb=(end_active - start_active) / (1024 * 1024),
peak_vram_mb=peak_mem / (1024 * 1024),
metal_available=metal_available,
)
def run_persona_motor_benchmark(
*,
steps: int = 1_000,
batch_size: int = 128,
mlx_status: dict[str, Any] | None = None,
) -> dict[str, Any]:
status = mlx_status or mlx_import_status()
if not status.get("import_succeeded"):
return {
"benchmark_name": BENCHMARK_NAME,
"benchmark_version": BENCHMARK_VERSION,
"track": "apple_uma_persona_motor",
"skipped": True,
"reason": f"MLX unavailable: {status.get('reason', 'mlx.core import failed')}",
"mlx_status": status,
"benchmark_only": True,
"serving_authorized": False,
}
results: list[dict[str, Any]] = []
for pack_id, axes in _PACK_DEFS:
motor = _build_manifold_and_motor(axes)
stats = profile_motor_sandwich(
motor,
pack_id=pack_id,
batch_size=batch_size,
steps=steps,
)
results.append(stats.as_dict())
# Cost-neutrality check: latency spread across packs should be <10%.
latencies = [r["per_step_ms"] for r in results]
lat_spread_pct = (
((max(latencies) - min(latencies)) / max(latencies)) * 100.0
if max(latencies) > 0
else 0.0
)
vram_deltas = [r["active_vram_delta_mb"] for r in results]
backpressure_valid = all(abs(d) < 1.0 for d in vram_deltas)
return {
"benchmark_name": BENCHMARK_NAME,
"benchmark_version": BENCHMARK_VERSION,
"track": "apple_uma_persona_motor",
"skipped": False,
"mlx_status": status,
"benchmark_only": True,
"serving_authorized": False,
"simulation": {
"steps": steps,
"batch_size": batch_size,
"cga_dim": CGA_DIM,
"eval_boundary_every_n_steps": 50,
},
"adr_claims": {
"ADR-0027_topological_cost_neutrality": {
"description": (
"Peak VRAM and step latency are statistically equal across "
"identity packs — changing persona incurs no alignment tax."
),
"latency_spread_pct": round(lat_spread_pct, 2),
"pass": lat_spread_pct < 10.0,
},
"ADR-0028_backpressure_validation": {
"description": (
"Active VRAM Delta ~0 MB proves that periodic mx.eval() "
"boundaries flush the lazy MLX graph safely, mirroring "
"ChatRuntime async token-yield backpressure."
),
"all_active_vram_deltas_mb": vram_deltas,
"pass": backpressure_valid,
},
},
"cases": results,
"non_claims": [
"No MLX serving-backend claim.",
"No replacement of the NumPy versor_apply canonical path.",
"No ANN or approximate search.",
"No CoreML or Neural Engine claim.",
],
}
def _cli_main(argv: list[str] | None = None) -> int:
parser = argparse.ArgumentParser(description=BENCHMARK_NAME)
parser.add_argument(
"--json", action="store_true", help="emit machine-readable JSON"
)
parser.add_argument(
"--steps", type=int, default=1_000,
help="number of sandwich-product propagation steps (default: 1000)",
)
parser.add_argument(
"--batch", type=int, default=128,
help="field walk batch size — rows in the (batch, 32) CGA matrix (default: 128)",
)
args = parser.parse_args(argv)
report = run_persona_motor_benchmark(steps=args.steps, batch_size=args.batch)
if args.json:
print(json.dumps(report, ensure_ascii=False, indent=2, sort_keys=True))
return 0
if report.get("skipped"):
print(f"{BENCHMARK_NAME} — SKIPPED: {report['reason']}")
return 0
print(f"\n=== {BENCHMARK_NAME} ===")
sim = report["simulation"]
print(
f"Simulation: {sim['steps']} steps | batch={sim['batch_size']} | "
f"CGA dim={sim['cga_dim']} | eval every {sim['eval_boundary_every_n_steps']} steps\n"
)
print(f"{'Pack ID':<40} {'Latency/step':>14} {'VRAM Delta':>12} {'Peak VRAM':>12}")
print("-" * 82)
for case in report["cases"]:
print(
f"{case['pack_id']:<40} "
f"{case['per_step_ms']:>13.4f}ms "
f"{case['active_vram_delta_mb']:>11.2f}MB "
f"{case['peak_vram_mb']:>11.2f}MB"
)
print()
claims = report["adr_claims"]
neutrality = claims["ADR-0027_topological_cost_neutrality"]
backpressure = claims["ADR-0028_backpressure_validation"]
print(
f"ADR-0027 Cost Neutrality — latency spread {neutrality['latency_spread_pct']:.1f}% "
f"{'PASS' if neutrality['pass'] else 'FAIL'}"
)
print(
f"ADR-0028 Backpressure — VRAM deltas {backpressure['all_active_vram_deltas_mb']} "
f"{'PASS' if backpressure['pass'] else 'FAIL'}"
)
print()
return 0
if __name__ == "__main__":
raise SystemExit(_cli_main())

View file

@ -288,7 +288,7 @@ def run_footprint(*, pack_id: str = "en_core_cognition_v1") -> FootprintReport:
rss_post = _rss_bytes()
py_bytes, py_modules = _measure_python_runtime()
pack_path = PROJECT_ROOT / "packs" / "data" / pack_id
pack_path = PROJECT_ROOT / "language_packs" / "data" / pack_id
vault_path = PROJECT_ROOT / "vault"
rust_path = _rust_artifact_path()
seed_packs_path = PROJECT_ROOT / "packs"

View file

@ -152,7 +152,7 @@ def bench_backend_speedup() -> BenchResult:
will be tracked when the doctrine clock advances.
"""
from field.operators import GraphDiffusionOperator
from packs.compiler import load_pack
from language_packs.compiler import load_pack
from scripts.run_pulse import _build_manifold
_, manifold = load_pack("en_core_cognition_v1")
@ -225,7 +225,7 @@ def bench_versor_closure_audit() -> BenchResult:
"""Run pulse for all eval cases, verify versor_condition < 1e-6 at every step."""
from algebra.backend import versor_condition
from field.operators import GraphDiffusionOperator, ConstraintCorrectionOperator
from packs.compiler import load_pack
from language_packs.compiler import load_pack
from scripts.run_pulse import _build_manifold
_, manifold = load_pack("en_core_cognition_v1")

View file

@ -1,17 +0,0 @@
# Calibration Package
`calibration/` is the deterministic operator-parameter replay and tuning
package. It explores bounded `CalibrationParams` candidates against eval cases
and emits before/after metrics for review.
It is not the ADR-0175 reliability ledger and it does not grant serving
licenses. Serving discipline is owned by `core.reliability_gate`; Workbench reads
that evidence through `workbench/calibration.py` without re-running lanes or
mutating license state.
This boundary is intentional:
- `calibration/params.py`, `calibration/replay.py`, `calibration/tune.py`, and
`calibration/report.py` support deterministic parameter audits.
- `workbench/calibration.py` projects committed practice and serving artifacts
into a read-only UI/API surface.

View file

@ -60,7 +60,7 @@ _ANCHOR_LENS_SUBSTRATE_PACK_IDS: dict[str, tuple[str, ...]] = {
_PACK_LEXICON_PATH = (
Path(__file__).resolve().parent.parent
/ "packs"
/ "language_packs"
/ "data"
/ PACK_ID
/ "lexicon.jsonl"
@ -76,7 +76,7 @@ _PACK_LEXICON_PATH = (
# lemmas — only lens-engagement reads from here.
_COLLAPSE_ANCHORS_LEXICON_PATH = (
Path(__file__).resolve().parent.parent
/ "packs"
/ "language_packs"
/ "data"
/ "en_collapse_anchors_v1"
/ "lexicon.jsonl"
@ -130,7 +130,7 @@ def _frame_gloss(lemma: str, pos: str, gloss: str) -> str:
* (unknown) -> "{Lemma}: {gloss}." (back-compat fallback)
The glosses are authored to match these frames exactly (see
the subagent briefs and ``packs/data/<pack>/glosses.jsonl``).
the subagent briefs and ``language_packs/data/<pack>/glosses.jsonl``).
Capitalization is applied only to the framed surface, never to
the lemma in the lexicon (which stays lowercase by convention).
"""
@ -253,7 +253,7 @@ def _substrate_lexicon_by_entry_id(pack_id: str) -> dict[str, tuple[str, ...]]:
"""
lexicon_path = (
Path(__file__).resolve().parent.parent
/ "packs"
/ "language_packs"
/ "data"
/ pack_id
/ "lexicon.jsonl"

View file

@ -32,10 +32,8 @@ Design constraints (CLAUDE.md / Reconstruction-over-storage):
from __future__ import annotations
import json
from dataclasses import dataclass
from functools import lru_cache
from pathlib import Path
from typing import Sequence
# Default mounted lexicon-pack ids that ADR-0063 surface composers
# consult. Order matters: earlier packs win on lemma collision. This
@ -71,47 +69,7 @@ DEFAULT_RESOLVABLE_PACK_IDS: tuple[str, ...] = (
"en_collapse_anchors_v1",
)
# 3-core-language (English + Hebrew root density + Koine Greek Logos precision)
# depth packs for LexicalResolution. These are used alongside DEFAULT when
# building PropositionGraph nodes with language/root/morphology_id for
# bidirectional comprehension/articulation/contemplation.
# Sourced to align with anchor-lens substrate packs.
DEPTH_PACK_IDS: tuple[str, ...] = (
"he_core_cognition_v1",
"he_logos_micro_v1",
"grc_logos_cognition_v1",
"grc_logos_micro_v1",
)
@dataclass(frozen=True, slots=True)
class LexicalResolution:
"""Immutable, shared lexical resolution for masterful bidirectional use.
Serves comprehension (ingest grounded PropositionGraph via resolve_gloss
path), articulation (graph realize_semantic can consult depth for
precision), and internal reasoning/contemplation (operations "between"
read/write on the same substrate).
Three core languages:
- English: operational base
- Hebrew: root density (e.g. ד-ב-ר for utterance/word)
- Koine Greek: Logos precision (structuring principle, John 1:1)
The geometric field and PropositionGraph are designed to hold this depth.
All fields are plain values; no mutation.
"""
pack_id: str
lemma: str
language: str
pos: str = ""
gloss: str | None = None
semantic_domains: tuple[str, ...] = ()
morphology_id: str | None = None
root: str | None = None
_PACK_ROOT = Path(__file__).resolve().parent.parent / "packs" / "data"
_PACK_ROOT = Path(__file__).resolve().parent.parent / "language_packs" / "data"
@lru_cache(maxsize=16)
@ -278,157 +236,6 @@ def resolve_gloss(
return None
@lru_cache(maxsize=16)
def _pack_full_lexicon_for(pack_id: str) -> dict[str, dict]:
"""Return richer {lemma_lower: entry} including language, morphology_id,
semantic_domains for 3-language depth resolution.
Mirrors _pack_lexicon_for structure but retains the full fields needed
for LexicalResolution (Hebrew/Greek root-linked entries etc.).
Immutable packs safe to cache.
"""
lexicon_path = _PACK_ROOT / pack_id / "lexicon.jsonl"
if not lexicon_path.exists():
return {}
out: dict[str, dict] = {}
for line in lexicon_path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line:
continue
try:
entry = json.loads(line)
except json.JSONDecodeError:
continue
if not isinstance(entry, dict):
continue
lemma = entry.get("lemma") or entry.get("surface")
if not lemma or not isinstance(lemma, str):
continue
key = lemma.strip().lower()
# retain relevant depth fields
out[key] = {
"language": entry.get("language") or "en",
"morphology_id": entry.get("morphology_id"),
"semantic_domains": tuple(str(d) for d in entry.get("semantic_domains", ())),
"pos": entry.get("pos") or "",
}
return out
@lru_cache(maxsize=16)
def _pack_morph_roots_for(pack_id: str) -> dict[str, str]:
"""Return {morphology_id: root} for the pack's morphology.jsonl.
Enables root-level depth (Hebrew triconsonantal, Greek stems) without
pulling the full MorphologyRegistry into the hot resolver path.
"""
morph_path = _PACK_ROOT / pack_id / "morphology.jsonl"
if not morph_path.exists():
return {}
out: dict[str, str] = {}
for line in morph_path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line:
continue
try:
entry = json.loads(line)
except json.JSONDecodeError:
continue
if not isinstance(entry, dict):
continue
mid = entry.get("morphology_id")
root = entry.get("root")
if isinstance(mid, str) and isinstance(root, str) and mid and root:
out[mid] = root
return out
def resolve_entry(
lemma: str,
pack_ids: tuple[str, ...] = DEFAULT_RESOLVABLE_PACK_IDS,
) -> LexicalResolution | None:
"""Return rich LexicalResolution for the first matching pack.
This is the canonical entry point for depth-aware resolution usable
both for comprehension grounding and (symmetrically) articulation /
contemplation. Falls back gracefully for English-centric packs.
First-match-wins, same order as resolve_lemma/resolve_gloss.
"""
if not lemma or not isinstance(lemma, str):
return None
key = lemma.strip().lower()
if not key:
return None
for pack_id in pack_ids:
full = _pack_full_lexicon_for(pack_id)
if key not in full:
continue
info = full[key]
# gloss is optional but preferred when present
glosses = _pack_glosses_for(pack_id)
pos, gloss = glosses.get(key, ("", None))
if not gloss:
# fall back to pos from full if no dedicated gloss
pos = info.get("pos", "") or pos
morph_id = info.get("morphology_id")
root = None
if morph_id:
roots = _pack_morph_roots_for(pack_id)
root = roots.get(morph_id)
return LexicalResolution(
pack_id=pack_id,
lemma=lemma,
language=info.get("language", "en"),
pos=pos or "",
gloss=gloss,
semantic_domains=info.get("semantic_domains", ()),
morphology_id=morph_id,
root=root,
)
return None
def resolve_token_depths(
tokens: Sequence[str],
pack_ids: tuple[str, ...] | None = None,
) -> tuple[dict[str, dict], str | None]:
"""Resolve he/grc depth for raw tokens before PropositionGraph exists.
Same-turn recognition needs root data before graph build fills
``node_depths`` with ``p*`` ids. This returns provisional depths keyed
by ``t{i}`` (token index) for tokens that resolve with he/grc language
and a root, plus the first such provisional node id as the agent
candidate.
Pure relative to pack lexicon lookups (deterministic exact match).
Empty when no depth-bearing tokens are present.
"""
if pack_ids is None:
pack_ids = DEFAULT_RESOLVABLE_PACK_IDS + DEPTH_PACK_IDS
depths: dict[str, dict] = {}
agent_node_id: str | None = None
if not tokens:
return depths, agent_node_id
for i, tok in enumerate(tokens):
if not isinstance(tok, str) or not tok.strip():
continue
res = resolve_entry(tok, pack_ids=pack_ids)
if res is None:
continue
lang = res.language
root = res.root
if lang not in ("he", "grc") or not root:
continue
nid = f"t{i}"
entry: dict = {"language": lang, "root": root}
if res.morphology_id:
entry["morphology_id"] = res.morphology_id
depths[nid] = entry
if agent_node_id is None:
agent_node_id = nid
return depths, agent_node_id
def clear_resolver_cache() -> None:
"""Drop all caches in this module — lexicon AND glosses.
@ -444,99 +251,3 @@ def clear_resolver_cache() -> None:
"""
_pack_lexicon_for.cache_clear()
_pack_glosses_for.cache_clear()
_pack_lemma_to_entry_id.cache_clear()
_pack_senses_for.cache_clear()
@lru_cache(maxsize=16)
def _pack_lemma_to_entry_id(pack_id: str) -> dict[str, str]:
"""Return ``{lemma_lower: entry_id}`` from the pack's lexicon.jsonl."""
lexicon_path = _PACK_ROOT / pack_id / "lexicon.jsonl"
if not lexicon_path.exists():
return {}
out: dict[str, str] = {}
for line in lexicon_path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line:
continue
try:
entry = json.loads(line)
except json.JSONDecodeError:
continue
if not isinstance(entry, dict):
continue
lemma = entry.get("lemma") or entry.get("surface")
if not lemma or not isinstance(lemma, str):
continue
entry_id = entry.get("entry_id")
if isinstance(entry_id, str) and entry_id.strip():
out[lemma.lower()] = entry_id.strip()
return out
@lru_cache(maxsize=16)
def _pack_senses_for(pack_id: str) -> dict[str, dict]:
"""Return ``{lemma_id: geometric_signature}`` from the pack's optional
``senses.jsonl`` file. Also maps ``lemma`` as a fallback key.
"""
path = _PACK_ROOT / pack_id / "senses.jsonl"
if not path.exists():
return {}
out: dict[str, dict] = {}
for line in path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line:
continue
try:
entry = json.loads(line)
except json.JSONDecodeError:
continue
if not isinstance(entry, dict):
continue
geometric_signature = entry.get("geometric_signature")
if not isinstance(geometric_signature, dict):
continue
lemma_id = entry.get("lemma_id")
if isinstance(lemma_id, str) and lemma_id.strip():
out[lemma_id.strip()] = geometric_signature
# Optional fallback for direct lemma matching if lemma is present
lemma = entry.get("lemma")
if isinstance(lemma, str) and lemma.strip():
out[lemma.strip().lower()] = geometric_signature
return out
def resolve_geometric_signature(
lemma: str,
pack_ids: tuple[str, ...] = DEFAULT_RESOLVABLE_PACK_IDS,
) -> tuple[str, dict] | None:
"""Return ``(pack_id, geometric_signature)`` for the first pack in
*pack_ids* that BOTH (a) ratifies *lemma* in its ``lexicon.jsonl``
AND (b) carries a geometric_signature for it in ``senses.jsonl``.
"""
if not lemma or not isinstance(lemma, str):
return None
key = lemma.strip().lower()
if not key:
return None
for pack_id in pack_ids:
lex = _pack_lexicon_for(pack_id)
if key not in lex:
continue
lemma_ids = _pack_lemma_to_entry_id(pack_id)
entry_id = lemma_ids.get(key)
senses = _pack_senses_for(pack_id)
if entry_id and entry_id in senses:
return (pack_id, senses[entry_id])
if key in senses:
return (pack_id, senses[key])
return None

View file

@ -20,8 +20,8 @@ The refusal surface remains:
``refusal_commitments`` to count. Safety is always in scope; the
pack-layer doctrine in ADR-0029 prohibits opting safety out.
See `docs/adr/ADR-0036-safety-refusal-policy.md` and
`docs/adr/ADR-0037-per-predicate-ethics-refusal.md`.
See `docs/decisions/ADR-0036-safety-refusal-policy.md` and
`docs/decisions/ADR-0037-per-predicate-ethics-refusal.md`.
"""
from __future__ import annotations

View file

@ -60,10 +60,6 @@ from core.engine_identity import (
IdentityReconciliation,
engine_identity_for_config,
reconcile_loaded_identity,
compute_engine_identity,
DEFAULT_SAFETY_PACK,
DEFAULT_REGISTER_PACK,
DEFAULT_ANCHOR_LENS,
)
from recognition.anti_unifier import derive_recognizer
from recognition.outcome import FeatureBundle
@ -107,7 +103,7 @@ from generate.result import GenerationResult
from generate.stream import generate
from generate.surface import SentenceAssembler, SentencePlan, SurfaceContext
from ingest.gate import inject
from packs import OOVPolicy, load_mounted_packs, load_pack, load_pack_entries
from language_packs import OOVPolicy, load_mounted_packs, load_pack, load_pack_entries
from persona.motor import PersonaMotor
from session.context import SessionContext
from session.correction import CorrectionPass
@ -608,7 +604,6 @@ class ChatRuntime:
pack_ids = tuple(config.input_packs)
self.config = resolved_config
self._last_node_depths: dict | None = None # 3-lang PropGraph depth propagation (he/grc roots) to contemplate paths; set by pipeline
manifests = []
manifolds = []
entries = []
@ -763,14 +758,7 @@ class ChatRuntime:
# next checkpoint). ``_loaded_engine_identity`` stays "" at genesis.
# ADR-0220: identity is the ratified PACKS only — the build revision is
# provenance (manifest written_at_revision), not an identity input.
pack_ids = {
"identity": self.identity_pack_id,
"safety": DEFAULT_SAFETY_PACK,
"ethics": self.ethics_pack_id,
"register": self.register_pack_id or DEFAULT_REGISTER_PACK,
"anchor_lens": self.anchor_lens_id or DEFAULT_ANCHOR_LENS,
}
self._engine_identity: str = compute_engine_identity(pack_ids)
self._engine_identity: str = engine_identity_for_config(self.config)
self._loaded_engine_identity: str = ""
# CL — the persistent reviewed-learning proposal log. ``idle_tick()``
# advances it during idle (proposal-only); it lives alongside the engine
@ -888,18 +876,10 @@ class ChatRuntime:
self._pending_recognizer_examples.clear()
candidates_to_save = self._pending_candidates
if self.config.auto_contemplate and candidates_to_save:
# 3-lang depth propagation contract (AC5 / review):
# _last_node_depths is written by CognitiveTurnPipeline after PropGraph construction
# (from resolver + build_node_depths on enriched GraphNodes). It is forwarded here
# as depth= into teaching.contemplation.contemplate so that framing findings and
# proposed_chain carry depth_roots for he/grc. Same contract used for runtime
# contemplate and candidate paths. See also core/cognition/result.py (node_depths /
# graph_anti_unify on result) + pipeline.py.
from teaching.contemplation import contemplate
vault_probe = _vault_probe_for_context(self._context) if self._context else None
depth = getattr(self, '_last_node_depths', None)
candidates_to_save = [
contemplate(c, vault_probe=vault_probe, depth=depth)
contemplate(c, vault_probe=vault_probe)
for c in candidates_to_save
]
# ADR-0219 — generation-dir atomic checkpoint. All files are written
@ -979,10 +959,8 @@ class ChatRuntime:
vault_probe = (
_vault_probe_for_context(self._context) if self._context else None
)
# 3-lang depth propagation contract (see checkpoint_engine_state)
depth = getattr(self, '_last_node_depths', None)
contemplated = [
contemplate(candidate, vault_probe=vault_probe, depth=depth)
contemplate(candidate, vault_probe=vault_probe)
for candidate in self._pending_candidates
]
contemplated_count = len(contemplated)
@ -1216,7 +1194,7 @@ class ChatRuntime:
``govern_response`` widens to APPROXIMATE iff the predicate-class holds a genuine
SERVE license, and ``shape_surface`` DISCLOSES it as ``[approximate] ``. An
unlicensed class stays STRICT (the surface is unchanged the honest refusal).
Off-flag turns never reach here. See ``docs/specs/runtime_contracts.md``.
Off-flag turns never reach here. See ``docs/runtime_contracts.md``.
"""
accrual = self._last_turn_accrual
if accrual is None:
@ -1550,10 +1528,8 @@ class ChatRuntime:
if self.config.vault_probe_discoveries
else None
)
# 3-lang depth propagation contract (see checkpoint_engine_state)
depth = getattr(self, '_last_node_depths', None)
candidates = tuple(
contemplate(c, vault_probe=vault_probe, depth=depth) for c in candidates
contemplate(c, vault_probe=vault_probe) for c in candidates
)
self._pending_candidates.extend(candidates)
sink = self._discovery_sink
@ -2433,8 +2409,6 @@ class ChatRuntime:
)
def chat(self, text: str, max_tokens: int | None = None) -> ChatResponse:
self._last_plan_findings = ()
self._last_plan_metrics = None
self._last_input_text = text # W-013: store for explain_last_turn()
tokens = self._tokenize(text)
filtered = self._apply_oov_policy(tokens)

View file

@ -21,7 +21,7 @@ Trust boundary (per CLAUDE.md):
* **Idempotent flush.** Each ``emit()`` flushes immediately so a
crashed turn loop still has its prior turns durable on disk.
See ``docs/adr/ADR-0040-telemetry-sink.md``.
See ``docs/decisions/ADR-0040-telemetry-sink.md``.
"""
from __future__ import annotations

View file

@ -1,14 +1,15 @@
"""Project-root conftest — test classification registries.
"""Project-root conftest — quarantine registry for known-failing tests.
The QUARANTINE set is the only allowed registry for known-failing tests.
It is currently empty. If it ever contains nodeids, the CI gate at
.github/workflows/full-pytest.yml runs ``pytest -m "not quarantine"``
so those explicitly tracked failures do not block unrelated PRs. The
suite is a ratchet: a quarantined test removed from this set must pass
on its own merits.
The QUARANTINE set lists test IDs that are pre-existing failures
predating the substrate-liveness audit work (verified via bisect
against c1a1b7a, the commit immediately before the first W-* PR
of 2026-05-24). The CI gate at .github/workflows/full-pytest.yml
runs ``pytest -m "not quarantine"`` so these failures do not block
PRs, but the suite is a ratchet: a quarantined test removed from
this set must pass on its own merits.
See docs/test-debt-quarantine.md for current policy and historical cluster
diagnoses.
See docs/test-debt-quarantine.md for cluster diagnoses, removal
policy, and the per-test rationale.
To remove a test from quarantine:
1. Land a PR that makes the test pass.

View file

@ -1,17 +0,0 @@
# Contemplation Artifacts
This directory is an artifact namespace for committed contemplation process
reports, especially the `runs/` evidence consumed by teaching queue and
Workbench readers.
It is intentionally not a Python package:
- no `contemplation/__init__.py`
- no Python source files under this tree
- executable contemplation code lives under `core/contemplation/`
- always-on runtime life writes local reports under
`<engine_state>/contemplation_runs/`
Keeping this split explicit prevents `import contemplation` ambiguity while
preserving the deterministic JSON evidence shape expected by queue and
Workbench surfaces.

View file

@ -32,13 +32,8 @@ pub fn cga_inner_raw(x: &[f32; 32], y: &[f32; 32]) -> Result<f32, CgaError> {
}
/// Check if X is on the null cone: |X·X| < tol.
///
/// For identical operands, the symmetric inner product collapses to the
/// scalar part of X*X, so compute the product once instead of routing through
/// cga_inner_raw(x, x).
pub fn is_null_raw(x: &[f32; 32], tol: f32) -> Result<bool, CgaError> {
let xx = geometric_product_raw(x, x)?;
Ok(xx[0].abs() < tol)
Ok(cga_inner_raw(x, x)?.abs() < tol)
}
/// Re-project X onto the null cone by extracting Euclidean components

View file

@ -35,14 +35,16 @@ const fn build_blade_masks() -> [u8; 32] {
// Hardcoded to guarantee exact parity with Python cl41.py.
[
// grade 0: ()
0b00000, // grade 1: (0,), (1,), (2,), (3,), (4,)
0b00000,
// grade 1: (0,), (1,), (2,), (3,), (4,)
0b00001, 0b00010, 0b00100, 0b01000, 0b10000,
// grade 2: (0,1), (0,2), (0,3), (0,4), (1,2), (1,3), (1,4), (2,3), (2,4), (3,4)
0b00011, 0b00101, 0b01001, 0b10001, 0b00110, 0b01010, 0b10010, 0b01100, 0b10100, 0b11000,
// grade 3: (0,1,2), (0,1,3), (0,1,4), (0,2,3), (0,2,4), (0,3,4), (1,2,3), (1,2,4), (1,3,4), (2,3,4)
0b00111, 0b01011, 0b10011, 0b01101, 0b10101, 0b11001, 0b01110, 0b10110, 0b11010, 0b11100,
// grade 4: (0,1,2,3), (0,1,2,4), (0,1,3,4), (0,2,3,4), (1,2,3,4)
0b01111, 0b10111, 0b11011, 0b11101, 0b11110, // grade 5: (0,1,2,3,4)
0b01111, 0b10111, 0b11011, 0b11101, 0b11110,
// grade 5: (0,1,2,3,4)
0b11111,
]
}
@ -58,6 +60,13 @@ const fn build_mask_to_idx() -> [u8; 32] {
lut
}
const fn popcount5(x: u8) -> u8 {
let mut n = x & 0x1F;
let mut c = 0u8;
while n != 0 { c += n & 1; n >>= 1; }
c
}
// Multiply two basis blades given as bitmasks. Returns (result_mask, sign).
// The sign is the parity of swaps needed to canonicalize A followed by B,
// multiplied by the metric contractions for repeated basis vectors.
@ -97,17 +106,17 @@ const fn blade_product(a: u8, b: u8) -> (u8, i8) {
}
struct Table {
idx: [[u8; 32]; 32],
idx: [[u8; 32]; 32],
sign: [[i8; 32]; 32],
}
fn build_table() -> Table {
let mut idx = [[0u8; 32]; 32];
let mut idx = [[0u8; 32]; 32];
let mut sign = [[0i8; 32]; 32];
for i in 0..32usize {
for j in 0..32usize {
let (result_mask, s) = blade_product(BLADE_MASKS[i], BLADE_MASKS[j]);
idx[i][j] = MASK_TO_IDX[result_mask as usize];
idx[i][j] = MASK_TO_IDX[result_mask as usize];
sign[i][j] = s;
}
}
@ -128,14 +137,10 @@ pub fn geometric_product_f64(a: &[f64; 32], b: &[f64; 32]) -> [f64; 32] {
let mut result = [0f64; 32];
for i in 0..32 {
let ai = a[i];
if ai == 0.0 {
continue;
}
if ai == 0.0 { continue; }
for j in 0..32 {
let bj = b[j];
if bj == 0.0 {
continue;
}
if bj == 0.0 { continue; }
let k = t.idx[i][j] as usize;
let s = t.sign[i][j] as f64;
result[k] += s * ai * bj;
@ -151,14 +156,10 @@ pub fn geometric_product_raw(a: &[f32; 32], b: &[f32; 32]) -> Result<[f32; 32],
let mut result = [0f32; 32];
for i in 0..32 {
let ai = a[i];
if ai == 0.0 {
continue;
}
if ai == 0.0 { continue; }
for j in 0..32 {
let bj = b[j];
if bj == 0.0 {
continue;
}
if bj == 0.0 { continue; }
let k = t.idx[i][j] as usize;
let s = t.sign[i][j] as f32;
result[k] += s * ai * bj;
@ -172,23 +173,15 @@ pub fn geometric_product_raw(a: &[f32; 32], b: &[f32; 32]) -> Result<[f32; 32],
/// Grade 0,1: +1. Grade 2,3: -1. Grade 4,5: +1.
pub fn reverse_raw(a: &[f32; 32]) -> [f32; 32] {
let mut r = *a;
for i in 6..=15 {
r[i] = -r[i];
}
for i in 16..=25 {
r[i] = -r[i];
}
for i in 6..=15 { r[i] = -r[i]; }
for i in 16..=25 { r[i] = -r[i]; }
r
}
/// Reverse anti-automorphism (f64).
pub fn reverse_f64(a: &[f64; 32]) -> [f64; 32] {
let mut r = *a;
for i in 6..=15 {
r[i] = -r[i];
}
for i in 16..=25 {
r[i] = -r[i];
}
for i in 6..=15 { r[i] = -r[i]; }
for i in 16..=25 { r[i] = -r[i]; }
r
}

View file

@ -11,6 +11,10 @@
use crate::cl41::geometric_product_f64;
use std::collections::HashMap;
/// Blade indices 9, 12, 14, 15 square to +1 (boost/hyperbolic planes involving e5).
/// Remaining bivector indices (6-8, 10-11, 13) square to -1 (rotation planes).
const BOOST_INDICES: [usize; 4] = [9, 12, 14, 15];
fn is_boost(blade_idx: usize) -> bool {
matches!(blade_idx, 9 | 12 | 14 | 15)
}
@ -22,9 +26,7 @@ fn is_boost(blade_idx: usize) -> bool {
pub fn unitize_f32(v: &[f32; 32]) -> [f32; 32] {
let v64: [f64; 32] = {
let mut arr = [0f64; 32];
for i in 0..32 {
arr[i] = v[i] as f64;
}
for i in 0..32 { arr[i] = v[i] as f64; }
arr
};
@ -38,9 +40,7 @@ pub fn unitize_f32(v: &[f32; 32]) -> [f32; 32] {
// Extract bivector content (indices 6..16)
let bv: [f64; 10] = {
let mut arr = [0f64; 10];
for i in 0..10 {
arr[i] = v64[6 + i];
}
for i in 0..10 { arr[i] = v64[6 + i]; }
arr
};
let bv_norm: f64 = bv.iter().map(|x| x * x).sum::<f64>().sqrt();
@ -57,9 +57,7 @@ pub fn unitize_f32(v: &[f32; 32]) -> [f32; 32] {
for i in 0..10usize {
let w = bv[i] / bv_norm;
if w.abs() < 1e-14 {
continue;
}
if w.abs() < 1e-14 { continue; }
let theta = angle * w;
let mut factor = [0f64; 32];
let blade_idx = 6 + i;
@ -74,15 +72,11 @@ pub fn unitize_f32(v: &[f32; 32]) -> [f32; 32] {
}
if v64[0] < 0.0 {
for x in rotor.iter_mut() {
*x = -*x;
}
for x in rotor.iter_mut() { *x = -*x; }
}
let mut result = [0f32; 32];
for i in 0..32 {
result[i] = rotor[i] as f32;
}
for i in 0..32 { result[i] = rotor[i] as f32; }
result
}
@ -109,27 +103,19 @@ pub fn graph_diffusion_step(
}
for (&node, srcs) in &neighbors {
if node >= n || srcs.is_empty() {
continue;
}
if node >= n || srcs.is_empty() { continue; }
// Current node in f64
let mut f = [0f64; 32];
for i in 0..32 {
f[i] = fields[node][i] as f64;
}
for i in 0..32 { f[i] = fields[node][i] as f64; }
// Neighbor average in f64
let mut avg = [0f64; 32];
for &src in srcs {
for i in 0..32 {
avg[i] += fields[src][i] as f64;
}
for i in 0..32 { avg[i] += fields[src][i] as f64; }
}
let inv = 1.0 / srcs.len() as f64;
for x in avg.iter_mut() {
*x *= inv;
}
for x in avg.iter_mut() { *x *= inv; }
// Blend
let mut blended = [0f32; 32];
@ -184,7 +170,7 @@ mod tests {
let mut v = [0f32; 32];
v[0] = 0.8;
v[6] = 0.3;
v[9] = 0.2; // boost blade
v[9] = 0.2; // boost blade
let result = unitize_f32(&v);
let cond = versor_condition_raw(&result).unwrap();
assert!(cond < 1e-4, "versor condition {} too large", cond);

81
core-rs/src/holonomy.rs Normal file
View file

@ -0,0 +1,81 @@
//! Holonomy encoder in Rust — the forward+reverse versor walk.
//!
//! This is in Rust because:
//! - Long prompts (100+ tokens) do 200+ geometric products in sequence
//! - Each geometric product is O(32^2) = 1024 multiply-adds
//! - Python overhead per call makes this 10-50x slower than necessary
//! - Rust collapses the entire walk into a single allocation-free loop
use crate::cl41::{geometric_product_raw, reverse_raw};
use crate::versor::normalize_to_versor_raw;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum HolonomyError {
#[error("Empty word list")]
Empty,
#[error("Versor error: {0}")]
Versor(String),
}
/// Compute holonomy of a word versor sequence.
///
/// Forward walk: F = w0 * w1 * ... * wn
/// Reverse walk: R = (1-alpha) * rev(wn) * ... * rev(w0)
/// Holonomy: H = normalize(F * R)
///
/// weights: per-word scalars (inverse frequency). If empty, uniform 1.0.
/// alpha: blend factor [0,1]. 0.5 recommended.
pub fn holonomy_encode_raw(
words: &[[f32; 32]],
weights: &[f32],
alpha: f32,
) -> Result<[f32; 32], HolonomyError> {
if words.is_empty() {
return Err(HolonomyError::Empty);
}
let n = words.len();
let use_weights = !weights.is_empty() && weights.len() == n;
// Forward accumulation
let mut scaled = words[0];
if use_weights {
let w = weights[0];
for x in scaled.iter_mut() { *x *= w; }
}
let mut f = normalize_to_versor_raw(&scaled)
.map_err(|e| HolonomyError::Versor(e.to_string()))?;
for k in 1..n {
let mut wk = words[k];
if use_weights {
let w = weights[k];
for x in wk.iter_mut() { *x *= w; }
}
let wk_norm = normalize_to_versor_raw(&wk)
.map_err(|e| HolonomyError::Versor(e.to_string()))?;
f = geometric_product_raw(&f, &wk_norm)
.map_err(|e| HolonomyError::Versor(e.to_string()))?;
}
// Reverse accumulation with (1-alpha) damping
let damp = 1.0 - alpha;
let mut last_rev = reverse_raw(&words[n - 1]);
for x in last_rev.iter_mut() { *x *= damp; }
let mut r = normalize_to_versor_raw(&last_rev)
.map_err(|e| HolonomyError::Versor(e.to_string()))?;
for k in (0..n - 1).rev() {
let rev_wk = reverse_raw(&words[k]);
let rev_norm = normalize_to_versor_raw(&rev_wk)
.map_err(|e| HolonomyError::Versor(e.to_string()))?;
r = geometric_product_raw(&rev_norm, &r)
.map_err(|e| HolonomyError::Versor(e.to_string()))?;
}
let h = geometric_product_raw(&f, &r)
.map_err(|e| HolonomyError::Versor(e.to_string()))?;
normalize_to_versor_raw(&h)
.map_err(|e| HolonomyError::Versor(e.to_string()))
}

View file

@ -6,7 +6,6 @@
//! - versor_condition (||F*rev(F) - 1||_F)
//! - cga_inner (symmetric inner product)
//! - vault_recall (parallel top-k scan)
//! - diffusion_step (zero-copy graph diffusion step)
//!
//! All multivectors are f32 arrays of length 32, passed as numpy arrays.
@ -22,25 +21,23 @@ pub mod versor;
use cga::cga_inner_raw;
use cl41::geometric_product_raw;
use diffusion::{graph_diffusion_step, unitize_f32};
#[allow(unused_imports)]
use vault::vault_recall_raw;
use versor::{
normalize_to_versor_raw, versor_apply_closed, versor_apply_closed_f64, versor_apply_raw,
versor_condition_raw,
};
/// Geometric product in Cl(4,1). Accepts two contiguous float32 arrays of length 32.
///
/// Inputs are read via ``PyReadonlyArray1`` zero-copy views into the NumPy
/// buffer. Wrong shape, dtype, or non-contiguous layout fails loudly — no
/// silent coercion.
/// Geometric product in Cl(4,1). Accepts two numpy-compatible f32 arrays of length 32.
#[pyfunction]
fn geometric_product(
py: Python<'_>,
a: numpy::PyReadonlyArray1<'_, f32>,
b: numpy::PyReadonlyArray1<'_, f32>,
a: &pyo3::types::PyAny,
b: &pyo3::types::PyAny,
) -> PyResult<PyObject> {
let a_slice = read_f32_cl41_mv(&a)?;
let b_slice = read_f32_cl41_mv(&b)?;
let result = geometric_product_raw(a_slice, b_slice)
let a_slice = extract_f32_slice(a)?;
let b_slice = extract_f32_slice(b)?;
let result = geometric_product_raw(&a_slice, &b_slice)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
f32_array_to_numpy(py, &result)
}
@ -49,13 +46,13 @@ fn geometric_product(
#[pyfunction]
fn versor_apply(
py: Python<'_>,
v: &Bound<'_, pyo3::types::PyAny>,
f: &Bound<'_, pyo3::types::PyAny>,
v: &pyo3::types::PyAny,
f: &pyo3::types::PyAny,
) -> PyResult<PyObject> {
let v_slice = extract_f32_slice(v)?;
let f_slice = extract_f32_slice(f)?;
let result =
versor_apply_raw(&v_slice, &f_slice).map_err(|e| PyValueError::new_err(e.to_string()))?;
let result = versor_apply_raw(&v_slice, &f_slice)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
f32_array_to_numpy(py, &result)
}
@ -64,13 +61,13 @@ fn versor_apply(
#[pyfunction]
fn versor_apply_with_closure(
py: Python<'_>,
v: numpy::PyReadonlyArray1<'_, f32>,
f: numpy::PyReadonlyArray1<'_, f32>,
v: &pyo3::types::PyAny,
f: &pyo3::types::PyAny,
) -> PyResult<PyObject> {
let v_slice = read_f32_cl41_mv(&v)?;
let f_slice = read_f32_cl41_mv(&f)?;
let result =
versor_apply_closed(v_slice, f_slice).map_err(|e| PyValueError::new_err(e.to_string()))?;
let v_slice = extract_f32_slice(v)?;
let f_slice = extract_f32_slice(f)?;
let result = versor_apply_closed(&v_slice, &f_slice)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
f32_array_to_numpy(py, &result)
}
@ -80,74 +77,41 @@ fn versor_apply_with_closure(
#[pyfunction]
fn versor_apply_with_closure_f64(
py: Python<'_>,
v: numpy::PyReadonlyArray1<'_, f64>,
f: numpy::PyReadonlyArray1<'_, f64>,
v: &pyo3::types::PyAny,
f: &pyo3::types::PyAny,
) -> PyResult<PyObject> {
let v_slice = read_f64_cl41_mv(&v)?;
let f_slice = read_f64_cl41_mv(&f)?;
let result = versor_apply_closed_f64(v_slice, f_slice)
let v_slice = extract_f64_slice(v)?;
let f_slice = extract_f64_slice(f)?;
let result = versor_apply_closed_f64(&v_slice, &f_slice)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
f64_array_to_numpy(py, &result)
}
/// ||F*reverse(F) - 1||_F. Returns scalar f32.
#[pyfunction]
fn versor_condition(f: numpy::PyReadonlyArray1<'_, f32>) -> PyResult<f32> {
let f_slice = read_f32_cl41_mv(&f)?;
versor_condition_raw(f_slice).map_err(|e| PyValueError::new_err(e.to_string()))
fn versor_condition(f: &pyo3::types::PyAny) -> PyResult<f32> {
let f_slice = extract_f32_slice(f)?;
versor_condition_raw(&f_slice).map_err(|e| PyValueError::new_err(e.to_string()))
}
/// Project F onto versor manifold: F / sqrt(|F*rev(F)|).
#[pyfunction]
fn normalize_to_versor(py: Python<'_>, f: &Bound<'_, pyo3::types::PyAny>) -> PyResult<PyObject> {
fn normalize_to_versor(
py: Python<'_>,
f: &pyo3::types::PyAny,
) -> PyResult<PyObject> {
let f_slice = extract_f32_slice(f)?;
let result =
normalize_to_versor_raw(&f_slice).map_err(|e| PyValueError::new_err(e.to_string()))?;
let result = normalize_to_versor_raw(&f_slice)
.map_err(|e| PyValueError::new_err(e.to_string()))?;
f32_array_to_numpy(py, &result)
}
/// Symmetric CGA inner product: 0.5 * scalar(X*Y + Y*X).
#[pyfunction]
fn cga_inner(
x: numpy::PyReadonlyArray1<'_, f32>,
y: numpy::PyReadonlyArray1<'_, f32>,
) -> PyResult<f32> {
let x_slice = read_f32_cl41_mv(&x)?;
let y_slice = read_f32_cl41_mv(&y)?;
cga_inner_raw(x_slice, y_slice).map_err(|e| PyValueError::new_err(e.to_string()))
}
/// Embed a Euclidean point [x, y, z] into the CGA null cone.
#[pyfunction]
fn embed_point(
py: Python<'_>,
p: numpy::PyReadonlyArray1<'_, f32>,
) -> PyResult<PyObject> {
let p_slice = read_f32_xyz(&p)?;
let result = crate::cga::embed_point_raw(p_slice);
f32_array_to_numpy(py, &result)
}
/// Re-project a multivector onto the null cone by Euclidean read-back + re-embed.
#[pyfunction]
fn null_project(
py: Python<'_>,
x: numpy::PyReadonlyArray1<'_, f32>,
) -> PyResult<PyObject> {
let x_slice = read_f32_cl41_mv(&x)?;
let result = crate::cga::null_project_raw(x_slice);
f32_array_to_numpy(py, &result)
}
/// Check whether a multivector lies on the null cone.
#[pyfunction]
fn is_null(
x: numpy::PyReadonlyArray1<'_, f32>,
tol: f32,
) -> PyResult<bool> {
let x_slice = read_f32_cl41_mv(&x)?;
crate::cga::is_null_raw(x_slice, tol)
.map_err(|e| PyValueError::new_err(e.to_string()))
fn cga_inner(x: &pyo3::types::PyAny, y: &pyo3::types::PyAny) -> PyResult<f32> {
let x_slice = extract_f32_slice(x)?;
let y_slice = extract_f32_slice(y)?;
cga_inner_raw(&x_slice, &y_slice).map_err(|e| PyValueError::new_err(e.to_string()))
}
/// Parallel top-k vault recall by CGA inner product (zero-copy).
@ -175,9 +139,9 @@ fn vault_recall(
)));
}
let n = shape[0];
let q_slice = query
.as_slice()
.map_err(|e| PyValueError::new_err(format!("query must be contiguous f32 (32,): {}", e)))?;
let q_slice = query.as_slice().map_err(|e| {
PyValueError::new_err(format!("query must be contiguous f32 (32,): {}", e))
})?;
if q_slice.len() != 32 {
return Err(PyValueError::new_err(format!(
"query must have length 32, got {}",
@ -185,7 +149,10 @@ fn vault_recall(
)));
}
let v_slice = versors.as_slice().map_err(|e| {
PyValueError::new_err(format!("versors must be C-contiguous f32 (N, 32): {}", e))
PyValueError::new_err(format!(
"versors must be C-contiguous f32 (N, 32): {}",
e
))
})?;
let mut q_arr = [0f32; 32];
q_arr.copy_from_slice(q_slice);
@ -197,7 +164,10 @@ fn vault_recall(
/// Unitize a multivector via the Cl(4,1) exponential map.
/// Distinguishes boost planes (cosh/sinh) from rotation planes (cos/sin).
#[pyfunction]
fn unitize_expmap(py: Python<'_>, v: &Bound<'_, pyo3::types::PyAny>) -> PyResult<PyObject> {
fn unitize_expmap(
py: Python<'_>,
v: &pyo3::types::PyAny,
) -> PyResult<PyObject> {
let v_slice = extract_f32_slice(v)?;
let result = unitize_f32(&v_slice);
f32_array_to_numpy(py, &result)
@ -247,10 +217,16 @@ fn diffusion_step<'py>(
}
let fields_slice = fields.as_slice().map_err(|e| {
PyValueError::new_err(format!("fields must be C-contiguous f32 (N, 32): {}", e))
PyValueError::new_err(format!(
"fields must be C-contiguous f32 (N, 32): {}",
e
))
})?;
let edges_slice = edges.as_slice().map_err(|e| {
PyValueError::new_err(format!("edges must be C-contiguous i32 (E, 2): {}", e))
PyValueError::new_err(format!(
"edges must be C-contiguous i32 (E, 2): {}",
e
))
})?;
// ``[f32; 32]`` and ``[i32; 2]`` are both ``Pod`` (arrays of POD
@ -259,7 +235,8 @@ fn diffusion_step<'py>(
let fields_blocks: &[[f32; 32]] = bytemuck::cast_slice(fields_slice);
let edges_blocks: &[[i32; 2]] = bytemuck::cast_slice(edges_slice);
let (new_fields, delta) = graph_diffusion_step(fields_blocks, edges_blocks, damping);
let (new_fields, delta) =
graph_diffusion_step(fields_blocks, edges_blocks, damping);
// ``Vec<[f32; 32]>`` → ``Vec<f32>`` is a zero-copy reinterpretation
// of the allocation (requires the ``extern_crate_alloc`` bytemuck
@ -276,59 +253,8 @@ fn diffusion_step<'py>(
Ok((numpy::IntoPyArray::into_pyarray_bound(arr, py), delta))
}
fn read_f32_cl41_mv<'a>(arr: &'a numpy::PyReadonlyArray1<'a, f32>) -> PyResult<&'a [f32; 32]> {
let len = arr.len()?;
if len != 32 {
return Err(PyValueError::new_err(format!(
"expected contiguous float32 array of length 32, got length {}",
len
)));
}
let slice = arr.as_slice().map_err(|e| {
PyValueError::new_err(format!("input must be C-contiguous float32 (32,): {}", e))
})?;
slice
.try_into()
.map_err(|_| PyValueError::new_err("expected contiguous float32 array of length 32"))
}
fn read_f64_cl41_mv<'a>(arr: &'a numpy::PyReadonlyArray1<'a, f64>) -> PyResult<&'a [f64; 32]> {
let len = arr.len()?;
if len != 32 {
return Err(PyValueError::new_err(format!(
"expected contiguous float64 array of length 32, got length {}",
len
)));
}
let slice = arr.as_slice().map_err(|e| {
PyValueError::new_err(format!("input must be C-contiguous float64 (32,): {}", e))
})?;
slice
.try_into()
.map_err(|_| PyValueError::new_err("expected contiguous float64 array of length 32"))
}
fn read_f32_xyz<'a>(arr: &'a numpy::PyReadonlyArray1<'a, f32>) -> PyResult<&'a [f32; 3]> {
let len = arr.len()?;
if len != 3 {
return Err(PyValueError::new_err(format!(
"expected contiguous float32 array of length 3, got length {}",
len
)));
}
let slice = arr.as_slice().map_err(|e| {
PyValueError::new_err(format!(
"input must be C-contiguous float32 (3,): {}",
e
))
})?;
slice.try_into().map_err(|_| {
PyValueError::new_err("expected contiguous float32 array of length 3")
})
}
fn extract_f32_slice(obj: &Bound<'_, pyo3::types::PyAny>) -> PyResult<[f32; 32]> {
let np = obj.py().import_bound("numpy")?;
fn extract_f32_slice(obj: &pyo3::types::PyAny) -> PyResult<[f32; 32]> {
let np = obj.py().import("numpy")?;
let arr = np.call_method1("asarray", (obj, "float32"))?;
let flat = arr.call_method0("flatten")?;
let list: Vec<f32> = flat.extract()?;
@ -344,14 +270,30 @@ fn extract_f32_slice(obj: &Bound<'_, pyo3::types::PyAny>) -> PyResult<[f32; 32]>
}
fn f32_array_to_numpy(py: Python<'_>, data: &[f32; 32]) -> PyResult<PyObject> {
let np = py.import_bound("numpy")?;
let np = py.import("numpy")?;
let list: Vec<f32> = data.to_vec();
let arr = np.call_method1("array", (list, "float32"))?;
Ok(arr.into_py(py))
}
fn extract_f64_slice(obj: &pyo3::types::PyAny) -> PyResult<[f64; 32]> {
let np = obj.py().import("numpy")?;
let arr = np.call_method1("asarray", (obj, "float64"))?;
let flat = arr.call_method0("flatten")?;
let list: Vec<f64> = flat.extract()?;
if list.len() != 32 {
return Err(PyValueError::new_err(format!(
"Expected array of length 32, got {}",
list.len()
)));
}
let mut out = [0f64; 32];
out.copy_from_slice(&list);
Ok(out)
}
fn f64_array_to_numpy(py: Python<'_>, data: &[f64; 32]) -> PyResult<PyObject> {
let np = py.import_bound("numpy")?;
let np = py.import("numpy")?;
let list: Vec<f64> = data.to_vec();
let arr = np.call_method1("array", (list, "float64"))?;
Ok(arr.into_py(py))
@ -366,9 +308,6 @@ fn core_rs(m: &Bound<'_, PyModule>) -> PyResult<()> {
m.add_function(wrap_pyfunction!(versor_condition, m)?)?;
m.add_function(wrap_pyfunction!(normalize_to_versor, m)?)?;
m.add_function(wrap_pyfunction!(cga_inner, m)?)?;
m.add_function(wrap_pyfunction!(embed_point, m)?)?;
m.add_function(wrap_pyfunction!(null_project, m)?)?;
m.add_function(wrap_pyfunction!(is_null, m)?)?;
m.add_function(wrap_pyfunction!(vault_recall, m)?)?;
m.add_function(wrap_pyfunction!(unitize_expmap, m)?)?;
m.add_function(wrap_pyfunction!(diffusion_step, m)?)?;

48
core-rs/src/propagate.rs Normal file
View file

@ -0,0 +1,48 @@
//! Propagation loop in Rust — tight versor_apply chain.
//!
//! propagate_n steps runs N versor_apply calls in a single Rust stack frame,
//! eliminating Python dispatch overhead for each step.
//! Used by generate/stream.py when stepping more than one token at a time
//! (e.g. prefill, speculative steps, or batch generation).
use crate::versor::versor_apply_raw;
use thiserror::Error;
#[derive(Debug, Error)]
pub enum PropagateError {
#[error("Versor error during propagation: {0}")]
Versor(String),
}
/// Run n versor_apply steps in sequence.
/// rotors: slice of n [f32;32] versors to apply in order
/// f0: initial field state
/// Returns final field state after n steps.
pub fn propagate_n_raw(
rotors: &[[f32; 32]],
f0: &[f32; 32],
) -> Result<[f32; 32], PropagateError> {
let mut f = *f0;
for v in rotors {
f = versor_apply_raw(v, &f)
.map_err(|e| PropagateError::Versor(e.to_string()))?;
}
Ok(f)
}
/// Parallel batch propagation: apply the same rotor V to a batch of field states.
/// Used for beam search or multi-hypothesis generation.
/// Returns new batch of field states.
pub fn propagate_batch_raw(
v: &[f32; 32],
fields: &[[f32; 32]],
) -> Result<Vec<[f32; 32]>, PropagateError> {
use rayon::prelude::*;
fields
.par_iter()
.map(|f| {
versor_apply_raw(v, f)
.map_err(|e| PropagateError::Versor(e.to_string()))
})
.collect()
}

View file

@ -33,8 +33,10 @@ pub enum VaultError {
/// basis. See `tests/test_vault_recall_vectorised.py` (Python
/// side) for the empirical derivation that pins this vector.
const CGA_INNER_METRIC: [f32; 32] = [
1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0,
-1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0,
1.0, 1.0, 1.0, 1.0, 1.0, -1.0, -1.0, -1.0,
-1.0, 1.0, -1.0, -1.0, 1.0, -1.0, 1.0, 1.0,
-1.0, -1.0, 1.0, -1.0, 1.0, 1.0, -1.0, 1.0,
1.0, 1.0, 1.0, -1.0, -1.0, -1.0, -1.0, -1.0,
];
/// Per-versor diagonal-metric CGA inner product. Same arithmetic
@ -289,7 +291,8 @@ pub trait SemilatticeDelta: Sized {
impl SemilatticeDelta for Delta {
fn join(&self, other: &Self) -> Self {
let mut merged = Vec::with_capacity(self.entries.len() + other.entries.len());
let mut merged =
Vec::with_capacity(self.entries.len() + other.entries.len());
merged.extend_from_slice(&self.entries);
merged.extend_from_slice(&other.entries);
Delta::from_entries(merged)

View file

@ -4,9 +4,7 @@
//! normalize_to_versor F/sqrt(|F*rev(F)|) — called once at injection gate
//! versor_condition ||F*rev(F)-1||_F — used in tests and gate only
use crate::cl41::{
geometric_product_f64, geometric_product_raw, reverse_f64, reverse_raw, Cl41Error,
};
use crate::cl41::{geometric_product_f64, geometric_product_raw, reverse_f64, reverse_raw, Cl41Error};
use thiserror::Error;
#[derive(Debug, Error)]
@ -18,6 +16,7 @@ pub enum VersorError {
}
const NEAR_ZERO_TOL: f64 = 1e-12;
const NULL_SCALAR_TOL: f64 = 1e-9;
const CONSTRUCTION_RESIDUE_TOL: f64 = 1e-2;
const SEED_BIVECTORS: [usize; 6] = [6, 7, 8, 10, 11, 13];
@ -53,9 +52,7 @@ fn unitize_closed(v: &[f64; 32]) -> Result<[f64; 32], ()> {
let inv = 1.0 / scalar_sq.sqrt();
let mut result = *v;
for x in result.iter_mut() {
*x *= inv;
}
for x in result.iter_mut() { *x *= inv; }
Ok(result)
}
@ -86,25 +83,19 @@ fn close_applied_versor(v: &[f32; 32]) -> [f32; 32] {
let v_f64: [f64; 32] = {
let mut arr = [0f64; 32];
for i in 0..32 {
arr[i] = v[i] as f64;
}
for i in 0..32 { arr[i] = v[i] as f64; }
arr
};
if let Ok(closed) = unitize_closed(&v_f64) {
let mut result = [0f32; 32];
for i in 0..32 {
result[i] = closed[i] as f32;
}
for i in 0..32 { result[i] = closed[i] as f32; }
return result;
}
if let Ok(seeded) = seed_to_rotor(&v_f64) {
let mut result = [0f32; 32];
for i in 0..32 {
result[i] = seeded[i] as f32;
}
for i in 0..32 { result[i] = seeded[i] as f32; }
return result;
}
@ -131,7 +122,10 @@ pub fn versor_apply_closed(v: &[f32; 32], f: &[f32; 32]) -> Result<[f32; 32], Ve
/// accepted — otherwise the deterministic `seed_to_rotor`
/// construction map is used. ADR-0020 parity gate
/// `tests/test_versor_apply_rust_parity.py`.
pub fn versor_apply_closed_f64(v: &[f64; 32], f: &[f64; 32]) -> Result<[f64; 32], VersorError> {
pub fn versor_apply_closed_f64(
v: &[f64; 32],
f: &[f64; 32],
) -> Result<[f64; 32], VersorError> {
let rev_v = reverse_f64(v);
let vf = geometric_product_f64(v, f);
let vfrv = geometric_product_f64(&vf, &rev_v);
@ -173,7 +167,10 @@ fn unitize_versor_f64(v: &[f64; 32]) -> Result<[f64; 32], ()> {
// `unitize_closed` signature; mirror Python's policy by gating
// the fallback on the dense-support heuristic, which is the
// condition Python also requires before invoking the rotor seed.
let support = v.iter().filter(|x| x.abs() > NEAR_ZERO_TOL).count();
let support = v
.iter()
.filter(|x| x.abs() > NEAR_ZERO_TOL)
.count();
if support < DENSE_SEED_MIN_COMPONENTS {
Err(())
} else {
@ -202,8 +199,8 @@ fn close_applied_versor_f64(v: &[f64; 32]) -> [f64; 32] {
/// Raw sandwich product V * F * reverse(V) without closure.
pub fn versor_apply_raw(v: &[f32; 32], f: &[f32; 32]) -> Result<[f32; 32], VersorError> {
let rev_v = reverse_raw(v);
let vf = geometric_product_raw(v, f)?;
let vfrv = geometric_product_raw(&vf, &rev_v)?;
let vf = geometric_product_raw(v, f)?;
let vfrv = geometric_product_raw(&vf, &rev_v)?;
Ok(vfrv)
}
@ -211,16 +208,14 @@ pub fn versor_apply_raw(v: &[f32; 32], f: &[f32; 32]) -> Result<[f32; 32], Verso
/// Called ONCE at ingest/gate. Never mid-propagation.
pub fn normalize_to_versor_raw(f: &[f32; 32]) -> Result<[f32; 32], VersorError> {
let rev_f = reverse_raw(f);
let frv = geometric_product_raw(f, &rev_f)?;
let n2 = frv[0]; // grade-0 = scalar part
let frv = geometric_product_raw(f, &rev_f)?;
let n2 = frv[0]; // grade-0 = scalar part
if n2.abs() < 1e-12 {
return Err(VersorError::NullVersor(n2));
}
let inv_norm = 1.0 / n2.abs().sqrt();
let mut result = *f;
for x in result.iter_mut() {
*x *= inv_norm;
}
for x in result.iter_mut() { *x *= inv_norm; }
Ok(result)
}

View file

@ -112,7 +112,9 @@ fn merge_kernel_equals_semilattice_fold() {
delta(vec![entry(5, "c")]),
delta(vec![entry(2, "b"), entry(9, "d")]), // overlaps the first delta
];
let folded = deltas.iter().fold(Delta::default(), |acc, d| acc.join(d));
let folded = deltas
.iter()
.fold(Delta::default(), |acc, d| acc.join(d));
// The cheap union-then-canonicalise path must equal the explicit
// semilattice fold, or the kernel has silently diverged from the trait.
assert_eq!(keys(&merge_kernel(&deltas)), keys(&folded));
@ -139,10 +141,7 @@ fn merge_result_is_content_sorted() {
let ks = keys(&d);
let mut sorted = ks.clone();
sorted.sort();
assert_eq!(
ks, sorted,
"merge output must be in content-addressed order"
);
assert_eq!(ks, sorted, "merge output must be in content-addressed order");
}
// --- LocalArena (ADR-0180 §2.1) -------------------------------------------

View file

@ -4,10 +4,7 @@ use core_rs::cga::{cga_inner_raw, embed_point_raw, is_null_raw, null_project_raw
fn test_embedded_point_is_null() {
let p = [1.0f32, 2.0, 3.0];
let x = embed_point_raw(&p);
assert!(
is_null_raw(&x, 1e-5).unwrap(),
"Embedded point should be null"
);
assert!(is_null_raw(&x, 1e-5).unwrap(), "Embedded point should be null");
}
#[test]
@ -30,11 +27,7 @@ fn test_cga_distance_identity() {
let x = embed_point_raw(&[0.0, 0.0, 0.0]);
let y = embed_point_raw(&[1.0, 0.0, 0.0]);
let inner = cga_inner_raw(&x, &y).unwrap();
assert!(
(inner - (-0.5)).abs() < 1e-5,
"Expected -0.5 for unit-distance points, got {}",
inner
);
assert!((inner - (-0.5)).abs() < 1e-5, "Expected -0.5 for unit-distance points, got {}", inner);
}
#[test]
@ -44,8 +37,5 @@ fn test_null_project_restores_null() {
x[0] += 0.05;
x[7] -= 0.03;
let fixed = null_project_raw(&x);
assert!(
is_null_raw(&fixed, 1e-5).unwrap(),
"null_project failed to restore null cone"
);
assert!(is_null_raw(&fixed, 1e-5).unwrap(), "null_project failed to restore null cone");
}

View file

@ -40,11 +40,7 @@ fn test_e1_e2_anticommute() {
let e1e2 = geometric_product_raw(&e1, &e2).unwrap();
let e2e1 = geometric_product_raw(&e2, &e1).unwrap();
for i in 0..32 {
assert!(
(e1e2[i] + e2e1[i]).abs() < 1e-6,
"e1*e2 + e2*e1 != 0 at index {}",
i
);
assert!((e1e2[i] + e2e1[i]).abs() < 1e-6, "e1*e2 + e2*e1 != 0 at index {}", i);
}
}
@ -61,18 +57,12 @@ fn test_reverse_grade2_sign() {
let mut a = [0f32; 32];
a[6] = 1.0;
let r = reverse_raw(&a);
assert!(
(r[6] + 1.0).abs() < 1e-6,
"reverse of grade-2 blade should negate"
);
assert!((r[6] + 1.0).abs() < 1e-6, "reverse of grade-2 blade should negate");
}
#[test]
fn test_reverse_grade1_unchanged() {
let e1 = basis(0);
let r = reverse_raw(&e1);
assert!(
(r[1] - 1.0).abs() < 1e-6,
"reverse of grade-1 blade should be unchanged"
);
assert!((r[1] - 1.0).abs() < 1e-6, "reverse of grade-1 blade should be unchanged");
}

Some files were not shown because too many files have changed in this diff Show more