Symptom

GET /incoming/{path} returned HTTP 200 but was a dead end: the hook node’s snapshot remained empty (no request data stored), and downstream observers (Calculations wired with sources=[hook], SOURCE_INVOKED) never fired.

Root cause

Two gaps in ServerWebHookInboundProcessor.process() and Routes.kt:

  1. No payload written. Routes.kt built allParams (path segments + query params) and sent it as the HTTP response body, but never stamped it into the hook node’s snapshot before calling invoke(). The processor received a node with an empty snapshot and published that empty state.

  2. Wrong persistence pattern. The processor called runProcessing { succeeded(node) } without first calling update(node, propagate=false) to persist the snapshot. The ServerButtonProcessor works because it calls succeeded(node) directly — the Button has no value to persist. A value-producing source like IncomingWebHook must persist first (update propagate=false) then fire (succeeded), so observers receive the durable value. The runProcessing wrapper was also unnecessary (Button doesn’t use it).

Fix

Prevention