fix: remove hidden score modifiers + fix Zürich auto-inference false positive

Score formula simplified to: finalScore = round(hard×60% + soft×40%)
Removes dataQualityModifier, confidenceModifier, mustHaveEval.scoreImpact —
the breakdown panel now always matches the displayed score.

Also removes 'industrie groove' from Zürich-only location signals — the phrase
describes an aesthetic preference, not a district reference.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Benjamin Sutter
2026-05-24 17:33:55 +02:00
parent 609a3634bd
commit 9df5d285f5
2 changed files with 5 additions and 15 deletions
+4 -13
View File
@@ -584,17 +584,8 @@ export function calculateScore(need: Need, property: Property): MatchEngineOutpu
] ]
const mustHaveEval = scoreMustHaves(allMustHaveText, property) const mustHaveEval = scoreMustHaves(allMustHaveText, property)
// ── Modifiers: data quality and confidence adjust the final score ───────── // ── Final score: hard/soft weighted sum, clamped 0100 ───────────────────
// These are intentionally applied after the hard/soft weighted sum so that const rawFinal = hardMatchScore * 0.60 + softFactorScore * 0.40 - hardFilter.severePenalty
// a low-confidence or poor-data property can be penalised without distorting
// the individual criterion breakdown.
const dataQualityModifier = calcDataQualityModifier(property)
const confidenceModifier = calcConfidenceModifier(property)
// ── Final score: hard/soft weighted sum + must-have penalty + modifiers ───
const baseScore = hardMatchScore * 0.60 + softFactorScore * 0.40
const rawFinal = baseScore - hardFilter.severePenalty + mustHaveEval.scoreImpact
+ dataQualityModifier + confidenceModifier
const finalScore = Math.round(Math.min(100, Math.max(0, rawFinal))) const finalScore = Math.round(Math.min(100, Math.max(0, rawFinal)))
// ── Factor classification — only use weighted soft factors for positive/negative ── // ── Factor classification — only use weighted soft factors for positive/negative ──
@@ -622,8 +613,8 @@ export function calculateScore(need: Need, property: Property): MatchEngineOutpu
finalScore, finalScore,
hardMatchScore, hardMatchScore,
softFactorScore, softFactorScore,
dataQualityModifier, dataQualityModifier: 0,
confidenceModifier, confidenceModifier: 0,
positiveFactors, positiveFactors,
negativeFactors, negativeFactors,
allHardFactors: hardFactors, allHardFactors: hardFactors,
+1 -2
View File
@@ -60,8 +60,7 @@ export function mockParseNeed(input: string): ParseNeedResult {
// Contextual Zürich inference: only truly Zürich-specific place/brand names // Contextual Zürich inference: only truly Zürich-specific place/brand names
const ZURICH_ONLY_SIGNALS = [ const ZURICH_ONLY_SIGNALS = [
'industrie groove', 'industrie-groove', 'pfingstweidstrasse', 'freilager', 'europaallee',
'pfingstweidstrasse', 'hardbrücke', 'freilager', 'europaallee',
'zürich-west', 'zürich west', 'zürich nord', 'zürich süd', 'zürich-west', 'zürich west', 'zürich nord', 'zürich süd',
'hürlimann', 'viadukt', 'schiffbau', 'hürlimann', 'viadukt', 'schiffbau',
] ]