spb/earth-now Public License
earth-now.co — real-time planetary dashboard: live world metrics modeled, not streamed.
TypeScript 93%
Shell 2.3%
SQL 1.4%
JavaScript 1.3%
Dockerfile 1.2%
CSS 0.8%
1/**2 * earth-now.co3 * Author: Simon-Pierre Boucher4 * Contact: contact@spboucher.ai5 * File: apps/api/src/scripts/refit.ts6 * Purpose: CLI re-fit + diff report (pnpm models:refit) — old vs new value at now, jump, deployability gate per metric7 */89import { counterValue, formatValue } from "@earth-now/counter";10import { assertDeployable, diffModels } from "@earth-now/models";11import { fitAllModels, loadFixtures, loadRegistry } from "@earth-now/registry";12import { fitHorizon, metricConstraints } from "../store.js";1314const nowMs = Date.now();15const registry = loadRegistry();16const fixtures = loadFixtures();17const { fromMs, toMs } = fitHorizon(nowMs);18const fitOpts = { validateFromMs: fromMs, validateToMs: toMs };19const horizon = { fromMs, toMs };2021// The currently deployed set. TODO: read from the counter_models table once the22// DB layer lands — until then the fixture fit stands in for it, so the reported23// jump is 0 by construction and the report exercises the full gate anyway.24const previousModels = fitAllModels(registry.metrics, fixtures, fitOpts).models;25const next = fitAllModels(registry.metrics, fixtures, fitOpts);2627let failures = 0;28console.log(29 `refit report @ ${new Date(nowMs).toISOString()} — ${registry.metrics.length} metrics, horizon ${new Date(fromMs).toISOString()} → ${new Date(toMs).toISOString()}\n`,30);3132for (const entry of registry.metrics) {33 const previous = previousModels.get(entry.id) ?? null;34 const model = next.models.get(entry.id);3536 if (!model) {37 failures += 1;38 const issues = next.blocked.find((b) => b.metricId === entry.id)?.issues ?? [];39 const detail = issues.map((i) => `${i.code}: ${i.message}`).join("; ") || "no model produced";40 console.log(`✗ ${entry.id}: BLOCKED — ${detail}`);41 continue;42 }4344 const hints = model.displayHints;45 const newValue = counterValue(model, nowMs);46 const oldText = previous ? formatValue(counterValue(previous, nowMs), hints) : "(new)";47 const jump = previous ? diffModels(previous, model, nowMs) : 0;48 const issues = assertDeployable(previous, model, metricConstraints(entry), nowMs, horizon);49 const ok = issues.length === 0;50 if (!ok) failures += 1;5152 console.log(53 `${ok ? "✓" : "✗"} ${entry.id}: old=${oldText} new=${formatValue(newValue, hints)} ${hints.unit} jump=${jump.toPrecision(3)}${ok ? "" : ` — NOT DEPLOYABLE: ${issues.map((i) => i.code).join(", ")}`}`,54 );55}5657console.log(58 `\n${failures === 0 ? "all models deployable" : `${failures} metric(s) NOT deployable — deployment must be blocked`}`,59);60if (failures > 0) process.exit(1);61