Skip to content

feat(platform)!: deletable charter team changes and lookups on deletableDocument references (PV14) - #4967

Merged
QuantumExplorer merged 2 commits into
v4.2-devfrom
feat/charter-deletable-team-changes
Sep 24, 2026
Merged

QuantumExplorer merged 2 commits into
v4.2-devfrom
feat/charter-deletable-team-changes

Conversation

@QuantumExplorer

@QuantumExplorer QuantumExplorer commented Sep 24, 2026 •

Copy link
Copy Markdown
Member

Issue being fixed or feature implemented

Follow-up to #4952 (elected moderation teams moderate from their stored charter), part of #4865.

In the moderation charters contract every team change was final. The leader took an added member off by filing a removedModerator for them, so:

  • the addedModerator stayed forever and kept its maxAddedModerators slot;
  • the member could never be added again;
  • checking whether a non-elected identity sits on the team took two point reads (the addition, then a removal);
  • nothing could undo a removal.

This PR makes addedModerator and removedModerator deletable, so deleting a team change undoes it. A removal may now only name an elected member. The one reference that has to follow a deletable addition is a resignation request's membership check, so this PR also adds a document reference found through a lookup on a deletableDocument target.

What was done?

Moderation charters contract (moderation-charters-contract, PV14 genesis)

  • addedModerator: canBeDeleted: true. Deleting an addition takes the member off the team and frees its cap slot. The member can then be added again.
  • removedModerator: canBeDeleted: true. Deleting a removal puts the elected member back. memberId now carries refersTo: { type: "listElement", documentType: "electedCharter", propertyAgreement: { electedCharterId: "$id" }, inList: "members" }, so a removal names an elected member and nobody else.
  • resignationRequest.ownerRefersTo.anyOf[1] is now a deletableDocument reference through the same byElectedCharterMember lookup, so an added member can file a request only while their addition exists. Its old permanentDocument form could not register against a deletable addedModerator.
  • Contract README, docs/protocol/moderation-charters.md and the book chapter describe the new rules.

Seating (drive-abci)

  • SeatedModerationCharter::seats does one point read:
    • an elected member is seated unless a removedModerator exists for them;
    • anyone else is seated if an addedModerator exists for them.
  • The maxAddedModerators cap counts the additions that exist now, using the same equality query as before. Deleting an addition frees its slot.

refersTo with a lookup on deletableDocument (rs-dpp, PV14)

  • New DocumentPropertyReferenceTarget::DeletableDocumentLookup, appended as variant 10 (the enum is encoded inside consensus errors), serialized under the deletableDocument tag.
  • Its meaning: a document with this key exists now. A key into a deletable type may find a later document once the one it found is deleted, so:
    • every replace re-validates it (binds_a_changed_property);
    • an immutable property may not hold it, alone or as an expression operand, because the clearing exception reads a document id and a lookup key is not one;
    • it is the one deletable form a reference expression (anyOf / allOf) takes as an operand;
    • ownerRefersTo takes it. The writer never changes, so every replace gates on the writer's document still existing.
    • creatorRefersTo refuses it. A transfer would leave the new owner unable to replace a document once the creator's document is gone.
  • Registration checks its referenced side like a permanent lookup: a unique index covering the keys, with keys that stay with the document. A deletable lookup into a type that forbids deletion is refused with ReferencedDocumentTypeNotDeletableError (40131), as the by-id form already is.
  • Composite and chained queries refuse to join through a deletable lookup, as they refuse a permanent one.
  • Meta-schema v3, the wasm-dpp2 Reference surface (lookup on deletableDocument, and operands that include listElement and deletableDocument), and the book's documents chapter are updated.

SDK

  • Sdk::fetch_pending_resignation_requests (pendingResignationRequests in JS) keeps the requests whose writer is still on the team. It reads the charter and both team-change lists, because an added member now leaves when their addition is deleted, which no removal records.

Before and after

Taking an added member off:

// Before: the leader files a removal; the addition stays, its slot is spent for good,
// and the member can never be added again (addedModerator is unique per member)
{ "$type": "removedModerator", "electedCharterId": "<charter>", "memberId": "<added member>" }

// After: the leader deletes the addition. The slot is free, and a later addition may add
// the member again. A removal naming them is refused: they are not an elected member
{ "$type": "removedModerator", "electedCharterId": "<charter>", "memberId": "<added member>" }
// -> ReferencedEntityNotFoundError (40120) at memberId

Undoing a removal:

Before: removedModerator for an elected member is final; that member is off the team for good.
After:  the leader deletes the removedModerator and the member moderates again.

The cap (maxAddedModerators: 2):

Before: add A, add B, remove A, add C  -> C refused (41202), two additions ever filed
After:  add A, add B, delete A's addition, add C  -> C accepted, two additions exist

