From 80e62689242185e980d7e3f8c791cde24deb0ba6 Mon Sep 17 00:00:00 2001 From: saravanakumardb1 Date: Sun, 29 Mar 2026 16:29:42 -0700 Subject: [PATCH] docs(vercel): improve Codex prompts with progress trackers, preconditions, verification gates, and per-repo checklists --- .../vercel/CODEX_PROMPTS_TRACK_A_AZURE_VM.md | 402 ++++++++++++++---- .../CODEX_PROMPTS_TRACK_B_VERCEL_CODE.md | 375 ++++++++++++---- 2 files changed, 619 insertions(+), 158 deletions(-) diff --git a/docs/devops/vercel/CODEX_PROMPTS_TRACK_A_AZURE_VM.md b/docs/devops/vercel/CODEX_PROMPTS_TRACK_A_AZURE_VM.md index c815e9f0..3682f65e 100644 --- a/docs/devops/vercel/CODEX_PROMPTS_TRACK_A_AZURE_VM.md +++ b/docs/devops/vercel/CODEX_PROMPTS_TRACK_A_AZURE_VM.md @@ -1,16 +1,67 @@ # Track A: Azure VM — Gateway, Dashboards & Security > **Agent:** Codex on Azure VM (`/opt/bytelyst/`) -> **Prompts:** 4 (run in order) -> **Dependency:** None — this track runs first, unblocks Track B Prompt 4 +> **Prompts:** 4 (run in dependency order below) +> **Unblocks:** Track B Prompt B4 (`.npmrc` migration — needs `gitea.bytelyst.com` live) +> **Reference docs:** +> +> - [`SECURE_API_EXPOSURE.md`](../single_azure_vm/docker/SECURE_API_EXPOSURE.md) — Caddy decision + architecture +> - [`DEPLOYMENT_STATUS_2026-03-29.md`](../single_azure_vm/docker/DEPLOYMENT_STATUS_2026-03-29.md) — current VM status +> - [`ECOSYSTEM_WEB_APPS_INVENTORY.md`](./ECOSYSTEM_WEB_APPS_INVENTORY.md) — full Vercel audit + +--- + +## Progress Tracker + +| # | Prompt | Status | Commit SHA | Verified | +| --- | ------------------------ | :------------: | :--------: | :------: | +| A1 | Caddy Gateway Setup | ⬜ Not started | — | ⬜ | +| A2 | Gitea HTTPS Exposure | ⬜ Not started | — | ⬜ | +| A3 | Dashboard Containers Fix | ⬜ Not started | — | ⬜ | +| A4 | NSG Lockdown | ⬜ Not started | — | ⬜ | + +**Execution order:** + +``` +A1: Caddy Gateway ──→ A2: Gitea HTTPS ──→ A4: NSG Lockdown + └──→ A3: Dashboard Fix (parallel with A2) +``` + +--- + +## DNS Prerequisites (manual — do before starting) + +These DNS records must exist before Caddy can obtain Let's Encrypt certificates: + +| Record | Type | Value | Purpose | +| ---------------------- | ---- | ---------------------- | ------------------- | +| `api.bytelyst.com` | A | `` | Backend API gateway | +| `gitea.bytelyst.com` | A | `` | Gitea npm registry | +| `admin.bytelyst.com` | A | `` | Admin dashboard | +| `tracker.bytelyst.com` | A | `` | Tracker dashboard | + +**Verify DNS before starting A1:** + +```bash +dig +short api.bytelyst.com # must return VM IP +dig +short gitea.bytelyst.com # must return VM IP +``` --- ## A1: Caddy Gateway — Replace Traefik with Caddy -> **Depends on:** Nothing +> **Depends on:** DNS records for `api.bytelyst.com` > **Blocks:** A2, A3, A4 +### Preconditions + +- [ ] `api.bytelyst.com` DNS A record resolves to Azure VM IP +- [ ] All 13 backend services are healthy: `docker compose ps | grep -c healthy` → 13+ +- [ ] Ports 80 and 443 are open in Azure NSG + +### Prompt + ``` You are working on the ByteLyst single-Azure-VM Docker deployment at /opt/bytelyst/. @@ -18,34 +69,66 @@ TASK: Replace the existing Traefik gateway container with Caddy to provide HTTPS CONTEXT: - The decision doc is at docs/devops/single_azure_vm/docker/SECURE_API_EXPOSURE.md in the learning_ai_common_plat repo -- The domain is bytelyst.com +- The domain is bytelyst.com — DNS A records already point to this VM - API hostname: api.bytelyst.com - Currently 13 backend services are healthy on ports 4003, 4005, 4007, 4010-4019 - Traefik is running but only as a passthrough — no TLS, no path routing hardened - All backend containers are on a Docker network -DELIVERABLES: -1. Create a Caddyfile with: +TASK LIST: +- [ ] 1. Create a Caddyfile at /opt/bytelyst/Caddyfile with: - api.bytelyst.com as the main host (automatic Let's Encrypt TLS) - Path-based routing to all 13 backends: - /platform/* → platform-service:4003 - /extraction/* → extraction-service:4005 - /mcp/* → mcp-server:4007 - /peakpulse/* → peakpulse:4010 - /chronomind/* → chronomind:4011 - /jarvisjr/* → jarvisjr:4012 - /nomgap/* → nomgap:4013 - /mindlyst/* → mindlyst:4014 - /lysnrai/* → lysnrai:4015 - /notelett/* → notelett:4016 - /flowmonk/* → flowmonk:4017 + /platform/* → platform-service:4003 + /extraction/* → extraction-service:4005 + /mcp/* → mcp-server:4007 + /peakpulse/* → peakpulse:4010 + /chronomind/* → chronomind:4011 + /jarvisjr/* → jarvisjr:4012 + /nomgap/* → nomgap:4013 + /mindlyst/* → mindlyst:4014 + /lysnrai/* → lysnrai:4015 + /notelett/* → notelett:4016 + /flowmonk/* → flowmonk:4017 /actiontrail/* → actiontrail:4018 /localmemgpt/* → localmemgpt:4019 - Strip the path prefix before forwarding (backends expect / not /platform/) -2. Add a caddy service to docker-compose replacing or alongside the gateway service -3. Stop publishing backend ports (4003, 4005, 4007, 4010-4019) publicly — only expose 80/443 -4. Verify all 13 backends respond via https://api.bytelyst.com//health -5. Update docs/devops/single_azure_vm/docker/README.md with the new access model +- [ ] 2. Add a caddy service to docker-compose replacing or alongside the gateway service + - Image: caddy:2-alpine + - Volumes: ./Caddyfile:/etc/caddy/Caddyfile, caddy_data:/data, caddy_config:/config + - Ports: 80:80, 443:443 + - Network: same Docker network as all backends +- [ ] 3. Remove published backend ports from docker-compose (4003, 4005, 4007, 4010-4019) + — backends remain accessible internally via Docker DNS +- [ ] 4. Restart the stack: docker compose up -d +- [ ] 5. Wait for Caddy to obtain TLS certificates (check logs: docker logs caddy --tail 50) +- [ ] 6. Update docs/devops/single_azure_vm/docker/README.md with the new access model + +VERIFICATION (run all — every check must pass): + # TLS certificate obtained + curl -sI https://api.bytelyst.com/platform/health | head -5 + # Expected: HTTP/2 200 + valid TLS + + # All 13 backends reachable via path routing + for svc in platform extraction mcp peakpulse chronomind jarvisjr nomgap mindlyst lysnrai notelett flowmonk actiontrail localmemgpt; do + echo -n "$svc: "; curl -sf https://api.bytelyst.com/$svc/health | jq -r '.status // "FAIL"' + done + # Expected: all 13 print "ok" + + # Direct port access blocked from outside + curl -sf --max-time 3 http://:4003/health && echo "FAIL: port still open" || echo "PASS: port closed" + # Expected: "PASS: port closed" + +SUCCESS CRITERIA: +- All 13 health checks return "ok" via https://api.bytelyst.com//health +- TLS certificate is valid (Let's Encrypt) +- No backend ports (4003-4019) accessible directly from public internet +- Traefik config preserved but not in use + +ROLLBACK: +If Caddy fails, revert docker-compose changes and restart Traefik: + git checkout -- docker-compose*.yml + docker compose up -d DO NOT: - Delete the existing Traefik config — just stop using it @@ -59,8 +142,16 @@ COMMIT: feat(gateway): replace Traefik with Caddy for HTTPS path routing ## A2: Gitea HTTPS Exposure via Caddy -> **Depends on:** A1 (Caddy must be running) -> **Blocks:** Track B Prompt B4 +> **Depends on:** A1 ✅ (Caddy running + TLS working) +> **Blocks:** Track B → B4 (`.npmrc` update across all repos) + +### Preconditions + +- [ ] A1 is complete — Caddy is running with valid TLS on `api.bytelyst.com` +- [ ] `gitea.bytelyst.com` DNS A record resolves to Azure VM IP +- [ ] Gitea container is running: `docker compose ps gitea` → healthy + +### Prompt ``` You are working on the ByteLyst single-Azure-VM Docker deployment at /opt/bytelyst/. @@ -69,66 +160,144 @@ TASK: Expose the Gitea npm registry over HTTPS via the Caddy gateway so that ext CONTEXT: - Gitea is running on port 3300 inside the Docker network -- Caddy is already configured for api.bytelyst.com (from A1) +- Caddy is already configured for api.bytelyst.com (from A1) and has valid TLS - The Gitea npm registry URL pattern is: /api/packages/ByteLyst/npm/ - Gitea needs its own subdomain because its internal routing expects to be at root - Target URL: https://gitea.bytelyst.com -DELIVERABLES: -1. Add a gitea.bytelyst.com block to the Caddyfile: - gitea.bytelyst.com { - reverse_proxy gitea:3300 - } -2. Ensure Gitea's ROOT_URL / SERVER__ROOT_URL is updated to https://gitea.bytelyst.com -3. Verify https://gitea.bytelyst.com loads the Gitea web UI -4. Verify npm registry is accessible: - curl -s https://gitea.bytelyst.com/api/packages/ByteLyst/npm/@bytelyst%2ferrors | head -5. Remove port 3300 from public NSG rules (Gitea only accessible via Caddy now) -6. Lock down other operational ports from public access where possible: - - 8025 (Mailpit) - - 8080 (Traefik dashboard — no longer needed) - - 10000 (Azurite) +TASK LIST: +- [ ] 1. Add a gitea.bytelyst.com block to the Caddyfile: + gitea.bytelyst.com { + reverse_proxy gitea:3300 + } +- [ ] 2. Reload Caddy: docker exec caddy caddy reload --config /etc/caddy/Caddyfile +- [ ] 3. Update Gitea's ROOT_URL to https://gitea.bytelyst.com + — Edit docker-compose env: SERVER__ROOT_URL=https://gitea.bytelyst.com + — Restart Gitea container: docker compose restart gitea +- [ ] 4. Remove port 3300 from docker-compose published ports (Gitea only via Caddy) +- [ ] 5. Remove stale public NSG rules for ports that should not be exposed: + - 3300 (Gitea) + - 8025 (Mailpit) + - 8080 (Traefik dashboard — no longer needed) + - 10000 (Azurite) + +VERIFICATION (run all — every check must pass): + # Gitea web UI loads + curl -sI https://gitea.bytelyst.com | head -3 + # Expected: HTTP/2 200 (or 302 redirect to login) + + # npm registry returns package metadata + curl -sf https://gitea.bytelyst.com/api/packages/ByteLyst/npm/@bytelyst%2ferrors | jq '.name' + # Expected: "@bytelyst/errors" + + # Authenticated npm install works + export GITEA_NPM_TOKEN= + npm view @bytelyst/errors --registry=https://gitea.bytelyst.com/api/packages/ByteLyst/npm/ + # Expected: package info printed (version, description, etc.) + + # Direct port 3300 access blocked from outside + curl -sf --max-time 3 http://:3300 && echo "FAIL" || echo "PASS: port closed" + # Expected: "PASS: port closed" + + # Internal Docker access still works + docker exec platform-service curl -sf http://gitea:3300/api/v1/version | jq '.version' + # Expected: Gitea version string + +SUCCESS CRITERIA: +- https://gitea.bytelyst.com serves Gitea web UI with valid TLS +- npm registry API returns valid package metadata +- Authenticated `npm view` succeeds +- Port 3300 not accessible from public internet +- Docker-internal access to gitea:3300 still works (other containers unaffected) DO NOT: - Break existing Docker-internal Gitea access (other containers still use gitea:3300) - Remove the Gitea container or change its internal port COMMIT: feat(gateway): expose Gitea npm registry via HTTPS at gitea.bytelyst.com + +AFTER COMPLETING: Notify the operator that Track B → B4 (.npmrc migration) is now unblocked. ``` --- ## A3: Fix admin-web and tracker-web Containers -> **Depends on:** A1 (Caddy gateway for subdomain routing) -> **Blocks:** Nothing +> **Depends on:** A1 ✅ (Caddy running for subdomain routing) +> **Blocks:** Nothing (independent of Track B) +> **Can run in parallel with:** A2 + +### Preconditions + +- [ ] A1 is complete — Caddy is running +- [ ] `admin.bytelyst.com` and `tracker.bytelyst.com` DNS A records resolve to VM IP +- [ ] Source code for dashboards is available on the VM + +### Prompt ``` You are working on the ByteLyst single-Azure-VM Docker deployment at /opt/bytelyst/. -TASK: Resolve the remaining admin-web and tracker-web containers so they are running and healthy. +TASK: Resolve the remaining admin-web and tracker-web containers so they are running and healthy, then expose them via Caddy. CONTEXT: - Per DEPLOYMENT_STATUS_2026-03-29.md, all 13 backend services are healthy -- admin-web and tracker-web are the two remaining unresolved containers -- Both are Next.js 16 apps from the learning_ai_common_plat repo under dashboards/ +- admin-web and tracker-web are the two remaining UNRESOLVED containers +- Both are Next.js 16 apps from learning_ai_common_plat under dashboards/ - Both use workspace:* dependencies resolved by pnpm - Both use the --webpack build flag -- admin-web runs on port 3001, tracker-web on port 3003 -- The Docker build context is dashboards/admin-web/ and dashboards/tracker-web/ +- admin-web port: 3001, tracker-web port: 3003 +- Docker build context: dashboards/admin-web/ and dashboards/tracker-web/ -DELIVERABLES: -1. Diagnose why admin-web and tracker-web builds failed -2. Fix the build issues (likely @bytelyst/* package resolution or standalone entrypoint) -3. Rebuild and start both containers -4. Verify health: - curl -s http://127.0.0.1:3001 | head - curl -s http://127.0.0.1:3003 | head -5. Add Caddy routes for the dashboards: - - https://admin.bytelyst.com → admin-web:3001 - - https://tracker.bytelyst.com → tracker-web:3003 -6. Update DEPLOYMENT_STATUS_2026-03-29.md to reflect the fix -7. Mark Phase 7 as DONE if all services are now healthy +TASK LIST: +- [ ] 1. Check current container status: + docker compose ps admin-web tracker-web + docker compose logs admin-web --tail 100 + docker compose logs tracker-web --tail 100 +- [ ] 2. Diagnose the build/runtime failure (likely causes): + - @bytelyst/* package resolution — may need .docker-deps/ tarballs + - Standalone entrypoint not found + - Missing env vars (COSMOS_ENDPOINT, COSMOS_KEY, JWT_SECRET) +- [ ] 3. Fix the identified issues +- [ ] 4. Rebuild and restart: + docker compose up -d --build admin-web tracker-web +- [ ] 5. Add Caddy routes for the dashboards to the Caddyfile: + admin.bytelyst.com { + reverse_proxy admin-web:3001 + } + tracker.bytelyst.com { + reverse_proxy tracker-web:3003 + } +- [ ] 6. Reload Caddy: docker exec caddy caddy reload --config /etc/caddy/Caddyfile +- [ ] 7. Update DEPLOYMENT_STATUS_2026-03-29.md to reflect the fix + +VERIFICATION (run all — every check must pass): + # Containers healthy + docker compose ps admin-web tracker-web + # Expected: both show "Up" and "healthy" (or at least "Up") + + # Local port access + curl -sf http://127.0.0.1:3001 | head -5 + curl -sf http://127.0.0.1:3003 | head -5 + # Expected: HTML response from each + + # HTTPS via Caddy + curl -sI https://admin.bytelyst.com | head -3 + # Expected: HTTP/2 200 (or 302) + + curl -sI https://tracker.bytelyst.com | head -3 + # Expected: HTTP/2 200 (or 302) + + # All containers now healthy + docker compose ps | grep -E "(unhealthy|Exit)" | wc -l + # Expected: 0 + +SUCCESS CRITERIA: +- Both admin-web and tracker-web containers are running +- Both respond on their ports (3001, 3003) +- Both accessible via HTTPS at admin.bytelyst.com and tracker.bytelyst.com +- DEPLOYMENT_STATUS doc updated with fix details +- Zero unhealthy containers in the full stack COMMIT: fix(deployment): resolve admin-web and tracker-web containers ``` @@ -137,41 +306,126 @@ COMMIT: fix(deployment): resolve admin-web and tracker-web containers ## A4: NSG Lockdown — Reduce Public Port Exposure -> **Depends on:** A1 + A2 (Caddy handling all traffic) +> **Depends on:** A1 + A2 + A3 all complete (all traffic routed through Caddy) > **Blocks:** Nothing +> **Run this LAST in Track A** + +### Preconditions + +- [ ] A1 complete — all backends accessible via `api.bytelyst.com` +- [ ] A2 complete — Gitea accessible via `gitea.bytelyst.com` +- [ ] A3 complete — dashboards accessible via `admin.bytelyst.com` and `tracker.bytelyst.com` +- [ ] All services verified working through Caddy (no direct port access needed) + +### Prompt ``` TASK: Reduce the Azure NSG (Network Security Group) rules so only ports 80, 443, and 22 are publicly accessible. CONTEXT: - The Azure VM currently exposes many service ports publicly (3000-3300, 4003-4019, 8025, 8080, 10000, 11434) -- Caddy now handles all public HTTPS traffic on 80/443 -- Backend services, Gitea, Grafana, Mailpit, and Azurite are all accessed through Caddy +- Caddy now handles ALL public HTTPS traffic on ports 80/443 +- All services (backends, Gitea, dashboards, monitoring) are accessed through Caddy subdomains - Only SSH (22) and HTTP/HTTPS (80/443) should remain publicly open - This follows the recommendation in SECURE_API_EXPOSURE.md -DELIVERABLES: -1. List current NSG rules: az network nsg rule list --resource-group --nsg-name -o table -2. Remove or deny inbound rules for all ports except 22, 80, 443 -3. Verify Caddy still serves all routes correctly -4. Verify SSH access still works -5. Document the changes in DEPLOYMENT_STATUS_2026-03-29.md +TASK LIST: +- [ ] 1. Snapshot current NSG rules (save output for rollback reference): + az network nsg rule list --resource-group --nsg-name -o table > /opt/bytelyst/nsg-rules-before.txt + cat /opt/bytelyst/nsg-rules-before.txt +- [ ] 2. Identify rules to remove — everything except: + - SSH (22/tcp) + - HTTP (80/tcp) + - HTTPS (443/tcp) +- [ ] 3. Delete each unnecessary rule: + az network nsg rule delete --resource-group --nsg-name --name +- [ ] 4. List final NSG rules to confirm only 3 remain: + az network nsg rule list --resource-group --nsg-name -o table +- [ ] 5. Document changes in DEPLOYMENT_STATUS_2026-03-29.md + +VERIFICATION (run all — every check must pass): + # SSH still works + ssh -o ConnectTimeout=5 @ echo "SSH OK" + # Expected: "SSH OK" + + # Caddy still serves all routes + curl -sf https://api.bytelyst.com/platform/health | jq '.status' + # Expected: "ok" + + curl -sf https://gitea.bytelyst.com/api/v1/version | jq '.version' + # Expected: version string + + curl -sI https://admin.bytelyst.com | head -1 + # Expected: HTTP/2 200 (or 302) + + # Direct port access blocked + for port in 3001 3003 3300 4003 4005 8025 8080 10000; do + curl -sf --max-time 3 http://:$port && echo "FAIL: $port open" || echo "PASS: $port closed" + done + # Expected: all "PASS: closed" + + # Only 3 NSG rules remain + az network nsg rule list --resource-group --nsg-name -o table | grep -c Allow + # Expected: 3 (SSH, HTTP, HTTPS) + +SUCCESS CRITERIA: +- Only ports 22, 80, 443 open in NSG +- SSH access confirmed working +- All Caddy routes still functional +- No direct port access possible from public internet +- Before/after NSG snapshots saved + +ROLLBACK: +If services become unreachable after lockdown: + # Re-add a rule temporarily + az network nsg rule create --resource-group --nsg-name \ + --name AllowTemp --priority 200 --access Allow --direction Inbound \ + --source-address-prefixes '*' --destination-port-ranges --protocol Tcp + # Then investigate Caddy routing DO NOT: -- Remove the SSH (22) rule -- Remove the HTTP (80) or HTTPS (443) rules -- Make changes without first listing the current rules +- Remove the SSH (22) rule — you will lose access +- Remove the HTTP (80) or HTTPS (443) rules — Caddy needs them +- Make changes without first saving the current rules snapshot COMMIT: chore(security): lock down Azure NSG to 22/80/443 only ``` --- -## Track A Execution Order +## Track A — Final Verification Checklist -``` -A1: Caddy Gateway ──→ A2: Gitea HTTPS ──→ A4: NSG Lockdown - └──→ A3: Dashboards Fix +After all 4 prompts are complete, run this full end-to-end verification: + +```bash +echo "=== Track A Final Verification ===" + +echo "--- DNS ---" +for d in api.bytelyst.com gitea.bytelyst.com admin.bytelyst.com tracker.bytelyst.com; do + echo -n "$d → "; dig +short $d +done + +echo "--- Backend Health (13 services) ---" +for svc in platform extraction mcp peakpulse chronomind jarvisjr nomgap mindlyst lysnrai notelett flowmonk actiontrail localmemgpt; do + echo -n "$svc: "; curl -sf https://api.bytelyst.com/$svc/health | jq -r '.status // "FAIL"' +done + +echo "--- Gitea npm registry ---" +curl -sf https://gitea.bytelyst.com/api/packages/ByteLyst/npm/@bytelyst%2ferrors | jq '.name' + +echo "--- Dashboards ---" +curl -sI https://admin.bytelyst.com | head -1 +curl -sI https://tracker.bytelyst.com | head -1 + +echo "--- Port lockdown ---" +for port in 3001 3003 3300 4003 4005 8025 8080 10000; do + curl -sf --max-time 3 http://:$port > /dev/null 2>&1 && echo "FAIL: $port open" || echo "PASS: $port closed" +done + +echo "--- Docker containers ---" +docker compose ps --format 'table {{.Name}}\t{{.Status}}' | grep -E "(unhealthy|Exit)" | wc -l +# Expected: 0 ``` -A3 can run in parallel with A2. A4 runs last after everything is routed through Caddy. +**All checks must pass before Track A is considered complete.** +Once A2 verification passes, signal to Track B that Prompt B4 is unblocked. diff --git a/docs/devops/vercel/CODEX_PROMPTS_TRACK_B_VERCEL_CODE.md b/docs/devops/vercel/CODEX_PROMPTS_TRACK_B_VERCEL_CODE.md index 8081bc54..8bba927e 100644 --- a/docs/devops/vercel/CODEX_PROMPTS_TRACK_B_VERCEL_CODE.md +++ b/docs/devops/vercel/CODEX_PROMPTS_TRACK_B_VERCEL_CODE.md @@ -2,6 +2,29 @@ > **Agent:** Any Codex agent with repo access (local or cloud) > **Prompts:** 4 (B1–B3 run in parallel, B4 runs last after Track A completes A2) +> **Reference docs:** +> +> - [`ECOSYSTEM_WEB_APPS_INVENTORY.md`](./ECOSYSTEM_WEB_APPS_INVENTORY.md) — full Vercel audit + per-app status +> - Individual `ROADMAP_*.md` files — per-app gaps, env vars, and deployment steps + +--- + +## Progress Tracker + +| # | Prompt | Repos | Status | Commits | Verified | +| --- | ------------------------ | :---: | :--------------: | :-----: | :------: | +| B1 | Fix `file:` refs | 7 | ⬜ Not started | 0/7 | ⬜ | +| B2 | Fix `output: standalone` | 4 | ⬜ Not started | 0/4 | ⬜ | +| B3 | EffoRise `vercel.json` | 1 | ⬜ Not started | 0/1 | ⬜ | +| B4 | Update `.npmrc` | 12 | ⬜ Blocked on A2 | 0/12 | ⬜ | + +**Execution order:** + +``` +B1: Fix file: refs ──────┐ +B2: Fix standalone ──────┼──→ B4: Update .npmrc (blocked until Track A → A2 done) +B3: EffoRise vercel.json ┘ +``` --- @@ -10,15 +33,28 @@ > **Depends on:** Nothing (packages are already published to Gitea) > **Blocks:** Nothing +### Per-Repo Checklist + +| # | Repo | File | Packages to fix | Edited | Built | Committed | Pushed | +| --- | ------------------------------ | ------------------------ | ----------------- | :----: | :---: | :-------: | :----: | +| 1 | `learning_ai_clock` | `web/package.json` | design-tokens, ui | ⬜ | ⬜ | ⬜ | ⬜ | +| 2 | `learning_ai_jarvis_jr` | `web/package.json` | ui | ⬜ | ⬜ | ⬜ | ⬜ | +| 3 | `learning_ai_fastgap` | `web/package.json` | ui | ⬜ | ⬜ | ⬜ | ⬜ | +| 4 | `learning_ai_flowmonk` | `web/package.json` | ui | ⬜ | ⬜ | ⬜ | ⬜ | +| 5 | `learning_ai_notes` | `web/package.json` | ui | ⬜ | ⬜ | ⬜ | ⬜ | +| 6 | `learning_ai_local_memory_gpt` | `web/package.json` | ui | ⬜ | ⬜ | ⬜ | ⬜ | +| 7 | `learning_ai_local_llms` | `dashboard/package.json` | design-tokens, ui | ⬜ | ⬜ | ⬜ | ⬜ | + +### Prompt + ``` TASK: Convert all file: references to @bytelyst/* packages into semver ^0.1.0 references across 7 web app repos. CONTEXT: -- 7 repos have web/package.json files that use file: references to ../../learning_ai_common_plat/packages/* -- These fail on Vercel because the sibling repo doesn't exist +- 7 repos have web/package.json (or dashboard/package.json) files with file: references to ../../learning_ai_common_plat/packages/* +- These fail on Vercel because the sibling repo doesn't exist during build - The packages are already published to the Gitea npm registry -- Each fix is: change "file:../../learning_ai_common_plat/packages/X" to "^0.1.0" -- After changing, run pnpm install to update the lockfile, then verify pnpm build passes +- Each fix: change "file:../../learning_ai_common_plat/packages/X" to "^0.1.0" REPOS AND FILES TO FIX: @@ -45,17 +81,35 @@ REPOS AND FILES TO FIX: - "@bytelyst/design-tokens": "file:..." → "^0.1.0" - "@bytelyst/ui": "file:..." → "^0.1.0" -FOR EACH REPO: -1. Edit the package.json — replace file: refs with ^0.1.0 -2. Run: cd web && pnpm install (or cd dashboard for LLM Lab) -3. Run: pnpm build -4. If build passes, commit: fix(web): convert file: refs to registry semver for Vercel compatibility -5. Push to origin main +FOR EACH REPO (repeat 7 times): +- [ ] 1. Edit the package.json — replace file: refs with ^0.1.0 +- [ ] 2. Run: cd web && pnpm install (or cd dashboard for LLM Lab) +- [ ] 3. Verify no file: refs remain: grep -r "file:.*common_plat" package.json + Expected: no output +- [ ] 4. Run: pnpm build + Expected: build succeeds with exit code 0 +- [ ] 5. Commit: fix(web): convert file: refs to registry semver for Vercel compatibility +- [ ] 6. Push to origin main + +VERIFICATION (after all 7 repos): + # Confirm zero file: refs remain across all repos + for repo in learning_ai_clock learning_ai_jarvis_jr learning_ai_fastgap learning_ai_flowmonk learning_ai_notes learning_ai_local_memory_gpt learning_ai_local_llms; do + echo -n "$repo: " + grep -rl "file:.*common_plat" $repo/web/package.json $repo/dashboard/package.json 2>/dev/null | wc -l | tr -d ' ' + echo " file: refs remaining" + done + # Expected: all show "0 file: refs remaining" + +SUCCESS CRITERIA: +- All 7 repos have zero file: references in their web/dashboard package.json +- All 7 builds pass (pnpm build exits 0) +- All 7 commits pushed to origin main +- No other dependencies or source code modified DO NOT: - Change any other dependencies - Modify any source code -- Skip the build verification step +- Skip the build verification step for any repo ``` --- @@ -65,16 +119,28 @@ DO NOT: > **Depends on:** Nothing > **Blocks:** Nothing +### Per-Repo Checklist + +| # | Repo | File | Pattern | Edited | Docker Build | Vercel Build | Committed | Pushed | +| --- | ------------------------------ | -------------------- | --------------------- | :----: | :----------: | :----------: | :-------: | :----: | +| 1 | `learning_ai_fastgap` | `web/next.config.ts` | output + tracing root | ⬜ | ⬜ | ⬜ | ⬜ | ⬜ | +| 2 | `learning_ai_notes` | `web/next.config.ts` | output + tracing root | ⬜ | ⬜ | ⬜ | ⬜ | ⬜ | +| 3 | `learning_ai_trails` | `web/next.config.ts` | output only | ⬜ | ⬜ | ⬜ | ⬜ | ⬜ | +| 4 | `learning_ai_local_memory_gpt` | `web/next.config.ts` | output only | ⬜ | ⬜ | ⬜ | ⬜ | ⬜ | + +> **Skip:** `learning_ai_local_llms/dashboard/next.config.ts` — LLM Lab not viable on Vercel (needs local Ollama) + +### Prompt + ``` TASK: Make the Next.js output mode Vercel-aware in 4 repos that currently hardcode output: 'standalone'. CONTEXT: - Vercel manages its own output mode — hardcoded standalone breaks Vercel builds -- The fix is: wrap standalone output in a process.env.VERCEL conditional +- The fix: wrap standalone output in a process.env.VERCEL conditional - Docker builds (which don't set VERCEL env var) will still get standalone mode -- 4 repos need this fix -REPOS AND FILES: +REPOS AND EXACT CHANGES: 1. learning_ai_fastgap/web/next.config.ts Change: @@ -87,7 +153,7 @@ REPOS AND FILES: }), 2. learning_ai_notes/web/next.config.ts - Same pattern as above. + Same pattern as #1 (output + outputFileTracingRoot). 3. learning_ai_trails/web/next.config.ts Change: @@ -96,16 +162,36 @@ REPOS AND FILES: ...(process.env.VERCEL ? {} : { output: 'standalone' }), 4. learning_ai_local_memory_gpt/web/next.config.ts - Same pattern as #3. + Same pattern as #3 (output only). -NOTE: learning_ai_local_llms/dashboard/next.config.ts also has this but LLM Lab is NOT viable on Vercel (needs local Ollama), so skip it. +FOR EACH REPO (repeat 4 times): +- [ ] 1. Edit next.config.ts with the VERCEL conditional +- [ ] 2. Verify Docker build still works (standalone activates without VERCEL env): + cd web && pnpm build + # Check output: look for "Creating an optimized production build" + ".next/standalone/" dir + ls -d .next/standalone/ && echo "PASS: standalone created" || echo "FAIL" +- [ ] 3. Verify Vercel-mode build works (standalone skipped with VERCEL env): + cd web && VERCEL=1 pnpm build + # Check output: .next/standalone/ should NOT exist + ls -d .next/standalone/ 2>/dev/null && echo "FAIL: standalone still created" || echo "PASS: no standalone" +- [ ] 4. Commit: fix(web): make output mode Vercel-aware — conditional standalone +- [ ] 5. Push to origin main -FOR EACH REPO: -1. Edit next.config.ts with the conditional -2. Verify Docker build still works: pnpm build (standalone should still activate without VERCEL env) -3. Verify VERCEL=1 pnpm build also works (output should be default, not standalone) -4. Commit: fix(web): make output mode Vercel-aware — conditional standalone -5. Push to origin main +VERIFICATION (after all 4 repos): + for repo in learning_ai_fastgap learning_ai_notes learning_ai_trails learning_ai_local_memory_gpt; do + echo "=== $repo ===" + cd $repo/web + grep -c "process.env.VERCEL" next.config.ts + # Expected: 1 (the conditional) + cd ../.. + done + +SUCCESS CRITERIA: +- All 4 next.config.ts files contain process.env.VERCEL conditional +- Docker build (no VERCEL env) produces .next/standalone/ +- Vercel build (VERCEL=1) does NOT produce .next/standalone/ +- All 4 commits pushed to origin main +- No other config options changed DO NOT: - Change any other config options in next.config.ts @@ -120,33 +206,63 @@ DO NOT: > **Depends on:** Nothing > **Blocks:** Nothing +### Checklist + +| Step | Description | Done | +| ---- | ------------------------- | :--: | +| 1 | Create `vercel.json` | ⬜ | +| 2 | Verify local build | ⬜ | +| 3 | Verify SPA routing config | ⬜ | +| 4 | Commit + push | ⬜ | + +### Prompt + ``` TASK: Create a vercel.json file for the EffoRise Vite SPA to handle client-side routing and API proxy configuration. CONTEXT: -- EffoRise is a Vite + React 19 SPA (not Next.js) in the learning_ai_efforise repo -- The Vite config is at the repo root (vite.config.ts) +- EffoRise is a Vite + React 19 SPA (NOT Next.js) in the learning_ai_efforise repo +- The Vite config is at the repo root (client/ directory has the SPA) - Build output goes to dist/public/ (custom, not default dist/) - Uses wouter for client-side routing — all routes must serve index.html - The backend API is at https://api.bytelyst.com/efforise - Currently uses a dev proxy /api → localhost:4020 -DELIVERABLES: -1. Create vercel.json at the repo root with: - { - "$schema": "https://openapi.vercel.sh/vercel.json", - "framework": "vite", - "buildCommand": "pnpm build", - "outputDirectory": "dist/public", - "rewrites": [ - { "source": "/api/(.*)", "destination": "https://api.bytelyst.com/efforise/$1" }, - { "source": "/(.*)", "destination": "/index.html" } - ] - } +TASK LIST: +- [ ] 1. Create client/vercel.json with: + { + "$schema": "https://openapi.vercel.sh/vercel.json", + "framework": "vite", + "buildCommand": "pnpm build", + "outputDirectory": "dist/public", + "rewrites": [ + { "source": "/api/(.*)", "destination": "https://api.bytelyst.com/efforise/$1" }, + { "source": "/(.*)", "destination": "/index.html" } + ] + } +- [ ] 2. Verify local build: cd client && pnpm build + Expected: exit code 0, dist/public/index.html exists +- [ ] 3. Verify vercel.json is valid JSON: cat client/vercel.json | jq . + Expected: formatted JSON output, no parse errors +- [ ] 4. Commit: feat(web): add vercel.json for Vite SPA deployment +- [ ] 5. Push to origin main -2. Verify pnpm build still works locally -3. Commit: feat(web): add vercel.json for Vite SPA deployment -4. Push to origin main +VERIFICATION: + # Build works + cd client && pnpm build && echo "BUILD: PASS" || echo "BUILD: FAIL" + + # Output directory exists + ls client/dist/public/index.html && echo "OUTPUT: PASS" || echo "OUTPUT: FAIL" + + # vercel.json valid + jq '.rewrites | length' client/vercel.json + # Expected: 2 + +SUCCESS CRITERIA: +- client/vercel.json exists with correct framework, build command, output directory, and rewrites +- Local build still produces dist/public/index.html +- vercel.json is valid JSON +- Commit pushed to origin main DO NOT: - Modify vite.config.ts @@ -158,12 +274,44 @@ DO NOT: ## B4: Update .npmrc Across All Product Repos -> **Depends on:** Track A Prompt A2 (gitea.bytelyst.com must be live and accessible) -> **Blocks:** Vercel deployment +> **Depends on:** Track A → A2 complete (`gitea.bytelyst.com` live + verified) +> **Blocks:** All Vercel deployments (this is the final gate) + +### Pre-flight Check (must pass before starting) + +```bash +# Verify gitea.bytelyst.com is live and serving packages +curl -sf https://gitea.bytelyst.com/api/packages/ByteLyst/npm/@bytelyst%2ferrors | jq '.name' +# Expected: "@bytelyst/errors" +# If this fails, DO NOT proceed — Track A → A2 is not complete +``` + +### Per-Repo Checklist + +| # | Repo | .npmrc locations | Updated | Install OK | Committed | Pushed | +| --- | ----------------------------------- | -------------------------- | :-----: | :--------: | :-------: | :----: | +| 1 | `learning_voice_ai_agent` | root, user-dashboard-web/ | ⬜ | ⬜ | ⬜ | ⬜ | +| 2 | `learning_ai_clock` | root, web/ | ⬜ | ⬜ | ⬜ | ⬜ | +| 3 | `learning_ai_jarvis_jr` | root, web/ | ⬜ | ⬜ | ⬜ | ⬜ | +| 4 | `learning_ai_fastgap` | root, web/ | ⬜ | ⬜ | ⬜ | ⬜ | +| 5 | `learning_ai_flowmonk` | root, web/, mobile/ | ⬜ | ⬜ | ⬜ | ⬜ | +| 6 | `learning_ai_notes` | root, web/, mobile/ | ⬜ | ⬜ | ⬜ | ⬜ | +| 7 | `learning_ai_trails` | root, web/, sdk/ | ⬜ | ⬜ | ⬜ | ⬜ | +| 8 | `learning_multimodal_memory_agents` | root, mindlyst-native/web/ | ⬜ | ⬜ | ⬜ | ⬜ | +| 9 | `learning_ai_local_memory_gpt` | root, web/ | ⬜ | ⬜ | ⬜ | ⬜ | +| 10 | `learning_ai_efforise` | root, mobile/ | ⬜ | ⬜ | ⬜ | ⬜ | +| 11 | `learning_ai_local_llms` | dashboard/ | ⬜ | ⬜ | ⬜ | ⬜ | +| 12 | `learning_ai_peakpulse` | root | ⬜ | ⬜ | ⬜ | ⬜ | + +### Prompt ``` TASK: Update .npmrc files in all product repos to point to the public Gitea npm registry at gitea.bytelyst.com. +PRE-FLIGHT (must pass before starting): + curl -sf https://gitea.bytelyst.com/api/packages/ByteLyst/npm/@bytelyst%2ferrors | jq '.name' + # Must return "@bytelyst/errors" — if not, STOP and report that Track A → A2 is not done + CONTEXT: - All product repos currently have .npmrc pointing to http://localhost:3300 (local Gitea) - Gitea is now accessible at https://gitea.bytelyst.com via Caddy reverse proxy @@ -171,67 +319,126 @@ CONTEXT: - Auth token is passed via GITEA_NPM_TOKEN env var - This change enables Vercel builds to install @bytelyst/* packages -NEW .npmrc CONTENT (same for all repos): -@bytelyst:registry=https://gitea.bytelyst.com/api/packages/ByteLyst/npm/ -//gitea.bytelyst.com/api/packages/ByteLyst/npm/:_authToken=${GITEA_NPM_TOKEN} +TARGET .npmrc CONTENT (same for all repos): + @bytelyst:registry=https://gitea.bytelyst.com/api/packages/ByteLyst/npm/ + //gitea.bytelyst.com/api/packages/ByteLyst/npm/:_authToken=${GITEA_NPM_TOKEN} -REPOS TO UPDATE (12 total): +REPOS TO UPDATE (12 total — see checklist above for .npmrc locations): -1. learning_voice_ai_agent (user-dashboard-web, backend) -2. learning_ai_clock (web, backend) -3. learning_ai_jarvis_jr (web, backend) -4. learning_ai_fastgap (web, backend) -5. learning_ai_flowmonk (web, backend, mobile) -6. learning_ai_notes (web, backend, mobile) -7. learning_ai_trails (web, backend, sdk) -8. learning_multimodal_memory_agents (mindlyst-native/web, backend) -9. learning_ai_local_memory_gpt (web, backend) -10. learning_ai_efforise (root, backend, mobile) -11. learning_ai_local_llms (dashboard) -12. learning_ai_peakpulse (backend) + 1. learning_voice_ai_agent (root .npmrc + user-dashboard-web/.npmrc) + 2. learning_ai_clock (root + web/) + 3. learning_ai_jarvis_jr (root + web/) + 4. learning_ai_fastgap (root + web/) + 5. learning_ai_flowmonk (root + web/ + mobile/) + 6. learning_ai_notes (root + web/ + mobile/) + 7. learning_ai_trails (root + web/ + sdk/) + 8. learning_multimodal_memory_agents (root + mindlyst-native/web/) + 9. learning_ai_local_memory_gpt (root + web/) +10. learning_ai_efforise (root + mobile/) +11. learning_ai_local_llms (dashboard/) +12. learning_ai_peakpulse (root) -FOR EACH REPO: -1. Find all .npmrc files (there may be multiple — root, web/, backend/, mobile/) -2. Update the @bytelyst registry line to use gitea.bytelyst.com -3. Add the auth token line if not present -4. Run: GITEA_NPM_TOKEN= pnpm install (verify packages resolve) -5. Commit: chore: update .npmrc to public Gitea registry at gitea.bytelyst.com -6. Push to origin main +FOR EACH REPO (repeat 12 times): +- [ ] 1. Find all .npmrc files: find . -name ".npmrc" -not -path "*/node_modules/*" +- [ ] 2. In each .npmrc, replace the @bytelyst registry line: + OLD: @bytelyst:registry=http://localhost:3300/api/packages/ByteLyst/npm/ + NEW: @bytelyst:registry=https://gitea.bytelyst.com/api/packages/ByteLyst/npm/ +- [ ] 3. Ensure the auth token line exists: + //gitea.bytelyst.com/api/packages/ByteLyst/npm/:_authToken=${GITEA_NPM_TOKEN} +- [ ] 4. Verify no localhost:3300 references remain: + grep -r "localhost:3300" . --include=".npmrc" + Expected: no output +- [ ] 5. Verify packages resolve: + GITEA_NPM_TOKEN= pnpm install + Expected: exit code 0, all @bytelyst/* packages resolved +- [ ] 6. Commit: chore: update .npmrc to public Gitea registry at gitea.bytelyst.com +- [ ] 7. Push to origin main -VERIFICATION: -After updating all repos, verify one end-to-end: +VERIFICATION (after all 12 repos — run from parent directory containing all repos): + echo "=== .npmrc Migration Verification ===" + + # Check no localhost:3300 remains anywhere + for repo in learning_voice_ai_agent learning_ai_clock learning_ai_jarvis_jr learning_ai_fastgap learning_ai_flowmonk learning_ai_notes learning_ai_trails learning_multimodal_memory_agents learning_ai_local_memory_gpt learning_ai_efforise learning_ai_local_llms learning_ai_peakpulse; do + count=$(grep -rl "localhost:3300" $repo/ --include=".npmrc" 2>/dev/null | wc -l | tr -d ' ') + echo "$repo: $count old refs remaining" + done + # Expected: all show "0 old refs remaining" + + # End-to-end build test on one repo cd learning_ai_clock/web - GITEA_NPM_TOKEN= pnpm install - pnpm build + GITEA_NPM_TOKEN= pnpm install && pnpm build && echo "E2E: PASS" || echo "E2E: FAIL" + # Expected: "E2E: PASS" + +SUCCESS CRITERIA: +- All 12 repos have zero localhost:3300 references in .npmrc files +- All 12 repos can resolve @bytelyst/* packages via gitea.bytelyst.com +- At least one repo verified with full pnpm install + pnpm build +- All 12 commits pushed to origin main +- No package versions changed, no auth tokens hardcoded DO NOT: - Change any package versions - Remove any other registry configurations (e.g., npmjs.org for non-@bytelyst packages) - Hardcode the auth token — always use ${GITEA_NPM_TOKEN} +- Proceed if the pre-flight check fails ``` --- -## Track B Execution Order +## Track B — Final Verification Checklist -``` -B1: Fix file: refs ──────┐ -B2: Fix standalone ──────┼──→ B4: Update .npmrc (waits for Track A → A2) -B3: EffoRise vercel.json ┘ +After all 4 prompts are complete, verify the full picture: + +```bash +echo "=== Track B Final Verification ===" + +echo "--- B1: file: refs (7 repos) ---" +for repo in learning_ai_clock learning_ai_jarvis_jr learning_ai_fastgap learning_ai_flowmonk learning_ai_notes learning_ai_local_memory_gpt learning_ai_local_llms; do + count=$(grep -rl "file:.*common_plat" $repo/web/package.json $repo/dashboard/package.json 2>/dev/null | wc -l | tr -d ' ') + echo "$repo: $count file: refs" +done +# Expected: all 0 + +echo "--- B2: standalone conditional (4 repos) ---" +for repo in learning_ai_fastgap learning_ai_notes learning_ai_trails learning_ai_local_memory_gpt; do + count=$(grep -c "process.env.VERCEL" $repo/web/next.config.ts 2>/dev/null) + echo "$repo: $count VERCEL conditionals" +done +# Expected: all 1 + +echo "--- B3: EffoRise vercel.json ---" +jq '.rewrites | length' learning_ai_efforise/client/vercel.json 2>/dev/null +# Expected: 2 + +echo "--- B4: .npmrc migration (12 repos) ---" +for repo in learning_voice_ai_agent learning_ai_clock learning_ai_jarvis_jr learning_ai_fastgap learning_ai_flowmonk learning_ai_notes learning_ai_trails learning_multimodal_memory_agents learning_ai_local_memory_gpt learning_ai_efforise learning_ai_local_llms learning_ai_peakpulse; do + count=$(grep -rl "localhost:3300" $repo/ --include=".npmrc" 2>/dev/null | wc -l | tr -d ' ') + echo "$repo: $count old .npmrc refs" +done +# Expected: all 0 ``` -B1, B2, B3 can all run in parallel immediately. B4 must wait until `gitea.bytelyst.com` is live (Track A, Prompt A2). +**All checks must pass before Track B is considered complete.** --- -## After Both Tracks Complete +## After Both Tracks Complete — Vercel Project Setup (Manual) -``` -Manual steps (Vercel UI): - → Create Vercel projects for each app - → Set GITEA_NPM_TOKEN + other env vars per project - → Trigger first deploys and verify -``` +Once Track A and Track B are both verified, the remaining work is manual in the Vercel UI: -See [`ECOSYSTEM_WEB_APPS_INVENTORY.md`](./ECOSYSTEM_WEB_APPS_INVENTORY.md) for the full app list and per-app environment variables. -See individual `ROADMAP_*.md` files for per-app Vercel project setup details. +| # | App | Vercel Root Dir | Framework | Key Env Vars | Roadmap | +| --- | ----------------- | ------------------------ | --------- | ------------------------------------------------------ | ---------------------------------------------------------- | +| 1 | Admin Dashboard | `dashboards/admin-web` | Next.js | `COSMOS_ENDPOINT`, `COSMOS_KEY`, `JWT_SECRET` | [ROADMAP_ADMIN_WEB.md](./ROADMAP_ADMIN_WEB.md) | +| 2 | Tracker Dashboard | `dashboards/tracker-web` | Next.js | `JWT_SECRET`, `PLATFORM_SERVICE_URL` | [ROADMAP_TRACKER_WEB.md](./ROADMAP_TRACKER_WEB.md) | +| 3 | User Dashboard | `user-dashboard-web` | Next.js | `PLATFORM_SERVICE_URL`, `ACTIONTRAIL_SERVICE_URL` | [ROADMAP_USER_DASHBOARD.md](./ROADMAP_USER_DASHBOARD.md) | +| 4 | ChronoMind Web | `web` | Next.js | `GITEA_NPM_TOKEN` | [ROADMAP_CHRONOMIND_WEB.md](./ROADMAP_CHRONOMIND_WEB.md) | +| 5 | JarvisJr Web | `web` | Next.js | `GITEA_NPM_TOKEN`, `NEXT_PUBLIC_PLATFORM_URL` | [ROADMAP_JARVISJR_WEB.md](./ROADMAP_JARVISJR_WEB.md) | +| 6 | NomGap Web | `web` | Next.js | `GITEA_NPM_TOKEN`, `NEXT_PUBLIC_BACKEND_URL` | [ROADMAP_NOMGAP_WEB.md](./ROADMAP_NOMGAP_WEB.md) | +| 7 | FlowMonk Web | `web` | Next.js | `GITEA_NPM_TOKEN`, `NEXT_PUBLIC_BACKEND_URL` | [ROADMAP_FLOWMONK_WEB.md](./ROADMAP_FLOWMONK_WEB.md) | +| 8 | NoteLett Web | `web` | Next.js | `GITEA_NPM_TOKEN`, `NEXT_PUBLIC_BACKEND_URL` | [ROADMAP_NOTELETT_WEB.md](./ROADMAP_NOTELETT_WEB.md) | +| 9 | ActionTrail Web | `web` | Next.js | `GITEA_NPM_TOKEN`, `NEXT_PUBLIC_BACKEND_URL` | [ROADMAP_ACTIONTRAIL_WEB.md](./ROADMAP_ACTIONTRAIL_WEB.md) | +| 10 | MindLyst Web | `mindlyst-native/web` | Next.js | `GITEA_NPM_TOKEN`, `COSMOS_ENDPOINT`, `OPENAI_API_KEY` | [ROADMAP_MINDLYST_WEB.md](./ROADMAP_MINDLYST_WEB.md) | +| 11 | LocalMemGPT Web | `web` | Next.js | `GITEA_NPM_TOKEN`, `NEXT_PUBLIC_BACKEND_URL` | [ROADMAP_LOCALMEMGPT_WEB.md](./ROADMAP_LOCALMEMGPT_WEB.md) | +| 12 | EffoRise Client | `client` | Vite | `GITEA_NPM_TOKEN`, `VITE_BACKEND_URL` | [ROADMAP_EFFORISE.md](./ROADMAP_EFFORISE.md) | + +**Every Vercel project needs `GITEA_NPM_TOKEN` set.** See individual roadmap files for the complete env var list per app.