Files
recap/ROADMAP.md
T
Keysat 1741fb11a5 Add design/ contract extracted from the as-built UI
Inventory the as-built recaps.cc look and distill it into a durable,
vendor-neutral design contract: design/DESIGN.md (nine-section brand
brief) + design/tokens.tokens.json (W3C DTCG tokens), plus brand icon
and provenance notes. Canonical calls reconciled with the owner:
indigo #818cf8 as the single interactive accent, purple #a855f7 for
premium only, the #0a0e1a->#111827->#0f172a surface ladder, and a
normalized type scale. Wire the AGENTS.md Design line and record the
contract-vs-code drift as a cleanup backlog in ROADMAP.md.
2026-06-16 23:08:41 -05:00

98 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 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.
- **Add Gemini 3.5 to model selection.** First have a research agent confirm which stable Gemini model versions are actually available and the correct model id/name before wiring anything. The model list is duplicated server + client (provider config under `server/providers/` + the model picker in `public/index.html`) — add the option in lockstep, like the URL-parser convention. Coordinate with the matching relay-side capture (the relay routes Gemini, so its model list must agree). — captured 2026-06-16
## Design-contract conformance cleanup (from the 2026-06-16 `/design` extract)
The `design/` contract (`design/DESIGN.md` + `design/tokens.tokens.json`) was extracted
from the as-built UI and reconciled with Grant on 2026-06-16. The code is **structurally
aligned** (right surface ladder, accent system, premium-purple, components) but a set of
legacy values survived the reconciliation and now read as off-contract drift. None are
release-blocking; all are mechanical token migrations. `design-checker` found seven
categories (counts approximate, from grep) across the three styling surfaces — the main
`public/index.html` `<style>` block, its `SHARE_PAGE_CSS` string, and `public/auth.html`.
**Edit all three in lockstep** (the SHARE_PAGE_CSS string carries its own copies that a
main-stylesheet grep won't catch). Do NOT do this in the same pass as the contract itself.
- **Legacy accent indigo → `#818cf8`** (~12 hard + ~16 tint). `#6366f1`/`#4f46e5`/`#4338ca`
fills/borders/hovers and `rgba(99,102,241,…)` tints → `#818cf8`/`#a5b4fc` + `rgba(129,140,248,…)`.
Biggest cluster is the activation screen (`index.html:1213-1216`, `:1565,1576,1579`) which
uses all three demoted values in one component; also inline buttons `:6496,7776,8183`,
`.license-block` `:1593,1601`.
- **Blue `#3b82f6` as a primary interactive color → indigo** (auth ×4 + index ×8). `auth.html`
is the worst offender — its primary button + input focus are blue (`auth.html:99,105-117`);
also wallet/sign-up/grant/password buttons (`index.html:5228,5763,8151,8163,7609,12384`).
Blue stays only for info/status + speaker chips.
- **Legacy dark backgrounds → ladder** (~10). `#0a0e17``#0a0e1a` (`:1156,1243,8662`);
`#0b1120``#0a0e1a` (`:1191` + SHARE_PAGE_CSS `:11111`); `#020617``#0f172a` (`:1562`);
and the auth sub-palette `#121828``#111827`, `#1f2942``#1e293b` (`auth.html:47-48,83`,
`index.html:400-401`).
- **Stray heading near-white `#f5f9ff``#f1f5f9`** (×9; 5 in `auth.html:59,63,87,94,96`,
4 in index `:418,660,6335,6343`).
- **Off-scale font sizes → nearest step** (~23). `15→16`, `24→22`, `9→10`, `12.5/11.5→12`,
`10.5→10` (e.g. `index.html:84,899,907`, `auth.html:86,110`).
- **Off-scale weights** (×3): `650→600` (`:1008`, SHARE_PAGE_CSS `:11133`), `680→700`
(SHARE_PAGE_CSS `:11113`).
- **Off-scale radii → snap** (~18). `3→4`, `5→6`, `7→6|8`, `9→8|10`, `11→10|12` (scrollbars,
history/queue action buttons, mobile submit/menu, buy inputs).
- **(stretch) Consolidate inline styles behind CSS custom properties.** ~447 inline `style=`
attrs + duplicated values are why this drift accumulates; a `:root` token block (or
`design/brand/palette.css` generated from the tokens) would make the contract enforceable
instead of advisory. Big diff — schedule deliberately.
## Known debt (P2, from the 2026-06-14 full-eval — `EVALUATION.md`)
Real but not release-blocking for self-host. The P0/P1 findings from the same eval were fixed 2026-06-15 (see git log + `EVALUATION.md`).
- **Operator-internal strings leak to cloud users at the SSE error boundary** (Parakeet/Gemma/CUDA/LAN IPs) — no scrub exists, violating the scrub convention in `AGENTS.md`. `server/index.js:3432,3003,4246` + `providers/relay.js:135-144`. (Sharp edge: `index.js:3419` *detects* these strings, then forwards them anyway.)
- **Credit over-spend TOCTOU on licensed installs** — N parallel requests pass the `total>0` check before any blind `debitOne` lands. Make check+debit atomic (reserve up front, refund on failure). `index.js:2497-2550` vs `:3158,:4197`.
- **Multi-mode tenant can spend the operator's server Gemini key** via `transcriptionProvider:"gemini"` + empty key (bypasses relay metering) — `providers/index.js:104-114`. Refuse the operator-key fallback for non-admin tenants.
- **`GET /api/history` parses every full session file** (transcript+summary, MB each) just to list ~8 metadata fields — cache them into `_meta.json` on save. `history.js:418-437`.
- **Dependency CVEs** — nodemailer 6.10.1 (high; low practical reach here), ws/qs/express/protobufjs (moderate). `npm audit fix` (nodemailer is a major bump).
- **No tests on the riskiest files** (`/api/process` gating, `relay.js`, `tenant-auth.js`, billing) — the real summarize→save→debit path can't run end-to-end without a key/credits. Add an integration test as the regression net.
- **Smaller hardening:** unsanitized IDs persisted to `_meta.json` (array-form library import + `PUT /api/history/move`) — no file-path escape (read-time `safeFilename` guards the load), but sanitize at write too; `PUT /api/history/meta` accepts arbitrary JSON shapes with no schema; `index.js` is 4351 lines mixing routing/pipeline/yt-dlp/SSE.
- **Doc drift (high-value):** AGENTS.md credit-gate order omits the "paid cloud user" bypass state (`:77` vs `index.js:2464-2472`); operator-facing `startos-registry/.../INSTRUCTIONS.md` + `assets/ABOUT.md` are stale Gemini-first (relay is the default provider).
## 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 community-registry submission — deferred (decision 2026-06-15: self-host + cloud only for now).** Hard blockers if/when we submit: add a root `instructions.md`; point `packageRepo`/`upstreamRepo` at a public source repo (currently `https://ten31.xyz`, a homepage); choose a source-available license for the wrapper (currently `Proprietary`). Softer polish: 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. None of this affects `make install`.
- **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.