The live path abstained too much: a detected product whose best candidate missed the accept line was left blank, so a shelf where 59% of in-catalog facings were shown read as broken. Show the best candidate immediately as `provisional`, keep the track re-classifiable so a later attempt upgrades or corrects it, and never blank a label already on screen. - identity.py / identity_lanes.py: every decision now carries `provisional_sku` - the row the evidence supports well enough to show, separate from `best_sku`, which still means "cleared the gate". The accept line does not move: far-unknown false accepts stay at 0.066%. - live.py: a below-gate candidate labels the track with `identity_level: provisional` and `confirmed = False`, so the planner and the crop lane keep offering it. Accepted labels stay frozen. Inherited provisional labels stay re-classifiable. - Clients render provisional distinctly (amber chip, dashed box) via `LiveScanOverlayPolicy.isProvisional(identity)`, which now reads the wire level instead of only the state. - build_stock_all.py merges stock, stock-lopia, stock-mediya and stock-milk150 into stock-all (111 images, 60 SKUs). The server could only read one folder, so anything outside it came back blank. stock-demo is byte-identical to mediya+milk150 and is not a source. Measured on add/image6.jpg at live resolution: in-catalog facings shown 59.4% -> 86.0%, correct shown 38 -> 55, precision 97.4% -> 87.3%. On the wire at 8fps 0.5x: 65 of 79 tracks labelled, 742 accepted + 309 provisional observations, first label 1.27s. docs/research/2026-09-29-shelf-intelligence/ carries the experiments and the decision: group signals (combined AUC 0.991 sibling / 0.990 unrelated), OCR scheduling (group representatives cut calls 4.4x and wall 3.3x), and the finding that group propagation damages more labels than it rescues on this shelf, so groups schedule compute rather than overwrite labels. 331 tests pass.
22 lines
3 KiB
JSON
22 lines
3 KiB
JSON
{
|
|
"scope": "Research prototypes only; production files unchanged",
|
|
"baseline_revision": "fbf31d74feb12538dc07207cb80722dac38dd438",
|
|
"primary_data": "eval/labels/image6.json",
|
|
"catalog": "stock-lopia-clean",
|
|
"data_status": "Single development photo, previously tuned. No cross-visit generalization claim.",
|
|
"controls": ["Freeze catalog and labels; hash inputs", "Extract evidence once, replay identical evidence across policies", "Inference receives boxes and observations, never identity ground truth", "Oracle arm alone receives true group membership", "Unknown is not a SKU: never merge unknowns merely because labels equal", "Report labels over all 109 facings and correct labels over all 64 catalog facings separately", "Keep geometry-derived rows fixed across methods", "Compare deterministic runs to chain dynamic programming", "Report group rescue, correction and damage separately", "Separate measured wall time from cached-evidence simulations", "Never call a synthetic pan a recorded walk", "Use paced video timestamps, not result-synchronous sends, for full-rate replay"],
|
|
"hypotheses": {
|
|
"A": "Color plus normalized appearance and local correspondences separate repeated facings better than either alone; brand siblings remain difficult.",
|
|
"B": "Geometry-only rows plus appearance-supported run edges identify repeats without truth labels; leave-one-row-out calibration limits same-row tuning.",
|
|
"C": "Perfect membership sharing observed strong member evidence recovers at least 5 percentage points of catalog-facing correct coverage. No oracle SKU assignment.",
|
|
"D": "Inferred group recovery retains useful oracle gain without more than one damaged originally correct facing; measure rather than require a positive result.",
|
|
"E": "Visual-first scheduling and selective group representative OCR reduce actual OCR calls and make first evidence available sooner; never infer speed from call counts alone.",
|
|
"F": "Coverage-first has greater product utility than strict even with lower shown-label precision. Near-argmax is a measured stress point, not the presumed winner.",
|
|
"G": "Independent frame pacing exposes queue churn that the existing send/result client cannot test because both wait for every result. Compare identical source frame sequences at native sample pacing and 0.5x."
|
|
},
|
|
"policies": {"strict":4.0,"balanced":2.0,"coverage-first":0.5,"near-argmax":0.0},
|
|
"confirmation": "Strict production decision is strong evidence, not calibrated probability. All extra identities provisional.",
|
|
"utility_costs_per_facing_second": {"blank":1.0,"wrong_sibling_provisional":1.5,"wrong_other_provisional":4.0,"wrong_confirmed":8.0,"correct":0.0,"flip_event":0.2},
|
|
"utility_sensitivity": "Sweep wrong-label cost; no empirical user-cost calibration claimed",
|
|
"required_outputs": ["raw per-facing and per-pair evidence", "boundary/group confusion counts", "policy frontier plot", "OCR arm costs", "per-frame live timelines", "latency p50/p95 and CPU", "decision A-H with limitations and implementation acceptance"]
|
|
}
|