feat(central-ui): IFlowDebugService script entry — GetInstanceScripts / PredictScript / RunScript over the flattened configuration

Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29
This commit is contained in:
Joseph Doherty
2026-09-08 01:14:40 -04:00
parent 3d5759613f
commit 5b185c93ff
3 changed files with 286 additions and 110 deletions
@@ -1,6 +1,7 @@
using System.Text.Json;
using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Debugging;
using ZB.MOM.WW.ScadaBridge.Commons.Types.Scripts;
using ZB.MOM.WW.ScadaBridge.ScriptAnalysis;
namespace ZB.MOM.WW.ScadaBridge.CentralUI.Services;
@@ -34,9 +35,32 @@ public sealed record FlowDebugOutcome(FlowGraph? Graph, FlowDebugRunResult? Raw,
public sealed record FlowOutboundActionResult(bool Success, string Message);
/// <summary>
/// CentralUI facade over the Flow Debugger (design 2026-09-05 §5.5/§7.1): static shape
/// prediction from script syntax, and the three management commands (Live run of a method,
/// Live run of a value change, Replay) dispatched to the per-node <c>ManagementActor</c>
/// One script of an instance as the Flow Debugger's script picker shows it (design 2026-09-08 §5):
/// canonical name, trigger kind, the trigger's attribute for write kinds, the effective parameter
/// definitions for call kinds, and the script's own execution timeout (client Ask sizing).
/// </summary>
/// <param name="CanonicalName">Path-qualified script name from the flattened configuration (<c>Module.Script</c> for a composed member).</param>
/// <param name="TriggerType">The effective trigger type, or null when the script has none.</param>
/// <param name="TriggerAttribute">The monitored attribute of a write-kind trigger; null for every other kind.</param>
/// <param name="ParameterDefinitions">JSON parameter definitions the Inputs card renders for a call kind; null = none.</param>
/// <param name="ExecutionTimeoutSeconds">The script's own execution timeout, or null to use the site default.</param>
public sealed record FlowScriptOption(
string CanonicalName, string? TriggerType, string? TriggerAttribute,
string? ParameterDefinitions, int? ExecutionTimeoutSeconds)
{
/// <summary>True when the debugger fires this script by writing <see cref="TriggerAttribute"/>.</summary>
public bool IsWriteKind => ScriptTriggerAttribute.IsWriteKind(TriggerType);
/// <summary>Picker label suffix: <c>ValueChange: Level</c>, <c>Call</c>, <c>no trigger</c>…</summary>
public string TriggerLabel => string.IsNullOrWhiteSpace(TriggerType) ? "no trigger"
: TriggerAttribute is { } a ? $"{TriggerType}: {a}" : TriggerType!;
}
/// <summary>
/// CentralUI facade over the Flow Debugger (design 2026-09-05 §5.5/§7.1, script entry
/// 2026-09-08 §5): the instance's script list read from its flattened configuration, static
/// shape prediction from script syntax, and the three management commands (Live run of a
/// method, Live run of a script, Replay) dispatched to the per-node <c>ManagementActor</c>
/// through the in-process <c>ManagementActorHolder</c> — the SAME Ask path the HTTP
/// <c>/management</c> endpoint uses, so the server stays the single enforcer of the Deployer
/// gate and the active-node refusal (<c>STANDBY_NODE</c>).
@@ -49,15 +73,16 @@ public interface IFlowDebugService
/// <returns>The predicted flow; <see cref="PredictedFlow.Empty"/> when the method does not exist.</returns>
Task<PredictedFlow> PredictMethodAsync(int apiMethodId, CancellationToken cancellationToken = default);
/// <summary>
/// Predicts the flow a value change on <paramref name="attributeName"/> would trigger: the
/// instance's template scripts whose value-change trigger names that attribute, each predicted
/// as a site script.
/// </summary>
/// <summary>The instance's scripts from its flattened configuration, ordered by canonical name; empty when the instance does not exist.</summary>
/// <param name="instanceId">The instance id.</param>
/// <param name="attributeName">Canonical attribute name.</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task<PredictedFlow> PredictValueChangeAsync(int instanceId, string attributeName, CancellationToken cancellationToken = default);
Task<IReadOnlyList<FlowScriptOption>> GetInstanceScriptsAsync(int instanceId, CancellationToken cancellationToken = default);
/// <summary>Predicts the flow of running <paramref name="scriptName"/>: the write + spawned script for a write kind, a RouteCall root + the script for a call kind.</summary>
/// <param name="instanceId">The instance id.</param>
/// <param name="scriptName">Canonical script name (case-insensitive).</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task<PredictedFlow> PredictScriptAsync(int instanceId, string scriptName, CancellationToken cancellationToken = default);
/// <summary>Runs an inbound API method with tracing on the active central node and returns the flow.</summary>
/// <param name="apiMethodId">The method id.</param>
@@ -65,12 +90,13 @@ public interface IFlowDebugService
/// <param name="cancellationToken">Cancellation token.</param>
Task<FlowDebugOutcome> RunMethodAsync(int apiMethodId, JsonElement? parameters, CancellationToken cancellationToken = default);
/// <summary>Writes an attribute through the traced path and returns the flow the change triggered.</summary>
/// <summary>Runs a script by name (write kinds take <paramref name="value"/>, call kinds <paramref name="parameters"/>) and returns the flow.</summary>
/// <param name="instanceId">The instance id.</param>
/// <param name="attributeName">Canonical attribute name.</param>
/// <param name="value">Value in canonical string form.</param>
/// <param name="scriptName">Canonical script name (case-insensitive).</param>
/// <param name="parameters">Call kinds: flat JSON object of parameters, or null.</param>
/// <param name="value">Write kinds: the new value for the trigger's attribute, in canonical string form.</param>
/// <param name="cancellationToken">Cancellation token.</param>
Task<FlowDebugOutcome> RunValueChangeAsync(int instanceId, string attributeName, string value, CancellationToken cancellationToken = default);
Task<FlowDebugOutcome> RunScriptAsync(int instanceId, string scriptName, JsonElement? parameters, string? value, CancellationToken cancellationToken = default);
/// <summary>Rebuilds a past flow from the central audit store.</summary>
/// <param name="executionId">Any execution id in the chain.</param>