Files
proof-of-work/ROADMAP.md
T
Keysat 7fda9ceb7e
CI / proof-of-work (Next.js app) (push) Waiting to run
CI / start9/0.4 (StartOS package code) (push) Waiting to run
design: extract document-as-is UI contract (DESIGN.md + DTCG tokens)
Case-B extract of the as-built dark UI into design/: 9-section DESIGN.md,
W3C DTCG tokens.tokens.json, brand/palette.css + assets, and inspiration/
provenance (incl. the rendered red-shade decision). Two owner calls layered
in: red elevated to the single brand accent (#DC2626), two-tier radius
(4px controls / 8px containers). AGENTS.md gains the read-before-UI Design
line + design/ in the layout tree; ROADMAP gains the design-checker cleanup
backlog. No UI code changed.
2026-06-19 12:14:51 -05:00

7.1 KiB
Raw Blame History

ROADMAP — Proof of Work

Longer-term backlog. Near-term state + next steps live in AGENTS.md → Current state.

Known bugs

  • Mobile-Safari first-login-tap fails ("An unexpected error occurred"); second tap works. Reproduced on iPhone/iPad Safari against 1.2.0:5 (desktop Safari untested — user declined). The first Sign In tap fails, a second manual tap succeeds. 1.2.0:2's retryOnTransportError does NOT fix it. Diagnosis so far: LoginForm only surfaces that error when both the initial action call and its in-tap retry throw, so the immediate retry isn't escaping the bad connection — only a fresh user-initiated tap does. Box app logs show no server-side error/500/reset around the attempt, so it's a transport-layer failure, not an app bug.
    • Gating data (do this first): capture the first failed request's error in Safari Web Inspector (iOS→Mac, Network/Console tab). The code picks the fix:
      • -1005 "The network connection was lost" → client-side stale keep-alive socket. Fix = a delayed retry (let Safari tear down the dead socket before retrying), not the current instant one.
      • 502/503 → StartOS-proxy↔Node keep-alive mismatch (Node closing idle conns the proxy reuses). Fix = raise Node keepAliveTimeout/headersTimeout server-side; a client retry only masks it.
    • Files: lib/retryAction.ts, app/auth/login/LoginForm.tsx, app/auth/signup/SignupForm.tsx.

AI quality

  • Tiered prompt formatting (also the immediate next step): JSON-Schema output enforcement via Ollama format and OpenAI response_format; pipe-separated library rows; XML-tagged prompt sections; Ollama-only few-shot example; stable prefix first for prompt-cache hits.
  • Keep MODEL_MENU / PRICES current as providers ship new models.

Security & hardening (from 2026-06-13 full-eval; full detail + file:line in EVALUATION.md)

  • Still open — verify on the box: whether the StartOS proxy forwards real client IPs to the app. The rate limiter now keys on the rightmost (trusted-proxy) X-Forwarded-For entry; if the proxy instead makes every client look like one IP, the per-IP cap collapses to a single global bucket. Confirm with live headers.
  • P3 hardening batch (remaining): CSP unsafe-eval vs comment, /api/health info disclosure, rate-limit map leak, configurable/shorter sessions (currently 30-day), no text max-length. Also unify the 3rd JSON-parse pattern in programs/[id]/days/[dayId]/start (try{json}catch{→{}}).

Done in 1.2.0:1:3: Next 14→15 / React 18→19 bump (1.2.0:1, closed RSC DoS / WS-upgrade SSRF / App Router XSS + middleware-bypass CVEs); iOS-Safari login first-tap retry (1.2.0:2); login timing oracle closed + exerciseId ownership enforced on all workout-write & program routes (1.2.0:3). Done in 1.1.0:9 (P2 batch): input-validation 500s → 400 (lib/http.ts readJsonBody + explicit guards); POST /api/auth rate-limited; XFF anti-spoof; container drops root via su-exec.

Packaging / distribution

  • Diagnose and fix the publish.sh Step-3 registry-register silent no-op.
  • Build for arm / additional arches once StartOS 0.4 supports them on the host.
  • Consider submission to the Start9 community registry (use the start9-spec-checker agent first). Blockers found 2026-06-13: non-SPDX "Proprietary" license, missing instructions.md, 404 packageRepo/upstreamRepo URLs, stale "0.3.5 data snapshot" install alert + long description; plus warnings (PNG vs SVG icon, migration-era README, no .github/workflows, generic docsUrls, Node 20 vs 22).

Product

  • Adherence tracking: compare logged workouts against the planned ProgramDay (the programDayId link already exists).
  • Per-user export/import polish and scheduled backups.
  • CSV export↔import round-trip: export writes setX-prefixed headers (setCalories/setWatts/setNotes) the importer doesn't read (it expects calories/watts/notes), so the app's own CSV export silently drops those on re-import (calories long-standing; watts since 1.2.0:4). Fix by aligning export header names with the parser, or adding the prefixed names as knownColumns aliases. (JSON account export/import round-trips fine.)
  • Charts/progress views over history (the data and 1RM estimates already exist).

Design

Cleanup backlog from the 2026-06-19 design-contract extract (design/DESIGN.md + tokens.tokens.json). The contract documents the intended system; these are the spots where the code diverges. All cosmetic/mechanical — none ship-blocking. Found by the design-checker agent (re-run it for fresh file:line lists).

  • Unify off-palette neutrals → zinc (21 hits, 12 files): every hover:bg-gray-100/active:bg-gray-200/hover:text-gray-200 on the white primary button → zinc-200/zinc-300. Canonical hover is zinc-200 (color.action.primary-hover-bg). Worst: app/main/workouts/page.tsx (4), auth LoginForm/SignupForm, dashboard/page.tsx, programs/page.tsx.
  • Unify success hue → emerald (10 hits): green-*emerald-*. Worst: components/settings/SettingsForm.tsx (6), WorkoutForm.tsx:696, SetRow.tsx:477, ExerciseCard.tsx:12.
  • Unify warning hue → amber (13 hits): yellow-*amber-*. Worst: app/main/import/page-csv.tsx (6), SettingsForm.tsx (6), ExerciseCard.tsx:14.
  • No solid red button (1): components/settings/DangerZone.tsx:101 uses bg-red-700 — convert to the ghost/wash destructive treatment (text-red-400 border-red-800 hover:bg-red-950/30). (Red elsewhere — 65 wash/outline/text uses — already conforms.)
  • Radius two-tier (4px control / 8px container): rounded-md (6px, 27 hits, mostly SetRow.tsx ×16 + WorkoutForm.tsx ×9) → rounded; rounded-lg on primary buttons (5 hits in workouts/page.tsx, dashboard/page.tsx) → rounded. (rounded-full on the FAB is fine.)
  • Shadows are overlay-only: drop shadow-2xl on the static login/signup cards (app/auth/{login,signup}/page.tsx:23) and shadow-lg on the FAB + desktop CTA (workouts/page.tsx:163,174) — depth = bg layering + border.
  • Sub-scale font sizes (10 hits): text-[10px]/text-[11px] (below the 12px text-xs floor) → text-xs.
  • Extract a shared <Button> (components/common/ is empty): the white primary pattern is inlined 44× across 21 files — the structural reason the gray-100 drift spread everywhere. A single component is the durable fix and the single point to enforce the contract. (Biggest item; do after the mechanical sweeps.)
  • Token wiring decision: design/brand/palette.css (--pow-* vars) isn't imported anywhere. For a Tailwind app the class names already are the tokens (hexes match the zinc/emerald/amber ramp), so the pragmatic path is to keep using Tailwind names and treat palette.css/tokens.tokens.json as the canonical cross-reference (and for any raw-CSS context). Alternatively import palette.css at the root and migrate the ~30 inlined bg-[#0A0A0A] canvas hexes to var(--pow-bg-canvas). Decide before doing a hex→var sweep.

Hygiene

  • Delete the legacy start9/0.4/workout-log_x86_64.s9pk build artifact; drop unused bcryptjs from start9/0.4/package.json.
  • Revisit workout-planner/ scratch dir — remove if truly unused.