Skip to content

fix(core): watch the channel path Slack replies actually arrive in - #34

Merged
khaliqgant merged 1 commit into
mainfrom
fix/slack-answer-channel-dir-discovery
Aug 17, 2026
Merged

fix(core): watch the channel path Slack replies actually arrive in#34
khaliqgant merged 1 commit into
mainfrom
fix/slack-answer-channel-dir-discovery

Conversation

@khaliqgant

@khaliqgant khaliqgant commented Aug 17, 2026

Copy link
Copy Markdown
Member

Stacked on #33#32#31. Review in that order; this PR's diff is the last commit only.

Summary

A Slack human-assistance gate could post its question and then never see a correctly threaded human answer, timing out after up to 24h with no indication why.

Relayfile materializes a channel under BOTH /slack/channels/<id> and /slack/channels/<id>__<name>, and delivers inbound messages and thread replies to the suffixed one. slackHumanAnswerSubscriptionPaths derived that suffix by slugifying whatever identifier the caller passed:

passed  : C0B9Z4CLG1J        (a channel ID — the documented alternative to a name)
guessed : /slack/channels/C0B9Z4CLG1J__c0b9z4clg1j/**     ← does not exist
actual  : /slack/channels/C0B9Z4CLG1J__watchdog-test/**   ← replies land here

Given a channel name it reconstructs the real directory. Given an ID there is no way to derive <name>, so the run watched only the bare-id path.

Observed failure

A gate asked in C0B9Z4CLG1J; a human replied in-thread 71 seconds later; the answer was never observed.

path entries newest
C0B9Z4CLG1J 30 2026-07-10
C0B9Z4CLG1J__watchdog-test 281 current

The reply was in …__watchdog-test/threads/1786974952_659989/replies/1786975023_841419 with thread_ts matching the question exactly.

Two earlier diagnoses were wrong and are worth recording so nobody repeats them: a missing /slack credential grant (it is mintable on demand via the cloud join route), and stale inbound sync (sync was healthy — the bare-id path was simply the wrong place to look). The subscription path was the entire problem.

Fix

The channel index already maps id -> title, and the runner already reads it to resolve a name to an id. This reads it the other way to recover the title, so the suffix is derived from data rather than from a guess about the caller's input. One file read, no pagination, no new API surface.

A missing or unreadable index is non-fatal: it logs that replies delivered to an <id>__<name> directory may be missed, rather than failing the run.

Rejected first attempt, for the record

Listing /slack/channels and matching directory names. That listing is paginated — 22 entries plus a cursor, alphabetically from C0AC… — so the target channel sat on a later page and the match silently returned nothing. It would have looked like a fix while doing nothing.

Verification

Live, end to end:

watching "/slack/channels/C0B9Z4CLG1J__watchdog-test/**" (resolved from the Slack channel index)
Received Slack human answer; injecting into ask-human-and-use-answer-38a3c2ef

Plus 10 new unit tests, including a regression guard that the id-slugified path is never produced. Suite 873 passed with the one pre-existing workflow-runner persona-runtime failure unchanged; no new typecheck errors.

⚠️ This required building packages/core by hand. npm run build --workspace=packages/core is red on main (7 pre-existing typecheck errors in builder.ts, persona-runtime.ts, runner.ts) and @relayflows/core resolves through dist/, so local runs in this repo silently execute the last successful build. Worth fixing separately — it makes local verification of any packages/core change misleading by default.

🤖 Generated with Claude Code


Summary by cubic

Ensure Slack human-assistance gates observe threaded replies when the caller provides a channel ID. Previously we subscribed to the bare <id> path and guessed the <id>__<slug> path from the input; now we resolve and subscribe to the real <id>__<name> path using the Slack channel index.

  • Read the synced Slack channel index to map id -> title and build the suffixed glob via new slackChannelTitleSlug and slackSuffixedChannelPath helpers; keep watching the bare-id and name-slug paths as fallbacks.
  • Treat a missing or unreadable index as non-fatal and log that replies in an <id>__<name> directory may be missed; do not fabricate the old id__id-slug path.
  • Resolve the index from /slack/channels/_index.json or /discovery/slack/channels/_index.json; this adds one read and no pagination.
  • Add tests for slugging, path construction, and a regression guard against the id__id-slug path; no API changes.

Written for commit 3b48816. Summary will update on new commits.

Review in cubic

@khaliqgant
khaliqgant force-pushed the fix/slack-gate-uses-integration-workspace branch from 363eb5b to 193a9d7 Compare August 17, 2026 14:32
@khaliqgant
khaliqgant force-pushed the fix/slack-answer-channel-dir-discovery branch from 3b48816 to 46117f1 Compare August 17, 2026 14:32
@khaliqgant
khaliqgant changed the base branch from fix/slack-gate-uses-integration-workspace to main August 17, 2026 14:32
A Slack human-assistance gate could post its question and then never see a
correctly threaded human answer, timing out after up to 24h with no indication
why.

Relayfile materializes a channel under BOTH `/slack/channels/<id>` and
`/slack/channels/<id>__<name>`, and delivers inbound messages and thread replies to
the suffixed one. `slackHumanAnswerSubscriptionPaths` derived that suffix by
slugifying whatever identifier the caller passed. Given a channel NAME that
reconstructs the real directory. Given a channel ID — the documented alternative,
and what a config referencing `C0...` uses — it produces
`C0B9Z4CLG1J__c0b9z4clg1j`, which does not exist, so the run watched only the
bare-id path.

Observed: a gate asked in `C0B9Z4CLG1J`, a human replied in-thread 71 seconds
later, and the answer was never observed. The bare-id directory held 30 entries
whose newest was five weeks old; the reply was in
`C0B9Z4CLG1J__watchdog-test/threads/1786974952_659989/replies/1786975023_841419`
alongside 281 current entries. Two earlier diagnoses — a missing `/slack` credential
grant, then stale inbound sync — were both wrong; sync was healthy and the
credential is mintable. The subscription path was the whole problem.

The channel index already maps id -> title, and the runner already reads it to
resolve a name to an id. This reads it the other way to recover the title, so the
suffix comes from data instead of a guess about the caller's input. A missing or
unreadable index is non-fatal and logs that replies delivered to an `<id>__<name>`
directory may be missed, rather than failing the run.

Rejected first attempt, for the record: listing `/slack/channels` and matching
directory names. That listing is paginated (22 entries plus a cursor, alphabetical),
so the target channel sat on a later page and the match silently returned nothing —
a fix that would have appeared to work while doing nothing. The index is one read
and needs no pagination.

Verified live, end to end: `watching "/slack/channels/C0B9Z4CLG1J__watchdog-test/**"
(resolved from the Slack channel index)` followed by `Received Slack human answer;
injecting into ask-human-and-use-answer-38a3c2ef`. Note this required building
packages/core by hand — `npm run build` is red on main (7 pre-existing typecheck
errors) and `@relayflows/core` resolves through `dist/`, so earlier local runs in
this repo silently executed a stale build.

10 new tests. Suite 873 passed with the one pre-existing workflow-runner
persona-runtime failure unchanged. No new typecheck errors.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@khaliqgant
khaliqgant force-pushed the fix/slack-answer-channel-dir-discovery branch from 46117f1 to f087adf Compare August 17, 2026 14:35
@khaliqgant
khaliqgant merged commit a7d4545 into main Aug 17, 2026
1 check passed
@coderabbitai

coderabbitai Bot commented Aug 17, 2026

Copy link
Copy Markdown
Contributor

Caution

Review failed

An error occurred during the review process. Please try again later.


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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to 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