Skip to content

Fix #507: warn on stale ledger reads - #511

Open
willwashburn wants to merge 6 commits into
mainfrom
issue-507-stale-ledger-warning
Open

Fix #507: warn on stale ledger reads#511
willwashburn wants to merge 6 commits into
mainfrom
issue-507-stale-ledger-warning

Conversation

@willwashburn

@willwashburn willwashburn commented Aug 3, 2026

Copy link
Copy Markdown
Member

Summary

  • Stores a durable ledger last-write timestamp in schema v7 and updates it once per successful write batch.
  • Exposes typed freshness metadata through the Rust and Node SDKs and includes it in MCP read-tool responses.
  • Warns on stderr for stale or never-written ledgers across summary, hotspots, hotspots --findings, and sessions list.
  • Supports a 24-hour default threshold, config/env overrides, and negative values to disable warnings.
  • Shows the last-write timestamp in burn state status.

Validation

  • cargo fmt --all -- --check
  • cargo test --workspace
  • pnpm --filter @relayburn/mcp build
  • pnpm run test

Fixes #507

🤖 Generated with Claude Code.

@coderabbitai

coderabbitai Bot commented Aug 3, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@willwashburn, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 39 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 2691c3c4-6055-4300-9891-1928a6aa82ea

📥 Commits

Reviewing files that changed from the base of the PR and between 1866848 and 51007f4.

📒 Files selected for processing (20)
  • README.md
  • crates/relayburn-cli/src/commands/freshness.rs
  • crates/relayburn-cli/src/commands/mcp_server.rs
  • crates/relayburn-cli/src/commands/state.rs
  • crates/relayburn-cli/src/util/time.rs
  • crates/relayburn-cli/tests/golden.rs
  • crates/relayburn-cli/tests/smoke.rs
  • crates/relayburn-sdk-node/src/lib.rs
  • crates/relayburn-sdk/src/ledger.rs
  • crates/relayburn-sdk/src/ledger/config.rs
  • crates/relayburn-sdk/src/ledger/db.rs
  • crates/relayburn-sdk/src/ledger/tests.rs
  • crates/relayburn-sdk/src/ledger/writer.rs
  • crates/relayburn-sdk/src/lib.rs
  • packages/mcp/src/tools/fingerprint.test.ts
  • packages/mcp/src/tools/fingerprint.ts
  • packages/mcp/src/tools/session-cost.test.ts
  • packages/sdk-node/test/conformance.test.js
  • tests/fixtures/cli-golden/snapshots/state-status-json.stdout.txt
  • tests/fixtures/cli-golden/snapshots/state-status.stdout.txt
📝 Walkthrough

Walkthrough

The ledger now records its last write time and evaluates configurable staleness. CLI read commands warn for stale data. SDK, Node, and MCP responses expose freshness metadata. Schema migrations, configuration tests, smoke tests, and changelogs cover the new behavior.

Changes

Ledger freshness

