detached e1622b9

fix(subagents): preserve primary trace activity

commit
e1622b937dad4a55e324fa19cd82067abf721d04
author
brice · · 2026-08-12 10:06 UTC
committer
brice · · 2026-08-12 12:33 UTC
parent
3e0f68b
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +35 -7
M packages/agent/modes/subagent_indicator.go +1 -1
@@ -46,7 +46,7 @@ func renderSubagentActivityLine(th tui.Theme, spinnerGlyph string, snapshot suba
4646 }
4747 activity := operation.Label()
4848 if observation != nil {
49- activity = observation.Label() + " · " + formatSubagentActivityAge(observation.At, now) + " ago"
49+ activity += " · " + observation.Label() + " " + formatSubagentActivityAge(observation.At, now) + " ago"
5050 }
5151 age := formatSubagentActivityAge(operation.StartedAt, now)
5252 plain, layout := fitSubagentActivityLine(spinnerGlyph, name, activity, age, width-2)
@@ -46,7 +46,7 @@ func renderSubagentActivityLine(th tui.Theme, spinnerGlyph string, snapshot suba
46 }46 }
47 activity := operation.Label()47 activity := operation.Label()
48 if observation != nil {48 if observation != nil {
49- activity = observation.Label() + " · " + formatSubagentActivityAge(observation.At, now) + " ago"49+ activity += " · " + observation.Label() + " " + formatSubagentActivityAge(observation.At, now) + " ago"
50 }50 }
51 age := formatSubagentActivityAge(operation.StartedAt, now)51 age := formatSubagentActivityAge(operation.StartedAt, now)
52 plain, layout := fitSubagentActivityLine(spinnerGlyph, name, activity, age, width-2)52 plain, layout := fitSubagentActivityLine(spinnerGlyph, name, activity, age, width-2)
M packages/agent/modes/subagent_indicator_test.go +1 -1
@@ -32,7 +32,7 @@ func TestRenderSubagentActivityLinesShowsSafeLastObservation(t *testing.T) {
3232 lines := plainActivityLines(renderSubagentActivityLines(tui.Dark, "/", []subagents.AgentSnapshot{{ID: "review-123", Subagent: "reviewer"}}, map[string]subagents.AgentTraceView{
3333 "review-123": {PrimaryOperation: &subagents.Operation{Type: "turn.started", StartedAt: now.Add(-time.Minute)}, LastObservation: &subagents.LiveObservation{Type: "assistant.stream.observed", At: now.Add(-time.Second)}},
3434 }, 100, now))
35- if got := strings.Join(lines, "\n"); !strings.Contains(got, "assistant streaming · 1s ago") || strings.Contains(got, "delta") {
35+ if got := strings.Join(lines, "\n"); !strings.Contains(got, "turn open · assistant streaming 1s ago") || strings.Contains(got, "delta") {
3636 t.Fatalf("activity lines = %#v", lines)
3737 }
3838 }
@@ -32,7 +32,7 @@ func TestRenderSubagentActivityLinesShowsSafeLastObservation(t *testing.T) {
32 lines := plainActivityLines(renderSubagentActivityLines(tui.Dark, "/", []subagents.AgentSnapshot{{ID: "review-123", Subagent: "reviewer"}}, map[string]subagents.AgentTraceView{32 lines := plainActivityLines(renderSubagentActivityLines(tui.Dark, "/", []subagents.AgentSnapshot{{ID: "review-123", Subagent: "reviewer"}}, map[string]subagents.AgentTraceView{
33 "review-123": {PrimaryOperation: &subagents.Operation{Type: "turn.started", StartedAt: now.Add(-time.Minute)}, LastObservation: &subagents.LiveObservation{Type: "assistant.stream.observed", At: now.Add(-time.Second)}},33 "review-123": {PrimaryOperation: &subagents.Operation{Type: "turn.started", StartedAt: now.Add(-time.Minute)}, LastObservation: &subagents.LiveObservation{Type: "assistant.stream.observed", At: now.Add(-time.Second)}},
34 }, 100, now))34 }, 100, now))
35- if got := strings.Join(lines, "\n"); !strings.Contains(got, "assistant streaming · 1s ago") || strings.Contains(got, "delta") {35+ if got := strings.Join(lines, "\n"); !strings.Contains(got, "turn open · assistant streaming 1s ago") || strings.Contains(got, "delta") {
36 t.Fatalf("activity lines = %#v", lines)36 t.Fatalf("activity lines = %#v", lines)
37 }37 }
38 }38 }
M packages/agent/tools/subagent_status.go +3 -3
@@ -68,8 +68,8 @@ type subagentStatusResult struct {
6868 State string `json:"state"`
6969 Available bool `json:"available"`
7070 Ref string `json:"ref,omitempty"`
71- Delivered bool `json:"delivered,omitempty"`
72- DeliveryFailed bool `json:"delivery_failed,omitempty"`
71+ Delivered bool `json:"delivered"`
72+ DeliveryFailed bool `json:"delivery_failed"`
7373 }
7474
7575 const subagentStatusSchema = `{
@@ -190,7 +190,7 @@ func publicSubagentStatus(snapshot subagents.AgentSnapshot, view subagents.Agent
190190 }
191191 if view.Result != nil {
192192 if entry.Result == nil {
193- entry.Result = &subagentStatusResult{}
193+ entry.Result = &subagentStatusResult{State: "unknown"}
194194 }
195195 entry.Result.Available = view.Result.Available
196196 if view.Result.Ref != "" {
@@ -68,8 +68,8 @@ type subagentStatusResult struct {
68 State string `json:"state"`68 State string `json:"state"`
69 Available bool `json:"available"`69 Available bool `json:"available"`
70 Ref string `json:"ref,omitempty"`70 Ref string `json:"ref,omitempty"`
71- Delivered bool `json:"delivered,omitempty"`71+ Delivered bool `json:"delivered"`
72- DeliveryFailed bool `json:"delivery_failed,omitempty"`72+ DeliveryFailed bool `json:"delivery_failed"`
73 }73 }
74 74
75 const subagentStatusSchema = `{75 const subagentStatusSchema = `{
@@ -190,7 +190,7 @@ func publicSubagentStatus(snapshot subagents.AgentSnapshot, view subagents.Agent
190 }190 }
191 if view.Result != nil {191 if view.Result != nil {
192 if entry.Result == nil {192 if entry.Result == nil {
193- entry.Result = &subagentStatusResult{}193+ entry.Result = &subagentStatusResult{State: "unknown"}
194 }194 }
195 entry.Result.Available = view.Result.Available195 entry.Result.Available = view.Result.Available
196 if view.Result.Ref != "" {196 if view.Result.Ref != "" {
M packages/agent/tools/subagent_status_test.go +30 -2
@@ -64,9 +64,10 @@ func TestSubagentStatusIncludesTurnCounters(t *testing.T) {
6464 func TestPublicSubagentStatusUsesOnlyPrimaryOperation(t *testing.T) {
6565 at := time.Date(2026, 8, 12, 7, 0, 0, 0, time.UTC)
6666 entry := publicSubagentStatus(subagents.AgentSnapshot{ID: "agent-1"}, subagents.AgentTraceView{
67- OpenOperations: []subagents.Operation{{Type: "turn.started", StartedAt: at}, {Type: "tool.started", StartedAt: at.Add(time.Second)}},
67+ OpenOperations: []subagents.Operation{{Type: "turn.started", StartedAt: at}},
68+ PrimaryOperation: &subagents.Operation{Type: "tool.started", StartedAt: at.Add(time.Second)},
6869 })
69- if entry.PrimaryOperation == nil || entry.PrimaryOperation.Type != "turn.started" {
70+ if entry.PrimaryOperation == nil || entry.PrimaryOperation.Type != "tool.started" {
7071 t.Fatalf("primary operation = %#v", entry.PrimaryOperation)
7172 }
7273 encoded, err := json.Marshal(entry)
@@ -80,6 +81,33 @@ func TestPublicSubagentStatusUsesOnlyPrimaryOperation(t *testing.T) {
8081 if _, found := fields["operation"]; found {
8182 t.Fatalf("obsolete operation field in %#v", fields)
8283 }
84+ if _, found := fields["primary_operation"]; !found {
85+ t.Fatalf("primary operation missing from %#v", fields)
86+ }
87+}
88+
89+func TestPublicSubagentStatusIncludesResultDeliveryFacts(t *testing.T) {
90+ entry := publicSubagentStatus(subagents.AgentSnapshot{ID: "agent-1"}, subagents.AgentTraceView{
91+ Result: &subagents.ResultFact{Available: true, Ref: "subagent://agent-1/result"},
92+ })
93+ if entry.Result == nil || entry.Result.State != "unknown" || entry.Result.Delivered || entry.Result.DeliveryFailed {
94+ t.Fatalf("result = %#v", entry.Result)
95+ }
96+ encoded, err := json.Marshal(entry)
97+ if err != nil {
98+ t.Fatal(err)
99+ }
100+ var fields map[string]json.RawMessage
101+ if err := json.Unmarshal(encoded, &fields); err != nil {
102+ t.Fatal(err)
103+ }
104+ var result map[string]any
105+ if err := json.Unmarshal(fields["result"], &result); err != nil {
106+ t.Fatal(err)
107+ }
108+ if result["delivered"] != false || result["delivery_failed"] != false {
109+ t.Fatalf("result JSON = %#v", result)
110+ }
83111 }
84112
85113 func TestSubagentStatusSchemaHasOptionalAgentID(t *testing.T) {
@@ -64,9 +64,10 @@ func TestSubagentStatusIncludesTurnCounters(t *testing.T) {
64 func TestPublicSubagentStatusUsesOnlyPrimaryOperation(t *testing.T) {64 func TestPublicSubagentStatusUsesOnlyPrimaryOperation(t *testing.T) {
65 at := time.Date(2026, 8, 12, 7, 0, 0, 0, time.UTC)65 at := time.Date(2026, 8, 12, 7, 0, 0, 0, time.UTC)
66 entry := publicSubagentStatus(subagents.AgentSnapshot{ID: "agent-1"}, subagents.AgentTraceView{66 entry := publicSubagentStatus(subagents.AgentSnapshot{ID: "agent-1"}, subagents.AgentTraceView{
67- OpenOperations: []subagents.Operation{{Type: "turn.started", StartedAt: at}, {Type: "tool.started", StartedAt: at.Add(time.Second)}},67+ OpenOperations: []subagents.Operation{{Type: "turn.started", StartedAt: at}},
68+ PrimaryOperation: &subagents.Operation{Type: "tool.started", StartedAt: at.Add(time.Second)},
68 })69 })
69- if entry.PrimaryOperation == nil || entry.PrimaryOperation.Type != "turn.started" {70+ if entry.PrimaryOperation == nil || entry.PrimaryOperation.Type != "tool.started" {
70 t.Fatalf("primary operation = %#v", entry.PrimaryOperation)71 t.Fatalf("primary operation = %#v", entry.PrimaryOperation)
71 }72 }
72 encoded, err := json.Marshal(entry)73 encoded, err := json.Marshal(entry)
@@ -80,6 +81,33 @@ func TestPublicSubagentStatusUsesOnlyPrimaryOperation(t *testing.T) {
80 if _, found := fields["operation"]; found {81 if _, found := fields["operation"]; found {
81 t.Fatalf("obsolete operation field in %#v", fields)82 t.Fatalf("obsolete operation field in %#v", fields)
82 }83 }
84+ if _, found := fields["primary_operation"]; !found {
85+ t.Fatalf("primary operation missing from %#v", fields)
86+ }
87+}
88+
89+func TestPublicSubagentStatusIncludesResultDeliveryFacts(t *testing.T) {
90+ entry := publicSubagentStatus(subagents.AgentSnapshot{ID: "agent-1"}, subagents.AgentTraceView{
91+ Result: &subagents.ResultFact{Available: true, Ref: "subagent://agent-1/result"},
92+ })
93+ if entry.Result == nil || entry.Result.State != "unknown" || entry.Result.Delivered || entry.Result.DeliveryFailed {
94+ t.Fatalf("result = %#v", entry.Result)
95+ }
96+ encoded, err := json.Marshal(entry)
97+ if err != nil {
98+ t.Fatal(err)
99+ }
100+ var fields map[string]json.RawMessage
101+ if err := json.Unmarshal(encoded, &fields); err != nil {
102+ t.Fatal(err)
103+ }
104+ var result map[string]any
105+ if err := json.Unmarshal(fields["result"], &result); err != nil {
106+ t.Fatal(err)
107+ }
108+ if result["delivered"] != false || result["delivery_failed"] != false {
109+ t.Fatalf("result JSON = %#v", result)
110+ }
83 }111 }
84 112
85 func TestSubagentStatusSchemaHasOptionalAgentID(t *testing.T) {113 func TestSubagentStatusSchemaHasOptionalAgentID(t *testing.T) {

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 1

Tags 476

↑↓ moveenter openesc close