Repository navigation
Fix: Draw a background job as tools, not as waiting on a subagent #1617
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -411,8 +411,8 @@ func (v ivs) cover(lo, hi time.Time) time.Duration { | |
|
|
||
| // waits classifies the gaps between an agent's consecutive turns, and the one after its last. | ||
| // | ||
| // - Woken by a subagent's result: waiting on its subagents, as is any time one of them or one | ||
| // of its background jobs was running. | ||
| // - Woken by a subagent's result: waiting on its subagents, as is any time one of them was | ||
| // running. A background job is a tool still running, and is drawn as one (see build). | ||
| // - The next call re-sent the conversation unchanged: retrying, the whole gap. | ||
| // - After a failed call otherwise: retrying until the last failed request to a model host in | ||
| // the gap, then waiting on the user, who had to act. Claude Code retries a cut stream for a | ||
|
|
@@ -428,11 +428,6 @@ func waits(tl *timeline.Timeline, agent string, children []string, life map[stri | |
| kids = append(kids, l) | ||
| } | ||
| } | ||
| for _, j := range tl.Jobs { | ||
| if j.Agent == agent { | ||
| kids = append(kids, [2]time.Time{j.Start, tlEndOr(j.End, now)}) | ||
| } | ||
| } | ||
| woke := map[int64]bool{} | ||
| for _, r := range tl.Returns { | ||
| if r.Into == agent { | ||
|
|
@@ -567,6 +562,13 @@ func tlAgentRows(tl *timeline.Timeline, ax tlAxis, from, to, now time.Time) []tl | |
|
|
||
| build := func(a timeline.Agent, depth int) tlAgentRow { | ||
| m, t := newIvs(model[a.ID]), newIvs(tool[a.ID]) | ||
| var js [][2]time.Time | ||
| for _, j := range tl.Jobs { | ||
| if j.Agent == a.ID { | ||
| js = append(js, [2]time.Time{j.Start, tlEndOr(j.End, now)}) | ||
| } | ||
| } | ||
| jobs := newIvs(js) | ||
| retry, subs, user := waits(tl, a.ID, children[a.ID], life, now) | ||
| user = newIvs(append(user, asking[a.ID]...)) | ||
| row := tlAgentRow{agent: a, depth: depth, cells: make([]share, ax.width()), alive: make([]bool, ax.width()), | ||
|
|
@@ -595,7 +597,18 @@ func tlAgentRows(tl *timeline.Timeline, ax tlAxis, from, to, now time.Time) []tl | |
| left -= f | ||
| return f | ||
| } | ||
| row.cells[c] = share{tool: take(t), model: take(m), retry: take(retry), subs: take(subs), user: take(user)} | ||
| // A background job is a tool still running: it takes what the model and the | ||
| // foreground tools did not, so a job left running through a reply hides no model. | ||
| s := share{tool: take(t), model: take(m)} | ||
| job := take(jobs) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. suggestion: There's an unmentioned priority change here. A job used to sit in I checked this with That may be intended, since foreground tools also outrank retry. If so, a line in the comment/README and a test pinning it would help. If not, take |
||
| s.tool += job | ||
| // Only the marker of a collapsed gap is drawn; the cells either side share its span, | ||
| // so counting them would take the stretch off the column more than once. | ||
| if !cell.gap || cell.marker { | ||
| row.toolTime += time.Duration(job * span) | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: CodeRabbit's double-count point is real, but it's only the per-cell coarseness you already describe under Known limit. |
||
| } | ||
| s.retry, s.subs, s.user = take(retry), take(subs), take(user) | ||
| row.cells[c] = s | ||
| } | ||
| return row | ||
| } | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Script executed:
rg -n 'run_in_background|BackgroundJob|\.Jobs|JobEnd' cmd/agentopRepository: rossoctl/cortex
Length of output: 750
Script executed:
Repository: rossoctl/cortex
Length of output: 18347
Script executed:
Repository: rossoctl/cortex
Length of output: 12827
Script executed:
Repository: rossoctl/cortex
Length of output: 1153
Include background-job ends in subagent row bounds when the job outlives the agent's last completed turn.
A background job can end after its subagent's last completed turn and tool. The launch tool closes when the job starts, so it does not cover the job's tail. When the job end comes from a stranded failure, the row's
lifebounds stop at that completed turn. The job still adds coverage totoolTime, so the row and the total can disagree. An open turn with no end is drawn tonow, so this case does not clip the row.Extend only the drawing bounds for that agent. Leave the
lifemap unchanged, becausewaitsreadslife[k]for child agents.Suggested fix