Files
worldmonitor/Dockerfile.relay
Elie Habib b8924eb90f feat(energy): Ember monthly electricity seed (V5-6a) (#2815)
* feat(energy): Ember monthly electricity seed — V5-6a

New seed-ember-electricity.mjs writes energy:ember:v1:<ISO2> and
energy:ember:v1:_all from Ember Climate's monthly generation CSV (CC BY 4.0).
Daily cron at 08:00 UTC, TTL 72h (3x interval), >=60 country coverage guard.

Registers in api/health.js, api/seed-health.js, cache-keys.ts, and
ais-relay.cjs. Dockerfile.relay COPY added.

🤖 Generated with Claude Sonnet 4.6 via Claude Code (https://claude.ai/code) + Compound Engineering v2.49.0

Co-Authored-By: Claude Sonnet 4.6 (200K context) <noreply@anthropic.com>

* fix(energy): add _country-resolver.mjs to Dockerfile.relay; correct Ember intervalMin (V5-6a)

Two bugs in the Ember seed PR:
1. Dockerfile.relay was missing COPY for _country-resolver.mjs, which
   seed-ember-electricity.mjs imports. Would have crashed with
   ERR_MODULE_NOT_FOUND on first run in production.
2. api/seed-health.js had intervalMin:720 (12h) for a daily (24h) cron.
   With stale threshold = intervalMin*2, this gave only 24h grace --
   the seed would flap stale during the CSV download window.
   Corrected to intervalMin:1440 so stale threshold = 48h (2x interval).

* fix(energy): wire energy:ember:v1:_all into bootstrap hydration (V5-6a)

Greptile P1: api/bootstrap.js was missing the emberElectricity slow-key
entry, violating the AGENTS.md requirement that new data sources be
registered for bootstrap hydration.

energy:ember:v1:_all is a ~60-country bulk map (monthly cadence) -
added to SLOW_KEYS consistent with faoFoodPriceIndex and other
monthly-release bulk keys.

Also updates server/_shared/cache-keys.ts BOOTSTRAP_CACHE_KEYS and
BOOTSTRAP_TIERS to keep the bootstrap test coverage green (bootstrap
test validates that SLOW_KEYS and BOOTSTRAP_TIERS are in sync).

* fix(energy): 3 review fixes for Ember seed (V5-6a)

1. Ember URL: updated to correct current download URL (old path
   returned HTTP 404, seeder could never run).
2. Count-drop guard after failure: failure path now preserves the
   previous recordCount in seed-meta instead of writing 0, so the
   75% drop guard stays active after a failed run.
3. api/seed-health.js: status:error now marks seed as stale/error
   immediately instead of only checking age; prevents /api/seed-health
   showing ok for 48h while the seeder is failing.

* fix(energy): correct Ember CSV column names + fix skipped-path meta (V5-6a)

1. CSV schema: parser was using country_code/series/unit/value/date
   but the real Ember CSV headers are "ISO 3 code"/"Variable"/"Unit"/
   "Value"/"Date". Added COLS constants and updated all row field
   accesses. The schema sentinel (hasFossil check) was always firing
   because r.series was always undefined, causing every seeder run to
   abort. Updated test fixtures to use real column names.
2. Skipped-path meta: lock.skipped branch now reads existing meta and
   preserves recordCount and status while refreshing fetchedAt.
   Previously writing recordCount:0 disabled the count-drop guard after
   any skipped run and made health endpoints see false-ok with zero count.

* fix(energy): remove skipped-path meta write + revert premature bootstrap (V5-6a)

1. lock.skipped: removed seed-meta write from the skipped path. The
   running instance writes correct meta on completion; refreshing
   fetchedAt on skip masked relay/lock failures from health endpoints.
2. Bootstrap: removed emberElectricity from BOOTSTRAP_CACHE_KEYS and
   BOOTSTRAP_TIERS — no consumer exists in src/ yet. Per energyv5.md,
   bootstrap registration is deferred to PR7 when consumers land.

* fix(energy): split ember pipeline writes; fix health.js recordCount lookup

- api/health.js: add recordCount fallback in both seed-meta count reads so
  the Ember domain shows correct record count instead of always 1
- scripts/seed-ember-electricity.mjs: split single pipeline into Phase A
  (per-country + _all data) and Phase B (seed-meta only after Phase A
  succeeds) to prevent preservePreviousSnapshot reading a partial _all key

* fix(energy): split ember pipeline writes; align SEED_ERROR in health.js; add tests

* fix(energy): atomic rollback on partial pipeline failure; seedError priority in health cascade

* fix(energy): DEL obsolete per-country keys on publish, rollback, and restore

* fix(energy): MULTI/EXEC atomic pipeline; null recordCount on read-miss; dataWritten guard

---------

Co-authored-by: Claude Sonnet 4.6 (200K context) <noreply@anthropic.com>
2026-04-08 12:25:54 +04:00

42 lines
1.7 KiB
Docker

# =============================================================================
# AIS Relay Sidecar
# =============================================================================
# Runs scripts/ais-relay.cjs as a standalone container.
# Dependencies: ws (WebSocket), telegram (OSINT polling), plus others in
# scripts/package.json (fast-xml-parser, @anthropic-ai/sdk, etc.)
# Set AISSTREAM_API_KEY in docker-compose.yml or Railway env.
# =============================================================================
FROM node:22-alpine
# curl required by OREF polling (Node.js JA3 fingerprint blocked by Akamai; curl passes)
RUN apk add --no-cache curl
WORKDIR /app
# Install scripts/ runtime dependencies (telegram, ws, fast-xml-parser, etc.)
COPY scripts/package.json scripts/package-lock.json ./scripts/
RUN npm ci --prefix scripts --omit=dev
# Relay script and shared helpers
COPY scripts/ais-relay.cjs ./scripts/ais-relay.cjs
COPY scripts/_proxy-utils.cjs ./scripts/_proxy-utils.cjs
COPY scripts/_seed-utils.mjs ./scripts/_seed-utils.mjs
COPY scripts/_country-resolver.mjs ./scripts/_country-resolver.mjs
COPY scripts/seed-climate-news.mjs ./scripts/seed-climate-news.mjs
COPY scripts/seed-chokepoint-flows.mjs ./scripts/seed-chokepoint-flows.mjs
COPY scripts/seed-ember-electricity.mjs ./scripts/seed-ember-electricity.mjs
# Shared helper required by the relay (rss-allowed-domains.cjs)
COPY shared/ ./shared/
# Data files required by the relay (telegram-channels.json, etc.)
COPY data/ ./data/
EXPOSE 3004
HEALTHCHECK --interval=30s --timeout=5s --start-period=10s --retries=3 \
CMD wget -qO- http://localhost:3004/health || exit 1
CMD ["node", "scripts/ais-relay.cjs"]