MariesWiki
MariesLib · Enhancement

Scanner Classification Enhancement Notes

Scanner Classification — Deferred Enhancements

Design notes Updated 26 Aug 2026 2 min read

Captured from the discovery pass on RecipeInheritanceStage.mergeQualifyingContributions (the scanner's own multi-value merge) after fixing B1/B2/B3 — the load-bearing fix that stops contested recipe mass from being silently dropped. These two items were deliberately scoped out of that fix and are not scheduled.

Part A — Ingredient-provenance plumbing (diagnostics only, no functional payoff yet)

What it would do: wire both RecipeInheritanceStage's 2-arg resolve(item, lookup) call sites (line 58, the scanner path; line 101, mergeTagAndRecipeScores) through the existing but currently dead 4-arg overload — resolve(item, lookup, traceOut, classificationTraceOut) — which already correctly captures real, distinct per-ingredient IDs (verified via resolveRecursive; not the parent item's ID repeated).

Why it's deferred: confirmed via discovery that this is purely additive diagnostics. MultiValueAnalysisPipeline.analyze only ever reads r.scores()/r.tagClassified() — it has no channel for per-ingredient data at all. Wiring the 4-arg overload today would produce richer trace output (real ingredient IDs instead of raw_onion_calzone repeated three times) with zero effect on multi-value/single-value classification counts, because nothing downstream consumes it.

What's needed before this has payoff:

  • RecipeInheritanceStage.apply's signature would need a trace parameter threaded from ItemClassifier.classify (plumbing target already exists, flows to ItemScanner's traceMap).
  • SourceCollector.toClassificationResult currently discards signals entirely (signals = List.of()) — would need a new ClassificationResult field or an itemId-keyed side map to carry provenance through that path.
  • buildIndex reduces each Ingredient to items[0] — acceptable for provenance identity as-is, but worth a explicit decision on whether to widen to all candidate items later (bounds how accurate the provenance ends up being even after this is wired).

Bottom line: worth doing once there's an actual consumer for the data (e.g. showing real ingredient breakdowns in ambiguous_sources.txt for human review) — not before.

Part C — "Ingredients disagree" as its own multi-value trigger

The idea: right now, multi-value detection is entirely driven by the shape of the merged score vector (does a secondary category clear the 0.15 absolute / 0.35 relative thresholds). A separate, independent trigger could instead ask: "do this item's distinct, confidently-classified ingredients disagree with each other?" — e.g. if a recipe's ingredients resolve to {grains, dairy, vegetables} with no single category dominating the ingredient list, that disagreement itself could be grounds for flagging multi-value, even in cases where the current score-based math doesn't clear the threshold.

Why it's deferred: this needs real threshold design, not a quick addition — "how many ingredients need to disagree, how confidently, by how much" is a genuine tuning question with no obvious default. It's also architecturally a third component:

Existing:  merged score vector → threshold check → multi-value?
Part C:    distinct ingredient classifications → disagreement check → multi-value?

Both could independently flag an item as multi-value; Part C would need to either merge with the existing check or run as a parallel, separately- reported signal.

Depends on: Part A landing first (or at least the ingredient-provenance capture, since this trigger needs to know what each ingredient actually resolved to, not just the aggregated map).

Priority note

Neither of these blocks anything currently shipping. The load-bearing fix (B1/B2/B3 — stop dropping contested mass, honest confidence, unconditional scanner surfacing) is what actually finds more real multi-value items like the calzone case. These two are refinements on top, worth revisiting once that fix has been run against real data and its output reviewed.

More MariesLib enhancements