Skip to content

feat: machine-readable export surface for sessions and checkpoints#1144

Merged
alishakawaguchi merged 8 commits intomainfrom
checkpoint-export-cli
May 7, 2026
Merged

feat: machine-readable export surface for sessions and checkpoints#1144
alishakawaguchi merged 8 commits intomainfrom
checkpoint-export-cli

Conversation

@alishakawaguchi
Copy link
Copy Markdown
Contributor

@alishakawaguchi alishakawaguchi commented May 7, 2026

https://entire.io/gh/entireio/cli/trails/320

Summary

Adds a thin, additive CLI surface that exposes session and checkpoint data
as a stable contract for external consumers (Baton, eval pipelines, the
session-handoff skill). Closes #1125.

No new top-level commands, no schema invention. JSON envelopes are
metadata-only; transcript bytes always stream to stdout from a flag.

New flags:

  • entire session info <id> --transcript / entire session current --transcript
    — stream the live raw agent transcript to stdout (snapshot semantics:
    trailing partial JSONL line is trimmed; <-ctx.Done() cancels the read).
  • entire session list --json — same per-entry envelope as
    session info --json, emitted as an array ([] for empty).
  • entire checkpoint explain --json — metadata-only envelope. List view
    if no target, single-checkpoint object if a target is given. Per-session
    read failures surface an explicit error field (no silent stub entries);
    list-view non-context errors propagate (no swallowed []).
  • entire checkpoint explain <id> --transcript — stream the normalized
    compact transcript (V2 transcript.jsonl on /main).
  • entire checkpoint explain <id> --raw-transcript --session-index N
    pick a specific session in a multi-session checkpoint (default: latest).
    --session-index also works with --transcript.

Internal:

  • Shared matchCheckpointPrefixWithRemoteFallback helper used by both the
    prose runExplainCheckpointWithLookup and the new export resolver.
  • New sessionOutputMode enum replaces (jsonOutput, transcriptOutput bool)
    so the trichotomy is total in code.
  • New exported strategy.ResolveTranscriptPath for live-transcript callers
    outside the strategy package.
  • New errCheckpointHasNoSessions sentinel — distinguishes empty vs
    missing checkpoints for callers using errors.Is.

Test plan

  • Unit + integration tests pass (mise run test:ci)
  • Lint clean (mise run lint)
  • Manual smoke test against a real repo with v1 + v2 checkpoints
  • --json envelope verified to never embed transcript bytes
    (TestRunExplainExport_JSONNeverEmbedsTranscript)
  • --session-index distinct content per session verified end-to-end
    (264KB session 0 vs 1.4MB session 1, distinct sessionIds)
  • Positional commit-SHA fallback verified end-to-end
  • <-ctx.Done() cancellation contract verified via close-on-done pattern
  • Partial trailing line trimmed
    (TestInfoCmd_TranscriptTrimsPartialTrailingLine)
  • Missing transcript path surfaces cause to stderr (not silent SilentError)

🤖 Generated with Claude Code


Note

Medium Risk
Adds new machine-readable CLI output modes that stream transcript bytes and emit JSON envelopes, which could affect scripting/automation and output/exit-code behavior if edge cases are missed.

Overview
Adds a machine-readable export surface to existing CLI commands.

entire checkpoint explain gains --json (metadata-only list or single-checkpoint envelope with partial + per-session error reporting), --transcript (stream v2 compact transcript), --session-index (select session in multi-session checkpoints), and --limit (cap JSON list output with truncation note). Export resolution now retries missing checkpoint prefixes via a shared remote-metadata fetch helper, and supports positional fallback to commit-trailer resolution.

entire session info and entire session current gain --transcript to stream the live on-disk agent transcript with snapshot/cancellation semantics and agent-aware shaping (JSONL line streaming vs whole-JSON validation), while entire session list gains --json to emit the same per-session JSON envelopes as session info --json. Also exports strategy.ResolveTranscriptPath for non-strategy callers and adds comprehensive tests for the new flags and edge cases.

Reviewed by Cursor Bugbot for commit 8eedead. Configure here.

alishakawaguchi and others added 3 commits May 7, 2026 09:42
Expose the rich data Entire already captures as a stable, supported CLI
contract for external consumers (Baton, eval pipelines, the
session-handoff skill) so they no longer parse internal storage:

