Skip to content

Fix subagent terminal result summaries#1519

Merged
Aaronontheweb merged 4 commits into
netclaw-dev:devfrom
Aaronontheweb:issue-1507-subagent-result-envelope
Jun 30, 2026
Merged

Fix subagent terminal result summaries#1519
Aaronontheweb merged 4 commits into
netclaw-dev:devfrom
Aaronontheweb:issue-1507-subagent-result-envelope

Conversation

@Aaronontheweb

@Aaronontheweb Aaronontheweb commented Jun 29, 2026

Copy link
Copy Markdown
Collaborator

Summary

Fixes the loop where the parent model receives a completed spawn_agent result as an undifferentiated text blob, misses that the subagent already finished the task, and delegates the same work again.

This change makes every spawn_agent completion return a clear terminal envelope with the subagent name, run id, outcome, optional reason, diagnostics pointer, and either a Summary: or Error: section. A run that exhausts its tool-iteration budget but still returns a useful final answer is now marked partial instead of looking like an ordinary success or a hard failure.

Structured findings remain available for the memory pipeline, but they are kept separate from the parent-facing tool result and no longer blind-truncate the subagent's final output.

Fixes #1507.

Validation

  • dotnet build Netclaw.slnx --no-restore
  • dotnet test src/Netclaw.Actors.Tests --no-build --filter "FullyQualifiedName~SubAgent|FullyQualifiedName~TurnStateTracker"
  • dotnet test src/Netclaw.Cli.Tests --filter "FullyQualifiedName~DaemonClientMappingTests"
  • dotnet slopwatch analyze
  • pwsh ./scripts/Add-FileHeaders.ps1 -Verify
  • Subagents eval category passed 5/5 against the configured eval provider

@Aaronontheweb Aaronontheweb added the subagents spawn_agent, SubAgentActor, definition loader, discovery context layer, and related features label Jun 29, 2026
@Aaronontheweb
Aaronontheweb enabled auto-merge (squash) June 29, 2026 19:55
@Aaronontheweb
Aaronontheweb disabled auto-merge June 29, 2026 19:55
Aaronontheweb

This comment was marked as low quality.

=> result.Success ? result.Output : $"Subagent '{agent}' failed: {result.Output}";
{
var builder = new StringBuilder();
builder.AppendLine("Subagent run finished.");

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the parent-facing behavior change for #1507: the tool result now starts with an explicit terminal marker instead of raw subagent prose, so the parent model has an unambiguous completion signal.

Complete(
true,
text,
_forcedFinalOutcomeReason.HasValue ? SubAgentRunOutcome.Partial : SubAgentRunOutcome.Completed,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This line is what turns a forced no-tools wrap-up into a partial run instead of a normal completion. That distinction matters because tool-budget exhaustion can still produce a useful summary, but it should be visible to the parent and operators as a degraded terminal outcome.

Shape = SubAgentFindingShape.Conclusion,
Title = $"subagent:{_definition.Name}",
Content = normalized,
Content = content,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the memory-side change: findings now preserve the full final output instead of blindly truncating it before review. The parent-facing spawn_agent result is still the terminal envelope; this content is only for the separate structured-finding path.

return result;
return result with
{
RunId = runId,

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This attaches the spawner-generated run id to the returned result, which is what lets the terminal tool envelope correlate with session output events and logs. Without this, the parent-facing diagnostics pointer would not be tied back to the actual child run.

RunId = info.RunId,
AgentName = new SubAgents.AgentName(info.AgentName),
Success = info.Success,
Outcome = info.Outcome ?? (info.Success ? SubAgentRunOutcome.Completed : SubAgentRunOutcome.Failed),

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the handoff from subagent runtime metadata into the session event model. It keeps completion events aligned with the terminal tool envelope, while still defaulting legacy success/failure notifications when older paths do not provide an explicit outcome.

Phase = msg.Phase.ToString().ToLowerInvariant(),
ToolCountSub = msg.ToolCount,
SubAgentSuccess = msg.Success,
SubAgentOutcome = msg.Phase == SubAgents.SubAgentPhase.Completed

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This intentionally emits outcome metadata only for completed subagent events. A started event has no terminal outcome yet, so leaving the DTO fields absent avoids sending a misleading completed default over SignalR.

@Aaronontheweb
Aaronontheweb enabled auto-merge (squash) June 29, 2026 23:56
@Aaronontheweb
Aaronontheweb merged commit 8d37e45 into netclaw-dev:dev Jun 30, 2026
15 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

subagents spawn_agent, SubAgentActor, definition loader, discovery context layer, and related features

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Parent LLM ignores sub-agent text output on tool result

1 participant