feat/powerradar
safeviewing older commit482cb88 · incrementalPR #327reviewed 2026-07-22 17:56 UTC0H · 0M · 1L · 7I- Purpose
- Add automatic API-based PowerRadar (Centrica / Panoramic Power) metrics integration using the portal's internal /eigw API with headless OAuth password-grant auth — replacing the manual-CSV path with native 5/15/60-min pull collection on the existing Site Collection SFN.
- Goal
- powerradar-api worker: headless auth + chart-data pull + §9 multi-granularity handler + CDK stack, leaving the CSV worker as deep-history fallback.
- Sub-goals
- SG-1: Auth + transport engine + live probe (OAuth broker password-grant, no MFA)
- SG-2: Manifest + catalog + provider registration + ARN routing
- SG-3: Pure chart-to-batu translation + 13 unit tests
- SG-4: Metrics handler (§9 one-fetch-per-grain, resilient skips) + 18 tests
- SG-5: CDK stack + paths + main.ts wiring + coordinator invoke-grant
- SG-6: Live e2e — 191 demand points at 15m, solar skipped correctly on load-only device
- SG-7: CLAUDE.md PowerRadar-API section (framework fold-back)
- What
- This incremental commit (482cb887) fixes a fragile infra pattern: CsvExportStorageStack previously published an SSM param (csv-export/bucket) that CsvExportLambdaStack read at deploy time via valueForStringParameter. The param was prone to out-of-band deletion while CFN still tracked it, causing hard deploy failures. Fix: both stacks now independently call s3BucketName() with identical inputs, deriving the name deterministically. SSM param removed from Storage; addDependency kept for physical-existence ordering.
- Why
- The SSM param was deleted in AWS while CFN still owned it as a stack resource; no-diff Storage redeployments never recreated it; Lambda stack deploys then hard-failed at 'Unable to fetch parameters'. Deterministic derivation eliminates the cross-stack SSM read entirely.
- Areas
- services/metrics/integrations/powerradar-api+1542−0infra/cdk+213−135packages/integration-manifests+109−0packages/database+17−1domains/core+4−3services/metrics/integrations/CLAUDE.md+51−0
- Blast
- 29 files, +2312/-453 across the branch. Core: new powerradar-api worker package (auth engine, translation, handler, tests) + manifest/catalog/DB registration + CDK Lambda stack. This commit: 3 infra/cdk files only.
Findings · 7
correctness3
accounts[targetEnv].slice(-4) on empty string produces empty suffix — both stacks consistently wrong but never mismatched
infra/cdk/src/stacks/services/utility/bills/csv-export/lambda.stack.ts:73
If a BatuConfig accidentally carries an empty account ID for an env, accountSuffix is '' and s3BucketName() produces a malformed bucket name. Storage would fail at creation time (CFN rejects the name), so Lambda would never deploy either. Both stacks use identical expressions so they remain in sync even in the degenerate case. Low because it only manifests on misconfiguration, not in normal operation.
First post-merge deploy will emit a CFN Delete on the old BucketParam SSM resource — expected, not an error
infra/cdk/src/stacks/services/utility/bills/csv-export/storage.stack.ts:75
The StringParameter (logical ID BucketParam) was a managed CFN resource in CsvExportStorageStack. Removing it from CDK produces a changeset that deletes the AWS::SSM::Parameter. This is intended and safe — the Lambda stack no longer reads it — but operators should expect to see a Delete action in cdk diff output on the first deploy.
addDependency bypassed by --exclusively in preview phases — comment is accurate but the guarantee is procedural, not structural
infra/cdk/src/app/main.ts:304
The comment correctly acknowledges this. If the phase ordering in preview-provision.yml ever changes, the physical-existence guarantee disappears silently — there is no synth-time enforcement.
tests1
No CDK synth test guards the naming invariant between the two stacks
infra/cdk/src/stacks/services/utility/bills/csv-export/lambda.stack.ts:74
The fix rests on both stacks independently computing the same s3BucketName(). A future refactor that changes service name or slice length in only one stack would produce a silently mismatched bucket name at runtime. A minimal aws-cdk-lib/assertions Template test asserting both outputs match would structurally prevent this. Absent from current practice — consistent with the repo's no-CDK-tests norm — but the introduced two-party naming invariant is exactly the kind of thing a synth test cheaply protects.
improvement3
Two independent NameParts locals with different names (nameParts vs parts) — silent divergence risk
infra/cdk/src/stacks/services/utility/bills/csv-export/lambda.stack.ts:53
Lambda stack uses `nameParts`, Storage uses `parts`, both with identical field values. No compile-time coupling between them. A shared helper (e.g. csvExportNameParts(cfg, env)) would make the single-source explicit and eliminate the copy-paste surface.
Storage stack NOTE comment should cross-reference lambda.stack.ts so a reader doesn't re-add the SSM param
infra/cdk/src/stacks/services/utility/bills/csv-export/storage.stack.ts:75
The comment explains the removal well but doesn't say where the bucket name is now consumed. A reader maintaining Storage in isolation might re-add the param as a convenience. Adding '— see lambda.stack.ts' would make the coupling explicit.
--exclusively in comment needs context about preview-provision phase ordering
infra/cdk/src/app/main.ts:302
A reader unfamiliar with the preview deploy workflow won't know what 'an earlier phase' means. Could expand to name that preview-provision.yml deploys CsvExportStorage in Phase 1 before CsvExportLambda in Phase 2, making the comment self-contained.
History · 15 commits
- f34e4ceneeds attentionincremental0H · 1M · 3L2026-07-23 00:32
- 1bc8aedneeds attentionincremental0H · 2M · 3L2026-07-22 21:34
- 31e63bbneeds attentionincremental0H · 2M · 5L2026-07-22 20:53
- 482cb88safeincremental0H · 0M · 1L2026-07-22 17:56current
- 9a73b79safeincremental0H · 0M · 0L2026-07-22 17:34
- 87f8298needs attentionincremental0H · 1M · 3L2026-07-22 16:59
- 1e80096safeincremental0H · 0M · 1L2026-07-22 16:42
- 6ed65b4safeincremental0H · 1M · 3L2026-07-22 00:00
- 74a1131needs attentionfull0H · 7M · 12L2026-07-21 19:06
- 563252bneeds attentionincremental4H · 9M · 8L2026-07-21 18:32
- beced58needs attentionincremental0H · 3M · 3L2026-07-21 01:13
- 7fa8684needs attentionincremental4H · 9M · 7L2026-07-20 22:56
- 230784fneeds attentionfull2H · 11M · 14L2026-07-10 00:15
- b876b54needs attentionincremental2H · 2M · 5L2026-07-08 04:44
- ec4847fneeds attentionfull2H · 11M · 8L2026-07-08 03:32