Skip to content

Own the attendee-response fold sequence in a shared helper - #372

Merged
KrisBraun merged 1 commit into
mainfrom
feat/fold-rsvp-helper
Aug 4, 2026
Merged

Own the attendee-response fold sequence in a shared helper#372
KrisBraun merged 1 commit into
mainfrom
feat/fold-rsvp-helper

Conversation

@KrisBraun

Copy link
Copy Markdown
Contributor

@plotday/rsvp-fold exported the predicates for folding an attendee's response onto its event's thread, but stopped short of the sequence around them. Each mail connector restated the same ~40 lines: build the marker key, read it, check for a repeat, decide whether the response earns a note, compose it, save it, record it.

That order is load-bearing and nothing enforced it. Checking the repeat second re-emits a re-delivered commented acceptance; writing the marker on the suppressed path leaves it describing something the event thread does not carry. Both mistakes surface identically — a thread people had already read goes unread again.

Three copies of a rule is also three chances to diverge, and one of them did.

What moves

foldRsvp() owns the sequence. What stays injected is what genuinely differs between connectors: how the marker is read and written, how the note is saved, and the note's key, timestamp and unread flag.

Every outcome — emitted, suppressed, already folded — means the response was dealt with, so each caller records "this message was folded" unconditionally after the call, in whatever form suits it: an in-pass set, durable per-thread metadata, or both.

The marker-write contract

Injecting the write is what lets a connector batch markers into one flush per pass to stay inside its request budget, and the contract now spells out both requirements that come with doing so — because missing either one re-emits notes:

  • flush even when a later response throws, or markers already earned are lost with it;
  • dedupe repeat deliveries within the pass by other means, since a batched marker is invisible to the read until it is flushed. A connector that can see one message twice in a pass — two mailbox copies, a conversation re-fetched under two ids — needs its own in-pass guard.

That second requirement was previously knowledge held inside one connector's implementation rather than in the contract.

No behaviour change

Gmail, Outlook and iCloud mail all call it, and no connector test was edited — 361, 143 and 486 tests respectively pass against the refactored code unchanged, plus 44 in the library (11 new, covering all four paths and that the marker is written on exactly the emitting path). Net 264 lines removed from the connectors.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GcnBUEdW86ovpv1pT2d3b3

The library exported the predicates for folding an attendee's response onto
its event's thread but stopped short of the sequence around them, so each mail
connector restated the same ~40 lines: build the marker key, read it, check
for a repeat, decide whether the response earns a note, compose it, save it,
record it. The order is load-bearing and none of it was enforced — checking
the repeat second re-emits a re-delivered commented acceptance, and writing
the marker on the suppressed path leaves it describing something the event
thread does not carry. Both mistakes surface the same way: a thread people
had already read goes unread again.
`foldRsvp()` owns that order. Genuinely per-connector concerns stay injected,
because they differ for real reasons: reading and writing the marker (write
through immediately, or collect and flush once per pass to stay inside the
request budget), saving the note, and the note's key, timestamp and unread
flag. Every outcome — emitted, suppressed, already folded — means the response
was dealt with, so each caller records "this message was folded" unconditionally
after the call, in whatever form is its own: an in-pass set, durable per-thread
metadata, or both. The returned outcome names which path ran.
Gmail, Outlook and iCloud mail now call it. No behaviour change: their test
suites pass unchanged.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GcnBUEdW86ovpv1pT2d3b3
@KrisBraun
KrisBraun merged commit 12a44f3 into mainAug 4, 2026
1 check passed
@KrisBraun
KrisBraun deleted the feat/fold-rsvp-helper branch August 4, 2026 01:09
Sign up for freeto 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

@KrisBraun