Skip to content

Restructure relayburn crates into a single SDK monolith#305

Merged
willwashburn merged 1 commit intomainfrom
claude/sdk-monolith-restructure
May 6, 2026
Merged

Restructure relayburn crates into a single SDK monolith#305
willwashburn merged 1 commit intomainfrom
claude/sdk-monolith-restructure

Conversation

@willwashburn
Copy link
Copy Markdown
Member

Summary

  • Absorbs the four lower crates (relayburn-{reader,ledger,analyze,ingest}) into relayburn-sdk as src/{reader,ledger,analyze,ingest}/ modules. The Rust workspace shrinks from seven crates to three: relayburn-sdk, relayburn-cli, relayburn-sdk-node.
  • Only the SDK and CLI publish to crates.io; the four absorbed modules become internal implementation rather than a public crates.io contract.
  • The CLI's new dep on relayburn-sdk exercises the SDK's public surface as an external embedder would, which is the property we wanted from the original split.

Why

The four-lower-crates layout (set up in #242#245 and made publish-ready in #304) forced a lockstep crates.io publish dance for what is really a single implementation. Continuing the conversation in #246's PR thread, we measured the rebuild tax: worst-case incremental goes from ~1.4s (workspace) → ~3–6s (monolith). At this codebase size, that's not meaningful, while exposing four internal crates as a public crates.io contract has real costs (semver pressure, naming, version coordination).

This PR reverts the lockstep-publish prep from #304 (the version-pinned path deps on the four absorbed crates) and replaces it with a single SDK crate that re-exports the same surface from internal modules.

Mechanics

  • git mv each lower crate's src/ into the corresponding SDK submodule, with each crate's old lib.rs becoming the module root (reader.rs, ledger.rs, analyze.rs, ingest.rs); blame history is preserved.
  • Cross-crate imports rewritten in two passes: scope-rename intra-module sibling refs (crate::config::crate::ledger::config::), then relayburn_X::crate::X:: everywhere.
  • models.dev.json moves to crates/relayburn-sdk/data/; the include_str! path and scripts/update-pricing.mjs follow. Nothing under packages/ is touched (project rule).
  • The four ingest integration tests (tests/*.rs) become in-crate #[cfg(test)] mods — they exercise crate-private items (parse_pending_stamp, set_ingest_gap_writer, LedgerLayout) that the SDK doesn't re-export. Their per-binary static ENV_LOCK / GAP_LOCK mutexes are consolidated into shared TEST_ENV_LOCK / TEST_GAP_LOCK in crate::ingest; without that, bundling them into one binary races $RELAYBURN_HOME and gap-state mutations across modules.
  • relayburn-cli and relayburn-sdk-node Cargo.tomls now depend only on relayburn-sdk. The CLI's version requirement is 0.0 (= >=0.0.0, <0.1.0) so it satisfies both the local 0.0.0 path dep and the published relayburn-sdk 0.0.1 on crates.io — no lockstep-bump tax on every release.
  • AGENTS.md (CLAUDE.md) Layout / When-in-doubt sections updated to describe the three-crate shape and module-path pointers (e.g. crates/relayburn-sdk/src/reader/classifier.rs rather than crates/relayburn-reader/src/classifier.rs).

Test plan

  • cargo build --workspace clean
  • cargo test --workspace green — 605 unit + 2 integration + 3 doctests, including all four absorbed ingest test files
  • cargo doc --no-deps -p relayburn-sdk clean (3 pre-existing intra-doc-link warnings — classify_fidelity etc. now point to private items since the lower crates aren't a public surface)
  • cargo publish --dry-run -p relayburn-sdk --allow-dirty succeeds (no transitive crates.io needed anymore — the whole point)
  • cargo publish --dry-run -p relayburn-cli --allow-dirty succeeds (resolves relayburn-sdk 0.0.1 from crates.io)
  • The integration test added in relayburn-sdk: integration test + make workspace publish-ready (closes #246) #304 (crates/relayburn-sdk/tests/integration.rs) still passes — exercises all 9 verbs against a fixture ledger
  • Tests verified stable across multiple consecutive runs (the TEST_ENV_LOCK / TEST_GAP_LOCK consolidation was needed to avoid intermittent failures from the absorbed test files racing on global state)

🤖 Generated with Claude Code

Absorbs `relayburn-{reader,ledger,analyze,ingest}` into `relayburn-sdk`
as `src/{reader,ledger,analyze,ingest}/` modules. The Rust workspace is
now three crates (`relayburn-sdk`, `relayburn-cli`, `relayburn-sdk-node`)
instead of seven. Only the SDK and CLI are published to crates.io; the
four absorbed modules become internal implementation details rather than
a public crates.io contract.

Why: keeping the lower crates separate forced a lockstep-publish
arrangement (the prep for which landed in #304) for what is really a
single implementation. The CLI's `relayburn-sdk` dep now exercises the
SDK's public surface as an external embedder would, which is the
property we actually wanted from the split.

Mechanics:

- `git mv` each lower crate's `src/` into the corresponding SDK
  submodule, with each crate's old `lib.rs` becoming the module root.
- Cross-crate imports rewritten in two passes: first scope-rename
  intra-module sibling refs (`crate::config::` → `crate::ledger::config::`),
  then `relayburn_X::` → `crate::X::`.
- `models.dev.json` moves to `crates/relayburn-sdk/data/`; the
  `include_str!` path and `scripts/update-pricing.mjs` follow.
- Ingest's four `tests/*.rs` files become in-crate `#[cfg(test)] mod`s
  (they exercise crate-private items that the SDK doesn't re-export).
  Their per-binary `static ENV_LOCK` / `GAP_LOCK` mutexes are
  consolidated into shared `TEST_ENV_LOCK` / `TEST_GAP_LOCK` in
  `crate::ingest`; without that, bundling them into one binary races
  `$RELAYBURN_HOME` and gap-state mutations across modules.
- `relayburn-cli` and `relayburn-sdk-node` `Cargo.toml`s now depend
  only on `relayburn-sdk`. The CLI's version requirement is `0.0`
  (= `>=0.0.0, <0.1.0`) so it satisfies both the local 0.0.0 path dep
  and the published `relayburn-sdk` 0.0.1 on crates.io.
- `AGENTS.md` (`CLAUDE.md`) Layout / When-in-doubt sections updated to
  describe the three-crate shape and module-path pointers.

Verification: `cargo build --workspace` and `cargo test --workspace`
green (605 unit + 2 integration + 3 doctests, including all four
absorbed ingest test files); `cargo doc --no-deps -p relayburn-sdk`,
`cargo publish --dry-run -p relayburn-sdk --allow-dirty`, and
`cargo publish --dry-run -p relayburn-cli --allow-dirty` all succeed.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

📝 Walkthrough

Walkthrough

This PR consolidates a multi-crate Rust workspace (relayburn-analyze, relayburn-ingest, relayburn-ledger, relayburn-reader) into a single monolithic relayburn-sdk crate. All inter-crate imports are replaced with internal crate::* module paths, old Cargo.toml files are deleted, and the SDK's public surface is reorganized to re-export from internal modules instead of external crates.

Changes

Workspace Consolidation

Layer / File(s) Summary
Manifest Cleanup
crates/relayburn-{analyze,ingest,ledger,reader}/Cargo.toml, crates/relayburn-sdk/Cargo.toml, crates/relayburn-cli/Cargo.toml, crates/relayburn-sdk-node/Cargo.toml, scripts/update-pricing.mjs
Old per-crate manifests deleted; relayburn-sdk manifest refactored to use workspace dependencies instead of path pins; relayburn-cli and relayburn-sdk-node now depend on relayburn-sdk; pricing output path moved from relayburn-analyze to relayburn-sdk/data.
Internal Module Structure
crates/relayburn-sdk/src/{analyze.rs,ingest.rs,ledger.rs,reader.rs}
New module declarations (mod analyze, mod ingest, mod ledger, mod reader) added to relayburn-sdk; each module file includes crate-level lint suppressions for dead_code/unused_imports.
Public Re-exports
crates/relayburn-sdk/src/lib.rs
Public surface switched from relayburn_* crates to crate::* modules; re-exports for analyze, ingest, ledger, and reader now route through internal modules; LedgerHandle documentation updated to reference crate::ledger.
Ledger API Surface
crates/relayburn-sdk/src/ledger.rs
Public method signatures on Ledger updated from relayburn_reader::* to crate::reader::* types (append_turns, append_compactions, append_relationships, append_tool_result_events, append_user_turns, append_content, query_); re-exports reorganized under ledger submodule paths (crate::ledger::config, crate::ledger::error, crate::ledger::) instead of top-level crate paths.
Ledger Module Imports
crates/relayburn-sdk/src/ledger/{config.rs,content.rs,db.rs,fingerprint.rs,query.rs,reader.rs,stamp.rs,tests.rs,writer.rs}
Import paths updated throughout ledger module: relayburn_reader → crate::reader, relayburn_ledger → crate::ledger, top-level crate::error/crate::paths/crate::schema moved to crate::ledger::*.
Analyze Module Imports
crates/relayburn-sdk/src/analyze/{claude_md.rs,compare.rs,compare_archive.rs,cost.rs,fidelity.rs,findings.rs,ghost_surface.rs,ghost_surface_inputs.rs,hotspots.rs,overhead.rs,patterns.rs,patterns_tests.rs,pricing.rs,provider.rs,quality.rs,replacement_savings.rs,subagent_tree.rs,tool_call_patterns.rs,tool_output_bloat.rs}
All import paths migrated: relayburn_reader/relayburn_ledger → crate::reader/crate::ledger; top-level crate::cost/crate::pricing/crate::findings → crate::analyze::cost/crate::analyze::pricing/crate::analyze::findings; test imports updated accordingly; pricing.rs include path adjusted from ../data to ../../data.
Ingest Module Imports
crates/relayburn-sdk/src/ingest/{ingest.rs,cursors.rs,gap.rs,gap_warning_tests.rs,orchestration_tests.rs,pending_stamps.rs,pending_stamps_compat_tests.rs,reingest.rs,watch_loop.rs,watch_loop_tests.rs}, crates/relayburn-sdk/src/ingest_verb.rs
Import paths refactored: relayburn_ingest/relayburn_ledger → crate::ingest/crate::ledger; per-module imports now under crate::ingest::*; test locks (TEST_ENV_LOCK, TEST_GAP_LOCK) centralized in ingest.rs and aliased in test modules; ContentStoreMode re-export moved from relayburn_reader to crate::reader.
Reader Module Imports
crates/relayburn-sdk/src/reader/{classifier.rs,claude.rs,codex.rs,codex/tests.rs,fidelity.rs,opencode.rs,opencode/tests.rs,opencode_stream.rs,opencode_stream/tests.rs,user_turn.rs}
Type imports reorganized: top-level crate::types → crate::reader::types; per-submodule paths updated (crate::classifier → crate::reader::classifier, etc.); test imports updated throughout.
Verb Re-exports
crates/relayburn-sdk/src/{export_verbs.rs,query_verbs.rs}
Documentation and imports updated to reference crate::ledger and crate::analyze instead of relayburn_* crates; GatheredOverhead.attribution type updated from relayburn_analyze to crate::analyze.
Documentation
AGENTS.md
Architecture section replaced Rust crate workspace layout description with monolithic structure; added explicit publishing details (relayburn-sdk, relayburn-cli, relayburn-sdk-node); updated API surface and architecture references to mention both TS and Rust surfaces (lib.rs and TS exports).

Estimated code review effort

🎯 5 (Critical) | ⏱️ ~110 minutes

Possibly related issues

Possibly related PRs

  • AgentWorkforce/burn#289: Absorbs relayburn-analyze modules (claude_md, subagent_tree) into relayburn-sdk and updates internal import paths correspondingly.
  • AgentWorkforce/burn#299: Continues the relayburn-sdk wiring and re-export consolidation, moving public surfaces from relayburn_* crates into crate::* equivalents and updating method signatures.
  • AgentWorkforce/burn#300: Modifies the same verb modules (export_verbs.rs, ingest_verb.rs, query_verbs.rs) and lib.rs re-export wiring affected by this PR's consolidation.

Poem

🐰 Hop hop, the crates are one now,
No relayburn_reader paths—just crate:: vow,
Monolith strong, all imports aligned,
A tidy workspace, peacefully designed! 🎯📦

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.46% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Restructure relayburn crates into a single SDK monolith' accurately summarizes the main change: consolidating four separate crates into one SDK monolith structure.
Description check ✅ Passed The description comprehensively explains the restructuring: absorbing four crates into relayburn-sdk as internal modules, reducing from seven to three crates, explaining the motivation (avoiding lockstep publishing), detailing the mechanics, and providing a thorough test plan.
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 claude/sdk-monolith-restructure

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

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
crates/relayburn-sdk/src/lib.rs (1)

6-10: 💤 Low value

Consider updating the module doc to reflect absorbed modules.

The doc still describes the crate as "a thin re-export over relayburn-reader, relayburn-ledger, relayburn-analyze, and relayburn-ingest" as if they're external crates. Since these are now internal modules, a minor wording update (e.g., "internal modules") would improve clarity for future readers.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@crates/relayburn-sdk/src/lib.rs` around lines 6 - 10, Update the crate-level
documentation in lib.rs to replace wording that implies external crates (e.g.,
"a thin re-export over `relayburn-reader`, `relayburn-ledger`,
`relayburn-analyze`, and `relayburn-ingest`") with language that reflects these
are now internal modules (for example "re-exports internal modules" or "a thin
re-export of internal modules"). Edit the module doc comment at the top of
lib.rs so the public API description and the mention of those module names
correctly indicate they are internal modules rather than separate external
crates.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@crates/relayburn-sdk/src/lib.rs`:
- Around line 6-10: Update the crate-level documentation in lib.rs to replace
wording that implies external crates (e.g., "a thin re-export over
`relayburn-reader`, `relayburn-ledger`, `relayburn-analyze`, and
`relayburn-ingest`") with language that reflects these are now internal modules
(for example "re-exports internal modules" or "a thin re-export of internal
modules"). Edit the module doc comment at the top of lib.rs so the public API
description and the mention of those module names correctly indicate they are
internal modules rather than separate external crates.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ef1555fe-7b16-42fc-bd81-e7886dc1c923

📥 Commits

Reviewing files that changed from the base of the PR and between 3b3bad4 and 14b0037.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (75)
  • AGENTS.md
  • crates/relayburn-analyze/Cargo.toml
  • crates/relayburn-cli/Cargo.toml
  • crates/relayburn-ingest/Cargo.toml
  • crates/relayburn-ledger/Cargo.toml
  • crates/relayburn-reader/Cargo.toml
  • crates/relayburn-sdk-node/Cargo.toml
  • crates/relayburn-sdk/Cargo.toml
  • crates/relayburn-sdk/data/models.dev.json
  • crates/relayburn-sdk/src/analyze.rs
  • crates/relayburn-sdk/src/analyze/claude_md.rs
  • crates/relayburn-sdk/src/analyze/compare.rs
  • crates/relayburn-sdk/src/analyze/compare_archive.rs
  • crates/relayburn-sdk/src/analyze/cost.rs
  • crates/relayburn-sdk/src/analyze/fidelity.rs
  • crates/relayburn-sdk/src/analyze/findings.rs
  • crates/relayburn-sdk/src/analyze/ghost_surface.rs
  • crates/relayburn-sdk/src/analyze/ghost_surface_inputs.rs
  • crates/relayburn-sdk/src/analyze/hotspots.rs
  • crates/relayburn-sdk/src/analyze/overhead.rs
  • crates/relayburn-sdk/src/analyze/patterns.rs
  • crates/relayburn-sdk/src/analyze/patterns_tests.rs
  • crates/relayburn-sdk/src/analyze/pricing.rs
  • crates/relayburn-sdk/src/analyze/provider.rs
  • crates/relayburn-sdk/src/analyze/provider_reattribution.rs
  • crates/relayburn-sdk/src/analyze/quality.rs
  • crates/relayburn-sdk/src/analyze/replacement_savings.rs
  • crates/relayburn-sdk/src/analyze/subagent_tree.rs
  • crates/relayburn-sdk/src/analyze/tool_call_patterns.rs
  • crates/relayburn-sdk/src/analyze/tool_output_bloat.rs
  • crates/relayburn-sdk/src/export_verbs.rs
  • crates/relayburn-sdk/src/ingest.rs
  • crates/relayburn-sdk/src/ingest/cursors.rs
  • crates/relayburn-sdk/src/ingest/gap.rs
  • crates/relayburn-sdk/src/ingest/gap_warning_tests.rs
  • crates/relayburn-sdk/src/ingest/ingest.rs
  • crates/relayburn-sdk/src/ingest/orchestration_tests.rs
  • crates/relayburn-sdk/src/ingest/pending_stamps.rs
  • crates/relayburn-sdk/src/ingest/pending_stamps_compat_tests.rs
  • crates/relayburn-sdk/src/ingest/reingest.rs
  • crates/relayburn-sdk/src/ingest/walk.rs
  • crates/relayburn-sdk/src/ingest/watch_loop.rs
  • crates/relayburn-sdk/src/ingest/watch_loop_tests.rs
  • crates/relayburn-sdk/src/ingest_verb.rs
  • crates/relayburn-sdk/src/ledger.rs
  • crates/relayburn-sdk/src/ledger/config.rs
  • crates/relayburn-sdk/src/ledger/content.rs
  • crates/relayburn-sdk/src/ledger/db.rs
  • crates/relayburn-sdk/src/ledger/error.rs
  • crates/relayburn-sdk/src/ledger/fingerprint.rs
  • crates/relayburn-sdk/src/ledger/paths.rs
  • crates/relayburn-sdk/src/ledger/query.rs
  • crates/relayburn-sdk/src/ledger/reader.rs
  • crates/relayburn-sdk/src/ledger/schema.rs
  • crates/relayburn-sdk/src/ledger/stamp.rs
  • crates/relayburn-sdk/src/ledger/tests.rs
  • crates/relayburn-sdk/src/ledger/writer.rs
  • crates/relayburn-sdk/src/lib.rs
  • crates/relayburn-sdk/src/query_verbs.rs
  • crates/relayburn-sdk/src/reader.rs
  • crates/relayburn-sdk/src/reader/classifier.rs
  • crates/relayburn-sdk/src/reader/claude.rs
  • crates/relayburn-sdk/src/reader/codex.rs
  • crates/relayburn-sdk/src/reader/codex/tests.rs
  • crates/relayburn-sdk/src/reader/fidelity.rs
  • crates/relayburn-sdk/src/reader/git.rs
  • crates/relayburn-sdk/src/reader/hash.rs
  • crates/relayburn-sdk/src/reader/opencode.rs
  • crates/relayburn-sdk/src/reader/opencode/tests.rs
  • crates/relayburn-sdk/src/reader/opencode_stream.rs
  • crates/relayburn-sdk/src/reader/opencode_stream/tests.rs
  • crates/relayburn-sdk/src/reader/types.rs
  • crates/relayburn-sdk/src/reader/user_turn.rs
  • crates/relayburn-sdk/tests/fixtures/ts_codex_stamp.json
  • scripts/update-pricing.mjs
💤 Files with no reviewable changes (4)
  • crates/relayburn-reader/Cargo.toml
  • crates/relayburn-analyze/Cargo.toml
  • crates/relayburn-ingest/Cargo.toml
  • crates/relayburn-ledger/Cargo.toml

@willwashburn willwashburn merged commit 09cc4e0 into main May 6, 2026
3 checks passed
@willwashburn willwashburn deleted the claude/sdk-monolith-restructure branch May 6, 2026 01:22
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