HardYards/web/world/js/ratings.js

69 lines
3.4 KiB
JavaScript

/**
* SHADES / HARD YARDS — Lane E owns this file.
*
* The runtime end of the factory's anchor ratings (SPRINT15 gate 1.2).
*
* THE BUG THIS CLOSES: `adoptAnchor` is the only place a baked `rating_hint`
* ever reached an anchor, and it only runs for anchors that name a GLB `node`.
* Every shipped sail post is a plain JSON entry with no `node`, so the
* factory's bake never arrived and `?? 1` won — the factory said 0.90, the sim
* played 1.00, and nothing anywhere could see the disagreement. Gate 1.1 ruled
* the shipped 1.00 canon (every measured number stands on it), and this module
* is the mechanism half of that ruling: the same silent divergence is now
* impossible for the whole extras family (`rating_hint`, `collateral`, and by
* exclusion `tie_off`) on ANY node-less JSON anchor whose type the factory
* rates.
*
* HOW: the factory emits `anchor_ratings.gen.js` from the re-imported GLBs —
* per anchor TYPE, the baked extras, but only where every rated node of that
* type in the palette agrees (`types`). Types whose nodes disagree (tree:
* 1.0 → 0.4 down the trunk) are listed under `ambiguous`, and this module
* REFUSES to guess for them — a node-less anchor of an ambiguous type keeps
* the default hint and warns loudly, and e.test.js pins that no shipped site
* declares one. Three-way agreement is pinned in e.test.js:
* GLB === manifest === runtime, so a re-bake that skips regeneration, a
* hand-edit to the gen file, or a future adoptAnchor-shaped gap all go red.
*
* WHO CALLS IT: world.js, at the JSON-anchor creation sites, as
* `Object.assign(makeStaticAnchor(...), ..., a.node ? null : factoryExtras(type))`
* — node-carrying anchors keep their graybox default and are corrected by
* adoptAnchor at dress time, exactly as before; node-less ones take the
* factory's word at construction, because dress will never reach them.
*/
import { FACTORY_ANCHOR_RATINGS } from './anchor_ratings.gen.js';
/** Warn once per type, not once per anchor — a site with four posts of a
* future ambiguous type should not print four identical warnings. */
const warned = new Set();
/**
* The factory's baked extras for an anchor TYPE, in runtime (camelCase) field
* names, ready to `Object.assign` onto an Anchor.
*
* @param {string} type An ANCHOR_TYPE string (contracts.js).
* @returns {{ratingHint: number, collateral?: string}|null}
* The extras when the factory rates this type unambiguously; null when it
* doesn't rate it at all (caller keeps its default) or rates it ambiguously
* (caller keeps its default AND this is almost certainly a site bug — a
* node-less anchor of an ambiguous type has no honest rating; declare a
* `node` instead. e.test.js refuses it in shipped sites).
*/
export function factoryExtras(type) {
const t = FACTORY_ANCHOR_RATINGS.types[type];
if (t) {
const out = { ratingHint: t.rating_hint };
if (t.collateral !== undefined) out.collateral = t.collateral;
return out;
}
if (FACTORY_ANCHOR_RATINGS.ambiguous[type] && !warned.has(type)) {
warned.add(type);
console.warn(
`[ratings] anchor type "${type}" is factory-AMBIGUOUS (its GLB nodes carry `
+ `different rating_hints) — a node-less "${type}" anchor falls back to the `
+ `default hint, which is the exact silent-divergence bug gate 1.2 buried. `
+ `Declare a \`node\` on the anchor so adoptAnchor can carry the real bake.`);
}
return null;
}