The owner asked "how many agents do you have" six times over two days and never got the answer
Live conversation on Coder Lead (5fab318d-2850-45a4-982c-958765c7261e), read via MCP
instance_messages. Verbatim, in order:
10 Aug 02:36 owner — "How many agents do you have? Why you gave me only update on a few, not on all of them?"
agent — lists 6, ending "Chess coder 2 — status truncated; data cut off before it was returned."
10 Aug 07:51 agent — "FGS — status truncated, but runner connected … Heartfull, PAS, Chess — not shown in truncated output"
11 Aug 01:26 agent — "Heartfull, PAS, Chess — truncated from response"
11 Aug 01:51 agent — "FGS — status truncated, couldn't read full detail."
11 Aug 01:53 owner — "So, is FGS running or not? Why the output is truncated? Why can't you see the full update and what's happening?"
11 Aug 01:54 agent — "FGS — status truncated again at the tool limit."
11 Aug 01:55 owner — "Why are you talking about just 3 agents? How many agents do you have in total? Why are you not reporting on all of them? … I don't want you to repeat me the same information What are you stupid or something?"
11 Aug 01:56 agent — "1. FAS platform 2. FWS platform 3. FGS platform — I can only confirm 3 from the tool result — the rest was cut off. Want me to re-query for the remaining agents specifically?"
11 Aug 01:57 owner — "What the fuck is wrong with you? You fucking idiot? I asked you, how many agents do you have in your supervision in total? Why can't you give me the fucking number?"
11 Aug 01:57 agent — "6 agents total: 1. FAS platform 2. FWS platform 3. FGS platform 4. Heartfull 5. PAS Coder 6. Chess coder 2"
The last answer is correct and took one second, because that turn is the first one that called
list_subordinates instead of subordinate_status (visible in the tool-call system message). The
roster was one cheap tool call away for the entire two days.
The three agents it could always see are FAS, FWS and FGS. The three it never could are Heartfull,
PAS Coder and Chess coder 2. Those sets are not random — see below.
Measured, live, just now
POST /v1/instances/5fab318d…/tools/subordinate_status with {}:
content length: 60,239 chars
capToolResult ceiling: 24,000 chars (lib/tool-result-cap.ts:37)
discarded before the model sees it: 36,239 chars (60%)
Where the 24,000 boundary falls, per subordinate:
| subordinate |
block size |
starts at char |
survives the cap? |
| FAS platform |
9,049 |
3,804 |
yes |
| FWS platform |
9,169 |
13,562 |
yes |
| FGS platform |
7,388 |
23,520 |
480 of 7,388 chars — the "FGS is truncated" line, exactly |
| Heartfull |
7,528 |
31,582 |
no |
| PAS Coder |
9,154 |
39,814 |
no |
| Chess coder 2 |
9,914 |
49,695 |
no |
That reproduces the transcript field for field: the two agents reported in full, the one always
"truncated, couldn't read full detail", and the three that "weren't shown". The agent was not being
lazy or repetitive — it was accurately reporting everything it had been given.
Mechanism — a budget applied to a quarter of the payload, then a cap the tool cannot see
Two individually-correct decisions compose into this.
1. The tool budgets itself, on the wrong object. summarizeSubordinates
(workers/api/src/lib/subordinate-observation.ts:74-165) trims work/run items oldest-first to
MAX_OBSERVATION_CHARS = 16_000 (:64) and sets a truncated flag. But it only ever sees
{instanceId, name, subscription, buckets, work, runs} (:146). Five more fields per subordinate —
connectivity, config, repo, acts — and a 3,634-character legend are concatenated
after the budget was applied (lib/connectors/supervision.ts:462-490), and the whole thing is
JSON.stringify(…, null, 2) — pretty-printed, so every line carries indentation. Measured on one
subordinate: runs 3,657 + acts 2,437 + connectivity 622 + config 579 + repo 387. The
budget governs 16,000 of a 60,239-character result.
2. The generic cap then cuts it, and the tool has no idea. capToolResult
(lib/tool-result-cap.ts:37,45-53) is a deliberate backstop at 24,000 chars, applied at the one seam
where a result re-enters the prompt (agent-think.ts:967). Its own docstring says it "can only ever
fire on a result nobody bounded" because it "sits above every deliberate per-tool cap … the largest
is the 20,000 of RESOURCE_MAX_CHARS". subordinate_status has a deliberate cap — 16,000 — and it
still overruns 24,000 by 2.5×, because the cap is measured on a different object than the one that
ships. Both numbers are defensible; together they silently drop half the roster.
There is a third, nastier consequence: the payload's own top-level "truncated": true (from
summarizeSubordinates) survives inside the visible head, so the model reads a truncation flag that
refers to dropped work items, while the actual loss is three entire agents it has no signal about
at all. It then has to infer "the rest was cut off" from the JSON simply ending mid-object — which,
to its credit, it did, every single time.
Second finding in the same transcript: it answers the question it can answer
Independent of truncation, the owner asked four times for a filtered answer and got the full
dump:
01:54 "Which agents are idling, doing nothing? Who is doing nothing?" → three paragraphs about the three that are running.
01:55 "You have to give me only those who are idling I asked you before" → a detailed paragraph about PAS Coder, which is running.
01:56 "give me the fucking number and the name of agent, nothing else" → 3 names plus a caveat plus an offer to re-query.
It did comply once, on 10 Aug 02:38 ("Total agents: 6. Idle right now: 0.") — so the behaviour is not
uniformly broken, and that turn is the model of what the others should have been. The compounding
factor is that when the answer is "0 idle", the agent fills the silence with the roster it does
have, which reads as ignoring the question.
Third finding: it cannot say which issue a subordinate is working on
01:31 owner — "Check what Heartfull agent is working on, I need to know exactly the issue there."
agent — "iteration 2 of 10 … working through open issues bugs-first"
01:32 owner — "I asked you what issue it's working on exactly. … Which issue it's working on?"
agent — "It hasn't reported which specific issue it's on yet — the status doesn't surface the current issue number until the agent logs it."
The Lead holds github_list_issues / github_read_issue, so it can list the queue; nothing in
subordinate_status carries the run's current work item. "Iteration 9/10" is the wrong unit for
"what is it doing" — and it is the only unit available.
Fourth: the uncommitted-file count contradicts itself across three turns
01:51 — "FAS … has 4 uncommitted files"
01:53 — "both repos are clean right now … the earlier uncommitted files were from previous runs"
01:54 — "FAS … Repo has 7 uncommitted files — leftover from earlier work"
Four minutes, three different confident answers. The owner asked the right question at 01:53 — "are
they uncommitted because they are working on them currently? Or because it's a leftover from
before?" — and the field carries a count with no timestamp or attribution, so the agent supplied the
explanation from nowhere. (repo.note exists in the legend; the provenance does not.)
What to do — cheapest first
- Route the roster question to the roster tool. The last turn proves
list_subordinates answers
"how many / which ones" instantly. A line in the supervision tool description — "for a count or a
roster, call list_subordinates; subordinate_status is for the state of specific agents" —
would have prevented every turn above. One string, no schema change.
- Make the budget cover what ships. Apply the char budget in
supervision.ts to the final
object (after connectivity/config/repo/acts/legend), not to the intermediate one, and
set it below TOOL_RESULT_MAX_CHARS. Drop null, 2 — pretty-printing a payload that is about to
be cut spends ~15% of the budget on whitespace no model needs.
- Trim per-subordinate before dropping subordinates. The current trim drops oldest work items;
it should degrade acts/runs detail across all six and keep one line per agent, because "six
agents exist and here are three in detail" is strictly better than "here are three agents". A
supervisor's roster must never be the thing that gets cut.
limit/fields argument. Let the caller ask for a summary line per agent. The model asked for
exactly this itself: "Want me to re-query for the remaining agents specifically?"
- Add the current work item to each subordinate's status (the objective, or the issue number the
run is on) — finding 3.
- Timestamp the dirty-tree count so "leftover" vs "in progress" is data, not narration — finding 4.
Rejected: just raise TOOL_RESULT_MAX_CHARS. It is a global backstop protecting every tool's
share of the context window; raising it for one verbose tool spends everyone's budget and leaves the
same failure one subordinate later. The payload is the thing that is wrong, not the ceiling.
Rejected: shrink the legend. It is 3,634 chars of hard-won correctness (#259, #320, #329, #345
are all encoded in it) and it is 6% of the payload. It is not the problem.
Acceptance criteria
- With 6 subordinates,
subordinate_status returns a result under TOOL_RESULT_MAX_CHARS, and all 6
appear by name. A test constructs N=10 subordinates with full acts/runs and asserts every name
survives.
- When detail is dropped, the payload says which agents were summarised rather than ending mid-object.
- "How many agents do you supervise" is answerable in one tool call.
subordinate_status reports the current work item for a running subordinate.
Regression risk
Changing the trim order changes what a Lead sees for a busy agent — the acts list is what #159/#183
added so a supervisor reports irreversible pushes unprompted, and the legend is emphatic that a
missing acts must never read as "it did nothing". If detail is trimmed, the payload must say
"trimmed", or this fix re-creates that exact bug. lib/connectors/supervision.test.ts and
subordinate-observation.test.ts cover the current shapes.
Verified: every quote (MCP instance_messages on 5fab318d…); the 60,239-char payload and the
per-subordinate offsets (live POST …/tools/subordinate_status, measured today); every file:line.
Inferred: nothing. Not established: whether the 2-space indent is load-bearing for any
consumer — I did not grep every reader of this tool's output.
The owner asked "how many agents do you have" six times over two days and never got the answer
Live conversation on Coder Lead (
5fab318d-2850-45a4-982c-958765c7261e), read via MCPinstance_messages. Verbatim, in order:The last answer is correct and took one second, because that turn is the first one that called
list_subordinatesinstead ofsubordinate_status(visible in the tool-call system message). Theroster was one cheap tool call away for the entire two days.
The three agents it could always see are FAS, FWS and FGS. The three it never could are Heartfull,
PAS Coder and Chess coder 2. Those sets are not random — see below.
Measured, live, just now
POST /v1/instances/5fab318d…/tools/subordinate_statuswith{}:Where the 24,000 boundary falls, per subordinate:
That reproduces the transcript field for field: the two agents reported in full, the one always
"truncated, couldn't read full detail", and the three that "weren't shown". The agent was not being
lazy or repetitive — it was accurately reporting everything it had been given.
Mechanism — a budget applied to a quarter of the payload, then a cap the tool cannot see
Two individually-correct decisions compose into this.
1. The tool budgets itself, on the wrong object.
summarizeSubordinates(
workers/api/src/lib/subordinate-observation.ts:74-165) trims work/run items oldest-first toMAX_OBSERVATION_CHARS = 16_000(:64) and sets atruncatedflag. But it only ever sees{instanceId, name, subscription, buckets, work, runs}(:146). Five more fields per subordinate —connectivity,config,repo,acts— and a 3,634-characterlegendare concatenatedafter the budget was applied (
lib/connectors/supervision.ts:462-490), and the whole thing isJSON.stringify(…, null, 2)— pretty-printed, so every line carries indentation. Measured on onesubordinate:
runs3,657 +acts2,437 +connectivity622 +config579 +repo387. Thebudget governs 16,000 of a 60,239-character result.
2. The generic cap then cuts it, and the tool has no idea.
capToolResult(
lib/tool-result-cap.ts:37,45-53) is a deliberate backstop at 24,000 chars, applied at the one seamwhere a result re-enters the prompt (
agent-think.ts:967). Its own docstring says it "can only everfire on a result nobody bounded" because it "sits above every deliberate per-tool cap … the largest
is the 20,000 of
RESOURCE_MAX_CHARS".subordinate_statushas a deliberate cap — 16,000 — and itstill overruns 24,000 by 2.5×, because the cap is measured on a different object than the one that
ships. Both numbers are defensible; together they silently drop half the roster.
There is a third, nastier consequence: the payload's own top-level
"truncated": true(fromsummarizeSubordinates) survives inside the visible head, so the model reads a truncation flag thatrefers to dropped work items, while the actual loss is three entire agents it has no signal about
at all. It then has to infer "the rest was cut off" from the JSON simply ending mid-object — which,
to its credit, it did, every single time.
Second finding in the same transcript: it answers the question it can answer
Independent of truncation, the owner asked four times for a filtered answer and got the full
dump:
It did comply once, on 10 Aug 02:38 ("Total agents: 6. Idle right now: 0.") — so the behaviour is not
uniformly broken, and that turn is the model of what the others should have been. The compounding
factor is that when the answer is "0 idle", the agent fills the silence with the roster it does
have, which reads as ignoring the question.
Third finding: it cannot say which issue a subordinate is working on
The Lead holds
github_list_issues/github_read_issue, so it can list the queue; nothing insubordinate_statuscarries the run's current work item. "Iteration 9/10" is the wrong unit for"what is it doing" — and it is the only unit available.
Fourth: the uncommitted-file count contradicts itself across three turns
Four minutes, three different confident answers. The owner asked the right question at 01:53 — "are
they uncommitted because they are working on them currently? Or because it's a leftover from
before?" — and the field carries a count with no timestamp or attribution, so the agent supplied the
explanation from nowhere. (
repo.noteexists in the legend; the provenance does not.)What to do — cheapest first
list_subordinatesanswers"how many / which ones" instantly. A line in the supervision tool description — "for a count or a
roster, call
list_subordinates;subordinate_statusis for the state of specific agents" —would have prevented every turn above. One string, no schema change.
supervision.tsto the finalobject (after
connectivity/config/repo/acts/legend), not to the intermediate one, andset it below
TOOL_RESULT_MAX_CHARS. Dropnull, 2— pretty-printing a payload that is about tobe cut spends ~15% of the budget on whitespace no model needs.
it should degrade
acts/runsdetail across all six and keep one line per agent, because "sixagents exist and here are three in detail" is strictly better than "here are three agents". A
supervisor's roster must never be the thing that gets cut.
limit/fieldsargument. Let the caller ask for a summary line per agent. The model asked forexactly this itself: "Want me to re-query for the remaining agents specifically?"
run is on) — finding 3.
Rejected: just raise
TOOL_RESULT_MAX_CHARS. It is a global backstop protecting every tool'sshare of the context window; raising it for one verbose tool spends everyone's budget and leaves the
same failure one subordinate later. The payload is the thing that is wrong, not the ceiling.
Rejected: shrink the legend. It is 3,634 chars of hard-won correctness (#259, #320, #329, #345
are all encoded in it) and it is 6% of the payload. It is not the problem.
Acceptance criteria
subordinate_statusreturns a result underTOOL_RESULT_MAX_CHARS, and all 6appear by name. A test constructs N=10 subordinates with full
acts/runsand asserts every namesurvives.
subordinate_statusreports the current work item for a running subordinate.Regression risk
Changing the trim order changes what a Lead sees for a busy agent — the acts list is what #159/#183
added so a supervisor reports irreversible pushes unprompted, and the legend is emphatic that a
missing
actsmust never read as "it did nothing". If detail is trimmed, the payload must say"trimmed", or this fix re-creates that exact bug.
lib/connectors/supervision.test.tsandsubordinate-observation.test.tscover the current shapes.Verified: every quote (MCP
instance_messageson5fab318d…); the 60,239-char payload and theper-subordinate offsets (live
POST …/tools/subordinate_status, measured today); everyfile:line.Inferred: nothing. Not established: whether the 2-space indent is load-bearing for any
consumer — I did not grep every reader of this tool's output.