Context
Surfaced during the #101 connector-dedupe review (item #2).
The declarative REST executor (src/connectors/adapters/declarative-rest.ts) marks mutations cas: 'native-idempotency' — it's even the default — but the request builder never sends inv.idempotencyKey as an idempotency header. So for any declarative connector, a native-idempotency mutation has no upstream dedup; only the platform-side MutationGuard record guards against duplicates.
Impact
If MutationGuard state is lost (process restart, guard miss) and a mutation is retried, the upstream sees a fresh request. Concretely: Notion databases.create / create_page can create duplicate resources on retry. The retired notion-database adapter forwarded Notion's Idempotency-Key header as a second line of defense; the promoted declarative notion does not.
This is renderer-wide and pre-existing (not introduced by #101), so it was deliberately scoped out of that PR.
Proposed fix
Add an optional per-spec idempotency-header mapping (e.g. idempotencyHeader?: string on RestConnectorSpec) that the executor populates from inv.idempotencyKey for native-idempotency mutations. Keep it per-spec opt-in rather than a blanket header, since header names and semantics vary by provider (Notion uses Idempotency-Key).
References
Context
Surfaced during the #101 connector-dedupe review (item #2).
The declarative REST executor (
src/connectors/adapters/declarative-rest.ts) marks mutationscas: 'native-idempotency'— it's even the default — but the request builder never sendsinv.idempotencyKeyas an idempotency header. So for any declarative connector, anative-idempotencymutation has no upstream dedup; only the platform-side MutationGuard record guards against duplicates.Impact
If MutationGuard state is lost (process restart, guard miss) and a mutation is retried, the upstream sees a fresh request. Concretely: Notion
databases.create/create_pagecan create duplicate resources on retry. The retirednotion-databaseadapter forwarded Notion'sIdempotency-Keyheader as a second line of defense; the promoted declarativenotiondoes not.This is renderer-wide and pre-existing (not introduced by #101), so it was deliberately scoped out of that PR.
Proposed fix
Add an optional per-spec idempotency-header mapping (e.g.
idempotencyHeader?: stringonRestConnectorSpec) that the executor populates frominv.idempotencyKeyfornative-idempotencymutations. Keep it per-spec opt-in rather than a blanket header, since header names and semantics vary by provider (Notion usesIdempotency-Key).References
src/connectors/adapters/declarative-rest.ts— header construction (~L145–157)