Data-center build gauge

An early-warning gauge for the semiconductor cycle

The whole semiconductor ecosystem rides on data-center buildout, and chips are bought near the end of a build. So the job is to measure how fast data centers are actually being built, watch the leading edge of the pipeline, and turn that into a forward read on AI-chip demand. Two builds of the same idea below — pick the tab.

MVP  The lean gauge

The smallest build that delivers the signal: a monthly read on the data-center build rate as an early warning for AI-chip demand. It stands on published trackers plus a short list of high-signal sources — no project-level database, no scraping fleet.

One question

Is the build rate accelerating, steady, or decelerating — and what does that imply for semiconductors over the next 2–4 quarters?

Scope

US + the top ~10 global markets, quarterly, aggregate GW kept separate by basis. North-America-heavy, transparent markets only.

Method

Reuse Knight Frank / JLL / Sightline / Goldman aggregates, a few utility interconnection queues, and hyperscaler capex. Compute the flow + one lead/lag check.

Output

A one-page dashboard: regime banner, build-rate chart, the indicator ladder, a lead/lag check, a track record (past forecast vs realized), and a one-paragraph read.

The headline the MVP producescharts/regime_gauge.png
Quarterly build-rate momentum gauge with an accelerating regime verdict

The build rate — the leading flow, not the finished stock — with a plain regime verdict on top. This single chart is the MVP's core deliverable.

Why MVP first. This captures roughly 80% of the signal for about 10% of the effort, and — critically — it lets us test whether the build rate actually leads chip sales before investing in the full pipeline. If the lead holds, the Expansive build is justified; if not, we re-anchor cheaply.

MVP  How the lean gauge runs — build spec

A monthly, resumable pipeline of five steps. A few agents, a fixed source list, and one SQLite file that every agent writes to as it works — so you can watch the run fill in live. This panel is the spec; it is detailed enough to hand to another agent and have it build the thing.

📄 Full implementation spec → BUILD_MVP.md (hand this to an agent)

The five steps

1 · GatherFetch each enabled source → store raw snapshot + hash.writes raw_documents
2 · ExtractRun each source's parser → normalized observations in GW/USD, by basis.writes observations
3 · ReconcilePromote one value per metric/region/basis/period (no cross-basis averaging).writes series
4 · ComputeBuild-rate, acceleration, regime, lead/lag vs NVDA → log the call.writes metrics, regime_calls
5 · PublishRender charts + the one-page dashboard from the DB.writes charts, gauge.html

Architecture at a glance

One SQLite file (WAL mode) is the whole data store — inspectable, and WAL lets a progress view read while agents write. Sources live in a registry + one adapter per source; the pipeline never special-cases a source. Agents are thin: read from the DB, write to the DB, update their progress row.

SQLite · WALregistry.yaml one adapter / sourcematplotlib + jinja2 FMP for financialsLLM only for messy PDFs
# repo layout — the source + extraction library
gauge/
  run.py                # orchestrator (python -m gauge.run)
  db/ schema.sql conn.py gauge.db
  sources/
    registry.yaml       # seeds the `sources` table
    base.py             # Extractor ABC + Observation
    capital_intent/  market_tracker/
    power_queue/     equipment/  target/
  extractors/ normalize.py  llm.py
  pipeline/ gather.py extract.py reconcile.py
            compute.py publish.py
  progress/ app.py  progress.html
  templates/ gauge.html.j2
  charts/  raw/

Agents write to the DB as they progress (real-time)

run_steps is the single source of truth for progress. Each step sets its row to running before work and done/error after, with a live rows_written count and a per-source sub-row. A tiny Flask view (or a polled progress.json) renders the step tree, updating every 3s while the run executes.

