{"record":{"id":"e5eaaa2d201620d3","repo":"koala73/worldmonitor","slug":"physical-divergence-composite-repeats-a-metal-r","errorCode":null,"errorMessage":"Physical divergence composite repeats a metal: ${reading.metal}","messagePattern":"Physical divergence composite repeats a metal: (.+?)","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"shared/physical-divergence-contract.js","lineNumber":137,"sourceCode":"  return STORED_READING_REASONS_BY_STATE[state]?.includes(reason) === true;\n}\n\nexport function isPhysicalDivergenceStoredCompositeReason(state, reason) {\n  if (state === 'ok') return reason === '';\n  return NON_OK_STATE_PRIORITY.includes(state)\n    && COMPOSITE_MEMBER_REASONS.includes(reason)\n    && reason.endsWith(`:${state}`);\n}\n\nexport function buildPhysicalStressComposite(readings) {\n  const values = Array.isArray(readings) ? readings : [];\n  const byMetal = new Map();\n  for (const reading of values) {\n    if (!METAL_ORDER.includes(reading?.metal)) {\n      throw new TypeError(`Unsupported physical divergence metal: ${String(reading?.metal)}`);\n    }\n    if (byMetal.has(reading.metal)) {\n      throw new TypeError(`Physical divergence composite repeats a metal: ${reading.metal}`);\n    }\n    if (!STATES.includes(reading.state)) {\n      throw new TypeError(`Unknown physical divergence state: ${String(reading.state)}`);\n    }\n    byMetal.set(reading.metal, reading);\n  }\n  const weights = METAL_ORDER.map((metal) => ({\n    metal,\n    weight: PHYSICAL_DIVERGENCE_CONTRACT.metals[metal].weight,\n    methodologyVersion: METHODOLOGY_VERSION,\n  }));\n  for (const metal of METAL_ORDER) {\n    if (!byMetal.has(metal)) {\n      return {\n        state: 'missing_input',\n        reason: `member_not_ok:${metal}:missing_input`,\n        index: null,\n        weights,","sourceCodeStart":119,"sourceCodeEnd":155,"githubUrl":"https://github.com/koala73/worldmonitor/blob/9361220cc013571781071f0206e4d80fd14b2f7f/shared/physical-divergence-contract.js#L119-L155","documentation":"Assertion error raised by shared/physical-divergence-contract.js when the physical divergence composite calculation receives a readings list containing the same metal more than once. Each metal must appear exactly once so the composite divergence score is unambiguous; duplicates make the per-metal weighting invalid, so the contract fails fast with the offending metal named in the message. Fix upstream by deduplicating readings (e.g., keep the latest reading per metal) before calling the composite builder.","triggerScenarios":"Passing duplicate readings for 'gold' or 'silver' — e.g. concatenating two snapshot arrays, appending a live update to an already-complete reading list, or a producer emitting both a stored and a corrected reading.","commonSituations":"Merging historical + realtime data without dedupe; retry logic re-inserting a reading; upstream publishing the same metal under slightly different keys that normalize to the same metal.","solutions":["Dedupe readings by metal (last-wins or newest-timestamp) before the call","Build readings as a Map keyed by metal so duplicates are impossible","Assert uniqueness before calling: new Set(readings.map(r => r.metal)).size === readings.length","Fix the producer to emit exactly one reading per contract metal"],"exampleFix":"// before\nbuildPhysicalStressComposite([...snapshotReadings, ...liveReadings])\n// after\nconst byMetal = new Map([...snapshotReadings, ...liveReadings].map((r) => [r.metal, r]));\nbuildPhysicalStressComposite([...byMetal.values()])","handlingStrategy":"validation","validationCode":"function dedupeByMetal(readings) {\n  const byMetal = new Map();\n  for (const r of Array.isArray(readings) ? readings : []) {\n    if (r?.metal) byMetal.set(r.metal, r); // last wins\n  }\n  return [...byMetal.values()];\n}","typeGuard":"function hasUniqueMetals(readings) {\n  const metals = (Array.isArray(readings) ? readings : []).map((r) => r?.metal);\n  return metals.every((m) => typeof m === 'string') && new Set(metals).size === metals.length;\n}","tryCatchPattern":"try {\n  composite = buildPhysicalStressComposite(readings);\n} catch (e) {\n  if (e instanceof TypeError && e.message.includes('repeats a metal')) {\n    composite = buildPhysicalStressComposite(dedupeByMetal(readings));\n  } else throw e;\n}","preventionTips":["Build reading lists as a Map keyed by metal","Dedupe when merging snapshot + live data","Prefer newest-timestamp reading when duplicates arise","Assert uniqueness in tests for any producer of reading arrays"],"tags":["contract","typeerror","duplicate-data"],"backgroundTag":"duplicate-enum-entry","analyzedSha":"9361220cc013571781071f0206e4d80fd14b2f7f","analyzedAt":"2026-09-01T10:32:37.851Z","contentChangedAt":"2026-09-01T10:32:37.851Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}