From 9f6287025c288e2cd687d0bbecd3c8fb4867effa Mon Sep 17 00:00:00 2001 From: t Date: Fri, 21 Aug 2026 14:11:28 -0600 Subject: Keep subagent boards pinned while background workflows run A background subagents.lua workflow outlives the tool call that started it, so settling the outer result unpinned a board whose agents were still working. Track a per-tool-call workflow count and derive the pin from both the tool being active and that count, so the board only returns to transcript order once its last workflow finishes. Also show the reasoning level alongside the model on agent cards. --- spec/test_luatool.lua | 34 ++++++++++++++++++++++++++++++++++ spec/test_progress.lua | 30 ++++++++++++++++++++++++++++++ spec/test_progress_replay.lua | 2 +- 3 files changed, 65 insertions(+), 1 deletion(-) (limited to 'spec') diff --git a/spec/test_luatool.lua b/spec/test_luatool.lua index 509b804..e7f2294 100644 --- a/spec/test_luatool.lua +++ b/spec/test_luatool.lua @@ -4,6 +4,7 @@ local fake = require("spec.fake_ext") local jobs = require("subagents.jobs") local luatool = require("subagents.luatool") +local progress = require("subagents.progress") local workflow = require("subagents.workflow") local uv = require("luv") @@ -115,6 +116,39 @@ return { end) end }, + { "a background workflow keeps its completed tool entry pinned", function() + with_host(function(handle, profiles) + progress.reset() + progress.begin_live_turn() + local pins + progress.claim({ + id = "pinned-workflow-call", + tool_name = "subagents.lua", + set_component = function() + pins = {} + return { + alive = function() return true end, + invalidate = function() end, + set_pinned = function(_, value) pins[#pins + 1] = value end, + } + end, + }) + handle.queue_for("alpha", { output = "finished" }) + local id = luatool.handle({ source = [[ + return subagents.workflow(function(ctx) + return ctx:agent{name="work", agent="alpha", prompt="go"}:await().output + end) + ]] }, profiles, { tool_call_id = "pinned-workflow-call" }) + progress.settle({ id = "pinned-workflow-call" }) + assert(#pins == 1 and pins[1] == true, "the outer result must not unpin the running workflow") + + local record = pump(id) + assert(record.status == "completed", tostring(record.error)) + assert(#pins == 2 and pins[2] == false, "workflow completion restores transcript order") + progress.reset() + end) + end }, + { "a later Lua call inspects workflows and records are read-only", function() with_host(function(_, profiles) local id = luatool.handle({ source = [[ diff --git a/spec/test_progress.lua b/spec/test_progress.lua index 5788998..76cd289 100644 --- a/spec/test_progress.lua +++ b/spec/test_progress.lua @@ -76,6 +76,36 @@ return { assert(pins.b[2] == false, "reset safely releases unresolved boards") end }, + { "background workflows keep their board pinned after the outer tool settles", function() + progress.reset() + progress.begin_live_turn() + local component, pins + progress.claim({ + id = "background-call", + tool_name = "subagents.lua", + set_component = function(_, value) + component, pins = value, {} + return { + alive = function() return true end, + invalidate = function() end, + set_pinned = function(_, value) pins[#pins + 1] = value end, + } + end, + }) + + progress.bind({ tool_call_id = "background-call" }) + progress.card("worker", "child") + progress.workflow_started("background-call") + progress.settle({ id = "background-call" }) + progress.reset() + assert(#pins == 1 and pins[1] == true, "turn boundaries must not unpin a running workflow") + assert(component:render(80)[#component:render(80)] == "", "a running workflow retains its spacing") + + progress.workflow_finished("background-call") + assert(#pins == 2 and pins[2] == false, "the board unpins when its final workflow settles") + progress.reset() + end }, + { "a pinned board leaves one blank line before the waiting indicator", function() progress.reset() local component, pins diff --git a/spec/test_progress_replay.lua b/spec/test_progress_replay.lua index ffe7e5c..ca87657 100644 --- a/spec/test_progress_replay.lua +++ b/spec/test_progress_replay.lua @@ -94,7 +94,7 @@ return { local text = plain(component:render(120)) has(text, "✔ reviewer child-direct") has(text, "completed") - has(text, "↳ openai:test") + has(text, "↳ openai:test (high)") has(text, "prompt: inspect the change") has(text, "checking") has(text, "std.read [tool-1]") -- cgit v1.3