fix(audit): criticals C1 (currency-scoped deposit gate), C2 (outcome-aware berth rule), C4 (/q/ allowlist)

C1: getDepositTotalForInterest now filters to the interest's
depositExpectedCurrency for the auto-advance gate, so a wrong-currency
payment can no longer satisfy the deposit expectation (and mark the berth
Sold). C2: setInterestOutcome fires interest_completed only for 'won';
lost/cancelled fire a new 'deal_lost' rule that frees the berth instead of
flipping it to 'sold'. C4: add '/q/' to proxy PUBLIC_PATHS so tracked
links in outbound mail reach external recipients.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-02 11:54:36 +02:00
parent 30f6723fef
commit 7aa639f195
4 changed files with 37 additions and 9 deletions

View File

@@ -1400,11 +1400,11 @@ export async function setInterestOutcome(
stageAtOutcome,
});
// G-C4: fire interest_completed berth-rule for any non-null outcome
// (won / lost / cancelled all qualify). Default rule mode is 'auto' →
// berth status flips to 'sold' for won, but admins can scope per outcome
// via system_settings.berth_rules.
void evaluateRule('interest_completed', id, portId, meta);
// Berth-rule on deal close (audit C2). Only a WON deal should drive the
// berth to 'sold' (via interest_completed). Lost/cancelled deals fire
// `deal_lost`, which frees the berth — previously every outcome reused
// interest_completed and silently flipped the berth to 'sold'.
void evaluateRule(data.outcome === 'won' ? 'interest_completed' : 'deal_lost', id, portId, meta);
// Phase 2 nested-subfolders - rename the interest's document folder
// to surface the outcome inline (e.g. "Deal A1-A3 (Won)"). Dynamic