diff --git a/fixtures/mail/README.md b/fixtures/mail/README.md new file mode 100644 index 0000000..c1fdb85 --- /dev/null +++ b/fixtures/mail/README.md @@ -0,0 +1,31 @@ +# Mail-behavior acceptance fixtures + +These fixtures define the **mail behavior Helpthread's engine must exhibit** — +they are the acceptance criteria the mail engine (and its threading logic, per +[`specs/mail/threading.md`](../../specs/mail/threading.md)) is tested against. +Each records an input scenario and the expected conversation/threading outcome. + +## What they assert + +- `new-conversation` — a fresh inbound email opens a new conversation. +- `reply-with-reference` — a reply carrying a valid signed reply token threads + into the same conversation. +- `token-authority` — a valid token threads even when the subject is unrelated; + the token, not the subject, is the threading authority. +- `forged-reply-token` — a tampered token is rejected and opens a new + conversation; the token is verified, not merely pattern-matched. +- `reply-subject-only` / `same-subject-different-customer` — a matching subject + with no valid token never threads; subject is never a threading signal. +- `auto-submitted` — auto-submitted mail still creates a conversation. +- `html-body` — an inbound HTML body (including a ` script tag. [HT7-23ad1ec4x-html-body]

", + "source": { + "type": "email", + "via": "customer" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "assignedTo": null, + "to": [ + "support@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T05:00:56Z", + "openedAt": null, + "_embedded": { + "attachments": [] + } + } + ] + } + } + }, + "notes": "Script tag included deliberately to observe sanitization behavior on the stored body.", + "runId": "23ad1ec4x", + "recordedAt": "2026-07-10T05:01:08.946Z" +} diff --git a/fixtures/mail/observed/new-conversation.json b/fixtures/mail/observed/new-conversation.json new file mode 100644 index 0000000..29f0692 --- /dev/null +++ b/fixtures/mail/observed/new-conversation.json @@ -0,0 +1,119 @@ +{ + "scenario": "new-conversation", + "title": "Fresh email creates a new conversation", + "expectation": "A brand-new, uniquely-marked email creates exactly one new conversation visible via the API.", + "runId": "23ad1ec4", + "recordedAt": "2026-07-10T04:11:05.939Z", + "sent": [ + { + "from": "customer-23ad1ec4-a@example.test", + "to": "support@example.test", + "subject": "Harness fresh conversation [HT7-23ad1ec4-new-conversation]", + "text": "Hello, this is a fresh test conversation. [HT7-23ad1ec4-new-conversation]", + "headers": {}, + "messageId": "<2ae092e6-48d3-2eb5-e640-5cc2fb3896c4@example.test>", + "envelope": { + "from": "customer-23ad1ec4-a@example.test", + "to": [ + "support@example.test" + ] + } + } + ], + "observed": { + "conversation": { + "id": 14, + "number": 14, + "threadsCount": 1, + "type": "email", + "folderId": 1, + "status": "active", + "state": "published", + "subject": "Harness fresh conversation [HT7-23ad1ec4-new-conversation]", + "preview": "Hello, this is a fresh test conversation. [HT7-23ad1ec4-new-conversation]", + "mailboxId": 1, + "assignee": null, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdAt": "2026-07-10T04:10:55Z", + "updatedAt": "2026-07-10T04:10:55Z", + "closedBy": null, + "closedByUser": null, + "closedAt": null, + "userUpdatedAt": null, + "customerWaitingSince": { + "time": "2026-07-10T04:10:55Z", + "friendly": "Just now", + "latestReplyFrom": "customer" + }, + "source": { + "type": "email", + "via": "customer" + }, + "cc": [], + "bcc": [], + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "_embedded": { + "threads": [ + { + "id": 29, + "type": "customer", + "status": "active", + "state": "published", + "action": { + "type": "", + "text": "Redacted Person started a new conversation #14", + "associatedEntities": [] + }, + "body": "Hello, this is a fresh test conversation. [HT7-23ad1ec4-new-conversation]", + "source": { + "type": "email", + "via": "customer" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "assignedTo": null, + "to": [ + "support@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T04:10:55Z", + "openedAt": null, + "_embedded": { + "attachments": [] + } + } + ] + } + } + }, + "notes": "Created conversation id=14 with unknown thread(s)." +} diff --git a/fixtures/mail/observed/reply-subject-only.json b/fixtures/mail/observed/reply-subject-only.json new file mode 100644 index 0000000..d277e1a --- /dev/null +++ b/fixtures/mail/observed/reply-subject-only.json @@ -0,0 +1,232 @@ +{ + "scenario": "reply-subject-only", + "title": "Customer reply with only a \"Re:\" subject (no reference headers)", + "expectation": "A \"Re:\" reply with no In-Reply-To/References may or may not thread correctly by subject alone — observed, not assumed.", + "runId": "23ad1ec4", + "recordedAt": "2026-07-10T04:18:08.397Z", + "sent": [ + { + "from": "customer-23ad1ec4-c@example.test", + "to": "support@example.test", + "subject": "Harness reply-subject-only [HT7-23ad1ec4-reply-subject-only]", + "text": "Initial message for reply-subject-only. [HT7-23ad1ec4-reply-subject-only]", + "headers": {}, + "messageId": "<816fe947-b04d-fd20-b15b-d5e8ab308569@example.test>", + "envelope": { + "from": "customer-23ad1ec4-c@example.test", + "to": [ + "support@example.test" + ] + } + }, + { + "from": "customer-23ad1ec4-c@example.test", + "to": "support@example.test", + "subject": "Re: Harness reply-subject-only [HT7-23ad1ec4-reply-subject-only]", + "text": "Follow-up with no In-Reply-To/References. [HT7-23ad1ec4-reply-subject-only]", + "headers": {}, + "messageId": "<26e928f6-a407-ed1e-46d4-6adeea587555@example.test>", + "envelope": { + "from": "customer-23ad1ec4-c@example.test", + "to": [ + "support@example.test" + ] + } + } + ], + "observed": { + "conversationId": 16, + "result": { + "outcome": "split", + "conversations": [ + { + "id": 17, + "number": 17, + "threadsCount": 1, + "type": "email", + "folderId": 1, + "status": "active", + "state": "published", + "subject": "Re: Harness reply-subject-only [HT7-23ad1ec4-reply-subject-only]", + "preview": "Follow-up with no In-Reply-To/References. [HT7-23ad1ec4-reply-subject-only]", + "mailboxId": 1, + "assignee": null, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdAt": "2026-07-10T04:17:54Z", + "updatedAt": "2026-07-10T04:17:54Z", + "closedBy": null, + "closedByUser": null, + "closedAt": null, + "userUpdatedAt": null, + "customerWaitingSince": { + "time": "2026-07-10T04:17:54Z", + "friendly": "Just now", + "latestReplyFrom": "customer" + }, + "source": { + "type": "email", + "via": "customer" + }, + "cc": [], + "bcc": [], + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "_embedded": { + "threads": [ + { + "id": 33, + "type": "customer", + "status": "active", + "state": "published", + "action": { + "type": "", + "text": "Redacted Person started a new conversation #17", + "associatedEntities": [] + }, + "body": "Follow-up with no In-Reply-To/References. [HT7-23ad1ec4-reply-subject-only]", + "source": { + "type": "email", + "via": "customer" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "assignedTo": null, + "to": [ + "support@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T04:17:54Z", + "openedAt": null, + "_embedded": { + "attachments": [] + } + } + ] + } + }, + { + "id": 16, + "number": 16, + "threadsCount": 1, + "type": "email", + "folderId": 1, + "status": "active", + "state": "published", + "subject": "Harness reply-subject-only [HT7-23ad1ec4-reply-subject-only]", + "preview": "Initial message for reply-subject-only. [HT7-23ad1ec4-reply-subject-only]", + "mailboxId": 1, + "assignee": null, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdAt": "2026-07-10T04:16:54Z", + "updatedAt": "2026-07-10T04:16:54Z", + "closedBy": null, + "closedByUser": null, + "closedAt": null, + "userUpdatedAt": null, + "customerWaitingSince": { + "time": "2026-07-10T04:16:54Z", + "friendly": "1 min ago", + "latestReplyFrom": "customer" + }, + "source": { + "type": "email", + "via": "customer" + }, + "cc": [], + "bcc": [], + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "_embedded": { + "threads": [ + { + "id": 32, + "type": "customer", + "status": "active", + "state": "published", + "action": { + "type": "", + "text": "Redacted Person started a new conversation #16", + "associatedEntities": [] + }, + "body": "Initial message for reply-subject-only. [HT7-23ad1ec4-reply-subject-only]", + "source": { + "type": "email", + "via": "customer" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "assignedTo": null, + "to": [ + "support@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T04:16:54Z", + "openedAt": null, + "_embedded": { + "attachments": [] + } + } + ] + } + } + ] + } + }, + "notes": "Subject-only \"Re:\" reply created a separate conversation from 16." +} diff --git a/fixtures/mail/observed/reply-with-reference.json b/fixtures/mail/observed/reply-with-reference.json new file mode 100644 index 0000000..28032ea --- /dev/null +++ b/fixtures/mail/observed/reply-with-reference.json @@ -0,0 +1,275 @@ +{ + "scenario": "reply-with-reference", + "title": "Customer reply with In-Reply-To/References threads correctly", + "expectation": "A customer reply whose In-Reply-To/References point at the helpdesk reply Message-ID is appended to the same conversation.", + "runId": "23ad1ec4", + "recordedAt": "2026-07-10T04:44:11.829Z", + "sent": [ + { + "step": "customer-reply", + "subject": "Re: Harness reply-with-reference [HT7-23ad1ec4-reply-with-reference]", + "headers": { + "In-Reply-To": "", + "References": "" + } + } + ], + "observed": { + "agentReplyEmail": { + "messageId": "", + "inReplyTo": "<25454420-276e-5396-0d3e-d846bdafe662@example.test>", + "references": "<25454420-276e-5396-0d3e-d846bdafe662@example.test> ", + "subject": "Re: Harness reply-with-reference [HT7-23ad1ec4-reply-with-reference]" + }, + "conversation": { + "id": 15, + "number": 15, + "threadsCount": 4, + "type": "email", + "folderId": 3, + "status": "active", + "state": "published", + "subject": "Harness reply-with-reference [HT7-23ad1ec4-reply-with-reference]", + "preview": "Customer reply threading against the agent reply. [HT7-23ad1ec4-reply-with-reference]", + "mailboxId": 1, + "assignee": { + "id": 1, + "type": "user", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "agent@example.test" + }, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdAt": "2026-07-10T04:11:54Z", + "updatedAt": "2026-07-10T04:43:54Z", + "closedBy": null, + "closedByUser": null, + "closedAt": null, + "userUpdatedAt": "2026-07-10T04:37:23Z", + "customerWaitingSince": { + "time": "2026-07-10T04:43:54Z", + "friendly": "Just now", + "latestReplyFrom": "customer" + }, + "source": { + "type": "email", + "via": "customer" + }, + "cc": [], + "bcc": [], + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "_embedded": { + "threads": [ + { + "id": 37, + "type": "customer", + "status": "active", + "state": "published", + "action": { + "type": "", + "text": "Redacted Person replied to conversation #15", + "associatedEntities": [] + }, + "body": "Customer reply threading against the agent reply. [HT7-23ad1ec4-reply-with-reference]", + "source": { + "type": "email", + "via": "customer" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "assignedTo": { + "id": 1, + "type": "user", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "agent@example.test" + }, + "to": [ + "support@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T04:43:54Z", + "openedAt": null, + "_embedded": { + "attachments": [] + } + }, + { + "id": 36, + "type": "message", + "status": "pending", + "state": "published", + "action": { + "type": "", + "text": "Redacted Person replied to conversation #15", + "associatedEntities": [] + }, + "body": "
Here’s a reply
", + "source": { + "type": "web", + "via": "user" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 1, + "type": "user", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "agent@example.test" + }, + "assignedTo": { + "id": 1, + "type": "user", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "agent@example.test" + }, + "to": [ + "customer@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T04:37:23Z", + "openedAt": "2026-07-10T04:37:51Z", + "_embedded": { + "attachments": [] + } + }, + { + "id": 31, + "type": "message", + "status": "active", + "state": "published", + "action": { + "type": "", + "text": "Redacted Person replied to conversation #15", + "associatedEntities": [] + }, + "body": "Thanks — can you share more detail?", + "source": { + "type": "api", + "via": "user" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 1, + "type": "user", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "agent@example.test" + }, + "assignedTo": null, + "to": [ + "customer@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T04:11:56Z", + "openedAt": "2026-07-10T04:16:47Z", + "_embedded": { + "attachments": [] + } + }, + { + "id": 30, + "type": "customer", + "status": "active", + "state": "published", + "action": { + "type": "", + "text": "Redacted Person started a new conversation #15", + "associatedEntities": [] + }, + "body": "Initial message for reply-with-reference. [HT7-23ad1ec4-reply-with-reference]", + "source": { + "type": "email", + "via": "customer" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "assignedTo": null, + "to": [ + "support@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T04:11:54Z", + "openedAt": null, + "_embedded": { + "attachments": [] + } + } + ] + } + }, + "appendedToSameConversation": true + }, + "notes": [ + "Deviation from the scripted scenario: the agent reply posted via the REST API (thread type \"message\") was created in the helpdesk but NEVER dispatched as an email — confirmed absent from the customer mailbox 25+ minutes later. The observable agent reply below was sent manually from the helpdesk UI instead. Recorded as an observation: API-created message threads do not (or at least did not here) trigger outbound mail; UI-sent replies do.", + "The outbound reply subject preserves the inbound subject with a Re: prefix, and its body carries a \"-- Please reply above this line --\" separator plus a satisfaction-rating footer.", + "Gmail SMTP rewrites plus-addressed From values to the canonical account address, so every \"customer\" in run 23ad1ec4 is a single customer identity. Multi-customer scenarios need distinct sending identities (Gmail send-as aliases or a second account).", + "The customer reply threaded into the same conversation via In-Reply-To/References against the helpdesk Message-ID." + ] +} diff --git a/fixtures/mail/observed/same-subject-different-customer.json b/fixtures/mail/observed/same-subject-different-customer.json new file mode 100644 index 0000000..c7ac26b --- /dev/null +++ b/fixtures/mail/observed/same-subject-different-customer.json @@ -0,0 +1,372 @@ +{ + "scenario": "same-subject-different-customer", + "title": "Same customer, identical subject, no reference headers (intended different-customer — see correction)", + "expectation": "A second email reusing the exact subject of an existing conversation, with no reference headers, may merge or create its own conversation — observed, not assumed.", + "runId": "23ad1ec4", + "recordedAt": "2026-07-10T04:20:05.411Z", + "sent": [ + { + "from": "customer-23ad1ec4-e@example.test", + "to": "support@example.test", + "subject": "Harness fresh conversation [HT7-23ad1ec4-new-conversation]", + "text": "Different customer, identical subject to scenario A. [HT7-23ad1ec4-new-conversation]", + "headers": {}, + "messageId": "<8738925d-de41-70ae-26ab-d5f3c830b662@example.test>", + "envelope": { + "from": "customer-23ad1ec4-e@example.test", + "to": [ + "support@example.test" + ] + } + } + ], + "observed": { + "priorConversationId": 14, + "before": [ + { + "id": 14, + "number": 14, + "threadsCount": 1, + "type": "email", + "folderId": 1, + "status": "active", + "state": "published", + "subject": "Harness fresh conversation [HT7-23ad1ec4-new-conversation]", + "preview": "Hello, this is a fresh test conversation. [HT7-23ad1ec4-new-conversation]", + "mailboxId": 1, + "assignee": null, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdAt": "2026-07-10T04:10:55Z", + "updatedAt": "2026-07-10T04:10:55Z", + "closedBy": null, + "closedByUser": null, + "closedAt": null, + "userUpdatedAt": null, + "customerWaitingSince": { + "time": "2026-07-10T04:10:55Z", + "friendly": "8 min ago", + "latestReplyFrom": "customer" + }, + "source": { + "type": "email", + "via": "customer" + }, + "cc": [], + "bcc": [], + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "_embedded": { + "threads": [] + } + } + ], + "result": { + "outcome": "own-conversation", + "conversations": [ + { + "id": 19, + "number": 19, + "threadsCount": 1, + "type": "email", + "folderId": 1, + "status": "active", + "state": "published", + "subject": "Harness fresh conversation [HT7-23ad1ec4-new-conversation]", + "preview": "Different customer, identical subject to scenario A. [HT7-23ad1ec4-new-conversation]", + "mailboxId": 1, + "assignee": null, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdAt": "2026-07-10T04:19:53Z", + "updatedAt": "2026-07-10T04:19:53Z", + "closedBy": null, + "closedByUser": null, + "closedAt": null, + "userUpdatedAt": null, + "customerWaitingSince": { + "time": "2026-07-10T04:19:53Z", + "friendly": "Just now", + "latestReplyFrom": "customer" + }, + "source": { + "type": "email", + "via": "customer" + }, + "cc": [], + "bcc": [], + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "_embedded": { + "threads": [ + { + "id": 35, + "type": "customer", + "status": "active", + "state": "published", + "action": { + "type": "", + "text": "Redacted Person started a new conversation #19", + "associatedEntities": [] + }, + "body": "Different customer, identical subject to scenario A. [HT7-23ad1ec4-new-conversation]", + "source": { + "type": "email", + "via": "customer" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "assignedTo": null, + "to": [ + "support@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T04:19:53Z", + "openedAt": null, + "_embedded": { + "attachments": [] + } + } + ] + } + }, + { + "id": 14, + "number": 14, + "threadsCount": 1, + "type": "email", + "folderId": 1, + "status": "active", + "state": "published", + "subject": "Harness fresh conversation [HT7-23ad1ec4-new-conversation]", + "preview": "Hello, this is a fresh test conversation. [HT7-23ad1ec4-new-conversation]", + "mailboxId": 1, + "assignee": null, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdAt": "2026-07-10T04:10:55Z", + "updatedAt": "2026-07-10T04:10:55Z", + "closedBy": null, + "closedByUser": null, + "closedAt": null, + "userUpdatedAt": null, + "customerWaitingSince": { + "time": "2026-07-10T04:10:55Z", + "friendly": "9 min ago", + "latestReplyFrom": "customer" + }, + "source": { + "type": "email", + "via": "customer" + }, + "cc": [], + "bcc": [], + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "_embedded": { + "threads": [ + { + "id": 29, + "type": "customer", + "status": "active", + "state": "published", + "action": { + "type": "", + "text": "Redacted Person started a new conversation #14", + "associatedEntities": [] + }, + "body": "Hello, this is a fresh test conversation. [HT7-23ad1ec4-new-conversation]", + "source": { + "type": "email", + "via": "customer" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "assignedTo": null, + "to": [ + "support@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T04:10:55Z", + "openedAt": null, + "_embedded": { + "attachments": [] + } + } + ] + } + } + ], + "allMatches": [ + { + "id": 19, + "number": 19, + "threadsCount": 1, + "type": "email", + "folderId": 1, + "status": "active", + "state": "published", + "subject": "Harness fresh conversation [HT7-23ad1ec4-new-conversation]", + "preview": "Different customer, identical subject to scenario A. [HT7-23ad1ec4-new-conversation]", + "mailboxId": 1, + "assignee": null, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdAt": "2026-07-10T04:19:53Z", + "updatedAt": "2026-07-10T04:19:53Z", + "closedBy": null, + "closedByUser": null, + "closedAt": null, + "userUpdatedAt": null, + "customerWaitingSince": { + "time": "2026-07-10T04:19:53Z", + "friendly": "Just now", + "latestReplyFrom": "customer" + }, + "source": { + "type": "email", + "via": "customer" + }, + "cc": [], + "bcc": [], + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "_embedded": { + "threads": [] + } + }, + { + "id": 14, + "number": 14, + "threadsCount": 1, + "type": "email", + "folderId": 1, + "status": "active", + "state": "published", + "subject": "Harness fresh conversation [HT7-23ad1ec4-new-conversation]", + "preview": "Hello, this is a fresh test conversation. [HT7-23ad1ec4-new-conversation]", + "mailboxId": 1, + "assignee": null, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdAt": "2026-07-10T04:10:55Z", + "updatedAt": "2026-07-10T04:10:55Z", + "closedBy": null, + "closedByUser": null, + "closedAt": null, + "userUpdatedAt": null, + "customerWaitingSince": { + "time": "2026-07-10T04:10:55Z", + "friendly": "9 min ago", + "latestReplyFrom": "customer" + }, + "source": { + "type": "email", + "via": "customer" + }, + "cc": [], + "bcc": [], + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "_embedded": { + "threads": [] + } + } + ] + } + }, + "notes": [ + "Created its own conversation, distinct from scenario A's (id 14).", + "CORRECTION (post-run): the run intended a DIFFERENT customer via a plus-addressed From, but Gmail SMTP rewrites plus-addressed From values to the canonical account address, so the sender was the SAME customer as the original conversation. Observation stands and is arguably stronger: even the same customer with an identical subject and no reference headers produced a SEPARATE conversation — threading does not use subject or customer identity. A true different-customer variant needs a distinct sending identity (send-as alias or second account)." + ] +} diff --git a/fixtures/mail/observed/token-authority.json b/fixtures/mail/observed/token-authority.json new file mode 100644 index 0000000..ebf3381 --- /dev/null +++ b/fixtures/mail/observed/token-authority.json @@ -0,0 +1,314 @@ +{ + "scenario": "token-authority", + "title": "Reply with a REAL token but an unrelated subject", + "expectation": "Does the signed token alone decide threading, regardless of subject? Observed.", + "sent": [ + { + "subject": "Completely unrelated subject [HT7-23ad1ec4x-token-authority]", + "headers": { + "In-Reply-To": "", + "References": "" + } + } + ], + "observed": { + "appendedToRealConversation": true, + "conversationAfter": { + "id": 15, + "number": 15, + "threadsCount": 5, + "type": "email", + "folderId": 3, + "status": "active", + "state": "published", + "subject": "Harness reply-with-reference [HT7-23ad1ec4-reply-with-reference]", + "preview": "Reply with the REAL token but an unrelated subject. [HT7-23ad1ec4x-token-authority]", + "mailboxId": 1, + "assignee": { + "id": 1, + "type": "user", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "agent@example.test" + }, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdAt": "2026-07-10T04:11:54Z", + "updatedAt": "2026-07-10T04:59:54Z", + "closedBy": null, + "closedByUser": null, + "closedAt": null, + "userUpdatedAt": "2026-07-10T04:37:23Z", + "customerWaitingSince": { + "time": "2026-07-10T04:43:54Z", + "friendly": "16 min ago", + "latestReplyFrom": "customer" + }, + "source": { + "type": "email", + "via": "customer" + }, + "cc": [], + "bcc": [], + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "_embedded": { + "threads": [ + { + "id": 39, + "type": "customer", + "status": "active", + "state": "published", + "action": { + "type": "", + "text": "Redacted Person replied to conversation #15", + "associatedEntities": [] + }, + "body": "Reply with the REAL token but an unrelated subject. [HT7-23ad1ec4x-token-authority]", + "source": { + "type": "email", + "via": "customer" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "assignedTo": { + "id": 1, + "type": "user", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "agent@example.test" + }, + "to": [ + "support@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T04:59:54Z", + "openedAt": null, + "_embedded": { + "attachments": [] + } + }, + { + "id": 37, + "type": "customer", + "status": "active", + "state": "published", + "action": { + "type": "", + "text": "Redacted Person replied to conversation #15", + "associatedEntities": [] + }, + "body": "Customer reply threading against the agent reply. [HT7-23ad1ec4-reply-with-reference]", + "source": { + "type": "email", + "via": "customer" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "assignedTo": { + "id": 1, + "type": "user", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "agent@example.test" + }, + "to": [ + "support@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T04:43:54Z", + "openedAt": null, + "_embedded": { + "attachments": [] + } + }, + { + "id": 36, + "type": "message", + "status": "pending", + "state": "published", + "action": { + "type": "", + "text": "Redacted Person replied to conversation #15", + "associatedEntities": [] + }, + "body": "
Here’s a reply
", + "source": { + "type": "web", + "via": "user" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 1, + "type": "user", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "agent@example.test" + }, + "assignedTo": { + "id": 1, + "type": "user", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "agent@example.test" + }, + "to": [ + "customer@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T04:37:23Z", + "openedAt": "2026-07-10T04:37:51Z", + "_embedded": { + "attachments": [] + } + }, + { + "id": 31, + "type": "message", + "status": "active", + "state": "published", + "action": { + "type": "", + "text": "Redacted Person replied to conversation #15", + "associatedEntities": [] + }, + "body": "Thanks — can you share more detail?", + "source": { + "type": "api", + "via": "user" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 1, + "type": "user", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "agent@example.test" + }, + "assignedTo": null, + "to": [ + "customer@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T04:11:56Z", + "openedAt": "2026-07-10T04:16:47Z", + "_embedded": { + "attachments": [] + } + }, + { + "id": 30, + "type": "customer", + "status": "active", + "state": "published", + "action": { + "type": "", + "text": "Redacted Person started a new conversation #15", + "associatedEntities": [] + }, + "body": "Initial message for reply-with-reference. [HT7-23ad1ec4-reply-with-reference]", + "source": { + "type": "email", + "via": "customer" + }, + "customer": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "createdBy": { + "id": 3, + "type": "customer", + "firstName": "Redacted", + "lastName": "Person", + "photoUrl": "https://helpdesk.example.test/avatar.jpg", + "email": "customer@example.test" + }, + "assignedTo": null, + "to": [ + "support@example.test" + ], + "cc": [], + "bcc": [], + "createdAt": "2026-07-10T04:11:54Z", + "openedAt": null, + "_embedded": { + "attachments": [] + } + } + ] + } + }, + "newConversation": null + }, + "notes": "Uses the genuine FS_reply Message-ID captured in the base run; only the subject differs.", + "runId": "23ad1ec4x", + "recordedAt": "2026-07-10T05:01:08.943Z" +}