Layer / File(s) Summary
Ledger storage and staleness configuration
crates/relayburn-sdk/src/ledger/*, crates/relayburn-sdk/src/query_verbs/*
The schema stores last_write_at_ms. Migrations seed the value for existing ledgers. Write, reset, archive-state, and configuration paths now maintain freshness data.
SDK freshness evaluation
crates/relayburn-sdk/src/lib.rs
LedgerHandle provides real-time and deterministic freshness queries. Results include the last write time, threshold, and stale status.
CLI stale-read warnings
crates/relayburn-cli/src/commands/*, crates/relayburn-cli/tests/smoke.rs, README.md, CHANGELOG.md
Summary, hotspots, and session-list commands warn when data is stale. Status output shows the last write time. Configuration documentation describes the threshold settings.
Node SDK freshness API
crates/relayburn-sdk-node/src/lib.rs, packages/sdk-node/src/*, packages/sdk-node/test/*
The Node SDK exposes ledgerFreshness() with typed options and results. Native bindings, facades, conformance tests, and changelog entries are updated.
MCP freshness payloads
packages/mcp/src/tools/*, packages/mcp/src/*test.ts, packages/mcp/CHANGELOG.md
Fingerprint and session-cost responses include ledgerFreshness. Both tools support injected freshness providers and concurrent lookups.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant ReadCommand
  participant LedgerHandle
  participant archive_state
  participant Renderer
  ReadCommand->>LedgerHandle: request ledger freshness
  LedgerHandle->>archive_state: read last_write_at_ms
  archive_state-->>LedgerHandle: return write timestamp
  LedgerHandle-->>ReadCommand: return stale status and threshold
  ReadCommand->>Renderer: emit warning and render result
Loading

Poem

A rabbit checks the ledger’s beat,
Finds fresh or stale beneath its feet.
Timestamps hop through SDK gates,
MCP shares the ledger’s state.
CLI warns with a friendly cheer.
“Your data’s clear—or overdue, dear!”

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly describes the main change: warnings for stale ledger reads.
Description check ✅ Passed The description directly summarizes stale-ledger tracking, SDK and MCP changes, warnings, configuration, and validation.
✨ 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 issue-507-stale-ledger-warning

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Devin Review found 1 potential issue.

View 1 additional finding in Devin Review.

Open in Devin Review

Comment thread crates/relayburn-cli/src/commands/state.rs Outdated

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

Copy link
Copy Markdown

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: 1866848c93

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

Comment thread crates/relayburn-sdk/src/ledger/config.rs Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
crates/relayburn-sdk/src/ledger/writer.rs (1)

314-361: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win

Only refresh ledger freshness when append_stamp writes derived rows.

append_stamp is reachable from burn stamp write and ingest-side pending-stamp resolution. It currently calls touch_last_write(&tx) after every stamp insert, but stamps are first-party data and append_stamp is considered part of derive replay. If no synthesized.relationships row is written, last_write_at_ms should not advance; update the clock only when synthesized.is_some(), or change the freshness contract and docs if stamp inserts should count as derived-ledger writes.

🤖 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/writer.rs` around lines 314 - 361, Update
append_stamp so touch_last_write(&tx) is called only when
synthesize_relationship(stamp) returns Some and a derived relationship row is
written; leave stamp-only inserts without refreshing last_write_at_ms.
🧹 Nitpick comments (1)
packages/mcp/src/tools/fingerprint.test.ts (1)

18-18: 🗄️ Data Integrity & Integration | 🔵 Trivial | ⚡ Quick win

Cover the complete freshness contract in both tests.

Both tests assert only ledgerFreshness.stale === false. A regression that drops lastWriteAtMs, changes staleAfterMs, or always returns false would pass. Assert all three fields and add a stale: true case.

  • packages/mcp/src/tools/fingerprint.test.ts#L18-L18: assert the complete freshness object and cover the stale case.
  • packages/mcp/src/tools/session-cost.test.ts#L28-L28: apply the same assertions to the session-cost response.
🤖 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 `@packages/mcp/src/tools/fingerprint.test.ts` at line 18, Expand the freshness
assertions in both tests: packages/mcp/src/tools/fingerprint.test.ts at lines
18-18 and packages/mcp/src/tools/session-cost.test.ts at lines 28-28 must verify
lastWriteAtMs, staleAfterMs, and stale, not only stale === false. Add a stale:
true scenario to each test while preserving the existing fresh-case coverage.
🤖 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.

Inline comments:
In `@crates/relayburn-cli/tests/smoke.rs`:
- Around line 74-126: Remove the inherited RELAYBURN_STALE_AFTER_HOURS override
from the freshness smoke-test setup by updating burn() or each of
stale_warning_is_uniform_across_requested_read_surface,
fresh_ledger_does_not_warn, and never_written_ledger_warns. Ensure these tests
use the staleness threshold from config.json and retain their expected warning
behavior.

In `@crates/relayburn-sdk-node/src/lib.rs`:
- Around line 391-403: Update the #[napi] ledger_freshness function to use the
project’s native asynchronous N-API pattern and move Ledger::open plus
ledger_freshness into tokio::task::spawn_blocking, returning the joined result
through the existing BurnError mapping while preserving the LedgerFreshness
conversion.

In `@packages/sdk-node/test/conformance.test.js`:
- Around line 89-94: Update the freshness assertions in the ledgerFreshness
conformance test to accept staleAfterMs as either a number or null, and add or
extend a fixture case with a negative thresholdHours to verify the
disabled-warning response shape if coverage is not already present.

In `@README.md`:
- Around line 215-222: Update the RELAYBURN_CONTENT_STORE table entry in the
README to escape each literal pipe character in the value, preserving the
documented full, hash-only, and off options while keeping the Markdown table at
two columns.

---

Outside diff comments:
In `@crates/relayburn-sdk/src/ledger/writer.rs`:
- Around line 314-361: Update append_stamp so touch_last_write(&tx) is called
only when synthesize_relationship(stamp) returns Some and a derived relationship
row is written; leave stamp-only inserts without refreshing last_write_at_ms.

---

Nitpick comments:
In `@packages/mcp/src/tools/fingerprint.test.ts`:
- Line 18: Expand the freshness assertions in both tests:
packages/mcp/src/tools/fingerprint.test.ts at lines 18-18 and
packages/mcp/src/tools/session-cost.test.ts at lines 28-28 must verify
lastWriteAtMs, staleAfterMs, and stale, not only stale === false. Add a stale:
true scenario to each test while preserving the existing fresh-case coverage.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: d7487436-3b45-44ca-b531-103f77f3551f

📥 Commits

Reviewing files that changed from the base of the PR and between 962b2b7 and 1866848.

📒 Files selected for processing (32)
  • CHANGELOG.md
  • README.md
  • crates/relayburn-cli/src/commands/freshness.rs
  • crates/relayburn-cli/src/commands/hotspots/mod.rs
  • crates/relayburn-cli/src/commands/mcp_server.rs
  • crates/relayburn-cli/src/commands/mod.rs
  • crates/relayburn-cli/src/commands/sessions.rs
  • crates/relayburn-cli/src/commands/state.rs
  • crates/relayburn-cli/src/commands/summary/mod.rs
  • crates/relayburn-cli/tests/smoke.rs
  • crates/relayburn-sdk-node/src/lib.rs
  • crates/relayburn-sdk/src/ledger.rs
  • crates/relayburn-sdk/src/ledger/config.rs
  • crates/relayburn-sdk/src/ledger/db.rs
  • crates/relayburn-sdk/src/ledger/schema.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/state.rs
  • crates/relayburn-sdk/src/query_verbs/tests.rs
  • packages/mcp/CHANGELOG.md
  • packages/mcp/src/end-to-end.test.ts
  • packages/mcp/src/tools/fingerprint.test.ts
  • packages/mcp/src/tools/fingerprint.ts
  • packages/mcp/src/tools/session-cost.test.ts
  • packages/mcp/src/tools/session-cost.ts
  • packages/sdk-node/CHANGELOG.md
  • packages/sdk-node/src/binding.d.ts
  • packages/sdk-node/src/index.cjs
  • packages/sdk-node/src/index.d.ts
  • packages/sdk-node/src/index.js
  • packages/sdk-node/test/conformance.test.js

Comment thread crates/relayburn-cli/tests/smoke.rs
Comment thread crates/relayburn-sdk-node/src/lib.rs
Comment thread packages/sdk-node/test/conformance.test.js
Comment thread README.md

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Review completed against the latest diff

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

Comment thread crates/relayburn-sdk-node/src/lib.rs Outdated
Comment thread crates/relayburn-sdk/src/lib.rs
Comment thread crates/relayburn-sdk/src/ledger/writer.rs
Comment thread crates/relayburn-sdk/src/ledger/db.rs
Comment thread packages/mcp/src/tools/fingerprint.ts
Comment thread packages/sdk-node/test/conformance.test.js Outdated
Comment thread crates/relayburn-sdk/src/ledger/db.rs Outdated
Comment thread crates/relayburn-sdk/src/ledger.rs
Comment thread crates/relayburn-cli/src/commands/mcp_server.rs
Comment thread crates/relayburn-cli/src/commands/mcp_server.rs
@willwashburn

Copy link
Copy Markdown
Member Author

Addressed the two non-inline CodeRabbit findings in 7461342:

  • append_stamp now refreshes freshness only when it actually inserts a synthesized derived relationship; annotation-only stamps no longer mask stale ingested data. Regression tests cover both cases.
  • MCP fingerprint and session-cost tests now assert the complete freshness object and include stale=true cases. The rebuilt MCP suite passes all 23 tests.

Validation also includes cargo test --workspace (841 passed, 5 ignored in the main SDK suite), cargo fmt --all -- --check, and the native-enabled Node SDK conformance suite (12/12).

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.

Stale ledger is silent — no staleness warning on read/report

1 participant