-- the live progress feed (DDL excerpt)
CREATE TABLE run_steps (
  run_id  TEXT, step TEXT,
  unit    TEXT DEFAULT '-',   -- source_id or '-'
  agent   TEXT,
  status  TEXT,               -- pending|running|done|error
  started_at TEXT, ended_at TEXT,
  rows_written INTEGER DEFAULT 0,
  message TEXT,
  PRIMARY KEY (run_id, step, unit)
);
// GET /api/progress?run_id=2026-06
{
  "run": {"status":"running","current_step":"extract"},
  "steps": [
    {"step":"gather","unit":"-","status":"done","rows_written":12},
    {"step":"gather","unit":"knight_frank","status":"done"},
    {"step":"extract","unit":"ercot_queue","status":"running"},
    {"step":"compute","unit":"-","status":"pending"}
  ]
}

Full schema (9 tables: runs · run_steps · sources · raw_documents · observations · series · metrics · regime_calls · review_queue) is in the spec. The regime_calls table is the seed of the track record — one logged call per run.

Step contracts

StepAgentReadsWritesDone when
1 · gatherGather agentsources (enabled)raw_documents, run_steps[unit=source]every source has a raw doc or error
2 · extractExtraction agentraw_documentsobservations, review_queueall docs parsed or queued
3 · reconcileReconcile fnobservationsseriesrequired metrics populated
4 · computeCompute agentseriesmetrics, regime_callsregime + lead/lag present
5 · publishPublish fnmetrics, series, regime_callscharts/*.png, gauge.htmlfiles written

Resumable: a step is skipped if its run_steps row is done for the run. --only <source> re-runs one source; --resume <run_id> continues a crashed run.

The source & extraction library

Every source implements one interface, so the extraction agent never branches per source. Two kinds behind it: structured (FMP API / CSV / HTML table → deterministic) and document (PDF / press release → schema-locked LLM extraction that must keep the source snippet). Add a source = add a registry row + a parser class.

# sources/base.py — the one interface
@dataclass
class Observation:
    metric: str      # operational_capacity | capex | ...
    region: str
    basis: str       # it_load | grid_demand | capex_usd ...
    period: str      # 2025 | 2025Q4 | 2026-05
    value: float; unit: str       # GW | USD_bn
    confidence: str  # verified|strongly|weakly
    snippet: str     # exact source text (audit)

class Extractor:
    source_id: str; family: str
    def fetch(self) -> RawDoc: ...
    def parse(self, raw) -> list[Observation]: ...
SourceFamilyBasisLeadKind
MSFT / AMZN / GOOGL / META / ORCL capexcapital_intentcapex $~10 qFMP
Knight Frankmarket_trackerIT loadcoincidentdoc/LLM
JLL · Cushmanmarket_trackerIT load~2 qdoc/LLM
Sightline Climatemarket_trackerIT load~6 qdoc/LLM
Goldman US activationsmarket_trackerIT load~4 qdoc/LLM
ERCOT · PJM queuespower_queuegrid~11 qPDF/XLSX
Vertiv book-to-billequipmentgrid~4 qtranscript/LLM
NVDA data-center revenuetargetrevenue $laggingFMP

Compute definitions & run

No-ambiguity definitions

build_rate_gw_q = quarterly net additions of the promoted World-ex-CRO IT-load series. regime = accelerating if YoY of the 4-q trailing avg > +10%, decelerating if < −10%, else steady. lead/lag = argmax cross-correlation of build rate vs NVDA DC revenue; < 8 overlapping quarters ⇒ confidence low. Thresholds live in config.py.

# run it
python -m gauge.run                # current month
python -m gauge.run --month 2026-06
python -m gauge.run --only ercot_queue
python -m gauge.run --resume 2026-06
python -m gauge.progress           # live view :8090

# build order: schema → conn → base+registry
# → 2 FMP extractors (nvda, msft) → gather
# → extract → reconcile → compute → publish
# → progress view → remaining extractors
Guardrails

Bases never averaged together. Only same-publisher / same-basis pairs count as forecast revisions. Every figure carries a source + confidence + snippet; the regime call carries a confidence tag. Idempotent per month; series upserts.

Deliberately skips

No per-project database, no permit scraping, no satellite, no cohort stage-tracking, no global long-tail. Those are the Expansive build — added only once the lead is proven.

MVP  The one-page gauge

Everything the MVP ships fits on one screen: the verdict, the chart behind it, which signals it watched, and whether the lead holds. Example run, June 6 2026.

ACCELERATING
Regime this run
~4.2 GW/qtr
Build rate now (+43% YoY)
Strengthening
Implied AI-chip demand into 2027
Medium
Confidence (short AI-era history)
Build-rate momentumcharts/regime_gauge.png
Quarterly build-rate gauge
Which signals it watchedcharts/indicator_ladder.png
Leading-indicator ladder
Does the build lead chips?charts/leadlag.png
Lead/lag cross-correlation
Track record — past forecasts vs what landedcharts/realized_vs_forecast.png
Past forecast vs realized capacity

The 2025 forecast (55.6 GW) came in low — actual was 60.0 GW. Forecasts have run ~5–8% below realized, so the gauge tags confidence medium and treats the current call as, if anything, conservative. This is what earns the regime call the right to be believed.

This run's read. Net additions are running ~4.2 GW/quarter and still climbing; every leading layer (power-queue requests, hyperscaler capex, construction starts) is rising in step. The build rate has historically led NVDA data-center revenue by ~5 quarters, so the signal points to continued AI-chip demand strength into 2027. Confidence is medium — the AI-era history is short, so treat the lead as directional, not precise.

Expansive  The full leading-indicator engine

The same idea, taken all the way: every layer of the indicator stack, global coverage, down to project-level stage tracking — with backtested lead/lag per indicator, a GW→dollar bridge, scenario forecasts, and a defensible audit trail. This turns the gauge from a read into a forecasting engine with attribution.

Power layer, in full

Interconnection queues across every major ISO/utility (ERCOT, PJM, AESO, Dominion, Georgia Power…), energization rates, and request withdrawals — the earliest and hardest-to-fake signal.

Physical observation

Permit filings, satellite-detected construction starts, and grid long-lead-equipment backlogs (transformers, turbines) — what's actually happening on the ground.

Project-level cohort tracking

A stage_transitions table records each project's stage at every snapshot → real conversion rates, dwell-time, and RFS schedule adherence.

GW → $ nowcast

A silicon-intensity model (HBM / CoWoS / ASP per MW) converts the physical build rate into an AI-chip-dollar demand nowcast.

Triangulation, track record + attribution

Per-indicator backtested lead/lag, a forecast-accuracy scorecard (incl. the gauge grading its own past calls), and a clear answer each run to "which layer flashed first?"

Scenarios + scope

Low / base / high build-rate fans, global coverage including long-tail markets, and human-review queues for ambiguous records.

The full indicator stackcharts/indicator_ladder.png
Leading-indicator ladder
GW → chip-$ bridgecharts/nowcast_bridge.png
Silicon-intensity bridge

Why expand. The aggregate trackers the MVP relies on update slowly and are pre-digested. Project-level tracking catches a slowdown earlier — as stalls, slipping RFS dates, and queue withdrawals — and it can attribute the move to a layer. The cost is real: a continuous multi-agent pipeline and a data layer that only gets accurate as it ages.

Expansive  How the full engine runs

A continuous, resumable, Codex-driven multi-agent pipeline writing to a database. Charts, the API, and the regime call regenerate on every run; the project/event/stage layer accumulates over time.

1 · Gather10+ source families, each with its own agent and snapshot store.per-source agents
2 · StageExtract observations from source-specific agents into staging tables.extract agents
3 · CanonicalResolve orgs, sites, projects, phases, aliases — dedupe campuses.matching agent
4 · ResolveEvents, stage_transitions, dwell-time, slippage, capacity reconcile.resolver + confidence
5 · ForecastBuild-rate + GW→$ nowcast + scenarios + lead/lag backtest.forecast agent
6 · PublishChart pack + API + regime call + QA/review queues.publish + writer

Source families

IR / SEC + hyperscaler capex Permits Utility / power queues Environmental / air Contractor pages Careers signals Network snapshots Spatial / satellite Grid long-lead-equipment backlog Electrical book-to-bill (Vertiv, Eaton) Advanced-packaging capacity (HBM / CoWoS) WSTS billings + NVDA DC revenue
The key new data asset

A stage_transitions table — every project's stage at each snapshot. This is what makes longitudinal forecast-vs-actual possible: of the capacity under construction last quarter, how much actually hit its forecast RFS date?

The hard parts (named honestly)

Cohort accumulation takes time to become accurate; satellite construction detection is noisy; the silicon-intensity model needs maintenance; and the lead/lag has a short AI-era history → confidence bands stay wide and are stated, not hidden.

Cadence: continuous / weekly, fully resumable so a run can be killed and resumed without losing the data store. Cost scales with the source-family fan-out; the QA queues keep a human in the loop where confidence is low.

Expansive  The full chart pack + forecasting engine

The complete output: the levels and build rate, the forecast-vs-actual evidence, the leading-indicator panel with attribution, and the GW→$ nowcast. Example run, June 6 2026.

ACCELERATING
Regime this run
~4.2 GW/qtr
Build rate (+43% YoY)
~5 qtrs
Build rate leads chip revenue by
Rising fast
GW→$ nowcast (intensity effect)
Power queues
Which layer flashed first
24 mkts · 100 ops
Coverage
Build-rate momentumcharts/regime_gauge.png
Build-rate gauge
Capacity over timecharts/trend.png
Capacity trend
Build pipeline by regioncharts/pipeline.png
Pipeline by region
Forecast vs actual (by vintage)charts/forecast_revisions.png
Forecast revisions
Where capacity leaks outcharts/build_funnel.png
Build funnel

Track record — past forecast vs realized

The scoreboard. Third-party forecasts vs what landed, and — the gold standard — the gauge grading its own past calls. Both feed the confidence bands on every forward number.

Third-party: forecast vs realizedcharts/realized_vs_forecast.png
Third-party forecast vs realized
The gauge grading itselfcharts/forecast_track_record.png
Gauge self-scorecard: past nowcast vs realized
ForecasterTargetPredictedRealized / currentMiss
Knight Frank (2025 vintage)2025 live IT GW55.660.0−7.3% (under)
Knight Frank (2025 vintage)2026 live IT GW66.573.6*−9.7% (under)
Goldman (scheduled)US 2025 activations8.5 realizedfills in as years realize
This gauge (self-scorecard)build-rate regime7 / 8 correctMAE ~0.3 GW/qtraccumulates each run

*current estimate, not yet fully realized. The project-level version of this is RFS schedule adherence (forecast RFS date vs actual) — see the spec tiles below.

Does the build lead chips?charts/leadlag.png
Lead/lag
GW → chip-$ nowcastcharts/nowcast_bridge.png
Nowcast bridge
The leading-indicator panelcharts/indicator_ladder.png
Indicator ladder

Per-indicator lead/lag & current reading

IndicatorTypical leadReading this runConfidence
Power-interconnection requests~11 qrising sharplyLow–med
Hyperscaler capex guidance~10 qrisingHigh
Construction starts~5 qrisingMed
Electrical-equipment book-to-bill~4 q> 1, risingMed
GW under construction~3 qrisingHigh
HBM / CoWoS bookings~1 qsold outMed

Charts still on the spec (need the stage layer)

Cohort conversion funnel

Per-cohort stage-to-stage conversion rates over time — needs the stage_transitions history.

Dwell-time by stage

How long capacity sits in each stage, and whether that's lengthening (an early stall signal).

RFS schedule adherence

Forecast RFS date vs actual RFS date — true schedule slippage, the cleanest project-level forecast-vs-actual.

This run's read. Every layer of the stack is rising in step, and the earliest layer — power-interconnection requests — flashed first, which is the most leading signal we have. The build rate leads NVDA data-center revenue by ~5 quarters, and the GW→$ bridge says chip dollars rise faster than GW because intensity is climbing. Net: AI-chip demand strength looks well-supported into 2027, with the usual caveat that scheduled activations carry delay/cancellation risk concentrated in the steep 2027 slate.