diff --git a/internal/agent/loop_run.go b/internal/agent/loop_run.go index 0607c877..595d808d 100644 --- a/internal/agent/loop_run.go +++ b/internal/agent/loop_run.go @@ -110,6 +110,13 @@ func (l *Loop) Run(ctx context.Context, req RunRequest) (*RunResult, error) { l.emitAgentSpanStart(ctx, agentSpanID, runStart, req.Message) } + // Child trace (announce run): set parent trace back to "running" while + // this run is active so the trace UI doesn't show "completed" with a + // "running" child span. + if isChildTrace && l.traceCollector != nil && traceID != uuid.Nil { + l.traceCollector.SetTraceStatus(ctx, traceID, store.TraceStatusRunning) + } + result, err := l.runLoop(ctx, req) // Finalize the root agent span. Uses EmitSpanUpdate (channel send) so it @@ -119,6 +126,15 @@ func (l *Loop) Run(ctx context.Context, req RunRequest) (*RunResult, error) { l.emitAgentSpanEnd(ctx, agentSpanID, runStart, result, err) } + // Child trace: restore trace status now that this run is done. + if isChildTrace && l.traceCollector != nil && traceID != uuid.Nil { + status := store.TraceStatusCompleted + if err != nil { + status = store.TraceStatusError + } + l.traceCollector.SetTraceStatus(ctx, traceID, status) + } + if err != nil { emitRun(AgentEvent{ Type: protocol.AgentEventRunFailed, diff --git a/internal/tracing/collector.go b/internal/tracing/collector.go index ffe561f8..33e4b9a3 100644 --- a/internal/tracing/collector.go +++ b/internal/tracing/collector.go @@ -156,6 +156,16 @@ func (c *Collector) EmitSpanUpdate(spanID, traceID uuid.UUID, updates map[string } } +// SetTraceStatus updates only the trace status and marks it dirty for re-aggregation. +// Used by child trace runs (e.g. announce) to toggle the parent trace back to +// "running" while the child is active, then "completed" when done. +func (c *Collector) SetTraceStatus(ctx context.Context, traceID uuid.UUID, status string) { + if err := c.store.UpdateTrace(ctx, traceID, map[string]any{"status": status}); err != nil { + slog.Warn("tracing: failed to set trace status", "trace_id", traceID, "error", err) + } + c.markDirty(traceID) +} + // FinishTrace marks a trace as completed and schedules aggregate update. func (c *Collector) FinishTrace(ctx context.Context, traceID uuid.UUID, status string, errMsg string, outputPreview string) { now := time.Now().UTC()