docs: record the OCR cache and the two review fixes

The pacing paragraph described a defect that no longer exists. Replaced with
the measured before/after, and added the two review findings (the missing
always-argmax baseline lane, the size-token fill-in) with their test counts.
This commit is contained in:
tngo 2026-09-28 23:27:29 +09:00
parent 5f6de4d48a
commit b4d7106d28

View file

@ -630,16 +630,24 @@ Van's 32/61. So the design does not yet beat Van on both axes at full
resolution, and the accept line is provisional: it is tuned on Tier 1 and needs
per-input-path calibration on Tier 2.
The live count is pacing-dependent, not a fixed property: a track needs two
attempts before it settles, so a harness that pushes frames faster than the
classifier drains them reports fewer labels. Re-run at four pacings on the same
12-frame clip: 12, 8, 2 and 6 named at full speed, 14 named with 0.1 s between
frames, and 38 named of 30 frames at 0.1 s. Precision stays 100% throughout and
0 unknowns are accepted at every pacing. Coverage is a gate (G1), not a
footnote: 12 of 61 streamed is a regression against Van's 57% until device data
says otherwise, and the likely cause is measurable - OCR runs on every crop at
every attempt, taking a crop from 5 ms to 25 ms and throughput from 789 to 60
crops/s with 8 workers.
The live count was pacing-dependent, and the cause was measurable: OCR ran on
every crop at every attempt, taking a crop from 5 ms to 25 ms and throughput
from 789 to 60 crops/s with 8 workers. The reading is now cached on the track
and later attempts re-run only the local lane. Measured through the real server
on `add/image6.jpg`:
saturated pool 15.9 -> 49.4 crops/s (8 workers, 64 crops)
per crop 142 -> 92 ms on a 300x400 crop
3 s clip, send 19.7 mean labels (15-24, unstable) -> 24.0 (23-25, stable)
12 s clip, send 48/131 -> 53/131; result pacing 53/134 -> 50/135
The 12 s numbers are within run-to-run noise; the short clip is where the
pacing sensitivity lived, and it is now flat across pacings. Precision stays
100% and 0 unknowns are accepted at every pacing. Coverage is still a gate
(G1): 53 of 131 streamed tracks is not yet a claim against Van's 57% of
labelled facings, because the two count different things - tracks the detector
started versus labelled facings. The label-matched comparison is the
evaluator's, above.
The shipped default scale now has test coverage. `make_session` builds only
`matcher`-backend engines, so `scale == "local"` in `_on_classified` never ran
@ -648,7 +656,18 @@ injects the lanes' evidence into the live path, and `LocalScaleTests` pins the
two jobs (a text-only read still labels the track; a text veto blocks a local
accept). `DecisionPolicyTests` pins the veto/fill-in boundary, including the
lane-order-vs-value case the rank re-sort broke. Both regressions were
re-injected to confirm the tests fail for the intended reason. Suite 251 -> 274.
re-injected to confirm the tests fail for the intended reason. Suite 251 -> 281.
Two further review findings, both fixed and both measured:
- The evaluator had no always-argmax lane, so every "vs Van" claim compared
against the *gated* matcher rather than Van's contract. The `van` lane is
that contract; the numbers above use it.
- A crop reading only "400g" scored 0.667 on three SKUs, above `TEXT_ACCEPT`,
so the text lane named a product from a size line. The fill-in job now
requires two non-digit bigrams; the veto is unchanged, so "65%" still
contradicts a local answer. Three tests cover it and all three fail when the
gate is removed.
The headline number is reproducible from the repo, not from a scratch script: