diff --git a/client/src/lib/__tests__/buildout-reality.test.ts b/client/src/lib/__tests__/buildout-reality.test.ts
new file mode 100644
index 0000000..5bdafc0
--- /dev/null
+++ b/client/src/lib/__tests__/buildout-reality.test.ts
@@ -0,0 +1,43 @@
+// The proportion bar must partition the announced build-out exactly; a
+// segment sum that drifts from the total would make the chart quietly lie.
+import { test } from "node:test";
+import assert from "node:assert/strict";
+import { computeBuildoutReality, EPOCH_CAPEX_B_PER_GW } from "../buildout-reality";
+
+const METRICS = {
+ totalPlannedMW: 124_646,
+ operationalMW: 11_189,
+ byStatus: [
+ { status: "operational", ratedMW: 11_189, plannedMW: 19_309 },
+ { status: "construction", ratedMW: 852, plannedMW: 70_000 },
+ { status: "announced", ratedMW: 0, plannedMW: 35_337 },
+ ],
+};
+
+test("segments partition totalPlannedMW exactly", () => {
+ const r = computeBuildoutReality(METRICS);
+ assert.equal(r.energizedMW + r.growingOnSiteMW + r.constructionMW + r.announcedMW, r.totalPlannedMW);
+ assert.equal(r.energizedMW, 11_189);
+ assert.equal(r.growingOnSiteMW, 19_309 - 11_189);
+ assert.equal(r.announcedMW, 35_337);
+});
+
+test("energized share and capex context", () => {
+ const r = computeBuildoutReality(METRICS);
+ assert.ok(Math.abs(r.energizedShare - 11_189 / 124_646) < 1e-9);
+ assert.equal(r.unbuiltMW, 124_646 - 11_189);
+ assert.equal(r.unbuiltCapexB, Math.round(((124_646 - 11_189) / 1000) * EPOCH_CAPEX_B_PER_GW));
+});
+
+test("degenerate inputs stay safe", () => {
+ const r = computeBuildoutReality({ totalPlannedMW: 0, operationalMW: 0, byStatus: [] });
+ assert.equal(r.energizedShare, 0);
+ assert.equal(r.unbuiltCapexB, 0);
+ // rated above planned on operational clusters must not go negative
+ const odd = computeBuildoutReality({
+ totalPlannedMW: 100,
+ operationalMW: 90,
+ byStatus: [{ status: "operational", ratedMW: 90, plannedMW: 80 }],
+ });
+ assert.equal(odd.growingOnSiteMW, 0);
+});
diff --git a/client/src/lib/buildout-reality.ts b/client/src/lib/buildout-reality.ts
new file mode 100644
index 0000000..ff9cee9
--- /dev/null
+++ b/client/src/lib/buildout-reality.ts
@@ -0,0 +1,58 @@
+/**
+ * Buildout reality: how much of the announced AI build-out is actually
+ * energized today. The segments partition totalPlannedMW exactly, so the
+ * proportion bar is mathematically true, never suggestive:
+ *
+ * energized rated MW of operational clusters (running today)
+ * growingOnSite operational clusters' planned MW beyond what runs
+ * construction planned MW of clusters under construction
+ * announced planned MW of announced-only clusters
+ *
+ * Capex context uses Epoch AI's published ~$38B per GW of IT capacity
+ * ($26B compute + $12B construction). Our MW mix facility and IT bases,
+ * so the dollar figure is order-of-magnitude context, labeled as such.
+ */
+
+export interface StatusBucketLite {
+ status: string;
+ ratedMW: number;
+ plannedMW: number;
+}
+
+export interface BuildoutReality {
+ totalPlannedMW: number;
+ energizedMW: number;
+ growingOnSiteMW: number;
+ constructionMW: number;
+ announcedMW: number;
+ energizedShare: number; // 0..1 of the full announced build-out
+ unbuiltMW: number;
+ unbuiltCapexB: number; // rough $B at Epoch's ~$38B/GW
+}
+
+export const EPOCH_CAPEX_B_PER_GW = 38;
+
+export function computeBuildoutReality(m: {
+ totalPlannedMW: number;
+ operationalMW: number;
+ byStatus: StatusBucketLite[];
+}): BuildoutReality {
+ const bucket = (s: string) => m.byStatus.find((b) => b.status === s);
+ const opPlanned = bucket("operational")?.plannedMW ?? 0;
+ const energizedMW = m.operationalMW;
+ const growingOnSiteMW = Math.max(0, opPlanned - energizedMW);
+ const constructionMW = bucket("construction")?.plannedMW ?? 0;
+ const announcedMW = bucket("announced")?.plannedMW ?? 0;
+ const totalPlannedMW = m.totalPlannedMW;
+ const unbuiltMW = Math.max(0, totalPlannedMW - energizedMW);
+ return {
+ totalPlannedMW,
+ energizedMW,
+ growingOnSiteMW,
+ constructionMW,
+ announcedMW,
+ energizedShare: totalPlannedMW > 0 ? energizedMW / totalPlannedMW : 0,
+ unbuiltMW,
+ unbuiltCapexB: Math.round((unbuiltMW / 1000) * EPOCH_CAPEX_B_PER_GW),
+ };
+}
diff --git a/client/src/pages/compute-frontier.tsx b/client/src/pages/compute-frontier.tsx
index 2f5d5f1..2b67efc 100644
--- a/client/src/pages/compute-frontier.tsx
+++ b/client/src/pages/compute-frontier.tsx
@@ -26,6 +26,7 @@ import { ArrowUpDown, Atom } from "lucide-react";
import { PageHeader, HeaderStat } from "@/components/PageHeader";
import { BRAND, INK, SURFACE, CATEGORY_COLORS, STATUS_COLORS } from "@/lib/tokens";
import { axisProps, gridProps, seriesMotion, tooltipContentStyle, tooltipItemStyle, tooltipLabelStyle } from "@/lib/chart-theme";
+import { computeBuildoutReality } from "@/lib/buildout-reality";
// ─── Types (mirror /api/clusters and /api/clusters/metrics) ────────────────
@@ -263,6 +264,58 @@ export default function ComputeFrontier() {
+ {/* Buildout reality: how much of the promise is energized today */}
+ {metrics && (() => {
+ const r = computeBuildoutReality(metrics);
+ const seg = [
+ // "growth at live sites" is a lighter step of the operational hue:
+ // same sites, capacity not yet running. Construction and announced
+ // keep their own status-ramp colors.
+ { key: "energized", label: "energized today", mw: r.energizedMW, color: STATUS_COLOR.operational },
+ { key: "growing", label: "growth at live sites", mw: r.growingOnSiteMW, color: `${STATUS_COLOR.operational}66` },
+ { key: "construction", label: "under construction", mw: r.constructionMW, color: STATUS_COLOR.construction },
+ { key: "announced", label: "announced only", mw: r.announcedMW, color: STATUS_COLOR.announced ?? INK.faint },
+ ].filter((s) => s.mw > 0);
+ return (
+
+
+ Of the{" "}
+ {gw(r.totalPlannedMW)} GW announced
+ across every tracked cluster,{" "}
+ {gw(r.energizedMW)} GW is energized
+ today — {(r.energizedShare * 100).toFixed(1)}%.
+
+
+ {seg.map((s) => (
+
+ ))}
+
+
+ {seg.map((s) => (
+
+
+ {s.label} {gw(s.mw)} GW
+
+ ))}
+
+
+ The unbuilt {gw(r.unbuiltMW)} GW implies very roughly ${(r.unbuiltCapexB / 1000).toFixed(1)}T of
+ capital still to be spent, at{" "}
+
+ Epoch AI's ~$38B per GW
+ {" "}
+ of IT capacity ($26B compute + $12B construction). Order-of-magnitude context only: tracked MW
+ mixes facility and IT power bases, and announced figures are targets, not commitments.
+
+
+ );
+ })()}
+
{/* Charts */}
OP_TOP_N ? ` (top ${OP_TOP_N})` : ""}`}>
diff --git a/package-lock.json b/package-lock.json
index 19dc994..af9d835 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -3117,6 +3117,19 @@
"node": ">= 0.4"
}
},
+ "node_modules/baseline-browser-mapping": {
+ "version": "2.11.20",
+ "resolved": "https://registry.npmjs.org/baseline-browser-mapping/-/baseline-browser-mapping-2.11.20.tgz",
+ "integrity": "sha512-H0ulySigv6icDJ1F7SjtdCD6PrhTpdYCmP0CactWy1+ekh0AFd0o1Wn5T8b+hnTmdBx19u9yhL6wvCylXMY7zw==",
+ "dev": true,
+ "license": "Apache-2.0",
+ "bin": {
+ "baseline-browser-mapping": "dist/cli.cjs"
+ },
+ "engines": {
+ "node": ">=6.0.0"
+ }
+ },
"node_modules/binary-extensions": {
"version": "2.3.0",
"resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
@@ -3205,9 +3218,9 @@
}
},
"node_modules/browserslist": {
- "version": "4.24.2",
- "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.24.2.tgz",
- "integrity": "sha512-ZIc+Q62revdMcqC6aChtW4jz3My3klmCO1fEmINZY/8J3EpBg5/A/D0AKmBveUh6pgoeycoMkVMko84tuYS+Gg==",
+ "version": "4.28.8",
+ "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.28.8.tgz",
+ "integrity": "sha512-V2NpofLblG64mfOtSgDhOJESZEGogzDMBv/q+W6oc4LXWP/q75eOXoOaaOu1EOadB9U4Bwx/e0yzbvwKH8zalA==",
"dev": true,
"funding": [
{
@@ -3225,10 +3238,11 @@
],
"license": "MIT",
"dependencies": {
- "caniuse-lite": "^1.0.30001669",
- "electron-to-chromium": "^1.5.41",
- "node-releases": "^2.0.18",
- "update-browserslist-db": "^1.1.1"
+ "baseline-browser-mapping": "^2.11.12",
+ "caniuse-lite": "^1.0.30001809",
+ "electron-to-chromium": "^1.5.402",
+ "node-releases": "^2.0.53",
+ "update-browserslist-db": "^1.3.0"
},
"bin": {
"browserslist": "cli.js"
@@ -3294,9 +3308,9 @@
}
},
"node_modules/caniuse-lite": {
- "version": "1.0.30001753",
- "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001753.tgz",
- "integrity": "sha512-Bj5H35MD/ebaOV4iDLqPEtiliTN29qkGtEHCwawWn4cYm+bPJM2NsaP30vtZcnERClMzp52J4+aw2UNbK4o+zw==",
+ "version": "1.0.30001810",
+ "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001810.tgz",
+ "integrity": "sha512-TITQPUkaz+aVk5GL6NhOdwk1aEaNTSDPsGFWrTuhKGtjTF70jL/Oht2W4c6rXUe5fu7Ie19VIahAXHIIiWWNeg==",
"dev": true,
"funding": [
{
@@ -4100,9 +4114,9 @@
"license": "MIT"
},
"node_modules/electron-to-chromium": {
- "version": "1.5.51",
- "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.51.tgz",
- "integrity": "sha512-kKeWV57KSS8jH4alKt/jKnvHPmJgBxXzGUSbMd4eQF+iOsVPl7bz2KUmu6eo80eMP8wVioTfTyTzdMgM15WXNg==",
+ "version": "1.5.420",
+ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.420.tgz",
+ "integrity": "sha512-2yD6XreGusOfNV+dUcvipJEXc3n/n7fgr7996aszTG+YY5E4mqM4tOq/3uhP129cazL9YHbVWSpc79ePotWtPA==",
"dev": true,
"license": "ISC"
},
@@ -4158,9 +4172,9 @@
}
},
"node_modules/es-object-atoms": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz",
- "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==",
+ "version": "1.1.2",
+ "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz",
+ "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0"
@@ -4382,9 +4396,9 @@
}
},
"node_modules/fflate": {
- "version": "0.7.4",
- "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.4.tgz",
- "integrity": "sha512-5u2V/CDW15QM1XbbgS+0DfPxVB+jUKhWEKuuFuHncbk3tEEqzmoXL+2KyOFuKGqOnmdIy0/davWF1CkuwtibCw==",
+ "version": "0.7.5",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.7.5.tgz",
+ "integrity": "sha512-QieYf//cis6ywHNi5qW1+PXPQ4bC+XVJAtS4AXIML8P76GroEiOxm/oQtn1f02UkJY1+KsXMJcC+R2v/Eg4G3g==",
"license": "MIT"
},
"node_modules/filename-reserved-regex": {
@@ -5237,11 +5251,14 @@
}
},
"node_modules/node-releases": {
- "version": "2.0.18",
- "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.18.tgz",
- "integrity": "sha512-d9VeXT4SJ7ZeOqGX6R5EM022wpL+eWPooLI+5UpWn2jCT1aosUQEhQP214x33Wkwx3JQMvIm+tIoVOdodFS40g==",
+ "version": "2.0.54",
+ "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.54.tgz",
+ "integrity": "sha512-YHs7BmmcsdAI5Ozuf8JZo6PT0mv2GIWC9vMfvUC3dp65M8hn7Ux8CPL+2oBI7juNuj9d0ndhTcznq2ODBps9cQ==",
"dev": true,
- "license": "MIT"
+ "license": "MIT",
+ "engines": {
+ "node": ">=18"
+ }
},
"node_modules/normalize-path": {
"version": "3.0.0",
@@ -5561,9 +5578,9 @@
}
},
"node_modules/postcss-selector-parser": {
- "version": "6.1.2",
- "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.2.tgz",
- "integrity": "sha512-Q8qQfPiZ+THO/3ZrOrO0cJJKfpYCagtMUkXbnEfmgUjwXg6z/WBeOyS9APBBPCTSiDV+s4SwQGu8yFsiMRIudg==",
+ "version": "6.1.4",
+ "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-6.1.4.tgz",
+ "integrity": "sha512-bIoJLOmjCO1S9XdY/DcnR5hJxvrDir1PbGChrzXG3vw0/FOliy/fA3dmdhQ441kah4gKv+TwckGzex6wNS5cnQ==",
"license": "MIT",
"dependencies": {
"cssesc": "^3.0.0",
@@ -5629,12 +5646,13 @@
}
},
"node_modules/qs": {
- "version": "6.15.2",
- "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.2.tgz",
- "integrity": "sha512-Rzq0KEyX/w/tEybncDgdkZrJgVUsUMk3xjh3t5bv3S1HTAtg+uOYt72+ZfwiQwKdysThkTBdL/rTi6HDmX9Ddw==",
+ "version": "6.16.0",
+ "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.0.tgz",
+ "integrity": "sha512-h6fhOIaRrID2CbEY2fqs+7t+UXZo+MLAnU5gRIq85uFtdiUPCdsApMlHhXogKVM4HM2DVbIjGNTTYH2OcmP1vA==",
"license": "BSD-3-Clause",
"dependencies": {
- "side-channel": "^1.1.0"
+ "es-define-property": "^1.0.1",
+ "side-channel": "^1.1.1"
},
"engines": {
"node": ">=0.6"
@@ -6256,14 +6274,14 @@
}
},
"node_modules/side-channel": {
- "version": "1.1.0",
- "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz",
- "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==",
+ "version": "1.1.1",
+ "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz",
+ "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
- "object-inspect": "^1.13.3",
- "side-channel-list": "^1.0.0",
+ "object-inspect": "^1.13.4",
+ "side-channel-list": "^1.0.1",
"side-channel-map": "^1.0.1",
"side-channel-weakmap": "^1.0.2"
},
@@ -6275,13 +6293,13 @@
}
},
"node_modules/side-channel-list": {
- "version": "1.0.0",
- "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz",
- "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==",
+ "version": "1.0.1",
+ "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz",
+ "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==",
"license": "MIT",
"dependencies": {
"es-errors": "^1.3.0",
- "object-inspect": "^1.13.3"
+ "object-inspect": "^1.13.4"
},
"engines": {
"node": ">= 0.4"
@@ -6862,9 +6880,9 @@
}
},
"node_modules/update-browserslist-db": {
- "version": "1.1.1",
- "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz",
- "integrity": "sha512-R8UzCaa9Az+38REPiJ1tXlImTJXlVfgHZsglwBD/k6nj76ctsH1E3q4doGrukiLQd3sGQYu56r5+lo5r94l29A==",
+ "version": "1.3.2",
+ "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.3.2.tgz",
+ "integrity": "sha512-UQ+MSxlhRm1bzjhU+DcuXfjFO1FzNtqhK5+9Yvlp90ItDLk5vT932A0rFu619nf7RVS+Y/VeaUW1jaRDqZ8VJw==",
"dev": true,
"funding": [
{
@@ -6883,7 +6901,7 @@
"license": "MIT",
"dependencies": {
"escalade": "^3.2.0",
- "picocolors": "^1.1.0"
+ "picocolors": "^1.1.1"
},
"bin": {
"update-browserslist-db": "cli.js"