feat/powerradar
needs attentionviewing older commit31e63bb · incrementalPR #327reviewed 2026-07-22 20:53 UTC0H · 2M · 5L · 6I- Purpose
- Add a native API-based PowerRadar (Centrica/Panoramic Power) metrics integration — replacing the manual-CSV path with headless 5/15/60-min collection via the portal's internal energy-insights-gateway (/eigw) API with OAuth2 password-grant auth.
- Goal
- Ship powerradar-api as a second access path to the powerradar brand, fitting the existing Site Collection SFN pull model, with live end-to-end validation on Energía Real devices.
- Sub-goals
- SG-1: Auth + transport engine + live probe (OAuth2 broker, chart-data client, WAF headers) ✓
- SG-2: Manifest + catalog + provider registration (integration-manifests, DB seed, schema enum) ✓
- SG-3: Pure translation layer + 13 unit tests ✓
- SG-4: Metrics handler + 18 handler tests (metrics-only, §9 multi-granularity) ✓
- SG-5: CDK stack + wiring (lambda.stack, IAM, paths, coordinator dependency) ✓
- SG-6: Live worker e2e — 191 demand points via 15m/2d run on site 168296 ✓
- SG-7: Framework fold-back — CLAUDE.md, memory, registry ✓
- What
- This commit adds Phase 5.5 to preview-provision.yml: a `heal_param` bash function that self-repairs drift-deleted SFN ARN SSM parameters after Phase 5. Phase 4 deletes SSM params out-of-band; CFN doesn't re-create them on stack UPDATE (only on CREATE or template change), so a second bootstrap would fail Phase 6 with 'Unable to fetch parameters'. The function reads the true ARN from CloudFormation's stack state and re-seeds the SSM param.
- Why
- Fix a repeat-bootstrap failure mode for preview environments: once Phase 5 has run once, subsequent re-runs of preview-provision would permanently fail at Phase 6 because Phase 4's SSM delete left drift CFN couldn't heal. A targeted, idempotent repair step at the right point in the workflow (after Phase 5, before Phase 6) breaks the cycle.
- Areas
- services/metrics/integrations/powerradar-api+1300−0infra/cdk/src/stacks/services/metrics/integrations/powerradar-api+165−0packages/integration-manifests+109−0packages/database+17−1infra/cdk/src/app + lib+29−2.github/workflows/preview-provision.yml+31−0.branch + CLAUDE.md+339−315
- Blast
- ~2000 net-adds across 29 files; core blast radius is the new powerradar-api lambda + CDK stack in the preview environment. The CI fix (this commit) touches only the workflow. No breaking changes to existing integrations or shared packages.
Findings · 12
correctness3
`set -e` swallowed in `$()` assignment — AWS errors become misleading 'no state machine found'
.github/workflows/preview-provision.yml
Bash does not abort when a failing command appears inside `$()` on the right-hand side of a variable assignment, even with `set -euo pipefail`. If `describe-stack-resources` fails for any reason (IAM permission denied, network timeout, AWS throttling), the script continues with `arn=''` and prints the misleading error '::error::$param missing after Phase 5 and no ${logical_prefix}* state machine found in $stack — cannot heal.' rather than surfacing the real AWS error. The step still fails via `exit 1`, so it won't silently pass, but diagnosis is harder. Fix: capture stderr explicitly or emit `$(cat /tmp/cfn_err)` in the error message.
`exit 1` inside `heal_param` correctly terminates the workflow step (not a subshell)
.github/workflows/preview-provision.yml
The function is called inline (not via `(heal_param ...)` subshell), so `exit 1` exits the entire bash process and fails the GHA step as intended. Confirmed correct.
awk prefix-match with `^` is correct for CDK-hashed logical resource IDs
.github/workflows/preview-provision.yml
CDK appends an 8-character hash to every logical resource ID (e.g. `CfePipelineStateMachineF3A8B201`). The `$1 ~ "^"p` prefix anchor is the right approach — an exact match would never work against CDK-synthesized IDs. Confirmed correct for the current use case.
security1
ARN logged in plaintext — acceptable; AWS account ID already present in same file
.github/workflows/preview-provision.yml
The `echo " healed $param -> $arn"` line writes the SFN ARN (which encodes account ID, region, and resource name) to the GHA log. ARNs are not secrets. The account ID is already hardcoded earlier in the same workflow. Low-information disclosure in a private repo; not a concern.
conventions2
Phase 5.5 naming is a minor convention divergence but clearly communicates intent
.github/workflows/preview-provision.yml
All other phases use whole integers (0–7). '5.5' is unconventional but is a well-understood idiom for an inserted step without renumbering downstream phases. Acceptable.
Stack names and SFN logical ID prefixes verified consistent with CDK stacks
.github/workflows/preview-provision.yml
`BatuCfeStepFunctions-${SLUG}-dev` and `BatuCfePaymentStatusSfn-${SLUG}-dev` match the Phase 5 deploy targets. Prefixes `CfePipelineStateMachine` and `PaymentStatusPipelineStateMachine` match the CDK `new StateMachine(this, '...')` constructor names. The heal targets are correct.
tests2
Fatal branch: document expected LogicalResourceId prefixes to aid future debugging
.github/workflows/preview-provision.yml
The `exit 1` path fires when the awk pattern finds no match. If the CDK logical ID changes (stack rename, CDK version upgrade), this will silently fail to heal with a 'cannot heal' message. A one-line comment above each `heal_param` call naming the exact CDK resource (`new StateMachine(this, 'CfePipelineStateMachine', ...)` in `BatuCfeStepFunctions stack`) would let a maintainer quickly verify the prefix without a live run.
No automated tests — appropriate for GHA bash helpers
.github/workflows/preview-provision.yml
This is CI-only infra glue calling AWS APIs. There is no unit test framework for GHA workflows in this repo. Manual/integration verification against a real preview stack is the practical path. Acceptable.
improvement4
A param that exists with a stale/wrong ARN is never corrected
.github/workflows/preview-provision.yml
The early-return guard (`aws ssm get-parameter → return`) means 'param exists = no heal needed'. If the parameter exists but holds a wrong ARN (e.g. from a prior stack that was torn down and re-created with a different ARN), the heal is silently skipped and Phase 6 will fail at runtime with a different error. A value-equality check before the early return — compare current value against the ARN from CFN, only skip if they match — would make the guard honest. Low probability today but worth noting for operational clarity.
`awk` prefix anchor without `$` — could select wrong state machine if sibling prefix added
.github/workflows/preview-provision.yml
The pattern `$1 ~ "^"p` matches any LogicalResourceId that *starts with* the prefix. If a future CDK template adds a second state machine whose logical ID starts with the same prefix (e.g. `CfePipelineStateMachineV2` alongside `CfePipelineStateMachineXXXXXXXX`), the function picks whichever CFN returns first — non-deterministic. The CDK-appended 8-char hash suffix means an exact match `$1 == p` won't work, but `$1 ~ "^"p"[A-Z0-9]{8}$"` would be more precise. For now this is safe since each stack has exactly one SFN per prefix.
CFN Outputs would be a more stable ARN source than StackResources + awk
.github/workflows/preview-provision.yml
Using `aws cloudformation describe-stacks --query 'Stacks[0].Outputs[?OutputKey=="SfnArn"].OutputValue'` would replace the LogicalResourceId prefix-matching heuristic with a named, explicit contract. This removes the need for `logical_prefix` entirely and would survive CDK renames. Requires adding a `CfnOutput` to each SFN stack. An improvement for a follow-up, not a blocker.
CloudFormation drift-detection is not the right mechanism here — SSM seeding is correct
.github/workflows/preview-provision.yml
CFN drift detection only identifies divergence from the template; it does not re-create SSM params that were deleted out-of-band when CFN still owns the resource. The heal_param pattern (read ARN from CFN, write to SSM) is the right approach for this class of drift. No architectural change needed.
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:53current
- 482cb88safeincremental0H · 0M · 1L2026-07-22 17:56
- 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