Give the stage 0 grammar set one home (V-693)

buildRouter held the real set and baselineGrammars in eval_test.go restated it
by hand, in the daemon's order, with its own comment saying so. Three test files
score against the fixture and nothing compared the two lists. They had already
drifted: BareCaptureGrammar went into the daemon with V-557 and never into the
fixture, so every routing measurement since has scored a set nobody runs. That
is the failure CLAUDE.md warns about by name, and a diff test would have caught
it one grammar late.

The list moves to router.StageZeroGrammars in internal/router/stagezero.go, with
the ordering comments, which are the load-bearing part. buildRouter and the
fixture both call it. One list cannot drift from itself.

Measured before and after on the 96-case fixture: classifier+onnx 72/96, 75.0%
intent, 33.3% destination, identical either way, and the deterministic claim and
reach hash ratchets do not move. So the missing grammar cost no measurable
accuracy. That is the point rather than a reprieve: the fixture had been scoring
the wrong set for four days and nothing could say so.

The invariants caveat is deleted, both entries, since V-692 landed the other
guard in the previous commit. The reasoning for both now sits in docs/routing.md
beside the subsystem, which is where a fix's durable record belongs.

Unrelated and pre-existing: TestONNXPersonalBoundary fails on "я рассказывал
тебе про байкал?" (personal 0.9068, world 0.9413) at the merge base too.
This commit is contained in:
2026-08-11 21:02:31 +04:00
parent d8efb667c7
commit 240d53a96a
7 changed files with 98 additions and 105 deletions
+17 -5
View File
@@ -1,6 +1,6 @@
# Routing
*Last verified: 2026-08-09 @ 31b5093*
*Last verified: 2026-08-11 @ 25ed201*
How an utterance becomes a `Decision`, why each stage exists, and what every
stage has measured. `CLAUDE.md` carries the rules an agent must not break. This
@@ -137,10 +137,15 @@ below.
Go's `\b` is ASCII-only and never fires after a Cyrillic letter. A pattern needs
an explicit `(\s|[?!.]|$)`.
`baselineGrammars` in `eval_test.go` mirrors `buildRouter` and has drifted before.
`WorldQueryGrammars` was wired into the daemon by V-655 and not into the mirror,
so the fixture scored a grammar set nobody runs. Fixed by V-659, worth 3 points
of destination.
The stage 0 set lives in `router.StageZeroGrammars` (`internal/router/stagezero.go`).
Both `buildRouter` and the eval fixture call it. The daemon and the measurement
cannot disagree about which rules exist, or in what order.
It was two lists until V-693 and it drifted twice. V-655 wired
`WorldQueryGrammars` into the daemon and not into the fixture. That cost 3 points
of destination and V-659 fixed it. `BareCaptureGrammar` then did the same thing,
from V-557 until V-693 found it. That one moved no number, which is the point:
the fixture had been scoring a set nobody ran and nothing said so.
### Praxis lifecycle rules
@@ -270,6 +275,13 @@ Three rules around it, each measured:
means the heads are nil. The cascade is then byte-for-byte what shipped before
them.
Pointing it at `model_path` is refused at config load (V-692). An unloadable
weights file is not fatal, because the heads are an accelerator. A working file
in the wrong role is a different thing. The heads then score with the graph the
resident embedder scored with, and recall degrades with no log line. The check
cleans and absolutises both paths, then compares them with `os.SameFile`, so a
symlinked copy is caught too.
### The tokenizer bug the heads found
`encodeWord` in `onnxembedder.go` read every long word backwards until 2026-08-08.