- entire session info <id> --transcript / session current --transcript
  stream the live raw agent transcript bytes to stdout. JSON envelopes
  remain metadata-only.

- entire checkpoint explain --json emits a metadata-only envelope: list
  view when no target is given, single-checkpoint envelope (with
  per-session metadata read via V2 ReadSessionMetadata) when targeted.
  Transcript bytes are never embedded.

- entire checkpoint explain <id> --transcript streams the normalized
  compact transcript (V2 transcript.jsonl on /main); --raw-transcript
  keeps the per-agent raw bytes.

- --session-index N selects a session within a multi-session checkpoint
  for --transcript / --raw-transcript (default: latest). The legacy
  raw-transcript dispatch silently ignored the index, so the cobra
  fork now routes --raw-transcript through the export path whenever
  --session-index is explicit.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: db502c03f98b
Codex/Claude reviews of the prior commit flagged correctness gaps in the new
machine-readable export modes. Fix them:

- Positional commit-SHA fallback. `explain --json <sha>` and
  `--transcript <sha>` failed when the positional didn't match a checkpoint
  prefix; now falls through to commit-ref resolution like the prose
  `runExplainAuto` does, and the prefix lookup also retries after a remote
  metadata fetch on miss.

- JSON envelope is lossless. Per-session metadata read failures now surface
  an explicit `error` field rather than silently producing a stub entry, and
  the v1 fallback uses `GitStore.ReadSessionMetadata` (metadata-only) instead
  of `ReadSessionContent`, which used to fail on v1 checkpoints whose raw
  transcript had been pruned. List-view JSON now propagates non-context
  errors (`fetch failed` is no longer indistinguishable from `[]`).

- Cancellation actually works. `streamTranscriptToStdout` wires `<-ctx.Done()`
  to a goroutine that closes the file, so Ctrl-C on a multi-MB transcript
  unblocks the read promptly instead of running to EOF.

- Live-transcript snapshot is shape-clean. The trailing partial line (from
  an agent mid-write) is trimmed so consumers never see a truncated JSONL
  record.

- User-visible errors. `session info|current --transcript` now prints the
  cause to stderr before returning SilentError; previously the silent path
  exited with no message.

- Distinct sentinel for empty checkpoint. `resolveSessionIndex` returns
  a new `errCheckpointHasNoSessions` for the empty-Sessions case so callers
  using `errors.Is(err, ErrCheckpointNotFound)` for UX no longer give the
  wrong diagnosis.

- Internal cleanup. Extracted `matchCheckpointPrefixWithRemoteFallback`
  shared by the prose and export paths. Replaced the
  `(jsonOutput, transcriptOutput bool)` pair with a `sessionOutputMode`
  enum so the trichotomy is total. The error message for
  `--raw-transcript` without a target now references `--raw-transcript`
  instead of `--transcript`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 4103be403127
Adds the metadata-only JSON envelope to the session list view, sharing the
exact same per-entry shape that `session info --json` and `session current
--json` already emit. Always emits a valid array (`[]` for empty) so
consumers can pipe through `jq` without special-casing "no sessions".

Closes the last gap from #1125: external tools (Baton, eval pipelines, the
session-handoff skill) can now enumerate sessions across worktrees without
globbing `.git/entire-sessions/*.json` directly.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 3f985842651a
Copilot AI review requested due to automatic review settings May 7, 2026 19:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds an additive, machine-readable CLI surface for exporting session and checkpoint metadata and streaming transcript bytes, enabling downstream tooling (handoff/eval pipelines) to consume Entire data without parsing internal storage.

Changes:

  • Introduces --json list output for entire session list, and --transcript streaming for entire session info / entire session current.
  • Adds entire checkpoint explain --json (metadata-only envelope) and --transcript / --raw-transcript --session-index transcript streaming paths via a new export implementation.
  • Refactors checkpoint-prefix matching into a shared matchCheckpointPrefixWithRemoteFallback helper and exports strategy.ResolveTranscriptPath for non-strategy callers.

Reviewed changes

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

