fix(apps): resolve the Let's Chat link to chat.{domain}, not the slug (BUNYIP-533) #531
Loading…
Reference in a new issue
No description provided.
Delete branch "fix/BUNYIP-533-lets-chat-subdomain"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
What and why
Resolves BUNYIP-533. On staging the "Let's Chat" link in the marketing footer (and the hub launch tile) resolved to https://lets-chat.a8n.systems/, which does not exist. It should be https://chat.a8n.systems/ on staging and https://chat.spa.systems/ on production.
Root cause
The footer builds each product link with app_link(app, app_domain), which returns https://{subdomain or slug}.{app_domain}. The domain half is BUNYIP_APP_DOMAIN (a8n.systems on staging, spa.systems on production), so it is already correct per environment. The subdomain half falls back to the application slug when the row has no subdomain set. The Let's Chat application row has slug "lets-chat" and no subdomain, so the link resolved to lets-chat.{app_domain} instead of chat.{app_domain}. The product is served at the "chat" subdomain; the OIDC-client migration 20260618032217 already names chat.a8n.systems as the canonical host.
Fix
A forward migration sets the lets-chat application's subdomain to "chat", so app_link resolves to https://chat.{app_domain}: chat.a8n.systems on staging and chat.spa.systems on production, from the single correct subdomain value (only the domain half varies per environment).
The migration is a targeted UPDATE guarded to the states that produce the wrong link (subdomain unset, empty, or the slug echoed back). It corrects the existing rows on staging and production, is a no-op on a fresh database that has no lets-chat row (those rows are admin-created, per migration 20241230000017), leaves a deliberate admin override alone, and is idempotent. Committed migrations are immutable, so this is a new file.
Not changed
app_link itself is correct: the subdomain field exists precisely so a launch host can differ from the slug. This is a data correction, not a link-builder change. A regression test on app_link locks the intended behavior (subdomain overrides slug; no apex domain yields a neutral href).
Verification
just check-containergreen (fmt + clippy -D warnings + workspace tests), including the new app_link regression test.Acceptance
🤖 Generated with Claude Code
https://claude.ai/code/session_01GkNbvEq6awuMRULFCiYKe3
The marketing footer and hub build each product link with app_link(app, app_domain), which returns https://{subdomain or slug}.{app_domain}. The domain half is BUNYIP_APP_DOMAIN (a8n.systems on staging, spa.systems on production), so it is already correct per environment; the subdomain half falls back to the application slug when the row has no subdomain set. The Let's Chat application row has slug "lets-chat" and no subdomain, so the link resolved to lets-chat.{app_domain} instead of chat.{app_domain}. The product is served at the "chat" subdomain (the OIDC-client migration 20260618032217 already names chat.a8n.systems as the canonical host). Fix: a forward migration sets the lets-chat application's subdomain to "chat", so app_link resolves to https://chat.a8n.systems/ on staging and https://chat.spa.systems/ on production from the single correct subdomain value. It is a targeted UPDATE guarded to the states that produce the wrong link (subdomain unset, empty, or the slug echoed back), so it corrects existing staging and production rows, is a no-op on a fresh database that has no lets-chat row, leaves a deliberate admin override alone, and is idempotent. app_link itself is correct - the subdomain field exists precisely so a launch host can differ from the slug - so this is a data correction, not a link-builder change. A regression test on app_link locks the intended behavior (subdomain overrides slug; no apex domain yields a neutral href). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GkNbvEq6awuMRULFCiYKe3