Skip to content

Add Codex session reader and CLI integration - #1

Merged
willwashburn merged 3 commits into
mainfrom
v0.1-codex
Apr 21, 2026
Merged

Add Codex session reader and CLI integration#1
willwashburn merged 3 commits into
mainfrom
v0.1-codex

Conversation

@willwashburn

Copy link
Copy Markdown
Member

Introduce support for Codex sessions: add a parseCodexSession reader, tests, and JSONL fixtures; export the parser from the reader package. Add a new CLI subcommand and wrapper (burn codex) that spawns the codex binary, finds new session files, parses turns, appends them to the ledger, and stamps enrichments. Refactor ingestion: consolidate ingestion logic into ingestOne/ingestAll and add ingestCodexSessions; update summary and by-tool commands to call ingestAll instead of the Claude-only ingest. The Codex parser computes per-turn usage deltas, extracts function/custom tool calls, maps filesTouched from patch_apply_end events, and preserves sessionPath when provided.

Introduce support for Codex sessions: add a parseCodexSession reader, tests, and JSONL fixtures; export the parser from the reader package. Add a new CLI subcommand and wrapper (burn codex) that spawns the codex binary, finds new session files, parses turns, appends them to the ledger, and stamps enrichments. Refactor ingestion: consolidate ingestion logic into ingestOne/ingestAll and add ingestCodexSessions; update summary and by-tool commands to call ingestAll instead of the Claude-only ingest. The Codex parser computes per-turn usage deltas, extracts function/custom tool calls, maps filesTouched from patch_apply_end events, and preserves sessionPath when provided.

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class support for ingesting Codex session logs into the Relayburn ledger, and wires that support into the CLI so existing reporting commands include Codex usage alongside Claude.

Changes:

  • Add parseCodexSession reader with tests + JSONL fixtures for multi-turn usage deltas and tool/file extraction.
  • Add burn codex CLI wrapper command and refactor ingestion into ingestOne + ingestAll (Claude + Codex).
  • Update summary and by-tool commands to ingest from all supported sources.

Reviewed changes

Copilot reviewed 11 out of 11 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
tests/fixtures/codex/with-tool-call.jsonlCodex fixture covering function/custom tool calls and patch-derived filesTouched.
tests/fixtures/codex/simple-turn.jsonlCodex fixture for a basic one-turn session and token_count handling.
tests/fixtures/codex/multi-turn.jsonlCodex fixture validating per-turn usage deltas across multiple turns.
packages/reader/src/index.tsExports Codex parser/types from the reader package.
packages/reader/src/codex.tsImplements Codex JSONL parsing into TurnRecords (usage deltas, tool calls, files touched).
packages/reader/src/codex.test.tsAdds unit coverage for Codex parsing behavior and stability guarantees (argsHash).
packages/cli/src/ingest.tsRefactors ingestion into ingestOne and adds Codex ingestion + ingestAll.
packages/cli/src/commands/summary.tsSwitches summary to ingest all sources before querying.
packages/cli/src/commands/by-tool.tsSwitches by-tool to ingest all sources before querying.
packages/cli/src/commands/codex.tsAdds burn codex wrapper to spawn codex and ingest newly created session files + stamp tags.
packages/cli/src/cli.tsRegisters the new codex subcommand and updates help text.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadpackages/reader/src/codex.ts Outdated
Comment threadpackages/cli/src/ingest.ts Outdated
Comment threadpackages/cli/src/commands/codex.ts Outdated
Comment threadpackages/cli/src/commands/codex.ts Outdated
- ingestAll loads HWM once and saves once, removing double I/O and reducing
clobber risk when multiple passes share state.
- Factor walkJsonl into packages/cli/src/walk.ts; reuse from ingest and codex
wrapper to keep traversal logic in one place.
- Drop the mtime-vs-spawn-start filter in findNewSessions; preSnapshot already
excludes pre-existing files, and the 1ms cushion is fragile on filesystems
with coarse timestamp granularity.
- If session_meta arrives after task_started, propagate its cwd to the open
turn's project when still unset.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class support for ingesting and analyzing OpenAI Codex CLI sessions alongside existing Claude Code ingestion, including a new reader/parser, fixtures/tests, and CLI wiring.