Show a summary per file
File Description
cmd/entire/cli/strategy/resolve_transcript.go Exposes an exported wrapper for transcript-path re-resolution so CLI commands can safely follow agent transcript relocations.
cmd/entire/cli/sessions.go Adds transcript streaming helper, session list JSON output, and a total sessionOutputMode for info/current.
cmd/entire/cli/sessions_test.go Adds unit tests for session list --json and session info --transcript (including trimming + mutual exclusion).
cmd/entire/cli/session_current.go Adds --transcript mode and routes output via the new sessionOutputMode.
cmd/entire/cli/explain.go Adds export flags (--json, --transcript, --session-index) and dispatches to export pipeline.
cmd/entire/cli/explain_export.go Implements the export pipeline: checkpoint/commit resolution, JSON envelopes, transcript streaming, and shared remote-fallback prefix matching.
cmd/entire/cli/explain_export_test.go Adds coverage for export JSON invariants, transcript streaming, session-index behavior, and commit-SHA fallback.

Comment thread cmd/entire/cli/sessions.go Outdated
Comment thread cmd/entire/cli/sessions.go Outdated
Comment thread cmd/entire/cli/explain_export.go Outdated
Comment thread cmd/entire/cli/explain_export.go Outdated
PR #1144 review feedback. Three real issues + one rename:

