fix(granola): add file.created storage-bridge trigger - #170
Conversation
…collision A channel thread reply was the only Slack record emitted as a flat leaf file, `threads/<threadTs>/replies/<replyTs>.json`, while its children (reactions) nest under a directory at the same stem, `threads/<threadTs>/replies/<replyTs>/...`. One name as both a file and a directory cannot be materialized on a POSIX mount: the relayfile mirror fails every sync cycle with `mkdir .../replies/<replyTs>.json: not a directory`, never completes bootstrap, and the teardown writeback flush hangs — which marked the daily-ship cron run FAILED even though its handler succeeded. Every other Slack record type already uses the `<ts>/meta.json` directory-record convention (messagePath, directMessagePath, threadPath). Bring thread replies in line: `threadReplyPath` now returns `replies/<ts>/meta.json`, so the reply and its `reactions/` are siblings under one `replies/<ts>/` directory — collision impossible by construction. - Add `threadReplyLegacyPath` + `slackThreadReplyReadCandidatePaths` for back-compat reads of replies mirrored by a pre-0.8.x adapter (mirrors the existing messagePath / messageLegacyPath / slackMessageReadCandidatePaths pattern). - `thread.ts` reply-listing regex accepts both the new `<ts>/meta.json` and the legacy `<ts>.json` form so a mid-migration mirror still maps a reply to its parent listing. - Update the LAYOUT.md prompt line and the two adapter tests that pinned the old flat path; add a regression test asserting the reply record and its reaction child cannot collide (and that legacy reads still resolve). Scope: channel thread replies only (the confirmed collision site). DM thread replies (`directMessageThreadReplyPath`) carry the same latent flat-file shape and should get the same treatment — left out here because that tree is under active change on the slack-dm-user-message-materialization branch; fold the same fix in there. The relayfile mount daemon is being hardened separately to quarantine any such collision regardless of adapter. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Bring 1:1 direct-message thread replies in line with channel thread replies and every other Slack record type: emit `users/<u>/messages/<ts>/replies/<ts>/meta.json` instead of the flat `replies/<ts>.json` leaf, so a reply's stem is a directory and cannot collide with a same-named child directory on a POSIX mount. - `directMessageThreadReplyPath` → `<ts>/meta.json`; add `directMessageThreadReplyLegacyPath` + `slackDirectMessageThreadReplyReadCandidatePaths`. - `parseSlackDirectMessageThreadReplyPath` accepts both the new `<ts>/meta.json` and legacy `<ts>.json` forms so routing works mid-migration. - Export the new helpers (and the channel-reply back-compat helpers from the prior commit) from the package index. - Update LAYOUT.md prompt + discovery `.adapter.md`; extend the DM path test with legacy-parse and read-candidate assertions. Typecheck clean; full slack suite green (104). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…rces CI's "generated writeback-path catalog is in sync with adapter resources" check (adapter-core) was failing: a prior commit changed dropbox/linear/hubspot `resources.ts` (e.g. dropbox `shared-folders`/`shared-links`/`folders` → `cursors`/`files`; linear dropped `agent-activities`) without regenerating the checked-in catalog. The generator reads each adapter's BUILT `dist/resources.js`, so the drift only surfaces on a fresh CI build — locally a stale `dist` masked it. Rebuilt all adapters and re-ran `adapter-core writeback-paths generate`. Slack is unaffected — this PR's reply-path change is read-side only and does not touch slack's writeback `resources.ts`, so its catalog entry is unchanged. `--check` passes; adapter-core (105) and slack (104) suites green. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…log to main The pr-reviewer bot's earlier commit on this branch changed dropbox/hubspot/linear `resources.ts` — unrelated to this slack reply-path PR — and in particular removed linear's `agent-activities` writeback resource (added in #146). That broke CI two ways: the writeback-path catalog went out of sync (adapter-core test), and once regenerated, `relay-helpers/src/linear.ts` failed to typecheck because `ProviderClient<"linear">` no longer had `agent-activities` (`agentActivity`/`respond`/`acknowledge`). Revert those three resources.ts files to origin/main and regenerate the writeback-path catalog (restoring agent-activities etc.), so this PR contains ONLY the slack thread/DM reply directory-record change it's about. The bot's slack-related edits (discovery readPaths, DM schema wording, emit-auxiliary-files) are kept — they align with this fix. Verified: adapter-core (105) and slack (104) suites green, relay-helpers typecheck clean, writeback-discovery + digest-contracts checks pass, full `turbo build` succeeds. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The NB-Whisper pipeline writes transcripts directly to the granola VFS mount (/granola/notes/<id>.json), which fires a storage-dispatcher file.created event — not the Granola-native recording.created webhook. Only recording.created was listed, so the deploy CLI warned that file.created was unknown and the cloud rejected the trigger at deploy time. Add file.created to granola.mapping.yaml and regenerate catalog.generated.ts so the deploy lint accepts it and the cloud can register the subscription. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Caution Review failedPull request was closed or merged during review 📝 WalkthroughWalkthroughThis PR registers Granola's new ChangesGranola file.created event support
Slack reply path refactoring to directory records
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~25 minutes Possibly related PRs
Suggested labels
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Code Review
This pull request transitions Slack thread replies and direct message thread replies from flat leaf files (replies/.json) to directory records (replies//meta.json) to prevent file/directory name collisions on POSIX mounts. It also introduces legacy fallback path mapping to maintain backward compatibility during migration, and adds a new file.created trigger for Granola. The review comments correctly point out minor inaccuracies in the updated documentation and discovery script regarding the legacy flat path structure for channel thread replies.
Important
The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.
| - `/slack/channels/<channelId>.json` - Channel records. | ||
| - `/slack/channels/<channelId>/messages/<messageTs>/meta.json` - Message records. | ||
| - `/slack/channels/<channelId>/messages/<messageTs>/replies/<replyTs>.json` - Thread reply records. | ||
| - `/slack/channels/<channelId>/threads/<threadTs>/replies/<replyTs>/meta.json` - Thread reply records (directory records; legacy flat `/messages/<messageTs>/replies/<replyTs>.json` writeback paths still route for edits/deletes). |
There was a problem hiding this comment.
| ['/slack/channels/<channelId>.json', 'Channel records.'], | ||
| ['/slack/channels/<channelId>/messages/<messageTs>/meta.json', 'Message records.'], | ||
| ['/slack/channels/<channelId>/messages/<messageTs>/replies/<replyTs>.json', 'Thread reply records.'], | ||
| ['/slack/channels/<channelId>/threads/<threadTs>/replies/<replyTs>/meta.json', 'Thread reply records (directory records; legacy flat `/messages/<messageTs>/replies/<replyTs>.json` writeback paths still route for edits/deletes).'], |
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
|
Reviewed the current checkout for Fixed two issues:
Addressed comments
Advisory Notes
Validated locally:
I did not verify remote GitHub check status or mergeability, per the instruction not to use |
|
pr-reviewer could not complete review for #170 in AgentWorkforce/relayfile-adapters. |
|
Fixed the two validated issues in the current checkout: the Slack channel thread-reply legacy path docs now correctly say Addressed comments
Advisory Notes
Validated locally:
I did not print |
|
pr-reviewer could not complete review for #170 in AgentWorkforce/relayfile-adapters. |
|
Fixed the validated PR review findings:
Addressed comments
Advisory Notes
Validated locally:
The default parallel Turbo commands hit sandbox resource instability earlier ( |
|
Implemented the validated fixes and regenerated the affected artifacts. Key fixes:
Addressed comments
Advisory Notes
Validation passed:
No merge-conflict markers were found locally. I did not print |
|
Implemented one scoped fix: clarified the generated Slack discovery wording so the current read path is Addressed comments
Advisory Notes
Verification run:
|
Summary
file.createdtopackages/granola/granola.mapping.yamlas a storage-bridge event (distinct from the nativerecording.createdGranola API webhook)packages/core/src/triggers/catalog.generated.tsto includefile.createdin the granola entryWhy
The NB-Whisper transcript pipeline writes notes directly to the granola VFS mount (
/granola/notes/<id>.json). That write goes through the storage-change dispatcher, which emits afile.createdevent — the same mechanism asgmail.file.createdandgoogle-drive.file.created. Onlyrecording.createdwas listed as a known granola trigger, so:trigger "file.created" is not in the known-trigger registry for granola (known: recording.created)recording.createdremains valid for the native Granola webhook path;file.createdcovers the storage-bridge path used by NB-Whisper.Merge order
This PR must merge before
AgentWorkforce/workforce— the workforce deploy package imports@relayfile/adapter-core/triggers(this catalog) and needsgranola.file.createdpresent so the lint stops warning and downstream catalog consumers see the correct set.Test plan
adapter-core triggers generateproduces the same catalog (or rerun and verifyfile.createdappears for granola)meeting-actionspersona — warning aboutgranola[0].on: file.createdshould be gone🤖 Generated with Claude Code