Skip to content

feat!: align action status taxonomy with reference SDK; fail loudly on broadcast misconfiguration#463

Merged
sgbett merged 9 commits intomasterfrom
feat/455-align-status-taxonomy
Apr 16, 2026
Merged

feat!: align action status taxonomy with reference SDK; fail loudly on broadcast misconfiguration#463
sgbett merged 9 commits intomasterfrom
feat/455-align-status-taxonomy

Conversation

@sgbett
Copy link
Copy Markdown
Owner

@sgbett sgbett commented Apr 16, 2026

Summary

Aligns bsv-wallet's action status taxonomy with the TS reference SDK (wallet-toolbox) and closes the silent-failure mode that caused x402-rack #148 and x402-doom #196.

Before

  • create_action without a broadcaster silently marked actions as 'completed' — nothing on chain, but looked like a successful broadcast.
  • internalize_action unconditionally marked received transactions as 'completed' regardless of whether the BEEF contained a merkle proof.
  • 'completed' conflated four or more distinct states.

After

  • create_action / sign_action raise BSV::Wallet::WalletError when no broadcaster is configured and options: { no_send: true } is not set (fails loud, before any storage writes).
  • Successful broadcast sets status to 'unproven' (on-chain, awaiting proof).
  • 'completed' now means a merkle proof is on file — reserved for internalize_action with a proven BEEF and a future proof-watcher.
  • BroadcastQueue#broadcast_enabled? allows the validation to work with queues that carry their own broadcaster (the documented SolidQueueAdapter pattern).
  • sign_action re-runs the validation on merged args so the no_send cannot be flipped after create_action.

Breaking change

Consumers querying list_actions(status: 'completed') will see fewer results — most fresh broadcasts are now 'unproven' until their proof arrives. x402-rack and x402-doom need to treat 'unproven' as the post-broadcast success state.

See the bsv-wallet CHANGELOG.md entry for full migration notes.

Status taxonomy

Status Meaning
'nosend' Transaction built but not broadcast (no_send: true)
'sending' Broadcast queued (async adapter); worker has not yet attempted broadcast
'unproven' Broadcast succeeded; awaiting merkle proof
'completed' Merkle proof received and stored
'failed' Broadcast attempted and rejected

Test plan

  • 1242 bsv-wallet specs pass (bundle exec rake spec:wallet)
  • 174 bsv-wallet-postgres specs pass (bundle exec rake spec:wallet-postgres; all pending without DATABASE_URL, as expected)
  • 30 bsv-attest specs pass (bundle exec rake spec:attest)
  • RuboCop clean: 101 files, 0 offences (bundle exec rubocop gem/bsv-wallet gem/bsv-wallet-postgres gem/bsv-attest)
  • All 7 sub-issues' acceptance criteria verified against code

Sub-issues

Closes #455, #456, #457, #458, #459, #460, #461, #462

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

sgbett and others added 8 commits April 15, 2026 23:46
Add broadcast_beef method to BSV::Network::ARC that POSTs raw BEEF
bytes to ARC with Content-Type: application/octet-stream. Reuses
build_post_request for headers (auth, deployment ID, callbacks,
wait_for, skip-validation) then overrides Content-Type and sets
binary body. Response handling is identical to broadcast via the
existing handle_broadcast_response path.

This is the foundation for gateway broadcast in x402-rack — both
BRC105Gateway and BRC121Gateway depend on this method.

Ref: sgbett/x402-rack#149

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add 0.9.0 (unreleased) CHANGELOG entry with breaking change marker for the
action status taxonomy realignment (HLR #455). Document the broadcaster
requirement and status table in README and wallet guide.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
`internalize_action` now inspects the BEEF for a BUMP keyed to the
subject transaction's txid. If a proof is present the stored action
status is `'completed'`; if absent it is `'unproven'`. Mirrors the TS
wallet-toolbox behaviour (`provenTx ? 'completed' : 'unproven'`).

`store_proofs_from_beef` is still called unconditionally so any proofs
carried in the BEEF are persisted regardless of the subject-tx status.

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

Add `broadcast_enabled?` to the `BroadcastQueue` module (default false),
with overrides in `InlineQueue` and `SolidQueueAdapter` delegating to
`@broadcaster` presence. `WalletClient#broadcast_enabled?` now delegates
to the queue so queue-embedded broadcasters are recognised correctly.

`validate_broadcast_configuration!` raises `WalletError` in `create_action`
and `sign_action` before any storage writes when broadcast is needed but
unavailable, closing the silent-failure path from #148.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…fallback (#457)

- broadcast_and_promote now passes explicit status: 'unproven' after a
  successful broadcast; 'completed' is reserved for proof-witnessed txs
- promote_without_broadcast raises WalletError unless accept_delayed_broadcast
  is set, removing the silent 'completed' fallback for the no-broadcaster path

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
On broadcast success, promote() now sets wallet_actions.status to
'unproven' (broadcast accepted, pending merkle proof) rather than
'completed'. The wallet_broadcast_jobs row status remains 'completed'
as it reflects the job-queue lifecycle, not the wallet action semantic.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update all wallet specs to reflect the status changes introduced by HLR
#455: broadcaster is now required (no_send: true exempted), successful
broadcast sets status to 'unproven', and 'completed' is reserved for
actions that have received a merkle proof via internalize_action.

Add new specs covering: validate_broadcast_configuration! guard in
create_action and sign_action, internalize_action status ('unproven' vs
'completed' based on BUMP presence), and SolidQueueAdapter job-vs-wallet
status distinction.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
broadcast_beef was added (88799c0) as "foundation for gateway broadcast
in x402-rack" but this is exactly the antipattern x402-rack#158 aims to
remove — gateways should verify on-chain via arc_client.status(txid),
not broadcast. The TS reference SDK has no broadcast_beef method;
consumers with BEEF use Transaction.fromAtomicBEEF() then
broadcaster.broadcast(tx).

Wallets with BEEF should use WalletClient(broadcaster: ARC.default) and
call create_action — the wallet handles broadcast internally. This
matches the pattern we've established across the project and the
correction made to x402-doom.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@sgbett sgbett merged commit d4099d2 into master Apr 16, 2026
3 of 9 checks passed
@sgbett sgbett deleted the feat/455-align-status-taxonomy branch April 21, 2026 01:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[HLR] Align action status taxonomy with reference SDK; fail loudly on broadcast misconfiguration

1 participant