feat(bundle): rivet bundle <ID> --depth N --as {yaml,jsonl} (#206)#266
Merged
Conversation
Implements the consumer surface proposed in #206 — emit the typed link-graph closure of an artifact as a single, pasteable document so an LLM agent can get root + neighbours in one round-trip instead of N. The MCP tool counterpart (`rivet_bundle`) lets agents call this without shelling out. What landed: - `rivet-core/src/bundle.rs` — new module. Breadth-first, visit-once traversal over `Store::get` + `Artifact::links`, returning a depth-stamped `Vec<BundleEntry>`. Cycles terminate naturally via the visited set. Depth 0 = root only; depth 1 = root + direct neighbours; etc. Dangling targets (links pointing outside the store) appear as `target:` references inside the parent's `links:` list but are not expanded. - `BundleFormat::{Yaml, Jsonl}` rendering. YAML is hand-rendered (not serde_yaml) because the inline `# satisfies -> REQ-002` annotations are the whole point of the format and serde can't round-trip comments. JSONL is one `serde_json` record per line for tool consumers / streaming readers. - `rivet-cli/src/main.rs` — new `Command::Bundle { id, depth, format }` variant with `--depth` (default 1) and `--as` (default yaml). Handler `cmd_bundle` loads the project context, calls into `bundle::bundle`, prints the rendered output. Invalid `--as` values produce a clear error listing the valid options. - `rivet-cli/src/mcp.rs` — new `rivet_bundle` MCP tool with `BundleParams { id, depth, format }`. Defaults match the CLI (depth=1, format=yaml). Returns the rendered bundle as text content. - Tests: 11 unit tests in `bundle::tests` covering depth 0/1/2 closure, cycle termination, dangling targets, missing-root error, the `# linktype -> target` annotation, JSONL line-per-record, format parsing, and YAML scalar quoting. 5 CLI integration tests in `cli_commands.rs` (depth-0 root only, depth-1 + annotations, jsonl shape, missing-root non-zero exit, invalid-format rejection). 2 MCP integration tests in `mcp_integration.rs` (yaml + jsonl). The tools-list count test was bumped from 15 to 16 to include `rivet_bundle`. Out of scope per the issue body: markdown rendering ("the closest Karpathy analogue is paste the wiki into the LLM context — for rivet, YAML preserves typed structure with zero information loss"). Acceptance — issue #206 (lifted from the 2026-04-26 + 2026-05-09 triage comments): - [x] New module `rivet-core/src/bundle.rs` with closure traversal over `Store` + `Link` graph, depth-bounded - [x] `rivet bundle <ID> --depth N --as {yaml,jsonl}` CLI command - [x] MCP tool `rivet_bundle` so agents can call it without shelling - [x] Output carries inline link annotations (`# satisfies -> REQ-004` style) - [x] Snapshot/regression test: depth-0 → only root; depth-1 → root + neighbours; depth-2 → two-hop closure - [x] Cycle handling tested (artifact A → B → A does not loop) - [x] Markdown rendering explicitly out of scope Verification: - `cargo test -p rivet-core --lib` — 931 pass (920 prior + 11 new) - `cargo test -p rivet-cli --test cli_commands` — 66 pass (61 prior + 5 new) - `cargo test -p rivet-cli --test mcp_integration` — 24 pass (22 prior + 2 new) - `cargo clippy --workspace --all-targets -- -D warnings` — clean - `cargo fmt --all -- --check` — clean - `cargo run --release -p rivet-cli -- validate` — error count byte-identical to pristine main (6 errors / 140 warnings / 0 broken cross-refs; the 6 errors live in the `spar:` external fixture and are unaffected) - `cargo run --release -p rivet-cli -- docs check` — PASS Closes #206 Implements: REQ-007 Refs: FEAT-010
📐 Rivet artifact deltaNo artifact changes in this PR. Code-only changes (renderer, CLI wiring, tests) don't touch the artifact graph. |
There was a problem hiding this comment.
⚠️ Performance Alert ⚠️
Possible performance regression was detected for benchmark 'Rivet Criterion Benchmarks'.
Benchmark result of this commit is worse than the previous benchmark result exceeding threshold 1.20.
| Benchmark suite | Current: 1cdcc18 | Previous: 6dfdca6 | Ratio |
|---|---|---|---|
store_insert/10000 |
14314238 ns/iter (± 1141835) |
10777546 ns/iter (± 201358) |
1.33 |
link_graph_build/10000 |
32984738 ns/iter (± 2919346) |
22905310 ns/iter (± 293676) |
1.44 |
validate/10000 |
16249974 ns/iter (± 1401829) |
12167984 ns/iter (± 144437) |
1.34 |
This comment was automatically generated by workflow using github-action-benchmark.
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
5 tasks
avrabe
added a commit
that referenced
this pull request
May 11, 2026
Three queued feature requests now land: rivet bundle (#266), rivet coverage --matrix (#243), s-expr linked-via operator (#265). Plus externals load their own schemas (#267) and STPA TCL numbering is corrected to ISO 26262-8 (#257). Infrastructure: CI concurrency control across all workflows (#258), migration to self-hosted smithy runners (#262), release-npm trigger fix that retroactively unblocked v0.7.0/v0.8.0 npm publication (#261), weekly dependabot (#216), and the wasmtime 42→43 upgrade that retires the RUSTSEC-2026-0114 suppression introduced in v0.8.0 (#260). #125 (provenance-lifecycle) intentionally deferred — 5-week-old branch with conflicts in heavily-churned files (CLAUDE.md, ci.yml, settings). Needs its own attention session, not safe to autonomously rebase. Refs: FEAT-001 Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Closes #206 — implements the
rivet bundle <ID> --depth N --as {yaml,jsonl}CLI command, therivet_bundleMCP tool, and therivet-core/src/bundle.rsmodule. An LLM agent reasoning about an artifact and its neighbours can now get the full link-graph closure in one round-trip instead of N — closing the gap the issue body identified after Karpathy's LLM Wiki.What changed
rivet-core/src/bundle.rs(new). Breadth-first, visit-once traversal overStore::get+Artifact::links, returning a depth-stampedVec<BundleEntry>. Cycles terminate via the visited set. Depth 0 = root only; depth 1 = root + direct neighbours. Dangling targets (links pointing outside the store) appear astarget:references inside the parent'slinks:list but are not expanded.BundleFormat::{Yaml, Jsonl}rendering. YAML is hand-rendered (notserde_yaml) because the# satisfies -> REQ-002inline annotations are the whole point of the format and serde can't round-trip comments. JSONL is oneserde_jsonrecord per line for tool consumers / streaming readers.rivet-cli/src/main.rs— newCommand::Bundle { id, depth, format }variant with--depth(default 1) and--as(defaultyaml). Handlercmd_bundleloads the project context, callsbundle::bundle, prints the rendered output. Invalid--asvalues produce a clear error listing the valid options.rivet-cli/src/mcp.rs— newrivet_bundleMCP tool withBundleParams { id, depth, format }. Defaults match the CLI. Returns the rendered bundle as text content.Acceptance — issue #206 (lifted from the 2026-04-26 + 2026-05-09 triage comments)
rivet-core/src/bundle.rswith closure traversal overStore+Linkgraph, depth-bounded —bundle::bundle(), lines 90–122.rivet bundle <ID> --depth N --as {yaml,jsonl}CLI command —Command::Bundlevariant +cmd_bundlehandler.rivet_bundleso agents call this without shelling —mcp.rs, decorated#[tool(...)]onrivet_bundle.# satisfies -> REQ-004style) — proven bybundle_depth_one_yaml_with_annotations(CLI) andtest_rivet_bundle_yaml_depth_one(MCP).depth_zero_returns_root_only,depth_one_returns_root_plus_neighbours,depth_two_includes_two_hop_closureinbundle::tests.cycle_terminates.Implements: REQ-007(CLI) andRefs: FEAT-010(MCP) — present.Test plan
cargo test -p rivet-core --lib— 931 pass (920 prior + 11 newbundle::tests).cargo test -p rivet-cli --test cli_commands— 66 pass (61 prior + 5 new bundle tests: depth-0 root only, depth-1 + annotations, jsonl shape, missing-root non-zero exit, invalid-format rejection).cargo test -p rivet-cli --test mcp_integration— 24 pass (22 prior + 2 new bundle tests; tools-list count test bumped 15 → 16 to registerrivet_bundle).cargo clippy --workspace --all-targets -- -D warnings— clean.cargo fmt --all -- --check— clean.cargo run --release -p rivet-cli -- validate—6 errors / 140 warnings / 0 broken cross-refs— byte-identical to pristinemain. The 6 errors live in thespar:external fixture and are unaffected.cargo run --release -p rivet-cli -- docs check—PASS (47 files scanned, 0 violations).Out of scope
docs/getting-started.mddeep-dive on bundle — left for a docs-only follow-up so the surface lands first and stays focused (one-PR-per-issue rule).🤖 Generated with Claude Code — issue-triage agent run 2026-05-09.
Generated by Claude Code