# Tracker Dashboard — Production Readiness Handoff Roadmap

**Date:** 2026-05-25
**Repo:** `learning_ai_common_plat/dashboards/tracker-web`
**Status:** Ready for incremental implementation
**Source playbook:** Adapted from `learning_ai_notes/docs/PRODUCTION_READINESS_HANDOFF_ROADMAP.md` (NoteLett) + `learning_ai_flowmonk/docs/HANDOFF_PLAYBOOK_FROM_NOTELETT.md` (FlowMonk)
**Companions:** [`docs/ROADMAP.md`](./ROADMAP.md) · [`docs/PRD.md`](./PRD.md) · [`docs/IMPLEMENTATION_TRACKER.md`](./IMPLEMENTATION_TRACKER.md)

---

## Purpose

Use this document as the **source of truth** for finishing tracker-web end-to-end and making it
production-ready. It reconciles the existing roadmaps, current code, and the reusable
capabilities in `learning_ai_common_plat/packages/*`.

**Implementation rule:** complete one checklist item (or one small cluster) at a time, run the
stated verification, commit with the repo convention, push, and record the commit hash in
[`IMPLEMENTATION_TRACKER.md`](./IMPLEMENTATION_TRACKER.md) before moving on.

---

## Current Baseline (May 25, 2026)

| Area                              | Current state                                                                                                                                  |
| --------------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------- |
| **Web**                           | Next.js 16 App Router with login, dashboard (overview/items/board), item detail, public roadmap, health endpoint                               |
| **Backend**                       | Proxied through `platform-service` (port `4003`); no dedicated `tracker-service` repo yet                                                      |
| **Mobile**                        | None — Phase 6 deliverable                                                                                                                     |
| **Tests present**                 | Vitest scaffolding + ~6 unit tests · Playwright scaffold · no `@axe-core/playwright` yet                                                       |
| **DevOps present**                | `Dockerfile` (standalone Next.js) · `docker-compose.yml` · `vercel.json` · no `scripts/docker-prep.sh` yet (must adopt from NoteLett/FlowMonk) |
| **Common platform packages used** | `@bytelyst/api-client` · `@bytelyst/dashboard-components` · `@bytelyst/react-auth` · `@bytelyst/logger` · `@bytelyst/telemetry-client`         |
| **Deployed status**               | `tracker-web` container `unhealthy` as of 2026-05-25 (B-001/B-002 root cause: valkey cascade — see Phase 1.A)                                  |

---

## Critical Observations

- **Container `unhealthy`** — `tracker-web` reports unhealthy because `/health` route does not actually probe deps; root cause is the `valkey` cascade that brought down platform-service (B-001 → B-002).
- **No UI drift ratchet yet** — direct `@bytelyst/ui` imports likely scattered through pages instead of routed through a `Primitives.tsx` adapter (NoteLett / FlowMonk pattern).
- **Docker images not built with `NEXT_PUBLIC_*` baked in** — runtime API URLs hardcoded; same regression NoteLett (`91b8597`) and FlowMonk (`5ecd829`) fixed last week.
- **Cosmos partition keys not exercised** in CI — NoteLett added a Cosmos emulator smoke job (`79e936b`) that catches partition-key bugs before prod.
- **No `MEK rotation` runbook** — sensitive data fields will need field-level encryption keys; NoteLett established the rotation pattern in `bcad7d3`.
- **React + React-DOM not pinned** via `pnpm.overrides` — risk of multi-instance React bugs when transitive deps pull a different version (fixed in NoteLett `a83e60a`, FlowMonk `59bc63f`).
- **No live shared-service smoke** — NoteLett's `pnpm run smoke:local` end-to-end verification against live `platform-service` + `extraction-service` + `mcp-server` is the only reliable signal that the cross-service contract holds; adopt the same pattern.

---

## Mapping to ROADMAP.md Topic Sections

This playbook groups work by **execution day**. The master roadmap groups the same work by
**topic**. Cross-reference when assigning tickets or marking progress:

