fix(apps): resolve the Let's Chat link to chat.{domain}, not the slug (BUNYIP-533) #531

Merged
longjacksonle merged 1 commit from fix/BUNYIP-533-lets-chat-subdomain into main 2026-08-13 04:24:50 +02:00

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-container green (fmt + clippy -D warnings + workspace tests), including the new app_link regression test.
  • Migration immutability guard green (new file, no committed migration touched).
  • No visual layout change: the footer renders identically; only the resolved href changes, which the app_link test asserts.

Acceptance

  • After the migration, the footer and hub "Let's Chat" links resolve to https://chat.a8n.systems/ on staging and https://chat.spa.systems/ on production.
  • A fresh database without a lets-chat application row is unaffected.
  • Re-running the migration changes nothing.

🤖 Generated with Claude Code

https://claude.ai/code/session_01GkNbvEq6awuMRULFCiYKe3

## 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-container` green (fmt + clippy -D warnings + workspace tests), including the new app_link regression test. - Migration immutability guard green (new file, no committed migration touched). - No visual layout change: the footer renders identically; only the resolved href changes, which the app_link test asserts. ## Acceptance - After the migration, the footer and hub "Let's Chat" links resolve to https://chat.a8n.systems/ on staging and https://chat.spa.systems/ on production. - A fresh database without a lets-chat application row is unaffected. - Re-running the migration changes nothing. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_01GkNbvEq6awuMRULFCiYKe3
fix(apps): resolve the Let's Chat link to chat.{domain}, not the slug (BUNYIP-533)
All checks were successful
E2E / PR gate (pull_request) Successful in 1m47s
Check / fmt + clippy + build + tests (pull_request) Successful in 14m18s
Create release / Create release from merged PR (pull_request) Has been skipped
43e043b8d4
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
longjacksonle scheduled this pull request to auto merge when all checks succeed 2026-08-13 04:10:58 +02:00
longjacksonle deleted branch fix/BUNYIP-533-lets-chat-subdomain 2026-08-13 04:24:50 +02:00
Sign in to join this conversation.
No reviewers
No labels
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
psa-systems/bunyip!531
No description provided.