1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
//! The voidable-candidate predicate — the SINGLE shared source of truth for "which reconcile
//! decisions may be swept-voided" (SPEC_bulk_void §Candidate set). Pure: it reads only the events +
//! the projected blockers, so it is reachable by the CLI (`Session::bulk_void_plan` via `project()`)
//! AND the TUI (`open_void_flow` / `open_bulk_void_flow` via the snapshot). There is NO second copy —
//! any drift between the single-void filter and the bulk sweep is a tax-safety bug (the #7 exclusion).
use crate;
use crateEventId;
use crate;
use BTreeSet;
/// Return `true` when `payload` is a revocable decision type.
///
/// Revocable: TransferLink, ReclassifyOutflow, ClassifyInbound, ManualFmv, ClassifyRaw,
/// MethodElection, LotSelection, ReclassifyIncome, SelfTransferPassthrough, SafeHarborAllocation.
/// Non-revocable (excluded from void list): SupersedeImport, RejectImport, VoidDecisionEvent,
/// and imported event payloads (Acquire, Income, Dispose, TransferOut, TransferIn, Unclassified,
/// ImportConflict — these carry Import EventIds, not Decision EventIds, so they cannot appear in
/// the void list; the check on Decision-id'd events guards the decision payload variants only).
/// Enumerate the reconcile **Decision** events that may be voided, applying the exact single-void
/// filter chain (SPEC_bulk_void §Candidate set) over `events` + the projected `blockers`:
///
/// 1. `EventId::Decision { .. }` — only decision events (imported/conflict ids can never be here).
/// 2. NOT already-voided — `e.id` is not the `target_event_id` of any `VoidDecisionEvent`.
/// 3. `is_revocable_payload(&e.payload)` — excludes `SupersedeImport` / `RejectImport` /
/// `VoidDecisionEvent` (a void is never itself voidable; resolve decisions are not swept here).
/// 4. `!effective_alloc` — **the #7 exclusion.** `effective_alloc` = the payload is a
/// `SafeHarborAllocation` AND NEITHER `SafeHarborTimebar` NOR `SafeHarborUnconservable` blocker
/// fired on `e.id`. Engine evidence: unconservable ⟹ blocker (`resolve.rs:989-994`), timebarred ⟹
/// blocker (`resolve.rs:997-1002`), and voiding an EFFECTIVE allocation → Hard `DecisionConflict`
/// (`resolve.rs:1030-1039`) — a permanent, damaging no-op that gates the whole tax year. INERT
/// allocations (timebarred OR unconservable) STAY voidable — the void applies cleanly (source
/// invariant `resolve.rs:1030-1031`; behavior pinned by `btctax-core/tests/transition.rs:403`).
///
/// Returned in `events` iteration order (the pre-sort filter order of the shipped single-void flow);
/// callers sort by `seq` for display.