Changes:

  • Add parseCodexSession to the reader package with JSONL fixtures + unit tests.
  • Refactor CLI ingestion into shared ingestOne/ingestAll and add Codex session ingestion via recursive JSONL discovery.
  • Add burn codex wrapper subcommand and update summary/by-tool to ingest all sources.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 1 comment.

Show a summary per file
FileDescription
tests/fixtures/codex/with-tool-call.jsonlFixture covering function calls, custom tool calls, and patch-based file touches.
tests/fixtures/codex/simple-turn.jsonlFixture for a minimal single-turn Codex session with token usage.
tests/fixtures/codex/multi-turn.jsonlFixture validating multi-turn usage deltas across cumulative token counts.
packages/reader/src/index.tsExports Codex parser + options from the reader package entrypoint.
packages/reader/src/codex.tsImplements Codex JSONL session parsing into TurnRecords (usage deltas, tool calls, files touched).
packages/reader/src/codex.test.tsAdds node:test coverage for parsing, usage deltas, targets, argsHash stability, sessionPath.
packages/cli/src/walk.tsAdds shared recursive JSONL file discovery helper for CLI ingestion/wrapper flows.
packages/cli/src/ingest.tsConsolidates ingestion logic and adds Codex ingestion + ingestAll().
packages/cli/src/commands/summary.tsSwitches summary command ingestion from Claude-only to ingestAll().
packages/cli/src/commands/codex.tsAdds burn codex wrapper to spawn Codex and ingest newly created sessions.
packages/cli/src/commands/by-tool.tsSwitches by-tool ingestion from Claude-only to ingestAll().
packages/cli/src/cli.tsWires the new burn codex subcommand into the CLI dispatcher and help text.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment threadpackages/cli/src/ingest.ts Outdated
Comment on lines +71 to +75
const st = await stat(file);
const prior = hwm[file];
if (prior && prior.mtimeMs >= st.mtimeMs) return;

const turns = await parse(file);
Wrap the stat/parse/append block in try/catch so a single bad session file
(removed mid-scan, permission denied, malformed JSONL that the parser chokes
on) logs to stderr and is skipped instead of aborting the entire ingestion
pass. Matches the error-swallowing posture of the surrounding directory
traversal helpers.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@willwashburn
willwashburn merged commit 78c6866 into mainApr 21, 2026
2 checks passed
@willwashburn
willwashburn deleted the v0.1-codex branch April 21, 2026 19:49

CopilotAI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds first-class Codex session support to relayburn by introducing a Codex JSONL session parser in @relayburn/reader, integrating Codex ingestion into the CLI ingestion flow, and adding a burn codex wrapper command to spawn Codex and ingest the resulting sessions into the ledger.

Changes:

  • Implement parseCodexSession (with fixtures + tests) and export it from @relayburn/reader.
  • Refactor CLI ingestion into shared helpers and add Codex ingestion (ingestAll, ingestCodexSessions, walkJsonl).
  • Add burn codex wrapper command and update summary / by-tool to ingest from all supported sources.

Reviewed changes

Copilot reviewed 12 out of 12 changed files in this pull request and generated 3 comments.

