fix(scanner): name background subagent dispatches instead of 'unknown' - #176
Open
retog wants to merge 1 commit into
Open
fix(scanner): name background subagent dispatches instead of 'unknown'#176retog wants to merge 1 commit into
retog wants to merge 1 commit into
Conversation
The scanner learned a subagent's name only from the parent's closing toolUseResult, which carries agentType only when the dispatch ran synchronously and completed. A background (isAsync) dispatch logs status: 'async_launched' with no agentType at all, so those agents got no row in the agents table and rendered as 'unknown' in Top Subagent Dispatches and the by-type breakdown. Every assistant record inside the subagent's own jsonl carries attributionAgent alongside agentId, so the name is recoverable regardless of how the dispatch was launched or whether it ever finished. Read it there too, as a name-only record; the parent's dispatch record stays the source for stats and status and its agentType is never clobbered. Includes a one-time backfill gated on schema_meta, mirroring the existing topic backfill, so existing databases pick the names up without a full rescan: already-processed transcripts are re-read for attributionAgent records only, leaving turns untouched so token totals cannot drift. On a real 2123-file transcript tree this named all 134 dispatches, of which only 40 were reachable from a parent agentType.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
Most of my subagent dispatches show as
unknownin Top Subagent Dispatches and in the by-type breakdown, even though the transcripts clearly identify them.extract_agent_dispatchlearns an agent's name from the parent's closingtoolUseResult, which carriesagentTypeonly when the dispatch ran synchronously and completed. A background (isAsync) dispatch logs a launch record instead:{"isAsync": true, "status": "async_launched", "agentId": "a777b13086edbcf85", "description": "...", "resolvedModel": "claude-sonnet-5", "prompt": "..."}No
agentType. So those agents never get a row inagents, theLEFT JOINmisses, andAGENT_TYPE_EXPRfalls through to'unknown'.Across my transcript tree (2,123 files), of 120 parent records carrying an
agentId:agentTypecompletedasync_launchedTwo thirds of dispatches were unnameable from the parent side alone.
The fix
Every assistant record inside the subagent's own jsonl carries
attributionAgentalongsideagentId— the same human-readable name (general-purpose,Explore, plugin agents likehealth:coach). It is present regardless of how the dispatch was launched or whether it ever finished, so it recovers the name in exactly the cases the parent record can't.extract_agent_attribution()returns a name-only record, taggedsource: "attribution".upsert_agents()now routes bysource: full dispatch records overwrite every column as before; attribution records useCOALESCE(NULLIF(agents.agent_type, ''), excluded.agent_type)so they never clobber a name learned from a parentagentType. Dispatches are applied first, so the outcome doesn't depend on file scan order.parse_jsonl_fileand the incremental tail parser inscan()).Backfill
Files already in
processed_filesare skipped by an incremental scan, so existing databases would keep showingunknownindefinitely._backfill_agent_types()mirrors the existing_backfill_topics()exactly — gated on aschema_metamarker (agent_type_backfill_done), re-reads onlyattributionAgentrecords with a cheap substring prefilter, and leavesturnsuntouched so token totals cannot drift.Verification
On a real 2,123-file transcript tree, before → after:
All 134 dispatches named, zero
unknown— where only 40 were reachable from a parentagentType.The backfill path was exercised on a simulated pre-fix database: 40 → 134 named agents with 0 new/updated files (2,122 skipped), confirming it works without a rescan.
Tests
6 new tests in
tests/test_subagent.py(TestAgentAttribution), covering extraction, the both-fields-required guard, parse capture, dispatch-name-wins precedence, the async end-to-end case, and the backfill on an already-scanned DB. Full suite: 153 passed.Versioning
Added a
## v1.5.6 — TBDheading with the CHANGELOG bullets and bumpedscanner.VERSION+vscode-extension/package.jsonin lockstep, sincetests/test_version.pyenforces parity. Happy to change the bump level or drop the heading entirely if you'd rather write it yourself at release time.Written with an agent, reviewed by Reto.