Parse a support thread into attributed events, link corrections and hold any decision whose authority or reference remains uncertain.
Project: reconstruct a support email decision ledger
Prepare the fixture
Create a fictional support thread about ticket-47: an initial 02:47 UTC window, a customer request to move it, an operator correction to 03:47 UTC and an old time quoted in the final reply. Add a forwarded message and an inline reply inside a quotation. Store source MIME, message IDs, parent links, sender identities and delivery timestamps. Annotate authored, quoted and uncertain spans before marking speech acts. Quote boundaries prevent copied text from becoming a new event.
Build the ledger
Create events for proposal, request, correction, acknowledgement and withdrawal. Require each correction to name a field and a prior event it supersedes. Keep a versioned decision record at every message cutoff; this exposes when a summary would have been wrong at that time. Thread order alone is not authority. Use trusted identity and role policy to decide who can approve an operational change. Decision state supplies the event semantics.
Release conservatively
The code below admits only a verified current decision with an explicit authorized acknowledgement. A statement labeled uncertain, a quote or a customer request cannot satisfy the approval gate. Keep a human-readable explanation tied to message IDs and source spans. This project does not send email or schedule a change; it produces a review record for a separate trusted workflow.
Test the outcome
Report authored-span accuracy, attribution errors, correction-link errors, active-window accuracy and false approved decisions. Re-run the test with duplicate and out-of-order messages. Compare the final ledger to reviewer annotations at each cutoff, not only one end-of-thread summary. Inspect every false approval individually because it has a larger operational cost than a held but valid request.
Implementation
def release_email_decision(state, authorized_senders):
if state.get("window_status") != "resolved":
return {"state": "hold", "reason": "window-unresolved"}
acknowledgement = state.get("acknowledgement", {})
if acknowledgement.get("provenance") != "authored":
return {"state": "hold", "reason": "quoted-acknowledgement"}
if acknowledgement.get("sender_id") not in authorized_senders:
return {"state": "hold", "reason": "sender-scope"}
if acknowledgement.get("window_utc") != state.get("window_utc"):
return {"state": "hold", "reason": "stale-acknowledgement"}
return {"state": "review-ready", "window_utc": state["window_utc"]}
decision = {"window_status": "resolved", "window_utc": "03:47Z",
"acknowledgement": {"provenance": "authored",
"sender_id": "operator-82",
"window_utc": "03:47Z"}}
assert release_email_decision(decision, {"operator-82"})["state"] == "review-ready"
assert release_email_decision({**decision, "acknowledgement":
{**decision["acknowledgement"],
"provenance": "quoted"}},
{"operator-82"})["state"] == "hold"
Performance and operating cost
With a hash-backed sender set, this final gate is expected O(1) time and space. MIME parsing and event reconstruction scale with thread size, while human review handles uncertain attribution. A review-ready ledger is not an email or scheduling action; that action requires its own authenticated controls.
Common Mistakes
- Allowing a quoted acknowledgement to pass as current authorization.
- Trusting a signature block for sender identity.
- Checking the latest timestamp without checking what the sender actually accepted.
- Evaluating only the final summary and missing earlier wrong states.