Show a summary per file
FileDescription
tests/fixtures/codex/with-tool-call.jsonlCodex fixture covering function + custom tool calls and patch events.
tests/fixtures/codex/simple-turn.jsonlCodex fixture for a minimal single-turn session + token_count shapes.
tests/fixtures/codex/multi-turn.jsonlCodex fixture validating per-turn cumulative-usage deltas across turns.
packages/reader/src/index.tsExports Codex parser + options from the reader package.
packages/reader/src/codex.tsNew Codex JSONL session parser producing TurnRecord[].
packages/reader/src/codex.test.tsTests for parsing, tool call extraction, filesTouched mapping, usage deltas, and sessionPath.
packages/cli/src/walk.tsShared directory walker to find .jsonl files recursively.
packages/cli/src/ingest.tsRefactors ingestion into ingestOne + adds Codex ingestion + ingestAll.
packages/cli/src/commands/summary.tsUses ingestAll so summary includes Codex sessions.
packages/cli/src/commands/codex.tsAdds burn codex wrapper: spawn codex, detect new session files, ingest + stamp tags.
packages/cli/src/commands/by-tool.tsUses ingestAll so by-tool includes Codex sessions.
packages/cli/src/cli.tsRegisters the new codex subcommand and updates help text.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +79 to +85
const newTurns = prior
? turns.filter(
(t) =>
t.ts > prior.lastTs || (t.ts === prior.lastTs && t.messageId !== prior.lastMessageId),
)
: turns;

