Skip to content

Implement burn state reset over a new SDK verb#389

Merged
willwashburn merged 2 commits intomainfrom
claude/check-issue-341-lkbiJ
May 8, 2026
Merged

Implement burn state reset over a new SDK verb#389
willwashburn merged 2 commits intomainfrom
claude/check-issue-341-lkbiJ

Conversation

@willwashburn
Copy link
Copy Markdown
Member

Summary

Replaces the burn state reset "not yet implemented" stub with a real
presenter, addressing the remaining live half of #341. (The burn summary
half — --by-tool, --quality, --workflow, etc. — has already been
wired up in tree, so this PR closes the last surface stub the issue called
out.)

  • New SDK verbs Ledger::reset() and Ledger::count_reset_targets()
    in crates/relayburn-sdk/src/ledger.rs. reset() is the harder-hitting
    sibling of rebuild_derivable: it wipes every derivable table, the
    stamps first-party table, the entire content.sqlite (FTS index
    included), and resets archive_state (preserves schema_version,
    blanks upstream_cursors_json / last_built_at / last_rebuild_at)
    so the next ingest walks every upstream file from offset 0.
  • CLI run_reset in crates/relayburn-cli/src/commands/state.rs now
    dispatches three modes:
    • no flags → dry run via count_reset_targets(), prints "would drop
      N event row(s) + M stamp(s) + K content row(s)"
    • --force → real wipe via reset()
    • --force --reingest → wipe then drive a single ingest_all sweep
      on the same handle (mirrors the runtime pattern in
      commands/summary.rs::run_ingest)
  • Both human and --json output paths covered. --json returns
    { executed, rowsDropped, stampsDropped, contentRowsDropped, reingest? }.

Closes #341.

Test plan

  • cargo test -p relayburn-sdk --lib reset — three new SDK tests
    cover: full wipe semantics (derivable + stamps + content + cursors +
    FTS); count_reset_targets() is read-only and idempotent; reset()
    is idempotent on an empty ledger (the archive_state row survives
    the bootstrap CHECK constraint).
  • cargo test -p relayburn-cli --test smoke — three new CLI smoke
    tests cover: dry-run does not mutate (asserts the "dry run" /
    --force breadcrumbs, and that both DB files exist after); --force
    emits the structured JSON envelope with executed: true; --reingest
    without --force is rejected at parse time by clap (the existing
    requires = "force" constraint).
  • cargo test --workspace — full suite green (no regressions in
    the ledger / ingest / analyze paths).
  • Manually traced the SDK reset() SQL: derivable + stamps wipes
    in a single BEGIN…COMMIT so a partial failure can't leave the
    events DB half-emptied; FTS sync triggers are dropped/recreated
    around the bulk delete (same dance as rebuild_derivable).

https://claude.ai/code/session_01VcLNz8KtnLe7zox9xdvfyj


Generated by Claude Code

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 8, 2026

Review Change Stack

Important

Review skipped

Review was skipped due to path filters

⛔ Files ignored due to path filters (1)
  • pnpm-lock.yaml is excluded by !**/pnpm-lock.yaml