| Day milestone (this doc)         | Topic (ROADMAP.md §)                                                  |
| -------------------------------- | --------------------------------------------------------------------- |
| 1.A Container Health Restoration | 1.1 Infrastructure Health                                             |
| 1.B Workspace Health             | 1.8 Workspace Health                                                  |
| 1.C Docker Hardening             | 1.2 Docker Hardening                                                  |
| 1.D UI Drift Ratchet             | 1.3 UI Drift Ratchet                                                  |
| 1.E Test Hardening               | 1.5 Test Coverage                                                     |
| 1.F Observability + Security     | 1.6 Error Handling & Observability + 1.7 Security + 1.4 Rate Limiting |

Mark progress under **both** numbering schemes when checking off in
[`IMPLEMENTATION_TRACKER.md`](./IMPLEMENTATION_TRACKER.md) — agents reading either doc
must be able to find the same checkboxes.

---

## Phase 1.A — Container Health Restoration (Day 1)

**Goal:** Get all tracker-related containers healthy. Block on this — everything else cascades.

### Tasks

- [ ] **1.A.1** Diagnose `valkey` container `unhealthy` status
  - Check: `docker logs learning_ai_common_plat-valkey-1 --tail=50`
  - Likely: stale snapshot, AOF corruption, or memory pressure
  - Verification: `docker exec learning_ai_common_plat-valkey-1 redis-cli ping` returns `PONG`
- [ ] **1.A.2** Restart `platform-service` after valkey is green
  - `docker restart learning_ai_common_plat-platform-service-1`
  - Verification: `curl http://localhost:4003/health` returns `200`
- [ ] **1.A.3** Implement real `/health` route in tracker-web
  - File: `src/app/health/page.tsx` and/or `src/app/api/health/route.ts`
  - Must probe: platform-service `/health`, optional Cosmos ping
  - Return non-200 if any dep is down
- [ ] **1.A.4** Add `restart: unless-stopped` to all tracker services in `docker-compose.yml`
- [ ] **1.A.5** Add 8 GB swap to VM
  - `fallocate -l 8G /swapfile && chmod 600 /swapfile && mkswap /swapfile && swapon /swapfile`
  - Persist in `/etc/fstab`
- [ ] **1.A.6** Limit concurrent Gitea CI runners
  - File: `act_runner` config or systemd unit
  - Cap to max 2 concurrent `next build` / `tsc` jobs

**Verification gate:** All containers healthy. `docker ps | grep tracker` shows `(healthy)`.

---

## Phase 1.B — Workspace Health (Day 1–2)

**Goal:** Get `pnpm run verify` passing cleanly with no React-instance warnings or stale package issues.

### Tasks

- [ ] **1.B.1** Pin React + React-DOM via `pnpm.overrides` in root `package.json`
  - Pattern: NoteLett `a83e60a`, FlowMonk `59bc63f`
  - Block: `"pnpm": { "overrides": { "react": "19.2.4", "react-dom": "19.2.4" } }`
- [ ] **1.B.2** Verify `pnpm-workspace.yaml` references canonical `packages/*` paths (no relative `../` indirection issues)
- [ ] **1.B.3** Clean `pnpm install` to relink `@bytelyst/*` packages from source, not stale registry tarballs
- [ ] **1.B.4** Run `pnpm --filter @bytelyst/tracker-web run typecheck`
- [ ] **1.B.5** Run `pnpm --filter @bytelyst/tracker-web run lint`
- [ ] **1.B.6** Run `pnpm --filter @bytelyst/tracker-web run test`
- [ ] **1.B.7** Run `pnpm --filter @bytelyst/tracker-web run build`
- [ ] **1.B.8** Resolve any actionable React-compiler lint warnings (NoteLett `3c4d46f` pattern)
- [ ] **1.B.9** Remove dead code surfaced by lint (NoteLett `f4564d7`)
- [ ] **1.B.10** Add dedicated `e2e/tsconfig.json` for Playwright specs (NoteLett `7ea2c48`)

**Verification gate:** Green typecheck + lint + test + build with no warnings about multiple React copies.

---

## Phase 1.C — Docker Hardening (Day 2–3)

**Goal:** Ship a self-contained Docker image that builds on the corp network, runs healthy in
the existing compose stack, and has correct runtime URLs.

### Tasks

