Repository navigation
Epic: relayhistory becomes the single owner of session sourcing; burn consumes it via SDK #160
Description
Goal
relayhistory becomes the single owner of sourcing session data from every agent harness (Claude Code, Codex, OpenCode, Cursor, Grok, Agent Relay, future collectors): discovery, parsing, incremental capture, storage, and a typed Rust SDK over it. AgentWorkforce/burn stops parsing logs and depends on that SDK for everything except pricing, cost, classification and analytics. Companion epic on the burn side: AgentWorkforce/burn#553.
Why
Both repos parse the same logs today (relayhistory crates/ai-hist/src/lib.rs; burn crates/relayburn-sdk/src/reader/) with no shared code and diverging semantics. relayhistory already defers cost to burn by name (plugins/relayhistory/rust/src/convergence.rs:58), stores model + token_json per event, and has the storage/identity/relationship/delivery layers burn lacks. But its Rust crates are not published, Cursor/Grok/OpenCode/Relay yield prompts only, hydration hard-codes capability: "full", and several record families burn depends on (requestId, stop_reason, compaction boundaries, tool-result sizes, fork/resume links) are dropped.
Shape of the SDK (decided)
- One published crate,
ai-hist([G1] Merge ai-hist-core and ai-hist-engine into one publishedai-histcrate with feature gates, a release pipeline and a semver policy #162 mergesai-hist-core+ai-hist-engine; every consumer already needed both, and core already contained parsers). CLI and napi stay unpublished workspace members. - One public entry type,
SessionStore, seven methods ([G2]SessionStorefacade: the single public entry point (seven methods, typed evidence, no raw connections, no contract constant) #178):open,sync,hydrate(SessionRef),watch,sessions(),session(SessionRef),changes_since(Watermark); staticSource::capabilities(). Typed evidence structs, norusqlitein any signature, JSON columns parsed once. - Cargo semver is the contract. No Rust contract-version constant; a
cargo public-apisnapshot in CI guards the surface ([G2] Embedder guide, example consumer crate, and a CI job that builds an out-of-tree Rust consumer against the published crates #182). Feature gatesdelivery,opencode-backup,git-hooksdefault off. - The CLI, napi and plugin crates are rewritten onto the facade, so the public API is proven by in-repo consumers before burn adopts it.
How to read the groups
Every issue in a group is independent of the others in that group and can be worked by a different agent in parallel. A group starts when the previous group is done (individual issues list their exact upstream dependencies, so early starts against a git dependency are possible where noted). Labels: sourcing-migration + group-1 / group-2 / group-3.
Group 1 — foundations and completeness (17 issues, all parallel)
| # | Issue | Closes gap |
|---|---|---|
| #161 | ADR: relayhistory owns session sourcing; burn consumes via Rust SDK | ownership + capture matrix |
| #162 | Merge into one published ai-hist crate; feature gates; release pipeline; semver policy |
no Rust distribution, two-crate ceremony |
| #163 | Checked-in fixture corpus (burn's 40 fixtures + new) and characterization snapshots | no fixture corpus |
| #164 | Per-message raw facts: requestId, stop_reason, agent version, sidechain/meta flags, Codex turn_id |
never captured |
| #165 | Dropped record types → session_markers (compaction/summary, system rows, non-text blocks, Codex lifecycle) |
_ => {} arms |
| #166 | Cursor event-level parity | prompts only |
| #167 | Grok event-level parity + real timestamps (absorbs burn #489) | prompts only, synthesized timestamps |
| #168 | OpenCode parity across SQLite and legacy JSON layouts, tokens/provider, parentID | prompts only |
| #169 | Hydration capability computed from covered kinds (bug) |
hard-coded "full" |
| #170 | Fork / resume / continuation relationships with cross-file reconciliation | delegated only |
| #171 | Tool-result fidelity: bytes, truncation, hash, ordering, error signals | not recorded |
| #172 | Usage normalization in core: per-request records, session summary, attribution (supersedes #99) | plugin-private |
| #173 | Incremental byte-offset hydration, in-progress handling, bounded memory | whole-file re-parse |
| #174 | Live capture: fs-event watch, stat-only fingerprint, --hook claude |
polling-only, no hooks |
| #175 | Canonical project_key on sessions/events for all sources |
cwd strings only |
| #176 | Sync/hydration throughput baseline + CI regression gate | no numbers |
| #177 | One-entry harness registry + local source plugins; absorb burn's collector backlog | multi-file registration, remote-only plugins |
Group 2 — the SDK (5 issues, all parallel; each depends on named group-1 issues)
| # | Issue |
|---|---|
| #178 | SessionStore facade: single entry point, seven methods, typed SessionEvidence |
| #179 | Change feed: revision-stamped changes_since() with named consumer cursors and commit |
| #180 | Control events as typed evidence (slash-command triads, task notifications, hooks, system reminders, Codex wrappers) |
| #181 | napi / TypeScript SDK / MCP exposure of all new evidence (JSON dispatcher over the facade) with contract bumps |
| #182 | Embedder guide, example consumer crate, cargo public-api snapshot, out-of-tree consumer CI job |
Group 3 — cutover support (2 issues, parallel with burn's group 3)
| # | Issue |
|---|---|
| #183 | Cross-repo parity acceptance + contract-drift CI + real-machine parity report (gates burn's cutover) |
| #184 | Retire duplicated harness knowledge: docs, sibling runbooks, collector transfer close-out, tripwire |
What relayhistory will never own
Pricing, cost, token estimation, activity classification, similarity-based session linking. Those stay in burn (or nowhere).
Related existing issues
#99 (usage summary — superseded by #172), #47 (single-writer spool — not needed: burn reads through the SDK and never writes ai-history.db), #42/#53 (large-history perf — addressed by #173/#176), #40 (hosted hook ingestion — out of scope here).
Activity
ai-histcrate with feature gates, a release pipeline and a semver policy #162--hook claude) #174SessionStorefacade: the single public entry point (seven methods, typed evidence, no raw connections, no contract constant) #178SessionStore::changes_since()with named consumer cursors, for incremental downstream ingest #179