CodeRabbit blocks several paths by default. You can override this behavior by explicitly including those paths in the path filters. For example, including **/dist/** will override the default block on the dist directory, by removing the pattern from both the lists.

⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 30b3faea-9d6e-49a2-b518-73027328fc27

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

This PR completes the burn state reset command, replacing a previous "not yet implemented" stub with a full implementation backed by new SDK ledger reset APIs. The feature wipes derived state and content rows from both ledger databases, clears ingest cursor state, and optionally runs a full reingest sweep in the same invocation when --reingest is passed.

Changes

State Reset Implementation

Layer / File(s) Summary
Data Contract
crates/relayburn-sdk/src/ledger.rs
New ResetSummary struct holds row/stamp/content drop counts with Default, Clone, Debug, PartialEq, Eq derives.
SDK Reset Methods
crates/relayburn-sdk/src/ledger.rs
Ledger::count_reset_targets() queries derivable/stamp/content row counts without mutation; Ledger::reset() deletes tables and content, resets archive_state cursors, rebuilds FTS index, returns captured counts.
SDK Test Coverage
crates/relayburn-sdk/src/ledger/tests.rs
Four tests verify reset wipes all data and clears cursors, count_reset_targets is read-only/idempotent, SQL errors propagate, and reset is repeatable on empty ledger with consistent cursor semantics.
Public API
crates/relayburn-sdk/src/lib.rs
ResetSummary added to crate::reader re-exports.
CLI Dependencies
crates/relayburn-cli/Cargo.toml
Development dependency tempfile = "3" added for isolated test home directories.
CLI Command Wiring
crates/relayburn-cli/src/commands/state.rs
SDK imports updated; run_reset() opens ledger, computes dry-run when --force absent, executes reset otherwise, conditionally triggers reingest with --reingest; run_reset_reingest() creates async runtime for ingest_all(); print_reset_report() formats JSON/human output with drop counts and optional reingest metrics.
CLI Smoke Tests
crates/relayburn-cli/tests/smoke.rs
Three tests: dry-run default checks stdout/DB creation; --force --json parses executed state and zero drops; --reingest without --force fails at parse time.
Documentation
CHANGELOG.md
Unreleased entry documents completed burn state reset with dry-run, --force, and --force --reingest semantics.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

  • AgentWorkforce/burn#313: Implements the previously-stubbed burn state reset command with full CLI wiring and SDK integration.
  • AgentWorkforce/burn#351: Addresses cursor persistence and ledger writer/CLI timestamp helpers used by the ingest/state reset flows.

Poem

🐰 The reset command hops out of the stub,
With dry-runs and forces, and reingest's love—
Ledgers grow clean, cursors find their place,
All wired up neat in a feature so base! ✨

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and concisely summarizes the main change: implementing the burn state reset command using a new SDK verb.
Description check ✅ Passed The description is detailed and directly related to the changeset, explaining the SDK verbs added, CLI modes supported, and test coverage for the new burn state reset feature.
Linked Issues check ✅ Passed The PR implements burn state reset as called for in #341, replacing the previous stub with a working presenter backed by new SDK Ledger::reset() and Ledger::count_reset_targets() methods, addressing the issue's core objective.
Out of Scope Changes check ✅ Passed All changes directly support the implementation of burn state reset and related SDK functionality. No unrelated modifications or scope creep detected in the changeset.
Docstring Coverage ✅ Passed Docstring coverage is 100.00% which is sufficient. The required threshold is 80.00%.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/check-issue-341-lkbiJ

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

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: f080d2f74c

ℹ️ 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".

let rt = tokio::runtime::Builder::new_current_thread()
.enable_all()
.build()?;
let opts = RawIngestOptions::default();
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Thread --ledger-path into reset re-ingest options

run_reset opens the ledger with globals.ledger_path, but the --reingest path calls ingest_all with RawIngestOptions::default(), which leaves ledger_home unset. In that case ingest-side config and pending-stamp resolution fall back to RELAYBURN_HOME/default home, so burn --ledger-path <custom> state reset --force --reingest can re-ingest with the wrong sidecar state (miss pending stamps, wrong content mode) even though it writes into the custom DB. Pass the same home override into ingest options here.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in b09d1dc. run_reset_reingest now takes the --ledger-path override from globals.ledger_path and threads it into RawIngestOptions::ledger_home, so the reingest sweep resolves config and pending-stamp manifests under the same home as the open handle (no more split-brain between custom DB and default $RELAYBURN_HOME sidecars).

Note: commands/summary.rs::run_ingest has the same shape and is similarly affected. Left it out of this PR since it's a pre-existing bug on a different surface; happy to file a follow-up if you want it tracked.


Generated by Claude Code

Comment thread crates/relayburn-sdk/src/ledger.rs Outdated
Comment on lines +386 to +387
.query_row(&format!("SELECT COUNT(*) FROM {table}"), [], |r| r.get(0))
.unwrap_or(0);
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P2 Badge Propagate SQL errors when counting reset targets

count_reset_targets() converts query_row failures to 0 via unwrap_or(0), which means schema/corruption/read errors in burn.sqlite are silently reported as successful zero-count dry-runs. Because this method returns Result<ResetSummary>, callers expect DB failures to surface; swallowing them can mislead operators and automation into thinking reset is safe/no-op when the ledger is actually unhealthy.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Fixed in b09d1dc. count_reset_targets() now uses ? on every query_row (no more unwrap_or(0) swallowing schema/corruption errors), and there's a new SDK test (count_reset_targets_propagates_sql_errors) that drops the turns table out from under an open handle and asserts the call returns Err.


Generated by Claude Code

Replace the "not yet implemented" stub with a real presenter that
dry-runs by default, wipes via `--force`, and optionally re-ingests
via `--force --reingest`. Adds `Ledger::reset()` and
`count_reset_targets()` to the SDK; reset is the harder-hitting
sibling of `rebuild_derivable` — it also drops stamps and blanks
ingest cursors so the next ingest walks every upstream file from
offset 0.

Closes #341.

https://claude.ai/code/session_01VcLNz8KtnLe7zox9xdvfyj
@willwashburn willwashburn force-pushed the claude/check-issue-341-lkbiJ branch from f080d2f to b09d1dc Compare May 8, 2026 10:59
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 (2)
CHANGELOG.md (1)

9-16: ⚡ Quick win

Drop the issue link per changelog guidelines.

The entry content is good (impact-first, names command/API, describes practical effect), but it includes (#341) at the end. As per coding guidelines, issue/PR links should be dropped from changelog entries.

Suggested fix
   same invocation. Replaces the prior "not yet implemented" stub.
-  (`#341`)
🤖 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 `@CHANGELOG.md` around lines 9 - 16, Remove the issue/PR link suffix "(`#341`)"
from the CHANGELOG entry that begins with "`relayburn-cli` / `relayburn-sdk`:
`burn state reset`..." so the descriptive text remains unchanged but complies
with changelog guidelines; also scan that same CHANGELOG.md entry for any other
parenthesized issue/PR references and delete them if present.
crates/relayburn-sdk/src/ledger.rs (1)

420-463: 💤 Low value

Atomicity gap between burn.sqlite and content.sqlite wipes.

The burn.sqlite wipe (derivable tables + stamps + archive_state reset) runs in a transaction (lines 427-444), but the content.sqlite wipe (lines 449-461) runs separately afterward. If the content wipe fails after the burn transaction commits, the ledger is left in an inconsistent state: cursors are blanked but content rows still exist.

This mirrors the existing rebuild_derivable() behavior (line 305), so it may be intentional given the two-DB architecture, but worth confirming the failure mode is acceptable or if a rollback-on-content-failure strategy is needed.

🤖 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/ledger.rs` around lines 420 - 463, The reset()
function currently commits the burn.sqlite transaction (conns.burn) before
executing the content.sqlite wipe (conns.content), creating an atomicity gap
similar to rebuild_derivable(); to fix, make the two wipes atomic by
coordinating them: perform the content wipe (the same batch used now) before
committing the burn transaction or use an explicit two-DB failure-recovery
pattern (e.g., begin a savepoint/prepare step, run conns.content.execute_batch,
and only commit conns.burn after content succeeds, or on content failure roll
back the burn transaction), updating reset() to ensure either both burn changes
and content changes succeed or the burn changes are rolled back; reference
reset(), conns.burn transaction(), the DERIVABLE_TABLES loop, and
conns.content.execute_batch when locating where to change the
sequence/transactional handling.
🤖 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 `@CHANGELOG.md`:
- Around line 9-16: Remove the issue/PR link suffix "(`#341`)" from the CHANGELOG
entry that begins with "`relayburn-cli` / `relayburn-sdk`: `burn state
reset`..." so the descriptive text remains unchanged but complies with changelog
guidelines; also scan that same CHANGELOG.md entry for any other parenthesized
issue/PR references and delete them if present.

In `@crates/relayburn-sdk/src/ledger.rs`:
- Around line 420-463: The reset() function currently commits the burn.sqlite
transaction (conns.burn) before executing the content.sqlite wipe
(conns.content), creating an atomicity gap similar to rebuild_derivable(); to
fix, make the two wipes atomic by coordinating them: perform the content wipe
(the same batch used now) before committing the burn transaction or use an
explicit two-DB failure-recovery pattern (e.g., begin a savepoint/prepare step,
run conns.content.execute_batch, and only commit conns.burn after content
succeeds, or on content failure roll back the burn transaction), updating
reset() to ensure either both burn changes and content changes succeed or the
burn changes are rolled back; reference reset(), conns.burn transaction(), the
DERIVABLE_TABLES loop, and conns.content.execute_batch when locating where to
change the sequence/transactional handling.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 6709e35e-7c11-4837-9efa-55677870f948

📥 Commits

Reviewing files that changed from the base of the PR and between 0f2dc86 and b09d1dc.

⛔ Files ignored due to path filters (1)
  • Cargo.lock is excluded by !**/*.lock
