{"record":{"id":"e1cfaa738cc99f56","repo":"koala73/worldmonitor","slug":"physical-divergence-composite-repeats-a-metal-weig","errorCode":null,"errorMessage":"Physical divergence composite repeats a metal weight","messagePattern":"Physical divergence composite repeats a metal weight","errorType":"exception","errorClass":"TypeError","httpStatus":null,"severity":"error","filePath":"server/_shared/physical-divergence-snapshot.ts","lineNumber":349,"sourceCode":"  };\n}\n\nfunction canonicalWeights(value: unknown): PhysicalDivergenceRawComposite['weights'] {\n  if (!Array.isArray(value) || value.length !== PHYSICAL_DIVERGENCE_METALS.length) {\n    throw new TypeError('Physical divergence composite has invalid weights');\n  }\n  const byMetal = new Map(value.map((entry) => {\n    const raw = object(entry);\n    const weightMetal = metal(raw.metal);\n    if (\n      !finite(raw.weight)\n      || raw.weight !== PHYSICAL_DIVERGENCE_CONTRACT.metals[weightMetal].weight\n      || methodology(raw.methodologyVersion) !== PHYSICAL_DIVERGENCE_METHODOLOGY_VERSION\n    ) throw new TypeError('Physical divergence composite has an invalid weight');\n    return [weightMetal, raw.weight] as const;\n  }));\n  if (byMetal.size !== PHYSICAL_DIVERGENCE_METALS.length) {\n    throw new TypeError('Physical divergence composite repeats a metal weight');\n  }\n  return PHYSICAL_DIVERGENCE_METALS.map((weightMetal) => ({\n    metal: weightMetal,\n    weight: PHYSICAL_DIVERGENCE_CONTRACT.metals[weightMetal].weight,\n    methodologyVersion: PHYSICAL_DIVERGENCE_METHODOLOGY_VERSION,\n  }));\n}\n\nfunction validateStoredComposite(\n  value: unknown,\n  readings: PhysicalDivergenceRawReading[],\n): void {\n  const raw = object(value);\n  methodology(raw.methodologyVersion);\n  canonicalWeights(raw.weights);\n  const expected = buildPhysicalStressComposite(readings);\n  const actualState = state(raw.state);\n  const actualIndex = nullableFinite(raw.index);","sourceCodeStart":331,"sourceCodeEnd":367,"githubUrl":"https://github.com/koala73/worldmonitor/blob/9361220cc013571781071f0206e4d80fd14b2f7f/server/_shared/physical-divergence-snapshot.ts#L331-L367","documentation":"canonicalWeights() validates a stored physical-divergence composite's weights against the PHYSICAL_DIVERGENCE_CONTRACT (exact per-metal weight, methodology version). After mapping, if the number of distinct metals (byMetal.size) is less than PHYSICAL_DIVERGENCE_METALS.length, two entries resolved to the same metal — meaning the stored composite repeats a metal weight and is structurally corrupt. It throws a TypeError.","triggerScenarios":"Loading/normalizing a stored composite snapshot whose weights array contains two entries for the same metal (e.g. two gold entries, silver missing), typically produced by an older methodology version or hand-edited/corrupted cache data.","commonSituations":"Redis/seeded cache written by an earlier code version whose contract changed; a partially overwritten snapshot; a test fixture with duplicated metals; manual cache surgery during an incident.","solutions":["Delete/regenerate the stored composite (invalidate the cache key) so it is rebuilt from current readings under the current contract.","Check PHYSICAL_DIVERGENCE_CONTRACT and PHYSICAL_DIVERGENCE_METHODOLOGY_VERSION: if the contract changed, migrate or purge old snapshots rather than re-validating them.","Fix the writer that produced the duplicate-metal entry so new snapshots contain exactly one weight per metal.","Update test fixtures / seed data to include exactly the metals in PHYSICAL_DIVERGENCE_METALS."],"exampleFix":"// before (stored weights)\n[{ metal: 'gold', weight: 0.6 }, { metal: 'gold', weight: 0.4 }]\n// after\n[{ metal: 'gold', weight: 0.6 }, { metal: 'silver', weight: 0.4 }]","handlingStrategy":"type-guard","validationCode":"const metals = new Set(weights.map((w) => w.metal));\nif (metals.size !== weights.length) throw new Error('duplicate metal in composite weights');","typeGuard":"function hasUniqueMetals(weights: ReadonlyArray<{ metal: string }>, expected: readonly string[]): boolean {\n  const metals = new Set(weights.map((w) => w.metal));\n  return metals.size === expected.length && expected.every((m) => metals.has(m));\n}","tryCatchPattern":"try {\n  const snapshot = normalizePhysicalDivergenceSnapshot(stored, Date.now());\n} catch (err) {\n  if (err instanceof TypeError && /repeats a metal weight|invalid weight/.test(err.message)) {\n    stored = await rebuildSnapshotFromReadings(); // purge + regenerate corrupt cache entry\n  } else throw err;\n}","preventionTips":["Write composites only from the canonical contract mapping (one entry per PHYSICAL_DIVERGENCE_METALS entry).","Bump methodology version and invalidate caches whenever the metal contract changes.","Add uniqueness assertions to seed/fixture generators.","Never hand-edit cached composites."],"tags":["data-integrity","validation","cache","snapshot","physical-divergence"],"backgroundTag":"snapshot-validation-failed","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"}