fix: finalize Flow v2 authoritative cutover semantics

This commit is contained in:
plx
2026-08-16 01:58:12 +00:00
parent 213ba6b41b
commit f96b1b3d6e
6 changed files with 73 additions and 8 deletions

View File

@@ -50,9 +50,30 @@ def test_due_and_future_customer_followup_semantics():
assert (due.effective_action, due.queue) == ("FOLLOW_UP_CUSTOMER_REVIEW", "do_now")
future = decide_authoritative_operation(projection(), obligations=[task("FOLLOW_UP_CUSTOMER_REVIEW", NOW + timedelta(days=1))], now=NOW)
assert (future.effective_action, future.queue) == (None, "waiting")
future_item = canonicalize_operations([{
"source": "flow_v2", "id": "opp", "opportunity_id": "opp",
"status": "projected", "action_code": "WAIT_CUSTOMER", "created_at": NOW,
}], {"opp": future.to_dict()})["items"]
assert len(future_item) == 1 and future_item[0]["operational_queue"] == "waiting"
assert decide_authoritative_operation(projection(), now=NOW).queue == "waiting"
def test_unanswered_send_info_remains_valid_on_awaiting_customer():
inbound = NOW - timedelta(hours=2)
obligation = task("SEND_INFO", latest_public_inbound=inbound, latest_public_outbound=None)
got = decide_authoritative_operation(projection(), obligations=[obligation], now=NOW)
assert (got.effective_action, got.queue, got.reason_code) == (
"SEND_INFO", "do_now", "ACTIVE_RESPONSE_OBLIGATION")
def test_send_info_satisfied_by_later_outbound_is_not_current():
inbound = NOW - timedelta(hours=2)
obligation = task("SEND_INFO", latest_public_inbound=inbound,
latest_public_outbound=inbound + timedelta(minutes=10))
got = decide_authoritative_operation(projection(), obligations=[obligation], now=NOW)
assert (got.effective_action, got.queue) == (None, "waiting")
def test_only_active_non_superseded_tasks_override_and_precedence():
stale = task("SUPPORT", resolved_at=NOW)
assert decide_authoritative_operation(projection("PROFORMA_REQUIRED", "CREATE_PROFORMA"), obligations=[stale], now=NOW).effective_action == "CREATE_PROFORMA"
@@ -81,6 +102,18 @@ def test_duplicate_is_suppressed_even_with_pending_legacy_task():
assert canonicalize_operations([source], {"opp": decision})["items"] == []
def test_xmat_duplicate_suppresses_diagnostic_review_business_action():
got = decide_authoritative_operation({
**projection("REVIEW_REQUIRED", "REVIEW_REQUIRED"),
"opportunity_id": "1816a06e-9a69-4a9b-9279-1263156892d3",
"canonical_opportunity_id": "dc89a466-db24-401b-bfe9-d47644b2d0c8",
"is_duplicate_representation": True,
}, now=NOW).to_dict()
assert got["business_next_action"] == "REVIEW_REQUIRED"
assert got["effective_action"] is None and got["suppress_current_card"] is True
assert got["canonical_opportunity_id"] == "dc89a466-db24-401b-bfe9-d47644b2d0c8"
def test_authoritative_queue_and_action_survive_canonical_boundary():
decision = decide_authoritative_operation(projection(), obligations=[task("FOLLOW_UP_CUSTOMER_REVIEW")], now=NOW).to_dict()
source = {"source": "task", "id": "old", "status": "pending", "action_code": "SEND_INVOICE",

View File

@@ -43,7 +43,8 @@ def named_decisions(fail=False):
for name, (oid, business_action, action, queue) in sim.NAMED.items():
result[oid] = {"business_next_action": business_action, "effective_action": action,
"operational_queue": "not_current" if queue == "suppressed" else queue,
"suppress_current_card": queue == "suppressed"}
"suppress_current_card": queue == "suppressed",
"canonical_opportunity_id": sim.DUPLICATE_CANONICAL.get(oid, oid)}
if fail:
result[sim.NAMED["Instalbeira"][0]]["effective_action"] = "SEND_INVOICE"
return result