fix(cli): flow-debug help text names the script entry; replay role note; run-script guard tests

Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29
This commit is contained in:
Joseph Doherty
2026-09-08 01:17:32 -04:00
parent 067a858ce9
commit 45c6b27d99
3 changed files with 31 additions and 8 deletions
@@ -6,11 +6,12 @@ using ZB.MOM.WW.ScadaBridge.Commons.Messages.Management;
namespace ZB.MOM.WW.ScadaBridge.CLI.Commands;
/// <summary>
/// The <c>flow-debug</c> command group (Flow Debugger design 2026-09-05 §7.1): a Live run of
/// an inbound API method or a value-change trigger with every routed site run traced, and a
/// Replay of a past execution chain from the central audit store. All three are Deployer
/// commands answered by the node the CLI is pointed at; a standby central refuses the two
/// run commands with <c>STANDBY_NODE</c> (Replay is served from any node).
/// The <c>flow-debug</c> command group (Flow Debugger design 2026-09-05 §7.1; script entry
/// point 2026-09-08 §6): a Live run of an inbound API method, a site script, or a
/// value-change trigger with every routed site run traced, and a Replay of a past execution
/// chain from the central audit store. The three run commands are Deployer-only commands
/// answered by the node the CLI is pointed at; a standby central refuses them with
/// <c>STANDBY_NODE</c>. Replay is served from any node and accepts a wider role set.
/// </summary>
public static class FlowDebugCommands
{
@@ -29,7 +30,7 @@ public static class FlowDebugCommands
/// <returns>The configured <c>flow-debug</c> command.</returns>
public static Command Build(Option<string> urlOption, Option<string> formatOption, Option<string> usernameOption, Option<string> passwordOption)
{
var command = new Command("flow-debug") { Description = "Flow Debugger: trace an inbound API method or value-change flow end to end, or replay a past one" };
var command = new Command("flow-debug") { Description = "Flow Debugger: trace an inbound API method, a site script, or a value-change flow end to end, or replay a past one" };
command.Add(BuildRunMethod(urlOption, formatOption, usernameOption, passwordOption));
command.Add(BuildRunScript(urlOption, formatOption, usernameOption, passwordOption));
+4 -2
View File
@@ -2278,8 +2278,10 @@ A Live run executes the real thing — the method script on the active central n
site script (called directly, or fired by writing its trigger attribute), or a real attribute
write at the site — with tracing switched on for the whole chain, so every routed site script
run, attribute write and attribute wait it causes is recorded as a `ScriptTrace` audit row and
returned inline. Replay reads those rows back for any past execution. All four sub-commands
require the **Deployer** role.
returned inline. Replay reads those rows back for any past execution. The three run commands
(`run-method`, `run-script`, `run-value-change`) require the **Deployer** role; `replay`
accepts the wider `AuthorizationPolicies.FlowDebuggerAccessRoles` set (Deployer,
Administrator, Viewer — arch-review F3), since it only reads the audit chain.
The three run commands are answered by the node the CLI is pointed at. Through the Traefik
load balancer that is the active node; pointed at a standby node directly they refuse with
@@ -68,6 +68,26 @@ public class FlowDebugCommandsTests
Assert.Equal(120, parse.GetValue((Option<int>)cmd.Options.Single(o => o.Name == "--timeout-seconds")));
}
[Fact]
public async Task RunScript_WaitSecondsZero_IsRejectedWithInvalidArgument()
{
var group = FlowDebugCommands.Build(Url, Format, Username, Password);
var exitCode = await group.Parse("run-script --instance-id 7 --script Manual --wait-seconds 0").InvokeAsync();
Assert.Equal(1, exitCode);
}
[Fact]
public async Task RunScript_TimeoutSecondsZero_IsRejectedWithInvalidArgument()
{
var group = FlowDebugCommands.Build(Url, Format, Username, Password);
var exitCode = await group.Parse("run-script --instance-id 7 --script Manual --timeout-seconds 0").InvokeAsync();
Assert.Equal(1, exitCode);
}
[Fact]
public void RunMethod_TimeoutSecondsDefaultsTo120()
{