The seat check for a non-elected identity:

Before: point read of addedModerator, then, when found, a point read of removedModerator
After:  point read of addedModerator only (a removal only ever names an elected member)

A lookup on a deletableDocument reference:

"ownerRefersTo": {
  "type": "deletableDocument",
  "documentType": "addedModerator",
  "lookup": { "index": "byElectedCharterMember", "keys": { "electedCharterId": "electedCharterId", "memberId": "." } }
}
// Before: refused, "deletableDocument refersTo does not take lookup: it is only allowed on
//         permanentDocument references"
// After:  DeletableDocumentLookup. The write is refused with 40120 at $ownerId once the
//         addition is deleted, on every replace too

How Has This Been Tested?

  • rs-dpp (--all-features --lib):
    • reference_lookup_tests: a deletable lookup parses, its referenced side is checked, it is refused in an immutable property, and element lookups work.
    • owner_reference_tests: ownerRefersTo takes a deletable lookup alone and in anyOf; creatorRefersTo refuses it through the meta-schema and the parser; the by-id messages are updated.
    • reference_expression_tests: a deletable lookup operand parses and is refused in an immutable property.
    • system_data_contracts: the three deletable team-change types, removedModerator.memberId as a listElement into members, and the resignation's deletable lookup.
    • state_error: the new variant is pinned at 10.
    • The property tests match exhaustively, with no wildcard arm.
    • 505 + 34 tests pass.
  • rs-drive-abci:
    • seated_team:
      • deleting an addition takes the member off and deleting a removal reinstates;
      • should_remove_only_an_elected_member (40120 for an added member and for a stranger);
      • should_cap_the_members_a_leader_adds_and_free_a_slot_when_an_addition_is_deleted.
    • owner_reference: should_check_a_deletable_owner_lookup_on_every_replace checks create acceptance and refusal, the lookup billed on a replace that leaves its keys alone, and the refusal after the seat is deleted.
    • moderation, batch::tests, data_contract, genesis and charter filtered runs: 1012 passed.
  • dash-sdk moderation_charters: 12 passed.
  • The moderation charters contract's JS spec and the wasm-dpp2 DocumentPropertyReference.spec.ts were updated (deletable types, the removal's listElement, and a deletable lookup operand reported with its lookup). They were not run locally.
  • cargo clippy -p dpp -p drive -p drive-abci -p dash-sdk --all-targets --all-features -- -D warnings passes.

Breaking Changes

Protocol version 14, which is unreleased:

  • The moderation charters system contract changes: two more deletable types, a listElement reference on removedModerator.memberId, and a deletableDocument lookup in resignationRequest.
  • DocumentPropertyReferenceTarget gains an appended variant. A client built before it cannot decode a consensus error that carries a deletable lookup target.
  • Meta-schema v3 accepts lookup on deletableDocument.

In-place changes to shipped generations

These can't change consensus at any shipped protocol version, because only the generation 3 document type parser (protocol version 14) produces DeletableDocumentLookup, and nothing before it produces any lookup:

  • create_document_types_from_document_schemas v1: skips the referenced-side lookup check when the reference kind and the target's deletability disagree (permanent == deletable) rather than whenever the target is deletable. Before protocol version 14 no lookup exists for this loop to see.
  • document_reference_validation v0 and data_contract_reference_validation v0: the new variant joins the arms of the other document references.

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
  • If I added or changed GroveDB structure, I described it in the area's structure.rs, regenerated grovedb-structure.json, and checked the structure viewer link posted on this pull request

For repository code-owners and collaborators only

  • I have assigned this pull request to a milestone

🤖 Generated with Claude Code

PR Hygiene · 5c59672

  • Bots — coderabbitai not yet · thepastaclaw not yet — /skip-bots proceeds without the ones not yet reported
  • Self-review — post /self-reviewed once the bots are done
  • Within your 5 open PRs — this one is beyond the limit; it waits until one merges
  • Build running
  • Approvals
    • files with no dedicated owner — you own it
    • js-wasm-sdk (packages/js-evo-sdk/README.md, packages/wasm-sdk/src/moderation_charters.rs) — shumkov
    • dpp — you own it
    • rs-drive-abci — you own it
    • rs-drive — you own it
    • rust-sdk (packages/rs-sdk/src/platform/moderation_charters/mod.rs, packages/rs-sdk/src/platform/moderation_charters/readers.rs, packages/rs-sdk/src/platform/moderation_charters/team.rs) — lklimek or shumkov

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

Summary by CodeRabbit

  • New Features
    • Moderation leaders can now remove an added member by deleting their addition, or restore an elected member by deleting their removal.
    • The added-moderator limit counts additions that currently exist; deleting an addition frees a slot. Removals are limited to elected members.
    • Pending resignation requests reflect whether the requester is still on the team.
    • Document references now support lookup-based references to deletable documents in supported contexts.
  • Documentation
    • Updated moderation and reference guidance to describe these rules.

