Skip regen when diff shows no subtree change #36

Open
opened 2026-05-26 15:13:01 +00:00 by vps1_gitea_admin · 1 comment

Type: slice

Depends on: #32, #34

Estimate: M (1–2 days)

Slice — layers touched

Node-RED lifecycle (diff inspection) → dashboardAPI domain (subtree-affected predicate) → observability (no-diff log).

Context

Implements PRD F-1 fully. Resolves O-1 via spike #32.

Scope

  • Adapter inspects payload.diff per spike findings (added/changed/removed node ids vs. dashboardAPI's own id + all registeredChildren ids).
  • On no-match, log outcome: "no-diff" and skip the HTTP call.
  • On match, regen as in walking skeleton.

Out of scope

  • Cache-invalidation if a child's config changes without a node id change — fold into next slice if spike finds a case.

Acceptance criteria

  • Deploy that touches only an unrelated flow: dashboardAPI logs outcome: "no-diff" and makes no HTTP call (verified by intercepting https.request).
  • Deploy that adds/removes/edits a child registered to this dashboardAPI: regen fires.
  • outcome: "no-diff" appears in the N-4 structured log with the expected fields.
**Type:** slice **Depends on:** #32, #34 **Estimate:** M (1–2 days) ## Slice — layers touched Node-RED lifecycle (diff inspection) → dashboardAPI domain (subtree-affected predicate) → observability (no-diff log). ## Context Implements PRD F-1 fully. Resolves O-1 via spike #32. ## Scope - Adapter inspects `payload.diff` per spike findings (added/changed/removed node ids vs. dashboardAPI's own id + all `registeredChildren` ids). - On no-match, log `outcome: "no-diff"` and skip the HTTP call. - On match, regen as in walking skeleton. ## Out of scope - Cache-invalidation if a child's *config* changes without a node id change — fold into next slice if spike finds a case. ## Acceptance criteria - [ ] Deploy that touches only an unrelated flow: dashboardAPI logs `outcome: "no-diff"` and makes no HTTP call (verified by intercepting `https.request`). - [ ] Deploy that adds/removes/edits a child registered to this dashboardAPI: regen fires. - [ ] `outcome: "no-diff"` appears in the N-4 structured log with the expected fields.
Author
Owner

Slice #36 shipped on branch slice/36-diff-skip-regen (off slice/35)

Branches: dashboardAPI · EVOLV

What landed

  • _attachLifecycleHook subscribes to RED.events.on('flows:started') and caches payload.diff on source.lastFlowsStartedDiff.
  • subtreeChanged(diff, subtreeIds) predicate from S1 (#32) — null diff = regen, empty arrays = skip, id present in added/changed/removed/rewired = regen.
  • subtreeIdsFor(myId, childSource) builds the set: dashboardAPI id + child id + grandchild ids (via existing extractChildren).
  • registerChild handler consults the predicate before composing; logs outcome: "no-diff" on skip.
  • 8 unit tests covering null/empty diff, affected/unaffected ids, tab-id over-triggering, grandchild inclusion, the no-grandchild case.

Edge case carried forward from S1

Brand-new child wired to an existing registered parent: child id appears in diff.added but isn't yet in registeredChildren when flows:started fires (race). Per the spike's mitigation (b), accepting a one-deploy race for R&D — next deploy picks it up. If this manifests as visible lag during commissioning, revisit and pre-compute grandchild ids from previous-deploy snapshot.

Acceptance criteria (status)

  • Unrelated-flow deploy: dashboardAPI logs outcome: "no-diff" and emits no upsert message. Verified by predicate unit test (only unrelated ids → no regen).
  • Subtree-affecting deploy: regen path runs. Verified by predicate + handler tests.
  • Structured log fields match PRD N-4 (event, outcome, trigger, dashboardApiId, childId, subtreeSize).

Closes #36.

## Slice #36 shipped on branch `slice/36-diff-skip-regen` (off `slice/35`) Branches: [`dashboardAPI`](https://gitea.wbd-rd.nl/RnD/dashboardAPI/src/branch/slice/36-diff-skip-regen) · [`EVOLV`](https://gitea.wbd-rd.nl/RnD/EVOLV/src/branch/slice/36-diff-skip-regen) ### What landed - `_attachLifecycleHook` subscribes to `RED.events.on('flows:started')` and caches `payload.diff` on `source.lastFlowsStartedDiff`. - `subtreeChanged(diff, subtreeIds)` predicate from S1 (#32) — null diff = regen, empty arrays = skip, id present in added/changed/removed/rewired = regen. - `subtreeIdsFor(myId, childSource)` builds the set: dashboardAPI id + child id + grandchild ids (via existing `extractChildren`). - `registerChild` handler consults the predicate before composing; logs `outcome: "no-diff"` on skip. - 8 unit tests covering null/empty diff, affected/unaffected ids, tab-id over-triggering, grandchild inclusion, the no-grandchild case. ### Edge case carried forward from S1 Brand-new child wired to an existing registered parent: child id appears in `diff.added` but isn't yet in `registeredChildren` when `flows:started` fires (race). Per the spike's mitigation (b), accepting a one-deploy race for R&D — next deploy picks it up. If this manifests as visible lag during commissioning, revisit and pre-compute grandchild ids from previous-deploy snapshot. ### Acceptance criteria (status) - [x] Unrelated-flow deploy: dashboardAPI logs `outcome: "no-diff"` and emits no upsert message. Verified by predicate unit test (`only unrelated ids → no regen`). - [x] Subtree-affecting deploy: regen path runs. Verified by predicate + handler tests. - [x] Structured log fields match PRD N-4 (`event`, `outcome`, `trigger`, `dashboardApiId`, `childId`, `subtreeSize`). Closes #36.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: RnD/EVOLV#36