← all branches

feat/powerradar

needs attentionviewing older commit
230784f · fullPR #283reviewed 2026-07-10 00:15 UTC2H · 11M · 14L · 7I
The branch
Purpose
Port 31 legacy PowerRadar (Centrica/Smarter) energy-meter devices from the old batu-monorepo system into the v2 metrics engine (Tinybird), enabling CFE bill cross-validation and multi-site dashboards for this brand.
Goal
CSV-first intake worker serving the PowerRadar hourly portfolio CSV format (latin-1, DST-aware, kW→W) plus the D3 unblock (powerradarChannel passthrough), CDK stack, and 91/91 parity day validation.
Sub-goals
  • SG-1: CSV worker + parser + 37 unit tests (DST, latin-1, channel mapping)
  • SG-2: CDK stack (intake bucket + Lambda) + coordinator grant + synth clean
  • SG-3: Preview data plane + powerradarChannel D3 unblock + real CSV characterization
  • SG-4: Live e2e intake → Tinybird → 91/91 parity days (60 CDMX exact + 31 Tijuana ≤0.0112%)
  • SG-N: 8 framework learnings → scope.md + integrations CLAUDE.md Tier-3 section
  • SG-6: Rebase onto §9 multi-granularity base (groupChannelsByGranularity + widened Granularity union)
  • SG-7: §9 handler conformance — grouped fetch shape, resilient non-1h skips, zero-point drops, +5 tests (43 total)
The changes (whole branch)
What
New CSV-first Lambda intake worker for PowerRadar (Centrica), new S3 intake bucket CDK stack, powerradarChannel passthrough in asset-provisioning cross-domain shell, 11 new integration manifests for future brand ports, legacy registry/secrets migration scripts, metric domain query updates, seed catalog updates.
Why
PowerRadar is a portfolio-CSV vendor with 31 legacy devices in batu-monorepo. This is one of 12 planned brand ports to the v2 metrics engine.
Areas
services/metrics/integrations/powerradar/+10130scripts/metrics/+12850infra/cdk/src/stacks/services/metrics/integrations/powerradar/+2220packages/integration-manifests/src/+6990domains/cross-domain/src/+458domains/metrics/src/+36936packages/database/src/+17814infra/cdk/src/app/+243services/metrics/engine/src/+8715
Blast
77 files, +6645/−201. New CDK stack isolated (no existing stack changes). Cross-domain shell change is additive (optional field, no break). 11 new manifests are pure data. Metric domain changes are schema/query updates.
new-infra s3-intake cross-domain-shell dst-timezone 12-port-campaign
ci· gh pr checks not accessible via PATcoderabbit· no .coderabbit.yaml in repocorrectness· 2 low findingssecurity· 3 medium, 3 lowconventions· 2 high, 2 mediumtests· 6 medium, 4 lowimprovement· 3 low suggestions

Findings · 34

correctness3

low

NaN Date when window.from/to is non-ISO — window filtering silently disabled

services/metrics/integrations/powerradar/src/handlers/metrics.lambda.ts:127

low

Unsafe sources as MetricChannelSpec[] cast — missing granularity → spurious unsupported-granularity skip

services/metrics/integrations/powerradar/src/handlers/metrics.lambda.ts:69

info

Fall-back disambiguation occurrence counter semantics correct — third occurrence correctly dropped

security7

medium

csvS3Key not validated against a prefix — unscoped s3:GetObject covers whole intake bucket

services/metrics/integrations/powerradar/src/handlers/metrics.lambda.ts:101

A manipulated SFN start input could read any object any tenant uploaded. Guard: csvS3Key.startsWith('powerradar/uploads/').

medium

CORS allowedHeaders wildcard includes Authorization — scope to presigned-PUT required headers

infra/cdk/src/stacks/services/metrics/integrations/powerradar/lambda.stack.ts:100

Use content-type, x-amz-date, x-amz-content-sha256, authorization instead of ['*'].

medium

powerradarChannel absent from ProvisionAssetStreamCreateSchema — silently dropped or unvalidated JSONB write

packages/api/src/schemas/metrics-wizards.schemas.ts

Field either never reaches the shell (breaking D3 apply) or persists without validation. Add to Zod schema with z.string().min(1).max(200).

low

No file-size guard before S3 download — large CSV can OOM 512 MB Lambda

services/metrics/integrations/powerradar/src/handlers/metrics.lambda.ts:133

low

Log injection via csvS3Key and channel names in JSON logs

services/metrics/integrations/powerradar/src/handlers/metrics.lambda.ts:94

low

No bucket versioning — overwritten CSV is unrecoverable

infra/cdk/src/stacks/services/metrics/integrations/powerradar/lambda.stack.ts:92

info

CORS GET on write-only intake bucket — remove to tighten surface

infra/cdk/src/stacks/services/metrics/integrations/powerradar/lambda.stack.ts:95

conventions9

high

ProvisionAssetStreamCreate: flat vendor fields will proliferate across 12 ports — use sourceConfig passthrough

domains/cross-domain/src/asset-provisioning.shells.ts:72

