Three findings from the deep review, all about the tag channel claiming more than it
can support.
1. The exact signal is a barcode. The reconciliation compared `tag.jan` against the
catalog's JAN, but that field held the regular price - a number compared against
a product code. It now reads a `barcode` field (populated only by a close-up
decode) and the printed number is compared as a price.
2. A price-mismatch violation required "an anchor", which was `tag.jan` - a price.
The anchor is now the tag's own arithmetic agreeing, or a name that actually
resembles the product, and the name signal carries its score as a field instead
of being parsed back out of a human-readable note string.
3. Tag identity could reach anywhere along a row: a facing whose own tag was not
detected inherited whichever readable tag was nearest, which can be a different
product several columns away. Assignment is now capped at the product's own run
(2.5 facing widths, at least 200 px), and a tag may only overrule the packaging
channel when it is corroborated or its name matches at 0.6 rather than 0.4.
162 tests pass.
Co-Authored-By: Devin <158243242+devin-ai-integration[bot]@users.noreply.github.com>