Execution graph substrate: SessionRelationshipRecord + ToolResultEventRecord (#42)#77
Conversation
…EventRecord (#42) First slice of the cross-source execution graph called for in #42. Introduces two normalized, append-only record types that sit beside `TurnRecord` and preserve passive-reader metadata that's currently flattened or lost — session relationships (root / continuation / fork / subagent) and tool-result event chronology keyed by `toolUseId`. Both ride at `v: 1`. The Claude passive reader populates them on the first pass: one root row per session id, one subagent row per discovered invocation (joining to the existing `Subagent.agentId`), and one `ToolResultEventRecord` per tool_result block in a user line, with monotonic `eventIndex` and per-toolUseId `callIndex`. Spawn events (Agent/Task tool_results that resolved to a sidechain) are post-annotated with the spawned subagent's `agentId` so consumers can join the two record types. The ledger picks up two new line kinds (`relationship` / `tool_result_event`) with matching `appendRelationships` / `appendToolResultEvents` writers and `queryRelationships` / `queryToolResultEvents` readers. Both dedup through the existing `ledger-index` namespace via new id-hash helpers, and `rebuildIndex` re-indexes both kinds. Old readers skip unknown kinds — the existing `isTurnLine` / `isStampLine` / `isCompactionLine` guards already filter to known shapes. `burn ingest` (runtime + hook paths) and `burn claude` now persist the new records when the Claude reader emits them, so the substrate lands automatically alongside turns. No new flags or output yet; this PR is purely the foundation #8 (subagent tree) and #11 (waste / retry / terminal-outcome analysis) should consume next. Codex `function_call_output` and OpenCode SQLite-backed event population are deferred to follow-up PRs, as is the cross-source query CLI surface (`burn summary --subagent-tree`, `burn diagnose`, etc.) that #42 sketches. Refs #42. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Devin Review found 1 potential issue.
⚠️ 1 issue in files not directly in the diff
⚠️ Root CHANGELOG.md missing [Unreleased] entry for cross-package work (AGENTS.md violation) (CHANGELOG.md:7)
This PR touches three packages (packages/reader, packages/ledger, packages/cli) and each package's CHANGELOG.md is updated under [Unreleased]. However, the root CHANGELOG.md has no corresponding entry. AGENTS.md states: "Update [Unreleased] only when the work spans packages or warrants a top-level summary; single-package work belongs only in that package's CHANGELOG." This PR clearly spans packages, so the root CHANGELOG should have an [Unreleased] entry describing the execution-graph substrate addition, consistent with how the existing MCP entry (CHANGELOG.md:11) was added for its cross-package work.
View 5 additional findings in Devin Review.
…Devin review on #77) Devin's review on PR #77 noted the root CHANGELOG.md was missing an [Unreleased] entry for this PR's cross-package work, which violates the AGENTS.md rule (line 38): "Update [Unreleased] only when the work spans packages or warrants a top-level summary." This PR touches reader, ledger, and cli, so it qualifies. Mirrors the shape of the existing MCP entry one bullet up: top-level summary + indented per-package bullets. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Re: Devin review — root Fixed in e0ee4f1. Added an The other 5 findings only render on Devin's site and were not posted as line-anchored review comments here, so I can't address them through GitHub. If any are blockers, please re-post them as inline review comments. |
# Conflicts: # packages/cli/CHANGELOG.md # packages/ledger/CHANGELOG.md # packages/reader/CHANGELOG.md # packages/reader/src/claude.test.ts # packages/reader/src/claude.ts # packages/reader/src/index.ts
Per AGENTS.md, new unreleased work belongs under [Unreleased]. The #42 entry was sitting under the released [0.13.1] section because it landed alongside the mcp-server PR before [Unreleased] was empty; with main having since cut 0.14–0.18, the entry now strictly belongs above 0.18.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Refs #42.
This is a tractable first PR for the cross-source execution graph called for in #42. The issue calls out that several backlog items (subagent tree #8, waste-pattern detection #11, future archive work) implicitly assume normalized session relationships and tool-result chronology that today's
TurnRecordis too lossy to carry. Rather than try to ship every source / consumer at once, this PR lays the foundation:What landed
@relayburn/reader— two new record shapes (additive,v: 1):SessionRelationshipRecord—relationshipType: 'root' | 'continuation' | 'fork' | 'subagent', plusparentToolUseId/agentId/subagentType/descriptionfor subagent edges. The schema covers all four relationship kinds the issue spells out; onlyrootandsubagentare populated in this PR (Claude historical logs don't surface fork / continuation evidence we don't already cover).ToolResultEventRecord— chronological tool-output stream keyed bytoolUseIdand a per-session monotoniceventIndex. Status:running/completed/errored/cancelled/unknown. DiscriminatoreventSourcecoverstool_result/subagent_notification/queue_event/progress_event/function_call_output. Metadata-only —contentLength+contentHash, never raw bytes.Claude passive reader (
parseClaudeSession+parseClaudeSessionIncremental) now returnsrelationshipsandtoolResultEventsalongside the existingturns/content/eventsarrays. Roots emit once per session id; onesubagentrow per distinct invocation discovered (joiningSubagent.agentId); eachtool_resultblock becomes aToolResultEventRecord. Agent/Task spawn events are post-annotated with the resolved subagent'sagentIdso the two record types can be joined. The incremental parser respects the sameendOffsetdeferral content/compaction events use, so resumed ingest doesn't double-emit.@relayburn/ledgerpicks up two newLedgerLinekinds —relationshipandtool_result_event— with matchingappendRelationships/appendToolResultEventswriters,queryRelationships/queryToolResultEventsreaders, andisSessionRelationshipLine/isToolResultEventLineguards. Both append-only; both dedup through the existing~/.relayburn/ledger-indexnamespace via newrelationshipIdHash(keyed on type + agentId + parentToolUseId) andtoolResultEventIdHash(keyed on sessionId + toolUseId + eventIndex).rebuildIndexre-indexes both kinds.@relayburn/cli—burn ingest(runtime + hook paths) andburn claudenow persist relationships + tool-result events when the Claude reader emits them. No new flags or output yet.What's deferred
function_call_output,spawn_agent/waitrelationships, terminal subagent notifications. The shape is in place; codex.ts wiring is a follow-up.#7) emitting the same shapes — current Claude hook ingest goes through the reader, so it picks up everything the reader emits, but a future hook-only event source (e.g.subagent_notification) needs its own emitter.burn summary --subagent-tree,burn diagnose,burn waste --patterns,burn summary --by-relationship. The execution graph is now persisted, so these can be built on top.parentUuidchains we already use for subagents; explicit fork/continuation evidence (across-filesourceSessionId, /resume markers) needs targeted parser work.Test plan
pnpm install && pnpm run test:ts— 347 tests pass (added 8 new tests).eventIndex, the rightstatus/isError/contentLength/contentHashfor the retry-loop fixture.agentId/parentToolUseId/relatedSessionId/subagentType/description, and the Agent/Task spawntool_resultevents get joined back to the right child invocation viaagentId.appendRelationships/queryRelationshipsandappendToolResultEvents/queryToolResultEvents; dedup is keyed correctly so re-appending the same record doesn't grow the file; query filters work forsessionId(matching child or parent) andsource.rebuildIndexre-indexes the new line kinds and re-appending after rebuild is a no-op.🤖 Generated with Claude Code