Single sourceConfig?: Record<string,unknown> with manifest-level validation is the canonical pattern. Each of the 12 planned ports adding a field requires a cross-cutting signature change.

high

manifest required:'powerradarChannel' not enforced at provision — silent gap between TS (optional) and manifest (required)

domains/cross-domain/src/asset-provisioning.shells.ts:883

Source inserted without powerradarChannel passes provision and fails silently at collection time with channel-not-configured.

medium

Outer try/catch tags all exceptions as UpstreamUnavailable (502, retryable) — misclassifies logic errors

services/metrics/integrations/powerradar/src/handlers/metrics.lambda.ts:218

Pre-existing eGauge pattern but semantically wrong for pure-function failures.

medium

INFRA_DESIGN.md missing — INTEGRATION_STANDARDS §8 checklist requires one

services/metrics/integrations/powerradar/

Growatt and eGauge both have INFRA_DESIGN.md. Architecture is documented only in Lambda handler JSDoc and CLAUDE.md.

low

INTEGRATION_STANDARDS §8 checklist has stale descriptor.ts requirements

services/metrics/integrations/INTEGRATION_STANDARDS.md:224

low

diagnostics spread of undefined — prefer initialized empty object

services/metrics/integrations/powerradar/src/handlers/metrics.lambda.ts:215

info

§9 conformance correct — groupChannelsByGranularity, resilient skips, zero-point drops

info

Manifest registration, CDK dependency wiring, and seed entries are correct

info

Error discriminated unions use canonical InvocationError tags

tests11

medium

Scalar params fallback (direct-invoke) has no test

services/metrics/integrations/powerradar/src/__tests__/metrics-handler.test.ts

medium

Empty sources:[] → TranslationFailed not tested

services/metrics/integrations/powerradar/src/__tests__/metrics-handler.test.ts

medium

M/D/YYYY re-export: only cell-level test, no full-CSV no-parseable-rows test

services/metrics/integrations/powerradar/src/__tests__/csv.test.ts

medium

CRLF line-ending variant not tested

services/metrics/integrations/powerradar/src/__tests__/csv.test.ts

medium

T-separator ISO timestamp accepted by TS_RE but not tested

services/metrics/integrations/powerradar/src/__tests__/csv.test.ts

medium

(kWh) suffix normalization: kWh ≠ kW — ×1000 scale would be wrong for energy cells

services/metrics/integrations/powerradar/src/__tests__/rows-to-batu.test.ts

low

Lambda handler export (ArnEnvVarMissing) untested

services/metrics/integrations/powerradar/src/__tests__/metrics-handler.test.ts

low

Multi-channel migration plan (DuplicateChannel risk) not tested

scripts/metrics/__tests__/legacy-registry-core.test.ts:182

low

Triplet local-timestamp correct-drop behavior not asserted

services/metrics/integrations/powerradar/src/__tests__/rows-to-batu.test.ts

low

toUtc exclusive boundary not isolated — bundled in rowsOutsideWindow===2 assertion

services/metrics/integrations/powerradar/src/__tests__/rows-to-batu.test.ts:121

info

America/Phoenix timezone recognized in header but not exercised end-to-end in rowsToBatu

improvement4

low

splitCsvLine duplicated from helioscope

services/metrics/integrations/powerradar/src/engine/csv.ts:63

low

DEFAULT_GRANULARITY = NATIVE_GRANULARITY alias is redundant

services/metrics/integrations/powerradar/src/handlers/metrics.lambda.ts:60

low

resolveChannels scalar fallback is dead weight for a new integration

services/metrics/integrations/powerradar/src/handlers/metrics.lambda.ts:66

info

diagnostics overwritten per group — defensive accumulation for future multi-granularity ports

History · 15 commits

  1. f34e4ceneeds attentionincremental0H · 1M · 3L2026-07-23 00:32
  2. 1bc8aedneeds attentionincremental0H · 2M · 3L2026-07-22 21:34
  3. 31e63bbneeds attentionincremental0H · 2M · 5L2026-07-22 20:53
  4. 482cb88safeincremental0H · 0M · 1L2026-07-22 17:56
  5. 9a73b79safeincremental0H · 0M · 0L2026-07-22 17:34
  6. 87f8298needs attentionincremental0H · 1M · 3L2026-07-22 16:59
  7. 1e80096safeincremental0H · 0M · 1L2026-07-22 16:42
  8. 6ed65b4safeincremental0H · 1M · 3L2026-07-22 00:00
  9. 74a1131needs attentionfull0H · 7M · 12L2026-07-21 19:06
  10. 563252bneeds attentionincremental4H · 9M · 8L2026-07-21 18:32
  11. beced58needs attentionincremental0H · 3M · 3L2026-07-21 01:13
  12. 7fa8684needs attentionincremental4H · 9M · 7L2026-07-20 22:56
  13. 230784fneeds attentionfull2H · 11M · 14L2026-07-10 00:15current
  14. b876b54needs attentionincremental2H · 2M · 5L2026-07-08 04:44
  15. ec4847fneeds attentionfull2H · 11M · 8L2026-07-08 03:32