Repository navigation
[G1] First-class fork / resume / continuation relationships with cross-file reconciliation (Claude and Codex) #170
Description
Part of #160 (group 1 — independent).
Problem
session_relationships models delegation only. RelationshipType is 'delegated' (sdk-ts/src/contracts.ts:220); the only other relationship value written is materialized_local (Claude remote→local, crates/ai-hist/src/lib.rs:2887-2907). Resumed, forked, and continued sessions are stored as unrelated sessions, so a consumer cannot roll a /resumed conversation up to its origin, and burn's continuity analytics (burn flow rail continuity, summary --by-relationship) have nothing to read.
burn detects all of these today in crates/relayburn-sdk/src/reader/claude/relationships.rs and emits SessionRelationshipRecord { relationship_type: root|continuation|fork|subagent, related_session_id, source_session_id, source_version, ts } (reader/types.rs:497-520):
- Explicit fields on Claude records:
forkSessionId,continuedFromSessionId,sourceSessionId(relationships.rs:45-114). /resumeand/continuemarkers in user text → local continuation (relationships.rs:259-321; fixtureresume-marker.jsonl).- Cross-file reconciliation (
relationships.rs:405-522): builds auuid → file_session_idmap across all parsed files; a file whosefirst_parent_uuidbelongs to another file is a continuation; ≥2 sessions sharing a foreignsourceSessionIdare forks (fixturescross-file-parent,original-session,fork-branch-a,fork-branch-b,explicit-line-relationships). - Evidence accumulated per file:
ClaudeRelationshipEvidence { file_session_id, first_ts, in_log_session_ids, source_version, first_parent_uuid, seen_uuids, has_resume_marker, resume_target_session_id, explicit_continuation_target_session_ids, explicit_fork_target_session_ids }(relationships.rs:23-38). file_session_idderivation honours only explicit signals (fileSessionId, then the transcript path basename) — never the opened path (relationships.rs:549-569); a transcript whose in-logsessionIds differ from its file name is itself relationship evidence.- Codex: resume is currently handled implicitly by the token baseline (
lib.rs:2260-2263); a resumed rollout'ssession_metamay carry the prior thread id — characterize on the fixture and record it.
relayhistory already stores parent_id (parentUuid) per event and has session_identity_correlations (crates/ai-hist-core/src/lib.rs:822) for remote/local pairs, so the substrate is close.
Scope
- Extend
session_relationships.relationshiptodelegated | materialized_local | continuation | fork | resumeand addorigin_session_id(thesourceSessionIda fork/continuation came from, when distinct from the parent) +evidence_refusage: theuuidthat linked the files, the marker text, or the explicit field name. Keep the PK(source, parent_session_id, relationship_uid);relationship_uid = "continuation:<child>"etc. - Claude parser: read
forkSessionId,continuedFromSessionId,sourceSessionId,/resume//continuemarkers, and accumulate the per-file evidence struct above into a newsession_continuity_evidencetable (source, session_id, file_session_id, first_parent_uuid, first_ts, in_log_session_ids_json, resume_target, explicit_targets_json, source_version), keyed per transcript, so reconciliation can run incrementally during targeted hydration (one file at a time) rather than only during a full sync with every file in memory. - Reconciliation pass: after any Claude hydration/sync, resolve
first_parent_uuidagainstsession_events.event_uid/message_idacross sessions and writecontinuation/forkrows; recordRELATIONSHIP_CONTINUITY_UNRESOLVEDdiagnostics when the parent uuid is not (yet) indexed rather than guessing. - Codex: characterize resume on a real resumed rollout (fixture) and record a
resumerelationship whensession_metanames a prior thread; otherwise document that Codex resume is not observable. - Traversal:
session_tree/session_parentsaccept arelationship_kindsfilter defaulting todelegatedonly (so existing tree semantics are unchanged) and can include continuity edges;getSessionRelationshipsreturns them under a newcontinuityarray. BumpSESSION_RELATIONSHIP_CONTRACT_VERSION(crates/ai-hist-core/src/relationships.rs:15); updatecontracts.ts,normalization.ts, MCPget_session_relationships,docs/architecture.md"Delegation topology" (rename to "Session topology").
Acceptance
- Corpus:
fork-branch-a+fork-branch-beach get aforkrow pointing atoriginal-session;cross-file-parentgets acontinuationrow;resume-markergets aresumerow;explicit-line-relationshipsyields the explicit rows withevidence_refnaming the field. - Hydrating
fork-branch-abeforeoriginal-sessionexists yields anRELATIONSHIP_CONTINUITY_UNRESOLVEDdiagnostic, and hydratingoriginal-sessionafterwards resolves it without re-hydratingfork-branch-a. getSessionTreedefault output is byte-identical to before for delegation-only databases (existingrelationships.test.tsunchanged).
Out of scope
Similarity-based linking (explicitly forbidden by docs/architecture.md: "title or prompt similarity is never used to infer one").
Activity