feat(management): dispatch FlowDebugRunScriptCommand (Deployer-only, long-running) on the ManagementActor

Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29
This commit is contained in:
Joseph Doherty
2026-09-08 01:18:52 -04:00
parent 51ce72404b
commit 3f4fde3e35
5 changed files with 23 additions and 11 deletions
@@ -197,7 +197,7 @@ public class ManagementActor : ReceiveActor
// arch-review F3: Flow Debugger Replay is an audit read, so it carries the same role
// set as the RequireFlowDebugger page policy ({Deployer, Administrator, Viewer}). The
// two Live run commands stay DeployerOnly.
// three Live run commands stay DeployerOnly.
private static readonly string[] FlowDebuggerReplayRoles = AuthorizationPolicies.FlowDebuggerAccessRoles;
// arch-review C6: area management is reconciled to any-of [Designer, Deployer].
@@ -310,12 +310,13 @@ public class ManagementActor : ReceiveActor
or RetryParkedMessageCommand or DiscardParkedMessageCommand
or DebugSnapshotCommand => DeployerOnly,
// Flow Debugger (design 2026-09-05 §9 / D6, arch-review F3). A Live run executes
// scripts and writes attributes on operator input, so it stays Deployer-only, like
// the Debug View. Replay only reads the audit chain, so it additionally accepts the
// OperationalAudit roles — the Audit Log / Execution Chain drill-ins are the
// feature's main entry for a past incident and an auditor must be able to use them.
FlowDebugRunMethodCommand or FlowDebugRunValueChangeCommand => DeployerOnly,
// Flow Debugger (design 2026-09-05 §9 / D6, arch-review F3; script entry added by
// design 2026-09-08). All three Live run commands execute scripts and write
// attributes on operator input, so they stay Deployer-only, like the Debug View.
// Replay only reads the audit chain, so it additionally accepts the OperationalAudit
// roles — the Audit Log / Execution Chain drill-ins are the feature's main entry for
// a past incident and an auditor must be able to use them.
FlowDebugRunMethodCommand or FlowDebugRunValueChangeCommand or FlowDebugRunScriptCommand => DeployerOnly,
FlowDebugReplayCommand => FlowDebuggerReplayRoles,
// Two-person secured write. Submit is an Operator action;
@@ -418,6 +419,7 @@ public class ManagementActor : ReceiveActor
// Flow Debugger (design 2026-09-05 §7.1) — one execution path for the UI and the CLI.
FlowDebugRunMethodCommand cmd => await sp.GetRequiredService<FlowDebug.FlowDebugRunner>().RunMethodAsync(sp, cmd, user, CancellationToken.None),
FlowDebugRunValueChangeCommand cmd => await sp.GetRequiredService<FlowDebug.FlowDebugRunner>().RunValueChangeAsync(sp, cmd, user, CancellationToken.None),
FlowDebugRunScriptCommand cmd => await sp.GetRequiredService<FlowDebug.FlowDebugRunner>().RunScriptAsync(sp, cmd, user, CancellationToken.None),
FlowDebugReplayCommand cmd => await sp.GetRequiredService<FlowDebug.FlowDebugRunner>().ReplayAsync(sp, cmd, CancellationToken.None),
// External Systems
@@ -58,6 +58,9 @@ public static class ManagementEndpoints
// Flow Debugger (design 2026-09-05 §7.1): a Live run lasts up to the method timeout
// (+ grace) or the value-change wait window — never the ordinary Ask timeout.
typeof(FlowDebugRunMethodCommand), typeof(FlowDebugRunValueChangeCommand),
// script entry (design 2026-09-08): a direct call lasts up to the script's timeout
// (+ grace), a write kind up to its wait window.
typeof(FlowDebugRunScriptCommand),
];
/// <summary>
@@ -160,8 +160,9 @@ public static class AuthorizationPolicies
/// This is page ACCESS only, and is deliberately NOT sufficient for a Live
/// run: the Run controls, the Live entry-point pickers and the parked-node
/// Retry/Discard actions are gated on <see cref="RequireDeployment"/> in the
/// page, and <c>FlowDebugRunMethodCommand</c> /
/// <c>FlowDebugRunValueChangeCommand</c> stay Deployer-only in the
/// page, and <c>FlowDebugRunMethodCommand</c>,
/// <c>FlowDebugRunValueChangeCommand</c> and
/// <c>FlowDebugRunScriptCommand</c> stay Deployer-only in the
/// ManagementActor role matrix. Only <c>FlowDebugReplayCommand</c> accepts
/// <see cref="FlowDebuggerAccessRoles"/>.
/// </remarks>
@@ -343,10 +343,15 @@ public class FlowDebugRunnerTests
}
[Fact]
public void RoleGate_TheTwoRunCommands_AreDeployerOnly()
public void RoleGate_TheThreeRunCommands_AreDeployerOnly()
{
// arch-review F3: a Live run executes scripts and writes attributes, so it stays Deployer-only.
foreach (var cmd in new object[] { new FlowDebugRunMethodCommand(1, null), new FlowDebugRunValueChangeCommand(1, "a", "b") })
foreach (var cmd in new object[]
{
new FlowDebugRunMethodCommand(1, null),
new FlowDebugRunValueChangeCommand(1, "a", "b"),
new FlowDebugRunScriptCommand(1, "Manual", null, null),
})
Assert.Equal(new[] { "Deployer" }, ManagementActor.GetRequiredRoles(cmd));
}
@@ -151,6 +151,7 @@ public class RequiredRoleMatrixTests
// for a past incident and an auditor must be able to follow them.
["FlowDebugRunMethod"] = [Roles.Deployer],
["FlowDebugRunValueChange"] = [Roles.Deployer],
["FlowDebugRunScript"] = [Roles.Deployer],
["FlowDebugReplay"] = FlowDebuggerReplayers,
// ---- Two-person secured write ----------------------------------------------