SPB Git

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%
2.5 KB · 61 lines typescript
Raw Blame History
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