📒 Files selected for processing (7)
  • CHANGELOG.md
  • crates/relayburn-cli/Cargo.toml
  • crates/relayburn-cli/src/commands/state.rs
  • crates/relayburn-cli/tests/smoke.rs
  • crates/relayburn-sdk/src/ledger.rs
  • crates/relayburn-sdk/src/ledger/tests.rs
  • crates/relayburn-sdk/src/lib.rs

The 2.4.0 release commit on main bumped each `packages/relayburn` and
`packages/sdk-node` optional-dep specifier from 2.3.0 to 2.4.0 in
package.json but did not regenerate `pnpm-lock.yaml`. CI's
`pnpm install --frozen-lockfile` therefore fails on every PR rebased
onto post-2.4.0 main with `ERR_PNPM_OUTDATED_LOCKFILE`.

This is a pure specifier sync: no resolved-version churn, no new deps.

https://claude.ai/code/session_01VcLNz8KtnLe7zox9xdvfyj
@willwashburn willwashburn merged commit 1b8c75d into main May 8, 2026
11 checks passed
@willwashburn willwashburn deleted the claude/check-issue-341-lkbiJ branch May 8, 2026 11:18
@coderabbitai coderabbitai Bot mentioned this pull request May 8, 2026
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.

Rust hygiene: trim 'not yet implemented' stubs from CLI subcommand surface

2 participants