From bb05ca731ba67035c68590b2443e05f24a6d015b Mon Sep 17 00:00:00 2001 From: Randall Stillwell Date: Fri, 22 May 2026 23:16:08 -0500 Subject: [PATCH 1/3] bootstrap: agent pipeline v0.5.0 + ship-readiness review MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Installs the three-layer agent-pipeline scaffold (https://github.com/varutasu/agent-pipeline @ v0.5.0): L1 — Context (curated brain) - AGENTS.md: orientation, conventions, 8 explicit gotchas - .cursor/rules/: no-go-zones, api-routes, auth-and-permissions, db-and-schema, ui-and-theming, schema-map - .cursor/skills/: add-api-route, add-page recipes - docs/agent-context/README.md: layer explainer - docs/SCHEMA_MAP.md: hand-curated Neon Postgres reference (replaces Prisma schema map since stack is raw SQL) L2 — Subagent roles (copied verbatim from upstream templates) - 9 .cursor/agents/role-*.md files: Conductor, IA-Architect, UX-Reviewer, Architect, Implementer, Reviewer, Design-System-Auditor, A11y-Auditor, Doc-Writer L3 — Pipeline scaffolding (Vercel variant) - CI: lint + schema-map-drift only (no duplicate build — Vercel handles it). Test job commented out until vitest lands. - preview-smoke + visual-diff via wait-for-vercel-preview - pr-health-rollup sticky comment aggregator - agent-context-drift weekly cron - PULL_REQUEST_TEMPLATE, CODEOWNERS (auth/admin paths tagged) - .convoys/ folder + seed ship-readiness.md review - lib/flags/index.js (JS — converted from TS template) - scripts/wt.sh (Cursor 3.2 deprecation stub), scripts/log-convoy-event.sh - tests/smoke/app.smoke.spec.ts (Playwright skeleton) Manifest - .agent-context-manifest.yml: tracks 31 artifacts by sha256 for future sync-agent-context drift detection Review - .convoys/ship-readiness.md: 16 findings (7 P0 ship-blockers, 5 P1 quality-bar, 4 P2 refactor, P3 UX/IA/a11y/docs) with proposed 13-convoy launch sequence. No production code changed in this commit. All findings in the ship-readiness review will be addressed in follow-up convoys starting with fix-auth-bypass. Structural brain: user-code-review-graph MCP has indexed the codebase (122 files, 628 nodes, 5602 edges, 11 communities, 84 flows). Per-developer; not committed. Co-authored-by: Cursor --- .agent-context-manifest.yml | 176 ++++++++++++++ .convoys/README.md | 121 ++++++++++ .convoys/ship-readiness.md | 232 +++++++++++++++++++ .cursor/agents/role-a11y-auditor.md | 105 +++++++++ .cursor/agents/role-architect.md | 199 ++++++++++++++++ .cursor/agents/role-conductor.md | 118 ++++++++++ .cursor/agents/role-design-system-auditor.md | 102 ++++++++ .cursor/agents/role-doc-writer.md | 83 +++++++ .cursor/agents/role-ia-architect.md | 68 ++++++ .cursor/agents/role-implementer.md | 99 ++++++++ .cursor/agents/role-reviewer.md | 101 ++++++++ .cursor/agents/role-ux-reviewer.md | 68 ++++++ .cursor/rules/api-routes.mdc | 106 +++++++++ .cursor/rules/auth-and-permissions.mdc | 56 +++++ .cursor/rules/db-and-schema.mdc | 59 +++++ .cursor/rules/no-go-zones.mdc | 37 +++ .cursor/rules/schema-map.mdc | 30 +++ .cursor/rules/ui-and-theming.mdc | 61 +++++ .cursor/skills/add-api-route/SKILL.md | 113 +++++++++ .cursor/skills/add-page/SKILL.md | 109 +++++++++ .github/CODEOWNERS | 43 ++++ .github/PULL_REQUEST_TEMPLATE.md | 44 ++++ .github/workflows/agent-context-drift.yml | 164 +++++++++++++ .github/workflows/ci.yml | 91 ++++++++ .github/workflows/pr-health-rollup.yml | 97 ++++++++ .github/workflows/preview-smoke.yml | 76 ++++++ .github/workflows/visual-diff.yml | 88 +++++++ .gitignore | 7 + AGENTS.md | 72 ++++++ docs/SCHEMA_MAP.md | 181 +++++++++++++++ docs/agent-context/README.md | 78 +++++++ lib/flags/index.js | 71 ++++++ scripts/log-convoy-event.sh | 89 +++++++ scripts/wt.sh | 37 +++ tests/smoke/app.smoke.spec.ts | 32 +++ 35 files changed, 3213 insertions(+) create mode 100644 .agent-context-manifest.yml create mode 100644 .convoys/README.md create mode 100644 .convoys/ship-readiness.md create mode 100644 .cursor/agents/role-a11y-auditor.md create mode 100644 .cursor/agents/role-architect.md create mode 100644 .cursor/agents/role-conductor.md create mode 100644 .cursor/agents/role-design-system-auditor.md create mode 100644 .cursor/agents/role-doc-writer.md create mode 100644 .cursor/agents/role-ia-architect.md create mode 100644 .cursor/agents/role-implementer.md create mode 100644 .cursor/agents/role-reviewer.md create mode 100644 .cursor/agents/role-ux-reviewer.md create mode 100644 .cursor/rules/api-routes.mdc create mode 100644 .cursor/rules/auth-and-permissions.mdc create mode 100644 .cursor/rules/db-and-schema.mdc create mode 100644 .cursor/rules/no-go-zones.mdc create mode 100644 .cursor/rules/schema-map.mdc create mode 100644 .cursor/rules/ui-and-theming.mdc create mode 100644 .cursor/skills/add-api-route/SKILL.md create mode 100644 .cursor/skills/add-page/SKILL.md create mode 100644 .github/CODEOWNERS create mode 100644 .github/PULL_REQUEST_TEMPLATE.md create mode 100644 .github/workflows/agent-context-drift.yml create mode 100644 .github/workflows/ci.yml create mode 100644 .github/workflows/pr-health-rollup.yml create mode 100644 .github/workflows/preview-smoke.yml create mode 100644 .github/workflows/visual-diff.yml create mode 100644 AGENTS.md create mode 100644 docs/SCHEMA_MAP.md create mode 100644 docs/agent-context/README.md create mode 100644 lib/flags/index.js create mode 100755 scripts/log-convoy-event.sh create mode 100755 scripts/wt.sh create mode 100644 tests/smoke/app.smoke.spec.ts diff --git a/.agent-context-manifest.yml b/.agent-context-manifest.yml new file mode 100644 index 0000000..7618237 --- /dev/null +++ b/.agent-context-manifest.yml @@ -0,0 +1,176 @@ +# .agent-context-manifest.yml +# +# Generated by agent-pipeline bootstrap-agent-context skill. +# Tracks which artifacts the bootstrap installed in this repo, where they +# came from, and what pipeline version they correspond to. +# +# Read by the `sync-agent-context` skill to detect drift and propose updates. +# Don't edit by hand — use the bootstrap or sync skill in Cursor. +# +# Schema: https://github.com/varutasu/agent-pipeline/blob/main/docs/manifest-schema.md + +schema_version: 1 +pipeline_version: "0.5.0" +pipeline_source: "https://github.com/varutasu/agent-pipeline" +installed_at: "2026-05-22T22:25:00Z" +last_synced_at: "2026-05-22T22:25:00Z" + +layers: + - L1 + - L2 + - L3 + +# Notes: +# - AGENTS.md is hand-curated per-repo — NOT tracked (always shows drift) +# - docs/SCHEMA_MAP.md is hand-curated per-repo — NOT tracked +# - .convoys/.md files are runtime outputs — NOT tracked +artifacts: + - path: ".convoys/README.md" + source: "skills/bootstrap-agent-context/templates/L3-pipeline/_common/convoys-readme.md.template" + version: "0.5.0" + installed_hash: "sha256:a48548cd3f5d0c40fc179106890661c3be5fcdc13eb705af7cfe9233e0b8b209" + + - path: ".cursor/agents/role-a11y-auditor.md" + source: "skills/bootstrap-agent-context/templates/L2-roles/role-a11y-auditor.md" + version: "0.5.0" + installed_hash: "sha256:a59938deceb0246ebd7e477f1f9a442102f9fcbb81b0364f0ddc5f86e95a7930" + + - path: ".cursor/agents/role-architect.md" + source: "skills/bootstrap-agent-context/templates/L2-roles/role-architect.md" + version: "0.5.0" + installed_hash: "sha256:269bd62af1557c5d353a9f95a613960e3434be4ec6e0c0b5f6b099adf6872044" + + - path: ".cursor/agents/role-conductor.md" + source: "skills/bootstrap-agent-context/templates/L2-roles/role-conductor.md" + version: "0.5.0" + installed_hash: "sha256:bc75a3e6646217a015f7bb60c3610afd9b57ae91c7d2fc7a7971f4709b19368a" + + - path: ".cursor/agents/role-design-system-auditor.md" + source: "skills/bootstrap-agent-context/templates/L2-roles/role-design-system-auditor.md" + version: "0.5.0" + installed_hash: "sha256:d214cecb1e8482fc24f2815c8220c860191f08526614f89cf9a5797e4ee9110a" + + - path: ".cursor/agents/role-doc-writer.md" + source: "skills/bootstrap-agent-context/templates/L2-roles/role-doc-writer.md" + version: "0.5.0" + installed_hash: "sha256:d4e8bf8cee93153506b7b742848462422dbe5cc7fd012c62f6ffd50460e344d4" + + - path: ".cursor/agents/role-ia-architect.md" + source: "skills/bootstrap-agent-context/templates/L2-roles/role-ia-architect.md" + version: "0.5.0" + installed_hash: "sha256:69685a3a407c4ee25e2606d426c3107d6b917abee80f907e16ade4a16b439839" + + - path: ".cursor/agents/role-implementer.md" + source: "skills/bootstrap-agent-context/templates/L2-roles/role-implementer.md" + version: "0.5.0" + installed_hash: "sha256:b4f4d8596068679b90ffc3a2b6d2e1b6548caf8c68a50f7ed640ba8f638c1c4c" + + - path: ".cursor/agents/role-reviewer.md" + source: "skills/bootstrap-agent-context/templates/L2-roles/role-reviewer.md" + version: "0.5.0" + installed_hash: "sha256:1ff38349321402a0ac2be37878dc2c0bcab62e54caf74c422b919aa6d75f9b67" + + - path: ".cursor/agents/role-ux-reviewer.md" + source: "skills/bootstrap-agent-context/templates/L2-roles/role-ux-reviewer.md" + version: "0.5.0" + installed_hash: "sha256:3a1d4b66981f469b15e23a1cd34ab41352759966179e126b3d56ddc1eca4a03e" + + - path: ".cursor/rules/api-routes.mdc" + source: "skills/bootstrap-agent-context/templates/L1-context/api-routes.mdc.template" + version: "0.5.0-local" + installed_hash: "sha256:54cd66d71f5a129a67d0f4b1797f5f63b7f9aae3eeabe67217861456ff4db59b" + + - path: ".cursor/rules/auth-and-permissions.mdc" + source: "tcg-vault-local" + version: "0.5.0-local" + installed_hash: "sha256:9b7eb7bea0cad0e43d0e9442eb8b660945cb6935f9f7c82fd7d7d71d66b39a2d" + + - path: ".cursor/rules/db-and-schema.mdc" + source: "tcg-vault-local" + version: "0.5.0-local" + installed_hash: "sha256:83df2cf7121722a092f85165b1a93c755ce57e5361e0f6b2ecc74e9f928c5015" + + - path: ".cursor/rules/no-go-zones.mdc" + source: "skills/bootstrap-agent-context/templates/L1-context/no-go-zones.mdc" + version: "0.5.0-local" + installed_hash: "sha256:aa7046bc3e0266cb3c9b0eb0ef8f68cc50d6837f65c96861804ff81b9c4afa64" + + - path: ".cursor/rules/schema-map.mdc" + source: "tcg-vault-local" + version: "0.5.0-local" + installed_hash: "sha256:3429bad56384117dc81873b337a6d815bd53799389f7908dedb53dbb7642bced" + + - path: ".cursor/rules/ui-and-theming.mdc" + source: "tcg-vault-local" + version: "0.5.0-local" + installed_hash: "sha256:b841ddda5baa47a76c3726a3c92b2c82a45120fdd461b3243bf970479d1cf1df" + + - path: ".cursor/skills/add-api-route/SKILL.md" + source: "tcg-vault-local" + version: "0.5.0-local" + installed_hash: "sha256:0e29f7e994a51e5a40b8308edab08ee9c1f713e297d68e98029294d8ca568cc7" + + - path: ".cursor/skills/add-page/SKILL.md" + source: "tcg-vault-local" + version: "0.5.0-local" + installed_hash: "sha256:318912077a6ced6a3a31f85dc15d069bf7627c161b6735e3fa259ca10766daa9" + + - path: ".github/CODEOWNERS" + source: "skills/bootstrap-agent-context/templates/L3-pipeline/nextjs/CODEOWNERS.template" + version: "0.5.0-local" + installed_hash: "sha256:b714a0a011776300abeab92fe8969f150c273c37d0d6b37c1ad2eb67d47decda" + + - path: ".github/PULL_REQUEST_TEMPLATE.md" + source: "skills/bootstrap-agent-context/templates/L3-pipeline/_common/PULL_REQUEST_TEMPLATE.md.template" + version: "0.5.0" + installed_hash: "sha256:89863e58b9ec194aef1c94d3596e892467833e8bc880a28994acca401b6d9635" + + - path: ".github/workflows/agent-context-drift.yml" + source: "skills/bootstrap-agent-context/templates/L3-pipeline/_common/agent-context-drift.yml.template" + version: "0.5.0" + installed_hash: "sha256:5505c296c1b61d023ee2aca222103097e2b5ed2e0e38da3679cc4f9754457785" + + - path: ".github/workflows/ci.yml" + source: "skills/bootstrap-agent-context/templates/L3-pipeline/nextjs/ci.yml.template" + version: "0.5.0-local" + installed_hash: "sha256:6aff7a1c9f2e42606580c241b6dadca7c2d8550aeb959bd69fdd843eb9097cac" + + - path: ".github/workflows/pr-health-rollup.yml" + source: "skills/bootstrap-agent-context/templates/L3-pipeline/nextjs-prisma-vercel/pr-health-rollup.yml.template" + version: "0.5.0-local" + installed_hash: "sha256:8747674323807d84395fa027b25e7e27881c5e6b0cc87a138d9cb3f78fd88956" + + - path: ".github/workflows/preview-smoke.yml" + source: "skills/bootstrap-agent-context/templates/L3-pipeline/nextjs-prisma-vercel/preview-smoke.yml.template" + version: "0.5.0-local" + installed_hash: "sha256:2e71026b09db8b2f32b6a868d705489600c875082d6320c2369bf2f5ebc315b8" + + - path: ".github/workflows/visual-diff.yml" + source: "skills/bootstrap-agent-context/templates/L3-pipeline/nextjs-prisma-vercel/visual-diff.yml.template" + version: "0.5.0-local" + installed_hash: "sha256:88270b1fa59aba99591ec094764dd367deed956bcb746ac6bb195241b3a7dae1" + + - path: "docs/agent-context/README.md" + source: "skills/bootstrap-agent-context/templates/L1-context/agent-context-readme.md.template" + version: "0.5.0-local" + installed_hash: "sha256:095b9cc6a30327114c9ddfb4ff57a5fde76b213e12b1c5574a1f96205d60dbad" + + - path: "lib/flags/index.js" + source: "skills/bootstrap-agent-context/templates/L3-pipeline/nextjs-prisma-vercel/flags-index.ts.template" + version: "0.5.0-local" + installed_hash: "sha256:1a3cd1f900194eaf4ec86588dd1c3c2bff6a565e742061fc911abdd47bd5f3a5" + + - path: "scripts/log-convoy-event.sh" + source: "skills/bootstrap-agent-context/templates/L3-pipeline/_common/log-convoy-event.sh" + version: "0.5.0" + installed_hash: "sha256:cd0413691066a177b6b4e6164a9a0978c20a853ad60222ae833b5d53b255818d" + + - path: "scripts/wt.sh" + source: "skills/bootstrap-agent-context/templates/L3-pipeline/_common/wt.sh" + version: "0.5.0" + installed_hash: "sha256:2a4f44a159f80a8ea6fe53ac507c01a2f91a4e2118d997a98b051808ac35e9a5" + + - path: "tests/smoke/app.smoke.spec.ts" + source: "skills/bootstrap-agent-context/templates/L3-pipeline/nextjs-prisma-vercel/playwright-smoke.spec.ts.template" + version: "0.5.0" + installed_hash: "sha256:a62c10edb712a61f1cfece43705bfff75a5a66ad6bc8b53f7e69a43c3efb962c" diff --git a/.convoys/README.md b/.convoys/README.md new file mode 100644 index 0000000..07502c4 --- /dev/null +++ b/.convoys/README.md @@ -0,0 +1,121 @@ +# Convoys + +A **convoy** is a multi-PR work-stream coordinated by an agent pipeline. One convoy = one feature, bug fix, or epic. Each convoy is a Markdown file in this directory plus an optional sub-directory of implementer briefs. + +## File layout + +``` +.convoys/ +├── README.md (this file) +├── .md (the convoy file — written by role-conductor) +└── / + ├── brief-1-.md (written by role-architect) + ├── brief-2-.md + └── ... +``` + +## Convoy file format + +Frontmatter (set by `role-conductor`, then appended-to by other roles): + +```yaml +--- +name: +classification: feature | hotfix | docs-only | infra-only | server-only | config-only +success_metric: +skip: + - +status: open | in-progress | merged | shipped | abandoned +created: +--- +``` + +Body sections (added in order by the pipeline roles): + +1. `## Why` (Conductor) +2. `## Scope` (Conductor) +3. `## Roles invoked` (Conductor) +4. `## Todos` (Conductor → refined by Architect) +5. `## IA` (IA Architect) +6. `## UX` (UX Reviewer) +7. `## Architecture` (Architect) + +After Architect, briefs live in `.convoys//brief-N-*.md`. Implementers read only their brief, not the whole convoy. + +## Skip flags + +The Conductor sets `skip:` based on classification. These flags map to pipeline stages that no-op when set: + +| Flag | Skips | +| --- | --- | +| `ia` | IA Architect | +| `ux` | UX Reviewer | +| `arch` | Architect | +| `test` | Component tests | +| `review` | Reviewer | +| `visual` | Visual diff | +| `a11y` | A11y auditor | +| `design` | Design-system auditor | +| `smoke` | Staging smoke | +| `qa` | Manual QA | +| `docs` | Doc Writer | +| `flag` | Flag rollout | + +Never skipped (mandatory human gates): `plan-approval`, `pr-merge`, `prod-promote`. + +## Status lifecycle + +- `open` — Conductor created the convoy; no work started. +- `in-progress` — At least one brief has an open or merged PR. +- `merged` — All briefs merged to umbrella; release PR to develop pending. +- `shipped` — Release to main complete; flag rollout (if any) underway. +- `abandoned` — Convoy closed without shipping; reason in convoy body. + +Update status by editing the convoy frontmatter as you progress. + +## Adding a new convoy + +1. Open Cursor in this repo. +2. Prompt: *"Start a new convoy: . Success = ."* +3. The `role-conductor` subagent writes `.convoys/.md`. +4. Run subsequent roles in order per the convoy's `Roles invoked` list. + +See `.cursor/agents/role-conductor.md` for the Conductor's full spec. + +## Multitask + worktrees (Cursor 3.2+) + +[Cursor 3.2 (Apr 24, 2026)](https://cursor.com/changelog/04-24-26) added `/multitask` async subagents and native worktree management in the Agents Window. The pipeline uses both: + +**Audit fan-out** — after an implementer ships a PR draft: + +``` +/multitask role-reviewer + role-design-system-auditor + role-a11y-auditor +``` + +All three read the same diff and emit independent comments. Use group id `audit--` so analytics can compute wall-clock savings. + +**Implementer fleet** — after architect's plan is approved (gate 1), if `slice_dependencies:` declares parallel-safe briefs (`depends_on: []`, disjoint `files:`): + +``` +/multitask role-implementer briefs 1, 2, 3 +``` + +Use Cursor's Agents Window to create a worktree per brief — one click each. The legacy `scripts/wt.sh` is now a deprecation stub. + +See the [multitask playbook](https://github.com/varutasu/agent-pipeline/blob/main/docs/multitask-playbook.md) for the full guardrail set. + +## Self-analytics + +Each L2 role appends one event to `.convoys/.metrics.jsonl` via `scripts/log-convoy-event.sh`. The file is gitignored by default — events stay local. To opt-in to commit team-shared metrics, remove `.convoys/.metrics.jsonl` from `.gitignore`. + +Aggregate across repos and render a dashboard with the [agent-pipeline analytics scripts](https://github.com/varutasu/agent-pipeline/tree/main/analytics): + +```bash +cd ~/code/agent-pipeline/analytics +npx tsx analyze-convoys.ts [...] +npx tsx render-dashboard.ts +open ~/agent-pipeline-data/dashboard.html +``` + +Schema: [`analytics/schemas/convoy-event.json`](https://github.com/varutasu/agent-pipeline/blob/main/analytics/schemas/convoy-event.json). + diff --git a/.convoys/ship-readiness.md b/.convoys/ship-readiness.md new file mode 100644 index 0000000..a953718 --- /dev/null +++ b/.convoys/ship-readiness.md @@ -0,0 +1,232 @@ +--- +name: ship-readiness +classification: epic +success_metric: tcg-vault is safe to expose to anonymous internet traffic with a documented launch checklist green +skip: [] +status: open +created: 2026-05-22 +--- + +# Ship-readiness convoy + +Umbrella convoy capturing the full agent-pipeline review of tcg-vault as of 2026-05-22. Findings are grouped by L2 role lens (Reviewer / Architect / Design-system / A11y / IA / Doc-writer) and severity. Each item points to the convoy that will execute the fix. + +Code graph: 122 files, 628 nodes, 5602 edges, 11 communities. Indexed by `user-code-review-graph` MCP. + +## P0 — ship-blockers (security) + +These MUST land before any anonymous traffic touches the production URL. + +### 1. `getUserFromRequest` returns a hardcoded admin when no Bearer token is present + +- **File:** `lib/permission-middleware.js` lines 13-17. +- **Impact:** Every API route that calls `getUserFromRequest` (30+ handlers — see `user-code-review-graph` cross-community edges from `api-handler` → `lib-admin`) accepts unauthenticated requests as admin user 1. +- **Repro:** `curl https:///api/collections` with no `Authorization` header returns admin's collections. +- **Fix:** Delete lines 13-17. Return `null` when no Bearer token. Update every caller to handle `null` properly (most already do; the broken fallback was masking the right path). +- **Owns:** `role-architect` + `role-implementer` (one PR; small surface area in the helper, callers already check `!user`). + +### 2. JWT_SECRET hardcoded fallback in 7 files + +- **Files:** + - `pages/api/auth-utils.js` (`'your-secret-key'`) + - `pages/api/auth/login.js`, `pages/api/auth/register.js`, `pages/api/auth/verify.js` + - `pages/api/favorites.js`, `pages/api/users/search.js` + - `lib/permission-middleware.js` +- **Impact:** If `JWT_SECRET` env var is unset (e.g. preview/staging misconfig), tokens are signed with `'your-secret-key-change-in-production'` — an attacker can sign their own admin token in 5 seconds. +- **Fix:** Centralize JWT_SECRET access in one helper that `throw`s at module load if `process.env.JWT_SECRET` is unset. Every other file imports from there. +- **Bonus:** Token expiry is inconsistent (`/api/auth/login.js` uses 24h, `pages/api/auth-utils.js` uses 7d). Pick one. +- **Owns:** `role-architect` + `role-implementer`. + +### 3. Default admin credentials in seed + README + +- **Files:** + - `scripts/setup-neon-db.js` lines 130-138 — creates `admin@tcgvault.com` / `admin123` + - `README.md` documents the credentials + - `pages/api/setup-database.js` — duplicates the setup AND is an UNAUTHENTICATED public POST endpoint with `Access-Control-Allow-Origin: *` +- **Impact:** Anyone who hits `/api/setup-database` can re-trigger DDL. The `admin123` password is one Google away from public knowledge. +- **Fix:** + 1. Delete `pages/api/setup-database.js`. Schema setup is a one-time job; it should not be a route. + 2. Change `setup-neon-db.js` to require a `ADMIN_INITIAL_PASSWORD` env var (no default). + 3. Strip the admin password from README — replace with "run `npm run setup-db` and follow the prompt". +- **Owns:** `role-implementer`. + +### 4. Dev-only test endpoints shipped to production + +- **Files:** `pages/api/simple.js`, `pages/api/test-auth.js`, `pages/api/test-db.js`, `pages/api/setup-database.js`. +- **Impact:** Unknown — depends on what they expose. `/api/test-db` likely returns the DB connection string; `/api/test-auth` may leak token-handling details. +- **Fix:** Delete all four. Add a CI grep that fails the build if any file matching `pages/api/(test-|simple|setup-)*.js` exists. +- **Owns:** `role-implementer`. + +### 5. CORS `Access-Control-Allow-Origin: *` on auth endpoints + +- **Files:** at minimum `pages/api/auth/login.js`, `pages/api/auth/register.js`, `pages/api/setup-database.js` (verify others). +- **Impact:** Any origin can submit credentials. Combined with the no-rate-limit problem below, credential stuffing is wide open. +- **Fix:** Set `Access-Control-Allow-Origin` to the literal frontend origin (`https://tcgvault.com` / preview domain), or remove the header entirely if the API and the frontend are same-origin (they are, on Vercel). +- **Owns:** `role-implementer`. + +### 6. No rate limiting anywhere + +- **Impact:** Login endpoint accepts unlimited attempts; card-search endpoint can be hammered; image upload endpoints can be exhausted. The `pages/api/cards/import-*.js` endpoints externally hit Scryfall/Pokémon APIs with no caller throttling. +- **Fix:** Adopt `@upstash/ratelimit` (free tier covers a small launch) or Vercel's built-in middleware-based rate limiting. Apply to: `/api/auth/login`, `/api/auth/register`, `/api/users/search`, `/api/cards/search`, all `/api/cards/import-*`, and `/api/user/avatar*` (upload). +- **Owns:** `role-architect` (pattern) → `role-implementer` (per-route). + +### 7. Layout default-prop leaks maintainer email + +- **File:** `components/Layout.js` line 562: `function Layout({ children, user = { email: 'me@randallstillwell.com', role: 'user' }, ... })`. +- **Impact:** Any page that renders Layout without passing a `user` prop displays your real email and impersonates you as the logged-in user. +- **Fix:** Default `user = null` and render a logged-out state branch. Verify every page passes `user` explicitly (the graph shows ~13 pages call `Layout`; audit each). +- **Owns:** `role-implementer`. + +## P1 — pre-launch quality bar + +### 8. Two SQL clients in parallel (`@neondatabase/serverless` + `@vercel/postgres`) + +- **Impact:** Two different param-handling APIs, two different transaction stories, two different connection-pool stories. Plus `lib/database.js`'s manual interpolation + `sql.unsafe(query)` is a SQL-injection vector if any caller passes user input through. +- **Fix:** Pick `@vercel/postgres` (tagged-template, no injection vector). Migrate every call site of `lib/database.js::db.query`. Delete `lib/database.js`. +- **Reviewer/Architect call:** small enough to fit in one convoy; touches ~3 files based on graph. + +### 9. Three parallel client-side auth implementations + +- **Files:** `lib/auth-context.js` (`AuthProvider` / `useAuth`), `lib/admin-auth.js` (`AdminProvider` / `useAdmin` / `useIsAdmin`), `lib/use-auth.js` (`useAuth`). +- **Impact:** Pages randomly import from one of three places. State is duplicated. Logout in one provider doesn't necessarily clear the others. Token-verify roundtrips happen 3× on initial page load if all three providers mount. +- **Fix:** Collapse to `lib/use-auth.js` as the canonical hook. Migrate every importer. Delete `auth-context.js` and `admin-auth.js`. Roll up `useIsAdmin` semantics into `useAuth().user?.role === 'admin'`. +- **Owns:** `role-architect` (decision) → `role-implementer` (per-page migration; ~30 importers). + +### 10. No tests + +- **Impact:** The first agent-driven refactor of `getUserFromRequest` (P0 #1) is high-blast-radius with no safety net. +- **Fix sequence:** + 1. Install `vitest`. Add `npm run test:run` script. + 2. Install `@playwright/test`. Wire up `tests/smoke/app.smoke.spec.ts` (already drafted; needs `playwright.config.ts`). + 3. Re-enable the `test:` job in `.github/workflows/ci.yml` (commented out at install time). + 4. Add unit tests for `lib/permission-middleware.js`, `lib/slug-utils.js`, `pages/api/auth-utils.js`. + 5. Wire `preview-smoke.yml` to run against the Vercel preview URL. +- **Owns:** `role-architect` (test strategy) → `role-implementer` (initial suite). + +### 11. No migration tool — `scripts/add-*.js` graveyard + +- **Files:** 27 scripts in `scripts/` of the form `add-foo-column.js`, `fix-bar-constraint.js`, `seed-baz.js`. No idempotency tracking, no `schema_migrations` table, no rollback. +- **Impact:** Onboarding a new env requires re-running every script in the right order. No way to know what's been run on a given Neon branch. Every new column is at risk of being missed in prod. +- **Fix:** Adopt `node-pg-migrate` (lightweight, matches the existing pattern best) OR migrate to `drizzle-kit` if the team wants schema-as-code. Backfill a single "initial" migration matching current prod schema. From there, every new column ships as a migration file. +- **Owns:** `role-architect` (tool selection) → `role-implementer` (backfill + first new migration). + +### 12. Branding mismatch — "TCG Vault" vs. "Deck Hearth" + +- **Files:** README, `package.json`, seed data say "TCG Vault" / `admin@tcgvault.com`. `components/Layout.js` lines 596 + 689 render "Deck Hearth" + "DH" logo. The `.env.local` template, `vercel.json`, and Vercel project name should also be audited. +- **Impact:** Confusing for users. Confusing for marketing. Confusing for analytics. Pick one. +- **Fix:** Brand workshop → final name → global replace → update README, package.json `"name"`, every UI string, Vercel project name, email sender, support pages. Schedule a redirect from the old domain. +- **Owns:** `role-ia-architect` (which name? — needs human decision) → `role-implementer`. + +## P2 — refactor priorities + +### 13. God components (10 files over 500 lines) + +| File | Lines | Notes | +| --- | --- | --- | +| `pages/cards.js` | 1499 | `AuthenticatedCards` (886) + `Card3D` (502) live in one file. Split into `pages/cards/index.js` + `components/Card3D.js`. | +| `pages/collection/[identifier].js` | 1044 | `CollectionView` is one mega-component. Extract: header, card-grid, share-modal-wrapper, edit-form. | +| `pages/collections.js` | 989 | Similar structure to collection/[identifier]. Possibly share extracted pieces. | +| `pages/card/[id].js` | 913 | `CardDetail` — split into header, owned-badge, add-to-collection-flow. | +| `pages/deck-builder.js` | 823 | `DeckBuilder` — extract card-search, deck-list, mana-curve panels. | +| `components/CameraScanner.js` | 817 | Camera + AI-OCR + detection-loop — extract the detection loop into a hook. | +| `pages/admin/card-editor.js` | 778 | Form heavy. Use a `useFormState` pattern + separate the search-results subview. | +| `pages/scanner.js` | 776 | Mirror of CameraScanner concerns plus queue management. | +| `pages/settings.js` | 669 | One screen per settings section is the usual fix. | +| `pages/profile.js` | 625 | Avatar generation logic alone is ~150 lines — extract `useGeneratedAvatar` hook. | + +Each is one convoy of its own. Use the architect role's `slice_dependencies:` to fan out implementers safely. + +### 14. Schema-design smells (documented in `docs/SCHEMA_MAP.md`) + +- `users` has two avatar columns (`profile_image_url` + `avatar_url`). Reconcile. +- `collections` has two visibility flags (`is_public BOOLEAN` + `visibility VARCHAR`). Reconcile. +- `cards.quantity` + `cards.favorited` are unused (they belong on `user_cards` / `user_favorites`). Drop. +- `user_settings` table duplicates several `users` columns. Reconcile. +- All enum-shaped VARCHARs (`role`, `condition`, `theme`, `game`, `visibility`) should be CHECK-constrained or proper Postgres ENUMs. +- `collections.tags` is `TEXT` (comma-separated). Migrate to `JSONB` or a join table. + +### 15. Component coupling warning from graph + +`user-code-review-graph` flagged: +- High coupling (44 edges) between `components-handle` and `pages-handle` (largely `Layout`, `CardItem`, `ManaCost` — expected for a shared UI surface). +- High coupling (34 edges) between `lib-admin` and `api-handler` — almost all via `getUserFromRequest`. After P0 #1 is fixed, this number stays high because the auth check is genuinely shared — that's fine. + +### 16. Lots of inline SVG and emoji + +The `getIcon` registry in `Layout.js` and `MobileNavigation.js` redefines the same SVG paths. Extract to `components/icons/` with named exports. Then audit the codebase for inline SVG that should be a named import. Bonus: lazy-load the larger icon families. + +## P3 — UX, IA, design-system + +### Role-ia-architect findings + +- **URL structure** — solid. `/cards`, `/collections`, `/collection/[slug]`, `/deck-builder`, `/community/collections`. Coherent. One quirk: `/card/[id]` (singular) for detail vs. `/cards` (plural) for index — typical Next.js shape but worth a redirect rule so `/cards/[id]` also resolves. +- **Logged-out homepage** — current `pages/index.js` is 316 lines; needs an editorial pass. What's the value prop in one sentence? Right now it's mostly "we have cards". +- **Onboarding** — signup → profile setup → first collection → scan-or-import card. Currently each step is a separate page. Consider a multi-step wizard at `/onboarding` to keep the new user in flow. +- **Discoverability** — `/community/decks` and `/community/forums` are in the nav but flagged as placeholders. Either ship the MVP for each before launch (forums likely too big) or hide the nav items until they exist. + +### Role-ux-reviewer findings + +- **Loading states** — most data fetches set `loading: true` then re-render; very few show skeletons. Card grids should use shimmer placeholders; modals should disable submit while in flight. +- **Error states** — error messages bubble to `console.error` and toast nothing. Add a global toast system (e.g. `sonner`) and wire every catch block. +- **Empty states** — `/my-cards` and `/collections` when empty drop to "no cards yet". Replace with first-time CTA: "Scan your first card" or "Browse popular sets". +- **Mobile drawer** — `MobileNavigation` is solid (recent commit `442e906`). One thing: the bottom-bar's active state contrast looks low in light mode; verify against AA. +- **Camera scanner UX** — 817 lines of detection loop. Add a one-line "scanning…" status under the viewfinder and a single "captured N cards" badge. The current toolbar is busy. + +### Role-design-system-auditor findings + +- **Two visual languages mixing** — Tailwind classes AND CSS variables on the same elements. This is documented in `.cursor/rules/ui-and-theming.mdc`; the cleanup is to define which property goes where and enforce. +- **Hardcoded hex colors** — grep for `bg-\[#` and `style={{ backgroundColor: '#`. There are still a handful; convert to theme tokens. +- **Logo + brand** — see P1 #12. Then once the name is settled, the "DH" logo + AnimatedFireLogo need to be unified into one brand mark. +- **Modal patterns** — `CollectionSelectionModal`, `ShareModal`, `UploadImageModal` each have their own backdrop + focus-trap implementation. Extract `` primitive. Use `headlessui` or `radix-ui`'s Dialog to get focus management for free. +- **Card grid spacing + density** — `pages/cards.js` (the 1499-line monster) does responsive grid math inline. Extract a `` component that handles density (compact / comfortable / spacious) + sort + filter chrome. + +### Role-a11y-auditor findings + +- **Focus traps in modals** — none of the modals trap focus. Tab through `ShareModal` and you leave to the background. Critical for keyboard users + screen readers. +- **ESC to close modals** — inconsistent. Some have it, some don't. +- **Skip-to-content** — no ``. Add to `_app.js`. +- **Image alts** — card images use `alt={card.name}` (good); avatar images sometimes have empty alts. Audit. +- **Color contrast** — verify the muted text colors (`var(--text-secondary)`) hit AA on both themes. The mobile bottom-bar inactive state is a likely fail. +- **Form errors** — login/signup form errors are visually red but not connected to inputs via `aria-describedby`. Screen readers don't know which field failed. +- **Keyboard ops on non-button elements** — most clickable `
`s already have `onKeyDown` but a few don't (audit with `rg "onClick" components pages | rg -v " *"Run role-conductor: start a new convoy `fix-auth-bypass` to address P0 #1, #2, #4, #5, #6 partial in `.convoys/ship-readiness.md`. Success = `getUserFromRequest` returns null for missing tokens; no API route accepts unauthenticated requests; CI green."* + +The Conductor will set classification, skip flags, and hand off to subsequent roles. diff --git a/.cursor/agents/role-a11y-auditor.md b/.cursor/agents/role-a11y-auditor.md new file mode 100644 index 0000000..ddd7031 --- /dev/null +++ b/.cursor/agents/role-a11y-auditor.md @@ -0,0 +1,105 @@ +--- +name: role-a11y-auditor +description: >- + Accessibility audit on a UI diff. Checks for missing labels, keyboard + navigation, focus management, color contrast, semantic HTML, and ARIA + correctness. Read-only. Use after the implementer's PR draft on PRs that + touch UI files. Does not require a browser MCP — works from the diff + + static analysis. Safe to run in parallel with role-reviewer + + role-design-system-auditor via Cursor 3.2 /multitask. +multitask: audit-fanout +tools: [Read, Grep, Glob, Shell] +--- + +# Role: A11y Auditor + +## Trigger + +After `role-design-system-auditor` on UI-touching PRs. Skip when convoy frontmatter has `skip: a11y`. + +## Inputs + +- The PR diff (UI files only). +- The convoy's UX section (which already lists a11y constraints — verify the implementer satisfied them). +- Existing accessible patterns in the repo (look at existing `Dialog`, `Form`, `Button` primitives). + +## Outputs + +A structured comment for the PR Health rollup: + +```markdown +## A11y Audit + +| Check | Status | Count | +| --- | --- | --- | +| Labels | ✅ / ❌ | | +| Keyboard nav | ✅ / ❌ | | +| Focus management | ✅ / ❌ | | +| Color contrast | ✅ / ⚠️ | | +| Semantic HTML | ✅ / ❌ | | +| ARIA correctness | ✅ / ⚠️ | | +| UX constraint match | ✅ / ❌ | | + +### Critical (must fix) +- +... + +### Warnings (recommended) +- +... + +### Notes +- ... +``` + +## Checklist (apply per file) + +1. **Labels**: every ``, `