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
Corrected 2 Sep 2026 against prototype/screens.jsx: the “Copy link” stub lives in ShareStreakSheet (line 411, onClick={() => setCopied(true)}), not at line 433.
What to build
A way to share a collectible. There is none today: the app's only share is the streak card, and the Cards tab has no share affordance at all.
This is the surface that makes #171 worth having — until it exists, nothing in the app can produce a /card/<id> link, and the only shareable URL is the marketing / one. The link itself now works; nothing hands one out.
⚠️ Net-new design, not a port
#34's resolution rules this explicitly. The prototype gestures at a link without ever defining one — its share sheet's "Copy link" tile is a stub that sets a flag and copies nothing (the ShareTarget tile in ShareStreakSheet, screens.jsx:411), and the Cards tab has no share entry point whatsoever. So a design pass is owed before a build one, on the same footing as the streak celebration rule from #26.
The payload is an image plus the URL. Render off-screen at a fixed logical size targeting ~1080px and hand it to the iOS system sheet through share_plus — the arrangement Streak surfaces: the streak screen, week strip, freeze tokens and share #26 shipped for the streak card, reused verbatim, with /card/<id> attached alongside.
The seam exists.SharePresenter (lib/services/share/) is the one thing in front of share_plus; today it offers sharePng only, so carrying a URL is a change to that contract.
The link resolves.PR #483 shipped the app half of #171 — /card/<id> opens the app on that card, and an unearned one shows its face. So there is now something at the other end of a link this ticket would mint, and the build can start.
Two things still sit in front of it, and neither is #171's app work:
The design. No share affordance exists on a collectible anywhere, so where it sits and what it looks like is owed before it is built.
Real-device proof only. The AASA file on brewpath.maximsan.dev is not served yet (the human step Universal links: the dedicated subdomain, the AASA file, and the card route #171 keeps), so a minted link cannot be tapped end to end until it is. That does not block writing or testing the share.
The unearned-card question that used to sit here is ruled — ADR-0015: a link shows the card's face and withholds its payload. That is what gives this share an audience, since the recipient has usually not earned what they are sent.
Acceptance criteria
A collectible can be shared from its sheet, and the share carries both the rendered image and its https://brewpath.maximsan.dev/card/<id> URL
The share affordance is designed before it is built — where it sits on the sheet and what it looks like, recorded where the ruling can be checked against it
No widget touches share_plus directly; the SharePresenter seam carries the URL, and a fake asserts both halves of the payload under test
What to build
A way to share a collectible. There is none today: the app's only share is the streak card, and the Cards tab has no share affordance at all.
This is the surface that makes #171 worth having — until it exists, nothing in the app can produce a
/card/<id>link, and the only shareable URL is the marketing/one. The link itself now works; nothing hands one out.#34's resolution rules this explicitly. The prototype gestures at a link without ever defining one — its share sheet's "Copy link" tile is a stub that sets a flag and copies nothing (the
ShareTargettile inShareStreakSheet,screens.jsx:411), and the Cards tab has no share entry point whatsoever. So a design pass is owed before a build one, on the same footing as the streak celebration rule from #26.What #34 already settled
share_plus— the arrangement Streak surfaces: the streak screen, week strip, freeze tokens and share #26 shipped for the streak card, reused verbatim, with/card/<id>attached alongside.SharePresenter(lib/services/share/) is the one thing in front ofshare_plus; today it offerssharePngonly, so carrying a URL is a change to that contract.Not blocked any more
The link resolves. PR #483 shipped the app half of #171 —
/card/<id>opens the app on that card, and an unearned one shows its face. So there is now something at the other end of a link this ticket would mint, and the build can start.Two things still sit in front of it, and neither is #171's app work:
brewpath.maximsan.devis not served yet (the human step Universal links: the dedicated subdomain, the AASA file, and the card route #171 keeps), so a minted link cannot be tapped end to end until it is. That does not block writing or testing the share.The unearned-card question that used to sit here is ruled — ADR-0015: a link shows the card's face and withholds its payload. That is what gives this share an audience, since the recipient has usually not earned what they are sent.
Acceptance criteria
https://brewpath.maximsan.dev/card/<id>URLshare_plusdirectly; theSharePresenterseam carries the URL, and a fake asserts both halves of the payload under test