Home / Work / Kestrel Ops / Test report
Proof project · concept, not client work
Kestrel Ops test report
Every check below is an automated test that calls the real API and then verifies the database. Plain-English names first; the technical detail is there if you want it.
Environment: local Wrangler with Cloudflare D1 emulation, synthetic data. Concurrency tests inject a change between a read and a write to prove stale actions are refused; they are not production load tests. This is not a security certification.
Reconciliation checks
35 rules · greenRun on the seeded data and again after the scenarios. Each rule must hold for the books to be considered correct.
- Every ledger transaction balances to zero
- No client wallet is negative
- Clearing account equals in-flight withdrawals
- Each withdrawal's reservation matches its status
- Each settled withdrawal has exactly one payment evidence (provider success or adjudication) for its amount
- Each settled withdrawal has a settlement entry for its exact amount
- Each payment confirmed after a reversal has one adjudication and a recovery entry for its amount
- No unsettled withdrawal carries settlement evidence
- Each partner's wallet matches its commission records, trade by trade
- Commission expense equals commission records
- Every commission line matches the agreement in force at trade close (payee, level, rate, rounding)
- Every processed trade pays exactly the partners entitled to it (attribution, tree at that version, lots, agreement)
- Cancelled trades net to zero commission
- No trade paid twice at the same level
- Partner tree has no loops
- No unresolved conflicting provider events
- No orphaned provider events
- Every received payment is posted exactly once for its received amount
- Unmatched money sits in suspense; matched money has left it
- Restricted deposits are held; available deposits were credited to the wallet once
- Each deposit instruction's paid amount equals the payments matched to it
- Refunds and chargebacks each stay within the payment, and refund records match
- Each chargeback's postings match its record (provider, wallet, receivable, fee)
- No conflicting payment events waiting for review
- Funds reserved for transfers equal transfers not yet final
- Each trading account's platform balance equals the transfers the CRM confirmed
- No transfer with an unknown outcome
- Every received payment event has been applied (no pending or rejected chargebacks)
- Every open chargeback on an allocated payment names that payment's client
- No trading account is overdrawn on the platform or below its margin in use
- Each transfer's status matches its single platform outcome (applied or cancelled)
- Each conversion links to one existing client, and no client is linked to two active leads
- Every lead is owned by active sales staff (or unassigned) and matches its latest assignment history
- Every unresolved provider report has an open case, and every open case still has unresolved work
- No unresolved success reports waiting for review
Withdrawals and approvals
59 / 59 passedMaker-checker, second approvals, stale versions, overdraw and duplicate-send protection.
- ✓
Start demo signs in as Finance Officer (Omar)
- ✓
Seed data reconciles
- ✓
1 Maker cannot approve own withdrawal
Technical detail
Maker-checker rule: you created or changed this withdrawal, so you cannot approve it. - ✓
1b Denied attempt is in the audit log
- ✓
2 Support cannot approve
- ✓
3 Admin cannot approve (segregation of duties)
- ✓
4 Approval for stale version rejected
- ✓
4b No approval stored
- ✓
Checker (Grace) approves
Technical detail
{"ok":true,"status":"approved","required":1} - ✓
5 Edit after approval → back to pending, version 2
- ✓
5b Old approval invalidated
- ✓
5c Ledger re-reserved and reconciles
- ✓
5d Cannot send after change without new approval
- ✓
5e Grace can re-approve v2 (she did not edit it)
- ✓
6 First approval on $12,500 keeps it pending
- ✓
6b Same approver twice denied
- ✓
6c Second independent approver completes approval
- ✓
7 Send with duplicate callback
Technical detail
{"ok":true,"outcome":"success","attempt":1,"callback":"settled","duplicate":"duplicate_ignored"} - ✓
7b Exactly one settlement debit
- ✓
8 Failed payout enters exception queue
- ✓
8b Retry succeeds → settled
- ✓
8c Reverse returns funds to wallet
- ✓
9 Concurrent double-approve stores one approval
Technical detail
200/409 - ✓
9b Concurrent double-send pays out once
Technical detail
200/409 - ✓
10 Overdraw attempt denied ($2,000 from a $1,500 wallet)
Technical detail
Insufficient available balance for this withdrawal. - ✓
11 Workspace B sees its own fresh data (WD-1002 still pending)
- ✓
11b Workspace B cannot touch workspace A's new withdrawal
- ✓
12 POST with wrong CSRF token rejected
- ✓
12b Old session revoked after switch
- ✓
Compliance releases hold with note
- ✓
14 Reconciliation green after all scenarios
- ✓
Audit CSV export works
- ✓
R1 Concurrent edits cannot overdraw: exactly one succeeds
Technical detail
200/409 - ✓
R1b Wallet never negative after edit race
Technical detail
$3550.00 - ✓
R2 Edit/create race leaves wallet non-negative
Technical detail
200/409 wallet $710.00 - ✓
R3 Support cannot simulate client requests (no reservation)
- ✓
R4 Concurrent creates get distinct references
Technical detail
WD-1009 / WD-1010 - ✓
R4b Retried create with the same request_id returns the original
Technical detail
WD-1011 / WD-1011 - ✓
R5 Injected failure: send committed, event rolled back, outcome reported as uncertain
Technical detail
500 sent events=0 - ✓
R5b Replay settles exactly once
- ✓
R6 Late success for attempt 1 does not settle attempt 2
Technical detail
unmatched_needs_review sent - ✓
R6b Reconciliation flags the unapplied success for review
- ✓
R6c With two success reports open (possible double payout), attempt 2 is also held for review
Technical detail
unmatched_needs_review - ✓
R6c2 Compliance resolves both reports; withdrawal settles exactly once
- ✓
R6d Callback with wrong amount is not applied
Technical detail
stale_ignored - ✓
R7 KYC change auto-holds the approved withdrawal and voids approvals
Technical detail
on_hold - ✓
R7b Send is refused while KYC is not current
- ✓
R7c A hold release cannot waive KYC
- ✓
R7d After KYC renewal, review and fresh approval, payout succeeds
- ✓
R8 Reassigned confirmation turns reconciliation red with specific findings
Technical detail
Missing or conflicting: WD-1001 | WD-1002 - ✓
R9 Database rejects UPDATE and DELETE on audit
Technical detail
{"audit_update":"blocked","audit_delete":"blocked","ledger_update":"blocked","ledger_delete":"blocked"} - ✓
R9b Database rejects UPDATE and DELETE on ledger
- ✓
B1 Beneficiary changes between validation and commit: nothing is sent
Technical detail
409 approved - ✓
B1b The changed instructions need fresh approval before sending
Technical detail
403 - ✓
D1 Impossible calendar dates are refused
Technical detail
{"error":"That date does not exist. Choose a real calendar date."} - ✓
R1 Every failing check carries a stable id and its own items (unbalanced ledger links to the withdrawal with a next step)
Technical detail
ledger_balanced:1,case_consistency:4,pe_unmatched:5 - ✓
R1b No reconciliation advice tells the operator to void and re-run
- ✓
AP1 After my first of two approvals the item leaves my 'Needs my approval' list but stays for the other checker
Technical detail
true/false/true - ✓
R1 Support cannot move funds, refund or create deposit instructions
Payment provider outcomes and investigations
72 / 72 passedLate, duplicate, conflicting and wrong-amount provider reports; cases opened and closed safely.
- ✓
X1 Late success after failure is held for review
- ✓
X1b Retry is refused while a success report is unresolved
Technical detail
403 - ✓
X1c Only Compliance can resolve provider events
- ✓
X1d Confirmed-paid resolution settles once with an audit record
- ✓
X2 Same callback ID with different content is flagged as a conflict, not a duplicate
Technical detail
conflict_needs_review - ✓
X2b Reversal blocked while the conflict is unresolved
- ✓
X2c Retry blocked while the conflict is unresolved
- ✓
X2d Reconciliation shows the open conflict
- ✓
X2e After a recorded 'not paid' decision, reversal proceeds
- ✓
X2f Reconciliation green after both investigations
- ✓
X3 Same request key with a different amount is refused
Technical detail
200/409 - ✓
X3b Invalid request key is rejected, not ignored
- ✓
C1a Conflict recorded first: reversal refused, withdrawal stays in exception
Technical detail
conflict_needs_review/409 → exception - ✓
C1b Late report after a committed reversal: reversal kept in history, open recovery case recorded
Technical detail
200/conflict_needs_review → reversed - ✓
C1c New withdrawals for that client are paused while the recovery case is open
Technical detail
409 - ✓
C1d Reconciliation flags the open post-reversal case
- ✓
C1e Authorized recovery: status 'paid after reversal', reversal credit recovered, adjudication stored
Technical detail
{"ok":true,"decision":"paid","resolution":"confirmed_paid_after_reversal"} - ✓
C1f Reconciliation green after the recovery
Technical detail
No unresolved conflicting provider events: Same event ID, different content: cb_w_268696067c714ed9_1 → WD-1006 - ✓
C2 Two concurrent investigation decisions: exactly one commits
Technical detail
200/409 - ✓
C3 Conflict confirmed paid: settled once, original messages preserved
- ✓
C3b Reconciliation green with adjudicated settlements
- ✓
C4 Opposing concurrent moves: one fails cleanly, no loop
Technical detail
200/409 - ✓
C5 Agreement effective today is refused (would touch same-day trades)
- ✓
C6 A zero-rounded commission is recorded and does not strand later trades
Technical detail
{"ok":true,"processed":2,"rows":5,"amount":1075} - ✓
C6b Reconciliation green after zero-value commission
- ✓
C7 Misallocated partner balance turns reconciliation red with the partner named
- ✓
V1 $1 reported against $300: a $1 verification is refused, and $300 needs an evidenced override
Technical detail
422/422 - ✓
V1b With evidence the override is recorded: expected, reported and verified amounts kept
- ✓
V1c A decided case cannot be decided again
- ✓
E0 Reconciliation green before the payee fixture
- ✓
E1 Wrong payee with valid rate and balanced ledger is detected by entitlement rebuild
Technical detail
1 trade(s): T-5043: expected ib_atlas L1; found unexpected ib_nova L1 - ✓
E1b Wallet-vs-records check alone would not catch it (records agree)
- ✓
K1 Identical conflicting report delivered twice: one investigation case
Technical detail
conflict_needs_review/conflict_duplicate_ignored cases 1 - ✓
P1 While the recovery case is open, an already-approved payout for the same client is blocked
Technical detail
409 - ✓
P1b Increases are blocked too
Technical detail
409 - ✓
K2 Same report after the decision: refers to the decision, no new case, no second financial effect
Technical detail
conflict_already_decided cases 1 - ✓
P1c After the decision, the paused payout can go
Technical detail
200 - ✓
I1 A report that needs a decision opens a case automatically with a 24-hour deadline
- ✓
I1b Reconciliation exception links straight to the withdrawal with the difference and next step
Technical detail
{"label":"WD-1006 · cb_case_n1","href":"#/withdrawal/w_1a966622c9d04ccf","diff":"Provider reported $275.00 paid; withdrawal is $275.00","next":"Compliance records what the bank or provider statement shows (Investigations)."} - ✓
I2 Support can add an internal note but cannot take the case
Technical detail
200/403 - ✓
I3 Compliance takes the case, records structured evidence and escalation (deadline within 4h)
- ✓
I3b Evidence without a source and reference is refused
- ✓
I4 The decision closes the case and is kept in its history; queue moves it to Closed
- ✓
I5 A closed case cannot be changed
- ✓
M1 Two distinct conflicting reports open two decisions
Technical detail
2 - ✓
M1b After the first decision the second report keeps all client payouts paused
Technical detail
200/409 - ✓
M1c Second report: 'paid again' refused, wrong reference refused, linked to the confirmed payment with no second effect
Technical detail
409/400/200 - ✓
M1d Only after every report is decided do payouts resume; reconciliation green
Technical detail
200 - ✓
DB1 Dashboard 'Paid out' includes the payment confirmed after reversal
Technical detail
{"paid0":180000,"paid1":{"settled":190000,"after_reversal":30000,"total":220000}} - ✓
CA-note Case closes between read and write: 409, no history entry, no successful audit row
Technical detail
409 entries=opened okAudit=0 - ✓
CA-evidence Case closes between read and write: 409, no history entry, no successful audit row
Technical detail
409 entries=opened okAudit=0 - ✓
CA-assign Case closes between read and write: 409, no history entry, no successful audit row
Technical detail
409 entries=opened okAudit=0 - ✓
CA-escalate Case closes between read and write: 409, no history entry, no successful audit row
Technical detail
409 entries=opened okAudit=0 - ✓
CL1 Case creation fails → the report is rolled back too; the replay records report and case together
Technical detail
500 unmatched_needs_review - ✓
CL3 A second report while the case is open is added to the same case
- ✓
CL2 Closing fails → the decision is rolled back too; the retry decides and closes exactly once
Technical detail
500/200 - ✓
H1 A new report opens a second case; both cases keep their own history and stay reachable
- ✓
CL4 Case consistency check: the normal lifecycle (WD-1010) is consistent, and the four cases force-closed by the race hook are flagged as reports without a case
Technical detail
["WD-1006:Unresolved provider report without an open case","WD-1007:Unresolved provider report without an open case","WD-1008:Unresolved provider report without an open case","WD-1009:Unresolved provider report without an open case"] - ✓
SC1 A dialog prepared for case A (now closed) is refused after case B opens; neither history changes
Technical detail
409 [1,3] - ✓
SC2 Missing case id, missing/null/string version refused (400), stale version refused (409), a fresh submission for case B succeeds; refused requests leave no history
Technical detail
400/409/200 - ✓
E2 Every entitlement failure branch returns its own item with link, difference and next step (no inputs, attribution, lots, chain)
Technical detail
["QA-MIRROR-914224:1 reversal line(s) vs 1 accrual line(s),","QA-NOCALC-392333:Marked processed, but the inputs used to","QA-ATTR-589415:Calculated for partner ib_nova; the clie","QA-ATTR-589415:Expected ib_nova L1 $7.00","QA-LOTS-238029:Calculated on 2.000 lots; the trade has ","QA-LOTS-238029:Expected ib_atlas L1 $9.00","QA-CHAIN-832673:Recorded chain ib_atlas → ib_nova; the t","QA-CHAIN-832673:Expected ib_atlas L1 $9.00"] - ✓
C1 Chargeback after funds left the wallet: wallet debited to zero, shortfall recorded as receivable, client restricted
Technical detail
chargeback_recorded wallet=0 recv=-79900 - ✓
C1b While the chargeback is open, transfers and withdrawals for the client are refused
Technical detail
409/409 - ✓
C2 Chargeback won: one compensating posting restores wallet and receivable; a repeated close changes nothing
Technical detail
chargeback_won/already_closed_ignored - ✓
C3 A bank-forced chargeback after a refund is recorded (the provider takes it) and flagged as overlapping the refund; the client owes the shortfall
Technical detail
200 chargeback_recorded - ✓
C3b A further chargeback beyond what is left on the payment is not applied; it is recorded as rejected for review
Technical detail
rejected_exceeds_payment - ✓
H1 A close naming a different payment cannot resolve the dispute: kept as a conflict, dispute open, wallet and audit unchanged
Technical detail
conflict_needs_review open - ✓
H1b A repeated 'opened' with the same dispute id but different content is a conflict; identical content is a duplicate; nothing is posted twice
Technical detail
conflict_needs_review/duplicate_dispute_ignored - ✓
H1c The genuine close for the right payment still applies
Technical detail
chargeback_won - ✓
H2 Dispute ids that look alike under SQL wildcards (dp_a, dpXa): exactly two chargeback rows totalling $20, no false reconciliation failure
Technical detail
2 rows 2000 - ✓
H2b Regression workspace reconciles except the deliberately conflicting events from H1/H1b
Technical detail
fund_conflicts - ✓
H3 Funding report counts a payment confirmed after reversal once, as an external withdrawal (same classification as the dashboard)
Technical detail
report 224500 vs dashboard 224500 (after reversal 21500)
Deposits, refunds and chargebacks
12 / 12 passedInstructions, matching, restricted funds, refunds, chargebacks and fees.
- ✓
F1 Payment matching the instruction is credited once for the received amount; fee booked separately
Technical detail
credited_available - ✓
F2 Same event q_again, a new event id for the same payment, and a changed amount: never a second credit
Technical detail
duplicate_event_ignored/same_payment_already_recorded/conflict_needs_review - ✓
F2b Reconciliation flags the conflicting payment event with a drill-down
- ✓
F3 Partial payments: two receipts complete one instruction; the overpayment waits in suspense instead of being allocated twice
Technical detail
credited_available/held_in_suspense/credited_available paid=100000 - ✓
F4 Suspense allocation needs a second person; it moves out of suspense exactly once
Technical detail
403/200/409 - ✓
F5 KYC not current: payment is recorded but held as restricted, not added to the usable wallet
Technical detail
credited_restricted - ✓
F5b Release is refused while KYC is not current (checked inside the transaction)
- ✓
F5c KYC expires between the check and the credit: nothing is released
Technical detail
409 - ✓
F5d After KYC approval the restricted funds are released once
Technical detail
200 - ✓
F6 Two concurrent refunds that together exceed the payment: exactly one applies
Technical detail
200/409 - ✓
F9 After the whole journey, reconciliation is green except the deliberately conflicting event and the rejected over-limit chargeback
Technical detail
fund_conflicts: 1 conflicting event(s) | fund_pending: 1 event(s) not applied - ✓
F10 Funding report: published formula, internal transfers excluded, totals traceable to lines
Trading-account transfers
15 / 15 passedWallet to trading account and back, with unknown outcomes and concurrent requests.
- ✓
G1 Two concurrent $80 transfers back from a $100 trading account: exactly one succeeds, the account is never overdrawn
Technical detail
200:confirmed / 409:Transfer refused: free margin on 5101207 is $20.00 (balance $20.00, margin in use $0.00). plat=2000 - ✓
G1b With the early check skipped, the platform itself refuses a debit beyond free margin in the same statement; reconciliation checks platform validity
Technical detail
failed plat=2000 - ✓
G2 Platform applied, response lost, retry answered 'reject': the applied outcome wins; confirmed, funds stay on the platform, nothing returned to the wallet
Technical detail
confirmed wallet=-10000 - ✓
G3 A retry lands on the platform just before a release: the release is refused and the transfer is confirmed, never both
Technical detail
409/confirmed - ✓
G3b Concurrent retry and release of the same operation: exactly one authoritative outcome (applied+confirmed or cancelled+released)
Technical detail
200/409 → confirmed - ✓
G4 After a chargeback opens, a retry that could create a new platform effect is refused; checking the platform is still allowed
Technical detail
409 Retry blocked: open chargeback. A retry could apply a new movement on the platform. You can still check the platform; release the reserved funds once the platform confirms cancellation. - ✓
G4b KYC expires between reservation and dispatch: the platform call is refused, the operation is cancelled and the reservation returned
Technical detail
failed - ✓
G5 Chargeback before its payment stays pending (flagged, replay retries instead of ignoring) and applies when the payment arrives
Technical detail
pending_payment_not_received/pending_payment_not_received/duplicate_event_ignored - ✓
G6 Wallet emptied concurrently with a chargeback: recovery and shortfall are computed inside the transaction; the chargeback still applies and restricts the client
Technical detail
chargeback_recorded 0/30000 - ✓
G7 'Closed' arriving before 'opened' stays pending, then applies right after the dispute is recorded
Technical detail
pending_dispute_not_open → won - ✓
G8 Suspense → chargeback → allocation to c7: the open chargeback restricts c7 at once; when won, $50 returns to client:c7 (never client:null)
Technical detail
chargeback_won won_to=client:c7 Δ=10000 - ✓
G8b Same path to a restricted client (lost): the hold keeps only the allocated part, the chargeback names c4, and the database rejects postings to a non-existent client account
Technical detail
chargeback_lost hold=6000 sql=rejected - ✓
G9 A rejection interleaved before the approval batch: approval fails and the instruction is NOT marked paid (claim first, everything else conditional)
Technical detail
409 paid=0 open - ✓
G10 Report by posting date: a chargeback stays in its period after it is later won; the recovery appears in the period it was posted
Technical detail
p1={"gross_deposits":50000,"refunds":0,"chargebacks":20000,"chargeback_recoveries":0,"external_withdrawals":0,"fees":1500,"internal_to_accounts":0,"internal_from_accounts":0,"net_external_funding":30000} p2 rec=20000 - ✓
G11 Reconciliation of the regression workspace is green after all reproductions (detection is not the only control: each case was prevented)
Partner commissions
26 / 26 passedAgreement versions, partner trees, rounding and voided trades.
- ✓
IB1 Seeded trades processed on start (no unprocessed left)
- ✓
IB2 Re-running the calculation pays nothing twice
- ✓
IB3 Every Atlas statement line reproduces from agreement, level and rounding
Technical detail
28/28 - ✓
IB3b Atlas level-1 lines use v1 or v2 depending on trade close date
- ✓
IB4 A level-3 client pays three partners (L1 Coastal, L2 Brightline, L3 Atlas)
Technical detail
ib_coast:L1,ib_bright:L2,ib_atlas:L3 - ✓
IB5 Support cannot run commissions
- ✓
IB6 Moving a partner under its own sub-partner is refused (cycle)
- ✓
IB7 Backdated agreement refused
- ✓
IB8 New agreement version leaves past statement lines unchanged
Technical detail
{"ok":true,"version":2} - ✓
IB9 Cancelling a trade reverses its commissions
- ✓
IB9b Cancelling twice is refused
- ✓
IB10 Concurrent calculation runs process each new trade once
Technical detail
4+0 - ✓
IB10b Reconciliation green including IB checks
- ✓
T1 Partner move right after the snapshot: the trade is not paid on a mixed tree, run reports the interruption
Technical detail
{"ok":true,"processed":0,"rows":0,"amount":0,"stopped":"Partner tree changed during the run; remaining trades will use the new tree on the next run."} | - ✓
T1b Next run pays the new chain (Brightline → Nova) and entitlement reconciles
Technical detail
{"ok":true,"processed":1,"rows":2,"amount":750} | - ✓
Z1 Voiding a trade with mixed $0.00 and non-zero commissions keeps reconciliation green
Technical detail
{"ok":true,"reversed":2,"ledger_lines":1} | - ✓
T1 Transfer to a trading account: reserved, applied once on the platform, confirmed
Technical detail
{"ok":true,"id":"tf_58135cf2a626442b","status":"confirmed"} - ✓
T2 Platform credits but the response is lost: transfer is 'uncertain', funds stay reserved, nothing is refunded
Technical detail
uncertain - ✓
T2b Reconciliation shows the uncertain transfer and the platform/CRM difference with drill-down
- ✓
T3 Retry of an operation the platform already applied: confirmed without a second platform effect, even if the platform now answers 'reject'
Technical detail
confirmed plat=100000 - ✓
T4 Releasing funds is refused when the platform shows the operation; the transfer can only be confirmed
Technical detail
409/409 - ✓
T5 Platform rejects: transfer failed and funds return to the wallet
Technical detail
failed - ✓
T6 Timeout without effect: the check finds nothing; release with a recorded confirmation returns the funds
Technical detail
false/failed - ✓
T7 Transfer back is limited by the platform's free margin; a covered amount returns to the wallet
Technical detail
409/confirmed - ✓
T8 Demo accounts cannot receive real funds
- ✓
T9 Two concurrent transfers that together exceed the wallet: exactly one reserves funds
Technical detail
200/409
Sales CRM
32 / 32 passedRow access, duplicates, contact rules, opt-outs, merges, exports and first-deposit reporting.
- ✓
S1 Agent sees only their own book; another agent's lead is 'not found'; client money, dashboard, ledger/lead exports and transfers are refused
Technical detail
5/404/403/403/403/403/403 - ✓
S1b Only the explicit export role (Operations Admin) can export leads: fixed columns without email/phone, audited with scope, columns and row count (no personal data in the audit)
Technical detail
200 - ✓
S2 Versioned assignment rules: Arabic → Arabic desk (rule r1 v1); no matching rule or missing country/language → unassigned queue with the reason; a disabled owner is skipped (unassigned)
Technical detail
Rule r1 v1 (language = ar) | No rule matched (language it, country IT): unassigned queue | Matching rule's owner is disabled: unassigned queue - ✓
S3 Duplicate review proposes, never decides: same email (case-insensitive) + same E.164 phone = strong; a phone without country context is not matched; agents outside both leads never see the item
Technical detail
Same email · Same phone / 0 / sara sees=false - ✓
S4 Reviewed merge: proposer cannot approve; survivor keeps both histories, the most restrictive preference (do-not-contact) survives, and the alias's queued message is cancelled
Technical detail
200/403/200 dnc=1 - ✓
S4b Merge approval is bound to both lead versions: a lead that changed after the proposal makes approval fail (nothing merged)
Technical detail
409 - ✓
S5 Concurrent assignment: exactly one applies; history is append-only; non-sales staff cannot own leads; an agent disabled between selection and assignment is refused
Technical detail
200/409 rejected 409:That agent is disabled or not sales staff. Choose an active agent. 409:That agent is disabled or not sales staff. Choose an active agent. last=[null,"u_sara"] owner=u_sara - ✓
S6 Reassignment revokes the former agent's access on the next request (lead and its follow-up), and open follow-ups move to the new owner
Technical detail
404/404 owner=u_sara - ✓
S7 'Contacted' means an actual two-way interaction: an unanswered call leaves the stage at new; a reached call sets contacted; a manual 'contacted' edit is refused
Technical detail
new → contacted / 400 - ✓
S8 History is never hidden: a contact that happened outside policy (do-not-contact) is recorded and flagged (activity + flagged audit), not refused
Technical detail
200 - ✓
S9 Outbound requests: blocked at request when suppressed; a dispatch timeout retried with the same key never duplicates; delivery is shown as simulated, never 'sent'
Technical detail
blocked/504/true/simulated_delivered - ✓
S9b Second check at dispatch: an SMS scheduled before the opt-out is blocked at dispatch; a call requested by the former owner is cancelled after reassignment
Technical detail
blocked/cancelled - ✓
S9c Quiet hours use the lead's IANA time zone across the DST change (13:30 UTC = 09:30 EDT allowed; next day 13:30 UTC = 08:30 EST deferred); unknown time zone defers calls for review
Technical detail
deferred/simulated_delivered/deferred - ✓
S10 Conversion: refused while a duplicate review is open; a double-click with the same request id creates exactly one client (KYC pending, money restricted)
Technical detail
409/200/200 created - ✓
S10b Response lost after commit: retry with the same request id returns the same client; only a team lead may link an existing client, and one client cannot be linked to two leads
Technical detail
504/200/403/200/409 - ✓
S11 FTD follows the published rule (ftd-v1) from ledger postings: date, amount and receipt recorded; after a full chargeback the FTD stays, a reversal indicator appears, net funding is $0 and the sales stage is untouched
Technical detail
true/chargeback/historically_qualified/qualified - ✓
S12 Reassignment after the FTD does not move historical credit: the activity report credits the FTD to the owner at that moment (Sara), not the new owner (Karim); agents see only their own credit
Technical detail
nina sara=1 karim=0 sara=1 - ✓
S12b Two report views with defined denominators: the cohort view publishes a conversion rate (cohort converted ÷ cohort leads) with an as-of date; the activity view has no conversion rate
Technical detail
33.3% - ✓
S13 A restricted deposit (KYC pending) does not count as FTD; it qualifies when the funds are released into the usable wallet
Technical detail
credited_restricted → FTD - ✓
S14 Reconciliation includes the sales checks (one client per active lead, ownership vs history incl. active owners) and the sales workspace reconciles after the whole journey
- ✓
J1 End-to-end Sales journey (one workspace): lead twice → duplicate reviewed → assigned → follow-up → opt-out blocks dispatch → convert without KYC bypass → FTD → chargeback → reassignment keeps credit and revokes access
Technical detail
✓ 1 Lead arrives twice → one strong duplicate item, nothing merged automatically (Same email · Same phone) | ✓ 2 Duplicate reviewed: agent proposes, team lead approves; the repeat becomes an alias (200/200) | ✓ 3 Agent assigned by versioned rule (English desk → Sara), recorded in append-only history (Rule r4 v1 (language = en)) | ✓ 4 Follow-up scheduled; a reached call sets 'contacted', then the agent qualifies the lead (qualified) | ✓ 5 Opt-out after scheduling blocks the queued SMS at dispatch (second check) (queued → blocked) | ✓ 6 Lead converts without bypassing KYC: client created with KYC pending, transfers blocked, agent gets no client-money access (pending/403) | ✓ 7 Compliance approves KYC; the qualifying deposit records the FTD from the ledger (rule ftd-v1) (credited_available / net_funded) | ✓ 8 Chargeback changes net funding without erasing history: FTD kept with a reversal flag, net $0, stage unchanged, both postings still in the ledger (chargeback/0/qualified/ledger rows=2) | ✓ 9 Reassignment keeps historical credit and revokes old access: Sara keeps the conversion + FTD credit, loses the lead and its task on the next request; the task moves to Karim (404/404 credit=u_sara) | ✓ 10 The workspace reconciles after the whole journey - ✓
R1 Replaying an outbound key after reassignment discloses nothing to the former owner (404, no destination); a changed payload on the same key is refused (409); an over-long key is rejected, never truncated
Technical detail
404/409/400 - ✓
R2 A delivery is never claimed after the state changed between check and claim: opt-out → blocked, reassignment → cancelled, requester disabled → cancelled, destination changed → cancelled
Technical detail
blocked:Blocked at dispatch: lead asked not to be contacted | cancelled:Lead was reassigned before dispatch; the new owner must request contact again | cancelled:Requester is no longer active | cancelled:Destination changed since the request (e.g. after a merge); request again - ✓
R3 Reassignment that wins before the commit rejects the old owner's note and new follow-up: 409, no activity row, no task, denied audit only
Technical detail
409/409 tasks=0 - ✓
R4 An opt-out recorded on a merged alias protects the surviving lead (DNC on the survivor, provenance names the alias); lifting through the alias is refused
Technical detail
200/1/409 - ✓
R5 Two successive merges (A→B→C): the final survivor shows the first alias's history and keeps the most restrictive preference
Technical detail
200 dnc=1 - ✓
R6 Sales CSV export neutralises formula prefixes and leading control characters (=, +, -, @, leading space or tab)
Technical detail
'=1+1 ¦ '@cmd ¦ '=HYPERLINK(""x"") ¦ '=1 - ✓
R7a Restricted deposit → partial refund → release: the FTD is the qualifying release posting ($200), not the original receipt amount ($300)
Technical detail
200/200 ftd=20000 deposit_release - ✓
R7b Historical as-of view: a chargeback posted later does not appear in an earlier as-of result (no reversal, net $200); after it, the reversal shows and the FTD is unchanged
Technical detail
before=null/20000 after=chargeback - ✓
R9 Quiet hours or time zone changed between the policy read and the claim: the claim is refused and the fresh decision defers the SMS (no delivered activity), for both a new quiet window and an unknown time zone
Technical detail
deferred:Deferred: quiet hours (12:00 Europe/London); allowed 15:00–12:00 local | deferred:Deferred: lead's time zone unknown; review before calling or texting - ✓
R10 Opt-out on alias A while its survivor B is merged into C: the restriction lands on the current survivor C (or the request is refused); never a success that leaves C contactable
Technical detail
200 applied_to=C C.dnc=1 - ✓
R8 The review workspace still reconciles after all race and merge scenarios
Daylight-saving time
5 / 5 passedReport date ranges on 23- and 25-hour days (TZ=Asia/Karachi run).
- ✓
DST1 Fall-back day (1 Nov 2026, 25 h): 11:30 PM that day is included
- ✓
DST1b 12:30 AM next day is excluded
- ✓
DST2 Spring-forward day (14 Mar 2027, 23 h): 12:30 AM on 15 Mar is excluded
- ✓
DST2b 11:30 PM on 14 Mar is included
- ✓
DST3 From boundary is local midnight