Capture Claude Code Background Subagents

can you take a look:
<pasted_content id="edc3"> Claude Code background subagents are not captured into checkpoints
Symptom: in Claude Code 2.1.288 sessions, checkpoints have no tasks/<tool_use_id>/ entry: no agent-<id>.jsonl and no task.json. This holds even though the subagent ran, edited files, and Claude wrote its transcript to ~/.claude/projects/<slug>/<session>/subagents/agent-<id>.jsonl.
Cause: Entire classifies the launch as foreground, but Claude actually ran the subagent in the background. Two ways this happens:
- run_in_background is a string. isBackgroundLaunch (cmd/entire/cli/hooks.go:64) unmarshals tool_input into a struct with a bool field. The model sent "run_in_background":"true". json.Unmarshal fails, the failure is logged at Debug only, and the function returns false.
- The parameter is missing, but the launch is async anyway. In 2.1.288 the Agent tool ran async with no run_in_background at all. The tool result reads "Async agent launched successfully". tool_input has nothing to detect.
What happens next, on the foreground path:
- completeSubagentTaskRecord runs at post-task time. The subagent has only just started, so no files have changed yet.
- lifecycle.go:1972 logs "no file changes detected, skipping task record" and cleans up the pre-task state. bypassNoChangesSkip is never set on this path.
- When SubagentStop arrives there's no in-flight marker, so lifecycle.go:1698 warns "no in-flight marker for completed subagent — foreground dedup, …" and drops the event.
Evidence (the agent-scenarios repo):
┌─────────────────────────────────────────────┬──────────────────────────────┬────────────────────────────────┬──────────────────────────────────────────────────────────────────┐ │ Run │ tool_input.run_in_background │ Tool result │ Checkpoint │ ├─────────────────────────────────────────────┼──────────────────────────────┼────────────────────────────────┼──────────────────────────────────────────────────────────────────┤ │ run/single-subagent/claude-code/2.1.288 │ absent │ Async agent launched │ 01M40SBCJC7NZA2MW5DT3SYPK2: no tasks/ │ │ │ │ successfully │ │ ├─────────────────────────────────────────────┼──────────────────────────────┼────────────────────────────────┼──────────────────────────────────────────────────────────────────┤ │ run/straddle-checkpoint/claude-code/2.1.288 │ "true" (string) │ Async agent launched │ 01M40T34RQCNGMDE9XCBZK94JX and 01M40T3GZ19VQQWHN6GD39FNEN: no │ │ │ │ successfully │ tasks/ │ └─────────────────────────────────────────────┴──────────────────────────────┴────────────────────────────────┴──────────────────────────────────────────────────────────────────┘
The same warning pair ("subagent-stop payload missing tool_use_id or session_id" followed by "no in-flight marker for completed subagent") shows up in agent-scenarios/.entire/logs/entire.log for this session's own background subagents.
Side effects of the same misclassification:
- Attribution: the subagent's edits are credited to the human: user_lines_added: 36 in single-subagent, 2 in straddle, with 0 agent lines. I haven't confirmed this comes from the missing task record, but it's likely related. Codex, which does get a task record, credits the agent correctly (agent_lines: 40).
Suggested fix:
- Accept a string "true" (for example a custom unmarshaller, or parse into any). Also raise the parse-failure log above Debug.
- Decide background versus foreground from tool_response, not just tool_input. An async launch returns immediately with an agentId and the "Async agent launched" text. Treat that as background and record the in-flight marker.
- Defensive option: let SubagentStop complete a record without an in-flight marker when no task record exists for that tool_use_id, the way CompletionWithoutLaunch does. A misclassified launch would then still get captured at stop time.
To check the fix: re-record both Claude scenarios. Each checkpoint should then contain tasks/<tool_use_id>/agent-<id>.jsonl and task.json:
./record.sh single-subagent claude-code ./record.sh straddle-checkpoint claude-code
Each re-recording gets a new -2 branch, and the old runs stay as the before-fix data.
Related: Codex subagent transcript is always "unresolvable"
Symptom: checkpoint 01M414VXYH4236W6PAGSV7QT0F (run/single-subagent/codex/0.159.3) contains tasks/<agent_id>/task.json with "transcript_unavailable_reason": "transcript path unresolvable" and no agent-*.jsonl. The rollout does exist: ~/.codex/sessions/2026/10/03/rollout-…-01a1024d-83c0-78f2-9d06-1d122de7dab4.jsonl.
Cause:
- DeclaredTranscriptPath is only set after the inventory refresh confirms the rollout belongs to that agent, or at SessionEnd (lifecycle.go:1346-1360 and :1302).
- Codex SubagentStop records only into the SubagentInventory, not the TaskRecord (:1557-1582).
- The fallback resolveTaskTranscriptPath (strategy/manual_commit_condensation.go:281-294) only knows Claude's directory layout.
- So condensation writes the reason-only task.json at manual_commit_condensation.go:396-399. That happened here with an ordinary commit made after the subagent had finished, not only with a commit made while a subagent is still running.
Suggested fix: run the inventory check, or copy the inventory's resolved path onto the TaskRecord, before condensation materializes task records. Alternatively, let the fallback look up Codex rollouts via session_meta.id. </pasted_content id="edc3">
The report's diagnosis checks out, but its fix wouldn't work on its own. Fixing launch detection (suggestions 1 and 2) would still capture nothing, because Claude's SubagentStop event in 2.1.288 doesn't include a tool_use_id, so the stop handler can't find the record it's supposed to finish.
What I confirmed
The misclassification is real. isBackgroundLaunch (cmd/entire/cli/hooks.go:65) only reads a boolean run_in_background from tool_input. If it's absent or is the string "true", the launch is treated as foreground. The launch is then finished immediately by completeSubagentTaskRecord, which skips at lifecycle.go:1972 because nothing has changed yet. That matches the report.
There's a better signal than tool_input. The Claude transcripts for both runs record the async launch result as:
So suggestion 2 is the right one. Check tool_response.isAsync (or status == "async_launched"), not the text "Async agent launched successfully". postToolHookInputRaw (agent/claudecode/types.go:66) currently parses only agentId from tool_response, so isAsync needs adding there.
What the report missed
The warning it calls "the same warning pair" is the bigger problem. In agent-scenarios/.entire/logs/entire.log, every SubagentStop logs has_tool_use_id:false, and the warning that follows logs tool_use_id:"". handleSubagentStopFinal looks up the in-flight record with state.FindTaskRecord(event.ToolUseID) (lifecycle.go:1678), and that lookup matches on ToolUseID only (session/state.go:677).
So even if the launch were correctly classified as background and the record were saved, SubagentStop would look for "", miss it, and drop the event. The completion step CompleteTaskRecord is also keyed by tool_use_id. The payload does include agent_id, and it matches the agentId in the launch result (for example a0ea1ded02cddc066 appears in both).
The fix needs three parts:
- Detect background launches from
tool_response.isAsync. Keep readingtool_input, but accept bothtrueand"true"there. - In
handleSubagentStopFinal, whenToolUseIDis empty, look up the record byAgentID. - Then fill in
event.ToolUseIDfrom that record, so the completion and thetasks/<tool_use_id>/path line up.
I'd push back on suggestion 3 (completing without a record, like CompletionWithoutLaunch does). With no tool_use_id it would write into tasks//. It would also capture a second time any foreground subagent that was already recorded at launch. Parts 1 and 2 above make it unnecessary.
One more thing: the has_tool_use_id check is described in a comment as "structurally impossible for a well-formed payload". That's no longer true, and the comment should change along with the code.
Codex part
The code matches the report. Codex's provisional stop (lifecycle.go:1557-1582) writes only to SubagentInventory. DeclaredTranscriptPath only reaches the task record through the inventory refresh or SessionEnd (around :1302 and :1346). resolveTaskTranscriptPath (manual_commit_condensation.go:281) knows only Claude's directory layout. Of the two suggested fixes, I'd copy the inventory's resolved path onto the matching task record before condensation. Teaching the strategy package to look up Codex rollouts by session_meta.id would add Codex-specific logic to a package that currently copies Claude's layout only because of an import cycle. I didn't reproduce this checkpoint myself; this part is from reading the code.
Attribution
I didn't check whether the attribution numbers come from the missing record, so that link is still unconfirmed.
I saved the missing-tool_use_id finding to memory. Next I can write the fix (TDD: a SubagentStop fixture without tool_use_id, plus an async tool_response launch) and re-record the two Claude scenarios.
Because the hook that fires when the Agent tool returns (PostToolUse) means different things in the two cases, and the code depends on that timing.
Foreground: the parent waits until the subagent finishes, so PostToolUse fires after the subagent's work is done. That makes it a safe moment to capture. Nothing else is editing while the parent waits, so every worktree change since the pre-task snapshot belongs to the subagent. The foreground path therefore diffs the whole worktree (DetectFileChanges) and records the task right away.
Background: PostToolUse fires a few seconds after launch, when the subagent has done nothing yet. Capturing then would record an empty task. That's exactly what's happening in this bug. So faa9a540df (2026-08-17) added the split. On a background launch, Entire records an in-flight marker and waits for SubagentStop, the first event that comes after the real work. The background path also differs in two other ways:
- It uses only the files the subagent says it changed (
analyzerFilesOnly). The parent keeps working at the same time, so a worktree-wide diff would credit the parent's edits to the subagent. - It always writes a record (
bypassNoChangesSkip), even for a read-only subagent, because SubagentStop is its only chance to save the transcript. The foreground path still drops read-only subagents.
Could we drop the split?
Partly. The foreground path is the only one most agents have. Cursor, Factory Droid and Vogon send only a launch-time SubagentEnd with no completion event. Codex goes through its own provisional-stop and inventory handling. Only Claude Code (claudecode/lifecycle.go:247) and Copilot CLI (copilotcli/lifecycle.go:227) send a final completion event. So for most agents, "capture when the tool returns" has to stay.
For Claude, the code suggests SubagentStop fires for foreground subagents too. The "foreground dedup" in that warning is exactly that duplicate stop event being dropped. I haven't checked that against a real payload. If it holds, Claude could skip the launch-time classification and always capture at SubagentStop:
- Record a marker for every launch, keyed by tool_use_id and agentId, and complete it at SubagentStop. Misclassification would then stop mattering, and read-only foreground subagents would be captured too.
- Keep the worktree-wide diff for foreground launches. A background launch can't safely use it, so at that point it's an optional extra, not a separate capture path.
That's a bigger change than the bug fix. The tool_use_id gap applies either way: completion would have to look records up by agent_id. Before choosing it, I'd confirm from a real foreground run's log that SubagentStop fires and includes agent_id.
SubagentStop does fire for foreground subagents too. But my "capture everything at SubagentStop" idea doesn't work, because in foreground mode SubagentStop arrives before the event that links it to a tool_use_id.
What I ran
In a throwaway repo I hooked every subagent-related event with a script that only dumps the payload, then ran claude -p (2.1.288, Haiku). One run launched the subagent in the foreground and one in the background. Order of events:
Foreground (run_in_background: false):
Background (run_in_background: true):
What that means
- SubagentStop never includes
tool_use_id, in either mode. The "structurally impossible" check is wrong for every Claude subagent. The "no in-flight marker" warning also fires for every foreground subagent, so it's noise. - PostToolUse is the only event that carries both IDs. In foreground mode it comes after SubagentStop, so the stop handler doesn't know the
tool_use_idyet. Matching PreToolUse to SubagentStart by order instead would break as soon as two subagents launch in parallel. So the foreground path stays where it is, at PostToolUse, which also fires once the subagent has finished. tool_response.statusis the reliable way to tell the two apart. It's"completed"for foreground and"async_launched"for background, whatever the model put intool_input. In my local history, all 316 Agent launches (2.1.221 to 2.1.288) were async.run_in_backgroundwas absent in 310 and the string"true"in 5, never the booleanisBackgroundLaunchchecks for. So on my machine the background path has effectively never run on real Claude data.
Revised fix
- At PostToolUse, classify by
tool_response.status, nottool_input."async_launched"records an in-flight marker withAgentIDfromtool_response.agentId."completed"captures right away, as foreground does today. For other agents, or a missing status, keep today'stool_inputparsing. - At SubagentStop, look up the marker by
agent_idand copy itsToolUseIDonto the event before completing the record. - Lower the two warnings to Debug for Claude and fix their comments. Missing
tool_use_idis normal, and finding no marker at SubagentStop is expected for foreground subagents.
Separately, the foreground path still drops read-only subagents because of the no-changes check. A "completed" status is reliable enough to record those too, but that's a product decision, so I've left it out of the bug fix.
I saved the payload dumps in the scratchpad (fgprobe/) and updated the memory note with the event order. Do you want me to implement this, with tests built from these real payloads?
I built the tests first, then the fix. Everything passes except two failures that have nothing to do with this change. Nothing is committed yet.
Tests (written first)
The test payloads are copied from the real Claude Code 2.1.288 hooks I captured earlier.
- Payload parsing (
agent/claudecode/lifecycle_test.go): an async launch, a foreground completion, a"completed"status overridingrun_in_background: true, and a response with no status, which stays unknown. Also SubagentStop without atool_use_id. TestIsBackgroundLaunch(hooks_test.go):"true"and"false"as strings, an unrecognized value, and the launch mode Claude reports taking priority in both directions.FindTaskRecordByAgentID(session/state_test.go): an empty ID never matches.- Lifecycle (
lifecycle_test.go):- A launch Claude reports as background, with no
run_in_backgroundin its input, records an in-flight marker. - A launch Claude reports as foreground completes immediately, even with
run_in_background: true. - A SubagentStop without
tool_use_idcompletes the marker it finds by agent ID. - A SubagentStop that matches no marker creates nothing, so there's no
tasks//.
- A launch Claude reports as background, with no
- Integration (
integration_test/subagent_checkpoints_test.go): both real event orders, sent through the actual hook binary.- Background: the subagent's transcript ends up under
tasks/<tool_use_id>/in the checkpoint. This test failed before the fix with "must leave an in-flight marker". - Foreground: SubagentStop arrives first, then PostToolUse with
"completed". This one already passed on the old code. It stops a later change from treating the string"true"as background without checking the response status.
- Background: the subagent's transcript ends up under
Fix
agent/event.go: newEvent.SubagentLaunchfield: Unknown, Foreground or Background.agent/claudecode: parsestool_response.statusandisAsync."async_launched"orisAsyncmeans Background;"completed"means Foreground. The SubagentStop warning now fires only whensession_idis missing, since a missingtool_use_idis normal.hooks.go:isBackgroundLaunchnow takes the event. The mode Claude reports wins. Otherwise it falls back torun_in_background, accepting a boolean or a boolean string. An unrecognized value is logged as a Warn instead of Debug.lifecycle.go: when a SubagentStop has noToolUseID, it finds the record by agent ID and uses that record'sToolUseID. The "no in-flight marker" warning is skipped for events without aToolUseID, because that's every foreground Claude subagent.
Verification
Format, lint (0 issues), integration tests, and the canary (Vogon and roger-roger) all pass. mise run check still exits non-zero because of two unrelated failures:
TestProbeConfigDir_AsksClaudestarts the realclaudebinary and timed out after 5s under full-suite load. It passes when run alone.TestOpenCodeSeedRepoPlantsDepsis the known local failure when the OpenCode deps bootstrap hasn't run.
Not done
- Re-recording the scenarios. I haven't re-recorded the two
agent-scenariosruns with real Claude. That's the true end-to-end check, and it costs a little in API calls. - Codex "transcript path unresolvable". That half of the report isn't touched; it's a separate change.
- Unrealistic test payloads. The existing
SimulateSubagentStophelper still sends atool_use_id, and the existing foreground test sends the stop after PostToolUse, which is the reverse of the real order. Those tests still pass but don't match real payloads.
Do you want me to commit and open a PR, re-record the scenarios first, or both?
The integration test harness now sends the hook payloads Claude Code really sends. Seven existing background-subagent tests fail on the old code and pass with the fix. Before, they all passed on the old code, because the payloads they sent hid the bug.
What changed in the tests
The integration test harness (integration_test/hooks.go):
SubagentStopInputno longer has aToolUseIDfield, because Claude never sends one in that event. Every SubagentStop in the suite now has to be matched by agent ID.PostTaskInput.RunInBackgroundis nowBackground. It controlstool_response:"async_launched"withisAsync: truefor background,"completed"for foreground.tool_inputno longer carriesrun_in_backgroundin either case.- Updated the callers in
read_only_session_test.go,subagent_commit_in_turn_test.goandsubagent_checkpoints_test.go.
TestSubagentCheckpoints_ForegroundDoubleFire_CapturesOnce now uses the real foreground order:
- SubagentStop arrives first and creates no record.
- PostToolUse with
"completed"captures the subagent. - A repeated SubagentStop finds the finished record by agent ID and changes nothing: no new commit, and the record is completed only once.
Claude parser unit tests: the SubagentStop payloads no longer include tool_use_id, and the PostToolUse fixture now includes a status.
Removed duplicates: I dropped the two raw-payload integration tests I added in the last round, plus their helper. The rewritten harness tests cover the same thing. The "true"-string case is still covered by unit tests in the parser, isBackgroundLaunch, and the lifecycle.
Verification
I temporarily reverted only the non-test code. Seven integration tests then failed:
- the background-capture test
- turn end followed by SubagentStop
- two review-session tests
- three commit-linking tests
With the fix re-applied, the full integration suite, the unit suites and lint (0 issues) all pass.
The two Claude scenarios in agent-scenarios still haven't been re-recorded with real Claude. Nothing is committed yet.
I fixed both findings, test first: each new test failed before its fix and passes after it.
P2: a resumed subagent could match an old finished record.
- Test: a
session/state_test.gosubtest builds a finished record and an in-flight record that share an agent ID. The lookup must return the in-flight one. Once both are finished, it must return the finished one, so a repeated SubagentStop is still skipped. - Test:
TestHandleLifecycleSubagentEnd_SubagentStop_WithoutToolUseID_ResumedSubagentchecks that the stop completes the resumed run's record and leaves the first run's record untouched. Before the fix, the stop matched the old record and the new one was never completed. - Fix:
FindTaskRecordByAgentID(session/state.go) now prefers a record that is still in flight. It returns a finished record only when no in-flight one exists. The doc comment explains why. - Caveat: I didn't confirm that Claude really reuses the agent ID when a subagent is resumed. The change is correct either way.
P2: the raw run_in_background value was logged as-is.
- Test:
TestIsBackgroundLaunch_UnrecognizedValueNotLoggedsends a secret-looking string and an object containing a token. It reads back the real log file and asserts the value never appears and only its type does. Before the fix, the object was logged in full. - Fix:
hooks.gonow logs only the value's Go type, via%T. I first used a hand-written type switch, but its"string"literal tripped the repo'sgoconstlint check, and%Twas simpler anyway. The log shows"type":"string"or"type":"map[string]interface {}".
Lint reports 0 issues, the Windows vet is clean, and the cli, session and integration suites all pass. Nothing is committed yet.
The trail is open: https://entire.io/gh/entireio/cli/trails/1469
The work is one commit, b6fe9c90df, on the new branch soph/claude-async-subagent-capture, pushed to origin. The trail description lays out the two causes, the real hook order from the captured payloads, the fix, the tests, and what's left out.
- Checks:
mise run checkstill exits non-zero because of the same two failures as before, neither related to this change.TestProbeConfigDir_AsksClaudetimes out under full-suite load and passes when run alone.TestOpenCodeSeedRepoPlantsDepsneeds the OpenCode deps set up locally. Lint, integration tests and the canary all pass. - Trail findings: none yet. The automated review has probably not finished, so I'll check again before calling it done if you want.
- Not done: the real-Claude re-recording of the two
agent-scenariosruns. It's the true end-to-end check and makes paid API calls, so I'll only run it if you ask.
I posted the trail body as the description of PR #2644: https://github.com/entireio/cli/pull/2644
It replaces only Entire's placeholder sentence ("This draft pull request was opened by Entire…"). I kept three things that tools rely on:
- the trail-link block at the top, which keeps the PR and trail connected
- the hidden
entire-shadow-prmarker - the Cursor Bugbot summary, which the bot updates itself
The PR is still a draft, and I didn't change its title or status.
Copilot left two comments. The docs one is valid. The resume one assumes something that doesn't happen, and the same false assumption was behind the earlier review comment I already fixed.
1. "A late duplicate SubagentStop can complete a resumed subagent's record" (rated high)
I ran the dump-only hook probe again (2.1.288, Haiku). It launched a background subagent, waited for it to finish, then continued the same subagent:
A resume goes through the SendMessage tool, not a new Agent call. There's no new tool_use_id and no Agent PostToolUse, so no second task record is ever created for that agent ID. Copilot's scenario needs a finished record and an in-flight record sharing an agent ID, and that can't happen. The earlier reviewer's premise ("a resume reuses the agent ID under a new tool-use ID") is wrong for the same reason.
The probe also found a real gap, which neither this PR nor the code before it handles. The resumed run's SubagentStop finds the already-finished record and is skipped, so its edits (e.txt) aren't credited to the subagent. The transcript is partly saved anyway: the record points at the same agent-<id>.jsonl file, so if the checkpoint is written after the resume, it includes the resumed part. Fixing this would mean treating a second SubagentStart for a known agent ID as a new run. That belongs in a separate change.
What I'd do:
- Keep the in-flight-first lookup. It's harmless with only one record per agent ID.
- Correct its comment. It currently says a resume reuses the agent ID "under a new tool_use_id", which is false.
- Reply to Copilot with the probe results instead of adding duplicate-stop tracking.
- Track the resumed-run gap separately.
2. "Docs describe the old event contract" (rated low)
Valid. Both docs are out of date:
docs/architecture/agent-guide.md:506listsToolUseIDas required onSubagentEnd, and it isn't on Claude's SubagentStop. It also doesn't mentionSubagentLaunch.docs/architecture/sessions-and-checkpoints.md:362-375says background mode comes fromrun_in_background: true, and describes SubagentStop as completing "the live record" without explaining how it's found by agent ID.
Next step
I can push one commit with the docs update and the corrected comment, and draft a reply to the first comment for you to review before I post anything. Should I go ahead, and do you want the resumed-run gap added to your priorities list or opened as an issue?
Base directory for this skill: /Users/soph/.claude/skills/shelve
Shelve a finding
The store is dumb on purpose: it finds candidates, you decide whether one is the same finding. Never bump or add without the user's answer to the questions below.
0. Preflight
Before anything else, confirm the plugin is reachable:
entire agent-help knows only the Entire CLI's built-in commands, so entire agent-help priority fails with unknown command "priority" even when the plugin is installed. Never use it as the check and never read it as proof the plugin is missing. Usage for this plugin comes from entire priority agent-help [command] or entire priority <command> --help.
If entire priority version itself fails with "unknown command" or "command not found", do not give up silently and do not fall back to another tool. Run these three and report all three outputs to the user:
Then tell them the fix: run mise run dev:publish inside the entire-priority checkout, which relinks the priority plugin into the Entire CLI, or, if entire plugin list itself is not a command, the entire first on PATH is a build without plugin support and a newer Entire CLI must come first on PATH. Stop until the user has fixed it.
1. Gather the finding
From the conversation, collect:
name: a short, specific title (under about 70 characters), phrased as the problem, not the fix. "Session cache never expires entries", not "Fix cache".description: two to four sentences with what is wrong, why it matters, and what was observed. Include enough that a future session with no context can act on it.fileandlines: the most relevant file and line range, if there is one. Pass the path as it appears in the conversation; the tool stores it repo-relative.
Run from inside the repository the finding belongs to, so repo, commit, and the checkout path are recorded automatically. repo is recorded as gh/<owner>/<repo> for GitHub and et/<project>/<repo> for an Entire-native repo. If the finding belongs to another repository, pass --repo with its key in that form, or with any of its remote URLs.
2. Look for duplicates
Item ids are ULIDs. The JSON carries the full item.id; the short id you show the user is its last 7 characters. Pass the full id to bump and set-priority.
The result has candidates, each with item, tier (exact or similar), score, and reason. exact means an open or in-progress item already has a sighting overlapping this file and line range, or has the same name after normalization. similar comes from full-text search and is often noise.
Read each candidate's item.name and item.description and judge whether it describes the same underlying problem. Same file is not enough; same root cause is.
3a. If one looks like the same item
Ask the user, in one message:
- "This looks like #<short id> <name> (seen <count> times, priority <priority>). Same item?"
- "Change its priority? It is currently <priority> (1 highest, 5 lowest)."
If the user confirms it is the same:
If the user says it is different, continue with 3b.
3b. If nothing matches
Ask for a priority (1 highest, 5 lowest; suggest one with a one-line reason, default 3), then:
You do not need to pass --checkpoint: the confirmed add or bump defaults to --checkpoint auto, which asks the Entire CLI which session is running the command (entire session current --json) and, only when that session is identified rather than guessed, records it and runs entire checkpoint create --json in the checkout, which snapshots the current session's transcript into an Entire checkpoint, and links the new sighting to it, so the user can reopen this session's log later with entire priority explain <id>. Never run add or bump before the user has confirmed. If the Entire CLI cannot identify the session, auto silently links nothing; pass --checkpoint create to force the attempt. --checkpoint none opts out. If the checkpoint cannot be created (no entire on PATH, no identifiable agent session, or an older Entire CLI), the sighting is still recorded, the command still exits 0, and one line starting with warning: on stderr says why; sighting.checkpoint_id in the JSON is then null.
Shelving several findings at once
When the user confirms more than one finding in one go, create a single checkpoint for the batch, since a checkpoint snapshots the whole session transcript so far:
Take checkpoint_id from its output and pass --checkpoint <that id> to every add or bump of the batch. Report one checkpoint line for the whole batch instead of one per item, for example "Shelved #7QX2M4B and #K3E95GF, both linked to checkpoint 01M3D8JVVFRC2K3E95GF2WEX97." If entire checkpoint create --json fails, run the adds and bumps without --checkpoint and report the failure once.
Forgetting this costs nothing: plain add and bump calls in the same identified session within two minutes of the first one share a checkpoint automatically, because --checkpoint auto reuses the one linked moments ago instead of creating another.
4. Report
Tell the user the item's short id (the last 7 characters of item.id), its priority, its count, and the linked checkpoint from sighting.checkpoint_id, in the form "Shelved as #<short id> (priority N, seen M times)", for example "Shelved as #7QX2M4B (priority 2, seen 3 times), checkpoint 01M3D8JVVFRC2K3E95GF2WEX97." If the warning: line fired, say instead that no checkpoint could be created and quote the reason from the warning, for example "Shelved as #7QX2M4B (priority 2, seen 3 times); no checkpoint could be created: entire checkpoint create --json: exit status 1: no agent session found." If sighting.checkpoint_id is null and no warning was printed, say that no checkpoint was linked because the Entire CLI could not identify the agent session running the command. Then return to the task at hand.
ARGUMENTS: Claude Code subagent resume (SendMessage) is not captured as task work
I shelved the resume gap as #MV3YSFR (priority 3, seen 1 time). It's linked to checkpoint 01M41DGV4TCJ2Q2NQ19B99RCQN, so this session's log can be reopened with entire priority explain MV3YSFR. The entry records the problem, the SendMessage hook order from the probe, the likely fix, and that it came up on PR #2644.
My earlier suggestion for the two Copilot comments is still waiting on you. It's one commit with the docs update and the corrected comment, plus a draft reply to the first comment for you to review before I post it. Should I go ahead?