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
Successor to chat#1859 (closed — the P1 canonical-artist funnel fix shipped + verified: a brand-new customer now completes a valuation and sees their number on chat.recoupable.dev, witnessed live). That issue made the funnel work for new customers. This one extends the same catalog-valuation experience to existing customers — and lands the homepage-honesty fleet that was in flight when #1859 closed. (Business context, once: new-signup coverage is done; the retention win now is the installed base seeing value on day one of the feature.)
Goal
Every rostered account — not just funnel-claimed ones — opens chat.recoupable.dev and sees an honest catalog valuation for their artists, and the homepage modules read truthfully for everyone:
Coverage: an account that has artists in its roster but never ran the funnel gets a catalog materialized from its roster → songs graph, so the artist-scoped hero (GET /api/catalogs/{catalogId}/measurements?artist_account_id=…) returns a value. Today 588 of 608 rostered accounts (~97%) see nothing, all because no catalog was ever created (analysis).
Homepage honesty (all customers): task runs carry real titles, the starter card offers the valuation report, in-progress valuations are labeled partial, and the $0/0-stream release tail is collapsed.
Handoff polish: intent resume after OTP is reliable; the marketing header reflects auth state (+ logout).
Integrity: the two valuation-formula implementations cannot silently drift.
Done at the macro level when: the coverage query (in the analysis) returns ~0 accounts-with-roster-but-no-valuation, and a returning existing customer opens the homepage and sees an honest valuation for a roster artist — witnessed live on prod.
PRs (updated 2026-07-08 — ship-priority order, top to bottom; 10 open, migrated from #1859; 1 new item)
PR
Item
Base
State
tbd — design note → api + chat
⭐ HIGH: auto-fill catalogs for all rostered accounts — materialize a catalog from each account's roster→songs graph so the hero shows a valuation for every account
main
⬜ not started — the single biggest coverage lever (588/608, analysis); needs the decision note first (trigger, cost control at scale, refresh cadence, ownership)
🔄 open — docs-only; ⚠ CONFLICTING with main, needs a rebase
tbd — marketing
Label in-progress valuations as partial on the card
main
⬜ not started (completed runs reconcile — only partial-as-final display remains)
Merge sequencing: the ⭐ auto-fill is the headline but design-gated — write the decision note first, then it splits into api (catalog-materialization trigger/job) + chat (surface) PRs. The homepage-honesty fleet is independent and ready to land now: titles trio docs#268 → api#764 → chat#1857 (contract-first; chat safe either side), then chat#1856 / marketing#42 / marketing#45 / marketing#43 in any order; drift-guard api#765 + marketing#44 land as a same-day pair; mono#45 needs a rebase before it can merge.
Open — coverage (the headline: existing customers)
⭐ HIGH — Auto-fill the account catalog for pre-funnel accounts (roster → songs graph), so the hero lights up for every account. (raised by sweetman 2026-07-06; prototype worked — 15 artists / 880 releases via measurement-jobs + catalogs/songs)
Fix (needs a decision note first): decide trigger (on artist-add? on first chat? a one-time backfill sweep?), cost control at 588-account scale (measurement-job spend across every roster), catalog shape, refresh cadence, ownership semantics — then implement the materialization (api) + surface (chat).
Done when: the coverage query returns ~0 accounts-with-roster-but-no-valuation; a returning existing customer sees a valuation for a roster artist, live on prod.
Open — homepage honesty (all customers; the in-flight fleet)
Why: run rows all read "Scheduled Task" (verified live 2026-07-07). Not observable on a day-one account (no runs yet) — bites on the return visit, so it matters most for existing customers. Perf note: one Trigger.dev call per schedule per list load — cache/TTL if schedule counts grow.
Done when: signed-in homepage lists runs by task title (fallback intact); verified live post-deploy.
Why: starter card offered a generic dashboard, not the valuation report (re-confirmed on a fresh account 2026-07-08); the card can present a partial measurement as final (07-06: $1.4M partial vs $2.1M complete); 22 $0 · 0 streams rows sat above the CTA (07-08).
Intent resume: "Value my catalog" auto-runs after the OTP — every time. (→ marketing#45) The pendingRun ref dies on the remount fresh-signup can cause; both 07-08 walkthroughs auto-resumed, so the failure is intermittent — ship I add new tool #45 as hardening. Done when: search → Value → OTP → runs with no further clicks, on a fresh account.
Marketing header auth state (+ logout). (→ marketing#43) Header shows Sign In/Sign Up while a live token sits in localStorage; the missing logout is half of the stale-session dead end below.
Drift guard: valuation-formula lockstep. (→ api#765 · marketing#44) Formula implemented twice; zero divergence confirmed live 2026-07-08 but unguarded. Hardening nit: assert the fixture SHA in each test so a lone-repo edit fails loudly. Done when: both suites green against the identical fixture; a single-repo formula change fails that repo's CI.
Root AGENTS.md cleanup. (→ mono#45) Docs-only; needs a rebase (currently conflicting).
Open — UX dead ends (found 2026-07-08, no PR yet)
Stale-Privy-session dead end — "User already has one email account linked." On a device holding a leftover Privy session (the httpOnly cookie at privy.recoupable.dev survives localStorage/cookie clears, and marketing has no logout), a new email + valid OTP fails with a cryptic error and no recovery path (reproduced 2026-07-08). Fix: handle the link-conflict error — log out the stale session and retry as a fresh login; land marketing#43's logout as the manual escape hatch. Done when: on a device holding account A's session, account B's email + valid OTP signs into B (or a one-click recovery is offered), verified live.
Circular homepage CTA — catalog-owning accounts are offered "Get a free catalog valuation →" back to marketing. (witnessed 2026-07-08). The P1 hero should replace it once it lights up, but the fallback must never offer the funnel to an account that already owns a catalog. Done when: an account owning ≥1 catalog never sees the marketing valuation CTA on the homepage.
Open — carried funnel/platform items (from #1859, unblocked, pick from the top)
Verify-and-close: the 1,000-row aggregate cap. Believed fixed by api#763 (merged 2026-07-07). One authed check: GET /api/catalogs/7d3e5c35…/measurements?limit=1 reports measured_song_count 2,679 and a band consistent with 16.31B streams → then close.
api: the artistName filter on GET /api/catalogs/songs no-ops on unlinked songs. Latent since api#681 (LEFT-join); returns every row for a non-member artist. No longer load-bearing for the hero — still a correctness bug for other consumers. RED test first: filtered query on a catalog with unlinked songs returns 0.
Data bug: lowercased Spotify artist ids in socials.profile_url (10 of 15 roster artists) — album resolution 502s. api#768's ingest is case-preserved (forward path); remaining: find the normalize-on-write culprit + backfill existing rows (lower(id) match makes it mechanical).
Ops: chat previews' Privy test app is at its user cap. Blocks fresh-account testing on previews. Workaround: fresh-account passes run on prod with plus-aliases. Fix: raise the cap in the Privy dashboard; decide the preview api-base story (previews call test-recoup-api.vercel.app).
Ops nit: flip the recoupable.com redirect from 307 to 301/308 (Vercel domain settings) so six years of .com link equity transfers.
Valuation band derived at read time, server-side; one formula. Two implementations temporarily guarded by the byte-identical golden fixture; a shared package is the endgame.
Artist scoping happens inside the measurements endpoint (artist_account_id filter via catalog_songs ∩ song_artists); per-artist catalogs rejected. REST shape: path for identity, query for modifiers.
Successor to chat#1859 (closed — the P1 canonical-artist funnel fix shipped + verified: a brand-new customer now completes a valuation and sees their number on
chat.recoupable.dev, witnessed live). That issue made the funnel work for new customers. This one extends the same catalog-valuation experience to existing customers — and lands the homepage-honesty fleet that was in flight when #1859 closed. (Business context, once: new-signup coverage is done; the retention win now is the installed base seeing value on day one of the feature.)Goal
Every rostered account — not just funnel-claimed ones — opens
chat.recoupable.devand sees an honest catalog valuation for their artists, and the homepage modules read truthfully for everyone:GET /api/catalogs/{catalogId}/measurements?artist_account_id=…) returns a value. Today 588 of 608 rostered accounts (~97%) see nothing, all because no catalog was ever created (analysis).$0/0-stream release tail is collapsed.Done at the macro level when: the coverage query (in the analysis) returns ~0 accounts-with-roster-but-no-valuation, and a returning existing customer opens the homepage and sees an honest valuation for a roster artist — witnessed live on prod.
PRs (updated 2026-07-08 — ship-priority order, top to bottom; 10 open, migrated from #1859; 1 new item)
mainTaskRunResponsegains nullabletitle(contract)mainfilter[schedule]lookup againstscheduled_actionsmainHomeRunRowrenders the real title,getTaskDisplayNamefallbackmainmain$0/0-stream release tailmain$0rows above the CTA (07-08)mainmainmaintest-branch PR guidance + internal issue-workflow skills sectionmainmain, needs a rebasemainOpen — coverage (the headline: existing customers)
measurement-jobs+catalogs/songs)Open — homepage honesty (all customers; the in-flight fleet)
_tbd_)$0 · 0 streamsrows sat above the CTA (07-08).$0rows render.Open — handoff polish + integrity
pendingRunref dies on the remount fresh-signup can cause; both 07-08 walkthroughs auto-resumed, so the failure is intermittent — ship I add new tool #45 as hardening. Done when: search → Value → OTP → runs with no further clicks, on a fresh account.Open — UX dead ends (found 2026-07-08, no PR yet)
privy.recoupable.devsurvives localStorage/cookie clears, and marketing has no logout), a new email + valid OTP fails with a cryptic error and no recovery path (reproduced 2026-07-08). Fix: handle the link-conflict error — log out the stale session and retry as a fresh login; land marketing#43's logout as the manual escape hatch. Done when: on a device holding account A's session, account B's email + valid OTP signs into B (or a one-click recovery is offered), verified live.Open — carried funnel/platform items (from #1859, unblocked, pick from the top)
GET /api/catalogs/7d3e5c35…/measurements?limit=1reportsmeasured_song_count2,679 and a band consistent with 16.31B streams → then close.artistNamefilter onGET /api/catalogs/songsno-ops on unlinked songs. Latent since api#681 (LEFT-join); returns every row for a non-member artist. No longer load-bearing for the hero — still a correctness bug for other consumers. RED test first: filtered query on a catalog with unlinked songs returns 0.socials.profile_url(10 of 15 roster artists) — album resolution 502s. api#768's ingest is case-preserved (forward path); remaining: find the normalize-on-write culprit + backfill existing rows (lower(id)match makes it mechanical).test-recoup-api.vercel.app)..comlink equity transfers.Architecture decisions (carried from #1859)
artist_account_idfilter viacatalog_songs ∩ song_artists); per-artist catalogs rejected. REST shape: path for identity, query for modifiers.api.recoupable.devendpoints — no direct Supabase reads from chat.Source references
chat/lib/home/*,marketing/hooks/useCatalogValuation.ts,api/lib/catalog/attachCanonicalArtistToAccount.ts, endpointGET /api/catalogs/{catalogId}/measurements,measurement-jobs+catalogs/songs(the auto-fill prototype path).