bullpen sessions --json: read the coordination plane without scraping - #6
Conversation
The store is the coordination plane, but until now the only way to read it from outside bullpen was to parse a padded column layout — which loses the full session id (the table truncates to 8 chars), breaks on titles that contain runs of spaces, and encodes parenthood as a `└ child of` glyph — or to open the SQLite file directly and couple to the schema. `--json` gives callers a stable surface that is neither. The wire shape is built in the CLI by a pure `sessions_json(&[Session])` rather than by deriving Serialize on `bullpen_store::Session`. The store struct is internal state; deriving on it would turn any future field rename into a silent breaking change for every script consuming this output. The cost is a hand-maintained mirror — a new field on `Usage` will not appear in `--json` until it is added here — which is noted on the helper. Every key is always present; absence is null, never a missing key, so a consumer can index without existence checks. `parent_session_id` and `pid` are the two that go null. Empty store emits `[]`, not the prose hint — a script that shells out gets parseable output on every path. Errors keep flowing through anyhow to stderr with a non-zero exit; they are deliberately not wrapped in JSON. Additive: `bullpen sessions` with no flag is byte-for-byte what it was. Verified: fmt, clippy -D warnings, and 94 workspace tests green (3 new, covering the empty case, the full element shape, and the null-vs-string parent field). Behavior checked against the built binary as well, since the workspace has no CLI end-to-end harness — table output unchanged, and an empty HOME prints `[]` with `--json` and the hint without it. Refs #5 Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01PfAfAujueuZ3rDTiL9apx3
|
Caution Review failedThe pull request is closed. ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: ⛔ Files ignored due to path filters (1)
📒 Files selected for processing (3)
📝 WalkthroughWalkthroughThe ChangesJSON session listing
Estimated code review effort: 2 (Simple) | ~10 minutes Possibly related issues
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Comment |
|
@coderabbitai review |
|
Closes the gap in #5: there was no machine-readable output anywhere in the CLI. A script that wanted to act on session state had two bad options — parse the fixed-width table, which truncates the session id to 8 chars, breaks on titles containing runs of spaces, and encodes parenthood as a
└ child ofglyph; or open the SQLite file directly and couple itself to the schema.bullpen sessions --jsonis a third option that is neither.Decisions a reviewer would otherwise have to reverse-engineer
The wire shape is built in the CLI, not derived on the store type.
sessions_json(&[Session]) -> Valuehand-maps the record instead of#[derive(Serialize)]onbullpen_store::Session. That struct is internal state; deriving on it would make any future field rename a silent breaking change for every script consuming this output, with nothing in the store crate to signal that a rename is now a public-API change. The cost is a hand-maintained mirror — add a field tobullpen_llm::Usageand it will not appear in--jsonuntil it is added here — and that tradeoff is stated in the doc comment on the helper. Keeping the function pure is also what makes the empty case and the field mapping unit-testable without a process harness.Absence is
null, never a missing key. Every session object always carries all eleven keys.parent_session_idandpidare the two that go null. Consumers can index without existence checks;jq '.[].pid'never silently yields nothing on a top-level session.Ids are full, not the table's 8-char prefix. The prefix is a display affordance; a prefix is not a key and can collide, so JSON emits the whole 36-char uuid. Same reasoning for
usage, which is a nested{input_tokens, output_tokens}object rather than two flattened columns.Errors are not wrapped in JSON. A store open or query failure still goes through anyhow to stderr with a non-zero exit. Emitting
{"error": ...}on stdout with exit 0 would make failures indistinguishable from data for a caller that only checks whether stdout parses.Order is
list_sessions()order (updated_at DESC), unchanged. Output is pretty-printed with a trailing newline — readable when a human pipes it, irrelevant to a parser.Scope
Additive.
bullpen sessionswith no flag is byte-for-byte what it was, including theno sessions yethint. The diff insidefn sessionsis additions only — theis_emptyearly-return and the entirefor s in sessionsprinting block appear as unchanged context, no reborrow and no re-indentation. README gains one line under the command block.Verification
cargo fmt --all --checkcargo test --workspacecargo clippy --workspace --all-targets -- -D warningsThe three new tests cover the empty case, the complete element shape, and the null-vs-string behavior of
parent_session_id.The workspace has no CLI end-to-end harness, so the three requirements in the issue were also checked against the built binary rather than trusted to unit tests alone:
./target/debug/bullpen sessions— same table, unchanged.HOME=$(mktemp -d) ./target/debug/bullpen sessions --json→[]; the same binary against the same empty HOME without--jsonstill prints the prose hint../target/debug/bullpen sessions --json | jq— valid, full 36-char uuid,usagenested,parent_session_id/pidpresent as null.Known gap, not closed here
Nothing automatically guards the default human table output. Closing that would mean pulling in
assert_cmdortrycmd— the first test-harness crate in a workspace that deliberately has none — so it is out of scope for this PR, but the guard genuinely does not exist.Summary by CodeRabbit
New Features
--jsonoption to thebullpen sessionscommand for machine-readable session lists.Tests
Documentation