From bb37a7082cf1d6ae53e28f276aa599c6a0caa557 Mon Sep 17 00:00:00 2001 From: bfoss765 <38437574+bfoss765@users.noreply.github.com> Date: Sat, 1 Aug 2026 08:01:31 -0400 Subject: [PATCH 01/11] docs(ffi): error-code registry for the FFI result space MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Four open PRs allocate discriminants into PlatformWalletFFIResultCode in parallel, with no shared source of truth. A duplicate discriminant does not produce a textual merge conflict, so the second merge silently misclassifies errors on every host — one such collision (code 29) has already required a renumber directive in review. Adds packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md: the merged allocation table (0-26, 98, 99), the proposed allocations with owning PRs (27-33), the rule for claiming the next free integer, the ABI-stability rule (never renumber after release, deprecate instead), and the currently contested allocations. Links it from the crate README's error-handling section. Docs only — no enum, mapping, or mirror is changed. Renumbering remains the owning PRs' work. Co-Authored-By: Claude Opus 4.8 --- .../ERROR_CODE_REGISTRY.md | 157 ++++++++++++++++++ packages/rs-platform-wallet-ffi/README.md | 6 + 2 files changed, 163 insertions(+) create mode 100644 packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md diff --git a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md new file mode 100644 index 00000000000..3d1c883b189 --- /dev/null +++ b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md @@ -0,0 +1,157 @@ +# FFI Error-Code Registry + +Single source of truth for the integer values of +`PlatformWalletFFIResultCode` (`packages/rs-platform-wallet-ffi/src/error.rs`). + +Every value in that enum is **public ABI**. `cbindgen` emits it into the +generated C header, and hosts compare against the integer — Swift +(`packages/swift-sdk/Sources/SwiftDashSDK/PlatformWallet/PlatformWalletResult.swift`) +mirrors it as a `RawRepresentable` enum, Kotlin +(`packages/kotlin-sdk/.../errors/DashSdkError.kt`) branches on it in +`fromPlatformWalletNative`. A shipped host binary that was compiled against +one numbering keeps using that numbering. + +This file exists because several feature branches allocate into the same +integer range in parallel. A duplicate discriminant in two branches does **not** +produce a textual merge conflict — the second merge silently misclassifies +errors on every host — so allocations have to be reconciled here, in one place, +rather than in each branch's diff. + +## Rules + +1. **Claim the next free integer** from the table below — the first value not + listed as merged, proposed, or reserved. Do not reuse a gap unless this file + marks it free. +2. **Record the claim in this file in the same PR** that adds the variant. A PR + that adds a code without a row here is incomplete. +3. **Never renumber a code after it has shipped in a release.** Deprecate + instead: leave the row, mark it deprecated, and allocate a new integer. Codes + that are still only proposed (unmerged) may be renumbered to resolve a + collision; codes on `v4.2-dev` may not. +4. **Do not reuse a retired integer.** Mark it reserved and move on. +5. **Update the mirrors in the same PR**: the Rust enum, the Swift + `PlatformWalletResultCode` + its `init(result:)` switch, and — where the code + deserves typed handling — the Kotlin `fromPlatformWalletNative` mapping and + `DashSdkErrorTest`. Kotlin is allowed to be non-exhaustive: unmapped codes + fall through to `PlatformWallet.Generic(code, …)`, which preserves the + integer. Swift is exhaustive; an unmirrored code surfaces as + `.errorUnknown` there and loses its identity. +6. **Blocks 98–99 are terminal sentinels** (`NotFound`, `ErrorUnknown`) and are + not an allocation frontier. New codes go after the highest allocated value + below them. + +## Merged allocations (`v4.2-dev`) + +These are shipped ABI. Do not renumber. + +| Code | Name | Notes | +| ---: | --- | --- | +| 0 | `Success` | | +| 1 | `ErrorInvalidHandle` | | +| 2 | `ErrorInvalidParameter` | | +| 3 | `ErrorNullPointer` | | +| 4 | `ErrorSerialization` | | +| 5 | `ErrorDeserialization` | | +| 6 | `ErrorWalletOperation` | | +| 7 | `ErrorIdentityNotFound` | | +| 8 | `ErrorContactNotFound` | | +| 9 | `ErrorInvalidNetwork` | | +| 10 | `ErrorInvalidIdentifier` | | +| 11 | `ErrorMemoryAllocation` | | +| 12 | `ErrorUtf8Conversion` | | +| 13 | `ErrorArithmeticOverflow` | Reserved slot — declared, no in-tree producer; holds the number for the mapping arriving via #3549 | +| 14 | `ErrorNoSelectableInputs` | | +| 15 | `ErrorWalletAlreadyExists` | | +| 16 | `ErrorShieldedBroadcastFailed` | | +| 17 | `ErrorShieldedBroadcastUnconfirmed` | | +| 18 | `ErrorShieldedSpendUnconfirmed` | | +| 19 | `ErrorShieldedNoRecordedAnchor` | | +| 20 | `ErrorTransactionBroadcastUnconfirmed` | | +| 21 | `ErrorAddressNonceMismatch` | | +| 22 | `ErrorCoreInsufficientFunds` | | +| 23 | `ErrorAssetLockNotTracked` | | +| 24 | `ErrorAssetLockAlreadyConsumed` | | +| 25 | `ErrorAssetLockFundingMismatch` | | +| 26 | `ErrorTransactionBroadcastRejected` | Merged in `9302c62e8b`; took a number several open branches had been treating as free | +| 98 | `NotFound` | Sentinel — `Option` returned as an error | +| 99 | `ErrorUnknown` | Sentinel — unmapped/flattened errors | + +**Next free integer: 34** (see the proposed table; 27–33 are claimed). + +## Proposed allocations (open PRs) + +Not yet ABI. Numbers here may still move; they move by agreement recorded in +this file. + +| Code | Name | Owning PR | Status | +| ---: | --- | --- | --- | +| 27 | `ErrorStaleReservationToken` | #4185 | In review (also carried by #4256) | +| 28 | `ErrorReservationTokenConsumed` | #4185 | In review (also carried by #4256) | +| 29 | `ErrorReservationWalletMismatch` | #4185 | **Collision** — see below | +| 29 | `ErrorAssetLockInsufficientFunds` | #4184 | **Collision** — see below | +| 30 | — | — | **Unallocated.** Reserved in sibling comments only; see below | +| 31 | `ErrorSigningKeyUnavailable` | #4183 | In review (also carried by #4204) | +| 32 | `ErrorTransactionBuild` | #4247 | In review (also carried by #4256) | +| 33 | `ErrorTransactionSigning` | #4256 | In review | + +Open PRs that touch `rs-platform-wallet-ffi` but claim **no** new code: #4186, +#4191, #4194, #4195, #4240, #4251, #4258. + +## Contested and pending + +### 29 — `ErrorReservationWalletMismatch` (#4185) vs `ErrorAssetLockInsufficientFunds` (#4184) + +Both PR heads define code 29. This is the known collision: review on #4185 +directed that PR to keep #4184's `29 = ErrorAssetLockInsufficientFunds` and move +`ErrorReservationWalletMismatch` to 30. That renumber has not landed on #4185's +head, and #4256 (stacked downstream) carries the pre-renumber `29`. + +Resolution of record: **#4184 keeps 29; #4185 moves to 30**, propagated through +the Rust enum, the FFI `From` mapping, Swift `PlatformWalletResult`, Kotlin +`DashSdkError` (+ `DashSdkErrorTest`), and the JNI rustdoc — plus #4256, which +inherits the value. + +### 30 — reserved in comments for a variant that no longer exists + +`ErrorAssetLockCrossDomainConsentRequired` is named as the holder of 30 in +in-tree comments on #4183, #4204, and #4247/#4256's numbering rationale. It is +**not defined anywhere** — #4184, the PR that would have introduced it, does not +contain it after a re-scope. 30 is therefore free, and is the slot the #4185 +renumber above should take. The stale "reserved for the consent code" comments +should be dropped by whichever PR touches them next. + +### 26 — `ErrorStaleReservationToken` on #4196 collides with merged ABI + +#4196 (stacked on #4185) branched before `26 = ErrorTransactionBroadcastRejected` +merged, and its head numbers the reservation trio **26 / 27 / 28**. Merging it as +it stands would give 26 two meanings and would contradict #4185's own 27 / 28 / 29 +for the same three names. #4196 needs a rebase onto current `v4.2-dev` and must +adopt whatever numbering #4185 lands with. No new integers are needed for it. + +### 31 vs 33 — two signing-related codes, deliberately distinct + +Review on #4256 suggested mapping its signing failure onto 31. #4256 declined and +took 33, on the grounds that 31 (`ErrorSigningKeyUnavailable`, #4183) asserts a +specific contract — the signer holds no usable private key for a requested public +key, restored from a typed signer completion code — whereas #4256's +`BuilderError::SigningFailed` also covers unresolved derivation paths, sighash +failures, and malformed signature encodings. Both codes are currently allocated. +Maintainers may still choose to collapse them; that decision belongs to #4183 and +#4256 jointly and should be recorded here. + +## Sibling FFI crates + +`rs-sdk-ffi`'s `DashSDKErrorCode` (`packages/rs-sdk-ffi/src/error.rs`) is a +**separate** integer space (0–10, plus `InternalError = 99`) and is not contested +by any of the PRs above — none of them modify it. Do not assume a number means +the same thing in both enums. + +## Survey provenance + +Compiled 2026-08-01 against `v4.2-dev` at `ed4116b26c` and the following PR +heads: #4183 `2cd948331b`, #4184 `a9e418af50`, #4185 `7d85953c2a`, #4186 +`6f7abbadc1`, #4191 `8acb0bd14c`, #4194 `9efc0b7e3a`, #4195 `4f2eb06d64`, #4196 +`ea4f783490`, #4204 `7bc8a845c6`, #4240 `9328609a16`, #4247 `72c000dcfd`, #4251 +`176f8ed3eb`, #4256 `d8943ccf10`, #4258 `5adfc40032`. Rows describing open PRs +reflect those heads and go stale as the PRs are updated; the merged table does +not. diff --git a/packages/rs-platform-wallet-ffi/README.md b/packages/rs-platform-wallet-ffi/README.md index 7a94d2a01f9..2380c9a1529 100644 --- a/packages/rs-platform-wallet-ffi/README.md +++ b/packages/rs-platform-wallet-ffi/README.md @@ -175,6 +175,12 @@ Error codes: - `PLATFORM_WALLET_FFI_ERROR_CONTACT_NOT_FOUND` - Contact not found - And more... +The result codes are **public ABI**: their integer values are consumed by the +generated C header and mirrored by the Swift and Kotlin SDKs. Before adding a +new code, read [ERROR_CODE_REGISTRY.md](ERROR_CODE_REGISTRY.md) — it holds the +authoritative integer→name allocation, the rule for claiming the next free +value, and the currently contested allocations across open PRs. + ## Testing Run the test suite: From 690d464a9103f8f1aa67b3e427bb6b130c25d1a8 Mon Sep 17 00:00:00 2001 From: bfoss765 <38437574+bfoss765@users.noreply.github.com> Date: Sat, 1 Aug 2026 15:18:58 -0400 Subject: [PATCH 02/11] docs(ffi): record the 29/30 resolution as landed; add missing 27/28 collisions The 29 collision is resolved and the renumber has now landed on #4185's branch: #4184 keeps 29 (ErrorAssetLockInsufficientFunds), #4185 takes 30 (ErrorReservationWalletMismatch). Table rows updated to match the code. Fixes the "30 is both free and assigned" inconsistency: the next-free line claimed 27-33 were claimed while the table showed 30 unallocated. 30 is now genuinely allocated to #4185, so the two agree. Adds allocations the survey had omitted, verified 2026-08-01 by reading error.rs at the head of all 62 open PRs: - #3968 numbers 26/27/28 (Persister* + a pre-merge TransactionBroadcastRejected) -> contradicts merged ABI at 26 and collides with #4185 at 27 and 28 - #3954 numbers ErrorShutdownIncomplete = 27 -> collides with #4185 at 27 - #4259 carries ErrorSigningKeyUnavailable = 31, inherited from #4183 rather than a new allocation The same sweep confirms no open PR anywhere defines a code 30. --- .../ERROR_CODE_REGISTRY.md | 68 ++++++++++++++----- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md index 3d1c883b189..a5fcde291f2 100644 --- a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md +++ b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md @@ -76,7 +76,9 @@ These are shipped ABI. Do not renumber. | 98 | `NotFound` | Sentinel — `Option` returned as an error | | 99 | `ErrorUnknown` | Sentinel — unmapped/flattened errors | -**Next free integer: 34** (see the proposed table; 27–33 are claimed). +**Next free integer: 34** — 27–33 are all claimed in the proposed table below. +(Before the 29/30 resolution landed this line disagreed with the table, which +still showed 30 as unallocated; 30 is now allocated to #4185 and the two agree.) ## Proposed allocations (open PRs) @@ -87,9 +89,8 @@ this file. | ---: | --- | --- | --- | | 27 | `ErrorStaleReservationToken` | #4185 | In review (also carried by #4256) | | 28 | `ErrorReservationTokenConsumed` | #4185 | In review (also carried by #4256) | -| 29 | `ErrorReservationWalletMismatch` | #4185 | **Collision** — see below | -| 29 | `ErrorAssetLockInsufficientFunds` | #4184 | **Collision** — see below | -| 30 | — | — | **Unallocated.** Reserved in sibling comments only; see below | +| 29 | `ErrorAssetLockInsufficientFunds` | #4184 | In review — **keeps 29** (collision resolved) | +| 30 | `ErrorReservationWalletMismatch` | #4185 | In review — **moved 29 → 30** (collision resolved; #4256 must inherit) | | 31 | `ErrorSigningKeyUnavailable` | #4183 | In review (also carried by #4204) | | 32 | `ErrorTransactionBuild` | #4247 | In review (also carried by #4256) | | 33 | `ErrorTransactionSigning` | #4256 | In review | @@ -99,26 +100,56 @@ Open PRs that touch `rs-platform-wallet-ffi` but claim **no** new code: #4186, ## Contested and pending -### 29 — `ErrorReservationWalletMismatch` (#4185) vs `ErrorAssetLockInsufficientFunds` (#4184) +### 29 — RESOLVED: #4184 keeps 29; #4185 moved to 30 -Both PR heads define code 29. This is the known collision: review on #4185 -directed that PR to keep #4184's `29 = ErrorAssetLockInsufficientFunds` and move -`ErrorReservationWalletMismatch` to 30. That renumber has not landed on #4185's -head, and #4256 (stacked downstream) carries the pre-renumber `29`. +Both PR heads defined code 29. Resolution of record: **#4184 keeps +`29 = ErrorAssetLockInsufficientFunds`; #4185 moves `ErrorReservationWalletMismatch` +to 30.** -Resolution of record: **#4184 keeps 29; #4185 moves to 30**, propagated through -the Rust enum, the FFI `From` mapping, Swift `PlatformWalletResult`, Kotlin -`DashSdkError` (+ `DashSdkErrorTest`), and the JNI rustdoc — plus #4256, which -inherits the value. +**This renumber has now landed on #4185's branch**, propagated through every +site: the Rust enum discriminant and its three rustdoc cross-references +(`rs-platform-wallet-ffi/src/error.rs`), the two `signed_payment.rs` doc +references, the JNI rustdoc (`rs-unified-sdk-jni/src/wallet_manager.rs`), Swift +`PlatformWalletResultCode`'s raw value + doc +(`PlatformWalletResult.swift`), and Kotlin's `fromPlatformWalletNative` branch, +class KDoc, code-98 comment (`DashSdkError.kt`), `WalletManagerNative.kt` KDoc, +and the `DashSdkErrorTest` offset assertion. -### 30 — reserved in comments for a variant that no longer exists +Both Swift `switch`es are symbolic — `init(ffi:)` matches cbindgen-generated +`PLATFORM_WALLET_FFI_RESULT_CODE_*` constants, so only the enum's raw value +carried the number. + +**Still outstanding:** #4256 is stacked downstream and its head still carries the +pre-renumber `29`; it must adopt 30 when it rebases. #4196 likewise (see below). + +### 30 — allocated to #4185; the old "consent code" reservation was stale `ErrorAssetLockCrossDomainConsentRequired` is named as the holder of 30 in in-tree comments on #4183, #4204, and #4247/#4256's numbering rationale. It is **not defined anywhere** — #4184, the PR that would have introduced it, does not -contain it after a re-scope. 30 is therefore free, and is the slot the #4185 -renumber above should take. The stale "reserved for the consent code" comments -should be dropped by whichever PR touches them next. +contain it after a re-scope. + +Verified 2026-08-01 by reading `packages/rs-platform-wallet-ffi/src/error.rs` at +the head of **every one of the 62 open PRs**: no PR anywhere defines a code 30. +30 was therefore genuinely free, and #4185 has taken it. The stale "reserved for +the consent code" comments should be dropped by whichever PR touches them next. + +### 27 / 28 — #3968 and #3954 collide with #4185's reservation trio + +Found by the same 2026-08-01 sweep; these were missing from the tables above. + +- **#3968** (`5931df745a`) numbers `ErrorPersisterTransient = 26`, + `ErrorPersisterFatal = 27`, `ErrorTransactionBroadcastRejected = 28`. It + branched before `26 = ErrorTransactionBroadcastRejected` merged, so it both + contradicts merged ABI at 26 **and** collides with #4185 at 27 and 28. +- **#3954** (`93d0bd49b7`) numbers `ErrorShutdownIncomplete = 27`, colliding with + #4185's `ErrorStaleReservationToken = 27`. +- **#4259** (`4270d827c2`) carries `ErrorSigningKeyUnavailable = 31` — the same + number and name as #4183, i.e. inherited rather than a new allocation, like + #4204. + +Both #3968 and #3954 need a rebase onto current `v4.2-dev` and fresh integers +from the frontier below; #4185's 27/28 are the older claim and should stand. ### 26 — `ErrorStaleReservationToken` on #4196 collides with merged ABI @@ -149,7 +180,8 @@ the same thing in both enums. ## Survey provenance Compiled 2026-08-01 against `v4.2-dev` at `ed4116b26c` and the following PR -heads: #4183 `2cd948331b`, #4184 `a9e418af50`, #4185 `7d85953c2a`, #4186 +heads: #3954 `93d0bd49b7`, #3968 `5931df745a`, #4183 `2cd948331b`, #4184 +`a9e418af50`, #4185 `0b0d5c76d6` (post-renumber), #4259 `4270d827c2`, #4186 `6f7abbadc1`, #4191 `8acb0bd14c`, #4194 `9efc0b7e3a`, #4195 `4f2eb06d64`, #4196 `ea4f783490`, #4204 `7bc8a845c6`, #4240 `9328609a16`, #4247 `72c000dcfd`, #4251 `176f8ed3eb`, #4256 `d8943ccf10`, #4258 `5adfc40032`. Rows describing open PRs From 6f2f2d0f85c06bcf9c871974c51f60dffa323d74 Mon Sep 17 00:00:00 2001 From: bfoss765 <38437574+bfoss765@users.noreply.github.com> Date: Sat, 1 Aug 2026 21:04:33 -0400 Subject: [PATCH 03/11] docs(ffi): give the unlisted allocations rows; correct provenance; record #4196 scope MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Clears the two review blockers on #4261 and re-syncs the registry with what the code on each branch actually does, re-read at every head rather than trusted from this file. Blocker (a) — #3968 / #3954 / #4259 were described in prose but had no rows, which is exactly what rule 2 forbids. They now have them: - A "Non-conforming allocations" table for #3968 (26/27/28) and #3954 (27). These are deliberately kept out of the proposed table: each row is a claim to be withdrawn and reissued, not an allocation of record. - An inherited-code table for the 31 that #4204 and #4259 carry but did not allocate (#4183 owns it), so it is not double-counted. - #4196 is recorded as claiming no integer at all: it routes a new token-less `StaleReservation` variant through the existing `ErrorStaleReservationToken`. The #3968 half is the serious one and is called out as such. Its 28 is not a new claim — it *moves the already-shipped* `ErrorTransactionBroadcastRejected` off 26 to make room for its own persister code. Rule 3 forbids that: a host compiled against merged ABI returns 26 for a broadcast rejection, and after #3968 the same condition returns 28 while 26 means a transient persister failure. Neither branch's diff shows the contradiction. Blocker (b) — 30 marked both free and assigned was already resolved by the preceding commit; verified consistent here (30 is allocated to #4185 throughout, frontier is 34, and the one remaining "genuinely free" is past tense explaining why #4185 could take it). Also corrected, all verified against the branches: - Survey provenance had #4185 at `0b0d5c76d6` labelled "(post-renumber)". Wrong twice: that commit is the *parent* of the renumber `d854debb`, and the head has since moved to `6c37e8679e`. #4184, #4247 and #4256 SHAs refreshed too. - #4256 has now taken 30 (`9481e5783b`) and dropped its stale "30 is reserved for the consent code" rationale; the equivalent comments on #4183 and #4204 are flagged as still present. - #4184 has a comment-only drift: it reserves "Codes 27-28" but names three codes. Correct when the trio was 27/28/29; it is now 27/28/30. Its discriminant is right and is the resolution of record — only the prose is stale, and #4184 is left untouched. - The #4196 section now records why the restack has not happened: its three own commits conflict in 3 files / 10 hunks against #4185's head, and the registry redesign underneath it (mandatory `registered_height`, new `WalletRemoved` variant, owner-stamped funding token) makes it author work rather than conflict resolution. Its trio numbers come from the #4185 copy it carries, so the restack fixes 28 -> 30 for free; the number #4196 itself must chase is 27, not 30. Verified: cargo fmt --all -- --check clean; cargo test -p platform-wallet-ffi -p platform-wallet = 738 passed / 0 failed. Docs-only change. --- .../ERROR_CODE_REGISTRY.md | 146 ++++++++++++++++-- 1 file changed, 133 insertions(+), 13 deletions(-) diff --git a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md index a5fcde291f2..27259d8b145 100644 --- a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md +++ b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md @@ -90,7 +90,7 @@ this file. | 27 | `ErrorStaleReservationToken` | #4185 | In review (also carried by #4256) | | 28 | `ErrorReservationTokenConsumed` | #4185 | In review (also carried by #4256) | | 29 | `ErrorAssetLockInsufficientFunds` | #4184 | In review — **keeps 29** (collision resolved) | -| 30 | `ErrorReservationWalletMismatch` | #4185 | In review — **moved 29 → 30** (collision resolved; #4256 must inherit) | +| 30 | `ErrorReservationWalletMismatch` | #4185 | In review — **moved 29 → 30** (collision resolved; #4256 has inherited it, #4196 inherits on restack) | | 31 | `ErrorSigningKeyUnavailable` | #4183 | In review (also carried by #4204) | | 32 | `ErrorTransactionBuild` | #4247 | In review (also carried by #4256) | | 33 | `ErrorTransactionSigning` | #4256 | In review | @@ -98,6 +98,40 @@ this file. Open PRs that touch `rs-platform-wallet-ffi` but claim **no** new code: #4186, #4191, #4194, #4195, #4240, #4251, #4258. +Two more carry a code they did not allocate, inherited from the PR they are +stacked on rather than claimed fresh — they must not be read as a second claim +on the number: + +| Code | Name | Carried by | Allocated to | +| ---: | --- | --- | --- | +| 31 | `ErrorSigningKeyUnavailable` | #4204, #4259 | #4183 | + +#4196 also claims no new integer: it adds a token-less +`PlatformWalletError::StaleReservation` variant and deliberately routes it +through the **existing** `ErrorStaleReservationToken`, so it allocates nothing +and only has to follow that code's number (see below). + +### Non-conforming allocations (rebase required) + +These branches allocate into the same range from a stale base. They are listed +here rather than in the proposed table because their numbers cannot stand as +written — each row is a claim to be **withdrawn and reissued**, not an +allocation of record. + +| Code | Name | Owning PR | Conflict | +| ---: | --- | --- | --- | +| 26 | `ErrorPersisterTransient` | #3968 | Contradicts **merged ABI** — 26 is `ErrorTransactionBroadcastRejected` | +| 27 | `ErrorPersisterFatal` | #3968 | Collides with #4185 `ErrorStaleReservationToken` | +| 28 | `ErrorTransactionBroadcastRejected` | #3968 | **Renumbers a shipped code** 26 → 28 — forbidden by rule 3 | +| 27 | `ErrorShutdownIncomplete` | #3954 | Collides with #4185 `ErrorStaleReservationToken` | + +#3968 is the serious one: rule 3 forbids renumbering a code that has shipped, +and `ErrorTransactionBroadcastRejected = 26` is merged ABI. Moving it to 28 +would silently reinterpret every 26 an already-compiled host returns. #3968 must +keep 26 where it is and take fresh integers from the frontier for its two +persister codes; #3954 likewise for its shutdown code. #4185's 27/28 are the +older claim and stand. + ## Contested and pending ### 29 — RESOLVED: #4184 keeps 29; #4185 moved to 30 @@ -119,8 +153,20 @@ Both Swift `switch`es are symbolic — `init(ffi:)` matches cbindgen-generated `PLATFORM_WALLET_FFI_RESULT_CODE_*` constants, so only the enum's raw value carried the number. -**Still outstanding:** #4256 is stacked downstream and its head still carries the -pre-renumber `29`; it must adopt 30 when it rebases. #4196 likewise (see below). +**#4256 has now adopted 30 as well** (`9481e5783b`), through the same mirror set +minus the code-98 comment, which that branch does not carry: the enum +discriminant and its rustdoc cross-reference, the `signed_payment.rs` doc, the +JNI rustdoc, the Swift raw value, and Kotlin's `fromPlatformWalletNative` branch, +class KDoc, `WalletManagerNative` KDoc and `DashSdkErrorTest` offset assertion. +#4256's other codes are untouched: it keeps 32 (shared with #4247) and 33. + +Note that neither #4184 nor #4256 was ever blocked by CI on this. Both are +MERGEABLE with green checks, because two branches assigning the same +discriminant produce no textual conflict — the collision surfaces only as an +E0081 after a textual merge, or silently as a wrong error code on the host. +That is the whole reason this file exists. + +**Still outstanding:** #4196 (see below). ### 30 — allocated to #4185; the old "consent code" reservation was stale @@ -134,30 +180,92 @@ the head of **every one of the 62 open PRs**: no PR anywhere defines a code 30. 30 was therefore genuinely free, and #4185 has taken it. The stale "reserved for the consent code" comments should be dropped by whichever PR touches them next. +#4256 has done so on its own branch (`9481e5783b`): its +`ErrorTransactionSigning` numbering rationale no longer describes 30 as reserved +for the consent code, and now names 30 as `ErrorReservationWalletMismatch`. The +equivalent stale comments on #4183 and #4204 are still there. + +#4184 has a smaller drift of the same kind, left in place because that branch is +settled and the drift is comment-only. Its reservation note reads "Codes 27-28 +are reserved" but then names **three** codes — `ErrorStaleReservationToken` / +`ErrorReservationTokenConsumed` / `ErrorReservationWalletMismatch`. That was +correct when the trio was 27/28/29 and #4184 was avoiding the range; after the +renumber the trio is 27/28/**30**, so the note should read "Codes 27-28 and 30". +The discriminant itself (`ErrorAssetLockInsufficientFunds = 29`) is correct and +is the resolution of record — only the prose is stale. + ### 27 / 28 — #3968 and #3954 collide with #4185's reservation trio -Found by the same 2026-08-01 sweep; these were missing from the tables above. +Found by the same 2026-08-01 sweep. These now have rows — see **Non-conforming +allocations** above for #3968 and #3954, and the inherited-code table for #4259. +The detail behind those rows: - **#3968** (`5931df745a`) numbers `ErrorPersisterTransient = 26`, `ErrorPersisterFatal = 27`, `ErrorTransactionBroadcastRejected = 28`. It branched before `26 = ErrorTransactionBroadcastRejected` merged, so it both contradicts merged ABI at 26 **and** collides with #4185 at 27 and 28. + + The 28 is the more serious half and is easy to miss, because it does not look + like an allocation at all: #3968 is not claiming 28 for something new, it is + *moving a code that has already shipped* out of the way of its own 26. Rule 3 + forbids that outright. A host compiled against merged ABI returns 26 for a + broadcast rejection; after #3968 the same condition returns 28, and 26 means + a transient persister failure. Nothing in either branch's diff shows the + contradiction. #3968 must leave 26 alone and take fresh integers for both + persister codes. - **#3954** (`93d0bd49b7`) numbers `ErrorShutdownIncomplete = 27`, colliding with - #4185's `ErrorStaleReservationToken = 27`. + #4185's `ErrorStaleReservationToken = 27`. Straightforward by comparison — a + proposed-vs-proposed collision, resolvable by renumbering either side. #4185's + claim is older and stands. - **#4259** (`4270d827c2`) carries `ErrorSigningKeyUnavailable = 31` — the same number and name as #4183, i.e. inherited rather than a new allocation, like - #4204. + #4204. No conflict; recorded so the number is not double-counted. Both #3968 and #3954 need a rebase onto current `v4.2-dev` and fresh integers -from the frontier below; #4185's 27/28 are the older claim and should stand. +from the frontier (34+); #4185's 27/28 are the older claim and should stand. ### 26 — `ErrorStaleReservationToken` on #4196 collides with merged ABI #4196 (stacked on #4185) branched before `26 = ErrorTransactionBroadcastRejected` merged, and its head numbers the reservation trio **26 / 27 / 28**. Merging it as -it stands would give 26 two meanings and would contradict #4185's own 27 / 28 / 29 -for the same three names. #4196 needs a rebase onto current `v4.2-dev` and must -adopt whatever numbering #4185 lands with. No new integers are needed for it. +it stands would give 26 two meanings and would contradict #4185's own +27 / 28 / 30 for the same three names. #4196 needs a rebase and must adopt +whatever numbering #4185 lands with. No new integers are needed for it. + +**All three of those numbers come from the copy of #4185 that #4196 carries, not +from #4196's own commits.** Restacking onto #4185's head therefore fixes the +trio for free — including `ErrorReservationWalletMismatch` 28 → 30, which #4196 +never had to move itself. The one number #4196 does own is a doc reference: its +`StaleReservation` variant and the matching Kotlin KDoc both cite +`ErrorStaleReservationToken` as **26**, and that becomes **27** post-restack. +So the number #4196 must chase is 27, not 30. + +**The restack is not mechanical — it is blocked on a redesign.** Rebasing +#4196's three own commits (`2d29451d06`, `c64af1a6eb`, `ea4f783490`) onto +#4185's head `6c37e8679e` conflicts in three files (10 hunks): `error.rs` (3), +`wallet/core/broadcast.rs` (1), `wallet/signed_payment_registry.rs` (6). The +`error.rs` hunks are genuinely mechanical. The other two are not, because #4185 +redesigned the registry underneath #4196 after it branched: + +- `registered_height` changed from `Option` to a mandatory `u32`. #4196's + age guard is built around the `None` case meaning "guard disabled"; that case + no longer exists. +- #4185 added a `SignedPaymentError::WalletRemoved` variant and an + owner-stamped `funding_reservation_token` field. #4196 predates both. +- #4196 wants to *move* `RESERVATION_MAX_AGE_BLOCKS` and `reservation_expired` + into `wallet/reservations.rs` so the V2 handle path can share them. #4185 has + since rewritten both in place, with new generation-binding rationale. +- #4196's V2 guard documents "leave the stale reservation for the TTL rather + than release by outpoint". #4185 now releases by owner-guarded *token*, which + changes that rationale rather than conflicting with it textually. + +Resolving this means re-deriving #4196's age guard against the new registry +shape, with real semantic decisions to make (does the V2 guard now release by +owner token? what replaces the `None`-disables-the-guard branch?). That is +author work, not conflict resolution, and it is why this was left rather than +forced through. shumkov's 07-24 request to restack onto #4185's post-renumber +head is actionable in the sense that the base now exists — but the restack +itself needs #4196's author. ### 31 vs 33 — two signing-related codes, deliberately distinct @@ -181,9 +289,21 @@ the same thing in both enums. Compiled 2026-08-01 against `v4.2-dev` at `ed4116b26c` and the following PR heads: #3954 `93d0bd49b7`, #3968 `5931df745a`, #4183 `2cd948331b`, #4184 -`a9e418af50`, #4185 `0b0d5c76d6` (post-renumber), #4259 `4270d827c2`, #4186 +`bd19a3e020`, #4185 `6c37e8679e` (post-renumber), #4259 `4270d827c2`, #4186 `6f7abbadc1`, #4191 `8acb0bd14c`, #4194 `9efc0b7e3a`, #4195 `4f2eb06d64`, #4196 -`ea4f783490`, #4204 `7bc8a845c6`, #4240 `9328609a16`, #4247 `72c000dcfd`, #4251 -`176f8ed3eb`, #4256 `d8943ccf10`, #4258 `5adfc40032`. Rows describing open PRs +`ea4f783490`, #4204 `7bc8a845c6`, #4240 `9328609a16`, #4247 `0dcdc743e7`, #4251 +`176f8ed3eb`, #4256 `9481e5783b`, #4258 `5adfc40032`. Rows describing open PRs reflect those heads and go stale as the PRs are updated; the merged table does not. + +Four of these were corrected on 2026-08-01 after the heads moved. The +`#4185 0b0d5c76d6 (post-renumber)` this list previously carried was wrong twice +over: `0b0d5c76d6` is the *parent* of the renumber commit `d854debb`, so it was +pre-renumber, and the branch has since advanced to `6c37e8679e`. #4184 was +recorded at `a9e418af50` (now `bd19a3e020`), #4247 at `72c000dcfd` (now +`0dcdc743e7`), and #4256 at `d8943ccf10` (now `9481e5783b`, which carries the +29 → 30 move). + +The 26 / 27 / 28 / 31 claims attributed to #3968, #3954 and #4259 were +re-verified on 2026-08-01 by reading `error.rs` at each of those three heads +directly, not from this file. From e915e5c3fdc07f1fb0ecb7f3e9221f3d5c8de5fc Mon Sep 17 00:00:00 2001 From: bfoss765 <38437574+bfoss765@users.noreply.github.com> Date: Sun, 2 Aug 2026 23:04:42 -0400 Subject: [PATCH 04/11] docs(ffi): record #4268's merged 27 and the trio's move to 34-36 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit dashpay/platform#4268 merged `ErrorShutdownIncomplete = 27` into the v4.2-dev ABI on 2026-08-02, taking the number #4185 had held. #4185 and #4256 moved the deferred-token trio to the contiguous block 34-36 in response. Registry changes: * 27 enters the merged table, owned by #4268. * The proposed table moves the trio to 34/35/36 and marks 28 and 30 free but deliberately not reissued. Next free integer is now 37. * New "Collision history" section records all three numberings of the trio (26/27/28 -> 27/28/30 -> 34/35/36) and, more usefully, corrects this file's own reasoning: on 2026-08-01 it recorded #3954's `ErrorShutdownIncomplete = 27` as a non-conforming claim that had to be withdrawn because #4185's claim was older. Seniority among open PRs does not decide an ABI number — merging does. #3954 was closed, its work landed as #4268, and 27 is now merged ABI. The trio therefore moved above every claimed number rather than into the next free gap, so nothing currently in flight can hit it again. * #3968's 27 is re-characterised: it was a proposed-vs-proposed collision, and is now a contradiction of merged ABI. Its frontier is 37+. * #4196 is now two moves behind at 26/27/28; the doc reference it owns has to chase 34, not 27. * Records a mirror gap found while grepping for this move: #4256 declares `ErrorTransactionBuild` (32) and `ErrorTransactionSigning` (33) in Rust and maps both in Kotlin, but declares neither in Swift, so both reach Swift hosts as `.errorUnknown`. Rule 5's Swift clause; left for that PR's author. * Provenance re-verified against v4.2-dev `5d68612a45`, including the check that 32 and 33 were already taken — which is why the trio went to 34-36 and not 32-34. Co-Authored-By: Claude Opus 4.8 --- .../ERROR_CODE_REGISTRY.md | 210 ++++++++++++------ 1 file changed, 141 insertions(+), 69 deletions(-) diff --git a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md index 27259d8b145..376a3f38421 100644 --- a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md +++ b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md @@ -73,12 +73,16 @@ These are shipped ABI. Do not renumber. | 24 | `ErrorAssetLockAlreadyConsumed` | | | 25 | `ErrorAssetLockFundingMismatch` | | | 26 | `ErrorTransactionBroadcastRejected` | Merged in `9302c62e8b`; took a number several open branches had been treating as free | +| 27 | `ErrorShutdownIncomplete` | Merged 2026-08-02 by **#4268** (`429667e723`). A quiesce/drain barrier missed its budget. **Took the number #4185 had held since before this file existed** — see the collision history below | | 98 | `NotFound` | Sentinel — `Option` returned as an error | | 99 | `ErrorUnknown` | Sentinel — unmapped/flattened errors | -**Next free integer: 34** — 27–33 are all claimed in the proposed table below. -(Before the 29/30 resolution landed this line disagreed with the table, which -still showed 30 as unallocated; 30 is now allocated to #4185 and the two agree.) +**Next free integer: 37** — 27–36 are claimed (27 merged; 29, 31–36 in the +proposed table below). **28 and 30 are free**: #4185 and #4256 vacated them when +the reservation trio moved to 34–36, but they are deliberately left unclaimed +rather than back-filled, so that the trio stays contiguous and no number is +reused within a single review cycle. A new code should take 37 unless it has a +reason to sit next to something. ## Proposed allocations (open PRs) @@ -87,13 +91,15 @@ this file. | Code | Name | Owning PR | Status | | ---: | --- | --- | --- | -| 27 | `ErrorStaleReservationToken` | #4185 | In review (also carried by #4256) | -| 28 | `ErrorReservationTokenConsumed` | #4185 | In review (also carried by #4256) | +| 28 | *(free)* | — | Vacated by #4185/#4256 on 2026-08-02; not reissued | | 29 | `ErrorAssetLockInsufficientFunds` | #4184 | In review — **keeps 29** (collision resolved) | -| 30 | `ErrorReservationWalletMismatch` | #4185 | In review — **moved 29 → 30** (collision resolved; #4256 has inherited it, #4196 inherits on restack) | -| 31 | `ErrorSigningKeyUnavailable` | #4183 | In review (also carried by #4204) | +| 30 | *(free)* | — | Vacated by #4185/#4256 on 2026-08-02; not reissued | +| 31 | `ErrorSigningKeyUnavailable` | #4183 | In review (also carried by #4204, #4259) | | 32 | `ErrorTransactionBuild` | #4247 | In review (also carried by #4256) | | 33 | `ErrorTransactionSigning` | #4256 | In review | +| 34 | `ErrorStaleReservationToken` | #4185 | In review — **moved 27 → 34** (also carried by #4256; #4196 inherits on restack) | +| 35 | `ErrorReservationTokenConsumed` | #4185 | In review — **moved 28 → 35** (also carried by #4256) | +| 36 | `ErrorReservationWalletMismatch` | #4185 | In review — **moved 30 → 36** (also carried by #4256) | Open PRs that touch `rs-platform-wallet-ffi` but claim **no** new code: #4186, #4191, #4194, #4195, #4240, #4251, #4258. @@ -121,44 +127,32 @@ allocation of record. | Code | Name | Owning PR | Conflict | | ---: | --- | --- | --- | | 26 | `ErrorPersisterTransient` | #3968 | Contradicts **merged ABI** — 26 is `ErrorTransactionBroadcastRejected` | -| 27 | `ErrorPersisterFatal` | #3968 | Collides with #4185 `ErrorStaleReservationToken` | +| 27 | `ErrorPersisterFatal` | #3968 | Contradicts **merged ABI** — 27 is #4268's `ErrorShutdownIncomplete` (was a #4185 collision until 2026-08-02) | | 28 | `ErrorTransactionBroadcastRejected` | #3968 | **Renumbers a shipped code** 26 → 28 — forbidden by rule 3 | -| 27 | `ErrorShutdownIncomplete` | #3954 | Collides with #4185 `ErrorStaleReservationToken` | + +#3954's `ErrorShutdownIncomplete = 27` used to sit in this table. It is gone +because that claim **won**: #3954 was closed and superseded by **#4268**, which +merged 27 into `v4.2-dev` on 2026-08-02. See the collision history below. #3968 is the serious one: rule 3 forbids renumbering a code that has shipped, and `ErrorTransactionBroadcastRejected = 26` is merged ABI. Moving it to 28 would silently reinterpret every 26 an already-compiled host returns. #3968 must -keep 26 where it is and take fresh integers from the frontier for its two -persister codes; #3954 likewise for its shutdown code. #4185's 27/28 are the -older claim and stand. +keep 26 where it is and take fresh integers from the frontier (37+) for its two +persister codes. Its 27 is now doubly wrong: 27 is merged ABI +(`ErrorShutdownIncomplete`), so rule 3 protects it too. ## Contested and pending -### 29 — RESOLVED: #4184 keeps 29; #4185 moved to 30 +### 29 — RESOLVED: #4184 keeps 29 (#4185 moved away, twice) Both PR heads defined code 29. Resolution of record: **#4184 keeps `29 = ErrorAssetLockInsufficientFunds`; #4185 moves `ErrorReservationWalletMismatch` to 30.** -**This renumber has now landed on #4185's branch**, propagated through every -site: the Rust enum discriminant and its three rustdoc cross-references -(`rs-platform-wallet-ffi/src/error.rs`), the two `signed_payment.rs` doc -references, the JNI rustdoc (`rs-unified-sdk-jni/src/wallet_manager.rs`), Swift -`PlatformWalletResultCode`'s raw value + doc -(`PlatformWalletResult.swift`), and Kotlin's `fromPlatformWalletNative` branch, -class KDoc, code-98 comment (`DashSdkError.kt`), `WalletManagerNative.kt` KDoc, -and the `DashSdkErrorTest` offset assertion. - -Both Swift `switch`es are symbolic — `init(ffi:)` matches cbindgen-generated -`PLATFORM_WALLET_FFI_RESULT_CODE_*` constants, so only the enum's raw value -carried the number. - -**#4256 has now adopted 30 as well** (`9481e5783b`), through the same mirror set -minus the code-98 comment, which that branch does not carry: the enum -discriminant and its rustdoc cross-reference, the `signed_payment.rs` doc, the -JNI rustdoc, the Swift raw value, and Kotlin's `fromPlatformWalletNative` branch, -class KDoc, `WalletManagerNative` KDoc and `DashSdkErrorTest` offset assertion. -#4256's other codes are untouched: it keeps 32 (shared with #4247) and 33. +**#4184's 29 is settled and has not moved.** #4185's third code moved to 30 to +clear it, and then — with the rest of the trio — to **36** when #4268 merged 27 +(see the collision history above). 30 is free again as a result. Nothing about +this section's resolution changed: 29 is #4184's. Note that neither #4184 nor #4256 was ever blocked by CI on this. Both are MERGEABLE with green checks, because two branches assigning the same @@ -168,7 +162,7 @@ That is the whole reason this file exists. **Still outstanding:** #4196 (see below). -### 30 — allocated to #4185; the old "consent code" reservation was stale +### 30 — free again; the old "consent code" reservation was stale `ErrorAssetLockCrossDomainConsentRequired` is named as the holder of 30 in in-tree comments on #4183, #4204, and #4247/#4256's numbering rationale. It is @@ -177,24 +171,29 @@ contain it after a re-scope. Verified 2026-08-01 by reading `packages/rs-platform-wallet-ffi/src/error.rs` at the head of **every one of the 62 open PRs**: no PR anywhere defines a code 30. -30 was therefore genuinely free, and #4185 has taken it. The stale "reserved for -the consent code" comments should be dropped by whichever PR touches them next. +30 was therefore genuinely free, and #4185 took it — then vacated it again on +2026-08-02 when the trio moved to 34–36. **30 is free once more, and is +deliberately not being reissued** (see the collision history above). The stale +"reserved for the consent code" comments should be dropped by whichever PR +touches them next. -#4256 has done so on its own branch (`9481e5783b`): its -`ErrorTransactionSigning` numbering rationale no longer describes 30 as reserved -for the consent code, and now names 30 as `ErrorReservationWalletMismatch`. The -equivalent stale comments on #4183 and #4204 are still there. +#4256 has done so on its own branch: its `ErrorTransactionSigning` numbering +rationale no longer describes 30 as reserved for the consent code. As of +`8febac177c` that rationale names #4268 as the owner of 27 and records where the +trio went. The equivalent stale comments on #4183 and #4204 are still there. #4184 has a smaller drift of the same kind, left in place because that branch is settled and the drift is comment-only. Its reservation note reads "Codes 27-28 are reserved" but then names **three** codes — `ErrorStaleReservationToken` / `ErrorReservationTokenConsumed` / `ErrorReservationWalletMismatch`. That was -correct when the trio was 27/28/29 and #4184 was avoiding the range; after the -renumber the trio is 27/28/**30**, so the note should read "Codes 27-28 and 30". -The discriminant itself (`ErrorAssetLockInsufficientFunds = 29`) is correct and -is the resolution of record — only the prose is stale. +correct when the trio was 27/28/29 and #4184 was avoiding the range. It is now +doubly stale: the trio is at **34-36**, and 27 belongs to #4268's merged +`ErrorShutdownIncomplete`. The note should simply say that 29 sits below the +trio's 34-36 block. The discriminant itself +(`ErrorAssetLockInsufficientFunds = 29`) is correct and is the resolution of +record — only the prose is stale, and #4184 need not move. -### 27 / 28 — #3968 and #3954 collide with #4185's reservation trio +### 27 / 28 — #3968 still collides; #3954's claim merged as #4268 Found by the same 2026-08-01 sweep. These now have rows — see **Non-conforming allocations** above for #3968 and #3954, and the inherited-code table for #4259. @@ -213,36 +212,41 @@ The detail behind those rows: a transient persister failure. Nothing in either branch's diff shows the contradiction. #3968 must leave 26 alone and take fresh integers for both persister codes. -- **#3954** (`93d0bd49b7`) numbers `ErrorShutdownIncomplete = 27`, colliding with - #4185's `ErrorStaleReservationToken = 27`. Straightforward by comparison — a - proposed-vs-proposed collision, resolvable by renumbering either side. #4185's - claim is older and stands. +- **#3954** (`93d0bd49b7`) numbered `ErrorShutdownIncomplete = 27`. This file + previously called that a proposed-vs-proposed collision and said #4185's older + claim should stand. **That was wrong, and it resolved the other way.** #3954 + was closed; its work landed as **#4268**, which merged 27 into `v4.2-dev` on + 2026-08-02. #4185 and #4256 moved their trio to 34–36 in response. Merging + decides an ABI number; being the older open claim does not. - **#4259** (`4270d827c2`) carries `ErrorSigningKeyUnavailable = 31` — the same number and name as #4183, i.e. inherited rather than a new allocation, like #4204. No conflict; recorded so the number is not double-counted. -Both #3968 and #3954 need a rebase onto current `v4.2-dev` and fresh integers -from the frontier (34+); #4185's 27/28 are the older claim and should stand. +#3968 needs a rebase onto current `v4.2-dev` and fresh integers from the +frontier (**37+**). It must leave 26 alone, and 27 is no longer available to it +either — that is merged ABI now. -### 26 — `ErrorStaleReservationToken` on #4196 collides with merged ABI +### 26 — #4196's trio collides with merged ABI (and is now two moves behind) #4196 (stacked on #4185) branched before `26 = ErrorTransactionBroadcastRejected` -merged, and its head numbers the reservation trio **26 / 27 / 28**. Merging it as -it stands would give 26 two meanings and would contradict #4185's own -27 / 28 / 30 for the same three names. #4196 needs a rebase and must adopt -whatever numbering #4185 lands with. No new integers are needed for it. +merged, and its head still numbers the reservation trio **26 / 27 / 28**. It is +now two moves behind: merging it as it stands would give 26 two meanings, give 27 +two meanings against #4268's merged `ErrorShutdownIncomplete`, and contradict +#4185's own **34 / 35 / 36** for the same three names. #4196 needs a rebase and +must adopt whatever numbering #4185 lands with. No new integers are needed for +it. **All three of those numbers come from the copy of #4185 that #4196 carries, not from #4196's own commits.** Restacking onto #4185's head therefore fixes the -trio for free — including `ErrorReservationWalletMismatch` 28 → 30, which #4196 -never had to move itself. The one number #4196 does own is a doc reference: its -`StaleReservation` variant and the matching Kotlin KDoc both cite -`ErrorStaleReservationToken` as **26**, and that becomes **27** post-restack. -So the number #4196 must chase is 27, not 30. +trio for free — including the two moves #4196 never had to make itself. The one +number #4196 does own is a doc reference: its `StaleReservation` variant and the +matching Kotlin KDoc both cite `ErrorStaleReservationToken` as **26**, and that +becomes **34** post-restack. So the number #4196 must chase is 34. **The restack is not mechanical — it is blocked on a redesign.** Rebasing #4196's three own commits (`2d29451d06`, `c64af1a6eb`, `ea4f783490`) onto -#4185's head `6c37e8679e` conflicts in three files (10 hunks): `error.rs` (3), +#4185's head (`6c37e8679e` when this was measured; now `3dec774929`) conflicts +in three files (10 hunks): `error.rs` (3), `wallet/core/broadcast.rs` (1), `wallet/signed_payment_registry.rs` (6). The `error.rs` hunks are genuinely mechanical. The other two are not, because #4185 redesigned the registry underneath #4196 after it branched: @@ -278,6 +282,65 @@ failures, and malformed signature encodings. Both codes are currently allocated. Maintainers may still choose to collapse them; that decision belongs to #4183 and #4256 jointly and should be recorded here. +## Collision history — the 27 / 28 / 30 → 34 / 35 / 36 move + +Recorded because the reservation trio has now been renumbered three times, and +because the reason it kept moving is the failure mode this file exists to catch. + +| When | Trio numbering | Why it moved | +| --- | --- | --- | +| original (#4185, #4196) | 26 / 27 / 28 | — | +| 2026-07 | 27 / 28 / 30 | `26 = ErrorTransactionBroadcastRejected` merged (`9302c62e8b`); 29 went to #4184 by agreement, so the third code took 30 | +| **2026-08-02** | **34 / 35 / 36** | **#4268 merged `ErrorShutdownIncomplete = 27` into the `v4.2-dev` ABI** | + +The third move is the instructive one. On 2026-08-01 this file recorded +#3954's `ErrorShutdownIncomplete = 27` as a *non-conforming* claim that had to +be withdrawn, on the reasoning that #4185's 27 was the older claim and should +stand. That reasoning was wrong in the only way that matters: seniority among +open PRs does not decide an ABI number — **merging does**. #3954 was closed and +its work landed as #4268, which merged 27 first. An unmerged claim, however old, +has no standing against merged ABI (rule 3, read from the other side). + +So the trio moved again, and this time it moved **above every number claimed by +anything** — merged or proposed — rather than into the next free gap: + +* 27 `ErrorShutdownIncomplete` (merged, #4268) +* 29 `ErrorAssetLockInsufficientFunds` (#4184) +* 31 `ErrorSigningKeyUnavailable` (#4183/#4204/#4259) +* 32 `ErrorTransactionBuild` (#4247/#4256) +* 33 `ErrorTransactionSigning` (#4256) + +Taking 34–36 rather than back-filling the vacated 28 and 30 costs two integers +in a space that is nowhere near exhausted, and buys two things: the trio reads +as one contiguous family, and it cannot be hit again by anything currently in +flight. **28 and 30 stay free.** Do not reissue them in this review cycle — a +reviewer who saw the earlier numbering would otherwise find a familiar number +attached to an unfamiliar meaning. + +The move landed on both branches on 2026-08-02: **#4185** (`3dec774929`) and +**#4256** (`8febac177c`), each across the Rust enum discriminants and every +rustdoc cross-reference, the two `signed_payment.rs` doc references, the JNI +rustdoc (`rs-unified-sdk-jni/src/wallet_manager.rs`), the Swift +`PlatformWalletResultCode` raw values, and Kotlin's `fromPlatformWalletNative` +branches, class KDoc, `WalletManagerNative.kt` KDoc and the `DashSdkErrorTest` +offset assertions. Both `switch`es in Swift are symbolic — `init(ffi:)` matches +cbindgen `PLATFORM_WALLET_FFI_RESULT_CODE_*` constants — so only the enum's raw +values carried a number there. + +Neither branch's CI could have caught the collision, for the reason given at the +top of this file: a duplicate discriminant across two branches produces no +textual conflict. Both were MERGEABLE and green throughout. + +### Known mirror gap on #4256 (not a numbering issue) + +Noted while grepping the mirrors for this move: #4256 declares +`ErrorTransactionBuild = 32` and `ErrorTransactionSigning = 33` in Rust and maps +both in Kotlin, but its Swift `PlatformWalletResultCode` declares **neither** — +no `case`, and no arm in `init(ffi:)`, so both fall into that switch's +`default:` and reach Swift hosts as `.errorUnknown`, losing their identity. That +is rule 5's Swift clause. Left for #4256's author rather than folded into the +renumber; it is a missing mirror, not a wrong number. + ## Sibling FFI crates `rs-sdk-ffi`'s `DashSDKErrorCode` (`packages/rs-sdk-ffi/src/error.rs`) is a @@ -287,14 +350,23 @@ the same thing in both enums. ## Survey provenance -Compiled 2026-08-01 against `v4.2-dev` at `ed4116b26c` and the following PR -heads: #3954 `93d0bd49b7`, #3968 `5931df745a`, #4183 `2cd948331b`, #4184 -`bd19a3e020`, #4185 `6c37e8679e` (post-renumber), #4259 `4270d827c2`, #4186 -`6f7abbadc1`, #4191 `8acb0bd14c`, #4194 `9efc0b7e3a`, #4195 `4f2eb06d64`, #4196 -`ea4f783490`, #4204 `7bc8a845c6`, #4240 `9328609a16`, #4247 `0dcdc743e7`, #4251 -`176f8ed3eb`, #4256 `9481e5783b`, #4258 `5adfc40032`. Rows describing open PRs -reflect those heads and go stale as the PRs are updated; the merged table does -not. +Compiled 2026-08-01 against `v4.2-dev` at `ed4116b26c`, and **re-verified +2026-08-02 against `v4.2-dev` at `5d68612a45`**, which is where +`ErrorShutdownIncomplete = 27` (#4268, `429667e723`) entered the merged table. +The 2026-08-02 pass re-read the added discriminants at the head of every open PR +that touches `error.rs`, `DashSdkError.kt` or `PlatformWalletResult.swift` +(#3968, #4183, #4184, #4185, #4186, #4191, #4194, #4195, #4196, #4204, #4243, +#4247, #4256, #4259) and confirmed the only claims in the 27–36 range are the +ones tabled above — in particular that 32 and 33 were **already taken** by +#4247/#4256, which is why the trio went to 34–36 rather than 32–34. + +PR heads of record: #3954 `93d0bd49b7` (closed), #3968 `5931df745a`, #4183 +`2cd948331b`, #4184 `bd19a3e020`, **#4185 `3dec774929`** (post-34/35/36 move), +#4186 `6f7abbadc1`, #4191 `8acb0bd14c`, #4194 `9efc0b7e3a`, #4195 `4f2eb06d64`, +#4196 `ea4f783490`, #4204 `7bc8a845c6`, #4240 `9328609a16`, #4247 `0dcdc743e7`, +#4251 `176f8ed3eb`, **#4256 `8febac177c`** (post-34/35/36 move), #4258 +`5adfc40032`, #4259 `4270d827c2`. Rows describing open PRs reflect those heads +and go stale as the PRs are updated; the merged table does not. Four of these were corrected on 2026-08-01 after the heads moved. The `#4185 0b0d5c76d6 (post-renumber)` this list previously carried was wrong twice From c6fb09ac62428813ef0553aa39cd06e52d533ab0 Mon Sep 17 00:00:00 2001 From: bfoss765 <38437574+bfoss765@users.noreply.github.com> Date: Mon, 3 Aug 2026 09:23:30 -0400 Subject: [PATCH 05/11] =?UTF-8?q?docs(ffi):=20registry=20=E2=80=94=20vacat?= =?UTF-8?q?ed=20codes=2028/30=20are=20reserved,=20not=20free;=20one=20allo?= =?UTF-8?q?cation=20frontier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md index 376a3f38421..0117af83bfa 100644 --- a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md +++ b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md @@ -78,7 +78,7 @@ These are shipped ABI. Do not renumber. | 99 | `ErrorUnknown` | Sentinel — unmapped/flattened errors | **Next free integer: 37** — 27–36 are claimed (27 merged; 29, 31–36 in the -proposed table below). **28 and 30 are free**: #4185 and #4256 vacated them when +proposed table below). **28 and 30 are RESERVED (do not reissue)**: #4185 and #4256 vacated them when the reservation trio moved to 34–36, but they are deliberately left unclaimed rather than back-filled, so that the trio stays contiguous and no number is reused within a single review cycle. A new code should take 37 unless it has a @@ -91,9 +91,9 @@ this file. | Code | Name | Owning PR | Status | | ---: | --- | --- | --- | -| 28 | *(free)* | — | Vacated by #4185/#4256 on 2026-08-02; not reissued | +| 28 | *(reserved — vacated)* | — | Vacated by #4185/#4256 on 2026-08-02; RESERVED, not reissuable — the next-free frontier is the only allocation source | | 29 | `ErrorAssetLockInsufficientFunds` | #4184 | In review — **keeps 29** (collision resolved) | -| 30 | *(free)* | — | Vacated by #4185/#4256 on 2026-08-02; not reissued | +| 30 | *(reserved — vacated)* | — | Vacated by #4185/#4256 on 2026-08-02; RESERVED, not reissuable — the next-free frontier is the only allocation source | | 31 | `ErrorSigningKeyUnavailable` | #4183 | In review (also carried by #4204, #4259) | | 32 | `ErrorTransactionBuild` | #4247 | In review (also carried by #4256) | | 33 | `ErrorTransactionSigning` | #4256 | In review | @@ -151,7 +151,7 @@ to 30.** **#4184's 29 is settled and has not moved.** #4185's third code moved to 30 to clear it, and then — with the rest of the trio — to **36** when #4268 merged 27 -(see the collision history above). 30 is free again as a result. Nothing about +(see the collision history above). 30 is vacated (reserved, not reissuable) as a result. Nothing about this section's resolution changed: 29 is #4184's. Note that neither #4184 nor #4256 was ever blocked by CI on this. Both are @@ -162,7 +162,7 @@ That is the whole reason this file exists. **Still outstanding:** #4196 (see below). -### 30 — free again; the old "consent code" reservation was stale +### 30 — vacated (reserved, not reissuable); the old "consent code" reservation was stale `ErrorAssetLockCrossDomainConsentRequired` is named as the holder of 30 in in-tree comments on #4183, #4204, and #4247/#4256's numbering rationale. It is From 2d2c6c84e6308af2829c3d136da900fb78f6c1e8 Mon Sep 17 00:00:00 2001 From: bfoss765 <38437574+bfoss765@users.noreply.github.com> Date: Mon, 3 Aug 2026 14:25:15 -0400 Subject: [PATCH 06/11] docs(ffi): register 37 = ErrorShieldedInviteAlreadyClaimed; record the 32 collision MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit #4204 allocated 32 without a row here, colliding with #4247's ErrorTransactionBuild. Caught for real during the v41int13 integration as an E0081, not on paper — the first collision this file has actually stopped. #4204 moves to 37; frontier advances to 38. Also records that the code was unmirrored on both hosts, which had Kotlin misclassifying it as ReservationWalletMismatch wherever #4185's 32 was present. Co-Authored-By: Claude Opus 4.8 --- .../ERROR_CODE_REGISTRY.md | 39 ++++++++++++++++++- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md index 0117af83bfa..6948879f696 100644 --- a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md +++ b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md @@ -77,11 +77,11 @@ These are shipped ABI. Do not renumber. | 98 | `NotFound` | Sentinel — `Option` returned as an error | | 99 | `ErrorUnknown` | Sentinel — unmapped/flattened errors | -**Next free integer: 37** — 27–36 are claimed (27 merged; 29, 31–36 in the +**Next free integer: 38** — 27–37 are claimed (27 merged; 29, 31–37 in the proposed table below). **28 and 30 are RESERVED (do not reissue)**: #4185 and #4256 vacated them when the reservation trio moved to 34–36, but they are deliberately left unclaimed rather than back-filled, so that the trio stays contiguous and no number is -reused within a single review cycle. A new code should take 37 unless it has a +reused within a single review cycle. A new code should take 38 unless it has a reason to sit next to something. ## Proposed allocations (open PRs) @@ -100,6 +100,7 @@ this file. | 34 | `ErrorStaleReservationToken` | #4185 | In review — **moved 27 → 34** (also carried by #4256; #4196 inherits on restack) | | 35 | `ErrorReservationTokenConsumed` | #4185 | In review — **moved 28 → 35** (also carried by #4256) | | 36 | `ErrorReservationWalletMismatch` | #4185 | In review — **moved 30 → 36** (also carried by #4256) | +| 37 | `ErrorShieldedInviteAlreadyClaimed` | #4204 | In review — **moved 32 → 37** (collided with #4247's `ErrorTransactionBuild`; see below) | Open PRs that touch `rs-platform-wallet-ffi` but claim **no** new code: #4186, #4191, #4194, #4195, #4240, #4251, #4258. @@ -143,6 +144,40 @@ persister codes. Its 27 is now doubly wrong: 27 is merged ABI ## Contested and pending +### 32 — RESOLVED: #4204 moved to 37 (first collision this file actually caught) + +Found 2026-08-03 while assembling the `v41int13` QA integration. #4204's head +commit `b6992a5dbc` — a review round, not the original feature work — added +`ErrorShieldedInviteAlreadyClaimed = 32` with **no row in this file**, in +direct violation of rule 2. 32 is allocated to `ErrorTransactionBuild` (#4247, +also carried by #4256). + +Unlike every other entry in this section, this one was not a paper conflict: +merging #4204 into an integration that already carried +`ErrorReservationWalletMismatch = 32` produced a hard +`error[E0081]: discriminant value 32 assigned more than once`. Resolution of +record: **#4204 moves 32 → 37**, the frontier. `ErrorTransactionBuild` keeps 32. + +The numbering was the lesser half of the defect. The code was **unmirrored on +both hosts** — absent from Swift's `PlatformWalletResultCode` and from Kotlin's +`fromPlatformWalletNative`. Per rule 5 that means Swift rendered it +`.errorUnknown` (identity lost), while Kotlin fell through to `Generic(32, …)` +— and in any tree carrying #4185's `ErrorReservationWalletMismatch = 32`, +Kotlin actively **misclassified** "shielded invite already claimed" as +"reservation wallet mismatch". That is the exact silently-wrong-error-on-every-host +failure this file's preamble describes, and it landed on the shielded-invite +claim-recovery path (the error is raised from four sites in +`wallet/shielded/operations.rs`, three of them inside the recovery function). + +Fixed on #4204 together with the renumber: typed +`PlatformWallet.ShieldedInviteAlreadyClaimed` (terminal, `isRetryable = false`), +the Swift case and its `init(ffi:)` arm, and a `DashSdkErrorTest` assertion that +pins 37 so a future move off the frontier fails the suite instead of the hosts. + +**Lesson for rule 2:** the violation entered on a *review-round* commit, well +after the PR's numbering had been reviewed and recorded as settled. Re-check +discriminants on every push that touches `error.rs`, not only at first review. + ### 29 — RESOLVED: #4184 keeps 29 (#4185 moved away, twice) Both PR heads defined code 29. Resolution of record: **#4184 keeps From 9f3dab1753f700ac489c717e2dbb0a97ec34c610 Mon Sep 17 00:00:00 2001 From: bfoss765 <38437574+bfoss765@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:33:17 -0400 Subject: [PATCH 07/11] docs(ffi): one allocation frontier at 38; complete the open-PR inventory MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Resolves the two blocking review findings and the live suggestions on #4261. Blocking — advance #3968's reissue frontier to 38. Code 37 is allocated to #4204, so the branch-specific guidance in the non-conforming section and in the 27/28 detail section could still have sent #3968 to 37 and recreated the collision with ErrorShieldedInviteAlreadyClaimed. Both references now say 38+, and both state that the reserved 28 and 30 are not available either. Blocking — 28 and 30 were labelled RESERVED in the table and the frontier paragraph but "free" in the code-30 section and the collision history. Rule 1 lets a contributor claim a gap this file marks free, so the two labels gave two different allocation frontiers. Everything now says reserved-not-free and cites rule 1 explicitly. Open-PR inventory: verified 2026-08-03 against each PR's file list and the error.rs at its head. Adds #3417, #3549, #3992 and #4243 (all touch the crate without claiming an integer; #4243 maps new wallet errors onto the existing ErrorInvalidParameter). Removes #4240 and #4251, whose heads touch no file under this crate; removes #4258, merged on 2026-08-03; records #4264 as closed with its work carried by #4243. Also in this pass: - #4204's Swift mirror is recorded as PARTIALLY fixed. At d78b940a03 the raw case and its init(ffi:) arm exist, but PlatformWalletError has no typed case and its exhaustive init(result:) has no arm, so the Swift package does not compile as written. Rule 5's Swift clause is not yet satisfied there. - The preamble no longer claims duplicate discriminants are always silent. Two different names on one integer is an E0081 after a merge (that is how the code-32 collision surfaced); the silent case is a meaning moving to a new integer, or an un-updated host mirror. - Code 13 ErrorArithmeticOverflow does have an in-tree producer (shielded_send.rs); the row said it had none. - #3968 is no longer described as simply un-rebased: its head does contain the 2026-08-01 base, it is behind the current base, and a rebase alone fixes nothing because git sees no conflict — the branch must edit its own enum. - Provenance re-read on 2026-08-03 from GitHub rather than carried forward, now as a table with a note per PR. #4183 and #4184 were rebased onto 5d68612a45 today and keep 31 and 29 respectively; their stale in-enum reservation comments were corrected in the same rebase, so the code-30 section now lists #4204 as the only branch still carrying one. - markdownlint: MD018 (18 occurrences) and MD004 (mixed bullet styles) are both clean; MD013 is down to long table rows only. --- .../ERROR_CODE_REGISTRY.md | 292 +++++++++++------- 1 file changed, 185 insertions(+), 107 deletions(-) diff --git a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md index 6948879f696..b30490b8232 100644 --- a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md +++ b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md @@ -12,10 +12,26 @@ mirrors it as a `RawRepresentable` enum, Kotlin one numbering keeps using that numbering. This file exists because several feature branches allocate into the same -integer range in parallel. A duplicate discriminant in two branches does **not** -produce a textual merge conflict — the second merge silently misclassifies -errors on every host — so allocations have to be reconciled here, in one place, -rather than in each branch's diff. +integer range in parallel, and a duplicate integer across two branches never +produces a textual merge conflict. What happens after the merge depends on the +shape of the duplication, and only one of the two shapes is caught by a +compiler: + +* **Two different variant names on the same integer.** The merged Rust enum has + two variants with one discriminant, so `rustc` refuses it with + `error[E0081]: discriminant value N assigned more than once`. Loud, but only + *after* someone actually merges both branches into one tree — neither + branch's own CI can see it, because neither branch contains both variants. + This is how the code-32 collision below was finally caught. +* **The same meaning moving to a different integer**, or a host mirror left + un-updated. Nothing fails to compile. A shipped host binary keeps the + numbering it was built against, so it silently reads the new integer as + whatever the old one meant — or, for an unmirrored code, loses the identity + entirely (`.errorUnknown` in Swift). This is the failure this file mainly + exists to prevent, and nothing in either branch's diff shows it. + +So allocations have to be reconciled here, in one place, rather than in each +branch's diff. ## Rules @@ -59,7 +75,7 @@ These are shipped ABI. Do not renumber. | 10 | `ErrorInvalidIdentifier` | | | 11 | `ErrorMemoryAllocation` | | | 12 | `ErrorUtf8Conversion` | | -| 13 | `ErrorArithmeticOverflow` | Reserved slot — declared, no in-tree producer; holds the number for the mapping arriving via #3549 | +| 13 | `ErrorArithmeticOverflow` | Produced in-tree by `shielded_send.rs` (the shielded-send amount/fee overflow guard). The variant's own rustdoc still calls it a reserved slot with no producer and credits #3549 with the eventual mapping — that comment is stale and should be corrected by whichever PR touches it next | | 14 | `ErrorNoSelectableInputs` | | | 15 | `ErrorWalletAlreadyExists` | | | 16 | `ErrorShieldedBroadcastFailed` | | @@ -77,12 +93,14 @@ These are shipped ABI. Do not renumber. | 98 | `NotFound` | Sentinel — `Option` returned as an error | | 99 | `ErrorUnknown` | Sentinel — unmapped/flattened errors | -**Next free integer: 38** — 27–37 are claimed (27 merged; 29, 31–37 in the -proposed table below). **28 and 30 are RESERVED (do not reissue)**: #4185 and #4256 vacated them when -the reservation trio moved to 34–36, but they are deliberately left unclaimed -rather than back-filled, so that the trio stays contiguous and no number is -reused within a single review cycle. A new code should take 38 unless it has a -reason to sit next to something. +**Next allocatable integer: 38** — 27–37 are all claimed (27 merged; 29 and +31–37 in the proposed table below; 28 and 30 reserved). **28 and 30 are +RESERVED, not free**: #4185 and #4256 vacated them when the reservation trio +moved to 34–36, but they are deliberately left unclaimed rather than +back-filled, so that the trio stays contiguous and no number is reused within a +single review cycle. Rule 1's "do not reuse a gap unless this file marks it +free" applies — this file does **not** mark 28 or 30 free, so the frontier is +the only allocation source and a new code takes 38. ## Proposed allocations (open PRs) @@ -102,10 +120,26 @@ this file. | 36 | `ErrorReservationWalletMismatch` | #4185 | In review — **moved 30 → 36** (also carried by #4256) | | 37 | `ErrorShieldedInviteAlreadyClaimed` | #4204 | In review — **moved 32 → 37** (collided with #4247's `ErrorTransactionBuild`; see below) | -Open PRs that touch `rs-platform-wallet-ffi` but claim **no** new code: #4186, -#4191, #4194, #4195, #4240, #4251, #4258. +Open PRs that touch `rs-platform-wallet-ffi` but claim **no** new code, verified +2026-08-03 against each PR's file list and the `error.rs` at its head: +`#3417`, `#3549`, `#3992`, `#4186`, `#4191`, `#4194`, `#4195`, `#4243`. -Two more carry a code they did not allocate, inherited from the PR they are +Four entries this list used to carry have been removed, each for a different +reason, so they are not silently re-added: + +| Was listed | Why it is gone | +| --- | --- | +| `#4240` | Its head touches no file under `rs-platform-wallet-ffi` at all | +| `#4251` | Same — no file under this crate | +| `#4258` | Merged into `v4.2-dev` on 2026-08-03 (`ce8233edb7`); it claimed no code, so the merged table is unchanged | +| `#4264` | Closed. Its `error.rs` change (mapping new wallet errors onto the existing `ErrorInvalidParameter`) is carried by `#4243`, which is still open and is listed above | + +`#4243` is worth naming explicitly: it *does* modify `error.rs`, but only to map +new wallet errors onto the **existing** `ErrorInvalidParameter`. Touching +`error.rs` is not the same as claiming an integer, and this list tracks the +latter. + +Two more PRs carry a code they did not allocate, inherited from the PR they are stacked on rather than claimed fresh — they must not be read as a second claim on the number: @@ -113,34 +147,42 @@ on the number: | ---: | --- | --- | --- | | 31 | `ErrorSigningKeyUnavailable` | #4204, #4259 | #4183 | -#4196 also claims no new integer: it adds a token-less +PR `#4196` also claims no new integer: it adds a token-less `PlatformWalletError::StaleReservation` variant and deliberately routes it through the **existing** `ErrorStaleReservationToken`, so it allocates nothing and only has to follow that code's number (see below). -### Non-conforming allocations (rebase required) +### Non-conforming allocations (withdraw and reissue) These branches allocate into the same range from a stale base. They are listed here rather than in the proposed table because their numbers cannot stand as written — each row is a claim to be **withdrawn and reissued**, not an allocation of record. +A rebase is a precondition, not the remedy. `#3968`'s head does contain the +2026-08-01 base `ed4116b26c` (merge commit `debf67bdae` brought it in), so it is +not simply an un-rebased branch; it is behind the *current* base `5d68612a45`, +which is where #4268's merged `ErrorShutdownIncomplete = 27` lives. Rebasing +picks that up, but rebasing alone will not resolve anything below, because git +sees no conflict in any of it — the branch has to **edit its own enum**. + | Code | Name | Owning PR | Conflict | | ---: | --- | --- | --- | | 26 | `ErrorPersisterTransient` | #3968 | Contradicts **merged ABI** — 26 is `ErrorTransactionBroadcastRejected` | | 27 | `ErrorPersisterFatal` | #3968 | Contradicts **merged ABI** — 27 is #4268's `ErrorShutdownIncomplete` (was a #4185 collision until 2026-08-02) | | 28 | `ErrorTransactionBroadcastRejected` | #3968 | **Renumbers a shipped code** 26 → 28 — forbidden by rule 3 | -#3954's `ErrorShutdownIncomplete = 27` used to sit in this table. It is gone -because that claim **won**: #3954 was closed and superseded by **#4268**, which -merged 27 into `v4.2-dev` on 2026-08-02. See the collision history below. +PR `#3954`'s `ErrorShutdownIncomplete = 27` used to sit in this table. It is +gone because that claim **won**: #3954 was closed and superseded by **#4268**, +which merged 27 into `v4.2-dev` on 2026-08-02. See the collision history below. -#3968 is the serious one: rule 3 forbids renumbering a code that has shipped, -and `ErrorTransactionBroadcastRejected = 26` is merged ABI. Moving it to 28 -would silently reinterpret every 26 an already-compiled host returns. #3968 must -keep 26 where it is and take fresh integers from the frontier (37+) for its two -persister codes. Its 27 is now doubly wrong: 27 is merged ABI -(`ErrorShutdownIncomplete`), so rule 3 protects it too. +PR `#3968` is the serious one: rule 3 forbids renumbering a code that has +shipped, and `ErrorTransactionBroadcastRejected = 26` is merged ABI. Moving it +to 28 would silently reinterpret every 26 an already-compiled host returns. +PR #3968 must keep 26 where it is and take fresh integers **from the frontier +(38+)** for its two persister codes. Its 27 is now doubly wrong: 27 is merged +ABI (`ErrorShutdownIncomplete`), so rule 3 protects it too. Note that 28 is +reserved, not free — it is not available to #3968 either. ## Contested and pending @@ -169,10 +211,21 @@ failure this file's preamble describes, and it landed on the shielded-invite claim-recovery path (the error is raised from four sites in `wallet/shielded/operations.rs`, three of them inside the recovery function). -Fixed on #4204 together with the renumber: typed +**Partially** fixed on #4204 together with the renumber. Landed at head +`d78b940a03`: the typed Kotlin `PlatformWallet.ShieldedInviteAlreadyClaimed` (terminal, `isRetryable = false`), -the Swift case and its `init(ffi:)` arm, and a `DashSdkErrorTest` assertion that -pins 37 so a future move off the frontier fails the suite instead of the hosts. +the Swift `PlatformWalletResultCode.errorShieldedInviteAlreadyClaimed = 37` raw +case with its `init(ffi:)` arm, and a `DashSdkErrorTest` assertion that pins 37 +so a future move off the frontier fails the suite instead of the hosts. + +**Still missing on #4204, and it does not compile without it:** Swift's +`PlatformWalletError` has no `.shieldedInviteAlreadyClaimed` case, and its +`init(result:)` switches exhaustively over `PlatformWalletResultCode` with no +`default:`. Adding the raw case without the matching `init(result:)` arm makes +that switch non-exhaustive, so the Swift package fails to build at +`d78b940a03`. Rule 5's Swift clause is therefore not yet satisfied — treat the +Swift mirror as incomplete until #4204 adds the typed error case and its +conversion arm. **Lesson for rule 2:** the violation entered on a *review-round* commit, well after the PR's numbering had been reviewed and recorded as settled. Re-check @@ -186,8 +239,8 @@ to 30.** **#4184's 29 is settled and has not moved.** #4185's third code moved to 30 to clear it, and then — with the rest of the trio — to **36** when #4268 merged 27 -(see the collision history above). 30 is vacated (reserved, not reissuable) as a result. Nothing about -this section's resolution changed: 29 is #4184's. +(see the collision history above), which is why 30 is reserved rather than +free. Nothing about this section's resolution changed: 29 is #4184's. Note that neither #4184 nor #4256 was ever blocked by CI on this. Both are MERGEABLE with green checks, because two branches assigning the same @@ -197,36 +250,36 @@ That is the whole reason this file exists. **Still outstanding:** #4196 (see below). -### 30 — vacated (reserved, not reissuable); the old "consent code" reservation was stale +### 30 — vacated, then RESERVED (not free) `ErrorAssetLockCrossDomainConsentRequired` is named as the holder of 30 in -in-tree comments on #4183, #4204, and #4247/#4256's numbering rationale. It is +in-tree comments on #4204 and on #4247/#4256's numbering rationale. It is **not defined anywhere** — #4184, the PR that would have introduced it, does not contain it after a re-scope. Verified 2026-08-01 by reading `packages/rs-platform-wallet-ffi/src/error.rs` at the head of **every one of the 62 open PRs**: no PR anywhere defines a code 30. -30 was therefore genuinely free, and #4185 took it — then vacated it again on -2026-08-02 when the trio moved to 34–36. **30 is free once more, and is -deliberately not being reissued** (see the collision history above). The stale -"reserved for the consent code" comments should be dropped by whichever PR -touches them next. - -#4256 has done so on its own branch: its `ErrorTransactionSigning` numbering -rationale no longer describes 30 as reserved for the consent code. As of -`8febac177c` that rationale names #4268 as the owner of 27 and records where the -trio went. The equivalent stale comments on #4183 and #4204 are still there. - -#4184 has a smaller drift of the same kind, left in place because that branch is -settled and the drift is comment-only. Its reservation note reads "Codes 27-28 -are reserved" but then names **three** codes — `ErrorStaleReservationToken` / -`ErrorReservationTokenConsumed` / `ErrorReservationWalletMismatch`. That was -correct when the trio was 27/28/29 and #4184 was avoiding the range. It is now -doubly stale: the trio is at **34-36**, and 27 belongs to #4268's merged -`ErrorShutdownIncomplete`. The note should simply say that 29 sits below the -trio's 34-36 block. The discriminant itself -(`ErrorAssetLockInsufficientFunds = 29`) is correct and is the resolution of -record — only the prose is stale, and #4184 need not move. +30 was therefore genuinely free at that moment, and #4185 took it — then vacated +it again on 2026-08-02 when the trio moved to 34–36. Vacating is not the same as +freeing: **30 is now RESERVED and must not be reissued** (see the collision +history below for why). The stale "reserved for the consent code" comments +should be dropped by whichever PR touches them next. + +Three branches have now done so: + +* **#4256** — as of `8febac177c` its `ErrorTransactionSigning` rationale names + #4268 as the owner of 27 and records where the trio went. +* **#4183** — its enum comment no longer describes 27–28 as reserved for the + trio; on the 2026-08-03 rebase it was rewritten to say 28 and 30 are reserved + and 29 belongs to #4184, and to point here. +* **#4184** — same rebase, same correction. Its note used to read "Codes 27-28 + are reserved" while naming **three** codes, which was correct only while the + trio sat at 27/28/29. It now says 28 is skipped, 28 and 30 are reserved, and + the trio is at 34–36. The discriminant itself + (`ErrorAssetLockInsufficientFunds = 29`) never moved and remains the + resolution of record. + +The equivalent stale comment on **#4204** is still there. ### 27 / 28 — #3968 still collides; #3954's claim merged as #4268 @@ -234,7 +287,7 @@ Found by the same 2026-08-01 sweep. These now have rows — see **Non-conforming allocations** above for #3968 and #3954, and the inherited-code table for #4259. The detail behind those rows: -- **#3968** (`5931df745a`) numbers `ErrorPersisterTransient = 26`, +* **#3968** (`5931df745a`) numbers `ErrorPersisterTransient = 26`, `ErrorPersisterFatal = 27`, `ErrorTransactionBroadcastRejected = 28`. It branched before `26 = ErrorTransactionBroadcastRejected` merged, so it both contradicts merged ABI at 26 **and** collides with #4185 at 27 and 28. @@ -247,29 +300,29 @@ The detail behind those rows: a transient persister failure. Nothing in either branch's diff shows the contradiction. #3968 must leave 26 alone and take fresh integers for both persister codes. -- **#3954** (`93d0bd49b7`) numbered `ErrorShutdownIncomplete = 27`. This file +* **#3954** (`93d0bd49b7`) numbered `ErrorShutdownIncomplete = 27`. This file previously called that a proposed-vs-proposed collision and said #4185's older claim should stand. **That was wrong, and it resolved the other way.** #3954 was closed; its work landed as **#4268**, which merged 27 into `v4.2-dev` on 2026-08-02. #4185 and #4256 moved their trio to 34–36 in response. Merging decides an ABI number; being the older open claim does not. -- **#4259** (`4270d827c2`) carries `ErrorSigningKeyUnavailable = 31` — the same +* **#4259** (`9336bdbb71`) carries `ErrorSigningKeyUnavailable = 31` — the same number and name as #4183, i.e. inherited rather than a new allocation, like #4204. No conflict; recorded so the number is not double-counted. -#3968 needs a rebase onto current `v4.2-dev` and fresh integers from the -frontier (**37+**). It must leave 26 alone, and 27 is no longer available to it -either — that is merged ABI now. +PR `#3968` needs a rebase onto current `v4.2-dev` **and** fresh integers from +the frontier (**38+**). It must leave 26 alone; 27 is no longer available to it +either (merged ABI now), and neither are the reserved 28 and 30. ### 26 — #4196's trio collides with merged ABI (and is now two moves behind) -#4196 (stacked on #4185) branched before `26 = ErrorTransactionBroadcastRejected` +PR #4196 (stacked on #4185) branched before `26 = ErrorTransactionBroadcastRejected` merged, and its head still numbers the reservation trio **26 / 27 / 28**. It is now two moves behind: merging it as it stands would give 26 two meanings, give 27 two meanings against #4268's merged `ErrorShutdownIncomplete`, and contradict -#4185's own **34 / 35 / 36** for the same three names. #4196 needs a rebase and -must adopt whatever numbering #4185 lands with. No new integers are needed for -it. +the #4185 numbering of **34 / 35 / 36** for the same three names. It needs a +rebase and must adopt whatever numbering #4185 lands with. No new integers are +needed for it. **All three of those numbers come from the copy of #4185 that #4196 carries, not from #4196's own commits.** Restacking onto #4185's head therefore fixes the @@ -279,22 +332,22 @@ matching Kotlin KDoc both cite `ErrorStaleReservationToken` as **26**, and that becomes **34** post-restack. So the number #4196 must chase is 34. **The restack is not mechanical — it is blocked on a redesign.** Rebasing -#4196's three own commits (`2d29451d06`, `c64af1a6eb`, `ea4f783490`) onto -#4185's head (`6c37e8679e` when this was measured; now `3dec774929`) conflicts +the three commits #4196 owns (`2d29451d06`, `c64af1a6eb`, `ea4f783490`) onto +the #4185 head (`6c37e8679e` when this was measured; now `8813e98533`) conflicts in three files (10 hunks): `error.rs` (3), `wallet/core/broadcast.rs` (1), `wallet/signed_payment_registry.rs` (6). The `error.rs` hunks are genuinely mechanical. The other two are not, because #4185 redesigned the registry underneath #4196 after it branched: -- `registered_height` changed from `Option` to a mandatory `u32`. #4196's +* `registered_height` changed from `Option` to a mandatory `u32`. #4196's age guard is built around the `None` case meaning "guard disabled"; that case no longer exists. -- #4185 added a `SignedPaymentError::WalletRemoved` variant and an +* #4185 added a `SignedPaymentError::WalletRemoved` variant and an owner-stamped `funding_reservation_token` field. #4196 predates both. -- #4196 wants to *move* `RESERVATION_MAX_AGE_BLOCKS` and `reservation_expired` +* #4196 wants to *move* `RESERVATION_MAX_AGE_BLOCKS` and `reservation_expired` into `wallet/reservations.rs` so the V2 handle path can share them. #4185 has since rewritten both in place, with new generation-binding rationale. -- #4196's V2 guard documents "leave the stale reservation for the TTL rather +* #4196's V2 guard documents "leave the stale reservation for the TTL rather than release by outpoint". #4185 now releases by owner-guarded *token*, which changes that rationale rather than conflicting with it textually. @@ -315,7 +368,7 @@ key, restored from a typed signer completion code — whereas #4256's `BuilderError::SigningFailed` also covers unresolved derivation paths, sighash failures, and malformed signature encodings. Both codes are currently allocated. Maintainers may still choose to collapse them; that decision belongs to #4183 and -#4256 jointly and should be recorded here. +to #4256 jointly and should be recorded here. ## Collision history — the 27 / 28 / 30 → 34 / 35 / 36 move @@ -329,9 +382,9 @@ because the reason it kept moving is the failure mode this file exists to catch. | **2026-08-02** | **34 / 35 / 36** | **#4268 merged `ErrorShutdownIncomplete = 27` into the `v4.2-dev` ABI** | The third move is the instructive one. On 2026-08-01 this file recorded -#3954's `ErrorShutdownIncomplete = 27` as a *non-conforming* claim that had to -be withdrawn, on the reasoning that #4185's 27 was the older claim and should -stand. That reasoning was wrong in the only way that matters: seniority among +the `ErrorShutdownIncomplete = 27` of #3954 as a *non-conforming* claim that +had to be withdrawn, on the reasoning that #4185's 27 was the older claim and +should stand. That reasoning was wrong in the only way that matters: seniority among open PRs does not decide an ABI number — **merging does**. #3954 was closed and its work landed as #4268, which merged 27 first. An unmerged claim, however old, has no standing against merged ABI (rule 3, read from the other side). @@ -348,9 +401,10 @@ anything** — merged or proposed — rather than into the next free gap: Taking 34–36 rather than back-filling the vacated 28 and 30 costs two integers in a space that is nowhere near exhausted, and buys two things: the trio reads as one contiguous family, and it cannot be hit again by anything currently in -flight. **28 and 30 stay free.** Do not reissue them in this review cycle — a -reviewer who saw the earlier numbering would otherwise find a familiar number -attached to an unfamiliar meaning. +flight. **28 and 30 are therefore RESERVED, not free.** Do not reissue them in +this review cycle — a reviewer who saw the earlier numbering would otherwise +find a familiar number attached to an unfamiliar meaning. Rule 1 only permits +reusing a gap this file marks free, and this file marks neither of them free. The move landed on both branches on 2026-08-02: **#4185** (`3dec774929`) and **#4256** (`8febac177c`), each across the Rust enum discriminants and every @@ -363,8 +417,9 @@ cbindgen `PLATFORM_WALLET_FFI_RESULT_CODE_*` constants — so only the enum's ra values carried a number there. Neither branch's CI could have caught the collision, for the reason given at the -top of this file: a duplicate discriminant across two branches produces no -textual conflict. Both were MERGEABLE and green throughout. +top of this file: a duplicate integer across two branches produces no textual +conflict, and neither branch's tree contains both variants, so neither +compiler ever sees the E0081. Both were MERGEABLE and green throughout. ### Known mirror gap on #4256 (not a numbering issue) @@ -385,32 +440,55 @@ the same thing in both enums. ## Survey provenance -Compiled 2026-08-01 against `v4.2-dev` at `ed4116b26c`, and **re-verified -2026-08-02 against `v4.2-dev` at `5d68612a45`**, which is where -`ErrorShutdownIncomplete = 27` (#4268, `429667e723`) entered the merged table. -The 2026-08-02 pass re-read the added discriminants at the head of every open PR -that touches `error.rs`, `DashSdkError.kt` or `PlatformWalletResult.swift` -(#3968, #4183, #4184, #4185, #4186, #4191, #4194, #4195, #4196, #4204, #4243, -#4247, #4256, #4259) and confirmed the only claims in the 27–36 range are the -ones tabled above — in particular that 32 and 33 were **already taken** by -#4247/#4256, which is why the trio went to 34–36 rather than 32–34. - -PR heads of record: #3954 `93d0bd49b7` (closed), #3968 `5931df745a`, #4183 -`2cd948331b`, #4184 `bd19a3e020`, **#4185 `3dec774929`** (post-34/35/36 move), -#4186 `6f7abbadc1`, #4191 `8acb0bd14c`, #4194 `9efc0b7e3a`, #4195 `4f2eb06d64`, -#4196 `ea4f783490`, #4204 `7bc8a845c6`, #4240 `9328609a16`, #4247 `0dcdc743e7`, -#4251 `176f8ed3eb`, **#4256 `8febac177c`** (post-34/35/36 move), #4258 -`5adfc40032`, #4259 `4270d827c2`. Rows describing open PRs reflect those heads -and go stale as the PRs are updated; the merged table does not. - -Four of these were corrected on 2026-08-01 after the heads moved. The -`#4185 0b0d5c76d6 (post-renumber)` this list previously carried was wrong twice -over: `0b0d5c76d6` is the *parent* of the renumber commit `d854debb`, so it was -pre-renumber, and the branch has since advanced to `6c37e8679e`. #4184 was -recorded at `a9e418af50` (now `bd19a3e020`), #4247 at `72c000dcfd` (now -`0dcdc743e7`), and #4256 at `d8943ccf10` (now `9481e5783b`, which carries the -29 → 30 move). - -The 26 / 27 / 28 / 31 claims attributed to #3968, #3954 and #4259 were -re-verified on 2026-08-01 by reading `error.rs` at each of those three heads -directly, not from this file. +Compiled 2026-08-01 against `v4.2-dev` at `ed4116b26c`, re-verified 2026-08-02 +against `v4.2-dev` at `5d68612a45` (where `ErrorShutdownIncomplete = 27`, +PR #4268 `429667e723`, entered the merged table), and **re-verified again +2026-08-03 against the same base `5d68612a45`**, which is still `v4.2-dev`'s +head. + +The 2026-08-03 pass re-read the added discriminants directly at the *current* +head of every open PR that touches `error.rs`, `DashSdkError.kt` or +`PlatformWalletResult.swift`, and separately checked each PR's file list to +decide whether it belongs in the no-new-code inventory above. It confirmed: + +* 32 and 33 are still #4247/#4256's, which is why the trio sits at 34–36; +* 37 is #4204's post-renumber `ErrorShieldedInviteAlreadyClaimed`, mirrored in + Kotlin and half-mirrored in Swift (see the code-32 section); +* nothing in flight has taken 28, 30, or 38; +* #4247's head now also carries the 34/35/36 trio, inherited from #4185 (which + it is stacked on) rather than claimed a second time. + +PR heads of record, all read on 2026-08-03: + +| PR | Head | Note | +| --- | --- | --- | +| #3954 | `93d0bd49b7` | Closed; superseded by #4268 | +| #3968 | `5931df745a` | Contains base `ed4116b26c` but **not** `5d68612a45` | +| #4183 | `8387858016` | Rebased onto `5d68612a45` on 2026-08-03; keeps 31 | +| #4184 | `5bdae75391` | Rebased onto `5d68612a45` on 2026-08-03; keeps 29 | +| #4185 | `8813e98533` | Post-34/35/36 move | +| #4186 | `951260520c` | | +| #4191 | `8acb0bd14c` | Touches only `dashpay.rs` in this crate | +| #4194 | `1d812c7297` | | +| #4195 | `c471dc9fe6` | | +| #4196 | `ea4f783490` | Still 26/27/28; restack blocked, see above | +| #4204 | `d78b940a03` | Post-renumber 32 → 37; pre-#4268 base | +| #4240 | `3c19977a5a` | No file under this crate | +| #4243 | `f4be5b32f0` | Modifies `error.rs`, claims no integer | +| #4247 | `540def16a0` | Carries #4185's trio | +| #4251 | `176f8ed3eb` | No file under this crate | +| #4256 | `a456664278` | Post-34/35/36 move | +| #4258 | `ce8233edb7` | **Merged** 2026-08-03; claimed no code | +| #4259 | `9336bdbb71` | Carries #4183's 31 | +| #4264 | `bf88c92b85` | Closed; work carried by #4243 | + +Rows describing open PRs reflect those heads and go stale as the PRs are +updated; the merged table does not. + +That churn is the point of dating the table. An earlier revision of this list +carried `#4185 0b0d5c76d6 (post-renumber)`, which was wrong twice over: +`0b0d5c76d6` is the *parent* of the renumber commit `d854debb`, so it was +pre-renumber, and the branch had already moved on. Every head above was read +from GitHub on the date given, not copied forward from a previous revision of +this file, and the claims attributed to #3968, #3954, #4204 and #4259 were +confirmed by reading `error.rs` at each of those heads directly. From 78023422b5e9c4788db08a1e06abffa29d467c43 Mon Sep 17 00:00:00 2001 From: bfoss765 <38437574+bfoss765@users.noreply.github.com> Date: Mon, 3 Aug 2026 19:40:12 -0400 Subject: [PATCH 08/11] docs(ffi): #4196 has restacked; spell out Swift's three mirror sites MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three more live review findings, all verified against current PR heads. Rule 5 named a `PlatformWalletResultCode.init(result:)` that does not exist — `init(result:)` belongs to the downstream `PlatformWalletError`. As written, a contributor could add the Swift raw case and the typed error handling and still omit `PlatformWalletResultCode.init(ffi:)`, which is where the generated C constant is recognised; that switch has a `default:` yielding `.errorUnknown`, so the omission compiles and silently loses the code's identity before typed handling sees it. Rule 5 now enumerates all three Swift sites and says how each one fails: (1) the raw case, (2) the `init(ffi:)` arm — silent, and (3) `PlatformWalletError` + its `init(result:)` arm — a hard compile error, since that switch is exhaustive with no `default:`. That third failure is exactly what #4204 is sitting on at `d78b940a03`. #4196 is no longer blocked. Its head moved to `12492e8c54`, the restack onto #4185 is done, #4185's head `8813e98533` is an ancestor, the trio reads 34/35/36, and the PR is MERGEABLE against v4.2-dev. Verified the numeric references it owns were carried too: the `StaleReservationToken` KDoc and `fromPlatformWalletNative` mapping in `DashSdkError.kt` both read 34, and the V2 broadcast KDoc in `ManagedCoreWallet.kt` reads 34 with the rest symbolic. `PlatformWalletError::StaleReservation` refers to the code symbolically and never carried a number. The section is now a resolution rather than an open item; the account of why the restack was hard is kept, since that was the substance of the delay. The code-30 sweep was overstated. "No PR anywhere defines a code 30" is false for the surveyed heads — #4185 and #4256 both did; that was the allocation, not a competing claim. It now reads "no PR unrelated to #4185 defines a code 30", which is the claim that actually supports the conclusion. The list of branches carrying the stale consent-code reservation is corrected to #4183, #4204 and #4256's pre-renumber rationale (#4247 was never one of them). Provenance and the proposed table pick up #4196's new head. markdownlint MD018/MD004 remain at 0. --- .../ERROR_CODE_REGISTRY.md | 141 ++++++++++-------- 1 file changed, 79 insertions(+), 62 deletions(-) diff --git a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md index b30490b8232..0e1d504be9a 100644 --- a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md +++ b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md @@ -45,13 +45,22 @@ branch's diff. that are still only proposed (unmerged) may be renumbered to resolve a collision; codes on `v4.2-dev` may not. 4. **Do not reuse a retired integer.** Mark it reserved and move on. -5. **Update the mirrors in the same PR**: the Rust enum, the Swift - `PlatformWalletResultCode` + its `init(result:)` switch, and — where the code - deserves typed handling — the Kotlin `fromPlatformWalletNative` mapping and - `DashSdkErrorTest`. Kotlin is allowed to be non-exhaustive: unmapped codes - fall through to `PlatformWallet.Generic(code, …)`, which preserves the - integer. Swift is exhaustive; an unmirrored code surfaces as - `.errorUnknown` there and loses its identity. +5. **Update the mirrors in the same PR.** Swift needs **three** edits, not one, + and they fail in different ways: + 1. `PlatformWalletResultCode` — the raw case. + 2. `PlatformWalletResultCode.init(ffi:)` — the arm mapping the generated C + constant. This switch has a `default:` that yields `.errorUnknown`, so + omitting the arm compiles fine and silently loses the code's identity + *before* any typed handling sees it. + 3. `PlatformWalletError` — the typed case, **and** its `init(result:)` arm. + That switch is exhaustive with no `default:`, so adding a raw case in (1) + without the matching arm here makes it non-exhaustive and the Swift + package stops compiling. + + Then, where the code deserves typed handling, the Kotlin + `fromPlatformWalletNative` mapping and `DashSdkErrorTest`. Kotlin is allowed + to be non-exhaustive: unmapped codes fall through to + `PlatformWallet.Generic(code, …)`, which preserves the integer. 6. **Blocks 98–99 are terminal sentinels** (`NotFound`, `ErrorUnknown`) and are not an allocation frontier. New codes go after the highest allocated value below them. @@ -115,7 +124,7 @@ this file. | 31 | `ErrorSigningKeyUnavailable` | #4183 | In review (also carried by #4204, #4259) | | 32 | `ErrorTransactionBuild` | #4247 | In review (also carried by #4256) | | 33 | `ErrorTransactionSigning` | #4256 | In review | -| 34 | `ErrorStaleReservationToken` | #4185 | In review — **moved 27 → 34** (also carried by #4256; #4196 inherits on restack) | +| 34 | `ErrorStaleReservationToken` | #4185 | In review — **moved 27 → 34** (also carried by #4256 and, post-restack, #4196) | | 35 | `ErrorReservationTokenConsumed` | #4185 | In review — **moved 28 → 35** (also carried by #4256) | | 36 | `ErrorReservationWalletMismatch` | #4185 | In review — **moved 30 → 36** (also carried by #4256) | | 37 | `ErrorShieldedInviteAlreadyClaimed` | #4204 | In review — **moved 32 → 37** (collided with #4247's `ErrorTransactionBuild`; see below) | @@ -150,7 +159,8 @@ on the number: PR `#4196` also claims no new integer: it adds a token-less `PlatformWalletError::StaleReservation` variant and deliberately routes it through the **existing** `ErrorStaleReservationToken`, so it allocates nothing -and only has to follow that code's number (see below). +and only has to follow that code's number. As of 2026-08-03 it has restacked +onto #4185 and follows 34 (see below). ### Non-conforming allocations (withdraw and reissue) @@ -253,17 +263,22 @@ That is the whole reason this file exists. ### 30 — vacated, then RESERVED (not free) `ErrorAssetLockCrossDomainConsentRequired` is named as the holder of 30 in -in-tree comments on #4204 and on #4247/#4256's numbering rationale. It is -**not defined anywhere** — #4184, the PR that would have introduced it, does not -contain it after a re-scope. +in-tree comments on #4183 and #4204, and in #4256's pre-renumber numbering +rationale. It is **not defined anywhere** — #4184, the PR that would have +introduced it, does not contain it after a re-scope. Verified 2026-08-01 by reading `packages/rs-platform-wallet-ffi/src/error.rs` at -the head of **every one of the 62 open PRs**: no PR anywhere defines a code 30. -30 was therefore genuinely free at that moment, and #4185 took it — then vacated -it again on 2026-08-02 when the trio moved to 34–36. Vacating is not the same as -freeing: **30 is now RESERVED and must not be reissued** (see the collision -history below for why). The stale "reserved for the consent code" comments -should be dropped by whichever PR touches them next. +the head of **every one of the 62 open PRs**. Stated precisely, because the +unqualified version of this sentence is false: **no PR unrelated to #4185 +defines a code 30.** #4185 itself, and #4256 downstream of it, did define +`ErrorReservationWalletMismatch = 30` at their surveyed heads — that was the +allocation, not a competing claim. So nothing contested 30, #4185's claim stood, +and the stale consent-code reservation never conflicted with it. + +PR #4185 then vacated 30 on 2026-08-02 when the trio moved to 34–36. Vacating +is not the same as freeing: **30 is now RESERVED and must not be reissued** (see +the collision history below for why). The stale "reserved for the consent code" +comments should be dropped by whichever PR touches them next. Three branches have now done so: @@ -314,50 +329,51 @@ PR `#3968` needs a rebase onto current `v4.2-dev` **and** fresh integers from the frontier (**38+**). It must leave 26 alone; 27 is no longer available to it either (merged ABI now), and neither are the reserved 28 and 30. -### 26 — #4196's trio collides with merged ABI (and is now two moves behind) - -PR #4196 (stacked on #4185) branched before `26 = ErrorTransactionBroadcastRejected` -merged, and its head still numbers the reservation trio **26 / 27 / 28**. It is -now two moves behind: merging it as it stands would give 26 two meanings, give 27 -two meanings against #4268's merged `ErrorShutdownIncomplete`, and contradict -the #4185 numbering of **34 / 35 / 36** for the same three names. It needs a -rebase and must adopt whatever numbering #4185 lands with. No new integers are -needed for it. - -**All three of those numbers come from the copy of #4185 that #4196 carries, not -from #4196's own commits.** Restacking onto #4185's head therefore fixes the -trio for free — including the two moves #4196 never had to make itself. The one -number #4196 does own is a doc reference: its `StaleReservation` variant and the -matching Kotlin KDoc both cite `ErrorStaleReservationToken` as **26**, and that -becomes **34** post-restack. So the number #4196 must chase is 34. - -**The restack is not mechanical — it is blocked on a redesign.** Rebasing -the three commits #4196 owns (`2d29451d06`, `c64af1a6eb`, `ea4f783490`) onto -the #4185 head (`6c37e8679e` when this was measured; now `8813e98533`) conflicts -in three files (10 hunks): `error.rs` (3), -`wallet/core/broadcast.rs` (1), `wallet/signed_payment_registry.rs` (6). The -`error.rs` hunks are genuinely mechanical. The other two are not, because #4185 -redesigned the registry underneath #4196 after it branched: - -* `registered_height` changed from `Option` to a mandatory `u32`. #4196's - age guard is built around the `None` case meaning "guard disabled"; that case - no longer exists. +### 26 — RESOLVED: #4196 restacked onto #4185 and is on 34 / 35 / 36 + +**Closed out 2026-08-03.** PR #4196 (stacked on #4185) branched before +`26 = ErrorTransactionBroadcastRejected` merged, and for most of this file's +life its head still numbered the reservation trio **26 / 27 / 28** — two moves +behind, in a state where merging it would have given 26 two meanings, given 27 +two meanings against #4268's merged `ErrorShutdownIncomplete`, and contradicted +the **34 / 35 / 36** of #4185 for the same three names. + +That is no longer the case. At head `12492e8c54` the restack is done: +`ErrorStaleReservationToken = 34`, `ErrorReservationTokenConsumed = 35`, +`ErrorReservationWalletMismatch = 36`, `ErrorShutdownIncomplete = 27` present +from the merged base, #4185's head `8813e98533` is an ancestor, and the PR is +MERGEABLE against `v4.2-dev`. It still allocates no integer of its own. + +The numeric references #4196 owns were carried along with it. Verified at +`12492e8c54`: + +* `DashSdkError.kt` — the `StaleReservationToken` KDoc reads "native code 34", + and `fromPlatformWalletNative` maps `34 -> PlatformWallet.StaleReservationToken`. +* `ManagedCoreWallet.kt` — its V2 broadcast KDoc reads "native code 34, shared + with the deferred-token surface"; the remaining mentions are symbolic + `[StaleReservationToken]` links carrying no number. +* `PlatformWalletError::StaleReservation` — refers to the FFI code symbolically + and has never contained a number, so it needed no update. + +**Kept for the record, because the delay was the interesting part.** The restack +was not mechanical. Rebasing the three commits #4196 owned onto #4185's head +conflicted in three files (10 hunks): `error.rs` (3), +`wallet/core/broadcast.rs` (1), `wallet/signed_payment_registry.rs` (6). Only +the `error.rs` hunks were mechanical, because #4185 had redesigned the registry +underneath #4196 after it branched: + +* `registered_height` changed from `Option` to a mandatory `u32`, and + #4196's age guard was built around the `None` case meaning "guard disabled". * #4185 added a `SignedPaymentError::WalletRemoved` variant and an - owner-stamped `funding_reservation_token` field. #4196 predates both. -* #4196 wants to *move* `RESERVATION_MAX_AGE_BLOCKS` and `reservation_expired` - into `wallet/reservations.rs` so the V2 handle path can share them. #4185 has - since rewritten both in place, with new generation-binding rationale. -* #4196's V2 guard documents "leave the stale reservation for the TTL rather - than release by outpoint". #4185 now releases by owner-guarded *token*, which - changes that rationale rather than conflicting with it textually. - -Resolving this means re-deriving #4196's age guard against the new registry -shape, with real semantic decisions to make (does the V2 guard now release by -owner token? what replaces the `None`-disables-the-guard branch?). That is -author work, not conflict resolution, and it is why this was left rather than -forced through. shumkov's 07-24 request to restack onto #4185's post-renumber -head is actionable in the sense that the base now exists — but the restack -itself needs #4196's author. + owner-stamped `funding_reservation_token` field, both of which #4196 predated. +* #4196 wanted to *move* `RESERVATION_MAX_AGE_BLOCKS` and `reservation_expired` + into `wallet/reservations.rs`; #4185 had since rewritten both in place. +* #4196's V2 guard documented "leave the stale reservation for the TTL rather + than release by outpoint", while #4185 now releases by owner-guarded *token*. + +Re-deriving the age guard against the new registry shape was author work, not +conflict resolution — which is why this sat for as long as it did rather than +being forced through by whoever was maintaining this file. ### 31 vs 33 — two signing-related codes, deliberately distinct @@ -455,6 +471,7 @@ decide whether it belongs in the no-new-code inventory above. It confirmed: * 37 is #4204's post-renumber `ErrorShieldedInviteAlreadyClaimed`, mirrored in Kotlin and half-mirrored in Swift (see the code-32 section); * nothing in flight has taken 28, 30, or 38; +* #4196 has restacked onto #4185 and now carries 34/35/36 rather than 26/27/28; * #4247's head now also carries the 34/35/36 trio, inherited from #4185 (which it is stacked on) rather than claimed a second time. @@ -471,7 +488,7 @@ PR heads of record, all read on 2026-08-03: | #4191 | `8acb0bd14c` | Touches only `dashpay.rs` in this crate | | #4194 | `1d812c7297` | | | #4195 | `c471dc9fe6` | | -| #4196 | `ea4f783490` | Still 26/27/28; restack blocked, see above | +| #4196 | `12492e8c54` | **Restacked onto #4185 on 2026-08-03**; trio now 34/35/36, MERGEABLE | | #4204 | `d78b940a03` | Post-renumber 32 → 37; pre-#4268 base | | #4240 | `3c19977a5a` | No file under this crate | | #4243 | `f4be5b32f0` | Modifies `error.rs`, claims no integer | From ae21604bae7cccb5d9a0d7d4d5df012cef5285ec Mon Sep 17 00:00:00 2001 From: bfoss765 <38437574+bfoss765@users.noreply.github.com> Date: Tue, 4 Aug 2026 07:53:50 -0400 Subject: [PATCH 09/11] docs(ffi): record 31 as merged ABI; sweep the stale open-PR state MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Re-verified the whole document against the CURRENT `origin/v4.2-dev` (`97904ed2fc`), not the `f53e5eef0a` the review comment cited and not the `5d68612a45` this file was last compiled against. `ErrorSigningKeyUnavailable = 31` is merged ABI. It landed in `189a3abb1c` (#4183, stacked on #4191) together with its Rust C-facing discriminant and complete Swift and Kotlin mirrors — the raw case, the `init(ffi:)` arm, the typed `PlatformWalletError` case with its `init(result:)` arm, and Kotlin's `31 -> PlatformWallet.SigningKeyUnavailable`. Leaving it under "Proposed allocations", whose preamble explicitly permits renumbering, contradicted rule 3. Moved to the merged table. Four PRs merged into `v4.2-dev` on 2026-08-04 and this file still treated all four as open: #4191 (`0e2282b586`), #4183 (`189a3abb1c`), #4277 (`6704a41a85`), #4251 (`7afc8a8ff3`). Only #4183 claimed an integer; the other three claimed none, and #4277 is now recorded as the merged precedent for "touches error.rs but allocates nothing" (it routes TxMetadataPayloadTooLarge onto the existing ErrorInvalidParameter). Dependent sections updated so nothing implies 31 may still move: the frontier breakdown (unchanged at 38), the proposed table, the inherited-code table (31 is trunk now, not an inheritable claim), the collision-history bullet list, the no-new-code open-PR inventory, the 31-vs-33 note (collapsing 31 is no longer available; only #4256's 33 is still open), and the survey provenance plus the PR-heads-of-record table. Also refreshed, because a re-dated provenance section must not carry claims that are now false: #4204's and #4256's Swift mirror gaps are both closed, and the stale ErrorAssetLockCrossDomainConsentRequired comments are gone from every branch that carried them. Every discriminant, mirror, PR state, and SHA above was read from git or the GitHub API on 2026-08-04. The four merge SHAs were confirmed ancestors of `97904ed2fc`. Co-Authored-By: Claude Opus 4.8 --- .../ERROR_CODE_REGISTRY.md | 231 ++++++++++++------ 1 file changed, 152 insertions(+), 79 deletions(-) diff --git a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md index 0e1d504be9a..f289cc4c59c 100644 --- a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md +++ b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md @@ -99,11 +99,12 @@ These are shipped ABI. Do not renumber. | 25 | `ErrorAssetLockFundingMismatch` | | | 26 | `ErrorTransactionBroadcastRejected` | Merged in `9302c62e8b`; took a number several open branches had been treating as free | | 27 | `ErrorShutdownIncomplete` | Merged 2026-08-02 by **#4268** (`429667e723`). A quiesce/drain barrier missed its budget. **Took the number #4185 had held since before this file existed** — see the collision history below | +| 31 | `ErrorSigningKeyUnavailable` | Merged 2026-08-04 by **#4183** (merge commit `189a3abb1c`, stacked on #4191). The signer holds no usable private key for a requested public key. Landed complete in that one commit: the Rust C-facing discriminant, Swift's `errorSigningKeyUnavailable = 31` raw case *and* its `init(ffi:)` arm *and* the typed `PlatformWalletError` case with its `init(result:)` arm, and Kotlin's `31 -> PlatformWallet.SigningKeyUnavailable`. Rule 3 now protects it — see the 31-vs-33 note below | | 98 | `NotFound` | Sentinel — `Option` returned as an error | | 99 | `ErrorUnknown` | Sentinel — unmapped/flattened errors | -**Next allocatable integer: 38** — 27–37 are all claimed (27 merged; 29 and -31–37 in the proposed table below; 28 and 30 reserved). **28 and 30 are +**Next allocatable integer: 38** — 27–37 are all claimed (27 and 31 merged; 29 +and 32–37 in the proposed table below; 28 and 30 reserved). **28 and 30 are RESERVED, not free**: #4185 and #4256 vacated them when the reservation trio moved to 34–36, but they are deliberately left unclaimed rather than back-filled, so that the trio stays contiguous and no number is reused within a @@ -121,7 +122,6 @@ this file. | 28 | *(reserved — vacated)* | — | Vacated by #4185/#4256 on 2026-08-02; RESERVED, not reissuable — the next-free frontier is the only allocation source | | 29 | `ErrorAssetLockInsufficientFunds` | #4184 | In review — **keeps 29** (collision resolved) | | 30 | *(reserved — vacated)* | — | Vacated by #4185/#4256 on 2026-08-02; RESERVED, not reissuable — the next-free frontier is the only allocation source | -| 31 | `ErrorSigningKeyUnavailable` | #4183 | In review (also carried by #4204, #4259) | | 32 | `ErrorTransactionBuild` | #4247 | In review (also carried by #4256) | | 33 | `ErrorTransactionSigning` | #4256 | In review | | 34 | `ErrorStaleReservationToken` | #4185 | In review — **moved 27 → 34** (also carried by #4256 and, post-restack, #4196) | @@ -129,38 +129,59 @@ this file. | 36 | `ErrorReservationWalletMismatch` | #4185 | In review — **moved 30 → 36** (also carried by #4256) | | 37 | `ErrorShieldedInviteAlreadyClaimed` | #4204 | In review — **moved 32 → 37** (collided with #4247's `ErrorTransactionBuild`; see below) | +**Code 31 left this table on 2026-08-04.** `ErrorSigningKeyUnavailable` sat here +as #4183's proposal until #4183 merged (`189a3abb1c`); it is now in the merged +table above and rule 3 applies to it in full. Nothing else in this table has +merged, and the frontier is unchanged at 38. + Open PRs that touch `rs-platform-wallet-ffi` but claim **no** new code, verified -2026-08-03 against each PR's file list and the `error.rs` at its head: -`#3417`, `#3549`, `#3992`, `#4186`, `#4191`, `#4194`, `#4195`, `#4243`. +2026-08-04 against each PR's file list and the `error.rs` at its head: +`#3417`, `#3549`, `#3992`, `#4186`, `#4194`, `#4195`, `#4243`. -Four entries this list used to carry have been removed, each for a different +Five entries this list used to carry have been removed, each for a stated reason, so they are not silently re-added: | Was listed | Why it is gone | | --- | --- | -| `#4240` | Its head touches no file under `rs-platform-wallet-ffi` at all | -| `#4251` | Same — no file under this crate | +| `#4240` | Its head touches no file under `rs-platform-wallet-ffi` at all — still true at `a167afe84c` | +| `#4251` | Was listed as touching no file under this crate; **merged into `v4.2-dev` on 2026-08-04** (`7afc8a8ff3`) having touched none, so nothing here changes | | `#4258` | Merged into `v4.2-dev` on 2026-08-03 (`ce8233edb7`); it claimed no code, so the merged table is unchanged | | `#4264` | Closed. Its `error.rs` change (mapping new wallet errors onto the existing `ErrorInvalidParameter`) is carried by `#4243`, which is still open and is listed above | +| `#4191` | **Merged into `v4.2-dev` on 2026-08-04** (`0e2282b586`). In this crate it only ever touched `dashpay.rs`; it claimed no code, so the merged table gained nothing from it — though #4183, which was stacked on it, did | -`#4243` is worth naming explicitly: it *does* modify `error.rs`, but only to map +`#4186`, `#4194`, `#4195` and `#4243` are worth naming explicitly: each *does* +modify `error.rs`, but none of them adds a discriminant — `#4243`'s change maps new wallet errors onto the **existing** `ErrorInvalidParameter`. Touching `error.rs` is not the same as claiming an integer, and this list tracks the latter. -Two more PRs carry a code they did not allocate, inherited from the PR they are -stacked on rather than claimed fresh — they must not be read as a second claim -on the number: +**`#4277` is the merged precedent for that distinction.** It merged into +`v4.2-dev` on 2026-08-04 (`6704a41a85`) with a change to this crate's +`error.rs`, and it claimed no integer: it routes +`PlatformWalletError::TxMetadataPayloadTooLarge` onto the existing +`ErrorInvalidParameter`, with an in-line comment saying it does so deliberately +"so no new numeric code churns the Swift/Kotlin mirror enums". The merged table +is unchanged by it. + +The **inherited-code table** that used to sit here is gone, and its single row is +worth recording rather than deleting: | Code | Name | Carried by | Allocated to | | ---: | --- | --- | --- | | 31 | `ErrorSigningKeyUnavailable` | #4204, #4259 | #4183 | +#4183 merged on 2026-08-04, so 31 is no longer an allocation anyone can inherit +— it is trunk. Every branch that has rebased onto current `v4.2-dev` carries it +from the base, which is not a claim and cannot be double-counted. Verified +2026-08-04 at the heads of #4184, #4185, #4186, #4194, #4195, #4196, #4204, +#4240, #4247, #4256 and #4259: all eleven show `ErrorSigningKeyUnavailable = 31` +inherited from the merged base. + PR `#4196` also claims no new integer: it adds a token-less `PlatformWalletError::StaleReservation` variant and deliberately routes it through the **existing** `ErrorStaleReservationToken`, so it allocates nothing -and only has to follow that code's number. As of 2026-08-03 it has restacked -onto #4185 and follows 34 (see below). +and only has to follow that code's number. As of 2026-08-04 (head `9909f77546`) +it is restacked onto #4185 and follows 34 (see below). ### Non-conforming allocations (withdraw and reissue) @@ -171,9 +192,10 @@ allocation of record. A rebase is a precondition, not the remedy. `#3968`'s head does contain the 2026-08-01 base `ed4116b26c` (merge commit `debf67bdae` brought it in), so it is -not simply an un-rebased branch; it is behind the *current* base `5d68612a45`, -which is where #4268's merged `ErrorShutdownIncomplete = 27` lives. Rebasing -picks that up, but rebasing alone will not resolve anything below, because git +not simply an un-rebased branch; it is behind the *current* base `97904ed2fc`, +which is where both #4268's merged `ErrorShutdownIncomplete = 27` and #4183's +merged `ErrorSigningKeyUnavailable = 31` live. Rebasing +picks those up, but rebasing alone will not resolve anything below, because git sees no conflict in any of it — the branch has to **edit its own enum**. | Code | Name | Owning PR | Conflict | @@ -221,21 +243,24 @@ failure this file's preamble describes, and it landed on the shielded-invite claim-recovery path (the error is raised from four sites in `wallet/shielded/operations.rs`, three of them inside the recovery function). -**Partially** fixed on #4204 together with the renumber. Landed at head +Fixed on #4204 together with the renumber. Landed at head `d78b940a03`: the typed Kotlin `PlatformWallet.ShieldedInviteAlreadyClaimed` (terminal, `isRetryable = false`), the Swift `PlatformWalletResultCode.errorShieldedInviteAlreadyClaimed = 37` raw case with its `init(ffi:)` arm, and a `DashSdkErrorTest` assertion that pins 37 so a future move off the frontier fails the suite instead of the hosts. -**Still missing on #4204, and it does not compile without it:** Swift's -`PlatformWalletError` has no `.shieldedInviteAlreadyClaimed` case, and its -`init(result:)` switches exhaustively over `PlatformWalletResultCode` with no -`default:`. Adding the raw case without the matching `init(result:)` arm makes -that switch non-exhaustive, so the Swift package fails to build at -`d78b940a03`. Rule 5's Swift clause is therefore not yet satisfied — treat the -Swift mirror as incomplete until #4204 adds the typed error case and its -conversion arm. +The Swift half of that fix was **incomplete at `d78b940a03`, and is complete +now.** At that head `PlatformWalletError` had no `.shieldedInviteAlreadyClaimed` +case even though `init(result:)` switches exhaustively over +`PlatformWalletResultCode` with no `default:` — adding the raw case without the +matching arm makes that switch non-exhaustive, so the Swift package did not +build. **Closed since.** Verified 2026-08-04 at #4204's current head +`4efecd5b71`: the raw case, its `init(ffi:)` arm, the typed +`PlatformWalletError.shieldedInviteAlreadyClaimed` case and its `init(result:)` +arm are all present. Rule 5's Swift clause is satisfied. Kept here because the +sequence is the lesson — one of rule 5's three Swift edits landed a full review +round after the other two. **Lesson for rule 2:** the violation entered on a *review-round* commit, well after the PR's numbering had been reviewed and recorded as settled. Re-check @@ -262,10 +287,12 @@ That is the whole reason this file exists. ### 30 — vacated, then RESERVED (not free) -`ErrorAssetLockCrossDomainConsentRequired` is named as the holder of 30 in +`ErrorAssetLockCrossDomainConsentRequired` was named as the holder of 30 in in-tree comments on #4183 and #4204, and in #4256's pre-renumber numbering rationale. It is **not defined anywhere** — #4184, the PR that would have -introduced it, does not contain it after a re-scope. +introduced it, does not contain it after a re-scope. Those comments are all gone +now: grepping `packages` at `v4.2-dev` `97904ed2fc` on 2026-08-04 returns no +occurrence of the name, and neither does #4204's head `4efecd5b71`. Verified 2026-08-01 by reading `packages/rs-platform-wallet-ffi/src/error.rs` at the head of **every one of the 62 open PRs**. Stated precisely, because the @@ -286,7 +313,10 @@ Three branches have now done so: #4268 as the owner of 27 and records where the trio went. * **#4183** — its enum comment no longer describes 27–28 as reserved for the trio; on the 2026-08-03 rebase it was rewritten to say 28 and 30 are reserved - and 29 belongs to #4184, and to point here. + and 29 belongs to #4184, and to point here. **#4183 merged on 2026-08-04**, so + that corrected comment is now the in-tree text at `v4.2-dev` — the reservation + of 28 and 30 and #4184's claim on 29 are recorded in `error.rs` itself, not + only here. * **#4184** — same rebase, same correction. Its note used to read "Codes 27-28 are reserved" while naming **three** codes, which was correct only while the trio sat at 27/28/29. It now says 28 is skipped, 28 and 30 are reserved, and @@ -294,7 +324,11 @@ Three branches have now done so: (`ErrorAssetLockInsufficientFunds = 29`) never moved and remains the resolution of record. -The equivalent stale comment on **#4204** is still there. +The equivalent stale comment on **#4204** is gone too. It was not fixed by hand: +#4204 rebased onto the merged base, and its `error.rs` at `4efecd5b71` now +carries #4183's corrected 28/29/30 note verbatim from trunk. All four branches +that ever held the stale reservation text — #4256, #4183, #4184, #4204 — are +clear, and #4183's version of the note is now trunk. ### 27 / 28 — #3968 still collides; #3954's claim merged as #4268 @@ -321,9 +355,11 @@ The detail behind those rows: was closed; its work landed as **#4268**, which merged 27 into `v4.2-dev` on 2026-08-02. #4185 and #4256 moved their trio to 34–36 in response. Merging decides an ABI number; being the older open claim does not. -* **#4259** (`9336bdbb71`) carries `ErrorSigningKeyUnavailable = 31` — the same - number and name as #4183, i.e. inherited rather than a new allocation, like - #4204. No conflict; recorded so the number is not double-counted. +* **#4259** carries `ErrorSigningKeyUnavailable = 31` — the same number and name + as #4183, i.e. inherited rather than a new allocation, like #4204. No + conflict; recorded so the number is not double-counted. This is now moot: + #4183 merged on 2026-08-04, so at #4259's current head `5b77dfd8f1` the 31 is + simply the merged base's, and there is no second claim to reconcile. PR `#3968` needs a rebase onto current `v4.2-dev` **and** fresh integers from the frontier (**38+**). It must leave 26 alone; 27 is no longer available to it @@ -382,9 +418,16 @@ took 33, on the grounds that 31 (`ErrorSigningKeyUnavailable`, #4183) asserts a specific contract — the signer holds no usable private key for a requested public key, restored from a typed signer completion code — whereas #4256's `BuilderError::SigningFailed` also covers unresolved derivation paths, sighash -failures, and malformed signature encodings. Both codes are currently allocated. -Maintainers may still choose to collapse them; that decision belongs to #4183 and -to #4256 jointly and should be recorded here. +failures, and malformed signature encodings. + +**That question is now half-settled by merging, not by agreement.** #4183 merged +on 2026-08-04, so **31 is ABI** and rule 3 forbids renumbering or retiring it; +it also ships with complete Swift and Kotlin mirrors, so hosts already +distinguish it. The only decision still open is #4256's: whether 33 stays a +separate code or `BuilderError::SigningFailed` is instead routed onto the +existing 31. That is #4256's alone to make now, and if it is made it should be +recorded here. What is no longer available is collapsing the pair *into* 33, or +moving 31 anywhere. ## Collision history — the 27 / 28 / 30 → 34 / 35 / 36 move @@ -410,10 +453,16 @@ anything** — merged or proposed — rather than into the next free gap: * 27 `ErrorShutdownIncomplete` (merged, #4268) * 29 `ErrorAssetLockInsufficientFunds` (#4184) -* 31 `ErrorSigningKeyUnavailable` (#4183/#4204/#4259) +* 31 `ErrorSigningKeyUnavailable` (**merged 2026-08-04, #4183**; was proposed when the trio jumped it) * 32 `ErrorTransactionBuild` (#4247/#4256) * 33 `ErrorTransactionSigning` (#4256) +Two of those five are now shipped ABI rather than proposals: 27 already was when +the trio moved, and 31 has merged since. That is the argument for the move made +retroactively — a number that looked merely "claimed by an open PR" on +2026-08-02 is unrenumberable ABI two days later, and anything sitting on it +would now be stuck there. + Taking 34–36 rather than back-filling the vacated 28 and 30 costs two integers in a space that is nowhere near exhausted, and buys two things: the trio reads as one contiguous family, and it cannot be hit again by anything currently in @@ -437,15 +486,23 @@ top of this file: a duplicate integer across two branches produces no textual conflict, and neither branch's tree contains both variants, so neither compiler ever sees the E0081. Both were MERGEABLE and green throughout. -### Known mirror gap on #4256 (not a numbering issue) +### Mirror gap on #4256 — CLOSED (was never a numbering issue) + +Noted while grepping the mirrors for this move: #4256 declared +`ErrorTransactionBuild = 32` and `ErrorTransactionSigning = 33` in Rust and +mapped both in Kotlin, but its Swift `PlatformWalletResultCode` declared +**neither** — no `case`, and no arm in `init(ffi:)`, so both fell into that +switch's `default:` and would have reached Swift hosts as `.errorUnknown`, +losing their identity. That is rule 5's Swift clause. It was left for #4256's +author rather than folded into the renumber, since it was a missing mirror and +not a wrong number. -Noted while grepping the mirrors for this move: #4256 declares -`ErrorTransactionBuild = 32` and `ErrorTransactionSigning = 33` in Rust and maps -both in Kotlin, but its Swift `PlatformWalletResultCode` declares **neither** — -no `case`, and no arm in `init(ffi:)`, so both fall into that switch's -`default:` and reach Swift hosts as `.errorUnknown`, losing their identity. That -is rule 5's Swift clause. Left for #4256's author rather than folded into the -renumber; it is a missing mirror, not a wrong number. +**Fixed.** Verified 2026-08-04 at #4256's head `862036b18d`: +`errorTransactionBuild = 32` and `errorTransactionSigning = 33` raw cases, both +`init(ffi:)` arms, the typed `PlatformWalletError.transactionBuild` / +`.transactionSigning` cases and their `init(result:)` arms are all present. Both +of this file's outstanding Swift mirror gaps — this one and #4204's — closed +between the 2026-08-03 and 2026-08-04 passes. ## Sibling FFI crates @@ -458,46 +515,62 @@ the same thing in both enums. Compiled 2026-08-01 against `v4.2-dev` at `ed4116b26c`, re-verified 2026-08-02 against `v4.2-dev` at `5d68612a45` (where `ErrorShutdownIncomplete = 27`, -PR #4268 `429667e723`, entered the merged table), and **re-verified again -2026-08-03 against the same base `5d68612a45`**, which is still `v4.2-dev`'s -head. - -The 2026-08-03 pass re-read the added discriminants directly at the *current* -head of every open PR that touches `error.rs`, `DashSdkError.kt` or -`PlatformWalletResult.swift`, and separately checked each PR's file list to -decide whether it belongs in the no-new-code inventory above. It confirmed: - -* 32 and 33 are still #4247/#4256's, which is why the trio sits at 34–36; -* 37 is #4204's post-renumber `ErrorShieldedInviteAlreadyClaimed`, mirrored in - Kotlin and half-mirrored in Swift (see the code-32 section); +PR #4268 `429667e723`, entered the merged table), re-verified 2026-08-03 against +that same base, and **re-verified again 2026-08-04 against `v4.2-dev` at +`97904ed2fc`**, which is the current head. + +The base moved on 2026-08-04, which is what made that pass necessary: four PRs +merged into `v4.2-dev` that day — **#4191** (`0e2282b586`), **#4183** +(`189a3abb1c`), **#4277** (`6704a41a85`) and **#4251** (`7afc8a8ff3`) — and one +of them, #4183, moved a number out of this file's proposed table and into the +ABI. Each of those four SHAs is the **merge commit on `v4.2-dev`**, confirmed by +`git merge-base --is-ancestor` against `97904ed2fc`, not a PR head SHA. + +The 2026-08-04 pass re-read the discriminants directly, in-tree and at the +*current* head of every open PR that touches `error.rs`, `DashSdkError.kt` or +`PlatformWalletResult.swift`, and separately re-checked each PR's file list. It +confirmed: + +* in-tree at `97904ed2fc`, `error.rs` runs 0–27 contiguously and then **31**, + with 28, 29, 30 and everything from 32 up absent. So 31 is the only number + this file had listed as proposed that is now merged, and the frontier is + unchanged at 38; +* `31 = ErrorSigningKeyUnavailable` has **complete** host mirrors on `v4.2-dev` + — Swift's raw case, its `init(ffi:)` arm, the typed `PlatformWalletError` case + and its `init(result:)` arm, and Kotlin's + `31 -> PlatformWallet.SigningKeyUnavailable` — all introduced by + `189a3abb1c` itself, so rule 5 was satisfied in the merging commit; +* 29 is still #4184's; 32 and 33 are still #4247/#4256's, which is why the trio + sits at 34–36; 37 is still #4204's; * nothing in flight has taken 28, 30, or 38; -* #4196 has restacked onto #4185 and now carries 34/35/36 rather than 26/27/28; -* #4247's head now also carries the 34/35/36 trio, inherited from #4185 (which - it is stacked on) rather than claimed a second time. +* #3968 is unchanged and still numbers 26 / 27 / 28; +* the two Swift mirror gaps this file was tracking — #4204's typed-case gap and + #4256's missing raw cases — have both been closed. -PR heads of record, all read on 2026-08-03: +PR heads of record, all read on 2026-08-04: | PR | Head | Note | | --- | --- | --- | -| #3954 | `93d0bd49b7` | Closed; superseded by #4268 | -| #3968 | `5931df745a` | Contains base `ed4116b26c` but **not** `5d68612a45` | -| #4183 | `8387858016` | Rebased onto `5d68612a45` on 2026-08-03; keeps 31 | -| #4184 | `5bdae75391` | Rebased onto `5d68612a45` on 2026-08-03; keeps 29 | -| #4185 | `8813e98533` | Post-34/35/36 move | -| #4186 | `951260520c` | | -| #4191 | `8acb0bd14c` | Touches only `dashpay.rs` in this crate | -| #4194 | `1d812c7297` | | -| #4195 | `c471dc9fe6` | | -| #4196 | `12492e8c54` | **Restacked onto #4185 on 2026-08-03**; trio now 34/35/36, MERGEABLE | -| #4204 | `d78b940a03` | Post-renumber 32 → 37; pre-#4268 base | -| #4240 | `3c19977a5a` | No file under this crate | -| #4243 | `f4be5b32f0` | Modifies `error.rs`, claims no integer | -| #4247 | `540def16a0` | Carries #4185's trio | -| #4251 | `176f8ed3eb` | No file under this crate | -| #4256 | `a456664278` | Post-34/35/36 move | -| #4258 | `ce8233edb7` | **Merged** 2026-08-03; claimed no code | -| #4259 | `9336bdbb71` | Carries #4183's 31 | +| #3954 | `31e22d5a90` | Closed; superseded by #4268 | +| #3968 | `5931df745a` | Head unchanged since 2026-08-03; still numbers 26 / 27 / 28. Contains base `ed4116b26c` but not `5d68612a45`, and so not `97904ed2fc` either | +| #4183 | `189a3abb1c` | **Merged 2026-08-04** (merge commit); 31 is now ABI | +| #4184 | `11c3677b1c` | Keeps 29 | +| #4185 | `326cd3eab6` | Trio at 34/35/36 | +| #4186 | `1fcdfd6b37` | Modifies `error.rs`; adds no discriminant | +| #4191 | `0e2282b586` | **Merged 2026-08-04** (merge commit); claimed no code | +| #4194 | `560f66a31d` | Modifies `error.rs`; adds no discriminant | +| #4195 | `7d20a638e5` | Modifies `error.rs`; adds no discriminant | +| #4196 | `9909f77546` | Restacked onto #4185; trio 34/35/36; allocates nothing of its own | +| #4204 | `4efecd5b71` | 37; rebased onto the merged base, so it now carries 27 and 31 from trunk; Swift mirror complete | +| #4240 | `a167afe84c` | No file under this crate | +| #4243 | `f4be5b32f0` | Head unchanged; modifies `error.rs`, claims no integer | +| #4247 | `8541073247` | 32, plus #4185's trio | +| #4251 | `7afc8a8ff3` | **Merged 2026-08-04** (merge commit); no file under this crate | +| #4256 | `862036b18d` | 32 / 33 plus the trio; Swift mirror now complete | +| #4258 | `ce8233edb7` | Merged 2026-08-03; claimed no code | +| #4259 | `5b77dfd8f1` | Carries 31 from the merged base | | #4264 | `bf88c92b85` | Closed; work carried by #4243 | +| #4277 | `6704a41a85` | **Merged 2026-08-04** (merge commit); modifies `error.rs`, claims no integer | Rows describing open PRs reflect those heads and go stale as the PRs are updated; the merged table does not. From 695ca92ea8c27c4ce1e6a0aed7de7be3e92c4593 Mon Sep 17 00:00:00 2001 From: bfoss765 <38437574+bfoss765@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:07:34 -0400 Subject: [PATCH 10/11] docs(ffi): name ErrorTransactionBuild as the code-32 peer; qualify host outcomes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Two corrections from the #4261 review round: - The code-32 collision peer was misidentified. #4204 defined ErrorShieldedInviteAlreadyClaimed = 32 (b6992a5dbc) and #4247 defines ErrorTransactionBuild = 32 (error.rs:257) — that is the E0081 pair. ErrorReservationWalletMismatch never held 32; its history runs 26/28 -> 30 -> 36. - "silently wrong on every host" overstated it. The three host outcomes differ and only one is a misclassification; spell them out. Also describe the inherited-code table as reduced to one row and retained for provenance rather than gone, since the row is rendered right below. Co-Authored-By: Claude Opus 4.8 --- .../ERROR_CODE_REGISTRY.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md index f289cc4c59c..abf7bda5ea2 100644 --- a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md +++ b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md @@ -163,8 +163,8 @@ latter. "so no new numeric code churns the Swift/Kotlin mirror enums". The merged table is unchanged by it. -The **inherited-code table** that used to sit here is gone, and its single row is -worth recording rather than deleting: +The **inherited-code table** has been reduced to one row and retained for +provenance: | Code | Name | Carried by | Allocated to | | ---: | --- | --- | --- | @@ -228,7 +228,7 @@ also carried by #4256). Unlike every other entry in this section, this one was not a paper conflict: merging #4204 into an integration that already carried -`ErrorReservationWalletMismatch = 32` produced a hard +`ErrorTransactionBuild = 32` produced a hard `error[E0081]: discriminant value 32 assigned more than once`. Resolution of record: **#4204 moves 32 → 37**, the frontier. `ErrorTransactionBuild` keeps 32. @@ -236,9 +236,15 @@ The numbering was the lesser half of the defect. The code was **unmirrored on both hosts** — absent from Swift's `PlatformWalletResultCode` and from Kotlin's `fromPlatformWalletNative`. Per rule 5 that means Swift rendered it `.errorUnknown` (identity lost), while Kotlin fell through to `Generic(32, …)` -— and in any tree carrying #4185's `ErrorReservationWalletMismatch = 32`, -Kotlin actively **misclassified** "shielded invite already claimed" as -"reservation wallet mismatch". That is the exact silently-wrong-error-on-every-host +— and in any tree also carrying the competing #4247 mapping for 32, Kotlin +actively **misclassified** "shielded invite already claimed" as "transaction +build failed". `ErrorReservationWalletMismatch` was never the peer here: its +allocation history runs 26/28 → 30 → 36 and never passes through 32. + +The three host outcomes therefore differ, and only the third is a +misclassification: Swift loses the code's identity as `.errorUnknown`, Kotlin +on #4204 alone preserves `Generic(32, …)`, and Kotlin misclassifies only where +the competing code-32 mapping is also present. That is the cross-host ABI failure this file's preamble describes, and it landed on the shielded-invite claim-recovery path (the error is raised from four sites in `wallet/shielded/operations.rs`, three of them inside the recovery function). From b7ba62e4a8fa75870588b201052ef671bd4fbdc8 Mon Sep 17 00:00:00 2001 From: bfoss765 <38437574+bfoss765@users.noreply.github.com> Date: Tue, 4 Aug 2026 15:08:26 -0400 Subject: [PATCH 11/11] docs(ffi): finish the MD018 sweep for leading PR references Three leading `#` line starts survived the earlier sweep (lines 173, 177, 334) and markdownlint still read them as malformed ATX headings. markdownlint-cli2 now reports zero MD018 on this file. Co-Authored-By: Claude Opus 4.8 --- packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md index abf7bda5ea2..39fa2136a14 100644 --- a/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md +++ b/packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md @@ -170,11 +170,11 @@ provenance: | ---: | --- | --- | --- | | 31 | `ErrorSigningKeyUnavailable` | #4204, #4259 | #4183 | -#4183 merged on 2026-08-04, so 31 is no longer an allocation anyone can inherit +`#4183` merged on 2026-08-04, so 31 is no longer an allocation anyone can inherit — it is trunk. Every branch that has rebased onto current `v4.2-dev` carries it from the base, which is not a claim and cannot be double-counted. Verified 2026-08-04 at the heads of #4184, #4185, #4186, #4194, #4195, #4196, #4204, -#4240, #4247, #4256 and #4259: all eleven show `ErrorSigningKeyUnavailable = 31` +`#4240`, #4247, #4256 and #4259: all eleven show `ErrorSigningKeyUnavailable = 31` inherited from the merged base. PR `#4196` also claims no new integer: it adds a token-less @@ -331,7 +331,7 @@ Three branches have now done so: resolution of record. The equivalent stale comment on **#4204** is gone too. It was not fixed by hand: -#4204 rebased onto the merged base, and its `error.rs` at `4efecd5b71` now +`#4204` rebased onto the merged base, and its `error.rs` at `4efecd5b71` now carries #4183's corrected 28/29/30 note verbatim from trunk. All four branches that ever held the stale reservation text — #4256, #4183, #4184, #4204 — are clear, and #4183's version of the note is now trunk.