…bleDocument references (PV14)

addedModerator and removedModerator are deletable: deleting an addition takes
the member off and frees its maxAddedModerators slot, deleting a removal puts
the elected member back. A removal names an elected member only (a listElement
into the charter's members), so the seat check is one point read and the cap
counts the additions that exist.

refersTo takes a lookup on a deletableDocument reference
(DeletableDocumentLookup, appended variant 10): the document exists now, every
replace re-validates it, an immutable property may not hold it, and it is an
expression operand and an ownerRefersTo target (never creatorRefersTo). The
resignation request's added-member operand uses it.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added this to the v4.2.0 milestone Sep 24, 2026
@coderabbitai

coderabbitai Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Review in Change Stack →

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

Note

Currently processing new changes in this PR. This may take a few minutes, please wait...

⚙️ Run configuration

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

Review profile: CHILL

Plan: Advanced

Run ID: f39d7576-565a-44d3-abaa-d073a6c61cff

📥 Commits

Reviewing files that changed from the base of the PR and between 7fc89b9 and 5c59672.

📒 Files selected for processing (39)
  • book/src/data-model/contract-moderation.md
  • book/src/data-model/documents.md
  • docs/protocol/moderation-charters.md
  • packages/js-evo-sdk/README.md
  • packages/moderation-charters-contract/README.md
  • packages/moderation-charters-contract/schema/v1/moderation-charters-contract-documents.json
  • packages/moderation-charters-contract/test/unit/moderationChartersContract.spec.js
  • packages/rs-dpp/schema/meta_schemas/document/v3/document-meta.json
  • packages/rs-dpp/src/data_contract/config/moderation/elected.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/create_document_types_from_document_schemas/v1/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v3/mod.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v3/owner_reference_tests.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v3/reference_expression_tests.rs
  • packages/rs-dpp/src/data_contract/document_type/class_methods/try_from_schema/v3/reference_lookup_tests.rs
  • packages/rs-dpp/src/data_contract/document_type/index/preallocation.rs
  • packages/rs-dpp/src/data_contract/document_type/property/mod.rs
  • packages/rs-dpp/src/errors/consensus/state/contract_moderation/moderation_charter_added_moderator_limit_reached_error.rs
  • packages/rs-dpp/src/errors/consensus/state/state_error.rs
  • packages/rs-dpp/src/moderation_charter/mod.rs
  • packages/rs-dpp/src/moderation_charter/tests.rs
  • packages/rs-dpp/src/system_data_contracts.rs
  • packages/rs-dpp/src/validation/meta_validators/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/common/seated_moderation_charter/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/action_validation/document/document_reference_validation/v0/mod.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/state/v0/added_moderator_cap.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/batch/tests/document/owner_reference.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/contract_user_moderation/tests/seated_team.rs
  • packages/rs-drive-abci/src/execution/validation/state_transition/state_transitions/data_contract_common/data_contract_reference_validation/v0/mod.rs
  • packages/rs-drive-abci/tests/supporting_files/contract/reference-validation/reference-validation-contract-owner-refers-to.json
  • packages/rs-drive/src/query/chained_document_query/mod.rs
  • packages/rs-drive/src/query/composite_document_query/mod.rs
  • packages/rs-platform-version/src/version/v14.rs
  • packages/rs-sdk/src/platform/moderation_charters/mod.rs
  • packages/rs-sdk/src/platform/moderation_charters/readers.rs
  • packages/rs-sdk/src/platform/moderation_charters/team.rs
  • packages/wasm-dpp2/src/data_contract/document_type_reference.rs
  • packages/wasm-dpp2/tests/unit/DocumentPropertyReference.spec.ts
  • packages/wasm-sdk/src/moderation_charters.rs
 __________________________________________________________________________________________
< Recursion is the root of computation since it trades description for time. - Alan Perlis >
 ------------------------------------------------------------------------------------------
  \
   \   (\__/)
       (•ㅅ•)
       /   づ
✨ 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 24, 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-09-24T05:40:44.747Z

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

thepastaclaw commented Sep 24, 2026 •

Copy link
Copy Markdown
Collaborator

🕓 Queued for automated review — 6th in line, estimated start in ~1.7 h (commit 5c59672)
Estimated review time once started: ~40 min (two-phase automated review; median of recent runs).

  • Request priority review — click to move this review to the front of the queue.

… reference

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@QuantumExplorer
QuantumExplorer merged commit c3ba931 into v4.2-dev Sep 24, 2026
8 of 10 checks passed
@QuantumExplorer
QuantumExplorer deleted the feat/charter-deletable-team-changes branch September 24, 2026 05:47
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