- [ ] **1.C.1** Adopt `scripts/docker-prep.sh` from NoteLett / FlowMonk pattern
  - Packs `@bytelyst/*` tarballs into `.docker-deps/`
  - Rewrites `web/package.json` refs to `file:../.docker-deps/<tarball>`
  - Restore mode: `--restore`
- [ ] **1.C.2** Add `.docker-deps/` to `.gitignore` (NoteLett `70623d9`)
- [ ] **1.C.3** Bake `NEXT_PUBLIC_*` values at build time via Dockerfile `ARG` + `ENV` (drop runtime hardcoding)
  - Pattern: NoteLett `91b8597`, FlowMonk `5ecd829`
- [ ] **1.C.4** Fix Next.js standalone static-chunks 404 — copy `.next/static` to standalone output in Dockerfile stage-1 (NoteLett `131b73c`, FlowMonk `5ecd829`)
- [ ] **1.C.5** Change Docker healthcheck to IPv4 `127.0.0.1` (avoids IPv6 resolution edge cases) (FlowMonk `5ecd829`)
- [ ] **1.C.6** Add corp-proxy build args: `NODE_TLS_REJECT_UNAUTHORIZED=0` + `NPM_CONFIG_STRICT_SSL=false` in builder stage only (NoteLett `e5221af`)
- [ ] **1.C.7** Add `docker-compose.override.yml` wiring backend to sibling `platform-service` (`4003`), `extraction-service` (`4005`), `mcp-server` (`4007`) with shared JWT secret
- [ ] **1.C.8** Verify `docker compose up -d --build` brings tracker-web up healthy

**Verification gate:** `docker ps | grep tracker-web` shows `(healthy)`; `curl http://localhost:3003/health` returns `200`; static chunks load (no 404 in browser DevTools).

---

## Phase 1.D — UI Drift Ratchet (Day 3–4)

**Goal:** Install the one-way CI gate that prevents UI regressions from creeping back in.

### Tasks

- [ ] **1.D.1** Add `scripts/ui-drift-audit.sh` counting:
  - Raw form controls (`<input>`, `<textarea>`, `<button>` outside Primitives)
  - Legacy global classes (`.surface-card`, `.surface-muted`, `.badge`, `.input-shell` — match NoteLett naming or define tracker-specific set)
  - Hardcoded color literals (regex for `#[0-9a-fA-F]{3,8}` and `rgba?\(`)
  - Direct `@bytelyst/ui` imports outside `src/components/ui/Primitives.tsx`
- [ ] **1.D.2** Add `scripts/ui-drift-ratchet.sh` that:
  - Reads previous counts from `.ui-drift-ratchet.json`
  - Compares to current audit output
  - Fails CI if any count increased
  - Updates file when counts decrease
- [ ] **1.D.3** Hard-zero categories: legacy globals, hardcoded colors, direct imports (any new occurrence fails CI)
- [ ] **1.D.4** Add `audit:release-guards` script combining ratchet + secret scan + token audit
- [ ] **1.D.5** Wire `audit:release-guards` into Gitea CI workflow as required check

**Verification gate:** `pnpm run audit:release-guards` passes; CI blocks PRs that increase drift counters.

---

## Phase 1.E — Test Hardening (Day 4–6)

**Goal:** Tests that catch real regressions before prod, not just paper-thin smoke.

### Tasks

- [ ] **1.E.1** Vitest unit tests ≥ 80 % on `src/lib/`
  - Target files: `tracker-client.ts`, `auth-context.tsx`, `utils.ts`
