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
Every link in rendered source-backed content that sends a reader to the DataTalks.Club Slack
community lands on this site's own /slack page instead of going straight to an external
Slack URL. /slack is the one canonical destination for community-Slack links.
Owner note (recorded at intake): the /slack page itself does not need to show a Slack
join/invite link. That is an explicit non-goal; the missing invite is not a gap.
Normative links
_docs/specs/02-url-link-seo-compatibility.md § "Route rules that must remain exact"
(historical hub aliases redirect permanently in one hop, queries preserved; no catch-all
redirects) and § "Link preservation checks" (rendered-page link resolution).
_docs/specs/01-platform-architecture.md § "Member signup, profile, Slack, and course
registration" (the authenticated Slack-access reveal is a different, member-flow surface —
see Non-goals).
/slack is served by content/review_views.py::slack from content/review_projection.json (slack record: title, lead, 8 channel chips, one
troubleshooting CTA) and is primary navigation
(templates/core/_site_shell_head.html:25,197).
Route and alias contract is exact and tested
(content/tests/test_canonical_routes.py::test_slack_route_and_alias_have_exact_method_and_query_contracts): GET/HEAD /slack → 200 with canonical https://datatalks.club/slack; POST → 405
(Allow: GET, HEAD, no-store); GET/HEAD /slack.html?<query> → 301 to /slack?<query> preserving the raw query byte-for-byte (website/urls.py:73).
Render-time hub-alias rewrites to /slack already exist:
Docs (content/docs_projection.py_INTERNAL_HUB_PATHS, lines 145-152): /slack.html
and /slack/guidelines.html, both relative and https://datatalks.club absolute, inside
Markdown link spans, preserving query and fragment.
Course FAQ (content/faq_data.py::render_faq_answer, the https?://datatalks.club/slack.html substitution).
Article FAQ (content/article_faq.py::_rewrite_legacy_links, same family).
Remaining inventory (checked projections at main)
Verified by scanning Markdown link destinations in content/docs_projection.json and content/faq_projection.json:
Workspace deep links in Docs — the core gap. 32 distinct external Slack URLs for this
workspace (31 https://app.slack.com/client/T01ATQK62F8/<channel> plus https://datatalks-club.slack.com/archives/<channel>), 72 link occurrences across 16 pages,
concentrated in /docs/general/slack/ (channel directory), course "getting started",
"resources", and zoomcamp-logistics pages, and community guidelines pages. All still go
straight to Slack.
FAQ workspace links. One app.slack.com/client/T01ATQK62F8/... link and one datatalks-club.slack.com/archives/... link, in answers rendered on /faq/machine-learning-zoomcamp.html and /faq/llm-zoomcamp.html.
Absolute canonical-form links in Docs. 6 pages link https://datatalks.club/slack (already the right destination, wrong form: absolute
same-host instead of root-relative /slack). The Repair stale and broken links in rendered documentation #167 map does not normalize it.
Classification of what is not a community-Slack link and must stay untouched:
https://slack.com/help/articles/... (2 in Docs, 1 in FAQ): Slack's own product
documentation, cited as reference, not an entry to our community.
/docs/general/slack/ and {{ '/general/slack/' | relative_url }} links: Docs-internal
pages about Slack, already routed correctly by _docs_url.
Event descriptions: content/event_description_link_policy.py already removes app.slack.com (provider action) and forbids join.slack.com (join host) in reviewed event
descriptions. There are no join.slack.com invite links anywhere in the Docs projection.
Destination contract
The canonical destination for community-Slack links in rendered source-backed content is
the root-relative /slack (canonical https://datatalks.club/slack).
A link is a community-workspace link when its host is datatalks-club.slack.com (any
path) or its host is app.slack.com and its first path segment is T01ATQK62F8 (this
workspace's ID). Workspace links rewrite to /slack and drop their query and fragment:
both address Slack's own UI (/client/..., /archives/...), not anchors on /slack.
Hub aliases (/slack.html, /slack/guidelines.html, absolute https://datatalks.club/slack)
continue to rewrite to /slack preserving query and fragment per the existing Repair stale and broken links in rendered documentation #167
mechanism; absolute https://datatalks.club/slack joins the map so it normalizes to
root-relative.
Preserving a ?utm_*/?source= tag on hub-alias rewrites stays safe: /slack has no
strict query grammar (the view ignores the query string), so the catalogue-query strictness
addressed in 643ea32 does not apply here.
Rewrites happen at render time, keeping the checked projections byte-identical — the
architecture Repair stale and broken links in rendered documentation #167 set and the immutability test enforces
(content/tests/test_docs_projection.py::test_rendering_keeps_projection_source_and_metadata_immutable).
Scope
Extend the render-time link policy in content/docs_projection.py with the
community-workspace rule above (Docs Markdown link destinations only, not literal text or
code spans, matching the existing span-scanning approach).
Extend content/faq_data.py::render_faq_answer with the same community-workspace rule.
Normalize absolute https://datatalks.club/slack link destinations in Docs to /slack.
Add the missing legacy alias: GET/HEAD /slack/guidelines.html?<query> → 301 /slack?<query> in one hop, query preserved, with the same method/cache contract as /slack.html (website/urls.py, alongside line 73). This serves inbound legacy links that
today get a 404, per spec 02's no-unexplained-404 parity rule.
Documentation: add one clarifying sentence to _docs/specs/01-platform-architecture.md
§ "Member signup, profile, Slack, and course registration" distinguishing the public /slack landing page from the specified authenticated /accounts/community/slack/ reveal surface. No change to that member-flow contract.
Focused tests: new rewrites (Docs and FAQ), unaffected links byte-for-byte, the new
redirect's method/query contract, and projection immutability.
Non-goals
No join/invite link on /slack (owner note above) and no change to the /slack page's
content, channels list, or design.
No per-channel anchors on /slack (e.g. /slack#ml-zoomcamp): the page lists channels as
plain chips today; adding 30+ targets is a separate product decision. Deep links rewrite to
plain /slack, accepting the loss of channel-specific landing.
No edits to content/docs_projection.json, content/faq_projection.json, or the upstream
content source; the immutability tests must stay green.
No changes to the event-description link policy (already removes these hosts there) or the
article-FAQ pipeline (zero workspace links in content/article_faq.json today; adding
untestable rewrite code there is not part of this slice).
No rewriting of slack.com/help/... product documentation links or any other external host.
No decision here on the member-flow Slack-access reveal: whether/how /accounts/community/slack/ gets built stays with the member-onboarding flow and _docs/specs/open-decisions.md (open decision 19). This issue only clarifies the wording.
Rendered Docs Markdown link destinations matching the community-workspace rule emit href="/slack"; no rendered Docs page emits href="https://app.slack.com/client/T01ATQK62F8/…"
or href="https://datatalks-club.slack.com/…".
All 16 affected Docs pages and both affected FAQ answers are covered by focused tests
that assert the rewrite (the FAQ test asserts the rendered answer HTML, not the source).
Absolute https://datatalks.club/slack link destinations in Docs render as
root-relative /slack with query and fragment preserved.
Links to slack.com/help/..., /docs/general/slack/, and every unrelated external link
remain byte-for-byte unchanged in rendered output.
GET/HEAD /slack/guidelines.html?x=1 returns 301 to /slack?x=1 preserving the raw
query; POST returns 405 with Allow: GET, HEAD and Cache-Control: no-store, max-age=0,
matching the /slack.html contract.
content/docs_projection.json and content/faq_projection.json are byte-identical
before and after rendering (existing immutability tests stay green and cover the new rule).
Link-preservation/compat checks over rendered pages stay green with the rewritten links
resolved internally against /slack.
Spec 01 clarification sentence added; the member-flow contract text is otherwise
unchanged.
Tester screenshots at desktop and mobile for /docs/general/slack/, /docs/courses/ml-zoomcamp/getting-started/, /faq/machine-learning-zoomcamp.html, and /slack show the expected pages with working links, not errors or broken layouts.
Browser scenarios
Open /docs/general/slack/; every channel entry links to /slack; following one lands on /slack (community page with channel chips), not Slack.
Open /docs/courses/ml-zoomcamp/getting-started/; the community/asking-questions links
land on /slack; a slack.com/help/... citation still opens Slack's own article in a new
context without being redirected.
Open /faq/machine-learning-zoomcamp.html; the "How do I get help if I'm stuck?" answer's
Slack link lands on /slack.
Visit /slack/guidelines.html?utm_source=docs; the browser ends at /slack?utm_source=docs in one hop showing the community page.
Repeat 1-3 at mobile width; content and links remain readable and usable.
User outcome
Every link in rendered source-backed content that sends a reader to the DataTalks.Club Slack
community lands on this site's own
/slackpage instead of going straight to an externalSlack URL.
/slackis the one canonical destination for community-Slack links.Owner note (recorded at intake): the
/slackpage itself does not need to show a Slackjoin/invite link. That is an explicit non-goal; the missing invite is not a gap.
Normative links
_docs/specs/02-url-link-seo-compatibility.md§ "Route rules that must remain exact"(historical hub aliases redirect permanently in one hop, queries preserved; no catch-all
redirects) and § "Link preservation checks" (rendered-page link resolution).
_docs/specs/01-platform-architecture.md§ "Member signup, profile, Slack, and courseregistration" (the authenticated Slack-access reveal is a different, member-flow surface —
see Non-goals).
extends.
Current state at
main(9603342)Already canonical today
/slackis served bycontent/review_views.py::slackfromcontent/review_projection.json(slackrecord: title, lead, 8 channel chips, onetroubleshooting CTA) and is primary navigation
(
templates/core/_site_shell_head.html:25,197).(
content/tests/test_canonical_routes.py::test_slack_route_and_alias_have_exact_method_and_query_contracts):GET/HEAD /slack→ 200 with canonicalhttps://datatalks.club/slack;POST→ 405(
Allow: GET, HEAD,no-store);GET/HEAD /slack.html?<query>→ 301 to/slack?<query>preserving the raw query byte-for-byte (website/urls.py:73)./slackalready exist:content/docs_projection.py_INTERNAL_HUB_PATHS, lines 145-152):/slack.htmland
/slack/guidelines.html, both relative andhttps://datatalks.clubabsolute, insideMarkdown link spans, preserving query and fragment.
content/faq_data.py::render_faq_answer, thehttps?://datatalks.club/slack.htmlsubstitution).content/article_faq.py::_rewrite_legacy_links, same family).Remaining inventory (checked projections at
main)Verified by scanning Markdown link destinations in
content/docs_projection.jsonandcontent/faq_projection.json:workspace (31
https://app.slack.com/client/T01ATQK62F8/<channel>plushttps://datatalks-club.slack.com/archives/<channel>), 72 link occurrences across 16 pages,concentrated in
/docs/general/slack/(channel directory), course "getting started","resources", and
zoomcamp-logisticspages, and community guidelines pages. All still gostraight to Slack.
app.slack.com/client/T01ATQK62F8/...link and onedatatalks-club.slack.com/archives/...link, in answers rendered on/faq/machine-learning-zoomcamp.htmland/faq/llm-zoomcamp.html.https://datatalks.club/slack(already the right destination, wrong form: absolutesame-host instead of root-relative
/slack). The Repair stale and broken links in rendered documentation #167 map does not normalize it.Classification of what is not a community-Slack link and must stay untouched:
https://slack.com/help/articles/...(2 in Docs, 1 in FAQ): Slack's own productdocumentation, cited as reference, not an entry to our community.
/docs/general/slack/and{{ '/general/slack/' | relative_url }}links: Docs-internalpages about Slack, already routed correctly by
_docs_url.content/event_description_link_policy.pyalready removesapp.slack.com(provider action) and forbidsjoin.slack.com(join host) in reviewed eventdescriptions. There are no
join.slack.cominvite links anywhere in the Docs projection.Destination contract
the root-relative
/slack(canonicalhttps://datatalks.club/slack).datatalks-club.slack.com(anypath) or its host is
app.slack.comand its first path segment isT01ATQK62F8(thisworkspace's ID). Workspace links rewrite to
/slackand drop their query and fragment:both address Slack's own UI (
/client/...,/archives/...), not anchors on/slack./slack.html,/slack/guidelines.html, absolutehttps://datatalks.club/slack)continue to rewrite to
/slackpreserving query and fragment per the existing Repair stale and broken links in rendered documentation #167mechanism; absolute
https://datatalks.club/slackjoins the map so it normalizes toroot-relative.
?utm_*/?source=tag on hub-alias rewrites stays safe:/slackhas nostrict query grammar (the view ignores the query string), so the catalogue-query strictness
addressed in 643ea32 does not apply here.
architecture Repair stale and broken links in rendered documentation #167 set and the immutability test enforces
(
content/tests/test_docs_projection.py::test_rendering_keeps_projection_source_and_metadata_immutable).Scope
content/docs_projection.pywith thecommunity-workspace rule above (Docs Markdown link destinations only, not literal text or
code spans, matching the existing span-scanning approach).
content/faq_data.py::render_faq_answerwith the same community-workspace rule.https://datatalks.club/slacklink destinations in Docs to/slack.GET/HEAD /slack/guidelines.html?<query>→ 301/slack?<query>in one hop, query preserved, with the same method/cache contract as/slack.html(website/urls.py, alongside line 73). This serves inbound legacy links thattoday get a 404, per spec 02's no-unexplained-404 parity rule.
_docs/specs/01-platform-architecture.md§ "Member signup, profile, Slack, and course registration" distinguishing the public
/slacklanding page from the specified authenticated/accounts/community/slack/reveal surface. No change to that member-flow contract.redirect's method/query contract, and projection immutability.
Non-goals
/slack(owner note above) and no change to the/slackpage'scontent, channels list, or design.
/slack(e.g./slack#ml-zoomcamp): the page lists channels asplain chips today; adding 30+ targets is a separate product decision. Deep links rewrite to
plain
/slack, accepting the loss of channel-specific landing.content/docs_projection.json,content/faq_projection.json, or the upstreamcontent source; the immutability tests must stay green.
article-FAQ pipeline (zero workspace links in
content/article_faq.jsontoday; addinguntestable rewrite code there is not part of this slice).
slack.com/help/...product documentation links or any other external host./accounts/community/slack/gets built stays with the member-onboarding flow and_docs/specs/open-decisions.md(open decision 19). This issue only clarifies the wording.Dependencies
renderer (Add source-backed navigation to the Documentation site #176, commit 896799b) are on
main.Acceptance criteria
href="/slack"; no rendered Docs page emitshref="https://app.slack.com/client/T01ATQK62F8/…"or
href="https://datatalks-club.slack.com/…".that assert the rewrite (the FAQ test asserts the rendered answer HTML, not the source).
https://datatalks.club/slacklink destinations in Docs render asroot-relative
/slackwith query and fragment preserved.slack.com/help/...,/docs/general/slack/, and every unrelated external linkremain byte-for-byte unchanged in rendered output.
GET/HEAD /slack/guidelines.html?x=1returns 301 to/slack?x=1preserving the rawquery;
POSTreturns 405 withAllow: GET, HEADandCache-Control: no-store, max-age=0,matching the
/slack.htmlcontract.content/docs_projection.jsonandcontent/faq_projection.jsonare byte-identicalbefore and after rendering (existing immutability tests stay green and cover the new rule).
resolved internally against
/slack.unchanged.
/docs/general/slack/,/docs/courses/ml-zoomcamp/getting-started/,/faq/machine-learning-zoomcamp.html, and/slackshow the expected pages with working links, not errors or broken layouts.Browser scenarios
/docs/general/slack/; every channel entry links to/slack; following one lands on/slack(community page with channel chips), not Slack./docs/courses/ml-zoomcamp/getting-started/; the community/asking-questions linksland on
/slack; aslack.com/help/...citation still opens Slack's own article in a newcontext without being redirected.
/faq/machine-learning-zoomcamp.html; the "How do I get help if I'm stuck?" answer'sSlack link lands on
/slack./slack/guidelines.html?utm_source=docs; the browser ends at/slack?utm_source=docsin one hop showing the community page.