docs(platform-wallet): error-code registry for the FFI result space - #4261
docs(platform-wallet): error-code registry for the FFI result space#4261bfoss765 wants to merge 3 commits into
Conversation
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 <noreply@anthropic.com>
|
Warning Review limit reached
Next review available in: 54 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (2)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
✅ Final review complete — no blockers (commit 6f2f2d0) |
thepastaclaw
left a comment
There was a problem hiding this comment.
Preliminary review — Codex only
The registry captures several surveyed allocations correctly, but it is not yet safe to use as the authoritative source it claims to be. It omits active incompatible allocations and simultaneously marks code 30 both free and assigned; the Swift update rule, code-13 provenance, and duplicate-discriminant rationale also need factual corrections.
Validated blockers were found in the Codex precheck. Sonnet is deferred until a fresh Codex revalidation clears the blocker gate.
Review provenance
- Codex reviewers:
gpt-5.6-sol— general (completed),gpt-5.6-sol— ffi-engineer (completed) - Verifier:
gpt-5.6-sol— verifier - Sonnet: not run (deferred by blocker gate)
🔴 2 blocking | 🟡 1 suggestion(s) | 💬 2 nitpick(s)
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md`:
- [BLOCKING] packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md:97-98: Registry omits incompatible allocations from active PRs
The open-PR inventory and survey provenance omit branches that modify this exact ABI enum. At current head `5931df745a`, #3968 assigns `ErrorPersisterTransient = 26`, `ErrorPersisterFatal = 27`, and renumbers the already-shipped `ErrorTransactionBroadcastRejected` from 26 to 28. At head `93d0bd49b`, #3954 assigns `ErrorShutdownIncomplete = 27`. This conflicts with shipped code 26 and with #4185's code-27 claim, while #3968 and #3954 also assign different meanings to 27. An existing Swift host would classify #3968's persister-transient code 26 as `.errorTransactionBroadcastRejected`, and its actual rejection code 28 would fall through `init(ffi:)` to `.errorUnknown`. #4259 at `64146a2bb6` should also be recorded as carrying the same code-31 allocation as #4183. The supposedly complete no-new-code inventory additionally omits open PRs #3417, #3549, #3992, and #4243. Reconcile the incompatible claims and recompute the next-free value before presenting this file as the source of truth.
- [BLOCKING] packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md:90-120: Code 30 is marked free even though the registry assigns it to #4185
The table marks 30 as `Unallocated`, and the later section explicitly calls it free, while the resolution of record assigns that value to #4185's `ErrorReservationWalletMismatch`; the stated next-free value of 34 also assumes that ownership. Rule 1 permits contributors to claim a gap when this file marks it free, so another PR could legitimately take 30 while #4185 follows the recorded renumber directive. Record 30 as allocated to #4185 and describe its current use of 29 only as stale surveyed-head state.
- [SUGGESTION] packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md:32-38: Require the actual C-to-Swift result-code switch
The generated C enum is first converted by `PlatformWalletResultCode.init(ffi:)` at `PlatformWalletResult.swift:75-138`. That switch has a default that maps an omitted native constant to `.errorUnknown`. The registry instead names a nonexistent `PlatformWalletResultCode.init(result:)`; the actual `init(result:)` belongs to the downstream `PlatformWalletError` conversion. A contributor could therefore add the Swift raw case and downstream error handling but omit `init(ffi:)`, losing the native code's identity before typed handling sees it. Require all three Swift locations explicitly.
| Open PRs that touch `rs-platform-wallet-ffi` but claim **no** new code: #4186, | ||
| #4191, #4194, #4195, #4240, #4251, #4258. |
There was a problem hiding this comment.
🔴 Blocking: Registry omits incompatible allocations from active PRs
The open-PR inventory and survey provenance omit branches that modify this exact ABI enum. At current head 5931df745a, #3968 assigns ErrorPersisterTransient = 26, ErrorPersisterFatal = 27, and renumbers the already-shipped ErrorTransactionBroadcastRejected from 26 to 28. At head 93d0bd49b, #3954 assigns ErrorShutdownIncomplete = 27. This conflicts with shipped code 26 and with #4185's code-27 claim, while #3968 and #3954 also assign different meanings to 27. An existing Swift host would classify #3968's persister-transient code 26 as .errorTransactionBroadcastRejected, and its actual rejection code 28 would fall through init(ffi:) to .errorUnknown. #4259 at 64146a2bb6 should also be recorded as carrying the same code-31 allocation as #4183. The supposedly complete no-new-code inventory additionally omits open PRs #3417, #3549, #3992, and #4243. Reconcile the incompatible claims and recompute the next-free value before presenting this file as the source of truth.
source: ['codex']
There was a problem hiding this comment.
Resolved in 690d464 — Registry omits incompatible allocations from active PRs no longer present.
Auto-resolved by the review system based on the latest commit diff. If you believe this was closed in error, reopen the thread.
| | 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 |
There was a problem hiding this comment.
🔴 Blocking: Code 30 is marked free even though the registry assigns it to #4185
The table marks 30 as Unallocated, and the later section explicitly calls it free, while the resolution of record assigns that value to #4185's ErrorReservationWalletMismatch; the stated next-free value of 34 also assumes that ownership. Rule 1 permits contributors to claim a gap when this file marks it free, so another PR could legitimately take 30 while #4185 follows the recorded renumber directive. Record 30 as allocated to #4185 and describe its current use of 29 only as stale surveyed-head state.
source: ['codex']
There was a problem hiding this comment.
Resolved in 690d464 — Code 30 is marked free even though the registry assigns it to #4185 no longer present.
Auto-resolved by the review system based on the latest commit diff. If you believe this was closed in error, reopen the thread.
| 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. |
There was a problem hiding this comment.
🟡 Suggestion: Require the actual C-to-Swift result-code switch
The generated C enum is first converted by PlatformWalletResultCode.init(ffi:) at PlatformWalletResult.swift:75-138. That switch has a default that maps an omitted native constant to .errorUnknown. The registry instead names a nonexistent PlatformWalletResultCode.init(result:); the actual init(result:) belongs to the downstream PlatformWalletError conversion. A contributor could therefore add the Swift raw case and downstream error handling but omit init(ffi:), losing the native code's identity before typed handling sees it. Require all three Swift locations explicitly.
| 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**: the Rust enum; the Swift | |
| `PlatformWalletResultCode`, its `init(ffi:)` switch that maps the generated C | |
| constants, and `PlatformWalletError` + 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's `init(ffi:)` has an unknown-value fallback; | |
| omitting its mapping surfaces the new code as `.errorUnknown` and loses its | |
| identity. |
source: ['codex']
| | 10 | `ErrorInvalidIdentifier` | | | ||
| | 11 | `ErrorMemoryAllocation` | | | ||
| | 12 | `ErrorUtf8Conversion` | | | ||
| | 13 | `ErrorArithmeticOverflow` | Reserved slot — declared, no in-tree producer; holds the number for the mapping arriving via #3549 | |
There was a problem hiding this comment.
💬 Nitpick: ErrorArithmeticOverflow already has an in-tree producer
The exact PR base already produces this code in packages/rs-platform-wallet-ffi/src/shielded_send.rs:204-213: platform_wallet_shielded_estimate_fee maps shielded fee-formula failures to ErrorArithmeticOverflow. That producer entered through commit c9f8ef57925 and is present on v4.1-dev; the current #3549 diff does not add this mapping. Calling the slot producerless and awaiting #3549 gives incorrect provenance for an ABI registry.
| | 13 | `ErrorArithmeticOverflow` | Reserved slot — declared, no in-tree producer; holds the number for the mapping arriving via #3549 | | |
| | 13 | `ErrorArithmeticOverflow` | Produced by `platform_wallet_shielded_estimate_fee` when shielded fee computation overflows | |
source: ['codex']
| 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. |
There was a problem hiding this comment.
💬 Nitpick: Duplicate Rust discriminants do not silently reach runtime
A combined Rust enum containing two variants with the same explicit value fails compilation with E0081, and Swift rejects duplicate raw enum values as well. A textual merge can therefore leave an invalid tree, but that tree cannot successfully build and silently misclassify runtime errors. The actual ABI hazard is that independent branches or releases can reuse or renumber an integer relative to hosts already compiled against another meaning, as #3968 currently does with shipped code 26. State that cross-branch and cross-version failure mode instead.
| 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. | |
| This file exists because several feature branches allocate into the same | |
| integer range in parallel. A combined Rust enum with duplicate discriminants | |
| fails to compile, but independent branches can still reuse or renumber an ABI | |
| value relative to a host that has already compiled the other meaning. Such a | |
| version mismatch silently misclassifies errors on that host, so allocations | |
| have to be reconciled here, in one place, rather than in each branch's diff. |
source: ['codex']
…t + finalize (dashpay#4185 review) Wallet removal was not linearized with the deferred-payment registry, so a retained handle could push a removed wallet's payment onto the network. Two independent windows: 1. remove-then-sweep. `platform_wallet_manager_remove_wallet` called `manager.remove_wallet` and only afterwards swept the registry, with no shared lock spanning the two — and the removal's own awaits (shielded coordinator + identity-sync unregistration) sat in the gap. A concurrent `core_wallet_signed_payment_broadcast` in that window passed every guard: `is_same_generation` compares two handles, so a removed generation matches itself; `last_processed_height` is `None` once the wallet is gone and `reservation_expired` maps `None` to "not expired"; and `broadcast_payment_releasing_reservation` has no wallet-existence gate. 2. in-flight finalizer. `finalize_transaction` drops the manager write lock before awaiting the signer, and `register` only validates the payment against its finalizing generation — never that the generation still exists. A removal during the signer await swept the registry, then the finalizer inserted a fresh token no later sweep would catch, contradicting the documented teardown invariant that dropping tokens makes stale handles inert. Remedies: * `SignedPaymentRegistry` gains a lifecycle gate (`tokio::RwLock`). Teardown takes the exclusive side across BOTH the manager removal and the sweep, making them one linearization point; broadcast and release take the shared side for their whole duration. The existing `entries` mutex cannot do this — it is dropped before every await by design. Lock order is always gate then manager. * Broadcast rejects an absent current generation via the new `CoreWallet::is_current_generation`, returning `SignedPaymentError:: WalletRemoved` instead of silently proceeding to the broadcaster. * `core_wallet_signed_payment_finalize` holds the shared gate across its liveness check and the synchronous `register`, abandoning the payment (reconciling its reservation) if the wallet went away during signing. The gate is taken after the signer await, not around it, so an open signing prompt cannot stall teardown. No new FFI error code: the wallet-removed case is reported as the existing `NotFound` (98), which both hosts already map. Deliberately avoids the 29/30 renumbering contested in dashpay#4261. Swift/Kotlin/Rust docs updated to record that 98 now also carries this case, and how it differs from `ErrorReservationWalletMismatch` (29). Adds three FFI regression tests. All three fail against the pre-fix code — the race test reports a payment reaching the broadcaster after teardown completed. Also serializes the registry-count-asserting tests, which the new tests would otherwise race in the shared process-global registry. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…ollisions The 29 collision is resolved and the renumber has now landed on dashpay#4185's branch: dashpay#4184 keeps 29 (ErrorAssetLockInsufficientFunds), dashpay#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 dashpay#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: - dashpay#3968 numbers 26/27/28 (Persister* + a pre-merge TransactionBroadcastRejected) -> contradicts merged ABI at 26 and collides with dashpay#4185 at 27 and 28 - dashpay#3954 numbers ErrorShutdownIncomplete = 27 -> collides with dashpay#4185 at 27 - dashpay#4259 carries ErrorSigningKeyUnavailable = 31, inherited from dashpay#4183 rather than a new allocation The same sweep confirms no open PR anywhere defines a code 30.
…30 (dashpay#4185 review) Code 29 collided with `ErrorAssetLockInsufficientFunds` on dashpay#4184. Per the resolution of record in dashpay#4261's ERROR_CODE_REGISTRY.md, dashpay#4184 keeps 29 and this PR moves to 30. Verified 30 was genuinely free by reading `rs-platform-wallet-ffi/src/error.rs` at the head of all 62 open PRs: no PR defines a code 30. The `ErrorAssetLockCrossDomainConsentRequired` that in-tree comments name as 30's holder does not exist anywhere after dashpay#4184's re-scope. The discriminant is public ABI, so every mirror moves together: - Rust enum + its three rustdoc cross-references (error.rs) - two doc references in core_wallet/signed_payment.rs - JNI rustdoc (rs-unified-sdk-jni/src/wallet_manager.rs) - Swift PlatformWalletResultCode raw value + doc - Kotlin fromPlatformWalletNative branch, class KDoc, code-98 comment, WalletManagerNative KDoc, and the DashSdkErrorTest offset assertion Both Swift switches are symbolic (cbindgen `PLATFORM_WALLET_FFI_RESULT_CODE_*` constants), so only the enum raw value carried the number. Also disarms the NativeCleaner backstop in SignedCoreTransactionTest by closing the SignedCoreTransaction, so the armed native release cannot fire from the cleaner thread in a pure-JVM test. Note: dashpay#4256 is stacked downstream and still carries the pre-renumber 29; it must adopt 30 on rebase.
…cord dashpay#4196 scope Clears the two review blockers on dashpay#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) — dashpay#3968 / dashpay#3954 / dashpay#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 dashpay#3968 (26/27/28) and dashpay#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 dashpay#4204 and dashpay#4259 carry but did not allocate (dashpay#4183 owns it), so it is not double-counted. - dashpay#4196 is recorded as claiming no integer at all: it routes a new token-less `StaleReservation` variant through the existing `ErrorStaleReservationToken`. The dashpay#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 dashpay#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 dashpay#4185 throughout, frontier is 34, and the one remaining "genuinely free" is past tense explaining why dashpay#4185 could take it). Also corrected, all verified against the branches: - Survey provenance had dashpay#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`. dashpay#4184, dashpay#4247 and dashpay#4256 SHAs refreshed too. - dashpay#4256 has now taken 30 (`9481e5783b`) and dropped its stale "30 is reserved for the consent code" rationale; the equivalent comments on dashpay#4183 and dashpay#4204 are flagged as still present. - dashpay#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 dashpay#4184 is left untouched. - The dashpay#4196 section now records why the restack has not happened: its three own commits conflict in 3 files / 10 hunks against dashpay#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 dashpay#4185 copy it carries, so the restack fixes 28 -> 30 for free; the number dashpay#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.
…30 (dashpay#4256) dashpay#4256 is stacked on dashpay#4185 and still carried the pre-renumber `29`, which now collides with `ErrorAssetLockInsufficientFunds = 29` on dashpay#4184. Per the resolution of record in dashpay#4261's ERROR_CODE_REGISTRY.md, dashpay#4184 keeps 29 and the `ErrorReservationWalletMismatch` family moves to 30; dashpay#4185 already made that move in `d854debb`. This brings dashpay#4256 in line. CI could not have caught this: dashpay#4256 and dashpay#4184 are both MERGEABLE with green checks, because two branches assigning the same discriminant produce no textual conflict. It surfaces only as an E0081 after a textual merge, or silently as a wrong error code on the host. The discriminant is public ABI, so every mirror moves together: - Rust enum + its rustdoc cross-reference (error.rs) - doc reference in core_wallet/signed_payment.rs - JNI rustdoc (rs-unified-sdk-jni/src/wallet_manager.rs) - Swift PlatformWalletResultCode raw value - Kotlin fromPlatformWalletNative branch, class KDoc, WalletManagerNative KDoc, and the DashSdkErrorTest offset assertion Both Swift switches are symbolic (cbindgen `PLATFORM_WALLET_FFI_RESULT_CODE_*` constants), so only the enum raw value carried the number. Also corrects this PR's own numbering rationale on `ErrorTransactionSigning` (33), which claimed 30 was "reserved for dashpay#4184's ErrorAssetLockCrossDomainConsentRequired". That code does not exist on any branch — dashpay#4184 dropped it in a re-scope — and 30 is now ErrorReservationWalletMismatch. dashpay#4256's codes are unchanged otherwise: it keeps 32 (ErrorTransactionBuild, shared with dashpay#4247) and 33. Verified: cargo fmt --all -- --check clean; cargo test -p platform-wallet-ffi -p platform-wallet = 805 passed / 0 failed; :sdk:test BUILD SUCCESSFUL with DashSdkErrorTest 9/9. Swift is unverified — it cannot be compiled here.
|
Both blockers addressed in (b) "code 30 marked both free and assigned" — resolved. The contradiction was internal to the doc: the next-free line claimed 27–33 were taken while the table row showed 30 unallocated. 30 is now recorded as allocated to the reservation-mismatch code, which is where it actually landed (#4185 (a) omitted allocations — added rows for #3968, #3954 and #4259, with corrected provenance. Verified by reading Recorded but not fixed, since they belong to other PRs:
Structural note worth capturing somewhere more durable than a doc: duplicate discriminants across branches produce no textual conflict, so two PRs can both be MERGEABLE and green while claiming the same integer — #4256 and #4184 were in exactly that state until today. This registry only helps if it is consulted; nothing enforces it.
|
thepastaclaw
left a comment
There was a problem hiding this comment.
Final validation — Codex + Sonnet
The two former blocking allocation defects are fixed: the registry now records the incompatible and inherited open-PR allocations, and code 30 is consistently assigned to #4185. No blocking issue remains, but the document still contains three procedural or inventory inaccuracies and four smaller provenance or rationale errors that should be corrected before it serves as the authoritative ABI ledger.
Review provenance
- Codex reviewers:
gpt-5.6-sol— general (completed),gpt-5.6-sol— ffi-engineer (completed) - Verifier:
gpt-5.6-sol— verifier (fallback) - Sonnet reviewers:
claude-sonnet-5— general (completed)
🟡 3 suggestion(s) | 💬 1 nitpick(s)
3 carried-forward finding(s) already raised on this PR; not re-posting as new inline comments.
🤖 Prompt for all review comments with AI agents
These findings are from an automated code review. Verify each finding against the current code and only fix it if needed.
In `packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md`:
- [SUGGESTION] packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md:114-119: Do not classify #3968 as a stale-base rebase
The surveyed #3968 head `5931df745a` already contains the registry's exact base `ed4116b26c`: `git merge-base` returns `ed4116b26c`, and merge commit `debf67bdae` brought that base into the branch. Its diff from that base explicitly adds persister codes 26 and 27 and moves `ErrorTransactionBroadcastRejected` from 26 to 28. The section heading, stale-base explanation, and instruction at lines 224-225 therefore prescribe a rebase that has already occurred. The substantive remedy stated elsewhere is correct: #3968 must edit the enum to restore broadcast rejection to 26 and assign fresh values to both persister codes.
- [SUGGESTION] packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md:235-241: List every numeric reference owned by #4196
Comparing #4196's three own commits using `911c8f7264..ea4f783490` shows three changed or added references to native code 26 that must become 27 during the restack: the mapping-test rustdoc in `rs-platform-wallet-ffi/src/error.rs`, the rewritten `StaleReservationToken` KDoc in `DashSdkError.kt`, and the public V2 broadcast KDoc in `ManagedCoreWallet.kt`. Conversely, the `PlatformWalletError::StaleReservation` variant only refers to the FFI code symbolically and contains no number. The current two-reference description can leave public Kotlin boundary documentation advertising code 26 after the runtime mapping moves to 27.
- [SUGGESTION] packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md:98-99: Make the no-new-code PR inventory complete and accurate
The list does not match the open PRs that modify this crate. GitHub's exact PR file lists show that #3417, #3549, #3992, #4243, and #4264 modify `packages/rs-platform-wallet-ffi` without adding a result-code integer, but they are omitted. #4243 and #4264 even modify `error.rs`, intentionally mapping new wallet errors to the existing `ErrorInvalidParameter` code. Conversely, the exact surveyed heads for listed PRs #4240 and #4251 contain no file under this crate. These discrepancies do not affect the next-free integer, but they make the registry's open-PR survey incomplete and inaccurate.
| ### 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. |
There was a problem hiding this comment.
🟡 Suggestion: Do not classify #3968 as a stale-base rebase
The surveyed #3968 head 5931df745a already contains the registry's exact base ed4116b26c: git merge-base returns ed4116b26c, and merge commit debf67bdae brought that base into the branch. Its diff from that base explicitly adds persister codes 26 and 27 and moves ErrorTransactionBroadcastRejected from 26 to 28. The section heading, stale-base explanation, and instruction at lines 224-225 therefore prescribe a rebase that has already occurred. The substantive remedy stated elsewhere is correct: #3968 must edit the enum to restore broadcast rejection to 26 and assign fresh values to both persister codes.
source: ['codex']
| **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. |
There was a problem hiding this comment.
🟡 Suggestion: List every numeric reference owned by #4196
Comparing #4196's three own commits using 911c8f7264..ea4f783490 shows three changed or added references to native code 26 that must become 27 during the restack: the mapping-test rustdoc in rs-platform-wallet-ffi/src/error.rs, the rewritten StaleReservationToken KDoc in DashSdkError.kt, and the public V2 broadcast KDoc in ManagedCoreWallet.kt. Conversely, the PlatformWalletError::StaleReservation variant only refers to the FFI code symbolically and contains no number. The current two-reference description can leave public Kotlin boundary documentation advertising code 26 after the runtime mapping moves to 27.
| **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. | |
| **All three reservation-trio values 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. | |
| The numeric references in #4196's own changed hunks still need explicit updates: | |
| the mapping-test rustdoc in `rs-platform-wallet-ffi/src/error.rs`, the rewritten | |
| `StaleReservationToken` KDoc in `DashSdkError.kt`, and the public V2 broadcast | |
| KDoc in `ManagedCoreWallet.kt` all cite **26** and must become **27**. The | |
| `PlatformWalletError::StaleReservation` variant itself contains no numeric | |
| reference. Thus #4196 allocates nothing, but it must update all three documents | |
| to code 27 during the restack. |
source: ['codex']
| Open PRs that touch `rs-platform-wallet-ffi` but claim **no** new code: #4186, | ||
| #4191, #4194, #4195, #4240, #4251, #4258. |
There was a problem hiding this comment.
🟡 Suggestion: Make the no-new-code PR inventory complete and accurate
The list does not match the open PRs that modify this crate. GitHub's exact PR file lists show that #3417, #3549, #3992, #4243, and #4264 modify packages/rs-platform-wallet-ffi without adding a result-code integer, but they are omitted. #4243 and #4264 even modify error.rs, intentionally mapping new wallet errors to the existing ErrorInvalidParameter code. Conversely, the exact surveyed heads for listed PRs #4240 and #4251 contain no file under this crate. These discrepancies do not affect the next-free integer, but they make the registry's open-PR survey incomplete and inaccurate.
| 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: #3417, | |
| #3549, #3992, #4186, #4191, #4194, #4195, #4243, #4258, #4264. |
source: ['claude', 'codex']
| `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. | ||
|
|
||
| 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 |
There was a problem hiding this comment.
💬 Nitpick: Qualify the code-30 sweep as pre-allocation
The exact surveyed heads later cited for #4185 (6c37e8679e) and #4256 (9481e5783b) both define ErrorReservationWalletMismatch = 30, so the unqualified statement that no open PR defines code 30 is false for the recorded survey. The cited #4247 head 0dcdc743e7 also does not mention ErrorAssetLockCrossDomainConsentRequired; the stale reservation appears on #4183, #4204, and #4256's pre-renumber rationale. The intended conclusion remains valid—no unrelated PR competes with #4185 for 30—but the provenance needs to distinguish the stale historical reservation from the post-allocation surveyed state.
| `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. | |
| 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 | |
| `ErrorAssetLockCrossDomainConsentRequired` is 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. | |
| At the surveyed heads, #4185 and its downstream #4256 define | |
| `ErrorReservationWalletMismatch = 30`; no unrelated open PR defines code 30. | |
| The stale consent-code reservation therefore does not conflict with #4185's | |
| allocation. |
source: ['codex']
Problem
PlatformWalletFFIResultCode(packages/rs-platform-wallet-ffi/src/error.rs) is public ABI —cbindgenemits the integers into the generated C header, Swift mirrors them inPlatformWalletResultCode, and Kotlin branches on them infromPlatformWalletNative. Four open PRs are currently allocating adjacent integers in that enum with no shared source of truth.A duplicate discriminant in two branches produces no textual merge conflict. The second merge just silently misclassifies errors on every host. That is not hypothetical here:
29is already claimed by two PRs, and review on #4185 had to issue a renumber directive (ErrorReservationWalletMismatch29 → 30) to resolve it. Three separate PRs now carry hand-written comments trying to reserve ranges on each other's behalf, and one of those reservations is for a variant that no longer exists.This PR adds a registry so the allocation is decided in one file instead of in four diffs.
What's here
packages/rs-platform-wallet-ffi/ERROR_CODE_REGISTRY.md:Plus a six-line pointer from the crate README's Error Handling section.
Docs-only. Zero code risk. No enum, no
Frommapping, no Swift/Kotlin mirror is touched, and nothing is renumbered — that stays the owning PRs' work. This PR only writes down what the branches already say.Codes claimed by the in-flight PRs
Reconciling in one place, so this is the table to correct:
ErrorStaleReservationTokenErrorReservationTokenConsumedErrorReservationWalletMismatchErrorAssetLockInsufficientFundsErrorSigningKeyUnavailableErrorTransactionBuildErrorTransactionSigningOpen PRs touching this crate that claim no new code: #4186, #4191, #4194, #4195, #4240, #4251, #4258. Next free integer: 34.
rs-sdk-ffi'sDashSDKErrorCodeis a separate integer space and is not modified by any of these PRs.Surveying the branches turned up a second collision that does not appear to have been raised in review.
#4196 branched before
26 = ErrorTransactionBroadcastRejectedmerged tov4.2-dev(in9302c62e8b), and its head numbers the reservation trio 26 / 27 / 28:So as it stands, #4196 would give
26two meanings on merge, and it also contradicts #4185 — the PR it is stacked on — which numbers those same three names 27 / 28 / 29. The fix is a rebase onto currentv4.2-devplus adopting whatever numbering #4185 lands with; no new integers are needed. Flagging rather than fixing, per the docs-only scope.Code 30 is free, despite what three branches say
ErrorAssetLockCrossDomainConsentRequiredis named as the holder of30in in-tree comments on #4183, #4204, and in #4247/#4256's numbering rationale. It is not defined anywhere — #4184, the PR that would have introduced it, dropped it in a re-scope.30is therefore genuinely free, and is the slot the #4185 renumber should take. Those stale comments should be dropped by whichever PR touches them next.Ambiguity left open for maintainers
Review on #4256 suggested mapping its signing failure onto
31; #4256 declined and took33, arguing that31(ErrorSigningKeyUnavailable, #4183) asserts a narrower contract — the signer holds no usable private key for a requested public key — whileBuilderError::SigningFailedalso covers unresolved derivation paths, sighash failures, and malformed signature encodings. Both are currently allocated. Collapsing them is a reasonable call, but it belongs to #4183 and #4256 jointly; the registry records the split as-is rather than pre-judging it.Please correct the table
The merged rows were read straight from
v4.2-devated4116b26c. The proposed rows were read from each PR's head commit (SHAs listed in the doc's provenance section) and will go stale as those PRs are updated. If a maintainer has a different intent for any allocation — particularly30, and the31/33split — please say so on this PR and I'll amend, or edit the file directly. The point is that there is now one place where that correction lands.🤖 Generated with Claude Code