- [ ] **1.E.2** Playwright E2E happy path: login → create item → claim → link PR → close
- [ ] **1.E.3** Playwright E2E public flow: submit + vote + status check
- [ ] **1.E.4** Docker compose E2E test script `scripts/e2e-docker-test.sh` (NoteLett `d5e857d`, FlowMonk `e48e75b`)
- [ ] **1.E.5** Seed scripts in `scripts/seed-test-data.sh` for bootstrapping the deployed stack
- [ ] **1.E.6** Port-conflict-proof Playwright config — random ports per run (NoteLett `7103660`)
- [ ] **1.E.7** Cleanup traps — `trap cleanup EXIT` to prevent orphan items between runs (FlowMonk `033c2c9`)
- [ ] **1.E.8** Playwright `BASE_URL` switch — local dev vs deployed container (FlowMonk `e48e75b`)
- [ ] **1.E.9** Install + wire `@axe-core/playwright` — `test:e2e:ci` script (FlowMonk `77f9bd9`)
- [ ] **1.E.10** API contract tests against platform-service OpenAPI schema
- [ ] **1.E.11** Cosmos emulator smoke job in CI exercising partition-key paths (NoteLett `79e936b`)
- [ ] **1.E.12** Live shared-service smoke `pnpm run smoke:local` (NoteLett `34cb219` pattern)

**Verification gate:** `pnpm test`, `pnpm test:e2e:ci`, `pnpm smoke:local` all green; CI's Cosmos emulator job passes.

---

## Phase 1.F — Observability + Security (Day 6–7)

**Goal:** Visible signals when things break, locked-down ingress.

### Tasks

- [ ] **1.F.1** Global React error boundary (`src/app/error.tsx` — already scaffolded; needs friendly content + logging)
- [ ] **1.F.2** Structured server-side logging via `@bytelyst/logger` on all API routes
- [ ] **1.F.3** Forward Next.js server logs to Loki (already deployed in compose stack)
- [ ] **1.F.4** Expose `/metrics` Prometheus endpoint; scrape from existing Prometheus container
- [ ] **1.F.5** Grafana alert: health-check failure + error rate > 1 %
- [ ] **1.F.6** Client-side error tracking via Sentry or `@bytelyst/diagnostics-client`
- [ ] **1.F.7** Security headers audit — CSP, HSTS, X-Frame-Options, Referrer-Policy, Permissions-Policy
- [ ] **1.F.8** CSRF tokens on all mutating routes (`POST` / `PATCH` / `DELETE`)
- [ ] **1.F.9** Audit log (`{ actor, action, field, before, after, timestamp }`) on every item mutation
- [ ] **1.F.10** PII scrubbing in logs — emails, names redacted from raw lines
- [ ] **1.F.11** MEK rotation runbook `docs/runbooks/MEK_ROTATION.md` (NoteLett `bcad7d3` template)
- [ ] **1.F.12** Secret management runbook `docs/runbooks/SECRET_MANAGEMENT.md` (NoteLett `bcad7d3` template)

**Verification gate:** Grafana dashboard shows tracker-web metrics; trigger a synthetic error and confirm alert fires; secret scan + token audit pass.

---

## Exit Criteria for Production Readiness (end of Phase 1)

All of the following must hold before declaring Phase 1 done:

- [ ] All tracker-related containers report `(healthy)` for > 24 h continuous
- [ ] `pnpm run verify` passes with zero warnings (typecheck, lint, test, build)
- [ ] `pnpm run audit:release-guards` passes (secret scan + UI drift ratchet + token audit)
- [ ] `pnpm test:e2e:ci` passes against deployed Docker stack with cleanup verified
- [ ] `pnpm smoke:local` passes end-to-end against live platform-service
- [ ] Cosmos emulator smoke job is green in CI
- [ ] Grafana alert wired and tested (synthetic error fires alert within 5 min)
- [ ] MEK rotation + secret management runbooks committed
- [ ] 8 GB swap configured + Gitea runner concurrency capped
- [ ] All `B-001`, `B-002`, `B-005`, `B-009`, `B-010`, `B-019`, `B-020`, `B-021` items closed

---

## After Phase 1

Phases 2–6 unblock once Phase 1 exits cleanly. See [`docs/ROADMAP.md`](./ROADMAP.md) for full
detail and [`docs/roadmaps/`](./roadmaps/) for focused per-area plans.

| Next phase    | Theme                          | Why now                                          |
| ------------- | ------------------------------ | ------------------------------------------------ |
| Phase 2.1     | UI Primitives migration        | Highest ratchet payoff; unblocks Phase 6 theming |
| Phase 3.1–3.2 | Agent API auth + core item ops | Highest agent productivity payoff                |
| Phase 4.4     | Cross-product routing          | Required as more products onboard                |
