Krill had two parallel node-on-node dispatch mechanisms: a structural,
local-only push (executeChildren() / forceExecuteChildren() — parent
stamps its children, verb = actionOf(parent)) and an identity-based,
cross-server-capable pull (executeSources() — but it applied the
receiver’s verb, actionOf(target)). A Button could not RESET a
HighThreshold because the threshold was a tree-locked child of a
DataPoint, and the only cross-server path applied the wrong node’s verb.
Design, not a bug: the verb was conflated with hierarchy (push path) and
with the receiver (pull path). Phase 1 (add-node-action-verb) shipped the
verb vocabulary but transported it by stamping the receiver’s
NodeState.RESET — a durable intent smuggled through a transient state.
SDK 0.0.24 universalised TargetingNodeMetaData (every meta type now
carries sources/targets/executionSource/nodeAction) and added
SourceTriggerPayload + EventType.SOURCE_TRIGGERED. On the krill side:
executeChildren/forceExecuteChildren were removed; executeSources()
now builds a SourceTriggerPayload(originator.id(), actionOf(originator))
and wakes each subscriber via a new KrillApp.wakeFromSource() →
NodeProcessor.onSourceTrigger(node, trigger) seam (the emit/post
router was refactored to a single KrillApp.processor() lookup so the two
paths can never diverge). doNodeVerbOnTarget no longer stamps
NodeState.RESET — it is EXECUTE-only self-execution; the verb only ever
reaches a receiver through a source read. Per-processor value-gating lives
in onSourceTrigger (e.g. ServerTaskListProcessor: Pin source + RESET +
value HIGH ⇒ reset tasks). New child nodes default
sources += parentIdentity + a firing executionSource in
ServerNodeManager.update()’s creation branch, preserving the
“drop-under-a-parent” UX. Phase-1 RESET regression tests were rewritten
against the new seam, not deleted.
KrillApp.processor() single-lookup refactor means a missing
when arm drops both the value-change and source-trigger dispatch
identically — no silent divergence between the two paths.NodeProcessor.onSourceTrigger defaults to a no-op: a processor
that does not understand the incoming verb does nothing. It must never
fall back to EXECUTE. Tests assert the negative
(ResetActionProcessorTest, the EXECUTE-on-TaskList no-op case).sources is inert under the new model. This was accepted
deliberately for the dev/demo swarm; no data migration, no compat
shim. If a previously-working automation goes silent after this lands
on an existing swarm, that is the expected D8 outcome, not a bug —
rewire the node’s sources/executionSource explicitly.