Skip to content

feat(cli): add txcript check, a report of what a conversion loses - #95

Draft
malihashar wants to merge 3 commits into
skillsynchq:mainfrom
malihashar:feat/check-command
Draft

malihashar wants to merge 3 commits into
skillsynchq:mainfrom
malihashar:feat/check-command

Conversation

@malihashar

@malihashar malihashar commented Oct 25, 2026 •

Copy link
Copy Markdown
Contributor

In short

txcript check is a new command that tells you what converting a session to another agent would lose, before you do it. For example, txcript check <session> --with codex lists what Codex would drop or change. This is step 3 of 3: the command itself, plus a one-line warning on continue when a conversion is lossy.

Part 3 of 3 for #92. It builds on #94, which builds on #93.

How to review this PR

This branch also contains the commits from #93 and #94 until they are merged. Please review only the last commit, feat(cli): add txcript check, a report of what a conversion loses. After the earlier PRs merge, this PR will show only its own changes.

What this PR adds

The txcript check command, which uses the library from #94.

txcript check <id|file|->[#range] [--with <harness>]... [--from <harness>]
              [--json] [--detail] [--fail-on none|content|accounting|metadata]
  • With --with, it prints a full report for each target. Without it, it prints a table with one row per writable target.
  • --json prints a stable, versioned format (schema 1). --detail shows longer examples.
  • --fail-on makes the exit code 1 when the loss reaches that level, so CI can refuse a lossy conversion.
  • check never writes anything and never starts an agent.

New flags on the existing commands:

  • continue and crop print one line on stderr when the chosen target loses or changes something. --quiet hides it.
  • continue --strict and crop --strict stop before writing anything if content would be lost.
  • continue --dry-run does the same as check.

Example

$ txcript check session.json --with codex
check 8d116ece (simple, 23 messages) -> codex
  content    CHANGED  1 tool name renamed  /release -> _release
             LOST     1 tool argument (Bash.description)  Run tests
  accounting LOST     4 usage records
             LOST     8 stop reasons  tool_use
  metadata   LOST     title  Session everything
             LOST     model  claude-opus-5
  structure  23 messages become 30 (the target splits turns)
  codec-level: measured on the target's session text; a store's own additions are not counted

What changes for existing users

  • Stdout of every existing command is exactly the same. A test checks this.
  • continue and crop print one extra line on stderr when a conversion is lossy. That is the only visible change. Resuming in place (same harness) prints nothing.
  • SessionCommand::Continue and SessionCommand::Crop gained fields, so code that builds or destructures them by hand needs .. or the new fields.

Tests

  • 36 end-to-end tests run the real binary: the report, the table, --json, --fail-on exit codes, --strict writing nothing, check and --dry-run writing nothing, ranges, files and stdin, the stderr note appearing once, a closed pipe.
  • Nine of them check the report against reality. For codex, pi, campfire, cursor, cursor_desktop, grok, fx, antigravity and claude_code, the test converts for real, reads the copy back, and checks that what check said was lost is what really disappeared.
  • 15 unit tests for the command, and the whole suite passes. cargo fmt and cargo clippy (0 warnings) are clean.

Hands-on testing

I also used it for about 30 minutes on synthetic sessions, from empty to 18 MB, with every agent as source and target and a 9-step chain of conversions. That found 10 false alarms, all fixed with a test each, and no missed losses. Speed: a 5,000-message session takes about 0.6 seconds for all targets together, and an 18 MB session about 1.6 seconds. Only a 363 MB stress session took over a second per target.

What I could not test

Real agent resumes, OpenCode and Cowork writes, live sources (ChatGPT, Claude Chat), and the interactive crop editor with --strict (it needs a terminal; the check it uses is unit-tested and shared with continue).

Open questions

  1. Should the stderr note always print, or only when stderr is a terminal? Today it always prints, because continue already prints similar lines.
  2. Should --strict refuse a conversion that only renames a tool argument, or only when text, tool calls or results are lost?

malihashar and others added 3 commits October 25, 2026 15:07
The two functions that parse or render a harness's native text by
HarnessId lived privately in src/wasm.rs, so only the WASM bindings could
use them. Move them to a new always-compiled txcript::dispatch module and
make wasm.rs call them. No behaviour change.

This lets other code (such as a conversion report) render a transcript
into any harness's format and read it back without going through WASM.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Add txcript::fidelity. assess(target, source) renders a transcript into a
harness's native text, reads it back, and compares the two, so it reports
exactly what that codec keeps, changes, loses, or makes up. check_all runs
it for every harness that can render.

Losses are grouped as content (text, tool calls and results, images),
accounting (usage, stop reasons, per-message model, thinking signatures)
and metadata (title, model, branch, cwd). Blocks are compared as a flat
stream, not message by message, because targets split and merge turns.
JSON re-spacing and regenerated ids are not counted as loss. The result is
serialisable. The measurement is codec-level and does not include what a
store adds, such as artifact files.

Tests: unit tests for each category, one golden table of what every
harness loses on a rich session (also published in docs/formats), a
proptest that no target panics and that Simple reports nothing, and the
docs table is checked against the codecs so it cannot drift.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
`continue --with codex` writes the copy and prints one line, even though
the conversion can drop the title, the model, usage records and stop
reasons, rename slash commands, or split turns. Show that before it
happens.

- `txcript check <id|file|->[#range] [--with <harness>]...` prints a table
  of every writable harness, or a full report per --with. It supports
  --json (schema 1), --detail, and --fail-on <none|content|accounting|
  metadata> so CI can fail on loss. It never writes anything.
- `continue` and `crop` print one stderr line when the chosen target loses
  or changes something. --quiet silences it. --strict on `continue` and
  `crop` stops before anything is written when content would be lost.
  `continue --dry-run` is `check` for the same arguments.
- Stdout is unchanged. The measurement is codec-level, as documented.

Tests: end-to-end tests drive the real binary, including nine that convert
for real, read the copy back, and check that what `check` predicted lost
matches what actually disappeared. Docs: a Fidelity section in usage.md.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>

This branch has not been deployed

No deployments
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.

1 participant