1. Whole-document JSON transcripts (Gemini's session-*.json) were being
   silently dropped: the trim-to-last-newline logic stripped the closing
   brace, leaving malformed output. Now `shapeTranscriptSnapshot` checks
   `json.Valid` first and emits the document intact when the buffer is a
   single JSON document; only JSONL streams (Claude/Cursor/Codex) get the
   trailing partial-line trim.

2. Snapshot semantics were inaccurate: io.ReadAll reads to current EOF, so
   bytes the agent appends after command start would be included silently.
   Now bound the read with io.LimitReader anchored to f.Stat().Size() at
   open. Help text updated to match.

3. `explainExportOptions` doc comment claimed "exactly one of json or
   transcript" but rawTranscript is also a valid mode. Reworded.

4. Renamed `bytes` local in runExplainStreamTranscript to `compact` so it
   doesn't shadow the stdlib package.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: c0c9dd5b48db
@alishakawaguchi
Copy link
Copy Markdown
Contributor Author

bugbot run

Comment thread cmd/entire/cli/explain_export.go
Comment thread cmd/entire/cli/sessions.go
alishakawaguchi and others added 3 commits May 7, 2026 13:05
Two real low-severity issues caught by cursor[bot]:

1. Unused `json` field in explainExportOptions: the JSON path was being
   reached by elimination ("not transcript, not raw-transcript") instead
   of by checking opts.json explicitly. A future caller invoking
   runExplainExport with all three mode flags false would silently
   produce JSON output. Replace the implicit dispatch with an explicit
   switch and a defensive default branch that errors out. Regression
   test: TestRunExplainExport_NoModeFlagFailsLoudly.

2. Double-close on context cancellation in streamTranscriptToStdout: the
   cancel goroutine and the defer both called f.Close() unconditionally.
   os.File.Close handles repeated calls gracefully today but it can trip
   the race detector under heavy fd reuse. Funnel both paths through a
   sync.Once so close happens exactly once.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 11908a40a40c
Three findings from /codex:adversarial-review:

[high] --session JSON filter dropped archived sessions in multi-session
checkpoints. runExplainListJSON only matched against p.SessionID (the
latest contributor), so any checkpoint where the searched session had
been archived to an older slot was silently omitted — exactly the
concurrent-session case the metadata model exists to support. New
checkpointMatchesSessionFilter helper now matches against both
SessionID and every entry in SessionIDs. Regression test:
TestCheckpointMatchesSessionFilter.

[medium] Transcript export buffered the entire session file before
writing. io.ReadAll on a multi-hour Claude Code transcript would spike
memory to O(file size); the help text positioned this as a streaming
export, so falling over on the largest transcripts is a real
regression. Now agent-aware:

  - JSONL agents (Claude Code/Cursor/Codex/etc.): line-buffered copy
    via bufio.Reader.ReadBytes('\n'). Only one line is held in memory
    at a time. Trailing partial line at EOF is dropped (existing
    contract). New writeJSONLTranscript.
  - Whole-document JSON agents (Gemini): buffer + json.Valid as before
    — these transcripts are bounded by conversation size and rarely
    exceed a few MB. Trim-to-last-newline would corrupt them.

[medium] Checkpoint JSON export silently produced partial data on
per-session read failures. Added a top-level `partial` flag to
checkpointExportJSON that's set when any session metadata fails to
read; the command also returns SilentError so the exit code is
non-zero. Per-session error fields stay so consumers can pinpoint the
failure. Contract test: TestCheckpointExportJSON_PartialContract.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: c774f18acf9a
Working through the in-flight code review on the export surface:

[H1] streamTranscriptToStdout silently disabled the snapshot bound when
f.Stat() failed: snapshotSize stayed at -1, the io.LimitReader was
skipped, and the help text's "snapshot bounded at command start" promise
was a lie. Now Stat failure returns an error rather than emitting an
unbounded read with a misleading guarantee.

[H2] The previous --raw-transcript --session-index test wrote one
session, so it could only catch a regression where the flag was
rejected outright — not one where it was silently ignored (which is
exactly the bug we fixed earlier in the branch). New
TestExplainCmd_RawTranscriptMultiSessionDistinctContent uses a v2
fixture with two byte-distinct sessions and asserts that index 0
and index 1 return different content.

[H3] The partial-failure JSON path was only contract-tested (struct
shape via direct marshal). To exercise the actual readSessionMetadataForExport
→ envelope.Partial → SilentError flow without corrupting a real v2 git
tree (the splice helper is unexported), extracted
buildCheckpointJSONEnvelope into a unit-testable helper and added
TestBuildCheckpointJSONEnvelope_PartialFailureFromMockReader using a
stubCommittedReader that hits the default branch in
readSessionMetadataForExport.

[M1] entire checkpoint explain --json was capped at 100 checkpoints
with no indication. Added a --limit flag and a stderr note when the cap
is hit (we probe limit+1 to detect overflow, trim, and tell the user
how to see more). The JSON shape stays a flat array so existing jq
pipelines don't break.

[M2] --commit <ref> skipped the remote-fetch retry that --checkpoint /
positional already used. Now resolveCheckpointFromCommitRef calls
matchCheckpointPrefixWithRemoteFallback when the trailer points at an
unknown checkpoint, so commit-SHA targets work the same as prefix
targets.

[M4] --transcript on a v1-only checkpoint used to error with "compact
transcript is only available for v2 (try --raw-transcript)". The user's
intent is "give me transcript bytes" and we have a way to satisfy it,
so transparently fall through to the raw transcript with a one-line
stderr note instead of forcing a re-run.

[L2] resolveExplainCheckpointID docstring updated to mention that
--commit also has remote-fetch retry now, matching the implementation.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 67e4b2cf7534
@alishakawaguchi
Copy link
Copy Markdown
Contributor Author

bugbot run

Comment thread cmd/entire/cli/sessions.go
Cursor bugbot finding: writeWholeDocumentJSONTranscript returned nil
(success) with no output when json.Valid was false. For machine
consumers, exit-code 0 + empty stdout was indistinguishable from
"transcript is empty" — a Baton-style script would silently proceed
with no data.

- Empty buffer (agent hasn't written anything yet) still succeeds
  cleanly, since that's a real "no data" case.
- Non-empty + invalid (agent mid-write or genuinely corrupt) now
  returns an error suggesting a retry. The non-zero exit lets
  consumers branch on stdout-vs-data.
- writeSessionTranscript sets cmd.SilenceUsage so the runtime error
  doesn't trigger cobra's usage block on top of any partial output.

Regression test: TestInfoCmd_TranscriptInvalidJSONErrorsLoudly.

The other two cursor[bot] comments on this PR are stale — both were
posted before commit 786b84b (which added the sync.Once close and the
explicit opts.json switch case).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 3fe20a6a7b12
@alishakawaguchi
Copy link
Copy Markdown
Contributor Author

bugbot run

Copy link
Copy Markdown

@cursor cursor Bot left a comment

Choose a reason for hiding this comment

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

✅ Bugbot reviewed your changes and found no new issues!

Comment @cursor review or bugbot run to trigger another review on this PR

Reviewed by Cursor Bugbot for commit 8eedead. Configure here.

@alishakawaguchi alishakawaguchi marked this pull request as ready for review May 7, 2026 22:28
@alishakawaguchi alishakawaguchi requested a review from a team as a code owner May 7, 2026 22:28
@alishakawaguchi alishakawaguchi merged commit e861149 into main May 7, 2026
10 checks passed
@alishakawaguchi alishakawaguchi deleted the checkpoint-export-cli branch May 7, 2026 22:38
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

[Feature Request] Support Baton-style handoff workflows from Entire session/checkpoint data

3 participants