Skip to content

feat(zendesk): draft the known answers before the digest posts - #80

Merged
Bilb merged 4 commits into
mainfrom
feat/zendesk-auto-drafts
Nov 3, 2026
Merged

Bilb merged 4 commits into
mainfrom
feat/zendesk-auto-drafts

Conversation

@Bilb

@Bilb Bilb commented Nov 2, 2026

Copy link
Copy Markdown
Collaborator

Before posting, the weekday digest writes a private draft note on each reported ticket that nobody has answered or picked up yet, when support already knows the answer. The note is the one claude: draft writes, so a human checks it and sends it with claude: reply N, or writes claude: draft <brief>, which starts over rather than amending the unreviewed draft. Nothing is sent to a customer without a human.

Three sources, in order:

  • Police and legal data requests (house-answer group law-enforcement-requests): the standard reply in STANDARD_REPLIES, word for word, untranslated.
  • Abuse reports (the digest's abuse_report category): composed from the standing brief in STANDING_BRIEFS, in the reporter's language. We can block the reported ID from Session-operated communities, and that is all. No placement call needed.
  • Everything else: a claude: draft brief that went out unamended as a ticket's first reply, on the same house-answer group. Claude picks the one that fully answers the ticket, or none, and rejects briefs that name another ticket, promise an action, or carry a fact that may have expired (a pending release, an ETA).

Why past briefs: over September and October the note flow sent 504 replies, reply 1 467 times, and 262 tickets went straight draft > reply. Briefs within a group repeat nearly word for word. The house answers stay a shape-only precedent, as before: they are not a fact source.

The briefs are cached in known_briefs.json beside the dedup state, topped up from tags:claude-sent each run and dropped after 180 days. The first run backfills about 450 tickets. Drafting is bounded to 20 minutes, highest priority first, stops after three failures and never fails the digest. A 📝 line in the digest lists the drafted tickets.

Also carries e7b0665 (live chats that keep a customer's message off the ticket), which this builds on and which was not pushed on its own.

Deploy

Needs ZENDESK_HOUSE_ANSWERS in /etc/session-ops/zendesk.env; without it the digest logs that drafting was skipped and posts as before.

Tested

  • uv run python -m unittest discover -s tests/zendesk -t .: 502 pass
  • zendesk-triage --window-hours 120 --dry-run --known-briefs <tmp> against the live account: 343 briefs cached from 443 sent tickets, 3 of 10 tickets drafted, nothing written
  • the three new paths dry-run on one real police request and two real abuse reports

Bilb added 4 commits October 26, 2026 09:51
…he ticket

Zendesk writes a messaging chat into the ticket only when the chat ends, and a
chat that starts while the ticket has an assignee is never ended. Until then the
customer's newest message is readable by the assignee alone.

The digest now sweeps unsolved Twitter DM tickets for an open chat, lists them
on their own line, and leaves them unrecorded so they are reported again once
the message lands. note_reply refuses draft, reply and english on such a
ticket rather than act on a conversation missing its newest message.
The digest now writes a private draft note on each ticket it reports that
nobody has answered yet, when support already knows the answer:

- a police or legal data request gets the standard reply, word for word
- an abuse report is composed from a standing brief, in the reporter's
  language: we can block the ID from Session-operated communities, no more
- anything else reuses a `claude: draft` brief that went out unamended as
  a first reply on the same house-answer group, when Claude judges it
  fully answers the ticket and holds no expired or ticket-specific fact

Those briefs are cached beside the dedup state and topped up from
`tags:claude-sent` each run. A draft is only ever sent by a human's
`claude: reply N`; a `claude: draft` after it starts over instead of
amending wording nobody approved. The digest lists the drafted tickets.
- Only tickets placed in DRAFT_GROUPS get an unprompted draft.
- Placement runs before the abuse standing brief, so a police request
  classified as an abuse report still gets the fixed legal text.
- Any error while drafting is reported per ticket and survived, so the
  digest always posts.
- The brief backfill reads the 180-day window past the 1000-result cap,
  and the scan only moves on when every search page and comment read
  succeeded.
- A ticket with a private note from a person is left alone.
@Bilb
Bilb merged commit fc86737 into main Nov 3, 2026
3 checks passed
@Bilb
Bilb deleted the feat/zendesk-auto-drafts branch November 3, 2026 06:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant