Skip to content

relayburn-cli: burn summary + hotspots presenters (#248 D1)#315

Merged
willwashburn merged 4 commits into
mainfrom
rust-port/cli-summary-hotspots
May 6, 2026
Merged

relayburn-cli: burn summary + hotspots presenters (#248 D1)#315
willwashburn merged 4 commits into
mainfrom
rust-port/cli-summary-hotspots

Conversation

@willwashburn

Copy link
Copy Markdown
Member

Summary

  • Implement burn summary and burn hotspots as thin presenters over relayburn_sdk::summary / ::hotspots, matching the TS CLI flag set + output byte-for-byte.
  • Un-ignore the summary / summary-json / hotspots / hotspots-json golden invocations.

Test plan

  • cargo test --workspace
  • BURN_GOLDEN=1 cargo test --test golden -p relayburn-cli (4 entries pass)
  • burn summary --help / burn hotspots --help match TS flag set

🤖 Generated with Claude Code

Wires burn summary and burn hotspots as thin presenters over
relayburn-sdk, matching the TS CLI's flag set and stdout output
byte-for-byte for the default + --json flows that drive the golden
snapshots.

- summary: aggregates by model (default) or provider (--by-provider),
  emitting the canonical TS shape (`ingest`, `turns`, `totalCost`,
  `byModel`/`byProvider`, `fidelity` with ordered byClass/byGranularity/
  missingCoverage keys, optional `replacementSavings`). Subagent /
  relationship / quality / by-tool flag wiring is in place but
  surfaces a clear "not yet implemented" stderr line + exit 2 (those
  modes land in follow-ups). Reuses the SDK's aggregate_by_provider /
  cost_for_turn / sum_costs / summarize_fidelity / summarize_replacement_savings.

- hotspots: opens the ledger, drives a synchronous ingest sweep, then
  calls relayburn_sdk::hotspots and unwraps the Attribution variant for
  rendering. JSON output drops the `kind` discriminator to match the
  TS surface. Per-session / patterns / findings / group-by surfaces
  print "not yet implemented" until the follow-up PRs.

Other changes:
- crates/relayburn-cli/src/render/format.rs: new module with TS-CLI-
  equivalent format_usd / format_int / render_table helpers and a
  coerce_whole_f64_to_int Value walker that matches JS JSON.stringify's
  int-vs-float behavior (the SDK uses f64 for cost / token-share fields).
- relayburn-sdk/src/lib.rs: re-exports the analyze types the CLI needs
  to construct option structs and walk results (UsageCostAggregateRow,
  ProviderAggregateRow, RowCoverage, FieldCoverage, CoverageField,
  AggregateByProviderOptions, ReplacementSavingsSummary, plus
  aggregate_by_provider + summarize_replacement_savings free fns).
- crates/relayburn-cli/tests/golden.rs: bootstraps the SQLite fixture
  from the committed ledger.jsonl on first run, normalizing the
  legacy eventSource: "transcript" tool-result-event flag into the
  current schema's "tool_result" so the strict serde derive accepts
  the synthetic fixture. The Rust SDK is sqlite-only; the in-tree
  fixture is JSONL because the sqlite blobs are gitignored.
- tests/fixtures/cli-golden/invocations.json: enable summary,
  summary-json, hotspots, hotspots-json.
- tests/fixtures/cli-golden/ledger/.gitignore: also ignore content.sqlite
  (the bootstrap rematerializes it alongside burn.sqlite).
- crates/relayburn-cli/tests/smoke.rs: drop summary + hotspots from
  the not-yet-implemented stub assertion (now real presenters).

Cargo:
- relayburn-cli adds tokio rt + indexmap; the rt feature lets the
  presenter drive ingest_all (async) from the otherwise-sync command
  body via a current-thread runtime.
@coderabbitai

coderabbitai Bot commented May 6, 2026

Copy link
Copy Markdown

Caution

Review failed

Failed to post review comments

📝 Walkthrough

Walkthrough

The PR implements two CLI commands (summary and hotspots) as thin presenters over relayburn-sdk. It introduces payload-bearing Command enum variants with per-command argument structs, adds full implementations with JSON and human-readable output formatting, includes formatting helpers aligned with the TypeScript CLI, updates test infrastructure to bootstrap SQLite from JSONL ledger events, and enables corresponding golden test invocations.

Changes

Summary and Hotspots CLI Commands

Layer / File(s) Summary
CLI Argument Shape
crates/relayburn-cli/src/cli.rs, crates/relayburn-cli/src/commands/summary.rs, crates/relayburn-cli/src/commands/hotspots.rs
Summary and Hotspots enum variants converted from unit-like to payload-bearing, carrying SummaryArgs and HotspotsArgs structs respectively. Each Args struct declares per-command flags (since, project, session, provider, by-tool, by-subagent-type, etc.).
Core Command Logic
crates/relayburn-cli/src/commands/summary.rs
Implements full summary workflow: opens ledger, ingests sessions, builds queries, aggregates by model or provider, computes cost/fidelity/replacement-savings via SDK, and routes to JSON or human output. Validates flag mutual exclusivity and deprecated features.
Core Command Logic
crates/relayburn-cli/src/commands/hotspots.rs
Implements full hotspots workflow: opens ledger, ingests data, computes per-source coverage breakdown, fetches hotspots via SDK, and renders output in JSON or human-readable format with per-file, per-bash, per-subagent detail.
Output Formatting Infrastructure
crates/relayburn-cli/src/render/format.rs, crates/relayburn-cli/src/render/mod.rs
Adds formatting helpers (format_usd, format_int, format_uint, render_table, coerce_whole_f64_to_int) matching TS CLI style; modularized into new format.rs and exported via mod.rs.
JSON & Human Rendering
crates/relayburn-cli/src/commands/summary.rs, crates/relayburn-cli/src/commands/hotspots.rs
Both commands include separate emit_json and emit_human entrypoints with JSON serialization helpers (cost_breakdown_to_json, fidelity_summary_to_json, replacement_savings_to_json, etc.) and rich human-readable table rendering with coverage markers, notices, and per-row metrics.
Dispatch & Integration
crates/relayburn-cli/src/main.rs, crates/relayburn-sdk/src/lib.rs, crates/relayburn-cli/Cargo.toml
Main.rs dispatch logic destructures payload from Command variants and passes to updated run() signatures. SDK exports new AggregateByProviderOptions type. Cargo.toml adds indexmap dependency with serde feature.
Test Bootstrap & Enablement
crates/relayburn-cli/tests/golden.rs, crates/relayburn-cli/tests/smoke.rs, tests/fixtures/cli-golden/invocations.json, tests/fixtures/cli-golden/ledger/.gitignore
Adds bootstrap_sqlite_from_jsonl() to replay ledger.jsonl into SQLite; normalizes tool_result events and constructs Stamps for SDK ingestion. Smoke tests updated to track UNIMPLEMENTED_SUBCOMMANDS separately. Four golden invocations (summary, summary-json, hotspots, hotspots-json) moved from disabled to enabled. .gitignore expanded to cover content.sqlite and archive.sqlite variants.
Documentation
CHANGELOG.md
Unreleased entry documents relayburn-cli wiring to relayburn-sdk with thin presenter pattern, parity with TS CLI flags and stdout behavior, and golden test enablement.

Sequence Diagram

sequenceDiagram
    actor User
    participant CLI as CLI Dispatcher
    participant Presenter as Summary/Hotspots Presenter
    participant SDK as relayburn-sdk
    participant Ledger as SQLite Ledger
    participant Render as Formatter/Renderer

    User->>CLI: run burn summary/hotspots [flags]
    CLI->>Presenter: dispatch with parsed Args
    Presenter->>Ledger: open ledger (burn.sqlite)
    Presenter->>SDK: ingest sessions via RawLedger
    Presenter->>SDK: build query (session/project/since filter)
    Presenter->>Ledger: fetch turns matching query
    Presenter->>SDK: aggregate by model/provider OR compute hotspots
    SDK-->>Presenter: aggregated rows / hotspots result
    
    alt JSON output mode
        Presenter->>Render: serialize to JSON (cost, fidelity, attribution)
    else Human output mode
        Presenter->>Render: format tables with coverage cells
        Presenter->>Render: format USD costs with locale separators
        Render-->>Presenter: formatted strings
    end
    
    Presenter-->>Render: rendered output
    Render-->>CLI: emit to stdout
    CLI-->>User: display summary/hotspots
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related issues

  • AgentWorkforce/burn#248: Implements the Rust CLI porting work (clap-based Command enum variants, payload-bearing Summary/Hotspots args, run() signatures, presenters, tests, and wiring).

Possibly related PRs

  • AgentWorkforce/burn#291: The hotspots CLI command implementation directly consumes the hotspots aggregation surface (attributions/aggregations) exported by this PR.
  • AgentWorkforce/burn#310: Main PR extends the cli-golden test harness and fixtures by implementing Summary/Hotspots CLI commands, enabling golden invocations, and adding ledger bootstrap logic.
  • AgentWorkforce/burn#301: The CLI Summary and Hotspots commands directly use the SDK-level summary and hotspots query verbs implemented in this PR.

Poem

🐰 A hop and a squeal—the presenters arise!
From thin wires to tables, with cost-column eyes,
JSON flows softly, humanity shines,
Bootstrap the ledger in SQLite's lines!
Four golden tests now skip no more—
Relayburn-cli stats adorn the store!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the main change: implementing burn summary and hotspots as presenters in the relayburn-cli.
Description check ✅ Passed The description is directly related to the changeset, detailing the implementation of burn summary and hotspots presenters, flag matching, and golden test enablement with test plan confirmation.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch rust-port/cli-summary-hotspots

Comment @coderabbitai help to get the list of available commands and usage tips.

…nline

Trivial cleanup: `run_ingest` was carrying a `ledger_home` argument
it never consulted (the open happens in `run_inner`), and the
`ledger_home = globals.ledger_path.clone()` local was a one-shot used
only to construct `LedgerOpenOptions`. Inline both.
cargo fmt --check trips on a few cosmetic items in the files this PR
adds — long match arms, attribute formatting, etc. Apply rustfmt only to
the files D1 actually owns (commands/{summary,hotspots}.rs,
render/format.rs, tests/golden.rs, tests/smoke.rs); leave the
harnesses/ + render/table.rs files for sibling PRs.
@chatgpt-codex-connector

Copy link
Copy Markdown

💡 Codex Review

let source_label = if missing_fields.contains(&"tool-result events") {
"codex"
} else {
"claude-code"
};

P2 Badge Preserve source attribution in hotspots coverage notice

The coverage notice hard-codes a single source label (codex vs claude-code) from aggregate missing-field stats, so any slice with excluded turns from multiple source kinds will produce a misleading explanation (for example, excluded Claude and OpenCode turns can still be reported as only codex). This affects user diagnosis of why attribution was excluded because the message no longer matches the actual source mix that hotspots processed.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

… notice

Codex review flagged that the previous `coverage_notice` synthesized
the source label from aggregate `missingCoverage` flags
("any tool-result-events missing → codex; otherwise claude-code"),
which silently misattributes excluded turns when a slice spans more
than one source kind (e.g. excluded codex + opencode turns both
reported as just "codex").

Re-walk the matched-window turns ourselves to compute a proper
per-`SourceKind` breakdown — same shape as the TS CLI's
`describeExcluded` — and render one inline clause per source joined
with " and ". The SDK's `HotspotsResult` aggregate `fidelity.summary`
block is unchanged; the breakdown is computed locally so the JSON
output stays byte-identical (the TS `--json` shape doesn't carry
per-source breakdowns either).
@willwashburn

Copy link
Copy Markdown
Member Author

Addressed in e50f490 — the hotspots presenter now re-walks the matched-window turns itself to build a per-SourceKind describeExcluded-style breakdown, then renders one inline clause per source joined with " and " (same shape as the TS CLI). This drops the brittle "any tool-result-events missing → codex; otherwise claude-code" heuristic the previous version used. JSON output is unchanged (the TS --json shape doesn't carry the per-source breakdown).

@willwashburn willwashburn merged commit 72f2927 into main May 6, 2026
8 checks passed
@willwashburn willwashburn deleted the rust-port/cli-summary-hotspots branch May 6, 2026 18:26
willwashburn added a commit that referenced this pull request May 6, 2026
Folds D1+D2+D3 (PRs #315/#312/#314) into the D4 state branch.

Resolutions:
- CHANGELOG.md: union all four bullets, alphabetized.
- crates/relayburn-cli/src/cli.rs: keep all five typed Command variants
  (Summary/Hotspots/Overhead/Compare/State); merge ValueEnum import; keep
  D4's state-related types (StateArgs et al.) plus D2/D3's OverheadArgs /
  CompareArgs / OverheadKind.
- crates/relayburn-cli/src/main.rs: auto-merged — five typed dispatch arms.
- crates/relayburn-cli/tests/smoke.rs: drop REAL_COMMANDS allow-list and
  early-continue; remove "state" from UNIMPLEMENTED_SUBCOMMANDS, leaving
  ["run", "ingest", "mcp-server"].
- tests/fixtures/cli-golden/ledger/.gitignore: superset of patterns.
- Cargo.lock: regenerated from origin/main.
- tests/fixtures/cli-golden/snapshots/state-status*.stdout.txt: keep D4's
  2.0 SQLite layout (deliberate divergence) but refresh row counts to
  reflect the bootstrapped fixture (golden.rs on main now replays
  ledger.jsonl into burn.sqlite before running, so the fixture has 18
  rows instead of 0).
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