You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Commit 43078fc
Browse filesBrowse the repository at this point in the historyBrowse files
Changed: `NewLinkWithNotes.channelId` (the type `integrations.saveLink()`/`saveLinks()` accept) is now required instead of optional. It was easy to set `channelId` only inside `meta` and forget the top-level field — that compiled fine but silently broke outbound write-back (`onNoteCreated` reads the channel back from the persisted `channelId`, not from `meta`) and bulk operations like `archiveLinks({ channelId })` on disable, with no error anywhere. The type system now catches this at compile time instead.
6
+
7
+
Added: `CreateLinkResult` — the return type for `Connector.onCreateLink()`. Identical to `NewLinkWithNotes` except `channelId` stays optional, since the platform auto-fills it from the compose draft when omitted. Connectors implementing `onCreateLink` should update their return type from `NewLinkWithNotes | null` to `CreateLinkResult | null`.
`channelId` is required on `NewLinkWithNotes` (the type `saveLink()`/`saveLinks()` accept) precisely because it's easy to set only inside `meta` and forget the top-level field — the type system will reject a link built that way. This isn't just about bulk operations like `integrations.archiveLinks({ channelId })` on disable: the platform persists `channelId` to the link's DB row and reads it back from there — not from `meta` — to populate `thread.meta.channelId` for connector callbacks like `onNoteCreated`. A link with `channelId` only inside `meta` will send replies to nobody: the connector's own `onNoteCreated` reads a channelId that was never actually saved, and typically no-ops silently (no error, nothing captured) because it can't resolve a client for the reply. Set `channelId` at the top level on every value you pass to `saveLink()`/`saveLinks()`.
312
+
313
+
`onCreateLink` is the one exception: its return type is `CreateLinkResult`, where `channelId` is optional — the platform auto-fills it from `draft.channelId` (the channel the user composed into) if you omit it.
314
+
311
315
## Classifier facets (optional)
312
316
313
317
Messaging-style connectors may set `link.facets` (`format` / `automation` / `reach` from `@plotday/twister/facets`) as internal classifier signal. Set a dimension only when a heuristic is confident; leave it `null`/omitted otherwise. See `gmail/src/gmail-facets.ts` and `slack/src/slack-facets.ts`.
@@ -520,7 +524,7 @@ For closed-roster DM-style compose set `compose.targets: "contacts"`; for open a
520
524
Then implement `onCreateLink` — return the link, do NOT call `integrations.saveLink()` (platform wires it to the originating thread):
2.Setting `channelId` only inside `link.meta` instead of at the top level → `NewLinkWithNotes.channelId` is what the platform actually persists and reads back for connector callbacks (`thread.meta.channelId` in `onNoteCreated`, etc.) and for bulk operations like `integrations.archiveLinks({ channelId })`on disable. A `meta`-only channelId compiles fine structurally but leaves outbound replies (and disable-time cleanup) silently broken — no error, nothing captured. The type system now requires the top-level field on `NewLinkWithNotes`, so this fails to compile instead of failing silently in production.
626
630
3. Not propagating `initialSync` through the whole pipeline → notification spam.
627
631
4. Mutable ids in `source` (e.g. Jira issue key) → use immutable id, store key in `meta`.
628
632
5.`source` that's only unique within one user's account → breaks cross-user dedup; add workspace/tenant/mailbox qualifier.
0 commit comments