# ROADMAP Longer-term backlog for Recaps. Near-term in-flight work and known issues live in `AGENTS.md` under **Current state**. ## Near-term backlog - **Persist provider preference server-side.** `processItemInternally` currently runtime-detects (relay-if-configured / gemini-fallback) because the user's choice lives only in the client's `localStorage`. Persist it so a fresh-container rebuild or any non-browser caller (cron, background processor) picks the right path. Probably a single key in the StartOS config blob + a small migration to seed it from the first authenticated client. - **Apply Export ▾ menu to the clip-collection panel.** The main view and history rows already have it; the clip collection still has the single legacy "Export PDF" button. Reuse the existing menu component. - **CI lint + type-check.** No `lint` script in `server/package.json`; top-level `tsconfig.json` exists but the server is pure `.js`. Decide: add ESLint, adopt JSDoc-driven TS checking, or remove the empty `tsconfig.json`. - **Surface failed auto-queue items in the dashboard.** Currently hidden by default behind a "Show all" toggle. Worth a small banner / count chip when failures exist so operators notice without hunting. - **Zaprite recurring card billing (BLOCKED on Zaprite).** Grant wants card payments to DEFAULT to recurring (buyer can opt out at checkout). Zaprite's public API (`api.zaprite.com/openapi.json`) only creates one-time `/v1/orders` — recurring is a hosted/dashboard feature with no per-buyer metadata, no renewal webhook, and no billing-portal URL via API. The shipped card rail is one-time prepaid. UNBLOCK by confirming with Zaprite support whether the account can: (a) attach a per-buyer reference/metadata to a recurring checkout (so a payment maps to a Recaps user), (b) fire a webhook on each renewal charge (so we extend the tier each period), (c) expose a customer/billing-portal URL (for the chosen "link to Zaprite portal" cancel path). Decisions already made: no reminder emails for auto-renewing cards; a failed charge = lapse at period end (the relay's expiry-enforcement already does this — a missed renewal just doesn't extend `expires_at`). - **Close the architecture-simplification gaps** (`docs/architecture-simplification-plan.md`). After core-decoupling + self-serve, these steps remain OPEN: **(8) "Take Recaps home"** — mint a fresh Keysat token on demand at click time; likely BROKEN today because relay-tier cloud users have no `keysat_license` for `/api/account/license-key` to return. **(10) cloud paid-only** — the free signed-in tier + signup-grant credits are still live; the plan wanted cloud to be paid-only with self-hosted as the free path (product call — confirm intent before building). **(5, partial) anon signup→Pro** still routes through `/api/license/purchase` + `pending_signups` (Keysat license) instead of the relay tier like the signed-in flow does. **(6, partial) tokenized renew** — the reminder email's renew link is `?renew=1` (requires sign-in); the plan wanted a one-time-token `/renew?token=…` for friction-free renewal. NOTE: the doc's Zaprite-*recurring* / cancel-button / Recaps-DB-owns-expiry parts were intentionally SUPERSEDED by the prepaid + relay-owns-tier model — don't build those. - **Decide the Max tier-quota default.** The relay code default is `max.monthly: null` (unlimited) → cards render "Unlimited" on a fresh install. The operator set `max.monthly: 120` on their box via the Adjust-Tier-Quotas action (so cards show 120 there). Decide whether a metered number (e.g. 120) should be the shipped default in `recap-relay/server/config.js` — note it also enforces the ceiling, not just the card label. ## Deferred hardening & cleanup (P3, from the 2026-06-14 full-eval — `EVALUATION.md`) Low-severity; batch when convenient. None block release. (P0/P1 work queue and P2 known debt live in `AGENTS.md` → Current state.) - **Request-size / fetch caps.** `express.json({limit:"100mb"})` on every route (`server/index.js:203`) is a cheap memory-exhaustion lever; tighten it. `downloadPodcastAudio` also needs a size/time cap — folds into the P0 SSRF fix. - **`/api/credits/claim` invoice-ID hijack.** A leaked anon BTCPay invoice ID is claimable by any signed-in account (`server/credits-purchase.js:342`); bind claims to the buyer's email. Random IDs keep this low-risk. - **Container runs as root** (no `USER` in `Dockerfile`) — acceptable under StartOS isolation; add a non-root user for the cloud image. - **In-memory auth rate-limit buckets reset on restart** (`server/auth-routes.js:106`) — fine for self-host single-operator; note for cloud HA. - **Repo hygiene.** Delete the stale `youtube-summarizer_x86_64.s9pk` (~223MB, old package ID) and rename the root `package.json` (still `youtube-summarizer-startos`). `cookies.txt` is sensitive plaintext in the repo root and is expiring (`/api/health` already reports `fileExpiring:true`) — gitignored, but rotate/move it. - **StartOS packaging polish** (only if submitting to the registry): empty `manifest.docsUrls`; verify the multi-tenant cloud actions (`enableMultiTenantMode` et al., `startos/actions/index.ts`) run cleanly — not stack-trace — in single mode; 172 empty version-file migration stubs are a growing maintenance surface. - **Doc reconciliation (bulk).** AGENTS.md directory layout omits ~25 server modules; `docs/guides/relay-client.md:17` Authorization header is missing the `Bearer` scheme; `index.html` is stated as `~10k` lines but is 12.5k (`AGENTS.md:51`); the `safeFilename()` convention (`AGENTS.md:79`) becomes accurate once the function is exported (the P0 fix). ## Larger plans (already drafted in `docs/`) - `docs/architecture-simplification-plan.md` — broader simplification arc - `docs/core-decoupling-plan.md` — separating the core summarize pipeline from billing / multi-tenant concerns - `docs/per-tenant-subscriptions-plan.md` — moving subscription state into the per-user scope - `docs/self-serve-purchase-plan.md` — buyer flow for Pro/Max and a la carte credits - `docs/path-2b-and-path-1-interweave.md` — sequencing for the multi-tenant cloud meetings work (depends on the relay's Path 2A) Treat the `docs/` plans as the source of truth for those items; cross-reference rather than restating here. ## Adjacent (lives in `../recap-relay`) The relay now has its own `AGENTS.md` + `ROADMAP.md` — track relay work there; this is just what the client surfaces or waits on. - **Speaker MERGE + re-run detection + re-polish — SHIPPED relay-side** (operator dashboard, live on the box at relay 0.2.124, 2026-06-13). Merge folds two clusters into one; re-run re-clusters at a new strictness to split over-merged speakers; re-polish rewrites topic summaries to corrected names. App-side UI for these is now unblocked if wanted. *(The relay tree is at 0.2.124 but uncommitted to git — see `../recap-relay/ROADMAP.md`.)* - Cross-call speaker fingerprint memory (recognize the same voice across meetings) — not yet shipped. - Phase 3 of Path 2A: multiple operator-editable meeting prompt sets (1on1 / all-hands / customer-interview / standup) selectable per upload — not yet shipped. Avoid building app-side UI for the unshipped items until the relay-side pieces land.