fix(flow-debugger): final-review remediation — 2000-row trace pull, shared ExceptionHeader parser, surrogate-safe clip, wording, alarm source, register renumber (I1/I2/M1-M8)

This commit is contained in:
Joseph Doherty
2026-09-09 13:30:48 -04:00
parent 0bacc3630e
commit 97f01ae98e
21 changed files with 462 additions and 71 deletions
@@ -82,6 +82,26 @@ public sealed class FlowDebugRunner
/// <summary>Quiet period after the last new row before a value-change run is declared complete.</summary>
public static readonly TimeSpan SettleWindow = TimeSpan.FromSeconds(2);
/// <summary>
/// Row cap asked of the site on each <see cref="IPullTraceEventsClient.PullAsync"/> poll — the
/// site's own ceiling (<c>SiteStreamGrpcServer.MaxTraceEventsPerPull</c> = 2000; a larger
/// <c>max_events</c> is rejected <c>InvalidArgument</c>), so this asks for everything the site
/// will serve.
///
/// <para>
/// Final review I1: this was 500, which was ample before step logs but is not any more. The
/// site query is <c>ORDER BY OccurredAtUtc ASC LIMIT</c>, so the page is the OLDEST rows in
/// the window — and a single traced run now emits up to 201 <c>ScriptLog</c> rows (design
/// §3, <c>MaxLogLinesPerRun</c> + the closing dropped-count row). Two chatty runs could
/// therefore fill a 500-row page and push a later spawned run's <c>ScriptRun</c> row past the
/// end of it, making <see cref="WriteAndPollAsync"/> report a false
/// <see cref="NoTriggerCode"/> for a run that did happen. 2000 makes that ~10x harder; the
/// real fix (paging by last-seen timestamp, or excluding <c>ScriptLog</c> from the awaited-run
/// poll) is deferred-work register row 117.
/// </para>
/// </summary>
public const int PullTraceEventsMax = 2000;
private readonly ILogger<FlowDebugRunner> _logger;
private readonly Func<TimeSpan, CancellationToken, Task> _delay;
private readonly Func<DateTimeOffset> _now;
@@ -487,7 +507,7 @@ public sealed class FlowDebugRunner
var d = AuditDetailsCodec.Deserialize(e.DetailsJson);
if (d.ExecutionId is { } x) ids.Add(x);
}
var pulled = await puller.PullAsync(site.SiteIdentifier, ids.Take(64).ToList(), since, 500, ct).ConfigureAwait(false);
var pulled = await puller.PullAsync(site.SiteIdentifier, ids.Take(64).ToList(), since, PullTraceEventsMax, ct).ConfigureAwait(false);
var added = false;
foreach (var e in pulled)
{