What it does
When a multi-turn negotiation session reachesstatus: "accepted", its
finalProposal represents the agreed terms (discount, term, bundle,
final price). Apply-mode promotes that proposal into the realtime
/api/v1/recommend response so downstream callers see the negotiated
terms alongside the next-best-action ranking.
Each ranked decision is decorated with one of:
appliedNegotiation: { sessionId, proposal }— every gate passed; the caller can present the negotiated terms.appliedNegotiationReject: { sessionId, reason }— a session existed but a gate blocked the apply (e.g., daily cap exceeded, regulator review still required, kill switch tripped).
Configuration
Response shape
meta.negotiationApply is omitted when the wire is in noOp (flag off
or no relevant sessions found).
Reject reasons
Audit trail
Every apply and every reject writes one audit-log row with:action:"negotiate_apply_realtime"(apply) or"negotiate_apply_realtime_reject"(reject).entityType:"negotiation_session",entityId: the session id.changes:{ sessionId, applied, proposal | reject }.
appliesUsedToday from a count of these rows, so
the audit chain is the source of truth for daily-cap accounting.
Failure modes — fail-CLOSED
Honest limits
-
Concurrency under-counting.
appliesUsedTodayis read-once + per-call increment in memory; the audit row is written fire-and-forget. Two simultaneous/recommendcalls for the same(tenant, day)can both read N and both apply up to cap, so the day total can exceed cap by(concurrency - 1) * applies_per_callbefore audit rows materialize. Acceptable for V1 because (a) the apply decoration is informational — no irreversible side effect — and (b) compliance retains the audit-log rows for retroactive reconciliation. A Redis atomic-counter increment with a TTL keyednegotiation:applies:{tenantId}:{YYYY-MM-DD}is on the roadmap and closes the gap. -
appliesUsedTodaycost.AuditLog.countis correct but slow under very high tenant volume. Same Redis follow-up addresses both this and #1. -
Placements path not wired. The multi-placement
/recommendresponse shape is decorated separately. Today the apply-mode wire only runs on the single-flow + auto-resolve paths. Wiring placements is a small follow-up.
Operational checklist
- Enable
applyModeEnabledfor one tenant first. Watch themeta.negotiationApplycounter on/recommendresponses and the AuditLog rows. - Run the negotiation eval harness for 30 nights. When
zeroViolationClearance >= 0.95for the full window, flipregulatorReviewClearedtotrue. - A regression in any of:
solverFailed > 0on therealtime ranking appliedlog line (see Lagrangian Ranking), sustainedkill_switch_tripped: { source: "auto_error_rate" }rejects, or AuditLog write failures aterrorseverity → flipapplyModeEnabledtofalseimmediately. The wire is designed so flag-off is bit-identical to the pre-W15 response.