Skip to content

feat(platform)!: require fee history for storage refunds and credit their recorded owners - #4706

Open
DCG-Claude wants to merge 27 commits into
v5.1-devfrom
dashvm/r12-02
Open

DCG-Claude wants to merge 27 commits into
v5.1-devfrom
dashvm/r12-02

Conversation

@DCG-Claude

@DCG-Claude DCG-Claude commented Sep 12, 2026 •

Copy link
Copy Markdown
Collaborator

Issue being fixed or feature implemented

Part 1 of 3 for R12-02 of the smart-contract plan (#4626, section 12, fee workstream #4689, preparation package #4675).

Storage refunds are priced against the fee history a block carries (previous_fee_versions in platform state). The shipped fee calculation (Drive::calculate_fee v0, consume_to_fees_v0) short-circuits fee version number 1: it prices every owner-attributed removal against an empty history, so a caller that forgets to pass the history silently refunds at the first generation's storage rates instead of failing. The fee version integrity rule in the fee DIP requires the opposite: a refund without the historical context of the removing block is an error, never a fallback to a schedule.

The same DIP requires refunds to follow the recorded owner and, for owners that no longer exist, to go to the current processing pool (acceptance Q26: a frozen but existing owner still receives native bookkeeping refunds without any spending permission; a wiped owner's refunds go to the processing pool). Today the only code that credits other owners (apply_balance_change_from_fee_to_identity v0) halts on an owner without a balance, and block lifecycle paths that free owner-flagged bytes never price or settle refunds at all.

This part establishes the explicit version boundary and the primitive the later parts build on:

  • protocol version 15 (packages/rs-platform-version/src/version/v15.rs) with DRIVE_VERSION_V10;
  • Drive::calculate_fee v1, which requires and consults the fee history for every owner-attributed storage removal;
  • Drive::credit_storage_refunds_to_owners_operations, which credits recorded owners without consulting any key or permission and reports the amount whose owner has no balance element.

Part 2 (same base) makes the vote poll end cleanup price and settle its refunds with this primitive. Part 3 (base v5.0-dev) routes state transition refunds for missing owners to the processing pool and carries the DIP text.

What was done?

Version tables (packages/rs-platform-version)

  • src/version/v15.rs: the base introduced PLATFORM_V15 as a copy of v14 (feat(platform): introduce protocol version 15 #5043); this PR's only change to it is drive: DRIVE_VERSION_V10 and the doc comment listing what the version hosts. LATEST_VERSION, PLATFORM_VERSIONS and LATEST_PLATFORM_VERSION already point at 15 on the base.
  • src/version/drive_versions/v10.rs: DRIVE_VERSION_V10, a copy of v9 with fees.calculate_fee: 1 and identity: DRIVE_IDENTITY_METHOD_VERSIONS_V3.
  • src/version/drive_versions/drive_identity_method_versions/mod.rs: DriveIdentityUpdateMethodVersions gains credit_storage_refunds_to_owners: OptionalFeatureVersion. Backfilled as None in v1.rs and v2.rs (shipped tables stay behaviour-preserving); the new v3.rs sets Some(0).

Fee calculation (packages/rs-drive/src/fees)

  • op.rs: LowLevelDriveOperation::consume_to_fees_v1 beside the untouched consume_to_fees_v0. The SectionedStorageRemoval arm takes the system bucket as before, then requires previous_fee_versions on every fee version number and returns DriveError::CorruptedCodeExecution("a storage refund needs the fee history of the block that removes the bytes") without it. The empty-map shortcut for number 1 is gone in v1.
  • calculate_fee/v1/mod.rs: Drive::calculate_fee_v1, a copy of v0 calling consume_to_fees_v1. calculate_fee/mod.rs dispatches 1 => and reports known_versions: vec![0, 1]. v0 is byte-identical. After the rebase onto the time-range TTL work (feat(drive): time-range index TTL — O(1) flat-drop drainage and ephemeral-bytes fees #4581) v1 carries v0's CalculatedEphemeralCostOperation arm unchanged: TTL'd index bytes bill to processing at the ephemeral rate, storage stays zero, their removal is basic and needs no history, and a sectioned removal inside an ephemeral batch is corrupted state.

Pricing before commit (packages/rs-drive)

From protocol version 15 a missing fee history is an error, and the fee-returning Drive entry points that start their own transaction when the caller passes none committed it before pricing, so that error could come back after the write had been persisted (found by the automated review). New generations hold the owned transaction until Drive::calculate_fee succeeded and drop it with everything it wrote on an error; with a caller transaction nothing is committed by Drive in either generation and nothing changes:

  • util/batch/drive_op_batch/drive_methods/apply_drive_operations/v2/mod.rs: generation 1 with the price computed before the owned transaction commits; finalize tasks still run after the commit. DRIVE_VERSION_V10 sets batch_operations.apply_drive_operations: 2.
  • drive/group/insert/add_group_action/v1/mod.rs: the fee-returning wrapper owns a transaction and prices before committing; add_group_action_add_to_operations v1 is a copy of v0. DRIVE_GROUP_METHOD_VERSIONS_V2 (insert.add_group_action: 1).
  • drive/contract/moderation/{remove_contract_ban,remove_contract_suspension,remove_contract_warnings,add_contract_suspension,add_contract_warning}/v1/mod.rs: the five moderation writers that can free moderator-flagged bytes (a removal, or a replacement of an existing entry by a shorter one), same shape; their _operations v1 are copies of v0.
  • drive/document/{delete/delete_document_for_contract,delete/delete_document_for_contract_id,delete/delete_index_only_document_for_contract_operations,update/update_document_for_contract,update/update_document_for_contract_id,update/update_document_with_serialization_for_contract,insert/add_document,insert/add_document_for_contract,insert_contested/add_contested_document,insert_contested/add_contested_document_for_contract}/v1/mod.rs: the ten fee-returning document wrappers, same shape; the _apply_and_add_to_operations methods production uses through apply_drive_operations are untouched, and the index-only operations builder stays at generation 0. add_document looks the contract up through the caller's transaction (the cache rule below) and can shrink an owner-flagged document through override_document; its v1 also keeps one operations vector, so the contract read's cost is priced with the write (generation 0 replaced the vector after the lookup and never priced the read; found by CodeRabbit). The index-only delete removes owner-flagged index entries. The two contested inserts remove the creator-flagged join-window end-date entry when the second contender of a contest resolved without locking moves the poll's end date (found by the automated review; the by-id one keeps one operations vector like add_document). DRIVE_DOCUMENT_METHOD_VERSIONS_V5 bumps the ten slots.
  • drive/contract/update/update_contract/v3/mod.rs and drive/contract/apply/apply_contract_with_serialization/v1/mod.rs: the two contract writers, same shape; the element writer stays generation 2 and the operation builder generation 0 (their dispatchers accept the wrapper's number). The contract cache keys its block-versus-committed behaviour on whether a transaction is present, so under an owned transaction the contract is looked up through the caller's (none) and the rewritten copy replaces the global entry only after the commit. DRIVE_CONTRACT_METHOD_VERSIONS_V5 bumps update_contract to 3, apply_contract_with_serialization to 1 and the five moderation slots to 1.

The bare group and moderation wrappers still pass no fee history, so a call that frees flagged bytes still fails at protocol version 15; the document and contract wrappers take the caller's history and fail the same way when it is missing. In every case the failure now comes before anything is written. Tests: apply_drive_operations::v2::tests (a batch that cannot be priced leaves the action open and the same batch with the history closes it; protocol version 14 still commits without a history), should_reject_closing_a_group_action_through_the_bare_wrapper_without_fee_history now asserts the action is still active and nothing moved, should_commit_the_owned_transaction_when_pricing_succeeds_at_the_latest_version, should_leave_a_ban_in_place_when_the_bare_wrapper_cannot_price_its_removal (root hash and every list unchanged after four rejected removals; estimation writes nothing; protocol version 14 removes), should_leave_a_document_in_place_when_the_wrapper_cannot_price_its_removal (the document and root hash unchanged after a rejected delete, the same delete with the history refunds the owner and commits, protocol version 14 deletes without one) should_leave_a_contract_in_place_when_the_wrapper_cannot_price_its_update (a shrinking update rejected without the history leaves the stored contract, the root hash and the cached copy unchanged; with the history it commits and the cache follows), should_leave_a_document_in_place_when_add_document_cannot_price_its_override (a shrinking override of an owner-flagged profile is rejected with the root hash unchanged, a fresh insert commits, protocol version 14 overrides without a history), should_leave_an_index_only_document_in_place_when_the_wrapper_cannot_price_its_removal (the index entries and root hash unchanged after a rejected index-only delete; with the history it deletes) and should_leave_a_warning_in_place_when_the_bare_wrapper_cannot_price_its_replacement (replacing a long warning entry by a short one is rejected with the entry and root hash unchanged; the production funnel replaces it with the history; protocol version 14 replaces without one). Each of the three was checked negatively as well: with the protocol 15 table pointed back at generation 0 all three fail on the unchanged-state assertion. should_price_the_contract_fetch_when_adding_a_document_by_contract_id estimates the same insert through the contract-reference wrapper and the by-id wrapper: at the latest version the by-id processing fee is higher by the contract read, at protocol version 14 the two are equal. should_leave_a_no_locking_contest_in_place_when_a_bare_wrapper_cannot_price_the_second_contender opens a no-locking contest (fixture copied from the drive-abci tests) and joins a second contender through each bare wrapper without a history: rejected with the root hash, the contenders and the end-date entries unchanged, the production funnel joins with the history and moves the end date to the vote window, protocol version 14 joins without one; should_open_a_contest_through_the_bare_wrappers_at_every_version covers the first contender (frees nothing, priced at every version, by-id fee includes the contract read from 15). The negative check holds for these too. The new regressions use fixed owner ids and seeded generators.

Recorded-owner credits (packages/rs-drive/src/drive/identity/update)

  • methods/credit_storage_refunds_to_owners_operations/{mod.rs, v0/mod.rs}: the new versioned method (Some(0) dispatch, None => VersionNotActive). For each owner in a FeeRefunds except an optional skip_owner (the state transition payer, whose own refund folds into its balance change), it sums the per-epoch credits with checked arithmetic, reads the balance element statefully once, feeds that read into add_to_previous_balance and the balance and negative credit update operations (the same shape the payer's own refund uses in apply_balance_change_from_fee_to_identity, so no element is read twice) when it exists, and otherwise adds the amount to routed_to_processing_pool. When the owner's balance is zero the shipped helper first clears its negative credit (identity debt) and only the remainder reaches the balance; the primitive takes the repaid share the helper's outcome reports (repaid_debt()), derives the balance share by checked subtraction and reports the repaid share as repaid_debt, because debt lives outside the credit sum trees and is processing fee the pools were short of when it was incurred. No key, signature or permission is read on any route. The primitive neither writes the processing pool nor records pending refunds; the caller writes processing_pool_share() (unrouted refunds plus repaid debt) once and records the pending refunds, so a block keeps one pool write and one pending-refund write per batch.
  • structs/storage_refund_credit_outcome/mod.rs: StorageRefundCreditOutcome { credited: BTreeMap<Identifier, Credits>, repaid_debt: Credits, routed_to_processing_pool: Credits } with checked processing_pool_share() and total().

Tests

  • fees/op.rs (storage_refund_fee_history module): v1 rejects a sectioned removal without history (v0 still prices it); a system-bucket-only sectioned removal is rejected too; unflagged (BasicStorageRemoval) bytes never need the history; the history is consulted for fee version number 1 (a synthetic number-2 schedule with doubled storage rates at epoch 10, bytes stored at epoch 12 and removed at 15: v0 refunds at the first generation's rate, v1 at the doubled rate); for every PLATFORM_VERSIONS entry and every history the epoch change hook could build, v1's FeeResults equal v0's; every shipped schedule keeps fee version number 1 and the first generation's storage rates (the premise of that equality); ephemeral TTL operations price identically in v0 and v1 with no history and a sectioned removal in an ephemeral batch is rejected in both.
  • fees/calculate_fee/mod.rs: through the dispatcher, PlatformVersion::get(14) prices an owner-attributed removal without history, PlatformVersion::latest() refuses it, and with a history both produce the same fees.
  • credit_storage_refunds_to_owners_operations/v0/mod.rs: each recorded owner credited by the sum of its epochs; an owner without a balance is reported, not an error, and no balance element is created; an owner whose keys are all disabled is credited; skip_owner is left alone; after the caller writes the pool and records the pending refunds, calculate_total_credits_balance stays balanced (TotalCreditsBalance::ok); refunds below, equal to and above an owner's outstanding debt report the repaid share and leave the credit sum balanced once the caller writes the pool share; a positive balance repays no debt; PlatformVersion::get(14) returns VersionNotActive.
  • drive/group/mod.rs: should_close_group_action_and_move_signers now closes the action the way production does (a GroupOperationType::AddGroupAction { closes_group_action: true, .. } through apply_drive_operations with the fee history). New: should_refund_signer_bytes_when_a_group_action_closes (the opening signer's flagged items are refunded against their storage epoch; the closing signer, whose items are unflagged, is not) and should_reject_closing_a_group_action_through_the_bare_wrapper_without_fee_history (CorruptedCodeExecution at the latest version, success at protocol version 14). The two other closing-branch tests (immediate close with new action info, cost estimation with apply = false) free no flagged bytes and stay on the bare wrapper, which pins that the rule fires only when flagged bytes are actually removed.
  • drive/contract/migration/strip_unknown_document_schema_properties.rs: should_keep_the_protocol_12_schema_strip_frozen_without_refunding_stripped_bytes, a freeze test at PlatformVersion::get(12): a user contract element carrying the owner's storage flags is inflated with a top-level schema property the v1 meta-schema forbids, the migration shrinks it back to its clean bytes, the flags are byte-equal, the owner's balance and the pending refund tree are unchanged, and the credit sum stays balanced. The doc comment carries the replay rationale (below).
  • Two existing tests replaced epoch-flagged documents without a history and failed under the strict rule (ranked_index_e2e_tests::estimated_and_actual_update_fees, update::tests::summable_index_update_changes_key_into_new_branch_materializes_aggregate_tree_type); both now pass the same one-entry history the neighbouring tests use. After the rebase onto the contract moderation work (feat(platform)!: contract moderation with a banlist and a suspension list #4830, feat(platform)!: a reason on contract bans and suspensions #4849), the moderation tests in drive/contract/moderation/tests.rs that unban, unsuspend or replace a suspension (removals of moderator-flagged entries) go through apply_drive_operations with a history, the funnel the moderation state transition uses; the bare remove_contract_ban, remove_contract_suspension, remove_contract_warnings and replacing add_contract_suspension wrappers pass no history and are otherwise called only from tests that insert. The structure fixture in structure/tests.rs closes its group action the same way, the TTL twin test in time_range_index_e2e_tests.rs passes a history when it deletes owner-flagged standing index bytes, and grovedb-structure.json is regenerated (only the @14 fixture labels become @15).

Book

book/src/fees/overview.md: a "Fee history and refund ownership (protocol version 15 onward)" subsection under Refunds, and a paragraph on fee_version_number under Fee Versioning.

Caller audit

Drive::calculate_fee has 129 call sites in 99 files of packages/rs-drive/src (14 forward a caller's history; the rest pass None). Grouped by what they can remove:

Class Sites History Verdict
Forwarded from the caller apply_drive_operations, update_contract, document delete (including the index-only delete), update and add methods, identity balance and revision, token balance updates caller's correct; every production state transition and block-end application passes previous_fee_versions. The document wrappers and update_contract gained generations (v1, v3) that price before committing the transaction they own when a caller passes none, so a missing history fails before mutation.
None, can remove owner-flagged bytes drive/group/insert/add_group_action (closing an action moves signer-flagged items) None production reaches the closing branch only through GroupOperationType::AddGroupAction inside apply_drive_operations, which forwards the history; the bare wrapper is called from test sites, of which the closing ones now use the production funnel. From protocol version 15 a closing call through the bare wrapper is a misuse the strict rule surfaces before anything is written (v1 prices before committing its owned transaction; pinned by test).
None, can remove owner-flagged bytes drive/contract/moderation/{remove_contract_ban,remove_contract_suspension,remove_contract_warnings,add_contract_suspension,add_contract_warning} bare wrappers (entries are flagged with the moderator; a removal or a shorter replacement frees them) None production reaches them only through ContractModerationOperationType inside apply_drive_operations (history forwarded, from the contract moderation transition); the wrappers are called from tests and drive-abci query fixtures, the removing ones now through the funnel. Their v1 generations price before committing an owned transaction.
None, can remove owner-flagged bytes drive/document/insert/add_document (an override_document of a document inserted with owner flags through OwnedDocumentInfo; v1 prices before committing an owned transaction) None no production caller: award_document_to_winner and the state transition paths use add_document_for_contract or the operation builders; the wrapper is called from tests. From protocol version 15 a shrinking override through it fails before anything is written (pinned by test).
None, can remove owner-flagged bytes drive/contract/apply/apply_contract_with_serialization (replace path; v1 prices before committing an owned transaction) None production callers: genesis (storage_flags: None), the protocol 13 and 14 transitions (perform_events_on_first_block_of_protocol_change/v0/mod.rs:683,707 re-store DPNS and DashPay over unflagged genesis elements with None flags, so the removal is BasicStorageRemoval), and create_mn_shares_contract (test-only). A sectioned removal here needs a flagged old element and flagged new flags, which only tests produce. Unchanged.
None, elements unflagged today disable_identity_keys/v0 (its TODO asks this question), update_keywords/v0, update_description/v0 (keyword and description documents are stored with DocumentOwnedInfo((doc, None))), token status, price, supply and contract info (Element::Item(.., None), SumItem(.., None)), identity nonce, revision and balance, vote sum items and references None safe today; listed so a future owner flag on any of them fails loudly under v1 instead of under-refunding
None, can remove owner-flagged bytes drive/document/insert_contested/{add_contested_document,add_contested_document_for_contract} (the second contender of a contest resolved without locking removes the creator-flagged join-window end-date entry; v1 prices before committing an owned transaction) None production reaches them only through DocumentOperationType::AddContestedDocument inside apply_drive_operations (history forwarded); the bare wrappers are called from tests. From protocol version 15 a second contender through them fails before anything is written (pinned by test).
None, inserts, fetches, proofs and queries only everything else (drive/document/query/**, query/**, token fetch and prove, identity fetch, vote registration, group inserts, insert_contract v0/v1) None safe; nothing is removed

drive-abci direct Drive::calculate_fee callers (fetch_contender.rs, the index-only delete and document create state validators) price reads only.

Block lifecycle applications in packages/rs-drive-abci/src/execution/platform_events (non-test code):

Path Frees owner-flagged bytes? Applies through History Fee result
voting/clean_up_after_contested_resources_vote_polls_end/{v0,v1}: deletes contested documents and contender trees (owner-flagged), the end-date entry (creator-flagged), votes and stored info (unflagged); v1 also empties prefunded balances yes apply_batch_low_level_drive_operations(None, ..) none never computed, refunds dropped: part 2
voting/award_document_to_winner/v0 no: inserts the winner's document unflagged (DocumentAndSerialization((doc, bytes, None))) add_document_for_contract(.., None) None discarded; the awarded record has no owner and is neither charged nor refundable (recorded for the owner-encoding refinement)
voting/keep_record_of_vote_poll/v0, voting/remove_votes_for_removed_masternodes/v0 no (unflagged) low-level none discarded
core_based_updates/update_masternode_identities/v0 no removals (key patches on unflagged keys) apply_drive_operations Some(platform_state.previous_fee_versions()) discarded
block_processing_end_events/process_block_fees_and_validate_sum_trees/v0 no apply_drive_operations Some(block_platform_state.previous_fee_versions()) discarded
withdrawals (dequeue_and_build_unsigned_withdrawal_transactions/v0, pool_withdrawals_into_transactions_queue/v1, rebroadcast_expired_withdrawal_documents/v1, update_broadcasted_withdrawal_statuses/v0) no: withdrawal documents are stored with owner_id: None and no flags (identity_credit_withdrawal_transition.rs, address_credit_withdrawal_transition.rs) apply_drive_operations None discarded
initialization/create_genesis_state/{v0,v1} no (inserts, storage_flags: None) apply_drive_operations None discarded
protocol_upgrade/perform_events_on_first_block_of_protocol_change/v0 (DPNS re-store at 13, DashPay at 14) no (unflagged genesis elements replaced by unflagged elements) apply_contract None discarded
protocol_upgrade/.../v0 transition_to_version_12 calling strip_unknown_document_schema_properties rewrites user contract items in place keeping their flags; a shrink removes owner-paid bytes whose refund is discarded grove_insert with a discarded cost vector none shipped and frozen; ran once at the protocol 12 activation; the recorded historical exception, pinned by the freeze test
fee pool inwards and outwards, epoch change, credit inflow and total-credits history, shielded anchors, address balance cleanup, version counters no operation builders and direct grove ops n/a n/a
check_tx/v0, process_raw_state_transitions/v0, execute_event/v0, validate_fees_of_event/v0 state transitions apply_drive_operations Some(state.previous_fee_versions()) applied through apply_balance_change_from_fee_to_identity

Conclusion: one production path frees owner-flagged bytes without fee context and without settling refunds (vote poll end cleanup, part 2); one shipped migration discarded refunds and is frozen (pinned here); one test-only wrapper closes group actions without history (its closing test now uses the production funnel); every other path is correct or touches unflagged bytes.

Owner-flagged writes (what the invariant covers)

Documents, index references, contested documents and contested index trees (document owner); the vote poll end-date entry (contest creator); user data contract items (insert_contract v1: every contract with can_be_deleted() || !readonly(), contract owner); group action info and signer sum items (signer); token distribution items (token owner, recipient, claimer). Not flagged: identities and their keys, withdrawal documents, keyword-search documents, votes, prefunded balances, epoch pools, withdrawal queue items, shielded anchors, stored vote poll info, and the system contracts at genesis. The document history contract registered at the protocol 13 transition goes through insert_contract v1 and therefore carries the system owner's flags; a future transition that re-stores it must pass its flags or hit grovedb's RemovingFlagsError (noted, no change here).

How Has This Been Tested?

Local gate (exit codes captured under the run directory):

cargo fmt --all -- --check
cargo clippy -p platform-version -p drive -p drive-abci --all-features --all-targets --no-deps -- -D warnings
cargo check --workspace --all-targets
cargo test -p platform-version --all-features
cargo test -p drive --lib -- fees::op::tests::storage_refund_fee_history
cargo test -p drive --lib -- fees::calculate_fee
cargo test -p drive --lib -- credit_storage_refunds_to_owners_operations
cargo test -p drive --lib -- drive::group
cargo test -p drive --lib -- drive::contract::migration
cargo test -p drive --lib

The full drive unit suite passes (3924 passed, 6 ignored); the rest of the workspace is CI's. The gate was run in a private CARGO_TARGET_DIR because the shared target directory is not writable from this account. No src/verify/** file is touched, so the verify-only cut is unaffected (cargo check --workspace --all-targets compiles the verify crates).

Breaking Changes

Consensus: protocol version 15 is introduced. Under its drive table a storage refund for owner-attributed bytes is priced only with the fee history of the removing block; a missing history is an internal error (a halt on a block execution path) instead of a silent fallback to the first generation's rates. The Drive entry points that own their transaction when a caller passes none (apply_drive_operations v2, the ten document wrappers v1, update_contract v3, apply_contract_with_serialization v1, add_group_action v1, five moderation writers v1) price before they commit, so that error never leaves a write persisted without its fee result. Every shipped schedule shares fee version number 1 and the same storage rates, so refund credits are unchanged for every shipped input; every shipped PLATFORM_V*, calculate_fee v0, consume_to_fees_v0, add_group_action, apply_balance_change_from_fee_to_identity v0 and the protocol 12 migration are byte-identical. Shipped identity tables gain one None field.

API: DriveIdentityUpdateMethodVersions gains a field (struct literals outside the version crate: none). Drive::credit_storage_refunds_to_owners_operations and StorageRefundCreditOutcome are new. No production path calls the primitive at this head; part 2 wires the vote poll end cleanup to it. No proto, SDK, wasm or FFI change; PlatformVersion::latest() now resolves to 15.

Checklist:

  • I have performed a self-review of my own code
  • I have commented my code, particularly in hard-to-understand areas
  • I have added or updated relevant unit/integration/functional/e2e tests
  • I have added "!" to the title and described breaking changes in the corresponding section if my code contains any
  • I have made corresponding changes to the documentation if needed

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

Decisions taken (provisional values)

  • Provisional: an owner without a balance element is the proxy for a wiped owner. Contracts cannot be deleted yet and identities are never removed, so Drive has no typed "wiped owner" today. The primitive treats a missing balance element as that case and reports the amount for the processing pool. When the owner-encoding refinement types the owner in the storage flags this proxy is replaced; nothing in state depends on it.
  • Strictness over leniency. A missed production path under v1 halts the chain instead of mispricing silently. Mitigation: the audit above, the lifecycle table, and part 2's tests that run the lifecycle paths at the latest version with sum-tree verification. Treating a missing history as the current schedule is the fallback the owner rejected.
  • The primitive reports, the caller writes the pool. add_epoch_processing_credits_for_distribution_operation is a read-modify-write of the epoch's pool sum item, so two of them in one batch collapse last-wins. Returning routed_to_processing_pool and repaid_debt (together processing_pool_share()) lets the caller issue exactly one pool write per batch and keep pending-refund recording with the existing versioned methods. Repaid debt goes to the processing pool because negative credit is processing fee the pools were short of when the identity could not cover it (fee_result_outcome drops that part from the block fees), so clearing it with a refund returns those credits to the pool they were owed to.
  • No shipped file edited; the bare group wrapper keeps its signature. The plan's earlier draft widened add_group_action with a history parameter; this PR instead routes the one closing test through the funnel production uses and pins the wrapper's strictness.
  • Protocol 12 schema migration excluded by name. The stripped byte counts were never recorded and the migration never runs again, so there is nothing to correct at protocol version 15; the invariant (written into the DIP in part 3) states that blocks before 15 replay exactly as executed and names this migration as the exception.
  • Storage-epoch pricing not duplicated. The change that prices a refund at the rate of the epoch the bytes were stored in is another lane's in-place edit to FeeRefunds::from_storage_removal; it is not on v4.3-dev yet, so the test that would pin it is deferred to part 2.
  • Protocol version 15 amended. The version was introduced on this branch while LATEST_VERSION was 14; the base has since introduced it as a copy of v14 (feat(platform): introduce protocol version 15 #5043), so after the rebase this PR only points its drive table at DRIVE_VERSION_V10.

Part 1 of 3 for R12-02.

Refs #4675
Refs #4689


🤖 Posted autonomously by DashVM (Claude Fable 5.1) on behalf of pasta.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features
    • Protocol 15 adds fee-history-based pricing for storage refunds and credits refunds to the recorded storage owner, including handling for debt and owners without balances.
    • Document, contract, moderation, group-action, and contested-document operations now use safer transaction handling: changes are committed only after fee calculation succeeds.
  • Bug Fixes
    • Operations requiring storage-refund pricing now fail without fee history instead of applying changes with incomplete pricing. Earlier protocol behavior remains unchanged.
  • Documentation
    • Clarified how refund ownership, fee history, and fee-version numbers affect storage refunds.

PR Hygiene · 324f10e

  • Bots — coderabbitai not yet · thepastaclaw ✓ — /skip-bots proceeds without the ones not yet reported
  • Self-review — not asked of a bot author
  • Within your 5 open PRs
  • Build failed
  • Approvals
    • files with no dedicated owner (book/src/fees/overview.md, packages/rs-platform-version/src/version/drive_versions/drive_contract_method_versions/mod.rs, packages/rs-platform-version/src/version/drive_versions/drive_contract_method_versions/v5.rs and 11 more) — QuantumExplorer or shumkov
    • rs-drive-abci (packages/rs-drive-abci/src/query/document_query/v1/dispatch/chained.rs) — QuantumExplorer or shumkov
    • rs-drive (packages/rs-drive/grovedb-structure.json, packages/rs-drive/src/drive/contract/apply/apply_contract_with_serialization/mod.rs, packages/rs-drive/src/drive/contract/apply/apply_contract_with_serialization/v1/mod.rs and 59 more) — QuantumExplorer or shumkov

When every box is checked the PR Hygiene check passes and this can merge.

@coderabbitai

coderabbitai Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Repository: dashpay/platform/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: 16853f0f-05af-41f7-95f7-4bc99f95183b

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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: dashpay/platform/.coderabbit.yaml

Review profile: CHILL

Plan: Advanced

Run ID: c608dd45-031e-4d6d-9a84-9c86ac3310d7

📥 Commits

Reviewing files that changed from the base of the PR and between 5e1a07a and c0f7248.

📒 Files selected for processing (12)
  • packages/rs-drive/src/drive/document/delete/mod.rs
  • packages/rs-drive/src/drive/document/insert/add_document/v1/mod.rs
  • packages/rs-drive/src/drive/document/insert/mod.rs
  • packages/rs-drive/src/drive/document/insert_contested/add_contested_document/mod.rs
  • packages/rs-drive/src/drive/document/insert_contested/add_contested_document/v1/mod.rs
  • packages/rs-drive/src/drive/document/insert_contested/add_contested_document_for_contract/mod.rs
  • packages/rs-drive/src/drive/document/insert_contested/add_contested_document_for_contract/v1/mod.rs
  • packages/rs-drive/src/drive/document/insert_contested/mod.rs
  • packages/rs-drive/tests/supporting_files/contract/dpns/dpns-contract-contested-unique-index-no-locking.json
  • packages/rs-platform-version/src/version/drive_versions/drive_document_method_versions/v5.rs
  • packages/rs-platform-version/src/version/drive_versions/v10.rs
  • packages/rs-platform-version/src/version/v15.rs
🚧 Files skipped from review as they are similar to previous changes (2)
  • packages/rs-platform-version/src/version/v15.rs
  • packages/rs-platform-version/src/version/drive_versions/v10.rs

Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review.


📝 Walkthrough

Walkthrough

Protocol version 15 adds fee-history-based storage refund pricing and refund crediting to recorded owners. New Drive method versions calculate fees before committing owned transactions. Protocol version 15 selects the new fee, method, and batch-operation versions.

Changes

Storage refund pricing and crediting

Layer / File(s) Summary
Fee-history pricing
packages/rs-drive/src/fees/*, book/src/fees/overview.md
Fee calculation version 1 uses persisted fee-version history to price sectioned storage removals. Missing history returns CorruptedCodeExecution. Documentation describes refund ownership and fee-history requirements.
Owner refund crediting
packages/rs-drive/src/drive/identity/update/*, packages/rs-platform-version/src/version/drive_versions/drive_identity_method_versions/*
The new operation credits refunds to owners with balances and reports amounts applied to debt or routed to the processing pool. Its outcome type provides checked aggregate totals.
Batch pricing and commit
packages/rs-drive/src/util/batch/drive_op_batch/drive_methods/apply_drive_operations/*, packages/rs-drive/src/drive/group/mod.rs, packages/rs-drive/src/structure/tests.rs
Batch-operation version 2 calculates fees before committing an owned transaction. Tests cover failed pricing without history and successful application with history.

Versioned contract, document, moderation, and group operations

Layer / File(s) Summary
Contract and document wrappers
packages/rs-drive/src/drive/contract/apply/*, packages/rs-drive/src/drive/contract/update/*, packages/rs-drive/src/drive/document/*, packages/rs-drive-abci/src/query/document_query/v1/dispatch/chained.rs
New method versions route contract and document operations through wrappers that calculate fees before committing owned transactions. Tests cover missing-history failures, state preservation, and refunds with supplied history.
Moderation and group wrappers
packages/rs-drive/src/drive/contract/moderation/*, packages/rs-drive/src/drive/group/*
New moderation and group method versions build operations and calculate fees before committing owned transactions. Tests check fee-history errors, refund attribution, and protocol version 14 behavior.

Protocol 15 selection

Layer / File(s) Summary
Version tables and structure metadata
packages/rs-platform-version/src/version/drive_versions/*, packages/rs-platform-version/src/version/v15.rs, packages/rs-drive/grovedb-structure.json
Protocol version 15 selects Drive version 10 and the new method-version tables. GroveDB structure provenance is updated to protocol version 15; the recorded tree layouts are unchanged.

Priority: ➖ Normal

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

Change: Feature

Sequence Diagram(s)

sequenceDiagram
  participant Caller
  participant apply_drive_operations_v2
  participant calculate_fee_v1
  participant OwnedTransaction
  Caller->>apply_drive_operations_v2: submit operations and fee history
  apply_drive_operations_v2->>calculate_fee_v1: calculate batch fees
  calculate_fee_v1-->>apply_drive_operations_v2: fee result or pricing error
  apply_drive_operations_v2->>OwnedTransaction: commit after successful pricing
Loading

Merge Risk: ⚪ Minimal · up to c0f72

No concrete issue remains that should delay merging after normal checks.

Security Architecture Review

Security architecture risk: 🟡 Moderate · up to c0f72

Refunds now fail rather than use an incorrect price when fee history is missing, and owned writes are priced before they commit. One document entrypoint cannot receive that history, however, so a valid refund-producing override fails through that route. The production impact of that limitation is not established.

Retained concerns

  • Medium · reliability · observed: The versioned add_document entrypoint cannot forward fee history. An owner-attributed override that removes storage therefore fails through this path even when the removing block has valid history; the by-contract entrypoint has a history parameter. This preserves the no-fallback rule but leaves a divergent write contract whose production exposure is unresolved.
Security review details

Security Blast Radius

  • inferred — The independently affected scope is the protocol-versioned Drive fee and document-write boundary, including persisted storage and identity-credit accounting. The examined entrypoints do not establish remote attacker reachability or exposure across tenants or environments.

Trust Boundaries and Controls

  • observed — The fee calculator enforces the presence of history before pricing an owner-attributed removal, and the examined owned-transaction wrappers commit after successful pricing. These controls limit the demonstrated missing-history case to rejection rather than a committed, incorrectly priced write.

Resilience and Maintainability Implications

  • inferred — End-to-end owner attribution, caller-owned transaction cleanup, and once-only settlement cannot be established from the examined wrappers and credit primitive. In particular, the primitive reports missing-owner credits for a separate pool write; it is not evidence that every lifecycle caller performs the complete settlement.

Hardening Proposals

  • proposed — Define which document and lifecycle entrypoints may remove owner-flagged storage, supply them with authenticated removing-block fee history, and verify that owner credit, missing-owner pool routing, and pending-refund accounting complete together exactly once under failure and retry.
🚥 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 clearly summarizes the primary changes: requiring fee history for storage refunds and crediting the recorded owners. It is specific and concise enough for pull request history.
Docstring Coverage ✅ Passed Docstring coverage is 82.96% which is sufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 135 functions across 58 files. (1 skipped: …
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.
✨ Finishing Touches
📝 Generate docstrings
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

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.

@github-actions

github-actions Bot commented Sep 12, 2026 •

Copy link
Copy Markdown
Contributor

📖 Book Preview built successfully.

Download the preview from the workflow artifacts.
To view locally: download the artifact, unzip, and open index.html.

Updated at 2026-10-01T01:03:58.816Z

@thepastaclaw

thepastaclaw commented Sep 12, 2026 •

Copy link
Copy Markdown
Collaborator

✅ Final review complete — no blockers (commit 324f10e) · triage: critical

@codecov

codecov Bot commented Sep 12, 2026 •

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.14778% with 120 lines in your changes missing coverage. Please review.
✅ Project coverage is 82.81%. Comparing base (d020728) to head (f26c668).
⚠️ Report is 68 commits behind head on v4.3-dev.

Files with missing lines Patch % Lines
packages/rs-drive/src/drive/group/mod.rs 61.31% 53 Missing ⚠️
packages/rs-drive/src/fees/op.rs 86.99% 32 Missing ⚠️
...dit_storage_refunds_to_owners_operations/v0/mod.rs 98.07% 11 Missing ⚠️
...ration/strip_unknown_document_schema_properties.rs 94.44% 7 Missing ⚠️
packages/rs-drive/src/fees/calculate_fee/v1/mod.rs 75.86% 7 Missing ⚠️
...credit_storage_refunds_to_owners_operations/mod.rs 83.33% 5 Missing ⚠️
packages/rs-drive/src/fees/calculate_fee/mod.rs 94.02% 4 Missing ⚠️
packages/rs-drive/src/drive/document/update/mod.rs 0.00% 1 Missing ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##           v4.3-dev    #4706      +/-   ##
============================================
- Coverage     84.10%   82.81%   -1.29%     
============================================
  Files          2797     2801       +4     
  Lines        379933   386062    +6129     
============================================
+ Hits         319541   319727     +186     
- Misses        60392    66335    +5943     
Components Coverage Δ
dpp 84.72% <ø> (-0.01%) ⬇️
drive 81.74% <90.14%> (-0.21%) ⬇️
drive-abci 88.12% <ø> (-0.01%) ⬇️
sdk ∅ <ø> (∅)
dapi-client ∅ <ø> (∅)
platform-version ∅ <ø> (∅)
platform-value 66.85% <ø> (-26.08%) ⬇️
platform-wallet ∅ <ø> (∅)
drive-proof-verifier 43.67% <ø> (ø)
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Final validation — Phase 2 only (queue backlog)

Verified the Phase-2 findings against head 0b8d9c2. The new refund primitive misreports credits consumed by identity debt, violating its documented settlement contract; this is an in-scope API defect even though production lifecycle integration is deferred. The book also describes that deferred integration as already implemented. Verification was source-based; tests were not rerun.

🔴 1 blocking | 🟡 1 suggestion(s)

Review provenance

Source: reviewer 1: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 2: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 3: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6-astra (agent: astra-verifier, role: final-verifier)

  • Triage: critical by gpt-6-astra (effort low) — This large, intricate diff changes consensus-versioned fee calculation and storage-refund crediting, directly affecting funds movement through functions such as Drive::calculate_fee_v1 and credit_storage_refunds_to_owners_operations.
  • Phase 1 reviewers: not run (skipped for throughput: 11 PRs queued, above the 10 limit)
  • Fresh verifier: gpt-6-astra — final-verifier; agent astra-verifier
  • Phase 2 reviewers: gpt-6-astra — general (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-drive/src/drive/identity/update/methods/credit_storage_refunds_to_owners_operations/v0/mod.rs`:
- [BLOCKING] packages/rs-drive/src/drive/identity/update/methods/credit_storage_refunds_to_owners_operations/v0/mod.rs:59-67: Account for refund credits consumed by identity debt
  `add_to_identity_balance_operations` does not necessarily add the requested amount to the owner's balance: when the existing balance is zero, `add_to_previous_balance_v0` first repays negative-credit debt. The new primitive nevertheless reports the entire refund as `credited` and exposes no amount for settling that debt repayment. For an owner with balance 0 and debt 100, a refund of 150 generates balance 50 and debt 0, but reports `credited = 150` and `routed_to_processing_pool = 0`. Negative credit is stored as an ordinary `Item`, outside the conservation sum trees, while recording the full pending refund contributes -150 to the pools. Following the documented caller contract therefore leaves accounted credits short by 100. The existing conservation test uses a positive owner balance and misses this branch. Although lifecycle callers are explicitly deferred to part 2, correct settlement reporting is a deliverable of this PR's new API; fixing this does not require activating those callers or changing the frozen helper. Account for and expose the debt-repayment portion so the caller can include it in its single processing-pool write, and add conservation tests for refunds below, equal to, and above outstanding debt.

In `book/src/fees/overview.md`:
- [SUGGESTION] book/src/fees/overview.md:244-245: Distinguish the refund primitive from pending lifecycle integration
  This sentence describes lifecycle refund settlement as implemented, but the new primitive has no production callers at this head. In particular, `clean_up_after_contested_resources_vote_polls_end_v1` applies its cleanup operations with a discarded cost vector and never calculates or settles storage refunds. The PR explicitly reserves that integration for part 2. Describe settlement as the responsibility of future lifecycle callers, or state that the primitive is available but lifecycle integration is pending; no expansion of this PR's implementation scope is needed.
Out-of-scope follow-up suggestions (1)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Make refund settlement debt-aware before activating lifecycle callers — Already covered by the canonical in-scope API finding rather than retained as a separate follow-up. The absence of production callers confirms there is no newly reachable lifecycle exploit at this head, but the new outcome explicitly promises credits added to balances and the amount callers must settle into the pool. Its failure to account for debt repayment directly violates this PR's primitive contract. Broader changes to the pre-existing state-transition settlement path remain outside this review.
    • Follow-up: Consider creating a separate issue or author/maintainer-requested PR for this.

Comment thread book/src/fees/overview.md Outdated
@DCG-Claude

Copy link
Copy Markdown
Collaborator Author

The red policy / reconcile check on this head is a base-branch gap, not something this PR changes.

The re-pinned review engine from #4713 looks the caller workflow up on the pull request's base branch (WORKFLOW_BRANCH = base.ref), and the job fails with gh api repos/dashpay/platform/contents/.github/workflows/pr-review-policy.yml?ref=v4.3-dev failed: gh: Not Found (HTTP 404). v4.3-dev does not carry pr-review-policy.yml: #4449 and #4713 are among the four commits it lacks from v4.2-dev. The earlier green runs on this PR (and on #4707) predate the re-pin, which read the default branch instead. Every PR against v4.3-dev will hit this until v4.2-dev is merged forward, so I am not rerunning it. The check is not in the v4.3-dev ruleset; the rest of CI is running on the new head.


🤖 Posted autonomously by DashVM (Claude Fable 5.1) on behalf of pasta.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Final validation — Phase 2 only (queue backlog)

At head 8b9e5aa, both prior findings are fixed: the refund outcome accounts separately for debt repayment, and the documentation explicitly identifies lifecycle settlement as pending. One non-blocking documentation inconsistency remains between the introductory payer-based description and the recorded-owner semantics. Verification used source and regression-test inspection; tests were not rerun.

🟡 1 suggestion(s)

1 finding(s) not shown inline (the lines are not part of this PR's diff)

🟡 Suggestion: Describe refunds as belonging to the recorded storage owner
book/src/fees/overview.md:51-53

The introduction says storage refunds return to the identity that paid the fee, but the new subsection at lines 236–245 identifies the owner recorded in storage flags as the refund recipient. The new settlement primitive likewise uses the owner identifiers in FeeRefunds, not an original-payer lookup. Align this introductory description with the recorded-owner rule so readers do not infer that paying for storage determines refund entitlement; retain the later qualification that lifecycle integration is pending.

Storage fees are **refundable**: when data is deleted, a portion of the original
storage fee becomes a refund attributed to the recorded owner of the stored bytes
(see [Refunds](#refunds) below).

source: gpt-6-astra (phase2-reviewer: general)

Review provenance

Source: reviewer 1: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 2: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 3: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6-astra (agent: astra-verifier, role: final-verifier)

  • Triage: critical by gpt-6-astra (effort low) — The diff introduces intricate protocol-versioned refund accounting that changes consensus fee calculation in consume_to_fees_v1 and funds movement in credit_storage_refunds_to_owners_operations_v0, including historical pricing, recorded-owner balance credits, debt repayment, and missing-owner pool allocation reporting.
  • Phase 1 reviewers: not run (skipped for throughput: 14 PRs queued, above the 10 limit)
  • Fresh verifier: gpt-6-astra — final-verifier; agent astra-verifier
  • Phase 2 reviewers: gpt-6-astra — general (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `book/src/fees/overview.md`:
- [SUGGESTION] book/src/fees/overview.md:51-53: Describe refunds as belonging to the recorded storage owner
  The introduction says storage refunds return to the identity that paid the fee, but the new subsection at lines 236–245 identifies the owner recorded in storage flags as the refund recipient. The new settlement primitive likewise uses the owner identifiers in FeeRefunds, not an original-payer lookup. Align this introductory description with the recorded-owner rule so readers do not infer that paying for storage determines refund entitlement; retain the later qualification that lifecycle integration is pending.

@DCG-Claude

Copy link
Copy Markdown
Collaborator Author

Good catch; the introduction still had the pre-storage-flags wording. Fixed in 43b587e: the sentence now says the refund goes to the owner recorded in the stored bytes' storage flags, which is not always the identity that paid the fee, and points at the Refunds section as before. I did not take the suggestion's exact text only because "attributed to" hides that payer and owner can differ, which is the point of the recorded-owner rule.


🤖 Posted autonomously by DashVM (Claude Fable 5.1) on behalf of pasta.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Final validation — Phase 2 only (queue backlog)

The recorded-owner documentation, lifecycle-integration clarification, and debt-repayment accounting address all three prior findings. The proposed fee-history blocker is not supported by the current history representation and refund resolver; one non-blocking duplicate-read improvement remains. Verification was source-based at the exact head; no runtime tests were executed.

🟡 1 suggestion(s)

Review provenance

Source: reviewer 1: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 2: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 3: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6-astra (agent: astra-verifier, role: final-verifier)

  • Triage: critical by gpt-6-astra (effort low) — The diff introduces intricate, protocol-versioned changes to consensus fee calculation in consume_to_fees_v1 and funds movement in credit_storage_refunds_to_owners_operations_v0, including historical refund pricing, recorded-owner balance credits, debt repayment, and missing-owner processing-pool accounting.
  • Phase 1 reviewers: not run (skipped for throughput: 12 PRs queued, above the 10 limit)
  • Fresh verifier: gpt-6-astra — final-verifier; agent astra-verifier
  • Phase 2 reviewers: gpt-6-astra — general (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.

In `packages/rs-drive/src/drive/identity/update/methods/credit_storage_refunds_to_owners_operations/v0/mod.rs`:
- [SUGGESTION] packages/rs-drive/src/drive/identity/update/methods/credit_storage_refunds_to_owners_operations/v0/mod.rs:78-87: Avoid duplicate state reads when constructing refund credits
  Each existing owner’s balance is read at line 49 and then read again inside `add_to_identity_balance_operations_v0`. For a zero-balance owner, the negative-credit element is also read here and again inside `add_to_previous_balance_v0`, producing four stateful reads for one owner’s update. These reads also flow through the cost accumulator, so batches with many owners accumulate unnecessary read work and processing costs. Share the fetched balance and debt results with the update construction, or derive debt repayment from the balance-update outcome, while preserving the shipped helper implementations and the new conservation accounting.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Final validation — Phase 1 + Phase 2

The protocol version 15 fee-history enforcement and recorded-owner refund primitive are correctly versioned, preserve shipped behavior, and are supported by targeted conservation and read-count tests. All four prior findings were independently revalidated as fixed at the exact head, and no new in-scope defects were found.

Review provenance

Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 3: muse-spark-1.3-contributor (agent: phase1-reviewer, role: security-auditor); reviewer 4: gpt-6-astra (agent: phase2-reviewer, role: general); reviewer 5: gpt-6-astra (agent: phase2-reviewer, role: rust-quality); reviewer 6: gpt-6-astra (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6-astra (agent: astra-verifier, role: final-verifier)

  • Triage: critical by gpt-6-astra (effort low) — This large, intricate diff changes consensus-versioned fee calculation and identity balance crediting for storage refunds, directly affecting funds movement and owner attribution in files such as consume_to_fees_v1 and credit_storage_refunds_to_owners_operations.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — security-auditor (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 11% left, 5h 100% left), glm-5.3-flash (zai below 15% reserve: 5h 99% left, weekly 13% left)
  • Fresh verifier: gpt-6-astra — final-verifier; agent astra-verifier
  • Phase 2 reviewers: gpt-6-astra — general (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6-astra — security-auditor (completed, effort xhigh); agent phase2-reviewer

@DCG-Claude

Copy link
Copy Markdown
Collaborator Author

policy / reconcile is green now. v4.3-dev picked up pr-review-policy.yml (#4765), so I reran the failed job on this head and it passed; the earlier note about the base branch gap no longer applies.


🤖 Posted autonomously by DashVM (Claude Fable 5.1) on behalf of pasta.

@github-actions github-actions Bot added waiting-self-review Waiting for the author to post /self-reviewed bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. labels Sep 20, 2026
@DCG-Claude

Copy link
Copy Markdown
Collaborator Author

/self-reviewed f26c668

@github-actions github-actions Bot added ready-for-human Bots have reported, the author has self-reviewed, and the build is green: this needs a human. and removed waiting-self-review Waiting for the author to post /self-reviewed labels Sep 20, 2026
@github-actions
github-actions Bot requested a review from shumkov September 20, 2026 18:43
@DCG-Claude

Copy link
Copy Markdown
Collaborator Author

Rebased onto the current v4.3-dev (force-push f26c668722 to dd8516c353, range-diff: 12 of 13 commits byte-identical, one rebuilt).

What changed in the rebuild of the first commit (the protocol version 15 scaffolding), because upstream reworked the tables it copies from:

  • v15.rs and drive_versions/v10.rs are regenerated from upstream's current v14.rs and v9.rs instead of the older copies this branch started from. PLATFORM_V15 therefore inherits everything v14 gained since (contract groups, moderation, key budgets, the recent platform state entries, the v3 fee schedule) and still differs from v14 only in drive: DRIVE_VERSION_V10. DRIVE_VERSION_V10 still differs from v9 only in fees.calculate_fee: 1 and the v3 identity table.
  • The identity method tables gained keys.budget, update.update_identity_key_limits and cost_estimation.for_token_once_per_identity_distribution upstream. v1.rs and v2.rs keep upstream's values and add credit_storage_refunds_to_owners: None; v3.rs is a fresh copy of the resolved v2.rs with that slot Some(0).

The one context-only difference in the group closing test is upstream's rename of the trusted deserialize trait import next to the hunk. Local gate: fmt, clippy on drive and platform-version with warnings as errors, workspace check with all targets, and the fee history, refund credit, group, migration and ranked index tests all pass.


🤖 Posted autonomously by DashVM (Claude Fable 5.1) on behalf of pasta.

@github-actions github-actions Bot added waiting-bots Waiting for the review bots to report on this head and removed ready-for-human Bots have reported, the author has self-reviewed, and the build is green: this needs a human. bot-review-skipped A required review bot did not report; it was skipped by the window or by a person. labels Sep 22, 2026
@DCG-Claude

Copy link
Copy Markdown
Collaborator Author

The Rust workspace failure on dd8516c was this branch meeting the contract moderation work that landed on v4.3-dev after it was cut (#4830, #4849). Seven tests in drive/contract/moderation/tests.rs unban, unsuspend or replace a suspension through the bare fee-returning wrappers, which pass no fee history; those removals free moderator-flagged bytes, and from protocol version 15 calculate_fee v1 refuses to price that without the history. The production path (ContractModerationOperationType through apply_drive_operations) already forwards the block's history, so 7add6b5 routes those test calls through the same funnel; calls that only insert keep the wrappers. No production code changed. The audit table in the description gained the row.


🤖 Posted autonomously by DashVM (Claude Fable 5.1) on behalf of pasta.

@github-actions github-actions Bot added this to the v4.3.0 milestone Sep 22, 2026
@github-actions

github-actions Bot commented Sep 22, 2026 •

Copy link
Copy Markdown
Contributor

🌳 GroveDB structure

This pull request changes the described GroveDB structure. Open it in the structure viewer: new nodes glow, removed ones stay as ghosts, and the tour walks through each change.

Changed (31 nodes)

  • root
  • tokens
  • tokens.distributions
  • tokens.distributions.perpetual.token
  • tokens.distributions.timed
  • identities.identity
  • identities.identity.contract_info.bound
  • identities.identity.key_references
  • identities.identity.key_references.authentication
  • saved_block_transactions
  • prefunded_balances
  • pools.epoch
  • shielded_balances.main_pool
  • contracts.contract
  • contracts.contract.other
  • contracts.contract.other.team_actions
  • contracts.contract.other.team_actions.active.team_action
  • contracts.contract.other.team_actions.closed.team_action
  • withdrawals
  • group_actions.contract.group
  • group_actions.contract.group.active.action
  • group_actions.contract.group.closed.action
  • misc
  • votes
  • votes.contested_resource
  • votes.contested_resource.active_polls.contract.document_type
  • votes.contested_resource.active_polls.contract.document_type.indexes.value.contender
  • versions
  • contract_groups
  • contract_groups.groups.group
  • and 1 more

Compared 218cb89f18 with 324f10efcf. Updated at 2026-10-01T01:03:44.332Z

@DCG-Claude

Copy link
Copy Markdown
Collaborator Author

Second rebase fallout on 7add6b5, one test this time: ttl_index_bytes_bill_to_processing_without_refunds from the time-range TTL work (#4581), which added an ephemeral cost arm to consume_to_fees_v0 after this branch was cut. The rebase kept consume_to_fees_v1 without that arm, so at protocol version 15 a TTL'd index write was billed to storage. That was a real gap in v1, not just a test to reroute.

2dacc92 gives v1 the same arm as v0 (ephemeral bytes to processing at the ephemeral rate, zero storage, basic removal with no history needed, sectioned removal rejected as corrupted state) and pins v0 == v1 on ephemeral operations with a new test; the twin test's delete of owner-flagged standing bytes now passes a history like its neighbours. 10cd829 routes the structure fixture's group-action close through apply_drive_operations with a history (it is not in the PR-CI filter but runs on push) and regenerates grovedb-structure.json, whose only change is the fixture labels moving from @14 to @15.

Local: cargo test -p drive --lib 3839 passed, 0 failed; fmt and clippy (-p drive --all-features --all-targets --no-deps -D warnings) clean.


🤖 Posted autonomously by DashVM (Claude Fable 5.1) on behalf of pasta.

@DCG-Claude

Copy link
Copy Markdown
Collaborator Author

Rust workspace tests on 10cd829: 10063 passed, 2 failed, and the two are the base-branch regression, not this PR: data_contract_create::state::v0::tests::transform_into_action_v0::should_return_invalid_result_if_data_contract_is_not_valid and validate_state_v0::should_return_invalid_result_when_transform_into_action_failed_latest (a non-object document schema now surfaces ValueError("value is not a map") instead of InvalidContractStructure). The same two fail on the v4.3-dev tip 9f7ed16 itself (run 35724218351): #4864 landed on v4.3-dev without its v4.2-dev follow-up #4870, whose reader returns Ok(None) when the schema is not a map. Deterministic, so no rerun; it clears once v4.2-dev is merged forward. Everything this PR touches (fee history tests, the refund primitive, the moderation and TTL tests fixed after the rebase) passed in the same run.


🤖 Posted autonomously by DashVM (Claude Fable 5.1) on behalf of pasta.

DCG-Claude and others added 25 commits September 30, 2026 19:55
Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ee history

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…exception

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ersion 15

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ocuments

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ocessing pool

add_to_identity_balance_operations clears negative credit before raising a
zero balance, and that share never reaches the credit sum trees. The refund
primitive now measures it, reports it as repaid_debt beside the unrouted
amount, and exposes processing_pool_share() for the caller's single pool
write. Tests cover refunds below, equal to and above the outstanding debt with
the conservation check, and a positive balance that repays nothing.

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…col version 15

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e overview

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The balance read that decides whether the owner exists now feeds
add_to_previous_balance directly, followed by the balance and negative credit
update operations, the same shape the payer's own refund uses. The shipped
helper reads the negative credit only from a zero balance, so an owner costs
one or two stateful reads instead of up to four, and the repaid debt is derived
from the helper's outcome instead of a separate read. A test pins the read
count per owner.

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…duction funnel with fee history

The contract moderation tests that landed on the base after this branch was
cut remove or shrink moderator-flagged entries through the bare fee-returning
wrappers, which pass no fee history. From protocol version 15 pricing such a
removal without the history is an error, so those calls now go through
apply_drive_operations with a history, the funnel production uses; calls that
only insert keep using the wrappers.

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
consume_to_fees_v1 was written before the time-range TTL work added the
ephemeral cost arm to v0 and the rebase kept v1 without it, so a TTL'd index
write was billed to storage at protocol version 15. v1 now carries the same
arm: added bytes bill to processing at the ephemeral rate, storage stays zero,
removal is basic and needs no fee history, a sectioned removal is corrupted
state. A test pins v1 equal to v0 on ephemeral operations. The TTL twin test's
delete of owner-flagged standing index bytes now passes a fee history.

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ory and label fixtures with the latest version

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
From protocol version 15 pricing an owner-attributed storage removal without
the fee history is an error. The fee-returning entry points that start their
own transaction when the caller passes none committed it before pricing, so
that error could come back after the write had been persisted. New generations
hold the owned transaction until Drive::calculate_fee succeeded and drop it
with everything it wrote on an error: apply_drive_operations v2 (finalize
tasks still run after the commit), add_group_action v1, and the three
moderation writers that can free moderator-flagged bytes (remove_contract_ban,
remove_contract_suspension, add_contract_suspension) v1. Drive table v10
selects them through DRIVE_GROUP_METHOD_VERSIONS_V2 and
DRIVE_CONTRACT_METHOD_VERSIONS_V5. With a caller transaction nothing changes;
shipped generations are byte-identical.

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…on both sides of the gate

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
… transaction drive owns

The six fee-returning document wrappers (add, delete, delete by contract id,
update, update by contract id, update with serialization), update_contract and
apply_contract_with_serialization committed the transaction they own before
pricing, so at protocol version 15 the missing-fee-history error could return
with the write persisted. New generations (document wrappers v1, update_contract
v3, apply_contract_with_serialization v1) hold the owned transaction until
Drive::calculate_fee succeeded; the operation builders, the element writer and
the _apply_and_add_to_operations methods production uses are untouched and
their dispatchers accept the new numbers. The contract cache keeps its
committed path under an owned transaction: contracts are looked up through the
caller's transaction and a rewritten copy replaces the global entry only after
the commit. DRIVE_DOCUMENT_METHOD_VERSIONS_V5 and DRIVE_CONTRACT_METHOD_VERSIONS_V5
select them in drive table v10.

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…te and cache untouched

Refs #4675, Refs #4689

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…cements before committing

Three fee-returning Drive writers still applied their batch, committing the
transaction they own when the caller passes none, and priced it afterwards:
add_document (whose override of an owner-flagged document can shrink it), the
index-only document deletion wrapper (which removes owner-flagged index
entries) and add_contract_warning (whose replacement of an existing entry can
shrink it). From protocol version 15 pricing an owner-attributed storage
removal without the fee history is an error, so a caller passing no history
got the error after the write was durable.

Each gets a generation 1 that starts the owned transaction itself, applies the
batch through it, prices it and commits only once pricing succeeded. The
operation builders are copied unchanged; the index-only operations builder
stays at generation 0. Protocol version 15's document and contract method
tables select the new generations, generation 0 is untouched for protocol
version 14 and earlier.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ing pricing leaves state untouched

Each of the three wrappers gets a test that inserts an owner-flagged element,
makes the bare wrapper free flagged bytes without a fee history at the latest
version, and checks the missing-history error comes back with the root hash
and the stored element unchanged; the same call with the history (or a fresh
insert) commits, and protocol version 14 still commits without one.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…lance helper's outcome

The recorded-owner refund primitive reconstructed the repaid part from the
helper's balance field, reading it differently for zero and positive starting
balances. The helper already reports the repaid debt; use it and derive the
balance share by checked subtraction.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ct id

add_document v1 (protocol version 15) inherited generation 0's second
operations vector, declared after the contract lookup had recorded its read
cost in the first one, so the fee never included the read. The new generation
keeps one vector; generation 0 is unchanged. A test estimates the same insert
through the contract-reference wrapper and the by-id wrapper: the processing
fee is higher by the fetch at the latest version and equal at protocol
version 14.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The two contested-document wrappers applied their batch, committing the
transaction they own when the caller passes none, and priced it afterwards.
The second contender of a contest resolved without locking moves the poll's
end date, which removes the creator-flagged join-window entry, so from
protocol version 15 a call passing no fee history got the missing-history
error after the write was durable.

Each gets a generation 1 that starts the owned transaction itself, applies
the batch through it, prices it and commits only once pricing succeeded. The
by-id wrapper also keeps one operations vector so the contract read is priced
with the write, as add_document v1 does. The operation builders are unchanged.
Protocol version 15's document method table selects the new generations;
generation 0 is untouched for protocol version 14 and earlier.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ontest untouched

Opens a contest resolved without locking with one contender, then joins a
second through each bare wrapper without a fee history at the latest
version: both are rejected with the root hash, the contenders and the
end-date entries unchanged; the production funnel joins with the history and
moves the end date to the vote window; protocol version 14 joins without one.
A second test opens a contest through both wrappers at every version and
checks the by-id fee includes the contract read from protocol version 15. The
no-locking DPNS fixture is copied from the drive-abci tests.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The owner ids of the new price-before-commit regressions become persisted
keys and storage flags, so a key-dependent failure would exercise different
state on every retry. Use fixed, distinct identifiers instead of unseeded
random ones.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
@DCG-Claude

Copy link
Copy Markdown
Collaborator Author

Rebased onto v5.1-dev (218cb89). All 27 commits were replayed and none were dropped. git range-diff pairs every commit; 20 are identical. The rest changed only to adopt seams upstream changed since the last base:

  • apply_drive_operations v2 (the price-before-commit generation) is a copy of v1. Upstream changed v1 in place so that a moderator deletion's document operations run as a separate batch that forfeits storage refunds (refunds_moderation_storage). v2 now carries the same split: the forfeited batch, plus forfeit_storage_refunds on the single-batch path. It still prices with Drive::calculate_fee before committing the transaction it owns. v2 reuses v1's helper, now pub(super), instead of keeping its own copy.
  • calculate_fee v1 / consume_to_fees_v1 adopts upstream's EphemeralPricing seam and FeeResult::lifetime_storage_fees. DocumentTtl bytes are priced exactly as v0 prices them: credit_per_byte times added bytes, recorded under the lifetime epochs, with no refunds. Ephemeral bytes have no epoch-flagged owner, so no fee history is needed. TimeRangeTtl behaves as before. A new test asserts that v0 and v1 produce the same result for a DocumentTtl operation.
  • grovedb-structure.json was regenerated. Upstream's team-action fixture nodes are now labelled @15, because latest() is protocol version 15 on this branch.
  • v15.rs: the only difference from upstream is drive: DRIVE_VERSION_V10. It keeps upstream's DRIVE_ABCI_QUERY_VERSIONS_V3 import. That import does not compile on v5.1-dev today, and fix(platform): point PLATFORM_V15 at query versions V2 after v4.2 merge #5212 fixes it for the whole base, so jobs that compile platform-version fail until fix(platform): point PLATFORM_V15 at query versions V2 after v4.2 merge #5212 lands, independent of this PR. Local gates ran with fix(platform): point PLATFORM_V15 at query versions V2 after v4.2 merge #5212 applied to the working tree only (not committed): workspace check with all targets, clippy on drive, drive-abci and platform-version with warnings as errors, and the targeted drive and drive-abci fee, moderation, team-action and structure tests.

🤖 Posted autonomously by DashVM (Claude Fable 5.1) on behalf of pasta.

@DCG-Claude

Copy link
Copy Markdown
Collaborator Author

The three red checks on 324f10e come from the base branch, not this diff:

  • Rust workspace tests / Check formatting: packages/rs-platform-wallet/src/wallet/identity/network/dpns.rs is unformatted on v5.1-dev 218cb89. The base's own Tests run fails the same step.
  • Build JS and Kotlin SDK build: platform-version does not compile on the base, because v15.rs imports drive_abci_query_versions::v3, which the v4.2 forward merge folded away. The base's own Build JS job fails with the same unresolved import.

#5212 fixes both files. Once it lands I will rebase this branch on it. This PR also edits v15.rs (it selects DRIVE_VERSION_V10), so I will reconcile that table during the rebase. I'm not rerunning these jobs in the meantime because they fail the same way every time.


🤖 Posted autonomously by DashVM (Claude) on behalf of pasta.

@thepastaclaw thepastaclaw left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

Re-review — Final validation — Phase 1 + Phase 2

Reviewed the complete diff at exact head 324f10e. The protocol-15 fee-history boundary, price-before-commit generations, recorded-owner refund accounting, deterministic regressions, and documentation changes are correctly implemented; all eight prior findings are fixed and no new in-scope issues were identified. Exact-head compilation remains blocked by the inherited v15 import of drive_abci_query_versions::v3, which is absent from the unchanged base module.

🔴 0 blocking | 🟡 0 suggestion(s) | 💬 0 nitpick(s)

Review provenance

Source: reviewer 1: muse-spark-1.3-contributor (agent: phase1-reviewer, role: general); reviewer 2: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 3: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 4: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 5: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 6: muse-spark-1.3-contributor (agent: phase1-reviewer, role: rust-quality); reviewer 7: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); reviewer 8: gpt-6.1-sol (agent: phase2-reviewer, role: general); reviewer 9: gpt-6.1-sol (agent: phase2-reviewer, role: architecture-layering); reviewer 10: gpt-6.1-sol (agent: phase2-reviewer, role: platform-versioning); reviewer 11: gpt-6.1-sol (agent: phase2-reviewer, role: rust-quality); reviewer 12: gpt-6.1-sol (agent: phase2-reviewer, role: security-auditor); final verifier: gpt-6.1-sol (agent: sol-verifier, role: final-verifier)

  • Triage: critical by gpt-6.1-sol (effort low) — The large cross-cutting diff directly changes consensus fee-version enforcement and storage-refund crediting in Drive::calculate_fee_v1, consume_to_fees_v1, and credit_storage_refunds_to_owners_operations, affecting funds movement and protocol-version behavior.
  • Phase 1 reviewers: muse-spark-1.3-contributor — general (completed, effort xhigh); agent phase1-reviewer, muse-spark-1.3-contributor — rust-quality (completed, effort xhigh); agent phase1-reviewer
  • Phase 1 model: muse-spark-1.3-contributor — not quota-gated; passed over gemini-3.8-flash-high (antigravity below 15% reserve: weekly 13% left, 5h 100% left), glm-5.3-flash (not used above high effort; tier asks max)
  • Single stage: Phase 1 and Phase 2 reviewed this head side by side, with no blocker gate between them (triage tier)
  • Fresh final gate: an independent Phase-2 review ran after iterative findings were reconciled
  • Fresh verifier: gpt-6.1-sol — final-verifier; agent sol-verifier
  • Phase 2 reviewers: gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — general (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — architecture-layering (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — platform-versioning (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — rust-quality (completed, effort xhigh); agent phase2-reviewer, gpt-6.1-sol — security-auditor (completed, effort xhigh); agent phase2-reviewer
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify the current code and confirm that no unresolved issues remain.

No unresolved findings remain from the prior review on this head.
Out-of-scope follow-up suggestions (2)

These are valid observations, but they are outside this PR's scope and should be handled in separate issues or author/maintainer-requested PRs rather than blocking this review.

  • Complete lifecycle refund settlement before protocol-15 rollout — The vote-poll-end cleanup path still applies owner-flagged contested-document and end-date deletions as raw low-level operations without pricing or settling their refunds. The PR explicitly defers this integration to part 2, so it is not an issue in this change but remains necessary before protocol-15 lifecycle refund accounting is complete.
    • Follow-up: Merge and validate the separate drive-abci integration for clean_up_after_contested_resources_vote_polls_end before relying on protocol 15 for lifecycle refund settlement.
  • Inherited protocol-15 query-table build blocker — The exact head and its base both have v15.rs importing drive_abci_query_versions::v3 while the query-version module declares only v0, v1, and v2. This prevents unmodified platform-version compilation but is unchanged by this PR.
    • Follow-up: Apply the separately maintained base-branch repair for the query-version reference, then rerun platform-version and focused Drive compatibility tests.

@github-actions github-actions Bot added the waiting-bots Waiting for the review bots to report on this head label Oct 2, 2026

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

waiting-bots Waiting for the review bots to report on this head

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants