Summary
The "Link" action added in PR #32 copies https://interlinedlist.com/messages/<id>. That is not the web app's shareable permalink — it is an authenticated route that redirects anonymous visitors to /login. Everyone a user shares a link with lands on a sign-in page instead of the message.
The web's shareable address is /user/<username>/status/<id>, and its Share menu offers two actions, of which we ship neither correctly:
- Copy link → the public permalink
- Get embed code → an HTML snippet for pasting into a blog/web page
Evidence (live, 2026-09-07)
GET https://interlinedlist.com/messages/d05faf17-… → 200, final URL = /login
GET https://interlinedlist.com/user/hubcity/status/d05f… → 200, serves the message (no auth)
/help/messages: "Every message has its own permanent page at an address like /user/<username>/status/<id> … Each message's action bar includes a Share button (the link icon) with two options: Copy link … Get embed code … Only public messages can be linked to or embedded for other people."
What we ship today
Packages/InterlinedDomain/Sources/InterlinedDomain/Models/MessagePermalink.swift — builds <base>/messages/<id>, documented as "matching the web app's own route". It does not.
- Three call sites consume it: the message row's Link action, the "Push & Comment" body, and
CreateIssueFromMessageViewModel's issue body — so a wrong URL is currently pasted into GitHub issues as well.
Division of labor
Domain — MessagePermalink
Domain — Message
App
Tests (BDD quartet)
Acceptance criteria
- Copying a link from a public message and opening it in a private browser window renders the message, not
/login.
- The URL in a GitHub issue created from a message resolves for someone who is not signed in.
- Private messages expose no Link/Embed action.
- Full E2E gate green (
.claude/skills/swift-engineer/assets/e2e-gate-checklist.md).
Notes
Found during the 2026-09-07 parity sweep. Nothing regressed — the action shipped with the wrong shape in PR #32 — but it is user-visible on dev today, so it is first in the queue.
Summary
The "Link" action added in PR #32 copies
https://interlinedlist.com/messages/<id>. That is not the web app's shareable permalink — it is an authenticated route that redirects anonymous visitors to/login. Everyone a user shares a link with lands on a sign-in page instead of the message.The web's shareable address is
/user/<username>/status/<id>, and its Share menu offers two actions, of which we ship neither correctly:Evidence (live, 2026-09-07)
/help/messages: "Every message has its own permanent page at an address like/user/<username>/status/<id>… Each message's action bar includes aSharebutton (the link icon) with two options: Copy link … Get embed code … Only public messages can be linked to or embedded for other people."What we ship today
Packages/InterlinedDomain/Sources/InterlinedDomain/Models/MessagePermalink.swift— builds<base>/messages/<id>, documented as "matching the web app's own route". It does not.CreateIssueFromMessageViewModel's issue body — so a wrong URL is currently pasted into GitHub issues as well.Division of labor
Domain —
MessagePermalink<base>/user/<username>/status/<id>. The author handle becomes a required input, so the API becomesurl(forMessageID:authorUsername:base:)./fromurlPathAllowed).nilwhen the author handle is missing or blank, so the UI hides the affordance rather than offering a broken link.embedHTML(forMessageID:authorUsername:base:)only after the web's snippet is captured verbatim — do not invent the markup. Probe: open a public message on the web, click Share ▸ Get embed code, paste the clipboard into this thread, then implement to match.Domain —
MessageMessageat every call site (timeline row, detail view, push-and-comment, create-issue). If a projection drops it, carry it.App
MessageRowActions/MessageRowView: the Link action copies the corrected permalink; add a sibling Copy embed code action once the snippet is known.CreateIssueFromMessageViewModeland the Push & Comment body pick up the corrected URL for free; assert it in their tests.Tests (BDD quartet)
/user/<handle>/status/<id>nil, affordance hiddenbasewith a trailing slashAcceptance criteria
/login..claude/skills/swift-engineer/assets/e2e-gate-checklist.md).Notes
Found during the 2026-09-07 parity sweep. Nothing regressed — the action shipped with the wrong shape in PR #32 — but it is user-visible on
devtoday, so it is first in the queue.