feat(management): FlowDebug run-method / run-value-change / replay commands (Deployer, active-node gated, inline trace)

Flow Debugger (design 2026-09-05 §7.1): FlowDebugRunMethodCommand / FlowDebugRunValueChangeCommand /
FlowDebugReplayCommand on the per-node ManagementActor (Deployer role; the two run commands
are long-running), all delegating to FlowDebugRunner. RunMethod refuses on a standby with
STANDBY_NODE (ManagementCommandException now carries an optional Code that replaces
COMMAND_FAILED), validates parameters through the real ParameterValidator/SchemaRefResolver
(made public), synthesizes the inbound row (actor = operator, Extra.source = flow-debugger)
and runs the method through InboundScriptExecutor with a trace-sink RouteHelper so the reply
carries the RouteCall row + the site's inline events. RunValueChange does a traced routed
write via IInstanceRouter, then polls IPullTraceEventsClient (interface moved to Commons so
ManagementService can consume it) until a ScriptRun parented to the root settles, else
NO_TRIGGER_WITHIN_WINDOW. Replay reads IAuditLogRepository.GetTraceEventsAsync.

Claude-Session: https://claude.ai/code/session_01GSLkkHKz9JrAtmJjmQK3cP
This commit is contained in:
Joseph Doherty
2026-09-06 00:04:18 -04:00
parent 9300bc1a4f
commit d11dcb77c5
11 changed files with 675 additions and 33 deletions
@@ -0,0 +1,27 @@
using ZB.MOM.WW.Audit;
namespace ZB.MOM.WW.ScadaBridge.Commons.Interfaces.Services;
/// <summary>
/// Flow Debugger (design 2026-09-05 §5.4): central-side client for the site-hosted
/// <c>PullTraceEvents</c> unary RPC — a point read of a site's <c>ScriptTrace</c> rows for a
/// set of execution ids (by <c>ExecutionId</c> or <c>ParentExecutionId</c>). NOT a
/// reconciliation path: it moves no cursor and flips no forward state. Best-effort like the
/// audit pull: a transport fault collapses to an empty list after one NodeB failover dial.
/// </summary>
public interface IPullTraceEventsClient
{
/// <summary>Pulls the site's trace rows for <paramref name="executionIds"/> (max 64) since <paramref name="sinceUtc"/>.</summary>
/// <param name="siteId">The site to pull from (resolved to its gRPC endpoint + preshared key).</param>
/// <param name="executionIds">Execution ids to match on <c>ExecutionId</c> or <c>ParentExecutionId</c>.</param>
/// <param name="sinceUtc">Lower bound on <c>OccurredAtUtc</c> (the run's start is the natural choice).</param>
/// <param name="maxEvents">Row cap (the site caps at 2000).</param>
/// <param name="ct">Cancellation token.</param>
/// <returns>The matching rows ordered <c>(OccurredAtUtc, EventId)</c>; empty on fault or unknown site.</returns>
Task<IReadOnlyList<AuditEvent>> PullAsync(
string siteId,
IReadOnlyCollection<Guid> executionIds,
DateTime sinceUtc,
int maxEvents,
CancellationToken ct);
}