Skip to content

feat(merk): extend EstimatedLayerSizes/EstimatedSumTrees for sum-bearing leaves and Provable* trees - #674

Merged
QuantumExplorer merged 8 commits into
developfrom
claude/thirsty-colden-c82bdf
May 19, 2026
Merged

feat(merk): extend EstimatedLayerSizes/EstimatedSumTrees for sum-bearing leaves and Provable* trees#674
QuantumExplorer merged 8 commits into
developfrom
claude/thirsty-colden-c82bdf

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented May 18, 2026

Copy link
Copy Markdown
Member

Summary

Extends merk's average-case cost estimation to fully account for v12-only sum-bearing element variants and the Provable* tree family. Building block for dash-platform PR #3661, which can switch its EstimatedLayerInformation plumbing onto these new variants once this lands and the pin is bumped.

Today, callers approximating layers built on Element::ItemWithSumItem or Element::ReferenceWithSumItem can only reach EstimatedLayerSizes::AllItems(...) / AllReference(...), undercharging by ~10 bytes per insert (an i64 sum_value's worst-case varint). For property-name trees declared range_summable / range_summable + range_countable, the Provable* tree types collapse into non_sum_trees_weight and silently report zero per-node aggregate cost.

What changed

EstimatedLayerSizes:

EstimatedSumTrees:

  • SomeSumTrees gains four weights: provable_sum_trees_weight, provable_count_trees_weight, provable_count_sum_trees_weight, provable_count_provable_sum_trees_weight.
  • Four AllProvable* homogeneous shortcut variants mirroring AllSumTrees / AllCountTrees etc.

Versioning:

  • MerkAverageCaseCostsVersions::sum_tree_estimated_size bumped 1 → 2 in v3 only. v0 and v1 formulas are byte-stable for already-shipped grove versions; the new provable_* weights are silently ignored on those paths.
  • New check_grovedb_v0_v1_or_v2! macro added to support the three-version dispatch.

All four propagate match arms (v0 + v1, each for replaced_bytes + storage_loaded_bytes) wired through the new variants and Mix fields.

The new EstimatedLayerSizes variants are not version-gated themselves — they're discriminants new at the type level, so existing callers compile unchanged. dash-platform's v11 sites stay on AllItems(...) (consensus-locked) and v12+ sites can opt in to AllItemsWithSumItem(...).

Tests

14 new unit tests in average_case_costs::tests:

  • +10 layer-size formula for AllItemsWithSumItem / AllReferencesWithSumItem (assertion: plain + 10).
  • add_average_case_merk_propagate cost strictly exceeds the plain variant for the same key/value/flags, on both replaced_bytes and storage_loaded_bytes.
  • Mix with only the new sum-item fields populated.
  • v0 output pinned under new provable_* weights (regression — grove v1 path).
  • v1 output pinned under new provable_* weights (regression — grove v2 production path).
  • v2 actually responds to the new weights (negative test for v0/v1 stability).
  • Homogeneous SomeSumTrees equals the matching AllProvable* shortcut.
  • v2 divide-by-zero guard still trips when all weights (including new ones) are zero.

All existing tests pass: grovedb-merk 645 ✓, grovedb 1834 ✓.

Test plan

  • cargo build — full workspace
  • cargo test -p grovedb-merk — 645 pass
  • cargo test -p grovedb — 1834 pass
  • No new clippy warnings introduced

Related

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Enhanced average-case cost estimation with weighted/provable weights and expanded layer/sum-tree sizing; versioned dispatch for multiple estimation strategies (v0/v1/v2).
    • Protocol v3 cost parameter updates to better reflect propagation and sum-tree sizing.
  • Tests

    • Broadened test coverage for weighted mixes, provable-weight scenarios, divide-by-zero and overflow guards, and version-specific behaviors.

Review Change Stack

…ing leaves and Provable* trees

Adds layer-level cost estimation support for the v12-only sum-bearing
element variants and the Provable* tree family. Without these, dash-platform's
EstimatedLayerInformation plumbing can only reach approximations that
under-count by ~10 bytes per ItemWithSumItem insert (an i64 sum_value's
worst-case varint), and silently zeros out the cost of every property-name
tree built on ProvableSumTree / ProvableCountTree / ProvableCountSumTree /
ProvableCountProvableSumTree.

EstimatedLayerSizes additions:
- AllItemsWithSumItem / AllReferencesWithSumItem variants matching the
  +10 sum-value worst-case constant used by
  Element::required_{item,reference}_with_sum_item_space (#673).
- Mix extended with items_with_sum_item_size /
  references_with_sum_item_size fields so a mixed layer can weight
  sum-bearing leaves alongside plain ones.

EstimatedSumTrees additions:
- SomeSumTrees gains four new weights:
  provable_sum_trees_weight, provable_count_trees_weight,
  provable_count_sum_trees_weight,
  provable_count_provable_sum_trees_weight.
- Four AllProvable* homogeneous shortcut variants mirroring the
  existing AllSumTrees / AllCountTrees etc.

Versioning:
- estimated_size bumped 1 -> 2 in v3 only. v0/v1 formulas remain
  byte-stable for already-shipped grove versions (regression tests
  pin this); the new provable_* weights are silently ignored by
  v0/v1 and only folded into the weighted average by v2.

All four propagate match arms (v0 + v1, replaced_bytes +
storage_loaded_bytes) wired through; new layer variants add +10 on top
of the +3/+5 plain-item/reference base. Test fixtures in
grovedb/src/batch and grovedb/src/tests/misc_coverage_tests updated for
the new SomeSumTrees fields (defaulted to 0).

14 new tests cover: the +10 layer-size formula for items/references,
propagate cost strictly exceeding plain variants, Mix with only
sum-item fields, v0/v1 output stability under new weights, v2 actually
using them, homogeneous-vs-shortcut equivalence, and the v2
divide-by-zero guard.

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

coderabbitai Bot commented May 18, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@QuantumExplorer has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 4 minutes and 52 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b512415b-74b6-441d-9ae9-ed7344472d64

📥 Commits

Reviewing files that changed from the base of the PR and between 8b6bbee and 49e17ce.

📒 Files selected for processing (12)
  • merk/src/estimated_costs/average_case_costs/add_average_case_merk_propagate/mod.rs
  • merk/src/estimated_costs/average_case_costs/add_average_case_merk_propagate/v0.rs
  • merk/src/estimated_costs/average_case_costs/add_average_case_merk_propagate/v1.rs
  • merk/src/estimated_costs/average_case_costs/add_average_case_merk_propagate/v2.rs
  • merk/src/estimated_costs/average_case_costs/estimated_sum_trees_size/mod.rs
  • merk/src/estimated_costs/average_case_costs/estimated_sum_trees_size/v0.rs
  • merk/src/estimated_costs/average_case_costs/estimated_sum_trees_size/v1.rs
  • merk/src/estimated_costs/average_case_costs/estimated_sum_trees_size/v2.rs
  • merk/src/estimated_costs/average_case_costs/mod.rs
  • merk/src/estimated_costs/average_case_costs/value_with_feature_and_flags_size/mod.rs
  • merk/src/estimated_costs/average_case_costs/value_with_feature_and_flags_size/v0.rs
  • merk/src/estimated_costs/average_case_costs/value_with_feature_and_flags_size/v1.rs
📝 Walkthrough

Walkthrough

Adds provable-weight fields and version-dispatched average-case Merk cost logic: new types/helpers, v0/v1/v2 sizing and propagation implementations (v2 folds provable weights), version metadata and a macro, plus updated tests to supply new provable weight fields.

Changes

Provable weights cost estimation

Layer / File(s) Summary
Version checking and metadata
grovedb-version/src/lib.rs, grovedb-version/src/version/v1.rs, grovedb-version/src/version/v2.rs, grovedb-version/src/version/v3.rs, grovedb-version/src/version/merk_versions.rs
Adds check_grovedb_v0_v1_or_v2! macro and new value_with_feature_and_flags_size feature-version field; updates GROVE_V1/V2/V3 initializers to include/version these fields.
Core average-case model and helpers
merk/src/estimated_costs/average_case_costs/mod.rs
New average_case_costs module: type aliases, EstimatedSumTrees, EstimatedLayerSizes, EstimatedLayerCount, EstimatedLayerInformation, KV/node sizing helpers, and public cost-accumulation helpers (feature-gated).
EstimatedSumTrees estimated_size (v0/v1/v2)
merk/src/estimated_costs/average_case_costs/estimated_sum_trees_size/*
Adds dispatcher EstimatedSumTrees::estimated_size and v0/v1/v2 implementations; v2 includes four new provable weight axes in totals and weighted sums.
Value-with-feature-and-flags sizing (v0/v1)
merk/src/estimated_costs/average_case_costs/value_with_feature_and_flags_size/*
Version-dispatched EstimatedLayerSizes::value_with_feature_and_flags_size: v0 preserves legacy unweighted-mix averaging; v1 performs weighted averaging across kinds.
add_average_case_merk_propagate dispatcher
merk/src/estimated_costs/average_case_costs/add_average_case_merk_propagate/mod.rs
Adds wrapper average_case_merk_propagate and dispatcher add_average_case_merk_propagate that routes to v0/v1/v2 implementations and returns version errors on unknown versions.
Propagation implementations (v0, v1, v2)
merk/src/estimated_costs/average_case_costs/add_average_case_merk_propagate/v0.rs, .../v1.rs, .../v2.rs
Adds three versioned propagation implementations computing nodes_updated, seek/hash counters, and storage replaced/loaded byte estimates; Mix arms perform weighted aggregations with checked arithmetic; v2 scales by nodes_updated before dividing by total_weight.
Grovedb tests and fixtures
grovedb/src/batch/estimated_costs/average_case_costs.rs, grovedb/src/tests/misc_coverage_tests.rs
Updates many test initializers to include new provable_* weight fields so new estimators and v2 logic are covered.

Sequence Diagram(s)

No sequence diagram generated in the visible section.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related issues

Poem

🐰 I hopped through weights both old and new,
V0 and V1 kept the familiar view,
V2 counts provable weights in the mix,
Tests updated, dispatch patched and fixed,
Cost trees now weigh what they’re due.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: extending EstimatedLayerSizes and EstimatedSumTrees to support sum-bearing leaves and Provable* trees, which is the primary focus across the file changes.
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 unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/thirsty-colden-c82bdf

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 and usage tips.

@codecov

codecov Bot commented May 18, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.44675% with 134 lines in your changes missing coverage. Please review.
✅ Project coverage is 91.41%. Comparing base (e47626e) to head (49e17ce).

Files with missing lines Patch % Lines
...e_case_costs/add_average_case_merk_propagate/v1.rs 81.68% 61 Missing ⚠️
...case_costs/value_with_feature_and_flags_size/v0.rs 65.11% 30 Missing ⚠️
.../average_case_costs/estimated_sum_trees_size/v1.rs 71.42% 12 Missing ⚠️
...e_case_costs/add_average_case_merk_propagate/v0.rs 96.69% 11 Missing ⚠️
.../average_case_costs/estimated_sum_trees_size/v0.rs 52.38% 10 Missing ⚠️
...e_case_costs/add_average_case_merk_propagate/v2.rs 97.59% 8 Missing ⚠️
merk/src/estimated_costs/average_case_costs/mod.rs 99.90% 1 Missing ⚠️
...case_costs/value_with_feature_and_flags_size/v1.rs 98.79% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff             @@
##           develop     #674      +/-   ##
===========================================
+ Coverage    91.39%   91.41%   +0.01%     
===========================================
  Files          224      235      +11     
  Lines        65441    66867    +1426     
===========================================
+ Hits         59811    61127    +1316     
- Misses        5630     5740     +110     
Components Coverage Δ
grovedb-core 88.93% <100.00%> (+<0.01%) ⬆️
merk 92.24% <94.43%> (+<0.01%) ⬆️
storage 86.36% <ø> (ø)
commitment-tree 96.43% <ø> (ø)
mmr 96.76% <ø> (ø)
bulk-append-tree 89.26% <ø> (ø)
element 97.36% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

…nd version-gate error

Closes the coverage gaps codecov/patch flagged on PR #674:

- v0 propagate (grove v1 dispatch) with `AllItemsWithSumItem` and
  `AllReferencesWithSumItem` — the +10 sum-value adjustment must flow
  through identically on the v0 path even though dash-platform's v11
  sites stay on plain `AllItems`.
- v0 propagate with `Mix` containing both `items_with_sum_item_size`
  and `references_with_sum_item_size` populated — exercises both the
  replaced_bytes and storage_loaded_bytes Mix arms in v0.
- v1 propagate with `Mix` containing only `references_with_sum_item_size`
  populated — pre-existing test covered the items branch but not refs.
- `EstimatedSumTrees::estimated_size` version-gate error path —
  exercises `check_grovedb_v0_v1_or_v2!` for an unknown version.

All 57 estimated_costs tests pass.

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 3

Caution

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

⚠️ Outside diff range comments (1)
merk/src/estimated_costs/average_case_costs.rs (1)

800-803: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

The mixed propagation total is divided by nodes_updated when it should be scaled by it.

total_updates_cost is already the weighted per-node sum. To get total bytes for the propagation, this needs to be converted with nodes_updated * total_updates_cost / total_weight (or an equivalent checked form). Dividing by nodes_updated * total_weight collapses multi-level mixes to a fraction of a single-node update, and the new *_with_sum_item_size branches inherit the same undercount.

Also applies to: 891-897, 981-983, 1082-1088, 1205-1207, 1296-1302, 1386-1388, 1487-1493

🤖 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 `@merk/src/estimated_costs/average_case_costs.rs` around lines 800 - 803, The
mixed-propagation calculation incorrectly divides by nodes_updated (and by
nodes_updated * total_weight) instead of scaling the weighted per-node sum to a
total; fix the branches that compute propagation bytes (including the new
*_with_sum_item_size branches) to compute total propagation as nodes_updated *
total_updates_cost / total_weight using checked arithmetic (e.g., compute
weighted_nodes_updated = (nodes_updated as u64).checked_mul(total_updates_cost
as u64)? then divide by total_weight via checked_div or equivalent), replacing
any instances where total_updates_cost is divided by nodes_updated or by
nodes_updated * total_weight; update the same pattern at the other listed sites
(around the weighted_nodes_updated use and at lines noted in the comment) to
ensure multi-level mixes are scaled up, not down.
🤖 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 `@grovedb-version/src/lib.rs`:
- Around line 85-94: The macro check_grovedb_v0_v1_or_v2! currently emits a bare
GroveVersionError::UnknownVersionMismatch which requires callers to have
GroveVersionError in scope; update the macro to reference the error via the
crate path (use $crate::error::GroveVersionError::UnknownVersionMismatch) so the
expansion is hygienic and independent of the caller's imports, and adjust any
other GroveVersionError occurrences in that macro to
$crate::error::GroveVersionError accordingly.

In `@merk/src/estimated_costs/average_case_costs.rs`:
- Around line 460-476: combined_size is computed by summing raw sizes rather
than size*weight, which undercounts items with weight>1; update the averaging to
multiply each size by its corresponding weight before adding. Specifically, in
the code that computes combined_size (currently using item_size, ref_size,
subtree_size, item_sum_size, ref_sum_size) replace the plain checked_add chain
with checked_mul of each size by its matching weight
(item_size.checked_mul(item_weight), ref_size.checked_mul(ref_weight),
subtree_size.checked_mul(subtree_weight),
item_sum_size.checked_mul(item_sum_weight),
ref_sum_size.checked_mul(ref_sum_weight)) and then checked_add them together,
propagating the same Error::Overflow on any checked_* failure, and finally
divide that weighted numerator by combined_weight as before.
- Around line 118-123: The v0 branch currently checks total_weight_legacy but
then divides by (*sum_trees_weight as u32 + *non_sum_trees_weight as u32), which
can still be zero; update the guard inside the version == 0 branch to explicitly
check the actual divisor (sum_trees_weight + non_sum_trees_weight) and return
Err(Error::DivideByZero(...)) when that sum is zero before performing the
division; locate the check and division around the variables version,
total_weight_legacy, sum_trees_weight, and non_sum_trees_weight and replace the
guard to validate the actual divisor used.

---

Outside diff comments:
In `@merk/src/estimated_costs/average_case_costs.rs`:
- Around line 800-803: The mixed-propagation calculation incorrectly divides by
nodes_updated (and by nodes_updated * total_weight) instead of scaling the
weighted per-node sum to a total; fix the branches that compute propagation
bytes (including the new *_with_sum_item_size branches) to compute total
propagation as nodes_updated * total_updates_cost / total_weight using checked
arithmetic (e.g., compute weighted_nodes_updated = (nodes_updated as
u64).checked_mul(total_updates_cost as u64)? then divide by total_weight via
checked_div or equivalent), replacing any instances where total_updates_cost is
divided by nodes_updated or by nodes_updated * total_weight; update the same
pattern at the other listed sites (around the weighted_nodes_updated use and at
lines noted in the comment) to ensure multi-level mixes are scaled up, not down.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 9e112fdc-e123-441f-a828-09e3ba855455

📥 Commits

Reviewing files that changed from the base of the PR and between e47626e and 22bfe42.

📒 Files selected for processing (5)
  • grovedb-version/src/lib.rs
  • grovedb-version/src/version/v3.rs
  • grovedb/src/batch/estimated_costs/average_case_costs.rs
  • grovedb/src/tests/misc_coverage_tests.rs
  • merk/src/estimated_costs/average_case_costs.rs

Comment thread grovedb-version/src/lib.rs Outdated
Comment thread merk/src/estimated_costs/average_case_costs.rs Outdated
Comment thread merk/src/estimated_costs/average_case_costs.rs Outdated
QuantumExplorer and others added 2 commits May 19, 2026 06:48
…atedSumTrees

Addresses CodeRabbit review on PR #674:

1. `check_grovedb_v0_v1_or_v2!` macro: qualify `GroveVersionError` as
   `$crate::error::GroveVersionError` so the expansion is hygienic and
   doesn't require callers to import the type. Matches the convention
   in the `*_with_cost` variants already in the file.

2. `EstimatedSumTrees::estimated_size` v0 path: guard the actual v0
   divisor `(sum_trees_weight + non_sum_trees_weight)` rather than
   the legacy total. A layer with only `big_sum_trees_weight` (or any
   count weight) populated produced a nonzero legacy total but a zero
   v0 denominator — silently panicking on `/ 0` instead of surfacing
   `DivideByZero`. The original guard had the same latent bug (this
   PR's branch reorg made it observable); fix in v0 only, v1/v2
   denominators remain `total_weight_legacy` / `total_weight` as
   before.

Regression test pins the new v0 guard.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…fixes

Closes the second half of CodeRabbit's review on PR #674. Both bugs are
pre-existing, but now version-gated so grove v1/v2 preserve their
shipped (buggy) outputs and only grove v3+ picks up the corrections.

### Bug 1: `EstimatedLayerSizes::value_with_feature_and_flags_size`

The Mix arm averaged kinds as:

    Σ size_i / Σ weight_i             (v0, legacy)

instead of the proper weighted average:

    Σ (size_i · weight_i) / Σ weight_i   (v1, fixed)

On a 3:1:2 item/ref/subtree mix that's `(37+30+7)/6 = 12` (legacy) vs
the correct `(3·37 + 1·30 + 2·7)/6 = 25`.

The function is now dispatched via a new
`MerkAverageCaseCostsVersions::value_with_feature_and_flags_size` field
(`v1.rs/v2.rs → 0`, `v3.rs → 1`). Non-Mix arms are unchanged.

### Bug 2: `add_average_case_merk_propagate` Mix arms

v1 (used by grove v2) computes:

    total_replaced_bytes = Σ(weight_i · cost_i) / (nodes_updated · total_weight)

This is the per-node weighted average divided by `nodes_updated`. For
an items-only Mix layer it returns `cost / nodes_updated²` — a Mix
that's semantically equivalent to `AllItems` underestimates by a
factor of `nodes_updated²`.

v2 (new, used by grove v3) computes:

    total_replaced_bytes = nodes_updated · Σ(weight_i · cost_i) / total_weight

which matches what the homogeneous `AllItems`/`AllSubtrees` arms
already do. Bumped `add_average_case_merk_propagate: 1 → 2` in v3 only.
Pinning test asserts v2 Mix-items-only equals AllItems exactly.

### Version table (grovedb-version)

- v1 grove (consensus-locked v11 production): unchanged — v0 propagate, v0 value_with...
- v2 grove (consensus-locked v12 production): unchanged — v1 propagate, v0 value_with...
- v3 grove (active dev version): v2 propagate, v1 value_with... (new formulas)

### Tests

8 new tests:
- `test_value_size_mix_weighted_combination_v1` — assert `25`, the correct weighted average
- `test_value_size_mix_weighted_combination_v0_legacy` — assert `12`, pinning the v0 path
- `test_value_with_feature_and_flags_size_unknown_version_error`
- `test_propagate_v2_mix_items_only_matches_all_items` — v2 invariant
- `test_propagate_v2_mix_exceeds_v1_mix` — v2 strictly larger than v1 on same Mix
- `test_propagate_v1_v2_agree_on_non_mix_variants` — only the Mix arm changed
- `test_propagate_unknown_version_error_lists_all_three` — error path for new dispatch row
- (renamed the original test_value_size_mix_weighted_combination → _v1)

All 657 merk tests pass; all 1834 grovedb tests pass (the existing
batch fee-cost tests don't use Mix, so no fee drift in production
scenarios).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…les (platform-style)

Restructures `merk/src/estimated_costs/average_case_costs.rs` from a
single file with inline version branches / co-located v* functions
into the dash-platform-style layout: one directory per versioned
function with a `mod.rs` dispatcher and per-version `v0.rs`/`v1.rs`/
`v2.rs` files.

```
merk/src/estimated_costs/average_case_costs/
├── mod.rs                                          # types, non-versioned methods, helpers, tests
├── estimated_sum_trees_size/                       # EstimatedSumTrees::estimated_size
│   ├── mod.rs                                      # version dispatcher
│   ├── v0.rs                                       # grove v1 formula
│   ├── v1.rs                                       # grove v2 formula
│   └── v2.rs                                       # grove v3 formula (adds Provable* weights)
├── value_with_feature_and_flags_size/              # EstimatedLayerSizes::value_with_...
│   ├── mod.rs
│   ├── v0.rs                                       # legacy unweighted Mix average
│   └── v1.rs                                       # proper weighted Mix average
└── add_average_case_merk_propagate/                # free fn dispatcher
    ├── mod.rs                                      # also exports average_case_merk_propagate wrapper
    ├── v0.rs                                       # grove v1
    ├── v1.rs                                       # grove v2
    └── v2.rs                                       # grove v3 (Mix divisor fix)
```

`EstimatedSumTrees::estimated_size` was the one CodeRabbit-style
target: previously it had an inline `if version == 0 { ... } else if
version == 1 { ... }` chain after a `check_grovedb_v0_v1_or_v2!`
macro. It now follows the same dispatcher-via-match pattern as the
other two functions (and the per-version logic is split across three
files for readability).

No behavior change — code was moved as-is. All 657 merk tests + 1834
grovedb tests pass.

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

@coderabbitai coderabbitai 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.

Actionable comments posted: 1

🧹 Nitpick comments (1)
merk/src/estimated_costs/average_case_costs/value_with_feature_and_flags_size/v0.rs (1)

116-118: 💤 Low value

Minor: Error message is misleading for checked_div.

checked_div returns None on divide-by-zero, not overflow. While this path is unreachable due to the nonzero_kinds == 0 guard at line 79, the error message could be more accurate for defensive coding.

🔧 Suggested improvement
 combined_size
     .checked_div(combined_weight)
-    .ok_or(Error::Overflow("overflow for value size"))
+    .ok_or(Error::Overflow("value size divide by zero"))
🤖 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
`@merk/src/estimated_costs/average_case_costs/value_with_feature_and_flags_size/v0.rs`
around lines 116 - 118, The error message for the checked_div call on
combined_size / combined_weight is misleading (checked_div returns None on
divide-by-zero); update the error to accurately reflect a division-by-zero
failure instead of overflow—replace the Error::Overflow("overflow for value
size") used with combined_size.checked_div(combined_weight) with a
DivisionByZero-style error or a message like "division by zero for value size"
(preserve the existing nonzero_kinds guard logic that makes this path
unreachable but keep defensive, clear wording).
🤖 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 `@merk/src/estimated_costs/average_case_costs/mod.rs`:
- Around line 199-213: The two calls that propagate errors
directly—estimated_sum_trees.estimated_size(grove_version) inside
EstimatedLayerSizes::AllSubtrees and est.estimated_size(grove_version) inside
EstimatedLayerSizes::Mix—should be wrapped with a map_err to add call-site
context (use Error::CorruptedData(format!("...: {}", e))) so failures include a
descriptive message; update both sites to replace the `?`-propagated call with
`.map_err(|e| Error::CorruptedData(format!("failed to estimate subtree size at
layer X: {}", e)))?` (adjust the literal context to mention subtree
sizing/level) to follow the repository error-wrapping guideline.

---

Nitpick comments:
In
`@merk/src/estimated_costs/average_case_costs/value_with_feature_and_flags_size/v0.rs`:
- Around line 116-118: The error message for the checked_div call on
combined_size / combined_weight is misleading (checked_div returns None on
divide-by-zero); update the error to accurately reflect a division-by-zero
failure instead of overflow—replace the Error::Overflow("overflow for value
size") used with combined_size.checked_div(combined_weight) with a
DivisionByZero-style error or a message like "division by zero for value size"
(preserve the existing nonzero_kinds guard logic that makes this path
unreachable but keep defensive, clear wording).
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: 27f99aa0-baa5-40f8-b332-eca0beb1347d

📥 Commits

Reviewing files that changed from the base of the PR and between 22bfe42 and 8b6bbee.

📒 Files selected for processing (18)
  • grovedb-version/src/lib.rs
  • grovedb-version/src/version/merk_versions.rs
  • grovedb-version/src/version/v1.rs
  • grovedb-version/src/version/v2.rs
  • grovedb-version/src/version/v3.rs
  • merk/src/estimated_costs/average_case_costs.rs
  • merk/src/estimated_costs/average_case_costs/add_average_case_merk_propagate/mod.rs
  • merk/src/estimated_costs/average_case_costs/add_average_case_merk_propagate/v0.rs
  • merk/src/estimated_costs/average_case_costs/add_average_case_merk_propagate/v1.rs
  • merk/src/estimated_costs/average_case_costs/add_average_case_merk_propagate/v2.rs
  • merk/src/estimated_costs/average_case_costs/estimated_sum_trees_size/mod.rs
  • merk/src/estimated_costs/average_case_costs/estimated_sum_trees_size/v0.rs
  • merk/src/estimated_costs/average_case_costs/estimated_sum_trees_size/v1.rs
  • merk/src/estimated_costs/average_case_costs/estimated_sum_trees_size/v2.rs
  • merk/src/estimated_costs/average_case_costs/mod.rs
  • merk/src/estimated_costs/average_case_costs/value_with_feature_and_flags_size/mod.rs
  • merk/src/estimated_costs/average_case_costs/value_with_feature_and_flags_size/v0.rs
  • merk/src/estimated_costs/average_case_costs/value_with_feature_and_flags_size/v1.rs
💤 Files with no reviewable changes (1)
  • merk/src/estimated_costs/average_case_costs.rs
✅ Files skipped from review due to trivial changes (1)
  • grovedb-version/src/version/v1.rs
🚧 Files skipped from review as they are similar to previous changes (1)
  • grovedb-version/src/lib.rs

Comment on lines +199 to +213
EstimatedLayerSizes::AllSubtrees(_, estimated_sum_trees, flags_size) => {
// 1 for enum type
// 1 for empty
// 1 for flags size
Ok(estimated_sum_trees.estimated_size(grove_version)?
+ flags_size.unwrap_or_default()
+ 3)
}
EstimatedLayerSizes::Mix { subtrees_size, .. } => match subtrees_size {
None => Err(Error::WrongEstimatedCostsElementTypeForLevel(
"this layer is a mix but doesn't have subtrees",
)),
Some((_, est, fs, _)) => {
Ok(est.estimated_size(grove_version)? + fs.unwrap_or_default() + 3)
}

@coderabbitai coderabbitai Bot May 19, 2026

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.

🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win

Wrap estimated_size errors with call-site context in subtree sizing.

Both estimated_size(grove_version)? paths bubble errors without context, making diagnosis harder in dispatch failures. Please wrap them with contextual map_err(...) per repository guideline.

Proposed patch
             EstimatedLayerSizes::AllSubtrees(_, estimated_sum_trees, flags_size) => {
                 // 1 for enum type
                 // 1 for empty
                 // 1 for flags size
-                Ok(estimated_sum_trees.estimated_size(grove_version)?
+                Ok(estimated_sum_trees
+                    .estimated_size(grove_version)
+                    .map_err(|e| {
+                        Error::CorruptedData(format!(
+                            "subtree_with_feature_and_flags_size (all_subtrees): {}",
+                            e
+                        ))
+                    })?
                     + flags_size.unwrap_or_default()
                     + 3)
             }
             EstimatedLayerSizes::Mix { subtrees_size, .. } => match subtrees_size {
                 None => Err(Error::WrongEstimatedCostsElementTypeForLevel(
                     "this layer is a mix but doesn't have subtrees",
                 )),
                 Some((_, est, fs, _)) => {
-                    Ok(est.estimated_size(grove_version)? + fs.unwrap_or_default() + 3)
+                    Ok(est
+                        .estimated_size(grove_version)
+                        .map_err(|e| {
+                            Error::CorruptedData(format!(
+                                "subtree_with_feature_and_flags_size (mix): {}",
+                                e
+                            ))
+                        })?
+                        + fs.unwrap_or_default()
+                        + 3)
                 }
             },

As per coding guidelines, "Wrap errors with context using .map_err(|e| Error::CorruptedData(format!("context: {}", e))) pattern in Rust source files".

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
EstimatedLayerSizes::AllSubtrees(_, estimated_sum_trees, flags_size) => {
// 1 for enum type
// 1 for empty
// 1 for flags size
Ok(estimated_sum_trees.estimated_size(grove_version)?
+ flags_size.unwrap_or_default()
+ 3)
}
EstimatedLayerSizes::Mix { subtrees_size, .. } => match subtrees_size {
None => Err(Error::WrongEstimatedCostsElementTypeForLevel(
"this layer is a mix but doesn't have subtrees",
)),
Some((_, est, fs, _)) => {
Ok(est.estimated_size(grove_version)? + fs.unwrap_or_default() + 3)
}
EstimatedLayerSizes::AllSubtrees(_, estimated_sum_trees, flags_size) => {
// 1 for enum type
// 1 for empty
// 1 for flags size
Ok(estimated_sum_trees
.estimated_size(grove_version)
.map_err(|e| {
Error::CorruptedData(format!(
"subtree_with_feature_and_flags_size (all_subtrees): {}",
e
))
})?
flags_size.unwrap_or_default()
3)
}
EstimatedLayerSizes::Mix { subtrees_size, .. } => match subtrees_size {
None => Err(Error::WrongEstimatedCostsElementTypeForLevel(
"this layer is a mix but doesn't have subtrees",
)),
Some((_, est, fs, _)) => {
Ok(est
.estimated_size(grove_version)
.map_err(|e| {
Error::CorruptedData(format!(
"subtree_with_feature_and_flags_size (mix): {}",
e
))
})?
fs.unwrap_or_default()
3)
}
}
🤖 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 `@merk/src/estimated_costs/average_case_costs/mod.rs` around lines 199 - 213,
The two calls that propagate errors
directly—estimated_sum_trees.estimated_size(grove_version) inside
EstimatedLayerSizes::AllSubtrees and est.estimated_size(grove_version) inside
EstimatedLayerSizes::Mix—should be wrapped with a map_err to add call-site
context (use Error::CorruptedData(format!("...: {}", e))) so failures include a
descriptive message; update both sites to replace the `?`-propagated call with
`.map_err(|e| Error::CorruptedData(format!("failed to estimate subtree size at
layer X: {}", e)))?` (adjust the literal context to mention subtree
sizing/level) to follow the repository error-wrapping guideline.

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.

This is Claude. Acknowledged but skipping: estimated_size(grove_version)? is propagated bare from ~16 call sites across this directory (value_with_feature_and_flags_size_v0/_v1 and the three add_average_case_merk_propagate_v0/_v1/_v2 Mix arms — see rg 'estimated_size\(grove_version\)\?'). Wrapping just these two would leave the other ~14 inconsistent, and the code in subtree_with_feature_and_flags_size is pre-existing (this PR only restructured files around it). Better to handle uniformly in a separate refactor PR — happy to file an issue if useful.

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.

@QuantumExplorer, that's a fair and well-reasoned call — patching just two of ~16 bare-propagation sites would leave the rest inconsistent and make the codebase harder to reason about. Deferring to a uniform refactor PR makes sense.

Would you like me to open a GitHub issue to track the follow-up refactor ("Wrap all estimated_size(grove_version)? call sites with map_err context across average_case_costs/")?


🧠 Learnings used
Learnt from: QuantumExplorer
Repo: dashpay/grovedb PR: 669
File: merk/src/proofs/query/count_offset/tests.rs:54-64
Timestamp: 2026-05-17T11:22:44.675Z
Learning: In Rust code reviews, do not flag `impl Trait` used in *argument-position* as an error when it appears inside a concrete generic type in a function parameter type, e.g. `fn foo(x: &Merk<impl StorageContext<'static>>)` / `Foo<impl Bar>`. This is valid argument-position `impl Trait` (APIT) that desugars to an anonymous generic parameter and compiles on stable Rust.

Only treat `impl Trait` as unsupported when it’s nested/used in an invalid `impl Trait`-within-`impl Trait` position (e.g. forms like `impl Fn(impl Trait)`).

Learnt from: QuantumExplorer
Repo: dashpay/grovedb PR: 670
File: grovedb/src/tests/provable_count_provable_sum_tree_tests.rs:80-86
Timestamp: 2026-05-17T14:06:21.953Z
Learning: When reviewing Rust `matches!(value, pattern)` usages, do not warn that `value` is being moved if the pattern uses only non-binding `_` discards (e.g., `SomeEnum::Variant(_, _, _, _)` or other `_`-only wildcards). In this case, `_` is a non-binding discard and will not move or borrow `value`, so `value` remains usable after the `matches!` call. A move can occur only if the pattern binds fields (e.g., `Some(x)`), especially for non-`Copy` fields. Therefore, `matches!(non_copy_value, AllWildcardPattern)` is safe and should not be flagged as a potential move.

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.

This is Claude. Thanks — leaving the tracking-issue decision to @QuantumExplorer rather than spawning one unprompted. The thread is enough breadcrumb if/when someone picks this up.

QuantumExplorer and others added 3 commits May 19, 2026 08:12
Addresses CodeRabbit nitpick on PR #674: the
`combined_size.checked_div(combined_weight).ok_or(Error::Overflow(...))`
on `value_with_feature_and_flags_size_v0` returned `Error::Overflow`
even though `checked_div` only fails on divide-by-zero. The path is
unreachable (the `nonzero_kinds == 0` guard above rules out a zero
divisor), but the wording was misleading. Now surfaces
`Error::DivideByZero("value size divisor was zero")` instead.

No behavior change for any reachable input.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…vel #![cfg(...)]

Inside the three per-function submodules (`add_average_case_merk_propagate/`,
`estimated_sum_trees_size/`, `value_with_feature_and_flags_size/`),
every item carried its own `#[cfg(feature = "minimal")]`. The parent
`average_case_costs/mod.rs` already gates each submodule with
`#[cfg(feature = "minimal")] mod foo;`, so when the feature is off
the entire submodule isn't compiled — the per-item gates were
redundant.

Replaced with a single `#![cfg(feature = "minimal")]` inner attribute
at the top of each file, removing ~50 redundant per-item gates across
11 files. Self-documents the file-level dependency without changing
behavior.

All 657 merk tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…subtree

`merk/src/estimated_costs/mod.rs` declares the submodule as:

    #[cfg(feature = "minimal")]
    pub mod average_case_costs;

so when the feature is off the entire module tree (including all the
new per-function subdirectories) is excluded by Cargo. Every per-item
`#[cfg(feature = "minimal")]` inside `average_case_costs/mod.rs` and
every file-level `#![cfg(feature = "minimal")]` inside the
`add_average_case_merk_propagate/`, `estimated_sum_trees_size/`, and
`value_with_feature_and_flags_size/` subdirs is therefore redundant.

Removed 41 redundant cfg attributes across 12 files (1 file-level
`#![cfg(...)]` per submodule file × 11, plus 30 per-item
`#[cfg(feature = "minimal")]` in `average_case_costs/mod.rs`).

No behavior change. All 657 merk tests still pass.

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

@QuantumExplorer QuantumExplorer left a comment

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.

Reviewed

@QuantumExplorer
QuantumExplorer merged commit ad2492d into develop May 19, 2026
10 checks passed
@QuantumExplorer
QuantumExplorer deleted the claude/thirsty-colden-c82bdf branch May 19, 2026 02:18
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