Skip to content

fix(slack): resolve dead product.slack.com citation links#55

Open
rajivml wants to merge 4 commits into
feature/darwinfrom
feature/slack_link_fix
Open

fix(slack): resolve dead product.slack.com citation links#55
rajivml wants to merge 4 commits into
feature/darwinfrom
feature/slack_link_fix

Conversation

@rajivml

@rajivml rajivml commented Jul 17, 2026

Copy link
Copy Markdown
Collaborator

Problem

Clicking a Slack citation in Darwin opened product.slack.com, a dead host — users couldn't reach the message (reported in #help-darwin).

Root cause is data, not the format string: Slack connectors were configured with the workspace display name ("Product", plus a "Product " trailing-space variant) instead of the URL subdomain (uipath-product), so every indexed permalink was built as https://Product.slack.com/... → browsers lowercase it → dead product.slack.com.

Blast radius: ~320,670 docs across 913 channels. Verified (via chat.getPermalink on a 110-channel sample) that all indexed Slack content genuinely lives in the uipath-product workspace.

Fix — two layers, no re-index / data migration

Read-time (fixes all existing docs). normalize_slack_link() rewrites the known-bad product subdomain → uipath-product via an explicit allow-map (case/whitespace-insensitive), applied once in _vespa_hit_to_inference_chunk. Every citation — chat, search, and the Slack bot — derives its link from source_links, so this single choke point covers all three. Genuinely different workspaces (uipath-customer-ops, uipath-marketing, …) and already-correct links are left untouched.

Index-time (prevents recurrence for any workspace). get_all_docs now resolves each channel's true workspace subdomain once via chat.getPermalink (correct even under Enterprise Grid, where a bot spans workspaces), caching per channel. The hand-typed workspace config is now only a fallback. The call goes through the connector's standard make_slack_api_rate_limited wrapper.

Validation (read-only, against prod)

  • Normalized link == Slack's authoritative chat.getPermalink for 40/40 sampled docs (host + path).
  • Real keyword_retrieval path returned 10/10 uipath-product links.
  • 14 unit tests (allow-map edge cases + resolver success/fallback); mypy + pre-commit clean.

Deploy status

Backend deployed to prod as vha-217 and validated live (retrieval on the shipped code → 10/10 normalized). The rate-limit hardening (86041d9b) is index-time-only and will ship in the next backend build.

Follow-ups (not in this PR)

  • Optional: backfill Postgres document.link + Vespa source_links, then retire the read-time shim.
  • The admin document view (server/manage/administrative.py) serves Postgres document.link directly, so it still shows the old host until a backfill — not in the user-facing citation path.

🤖 Generated with Claude Code

rajivml and others added 4 commits July 17, 2026 15:26
Slack docs were indexed with the workspace stored as its display name
("Product", plus a "Product " space variant), so every citation permalink
pointed at the dead host product.slack.com. ~320k docs (913 channels)
affected; all verified to genuinely live in the uipath-product workspace.

Two layers, no re-index / data migration required:

- Read-time: normalize_slack_link() rewrites the known-bad "product" subdomain
  to "uipath-product" via an explicit allow-map (case/space-insensitive), applied
  in _vespa_hit_to_inference_chunk. Every citation (chat, search, Slack bot)
  derives its link from source_links, so one choke point fixes all three.
  Genuinely different workspaces (uipath-customer-ops, uipath-marketing, ...)
  are left untouched.
- Index-time: get_all_docs now resolves each channel's true workspace subdomain
  once via chat.getPermalink (Grid-correct), so new scrapes are right regardless
  of the configured workspace; the configured value is only a fallback.

Verified on prod (read-only): normalized links matched Slack's authoritative
chat.getPermalink for 40/40 sampled docs, and the real keyword_retrieval path
returned 10/10 uipath-product links.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…call

resolve_workspace_subdomain called client.chat_getPermalink raw, bypassing the
make_slack_api_rate_limited / make_slack_api_call_logged wrappers that every
other Slack API call in this connector uses. On a 429 it would fall back to the
configured workspace instead of retrying with Retry-After. Wrap it so a new
workspace's first scrape resolves correctly under rate limiting. Index-time only
(one call per channel); no behavior change to the read-time citation fix.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
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