#!/usr/bin/env bash # post-upgrade.sh [step…] — data repair after the 2026-09-12 claim-first upgrade, run from the laptop against the data node. # steps (default: all, in order): # baseline dci quality + dci snapshot (flags + snapshot BEFORE repairs) # repair scripts/quality-apply.ts --hide-false-positives --fix-slugs --link-campuses # reprocess dci reprocess --stale (offline, from archived bodies; new parser versions) # unbacked scripts/quality-apply.ts --null-unbacked-mw (after reprocess: figures with no site-scoped current claim) # refresh dci refresh-stats + dci rank + dci quality + dci snapshot # audit largest values (section 120 of the brief) printed for review # Everything is idempotent; projects are hidden, never deleted; the pre-upgrade pg_dump is in /srv/dci/backups/pg. set -euo pipefail source "$(dirname "${BASH_SOURCE[0]}")/lib.sh" steps=("$@"); [[ ${#steps[@]} -eq 0 ]] && steps=(baseline repair reprocess unbacked refresh audit) has() { local s; for s in "${steps[@]}"; do [[ $s == "$1" ]] && return 0; done; return 1; } # `cli` service runs the worker image; scripts/ is not baked into the image → bind-mount it CLI="$(compose_prefix data) run --rm -T" CLI_SCRIPTS="$(compose_prefix data) run --rm -T -v $REMOTE_DIR/scripts:/app/scripts -v $REMOTE_DIR/packages/core/src:/app/packages/core/src cli node /app/node_modules/tsx/dist/cli.mjs /app/scripts" dci() { remote data "$CLI cli cli $*"; } script() { remote data "$CLI_SCRIPTS/$*"; } psqlq() { remote data "docker exec -i dci-postgres-1 psql -U dci -d dci -Atc \"$1\""; } if has baseline; then log "baseline: quality sweep + snapshot" dci quality | tail -20 dci snapshot | tail -8 fi if has repair; then log "repair: hide vetoed projects, fix slugs, link campuses" script quality-apply.ts --hide-false-positives --hide-unconfirmed --fix-slugs --link-campuses | tail -40 fi if has reprocess; then log "reprocess: every connector, stale documents only (offline)" ids=$(psqlq "select string_agg(id, ' ' order by id) from connectors where enabled") for id in $ids; do remote data "$CLI cli cli reprocess $id --stale 2>&1 | grep -E 'reprocess → |selected' | tail -2" || true done fi if has unbacked; then log "unbacked: null project figures no site-scoped current claim backs" script quality-apply.ts --null-unbacked-mw | tail -20 fi if has refresh; then log "refresh: stats, rankings, quality, snapshot" dci refresh-stats | tail -1 dci rank | tail -1 dci quality | tail -20 dci snapshot | tail -8 fi if has audit; then log "audit: largest values" psqlq "select 'FAC-OPER' k, f.name, f.it_capacity_mw, f.total_power_mw, f.capacity_scope, f.capacity_semantics, f.record_scope from facilities f where f.merged_into is null and f.status in ('operational','partially_operational','expansion') order by coalesce(f.it_capacity_mw, f.total_power_mw) desc nulls last limit 12" psqlq "select 'FAC-CONSTR' k, f.name, coalesce(f.planned_power_mw, f.it_capacity_mw, f.total_power_mw) mw, f.capacity_scope from facilities f where f.merged_into is null and f.status = 'under_construction' order by 3 desc nulls last limit 8" psqlq "select 'PRJ-MW' k, p.name, p.planned_mw, p.capacity_scope, p.project_class, p.evidence_level, left(p.source_url, 80) from projects p where p.merged_into is null and not p.hidden order by p.planned_mw desc nulls last limit 12" psqlq "select 'PRJ-INV' k, p.name, p.investment_usd/1e9 inv_b, p.investment_scope, p.project_class from projects p where p.merged_into is null and not p.hidden order by p.investment_usd desc nulls last limit 8" psqlq "select 'OP-PIPE' k, o.name, o.stats->>'plannedMw' planned, o.stats->>'constructionMw' construction, o.stats->>'projectPlannedMw' project_mw from operators o order by coalesce((o.stats->>'projectPlannedMw')::float,0) + coalesce((o.stats->>'plannedMw')::float,0) desc limit 8" psqlq "select 'METRO-PIPE' k, m.name, m.stats->>'plannedMw' planned, m.stats->>'constructionMw' construction, m.stats->>'projectPlannedMw' project_mw from metros m order by coalesce((m.stats->>'projectPlannedMw')::float,0) + coalesce((m.stats->>'plannedMw')::float,0) desc limit 8" psqlq "select 'TOTALS' k, (select count(*) from facilities where merged_into is null), (select count(*) from projects where merged_into is null and not hidden), (select count(*) from projects where hidden), (select count(*) from claims), (select count(*) from claims where status='unscoped'), (select count(*) from quality_flags where status='open')" fi log "post-upgrade done"