Comment on lines +39 to +46
for (const file of newFiles) {
const turns = await parseCodexSession(file, { sessionPath: file });
if (turns.length === 0) continue;
await appendTurns(turns);
const sessionId = turns[0]!.sessionId;
if (sessionId) await stamp({ sessionId }, tags);
process.stderr.write(`[burn] ingested ${turns.length} turns from ${file}\n`);
}
Comment on lines +40 to +45
const turns = await parseCodexSession(file, { sessionPath: file });
if (turns.length === 0) continue;
await appendTurns(turns);
const sessionId = turns[0]!.sessionId;
if (sessionId) await stamp({ sessionId }, tags);
process.stderr.write(`[burn] ingested ${turns.length} turns from ${file}\n`);
willwashburn added a commit that referenced this pull request Apr 22, 2026
- Swap delegation/planning priority so spawning a subagent dominates a
plan-mode transition (Copilot #1).
- Promote non-edit turns to debugging when any tool call errored, so a
failing pytest/git push/build lands in debugging rather than
testing/git/build-deploy (Copilot #2).
- Add classifier tests covering the failed-bash promotion (Copilot #3).
- Thread lastUserText through parseClaudeSessionIncremental options and
result, persist it on ClaudeCursor, and pass it back from the CLI
ingest loop, so the user prompt survives a resume when endOffset
backed up past it to defer an incomplete assistant turn. Without
this the resumed turn classified as coding instead of debugging
(Devin).
willwashburn added a commit that referenced this pull request May 7, 2026
…l extract)
Three fixes on top of the merge with origin/main:
P1 (Codex 3198179089) — burn run driver skipped finalization on spawn
failure. before_spawn may have written a session stamp / pending-stamp
manifest, and the watcher's first tick may have accumulated reports;
returning early on Err(spawn) left both unreconciled and dropped the
expected `[burn] <name> ingest: ...` summary line. Refactored the spawn
block to capture the outcome into a local SpawnOutcome enum, then run
watcher.stop() / adapter.after_exit() / summary emission unconditionally
before mapping to the exit code (127 on spawn failure, child code
otherwise).
P2 (Codex 3198179092) — driver ran on a current-thread Tokio runtime but
blocked it with std::process::Command::status(), starving any watcher
ticks scheduled on the same runtime. Switched to
tokio::process::Command::status().await so the runtime can yield while
the child is alive. Added the `process` feature to the relayburn-cli
tokio dep (the rest of the workspace doesn't need it).
CodeRabbit nitpick #2 — duplicate iso_now / civil_from_days across
harnesses/claude.rs and commands/run.rs. Extracted to a new shared
crate-internal module crates/relayburn-cli/src/util/time.rs (iso_now,
iso_from_system_time, civil_from_days). Both call sites now import from
relayburn_cli::util::time so the keep-in-sync comment in run.rs is no
longer load-bearing.
Pushed back on two CodeRabbit nitpicks (no code change):
- #1 (claude.rs HOME portability via `dirs` crate): MVP target is
macOS + Linux; existing fallback to "." is acceptable. Adding a dep
for non-MVP platforms is unnecessary.
- #3 (run.rs Mutex.lock().unwrap() poison panic): CodeRabbit's own
assessment was "acceptable for now"; no change.
Verified with cargo build --workspace, cargo test --workspace (all
green, 21 cli tests + 610 sdk tests), BURN_GOLDEN=1 cargo test --test
golden -p relayburn-cli, and a manual spawn-failure smoke
(`PATH=/usr/bin:/bin burn run claude -- --version` exits 127 with the
ingest summary line emitted post-cleanup as expected).
willwashburn added a commit that referenced this pull request May 7, 2026
* relayburn-cli: burn run driver + Claude adapter (#248 D5)
Wire `burn run <harness>` as a real driver over the harness substrate
(#248 part b). Lifecycle: `plan -> before_spawn -> spawn (inherited
stdio) -> after_exit`, with an optional `start_watcher` slot that
codex/opencode (#248 D6) will populate. Reports from the watcher and
`after_exit` fold into a single `[burn] <name> ingest: N session(s)
(+M turns)` line on stderr; the user-visible exit code is the child's,
matching the TS sibling.
Claude adapter lands as `CLAUDE_ADAPTER` in `EAGER_ADAPTERS` (eager
unit-struct tier — value is a const expression, no `Box::leak` needed).
`plan` mints a v4 UUID and injects it via `--session-id` plus
`RELAYBURN_SESSION_ID` so transitive `burn ...` invocations inherit the
id; `before_spawn` writes a session-targeted stamp via the SDK ledger;
`after_exit` runs the per-session fast-path
(`relayburn_sdk::ingest_claude_session`).
`relayburn-sdk` re-exports `ingest_claude_session` so the adapter
doesn't have to reach into private ingest modules.
* relayburn-cli: address PR #318 review (spawn cleanup, async wait, util extract)
Three fixes on top of the merge with origin/main:
P1 (Codex 3198179089) — burn run driver skipped finalization on spawn
failure. before_spawn may have written a session stamp / pending-stamp
manifest, and the watcher's first tick may have accumulated reports;
returning early on Err(spawn) left both unreconciled and dropped the
expected `[burn] <name> ingest: ...` summary line. Refactored the spawn
block to capture the outcome into a local SpawnOutcome enum, then run
watcher.stop() / adapter.after_exit() / summary emission unconditionally
before mapping to the exit code (127 on spawn failure, child code
otherwise).
P2 (Codex 3198179092) — driver ran on a current-thread Tokio runtime but
blocked it with std::process::Command::status(), starving any watcher
ticks scheduled on the same runtime. Switched to
tokio::process::Command::status().await so the runtime can yield while
the child is alive. Added the `process` feature to the relayburn-cli
tokio dep (the rest of the workspace doesn't need it).
CodeRabbit nitpick #2 — duplicate iso_now / civil_from_days across
harnesses/claude.rs and commands/run.rs. Extracted to a new shared
crate-internal module crates/relayburn-cli/src/util/time.rs (iso_now,
iso_from_system_time, civil_from_days). Both call sites now import from
relayburn_cli::util::time so the keep-in-sync comment in run.rs is no
longer load-bearing.
Pushed back on two CodeRabbit nitpicks (no code change):
- #1 (claude.rs HOME portability via `dirs` crate): MVP target is
macOS + Linux; existing fallback to "." is acceptable. Adding a dep
for non-MVP platforms is unnecessary.
- #3 (run.rs Mutex.lock().unwrap() poison panic): CodeRabbit's own
assessment was "acceptable for now"; no change.
Verified with cargo build --workspace, cargo test --workspace (all
green, 21 cli tests + 610 sdk tests), BURN_GOLDEN=1 cargo test --test
golden -p relayburn-cli, and a manual spawn-failure smoke
(`PATH=/usr/bin:/bin burn run claude -- --version` exits 127 with the
ingest summary line emitted post-cleanup as expected).
Sign up for freeto 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.

2 participants

@willwashburn