feat(flow-debugger): carry site-side trace truncation on the route reply (ScadaBridge#54)

ScriptCallResult and RouteToCallResponse gain trailing Truncated/LogsTruncated,
filled from the site FlowTraceCollector after Complete(); RouteToCallResponseDto
fields 8/9 carry them (sitecommand regen). FlowDebugRunner ORs them into
FlowDebugRunResult on the script entry, and on the method entry through a new
optional truncation sink on RouteHelper.WithTraceSink (carried through every
With* clone). Previously a site run whose collector dropped rows came back as a
silently short list. Older sites send false.

Claude-Session: https://claude.ai/code/session_01GT67AEHd2Acv1YPwGfXEoV
This commit is contained in:
Joseph Doherty
2026-11-09 12:37:57 -05:00
parent 5310fd0e26
commit 6d5bb6189f
15 changed files with 484 additions and 155 deletions
@@ -174,7 +174,19 @@ public sealed class FlowDebugRunner
// only known once the script runs. So the operator's scope is pushed into the
// RouteHelper and enforced at route time, before any site traffic.
var scope = await ResolvePermittedSiteIdentifiersAsync(sp, user, ct).ConfigureAwait(false);
var route = sp.GetRequiredService<RouteHelper>().WithTraceSink(evt => collector.Add(evt, lowPriority: FlowStepProjector.IsLogLine(evt))).WithSiteScope(scope);
// Gitea #54: a routed site run whose own collector dropped rows reports it here; the flags
// are OR-ed into the result so the page labels the short site lane as truncated.
var siteTruncated = 0;
var siteLogsTruncated = 0;
var route = sp.GetRequiredService<RouteHelper>()
.WithTraceSink(
evt => collector.Add(evt, lowPriority: FlowStepProjector.IsLogLine(evt)),
(truncated, logsTruncated) =>
{
if (truncated) Interlocked.Exchange(ref siteTruncated, 1);
if (logsTruncated) Interlocked.Exchange(ref siteLogsTruncated, 1);
})
.WithSiteScope(scope);
// Step-logs D7: a ScriptLog row rides the collector's low-priority lane so the step rows
// emitted at run end are never crowded out of the inline copy (Replay stays complete).
var executor = sp.GetRequiredService<InboundScriptExecutor>();
@@ -235,7 +247,8 @@ public sealed class FlowDebugRunner
return new FlowDebugRunResult(
root, result.Success, result.ErrorMessage, result.Success ? null : result.ErrorCode,
ordered, Truncated: collector.Truncated, startedAt, finishedAt, LogsTruncated: collector.LogsTruncated);
ordered, Truncated: collector.Truncated || Volatile.Read(ref siteTruncated) == 1, startedAt, finishedAt,
LogsTruncated: collector.LogsTruncated || Volatile.Read(ref siteLogsTruncated) == 1);
}
/// <summary>
@@ -438,8 +451,10 @@ public sealed class FlowDebugRunner
return new FlowDebugRunResult(
root, response.Success, response.ErrorMessage,
response.Success ? null : faultCode ?? CallFailedCode,
ordered, Truncated: collector.Truncated, startedAt, DateTimeOffset.UtcNow,
LogsTruncated: collector.LogsTruncated);
// Gitea #54: OR in the SITE collector's flags — central's collector only sees the
// rows the site sent, so a site run that dropped rows used to look complete.
ordered, Truncated: collector.Truncated || response.Truncated, startedAt, DateTimeOffset.UtcNow,
LogsTruncated: collector.LogsTruncated || response.LogsTruncated);
}
/// <summary>