feat(flow-debugger): predict the reset write + echo wait of the reset-aware handshake (#36)
Claude-Session: https://claude.ai/code/session_01XPsuy2ZZ5Gbyfpt8PrK5BV
This commit is contained in:
@@ -45,6 +45,8 @@ public sealed record PredictedFlow(IReadOnlyList<PredictedNode> Nodes, IReadOnly
|
||||
/// script API exposes (<c>Route.To(x).Call(y)</c>, <c>Scripts.Call(y)</c>,
|
||||
/// <c>….Attributes.WriteBatchAndWaitAsync(…)</c>, <c>….Attributes[x] = v</c>, …) by text with
|
||||
/// whitespace stripped, exactly as <see cref="ScriptTrustValidator"/>'s hardening walker does.
|
||||
/// The nine-argument reset-aware <c>WriteBatchAndWaitAsync</c> overload (Gitea #36) additionally
|
||||
/// predicts a reset attribute write + echo wait ahead of the batch write + wait pair.
|
||||
/// String-literal arguments resolve to titles; anything else is flagged
|
||||
/// <see cref="PredictedNode.InputDependent"/>. Branching is not modelled: every recognised call
|
||||
/// is emitted once in source order, and an <c>if (…) return …;</c> guard that precedes the next
|
||||
@@ -369,9 +371,18 @@ public static class FlowShapePredictor
|
||||
{
|
||||
case "WriteBatchAndWaitAsync":
|
||||
{
|
||||
// (batch, triggerAttr, triggerValue, waitAttr, waitValue, timeout)
|
||||
// (batch, triggerAttr, triggerValue, waitAttr, waitValue, timeout
|
||||
// [, resetAttr, resetValue, resetTimeout]) — Gitea #36: the reset-aware
|
||||
// overload writes + waits on the reset attribute BEFORE the batch.
|
||||
var (trigger, triggerDependent) = LiteralOrExpr(args.ElementAtOrDefault(1)?.Expression);
|
||||
var (wait, waitDependent) = LiteralOrExpr(args.ElementAtOrDefault(3)?.Expression);
|
||||
var resetArg = ArgAt(args, 6, "resetKey");
|
||||
if (resetArg is not null)
|
||||
{
|
||||
var (reset, resetDependent) = LiteralOrExpr(resetArg.Expression);
|
||||
_b.AddNode(FlowStepKind.AttributeWrite, FlowLane.Tags, Qualify(reset), resetDependent, "reset");
|
||||
_b.AddNode(FlowStepKind.AttributeWait, FlowLane.Site, Qualify(reset), resetDependent, "reset echo");
|
||||
}
|
||||
_b.AddNode(FlowStepKind.AttributeWrite, FlowLane.Tags, Qualify($"batch + {trigger}"), triggerDependent, null);
|
||||
_b.AddNode(FlowStepKind.AttributeWait, FlowLane.Site, Qualify(wait), waitDependent, null);
|
||||
return true;
|
||||
@@ -463,6 +474,16 @@ public static class FlowShapePredictor
|
||||
private static string TrimAsyncSuffix(string member)
|
||||
=> member.Length > 5 && member.EndsWith("Async", StringComparison.Ordinal) ? member[..^5] : member;
|
||||
|
||||
/// <summary>An argument by name (<c>name: expr</c>) or, failing that, by position among the unnamed arguments.</summary>
|
||||
private static ArgumentSyntax? ArgAt(SeparatedSyntaxList<ArgumentSyntax> args, int index, string name)
|
||||
{
|
||||
var named = args.FirstOrDefault(a => a.NameColon?.Name.Identifier.ValueText == name);
|
||||
if (named is not null)
|
||||
return named;
|
||||
var positional = args.ElementAtOrDefault(index);
|
||||
return positional is { NameColon: null } ? positional : null;
|
||||
}
|
||||
|
||||
/// <summary>A string literal resolves to its value; anything else is input-dependent and shown as its source text.</summary>
|
||||
private static (string Text, bool Dependent) LiteralOrExpr(ExpressionSyntax? expr)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user