feat(bond): cause-aware bond-slashed handling (dispute vs timeout)#619
Conversation
- Add pure bondSlashCause(): dispute when the history has a dispute/admin resolution, timeout otherwise - Add orderBondWasSlashed() to detect a bond-slashed notice in the history
- Infer the slash cause (timeout vs dispute) from order history at notification creation and persist it - Select the message and detail variants by cause in the mapper and the forfeiture dialog - Add dispute-variant strings in en/es/it/de/fr
- Add a durable order-details line shown only when the bond was slashed by a dispute resolution - Read the amount from the bond-slashed message via slashedBondAmount() - Timeout slashes show nothing here; add the localized notice in en/es/it/de/fr
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
WalkthroughAdds ChangesBond-slash dispute differentiation
Sequence Diagram(s)sequenceDiagram
participant MostroEvent as Mostro Event
participant Extractor as NotificationDataExtractor
participant Helpers as bond_slash_helpers
participant Storage as OrderMessageStorage
participant Mapper as NotificationMessageMapper
participant UI as NotificationItem / MostroMessageDetail
MostroEvent->>Extractor: Action.bondSlashed received
Extractor->>Storage: fetchMessages(orderId)
Storage-->>Extractor: List<MostroMessage>
Extractor->>Helpers: bondSlashCause(messages)
Helpers-->>Extractor: BondSlashCause.timeout | .dispute
Extractor-->>Mapper: values{slash_cause, amount, order_id, ...}
Mapper->>Mapper: slash_cause == 'dispute'?
alt dispute
Mapper-->>UI: notification_bond_slashed_dispute_message
UI->>UI: show dispute-specific detail dialog
else timeout
Mapper-->>UI: notification_bond_slashed_message
UI->>UI: show generic timeout detail dialog
end
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested reviewers
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 2
🧹 Nitpick comments (1)
test/shared/utils/bond_slash_helpers_test.dart (1)
24-79: ⚡ Quick winAdd regression coverage for legacy and mixed history markers
The suite doesn’t currently lock behavior for legacy dispute/admin actions (
Action.dispute,Action.adminSettle,Action.adminCancel) or a mixedcanceled + disputehistory. Adding those cases will prevent silent drift in slash classification and timeout deletion behavior.Suggested test additions
+ test('dispute for legacy dispute/admin actions', () { + expect(bondSlashCause([_msg(Action.dispute)]), BondSlashCause.dispute); + expect( + bondSlashCause([_msg(Action.adminSettle)]), + BondSlashCause.dispute, + ); + expect( + bondSlashCause([_msg(Action.adminCancel)]), + BondSlashCause.dispute, + ); + }); @@ + test('false when canceled and dispute markers coexist', () { + expect( + bondSlashIsTimeout([ + _msg(Action.canceled), + _msg(Action.disputeInitiatedByPeer), + _bondSlashedMsg(300), + ]), + isFalse, + ); + });Also applies to: 107-145
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@test/shared/utils/bond_slash_helpers_test.dart` around lines 24 - 79, The bondSlashCause test group is missing regression coverage for legacy action types and mixed history scenarios. Add new test cases within the bondSlashCause group to cover legacy Action types such as Action.dispute, Action.adminSettle, and Action.adminCancel, as well as mixed history patterns combining canceled with dispute-related actions. This will prevent silent drift in slash classification behavior for these edge cases.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@lib/features/order/notifiers/abstract_mostro_notifier.dart`:
- Around line 270-280: The session deletion logic has two issues: the
_bondCancelDeletionTimers.remove(orderId)?.cancel() call occurs before
validating the message history, which can strand timeout sessions if the history
read fails, and the bondSlashIsTimeout() check doesn't verify that dispute or
admin actions don't exist in the bondSlashMessages. Move the timer cancellation
to after the getAllMessagesForOrderId() call succeeds, and update the deletion
condition to ensure the session is only deleted when bondSlashIsTimeout()
returns true AND the bondSlashMessages contain no dispute or admin actions that
would indicate a terminal trade that should be preserved.
In `@lib/shared/utils/bond_slash_helpers.dart`:
- Around line 17-22: The _disputeActions set on line 17 is incomplete and
missing the base action variants Action.dispute, Action.adminSettle, and
Action.adminCancel, which should be included alongside the existing variant
actions. Additionally, the timeout detection logic around line 40-41 currently
classifies any Action.canceled as a timeout without checking if dispute markers
also exist, which can misclassify dispute slashes. Fix this by adding the
missing base action variants to the _disputeActions set, and modify the timeout
detection logic to ensure it only classifies an action as a timeout when no
dispute markers are present, preventing incorrect classification of
dispute-related actions as simple timeouts.
---
Nitpick comments:
In `@test/shared/utils/bond_slash_helpers_test.dart`:
- Around line 24-79: The bondSlashCause test group is missing regression
coverage for legacy action types and mixed history scenarios. Add new test cases
within the bondSlashCause group to cover legacy Action types such as
Action.dispute, Action.adminSettle, and Action.adminCancel, as well as mixed
history patterns combining canceled with dispute-related actions. This will
prevent silent drift in slash classification behavior for these edge cases.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 21d47ee8-136e-43e5-a145-d8c6a5d1818e
📒 Files selected for processing (13)
docs/architecture/ANTI_ABUSE_BOND.mdlib/features/notifications/utils/notification_data_extractor.dartlib/features/notifications/utils/notification_message_mapper.dartlib/features/notifications/widgets/notification_item.dartlib/features/order/notifiers/abstract_mostro_notifier.dartlib/features/trades/screens/trade_detail_screen.dartlib/l10n/intl_de.arblib/l10n/intl_en.arblib/l10n/intl_es.arblib/l10n/intl_fr.arblib/l10n/intl_it.arblib/shared/utils/bond_slash_helpers.darttest/shared/utils/bond_slash_helpers_test.dart
There was a problem hiding this comment.
I like the direction of this PR, but I do see one real correctness issue that I would block on before merge.
Blocking issue:
- In
notification_data_extractor.dart, the slash-cause inference reads history withgetAllMessagesForOrderId(event.id!). - But in that same branch the canonical order identity is coming from the payload (
order.id), and the extractor later persistsorder_id: order.idinto the notification data. - If
event.idhere is the incoming message/event identifier rather than the Mostro order id, the history lookup can miss the actual order history or read the wrong one, and the fallback path will silently persistslash_cause = timeoutfor a real dispute slash.
Why this matters:
- The inferred cause is persisted into the notification and later drives which copy the user sees.
- More importantly, this PR is explicitly about making timeout vs dispute handling cause-aware, so anchoring the inference on the wrong identifier would undermine the core contract of the feature.
- Because the fallback is
timeout, this would fail in the misleading direction instead of failing loudly.
What I would want:
- Use the actual order id from the payload for the history lookup in this path, not
event.id. - In other words, the slash-cause inference should be keyed on the same durable order identity that the notification stores as
order_id.
Aside from that, the helper extraction and the timeout-vs-dispute session handling direction look good to me.
|
I dug into this and On the wire the envelope The rest of the notifier already treats That said, your readability point stands: I've switched the lookup to |
There was a problem hiding this comment.
I re-checked your comment and the underlying model, and you are right: the blocker I raised does not hold under the actual semantics here.
What changed my mind:
- In this path
eventis aMostroMessage, not the outer Nostr gift-wrap event. MostroMessage.idis the Mostro order id, and that is the same UUID carried in the payloadorder.idfor thisbond-slashednotice.- So the history lookup is already anchored on the correct durable order identity, not on a separate transport-level event id.
That means my previous concern about silently reading the wrong history and defaulting a real dispute slash to timeout was based on the wrong identifier model.
I still agree with your readability point that order.id is more self-documenting in this branch, but that is not a correctness blocker on the current head.
I re-checked the rest of the PR and I do not see another blocking issue. The cause-aware notification copy, dispute-vs-timeout session handling, and dispute-only order-details notice all look coherent with the inferred-history model.
Approved on the current revision.
…l card - Render the message in MostroMessageDetail instead of the bond-slashed fallback - Read the amount from the bond-slashed message; blank while history loads - Remove the redundant _buildBondLostDisputeNotice in trade_detail_screen
There was a problem hiding this comment.
I re-reviewed the new head and I do not see a new blocking issue.
What I checked on the latest commit:
- The new UI move from the old trade-detail fallback into
MostroMessageDetaildoes not appear to change the underlying cause inference model or the timeout-vs-dispute session behavior. - The dispute-only rendering still depends on the same history-derived slash classification and slashed-amount extraction, so this looks like presentation cleanup rather than a semantic change.
- I do not see a regression that would justify reopening a blocker on the current revision.
Approved on the latest head.
The daemon sends the same
bond-slashednotice for timeout and dispute-resolution slashes (identical payload, no reason field). The client assumed timeout always, causing two issues, both fixed here:Changes
bond_slash_helpers.dart).with the amount from the message.
bond-slasheddeletes the session only on timeout; a dispute slash keeps it(trade stays in My Trades with its role intact).
How to test manually
apply_to = both) and take/create a bonded order.slash_on_waiting_timeout = true). Expect: notification says you didn't respond in time; the order is removed from My Trades; no line in order details.Summary by CodeRabbit
New Features
Documentation
Tests