Squashes the arc's work into one commit; the workflow-file edit it originally carried is excluded (see the end of this message). ## Lane 1 — Workbench recorded a proved answer as ungrounded With deduction_serving_enabled ratified ON (ADR-0256), workbench/api.py's live chat route builds a bare ChatRuntime(), so the deduction composer decides Workbench turns and stamps grounding_source="deduction" — but _coerce_grounding_source carried a hand-copied whitelist of the six pre-arc labels and silently rewrote anything else to "none". The runtime comment reasoned this was inert because "REPL turns do not flow through Workbench's CognitivePipelineRecord path". True, and irrelevant: the traffic flows the other way. Stale since 2026-07-24. Scope is one field. workbench/api.py:818 prefers TurnEvent.epistemic_state, which read epistemic_state_needed — honest. So the UNregistered path degraded honestly while the hand-copied whitelist asserted a falsehood; a second copy of a closed enum was worse than no copy. Hence registration AND derivation: GROUNDING_SOURCES exposes the Literal's members, and the coercion reads it. workbench-ui badges/tokens/snapshot follow; enumCoverage.test.ts forces atomicity. ## Lane 2 — the ratification ceremony The discovery loop was instrumented but not closed. teaching/ratification.py turns a reviewed decision into a chain record, a corpus commit, and a receipt. Its design turns on one observation: _ratified_rows DROPS unadmissible rows silently — correct when serving, a trap when ratifying, because the file grows, the commit lands, and the band count does not move. So the ceremony refuses to call an append a ratification until it has re-read the curriculum through the real loader and seen the chain arrive; a non-admitted append is rolled back. Validation is a pre-flight courtesy, admission is the proof. Arena queue entry and ledger reseal are deliberately NOT performed (bridge rule 1); the receipt names them. Front door: `core proposal-queue ratify`, a sibling of `review` rather than a flag on it. ## Lane 3 — structural closures - ADR-0263 gains rule 5: absence policy is DECLARED in CAPABILITY_LEDGERS, not passed at the call site. An AST-matched test fails if a serving path passes missing_ok again. - Deductive suite added WHOLE to the pre-push gate: 285 tests in 29s against smoke's 216 in 62s, so no coverage trade was needed. - Smoke/CI parity assertion made bidirectional. It was one-directional, and had drifted. - test_prior_surface_deduction_binding.py pins correction binding on the deduction path. The review's diagnosis did NOT reproduce — hash_surface moves in lockstep — so it pins what is there. Mutation-checked. - Domain-keyed ADR index over 312 flat-numbered files, explicitly partial. - Arc-close brief template, plus this arc's own brief filled in against it. ## Lanes 4 and 5 — two premises falsified by measurement, one of them mine Math 4.2: baseline reproduced (correct=5 wrong=0 refused=495); all four named cases traced to one seam with each gap isolated by one-variable probes. Then the number that changes the recommendation: the gap blocking case 0000 affects 1 case in 500, the 'than' gap blocking 0001 affects 2. ADR-0251's prohibition on per-case growth now rests on a count. No reader change made. CGA: versor_condition is 0.22% of a turn, not the "~10x proof latency" I claimed — that multiplied an isolated microbenchmark by a call count and compared it to a single verdict's latency. The real cost is geometric_product at 33,986 calls/turn (~73%) via cga_inner in search paths. The obvious closed form is NOT bit-exact (954/4000 in f32); backend.vault_recall's serial fold IS (3000/3000, worst-rel 0) and is the correct target. cargo test could not run — static.crates.io is denied by the sandbox network policy — so the Rust parity question stays open and the typestate lane is carried forward, not shipped uncompiled. ## Not landed: three lines owed to .github/workflows/smoke.yml The CI smoke gate is narrower than the local one — test_pack_draft_serve_boundary.py (ADR-0253 INV-33) has been local-only, unseen because the parity pin checked one direction. The edit was authored and rejected at push for lacking the `workflow` OAuth scope, so it is recorded as a named, dated PENDING_IN_CI exception rather than dropped: the assertion still fires on any new divergence, and a second guard fires once the three land. [Verification]: pre-push gates all green — smoke 236 passed, warmed_session 10 passed, deductive 285 passed. Ratification 14, ADR index 5, CLI suites 10. Grounding/epistemic sweep 741 passed 1 skipped. workbench-ui 598 passed across 73 files, tsc -b clean. capability index 11 passed, digest unchanged. Math holdout correct=5 wrong=0 refused=495. Committed chain corpora byte-unchanged after the tests that write to them. Environment caveat: the repo pins requires-python ==3.12.13, which uv cannot fetch for linux-x86_64, so `uv sync --locked` fails. All Python runs used a scratch venv on 3.12.11 with declared deps — not the locked universe, not the full ~12k suite. The pin was left untouched. Re-run on a 3.12.13 host before treating this as merge evidence. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01FduW6Krm3PPQv3P5iwBYtx
7.5 KiB
Where the per-turn CGA cost actually is — measured, 2026-07-25
Result: the substrate-performance premise in circulation (mine and an
external hardware blueprint's) was wrong. versor_condition is 0.22% of a
turn. The real cost is cga_inner → geometric_product at 33,986 calls per
turn, ~73% of turn time, driven by nearest-neighbour and salience search.
The obvious algebraic shortcut is not bit-safe and must not ship. A
different, already-ratified pattern in this repo is bit-exact and is the
correct target.
1. What was claimed
Two claims were in play going into this measurement, and both were reasoned from microbenchmarks rather than from a turn:
- An external Apple-Silicon blueprint proposed custom
.metalCl(4,1) kernels, SoA layouts, MLX kernel-fusion of the versor invariant, and bfloat16 routing — arguing the CPU is the bottleneck. - My own assessment claimed
versor_conditionis the hot spot: three calls per turn × the benchmark report'sp50 = 0.536 ms≈ 1.6 ms/turn, "~10× the entire proof latency."
Claim 2 is wrong, and it is worth naming the error precisely because it is
easy to repeat: it multiplies an isolated microbenchmark by a call count and
compares the product against FrameVerdict TTFV (0.151 ms) — which is the
latency of a single proof verdict, not of a turn. Those are not comparable
quantities. The correct denominator is the turn.
2. Direct measurement
versor_condition wrapped in a counter, three real ChatRuntime.chat() turns:
turns : 3
versor_condition calls : 9 (3.0 per turn)
time inside it : 1.34 ms total (0.448 ms/turn)
total chat() wall : 601.5 ms (200.5 ms/turn)
share of turn : 0.22%
The call count (3/turn) was right. The cost conclusion was not. Switching the Rust backend on would recover 0.22% of a turn through this path. That is not a reason to do it, and it removes the only quantified justification the substrate track had.
3. Where the time actually goes
cProfile, three turns, post-warmup:
| calls | tottime | cumtime | function |
|---|---|---|---|
| 3 | 0.001 | 2.242 | chat/runtime.py::chat |
| 33,986 | 1.631 | 1.656 | algebra/cl41.py::geometric_product |
| 16,418 | 0.029 | 1.608 | algebra/cga.py::cga_inner |
| 24 | 0.009 | 1.156 | generate/proposition.py::_nearest_by_cga |
| 3 | 0.023 | 0.626 | generate/salience.py::compute |
| 4,577 | 0.003 | 0.458 | algebra/backend.py::cga_inner |
So the CGA algebra is the dominant per-turn cost — roughly 73% — but
through cga_inner in nearest-neighbour and salience search, not through the
versor invariant. Both documents aimed at the wrong function.
The multiplier is structural. algebra/cga.py::cga_inner is:
XY = geometric_product(X, Y)
YX = geometric_product(Y, X)
return 0.5 * scalar_part(XY + YX)
Two full 32×32 products — ~2,048 multiply-adds — to read one scalar. At 16,418 calls per turn that is the entire profile.
4. The tempting shortcut, and why it must not ship
algebra/backend.py already documents the closed form: for Cl(p,q) basis
blades e_i * e_j is scalar only when i == j, so
cga_inner(X, Y) == sum_i metric[i] * X[i] * Y[i]
32 multiply-adds instead of 2,048. Tested against the scalar path, 4,000 random
pairs per dtype, using np.sum:
float32: bit-exact 954/4000 worst-rel 1.037e-04
float64: bit-exact 929/4000 worst-rel 2.283e-13
Not bit-exact. A 1e-4 relative divergence in f32 would move surfaces that
trace_hash folds, break the committed lane SHA pins, and violate the
Rust/Python parity contract (core-rs/tests/test_crdt_hash_parity.rs). This is
the same class of error as proposing bfloat16 against a 1e-6 versor gate:
mathematically identical, numerically not.
5. The pattern that is exact
The divergence above is not the identity's fault — it is the reduction order.
np.sum reduces pairwise. algebra/backend.py::vault_recall folds serially in
component order:
scores = np.zeros(M.shape[0], dtype=np.float32)
for i in range(M.shape[1]):
scores += (_CGA_INNER_METRIC[i] * M[:, i]) * q[i]
Its docstring claims this is "bit-identical to the scalar cga_inner path
because the per-versor sum is folded in the same serial component order
(ADR-0019 Stage 1)". Tested directly, 3,000 random f32 pairs:
serial-fold vs scalar cga_inner (f32): bit-exact 3000/3000 worst-rel 0.000e+00
The claim holds exactly. So this repo already contains a proven, ratified, bit-exact vectorization of the very operation that dominates the profile — scoring one query against N versors without a Python-level product loop.
6. The actual next step
Apply the vault_recall serial-fold pattern to the nearest-neighbour search
paths that dominate the profile — generate/proposition.py::_nearest_by_cga
and generate/salience.py::compute — which today call scalar cga_inner in a
Python loop over candidates.
Why this is the right target:
- No GPU, no Metal, no MLX, no Rust, no new numerics. Nothing in the determinism doctrine has to move.
- The exactness is proven, not assumed (§5), and the acceptance test is
falsifiable and cheap: N turns before/after must produce byte-identical
surfaces and identical
trace_hashvalues. - It attacks 73% of turn time rather than 0.22%.
Not done in this session, deliberately. These functions feed articulation, and the local-first merge bar is the full suite; this session could run curated suites only (see §7). The measurement and the exactness proof are the expensive parts and they are done — the change itself should land where it can be gated properly.
7. What could not be verified here, and why
cargo test in core-rs/ could not run: the sandbox network policy denies
static.crates.io (the proxy's noProxy list covers index.crates.io, so the
sparse index resolves but crate tarballs 403 at the gateway). No crates are
cached. So:
- "Does
core_rsstill hold bit-exact parity?" — the question the plan put first, and the one the hardware blueprint skipped — remains open. It is still the right question; §2 just removes the urgency the performance argument was supplying. - The Rust typestate lane (
UnverifiedClaim→VersorClaim) was not written. It is still the strongest of the blueprint's five proposals — the only one with no determinism cost, and it needs no GPU — but shipping Rust that cannot be compiled or tested in the session that wrote it is not something to do. It carries forward unchanged.
Python measurements above ran on a scratch venv at 3.12.11; the repo pins
==3.12.13, which uv cannot fetch for linux-x86_64. The pin was left
untouched.
8. Standing verdicts on the blueprint's five items
Unchanged by this measurement except where noted.
| Proposal | Verdict |
|---|---|
| Rust typestate | Adopt — carried forward, blocked only on a build. |
| SoA layouts | Resequence — and now further down: the copy boundary is not what the profile shows. |
Custom sparse .metal Cl(4,1) kernel |
Premature, and now doubly so: there is a bit-exact CPU win (§5–6) available before any GPU question is live. |
| MLX lazy kernel fusion of the versor invariant | Rejected on target selection, additionally to the doctrine objection: the function it fuses is 0.22% of a turn. |
| bfloat16 asymmetric precision | Reject — §4 is the empirical form of the same argument at a much coarser epsilon. |
Relates to project-generalization-arc.