Repository navigation
telemetry: attribute Claude Code subagent dispatches to their Delivery (Refs #5171) - #5178
Merged
Merged
Conversation
…y (Refs #5171) Passive capture now derives dispatch facts for a selected Claude Code subagent transcript from data Claude Code already writes: the sibling agent-<id>.meta.json, the transcript's first record, and the launch result in the spawning transcript. It reuses expected-dispatch, invocation-lineage, runtime-admission, runtime-gap and diagnostic, so neither the receiver nor the store schema changes. - Dispatch id = sha256("claude-code-subagent-dispatch/1", session, toolUseId)[:32]; relation child of the session (or the spawning subagent for a nested spawn); runtime claude-code-subagent; expectedAt from the first record. - Invocation id = the subagent's existing passive thread id, so per-response provider-usage-observation/1 facts join by threadId. - requestedModel = the launch result's resolvedModel; producerStream carries the role. - The item comes only from the brief's first line `fsgg-dispatch: item=OWNER/REPO#N role=...`; a missing or malformed marker is an item-less item-unattributed diagnostic, never a guess. Missing metadata is dispatch-identity-unavailable; a launch result still missing at retirement is a claude-launch-result-unavailable runtime gap. - Deterministic contents and a per-source status (cursor "dispatch", retired "d") make a re-read replay identical facts and a resumed agent add turns to the same dispatch. work-board and pnext-item (both roots) state the dispatch marker; manifests, repos.lock, skills.yml work-board digest, census and skills changelog refreshed. The utel roadmap records the design. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…s (Refs #5171) signature-doc-siting refuses XML doc comments in a .fs with a sibling .fsi. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…, lock and census (Refs #5171) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…ease-notes line, regenerated manifests and lock (Refs #5171) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Root/fdev: independent review APPROVED
Fix now
Follow-up: F2, foreground-subagent admission across a long quiet run, is #5198. Accepted: F4 (meta race unlikely), and F6: mixed codex/Claude items keep their budget population open, as declared. F7: the drive-board wrappers contain no brief text, so there is nothing to edit; removed from #5185's scope. Version: the 0.114.2 candidate ( Refs #5171 |
…ilable and reset controls (Refs #5171) Root review of a13dcff: - F1: the item-unattributed diagnostic identity now ends in its reason, so a re-read after a producer-state reset that finds a different reason adds a fact instead of an identity conflict that would quarantine the batch and its usage. Control: a reset producer state with a new reason is accepted with both gaps and both usage observations; without the reason the receiver rejects it. - F5: test for item-unattributed:prompt-unavailable (opening record is not the user prompt). - F3: the roadmap notes that trailing whitespace on the marker line is trimmed. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
…5171 (Refs #5171) 0.114.2 was cut from 55a24ab before this PR and is published, so the packaged pnext-item change moves the coherent set 0.114.2 -> 0.114.3 (Directory.Build.props, source preparation only). The CLI PackageReleaseNotes carry a 0.114.3 heading with the subagent dispatch attribution and the marker guidance; driver manifest, census digest and skills changelog refreshed. Picks up #5201 (FakeClock). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Contributor
Author
|
Root/fdev delta check since the approved
The approval carries over. Merging. Release 0.114.3 follows, then installed proof of the live-store join. Refs #5171 |
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.
Refs #5171. Installed proof follows the coherent release (Root's held remainder below).
Design
Passive capture already reads a selected subagent transcript
<project>/<session>/subagents/agent-<id>.jsonl. It now also reads, bounded and through the same no-follow opener, three things Claude Code already writes:agent-<id>.meta.json:toolUseId,spawnDepth,parentAgentId;toolUseIdand agent in the spawning transcript:resolvedModel.It emits only kinds the receiver already admits, so no receiver change and no store migration:
expected-dispatchsha256("claude-code-subagent-dispatch/1"␟session␟toolUseId)[:32]; relationchild; parent is the session (claude-code-session-dispatch/1) or, nested, the spawning subagent's dispatch; runtimeclaude-code-subagent;expectedAt= first record time (host-wall); activation id names the session, no activation factinvocation-lineageprovider-usage-observation/1joins onthreadId; root = session idruntime-admissionrequestedModel= launchresolvedModel;featureId= item;attemptId= dispatch id;producerStream=claude-code-subagent:<role>The resolved (observed) model is the per-response
modelon each joined usage row.Item. It comes only from the brief's first line,
fsgg-dispatch: item=OWNER/REPO#N role=worker|critic|analyst|report. If the marker is missing or malformed, or the first record is unreadable, no dispatch rows are written. Instead an item-lessdiagnosticitem-unattributed:<reason>names the dispatch, the invocation and the reason in its identity, so a re-read after a producer-state reset that finds another reason adds a fact rather than conflicting. Trailing whitespace on the marker line is trimmed.expected_dispatches.item_idis NOT NULL, and a sentinel item would be a guess.Gaps.
dispatch-identity-unavailable.runtime-gapclaude-launch-result-unavailable.Re-read and resume. Identities and contents are deterministic, so a re-read replays byte-identical facts. A per-source status (cursor
dispatch, retired entryd) stops a pass from re-emitting, so a resumed agent (SendMessage) adds turns to the same dispatch.Limits.
claude-code-subagentis not a settled budget runtime, so an item with these dispatches keeps its budget population open.The design is also recorded in
docs/roadmaps/utel-operational-completeness.md(new subsection).Guidance
work-board/SKILL.md(after the boundary-order paragraph) andpnext-item/SKILL.md(step 1) now say that every dispatch brief starts with the marker. This is in both roots, which are byte-identical.I did not edit
lifecycle-contract.mdorstatus-lifecycle.md(#5136 PR 6) or any other skill (#5172).The following were refreshed:
repos.lock(pnext-item);skills.ymlwork-boarddigest andupdated:;skills.yml;skills.CHANGELOG.Version. 0.114.2 was cut from
55a24ab6before this PR and is published. This branch mergedorigin/main, including #5201 (FakeClock), and moves the coherent set to 0.114.3:Directory.Build.props, a CLI 0.114.3PackageReleaseNotesheading for this change, and the census digest. Kit pr-arm reports0.114.3 is ahead of the newest published FS.GG.Kit (0.114.2). This is source preparation only; the release is a separate step.Tests (local, exact source before push)
4daf0515, main merged): the two touched telemetry suitesPassed: 45, Failed: 0; CoreTelemetryTestsPassed: 20, Failed: 0;V2TendProposeTestsPassed: 12, Failed: 0;engine release-notes fixture: 32 passed, 0 failed;check-engine-release-notesok;signature-doc-siting90 passed, 0 failed; manifests current;repos-registry: OK;skill-union-assert: OK;v1-writer-census: PASS.a45dd7a8):ClaudeSubagentDispatchTests|WorkspaceTelemetryApplicationTestsPassed: 45, Failed: 0(11 dispatch tests). The new tests are aprompt-unavailablegap (F5) and a reset producer state with a different reason (F1): the receiver accepts it with 2 gaps and 2 usage observations. A mutation that drops the reason from the identity fails 4 tests, and in the reset test the receiver reports"applied":0,"rejected":1.ClaudeSubagentDispatchTests(new, CLI):Passed: 9, Failed: 0. These run the real capture, submit each produced batch through an authenticated generic receipt into a fresh local store, and run the join below verbatim. Causal controls:accepted 0, replayed 3with one dispatch;TelemetryTests: 2 new tests, for the marker grammar (4 marked, 4 missing, 8 malformed) and the meta, opening and launch decoders. Both pass.WorkspaceTelemetryApplicationTests|ClaudeSubagentDispatchTests|TelemetryStoreApplicationTests|TelemetryReceiptTestsPassed: 186, Failed: 0;HostPassiveReceiptTests|HostSchemaProfileTestsPassed: 3, Failed: 0.signature-doc-sitingfixture (after the comment repair on the second head):90 passed, 0 failed.ClaudeSubagentDispatchTests|WorkspaceTelemetryApplicationTestsPassed: 43, Failed: 0; CoreTelemetryTestsPassed: 20, Failed: 0; kit pr-armok; manifests current;repos-registry: OK; censusPASS.skill-quality: OK — 29 skills;skill-union-assert: OK(29/29 byte-identical);skill-view-parity: AGREE;check-skillmirror-freshness: OK;v1-writer-census: PASS (structural);repos-registry: OK;generate-driver-manifest: current.item-unattributed:missing-marker(briefs from before the marker), and 1112 usage observations. Two independent runs gave identical dispatch ids, and one id matched an independent Python recomputation.Fixture clocks. Record timestamps are fixed literals used as input. The retirement test ages its file relative to the host wall clock. Stores and homes are per-test temp sandboxes. The transcript, meta and launch records are synthetic, and no private transcript text is used.
Held remainder: installed proof (Root)
This needs the engine released and installed through the coherent route (0.114.2). Then read the live store read-only:
join.sql(identical toClaudeSubagentDispatchTests.JoinSql):Gaps:
To check that a row's counters equal the transcript's own usage, sum that subagent transcript's completed assistant records. Count each
message.idonce and take a record only if it has a non-nullstop_reason, a model other than<synthetic>, and a preceding prompt. Input isinput_tokens + cache_read_input_tokens + cache_creation_input_tokens.Only marked briefs (from 2026-11-01) are attributed. Capture is forward-only: a transcript already retired by the old engine gets dispatch facts only if it grows again.
🤖 Generated with Claude Code