Portal Community

The Publishing Path

report (ExecutionProgressReport)
  → ReportExecutionProgressLifeCycleAsync(ctx, report)
    → ReportExecutionProgressAsync(ctx, report)          // BaseNodeExecutor.ProgressReport.cs
      ├── mirror: ActivityPublisher.ReportExecutionProgressAsync(...)   // best-effort, logged on failure
      │     → converts to ExecutionActivityMilestone, routes to enabled targets
      │     → milestone has NO Metadata property — report.Metadata is not carried into it
      └── if report.RequiresEventLog:
            sync report.Metadata → report.EventDescription.Metadata
            → Processor.RaiseNodeProgressEvent4Async(ctx, report.EventDescription, message)
              ← THIS is what reaches the frontend/SignalR
The file that does this is marked "Legacy" but is still the active code path BaseNodeExecutor.Reporting.Legacy\BaseNodeExecutor.ProgressReport.cs carries a large comment block saying it's "DEPRECATED & CONVERTED TO ExecutionActivityPublisher" — but its ReportExecutionProgressAsync(NodeExecutionContext, ExecutionProgressReport) method is the one every stage report actually flows through, including the mirror call into the newer ExecutionActivityPublisher. The newer publisher does not yet replace it end-to-end; it supplements it.

What Each Stage Actually Carries Today

StageWhere builtMetadata attached
InitiatedOnExecute (BaseNodeExecutor.Orchestrate.cs:95-98)inputData — scrubbed InputData. The only stage carrying input today.
Entryline 100-101none
EntryValidateline 103-104none
PreValidationGuardRailsline 106-107none
PreProcessline 112-113none
Processline 116-117none
PostProcessline 119-120none
PostValidationGuardRailsline 122-123none
Exitline 128-129none
Completed / ErrorExecute() line 78/84, via CreateStageReportByResultsafe scalar summary only: OutputPortKey, IsSuccess, IsSkipped, IsRetry, DurationMs, ErrorMessage, ExceptionMessage — explicitly not raw OutputData/OutputBranches/Exception
Why Completed/Error is scalar-only — a real crash, not caution for its own sake ExecutionProgressReportFactoryResultExtensions.cs:58-61 has this comment on the code that builds the Completed/Error metadata:
// Project a safe scalar summary instead of the raw NodeExecutionResult — the raw object
// graph (Exception, OutputPort, OutputBranches) is not guaranteed JSON-serializable and
// previously crashed SignalR's write pump (System.NotSupportedException on IntPtr/WaitHandle
// reachable through Exception), silently corrupting the client connection.
Any change that adds output data back into this stage's metadata must keep this constraint in mind — project a safe, scrubbed summary, never the raw NodeExecutionResult/Exception/OutputBranches graph.

Correcting a Common Assumption

It's easy to assume every stage broadcasts both input and output, given how many stages exist. In the current code, 8 of the 10 stages carry no data payload at all, and none currently carry full OutputData — only Initiated (input) and Completed/Error (a scalar result summary) carry anything.

If a UI or log is showing more than that, the most likely sources are outside this lifecycle pipeline:

Recommended Shape (Not Yet Implemented)

1

Input at Entry, not Initiated

Move the inputData metadata attachment to fire on the Entry report instead of (or in addition to, during a transition) Initiated, so "input" and "the stage named Entry" mean the same thing to anyone reading the event stream.

2

Output at Completed

Add a scrubbed, safe projection of OutputData to the Completed stage's metadata — following the same JSON-serializability discipline already established for the scalar summary, not the raw dictionary.

3

Never publish OutputBranches' duplicated Data

If any future change is tempted to attach OutputBranches to an event for convenience, replace it with a path reference instead (e.g. nodeKey + field name) — the branch data is already retrievable on demand via memory.GetNodeResultBranches(nodeKey), so publishing the duplicated payload adds size and cycle risk (see Pinned Data) for no new information.