Closer
Closes a Linear ticket and emails the requester after a PR merges, then verifies both actions before reporting success.
- result
- A 13-case golden set checks the full workflow, including a simulated email API success that never delivers a message. Closer catches the missing email and retries only the failed action.
- my contribution
- Solo hackathon build: workflow, integrations, interface, and tests.

- 2026
- ai
how it works
I built Closer solo for a hackathon, owning the workflow, integrations, interface, and tests.
Paste a merged GitHub PR and Closer finds the Linear issue it ships, marks it Done with a comment linking the PR, and emails whoever asked for it. Then it reads both apps again. A run only counts as done when the issue is in a Done state, the comment mentions the PR, and the message shows up in sent mail.
Writes sit behind a confidence gate. The match and the recipient are scored separately and the lower score decides; below 0.75, or on a PR that never merged, it escalates and writes nothing. When two tickets both fit, it stops and asks a person to pick one rather than guessing. A hackathon project, built in days on Cloudflare Workers with a Durable Object holding state, and verified anyway: a 13-case golden set runs the whole pipeline in plain Node, with exact assertions on every write.
engineering details
- Separated the writer from the verifier: after writing, a second pass re-reads issue state, comments, and in:sent, and the run's status comes only from what that re-read finds missing.
- Built the silent 200 as a scripted mock failure: Gmail returns a send id without the message ever landing, so the writer sees success and the verifier marks the run failed with missing: gmail.
- Kept every write going when an earlier one throws, so a run always reaches the verifier with an accurate record, and retry re-attempts exactly the entries verification reported missing.
- Kept the orchestrator as pure functions over one Apps interface for GitHub, Linear, and Gmail, so 29 unit tests and the 13-case golden set run the full pipeline with no Worker runtime.
- Traced a retry that could never finish to the Durable Object rebuilding its in-memory apps on every call: fixing Gmail lost the Linear writes and fixing Linear lost the mail. One cached instance per run fixed it, checked by a script that drives all four demo beats over the real WebSocket.
in the product
The Silent 200, Caught
Linear writes verify, Gmail returns 200, and the verifier finds nothing in sent mail. The run goes red with missing: gmail instead of reporting success.

Recovered in One Retry
Retry re-sends only what verification reported missing, then re-reads sent mail again before calling the run done.

Two Tickets, Zero Guesses
When a PR fits two Linear issues, both render as choices and nothing is written until a person picks one. The pick still re-runs the confidence gate.
