Skip to content

Emit fork and continuation relationships from Claude parser (#112)#122

Merged
willwashburn merged 3 commits intomainfrom
feat/claude-fork-continuation-112
Apr 26, 2026
Merged

Emit fork and continuation relationships from Claude parser (#112)#122
willwashburn merged 3 commits intomainfrom
feat/claude-fork-continuation-112

Conversation

@willwashburn
Copy link
Copy Markdown
Member

@willwashburn willwashburn commented Apr 26, 2026

Summary

  • Closes the deferred-work item from Execution graph substrate: SessionRelationshipRecord + ToolResultEventRecord (#42) #77/Execution graph for passive readers: session relationships and tool-result event chronology #42: the Claude passive reader now populates the full RelationshipType lattice (root / continuation / fork / subagent), not just root and subagent.
  • The single-file parser collects per-file evidence (in-log sessionId vs filename mismatches, the first user line's parentUuid, version, all in-file uuids, and /resume / /continue slash-command markers) and surfaces it as a new evidence: ClaudeRelationshipEvidence field on ParseResult / ParseIncrementalResult. A /resume marker emits a local continuation row directly.
  • A new exported reconcileClaudeSessionRelationships(inputs) helper takes per-file evidence from a multi-file pass and emits the cross-file fork / continuation rows single-file parsers can't surface. The CLI ingest path runs it at end of pass.
  • Existing root / subagent rows are stamped with sourceSessionId (foreign in-log id) and sourceVersion whenever the file carries them.

Reconciliation strategy

Append, not mutate. relationshipIdHash keys on relationshipType, so a prior root row and a later continuation / fork row for the same session id produce different hashes — both rows coexist on disk after a follow-up reconciliation pass. Consumers that care about "is this session a child of another?" prefer the more specific row (fork / continuation > root) when both are present. This keeps the ledger append-only and re-ingest idempotent (the writer's existing relationshipIdHash dedup folds duplicates of the new types too).

Test plan

  • pnpm run test:ts (461 tests, all pass)
  • New tests in packages/reader/src/claude.test.ts:
    • emits a continuation row from a /resume marker, with relatedSessionId set to the resumed-from id
    • populates sourceSessionId and sourceVersion on existing root rows when the in-log id differs from the file id
    • exposes per-file evidence so a cross-file pass can resolve fork / continuation
    • reconcileClaudeSessionRelationships emits a continuation row when one file's first parentUuid lives in another file
    • reconcileClaudeSessionRelationships emits fork rows when two files share a sourceSessionId
    • reconcileClaudeSessionRelationships does not emit a fork row when one file is a strict continuation of the other
    • re-parsing the same session produces relationship rows with stable hashes (dedup target)
    • reconciliation skips a duplicate continuation when the local /resume already named the same parent
    • preserves sourceSessionId / sourceVersion on subagent rows when the in-log id differs from the file basename
  • New fixtures: resume-marker.jsonl, original-session.jsonl, cross-file-parent.jsonl, fork-branch-a.jsonl, fork-branch-b.jsonl

Refs

🤖 Generated with Claude Code


Open in Devin Review

Closes the deferred-work item from #77/#42: the Claude passive reader
now populates the full RelationshipType lattice instead of only `root`
and `subagent`. Per-file evidence — in-log sessionId vs filename
mismatches, the first user line's parentUuid, version, all in-file
uuids, and `/resume` / `/continue` slash-command markers — is collected
during the existing parse pass and surfaced as a new
`evidence: ClaudeRelationshipEvidence` field on ParseResult /
ParseIncrementalResult. A `/resume` marker in the file emits a local
`continuation` row directly; a new exported
`reconcileClaudeSessionRelationships(inputs)` helper takes per-file
evidence from a multi-file pass and emits the cross-file `fork` /
`continuation` rows that single-file parsers can't surface. Existing
`root` / `subagent` rows are stamped with `sourceSessionId` (foreign
in-log id) and `sourceVersion` whenever the file carries them.

Reconciliation strategy is append, not mutate: a prior `root` row and
a later `continuation` / `fork` row for the same session id produce
different relationshipIdHash values (the hash includes
relationshipType), so both rows coexist on disk and consumers prefer
the more specific row when both are present. Re-ingesting the same
session is idempotent — the writer's existing dedup folds duplicates
across the new types too.

The CLI ingest path runs the cross-file reconciliation step at end of
each pass after every Claude file has parsed. New fixtures cover
resume-marker, cross-file parentUuid continuation, and shared-source
fork detection.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
devin-ai-integration[bot]

This comment was marked as resolved.

willwashburn and others added 2 commits April 25, 2026 22:18
# Conflicts:
#	packages/cli/CHANGELOG.md
#	packages/cli/src/ingest.ts
…w on #112)

A leading sidechain user line was arming the WeakSet, which then blocked
the first non-sidechain user line from setting `firstParentUuid` and
silently dropped the cross-file continuation signal for that session.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Claude reader: populate fork and continuation SessionRelationshipRecord rows

1 participant