bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
---
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.
docs: post-convoy cleanup for cors-tighten
Reflects the merged cors-tighten convoy (PR #19, squash commit da50d78)
in repo documentation. Closes P0 #5 (Wildcard CORS on API surface) from
PARTIAL -> RESOLVED, leaving only P0 #6 (full add-rate-limiting) open
of the original P0 ship-blocker set. One brief in the convoy: Brief 1
shipped as planned with no scope expansions and no implementer deviations
from the verbatim spec.
.convoys/cors-tighten.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section: all 5 architect-self-ratifiable decisions
ratified verbatim (D1 Option B / D2 delete OPTIONS / D3 moot / D4
no new tests / D5 add CI lock); Pattern split (16 Pattern A + 8
Pattern B) per architect's 10-file audit + implementer's per-file
diff review; diff size (25 files, +29/-261); empirical CI metrics
from post-merge run 26378806555 (forbidden-cors-headers 4s PASS,
Playwright smoke 56s 3/3 in 3.3s, Screenshot diff continue-on-error
0 with the documented Decision-4 missing-baseline failure beneath);
cross-validation that Playwright smoke continues to pass post-CORS
removal (the auth + public surfaces don't depend on the wildcard
header); implementer subagent-retry footnote (HEAD already at
a843736 when retry woke up - transient retry, work is canonical);
operator-action-required-going-forward: none; What did NOT change
audit trail.
.convoys/ship-readiness.md:
- new ## Status summary at the top (right after the code-graph line):
P0 set is now 7/8 RESOLVED; only #6 (rate-limiting) remains. Table
lists each P0 with its resolving convoy + squash commit for a quick
scan of remaining work.
- P0 #5 marked RESOLVED 2026-05-24. Added the cors-tighten as-shipped
block (24 files swept, new CI job, 16/8 Pattern split, 5 decisions
ratified, diff stat, post-merge CI metrics, transient retry
footnote, operator-action: none). Brief 4's 2026-05-23 partial
is preserved as the prior as-shipped layer above the cors-tighten
layer to maintain the audit trail.
- Queued convoys: removed the cors-tighten entry (no longer queued).
Added a new tighten-visual-diff-path-filter entry (P3 polish) -
Screenshot diff workflow triggered on API-only PR #19 because its
paths: filter is pages/** which matches pages/api/** too. ~55s of
CI waste per API-only PR; one-line YAML tweak; verify GitHub
Actions' negated-glob semantics before merging.
.cursor/rules/api-routes.mdc:
- new ## CORS section near the existing ## Dev/test endpoints
(removed) section. Documents the no-CORS-by-default convention,
the brief-4 + cors-tighten lineage, the new forbidden-cors-headers
CI gate, and three forward-conventions (no setHeader for CORS,
no OPTIONS preflight handlers, design a proper middleware layer
if a future cross-origin caller is needed - not wildcards in
individual handlers).
AGENTS.md intentionally untouched. Gotcha #5 (the public
setup-database.js endpoint) is already RESOLVED by fix-auth-bypass
Brief 3 and unrelated to this convoy. The new convention belongs in
.cursor/rules/api-routes.mdc (where API conventions live) rather than
AGENTS.md; the convoy file + the new CI gate are sufficient
documentation for the audit trail. Per convoy spec, no new gotcha
entry needed.
No changes to: package.json, package-lock.json, pages/api/**, lib/**,
components/**, scripts/**, test/**, tests/**, .github/workflows/**,
README.md, TESTING_GUIDE.md, playwright.config.js.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 21:49:30 -04:00
## Status summary (as of 2026-05-24)
docs: post-convoy cleanup for add-rate-limiting — MILESTONE, last P0 closed
Reflects the merged add-rate-limiting convoy (PR #20, squash commit
708ef45) in repo documentation. **This is the milestone cleanup** —
add-rate-limiting closed P0 #6 (No rate limiting anywhere), the LAST
open P0 ship-blocker. `.convoys/ship-readiness.md`'s § Status summary
flips from "7 of 8 RESOLVED; 1 remains" to **"8 of 8 RESOLVED.
Launch-readiness P0 checklist is empty."** One brief in the convoy:
Brief 1 shipped as planned with no scope expansions and no implementer
deviations from the verbatim spec; all six architect decisions ratified
verbatim at gate 1 (D1 operator-ratified Option A; D2-D6
architect-self-ratified).
.convoys/add-rate-limiting.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section. Opens with the milestone language
pointing back at ship-readiness.md's flipped § Status summary.
Decisions section captures all 6 ratifications (D1 operator-
ratified Option A — Critical: WHY the atomic admin UI fix in
pages/admin/card-import.js was Decision 1's hidden coupling
requirement, since API gating alone would have broken every
"Import Cards" click; D2 hybrid named-limiter shape with
Map<className, Ratelimit> cache; D3 per-class table including
the two D3 tuning-evidence raises — search 30 -> 60/min because
ShareModal.handleSearch has no debounce so a 17-char email = 16
requests in <5s, and generate kept at 5/hour because DiceBear is
free not paid AI; D4 two-extractor shape with defensive THROW
on null/empty userId; D5 uniform 429 message; D6 no new vitest
or playwright specs deferred to fill-vitest-handler-coverage).
As-shipped surface broken into 4 layers (1 lib refactor + 6 route
gates + 1 atomic admin UI fix + 1 rule extension) mirroring the
cors-tighten cleanup's pattern-split shape. Empirical CI metrics
from post-merge run 26382185019 (Playwright smoke 59s 3/3 in
3.8s, forbidden-cors-headers pass, vitest 21/21, lint 128 baseline,
Screenshot diff continue-on-error swallow per Decision 4).
Cross-validation finding: smoke test 2 still passes against the
post-rate-limit preview — that's three convoys in a row (PR #15
Layout default-user, PR #19 CORS-tighten, PR #20 rate-limiting)
where the same 3-test smoke spec defended the auth surface
through sweeping changes. Operator-action-required: none. What
did NOT change audit trail.
.convoys/ship-readiness.md:
- § Status summary at the top flipped from 7/8 to 8/8 RESOLVED.
Header text updated to "Launch-readiness P0 checklist is empty."
P0 #6 row in the table flips from PARTIAL to RESOLVED with the
two-convoy lineage (fix-auth-bypass Brief 4 + add-rate-limiting).
Trailing paragraph rewritten as a milestone note: security gate
closed; remaining launch work is P1 quality bar + P2/P3 polish.
- P0 #6 entry flipped from PARTIAL to RESOLVED with the full
add-rate-limiting as-shipped block (8 sub-bullets covering the
lib refactor shape, the per-class table, the defensive THROW,
the three import routes' auth-gating, the atomic admin UI fix
and WHY, the rule extension, the 6 decisions, and the diff
breakdown). Brief 4's 2026-05-23 partial is preserved as the
prior as-shipped layer to maintain the audit trail.
- Launch sequence step 4 marked RESOLVED 2026-05-24 with the
squash commit + smoke metrics inline.
- Queued convoys: removed the add-rate-limiting entry (it shipped).
Added a new delete-dead-lorcana-import entry (P3 polish; the
Lorcana import route was gated defensively in PR #20 despite
zero current frontend callers — pages/admin/card-import.js's
<select> only offers mtg + pokemon — so if Lorcana stays
permanently out of the admin UI, this is the cleanup PR).
Added three "flagged but kept out of scope" follow-ups per the
convoy's § What did NOT change: harden-multipart-parser (P2;
5MB body still consumed before the 429 path on avatar.js),
god-function-split / refactor-cards-search-sql (P2; 240-line
7-branch SQL in cards/search.js), and withAdmin(handler) wrapper
extraction (P3 DX; the three import routes are call sites #3-5
in the codebase but uniform inline shape was preserved for
convoy atomicity). Updated tighten-visual-diff-path-filter to
note PR #20 also tripped the same false-positive.
AGENTS.md:
- Gotcha #12 extended end-to-end. Was the single-class auth-only
lib + the env-var contract; is now the 5-class reality with a
full per-class table (helper / limit-window / key / routes),
the defensive THROW pattern in extractUserIdentifier, the
gate-ordering rule for per-user limiters, and the
auth → admin-role → rate-limit ordering for the three import
routes. Prominent milestone line opens the new content:
"add-rate-limiting convoy (squash 708ef45, PR #20, 2026-05-24)
closed P0 #6 — all 8 P0s now RESOLVED." Original env-var
contract paragraph (KV_REST_API_URL / KV_REST_API_TOKEN,
fail-closed-in-prod / warn-and-noop-in-dev) is preserved
verbatim above the new content.
- § 6 Testing: intentionally untouched (no test surface changed;
vitest 21/21 and smoke 3/3 still apply).
- § 7 Deployment: intentionally untouched (no deployment-shape
changed; same KV_REST_API_* env vars from Brief 4).
.cursor/rules/api-routes.mdc:
- The implementer extended § Rate limiting in PR #20 with the
per-class table + verbatim call shape + gate-ordering rules +
identifier-extraction + uniform 429 + fail-closed env-var
contract + fail-open Upstash-outage behavior. Doc-writer pass
verified completeness; added a one-sentence convoy-attribution
line at the top of § Rate limiting citing the two-convoy
lineage (fix-auth-bypass Brief 4 for the auth class +
add-rate-limiting for the other four classes and 7 newly-gated
routes), mirroring the post-cors-tighten § CORS attribution
shape. No other touch-ups needed.
No changes to: package.json, package-lock.json, lib/rate-limit.js,
pages/**, components/**, scripts/**, test/**, tests/**,
.github/workflows/**, README.md, TESTING_GUIDE.md, playwright.config.js,
eslint.config.mjs.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 00:09:07 -04:00
**P0 ship-blockers: 8 of 8 RESOLVED. Launch-readiness P0 checklist is empty.**
docs: post-convoy cleanup for cors-tighten
Reflects the merged cors-tighten convoy (PR #19, squash commit da50d78)
in repo documentation. Closes P0 #5 (Wildcard CORS on API surface) from
PARTIAL -> RESOLVED, leaving only P0 #6 (full add-rate-limiting) open
of the original P0 ship-blocker set. One brief in the convoy: Brief 1
shipped as planned with no scope expansions and no implementer deviations
from the verbatim spec.
.convoys/cors-tighten.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section: all 5 architect-self-ratifiable decisions
ratified verbatim (D1 Option B / D2 delete OPTIONS / D3 moot / D4
no new tests / D5 add CI lock); Pattern split (16 Pattern A + 8
Pattern B) per architect's 10-file audit + implementer's per-file
diff review; diff size (25 files, +29/-261); empirical CI metrics
from post-merge run 26378806555 (forbidden-cors-headers 4s PASS,
Playwright smoke 56s 3/3 in 3.3s, Screenshot diff continue-on-error
0 with the documented Decision-4 missing-baseline failure beneath);
cross-validation that Playwright smoke continues to pass post-CORS
removal (the auth + public surfaces don't depend on the wildcard
header); implementer subagent-retry footnote (HEAD already at
a843736 when retry woke up - transient retry, work is canonical);
operator-action-required-going-forward: none; What did NOT change
audit trail.
.convoys/ship-readiness.md:
- new ## Status summary at the top (right after the code-graph line):
P0 set is now 7/8 RESOLVED; only #6 (rate-limiting) remains. Table
lists each P0 with its resolving convoy + squash commit for a quick
scan of remaining work.
- P0 #5 marked RESOLVED 2026-05-24. Added the cors-tighten as-shipped
block (24 files swept, new CI job, 16/8 Pattern split, 5 decisions
ratified, diff stat, post-merge CI metrics, transient retry
footnote, operator-action: none). Brief 4's 2026-05-23 partial
is preserved as the prior as-shipped layer above the cors-tighten
layer to maintain the audit trail.
- Queued convoys: removed the cors-tighten entry (no longer queued).
Added a new tighten-visual-diff-path-filter entry (P3 polish) -
Screenshot diff workflow triggered on API-only PR #19 because its
paths: filter is pages/** which matches pages/api/** too. ~55s of
CI waste per API-only PR; one-line YAML tweak; verify GitHub
Actions' negated-glob semantics before merging.
.cursor/rules/api-routes.mdc:
- new ## CORS section near the existing ## Dev/test endpoints
(removed) section. Documents the no-CORS-by-default convention,
the brief-4 + cors-tighten lineage, the new forbidden-cors-headers
CI gate, and three forward-conventions (no setHeader for CORS,
no OPTIONS preflight handlers, design a proper middleware layer
if a future cross-origin caller is needed - not wildcards in
individual handlers).
AGENTS.md intentionally untouched. Gotcha #5 (the public
setup-database.js endpoint) is already RESOLVED by fix-auth-bypass
Brief 3 and unrelated to this convoy. The new convention belongs in
.cursor/rules/api-routes.mdc (where API conventions live) rather than
AGENTS.md; the convoy file + the new CI gate are sufficient
documentation for the audit trail. Per convoy spec, no new gotcha
entry needed.
No changes to: package.json, package-lock.json, pages/api/**, lib/**,
components/**, scripts/**, test/**, tests/**, .github/workflows/**,
README.md, TESTING_GUIDE.md, playwright.config.js.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 21:49:30 -04:00
| Item | Status | Convoy |
| --- | --- | --- |
| P0 #1 — `getUserFromRequest` hardcoded admin | **RESOLVED** 2026-05-23 | `fix-auth-bypass` Brief 2 (`258e479`) |
| P0 #2 — `JWT_SECRET` hardcoded fallback | **RESOLVED** 2026-05-23 | `fix-auth-bypass` Brief 1 (`4a10dce`) |
| P0 #3 — Default admin credentials in seed | **RESOLVED** 2026-05-23 | `drop-public-setup` (`ff80753` + `b63b509` ) |
| P0 #4 — Dev-only test endpoints | **RESOLVED** 2026-05-23 | `fix-auth-bypass` Brief 3 (`fc0dd73`) |
| P0 #5 — Wildcard CORS on API surface | **RESOLVED** 2026-05-24 | `fix-auth-bypass` Brief 4 (`297afca`) + `cors-tighten` (`da50d78`) |
docs: post-convoy cleanup for add-rate-limiting — MILESTONE, last P0 closed
Reflects the merged add-rate-limiting convoy (PR #20, squash commit
708ef45) in repo documentation. **This is the milestone cleanup** —
add-rate-limiting closed P0 #6 (No rate limiting anywhere), the LAST
open P0 ship-blocker. `.convoys/ship-readiness.md`'s § Status summary
flips from "7 of 8 RESOLVED; 1 remains" to **"8 of 8 RESOLVED.
Launch-readiness P0 checklist is empty."** One brief in the convoy:
Brief 1 shipped as planned with no scope expansions and no implementer
deviations from the verbatim spec; all six architect decisions ratified
verbatim at gate 1 (D1 operator-ratified Option A; D2-D6
architect-self-ratified).
.convoys/add-rate-limiting.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section. Opens with the milestone language
pointing back at ship-readiness.md's flipped § Status summary.
Decisions section captures all 6 ratifications (D1 operator-
ratified Option A — Critical: WHY the atomic admin UI fix in
pages/admin/card-import.js was Decision 1's hidden coupling
requirement, since API gating alone would have broken every
"Import Cards" click; D2 hybrid named-limiter shape with
Map<className, Ratelimit> cache; D3 per-class table including
the two D3 tuning-evidence raises — search 30 -> 60/min because
ShareModal.handleSearch has no debounce so a 17-char email = 16
requests in <5s, and generate kept at 5/hour because DiceBear is
free not paid AI; D4 two-extractor shape with defensive THROW
on null/empty userId; D5 uniform 429 message; D6 no new vitest
or playwright specs deferred to fill-vitest-handler-coverage).
As-shipped surface broken into 4 layers (1 lib refactor + 6 route
gates + 1 atomic admin UI fix + 1 rule extension) mirroring the
cors-tighten cleanup's pattern-split shape. Empirical CI metrics
from post-merge run 26382185019 (Playwright smoke 59s 3/3 in
3.8s, forbidden-cors-headers pass, vitest 21/21, lint 128 baseline,
Screenshot diff continue-on-error swallow per Decision 4).
Cross-validation finding: smoke test 2 still passes against the
post-rate-limit preview — that's three convoys in a row (PR #15
Layout default-user, PR #19 CORS-tighten, PR #20 rate-limiting)
where the same 3-test smoke spec defended the auth surface
through sweeping changes. Operator-action-required: none. What
did NOT change audit trail.
.convoys/ship-readiness.md:
- § Status summary at the top flipped from 7/8 to 8/8 RESOLVED.
Header text updated to "Launch-readiness P0 checklist is empty."
P0 #6 row in the table flips from PARTIAL to RESOLVED with the
two-convoy lineage (fix-auth-bypass Brief 4 + add-rate-limiting).
Trailing paragraph rewritten as a milestone note: security gate
closed; remaining launch work is P1 quality bar + P2/P3 polish.
- P0 #6 entry flipped from PARTIAL to RESOLVED with the full
add-rate-limiting as-shipped block (8 sub-bullets covering the
lib refactor shape, the per-class table, the defensive THROW,
the three import routes' auth-gating, the atomic admin UI fix
and WHY, the rule extension, the 6 decisions, and the diff
breakdown). Brief 4's 2026-05-23 partial is preserved as the
prior as-shipped layer to maintain the audit trail.
- Launch sequence step 4 marked RESOLVED 2026-05-24 with the
squash commit + smoke metrics inline.
- Queued convoys: removed the add-rate-limiting entry (it shipped).
Added a new delete-dead-lorcana-import entry (P3 polish; the
Lorcana import route was gated defensively in PR #20 despite
zero current frontend callers — pages/admin/card-import.js's
<select> only offers mtg + pokemon — so if Lorcana stays
permanently out of the admin UI, this is the cleanup PR).
Added three "flagged but kept out of scope" follow-ups per the
convoy's § What did NOT change: harden-multipart-parser (P2;
5MB body still consumed before the 429 path on avatar.js),
god-function-split / refactor-cards-search-sql (P2; 240-line
7-branch SQL in cards/search.js), and withAdmin(handler) wrapper
extraction (P3 DX; the three import routes are call sites #3-5
in the codebase but uniform inline shape was preserved for
convoy atomicity). Updated tighten-visual-diff-path-filter to
note PR #20 also tripped the same false-positive.
AGENTS.md:
- Gotcha #12 extended end-to-end. Was the single-class auth-only
lib + the env-var contract; is now the 5-class reality with a
full per-class table (helper / limit-window / key / routes),
the defensive THROW pattern in extractUserIdentifier, the
gate-ordering rule for per-user limiters, and the
auth → admin-role → rate-limit ordering for the three import
routes. Prominent milestone line opens the new content:
"add-rate-limiting convoy (squash 708ef45, PR #20, 2026-05-24)
closed P0 #6 — all 8 P0s now RESOLVED." Original env-var
contract paragraph (KV_REST_API_URL / KV_REST_API_TOKEN,
fail-closed-in-prod / warn-and-noop-in-dev) is preserved
verbatim above the new content.
- § 6 Testing: intentionally untouched (no test surface changed;
vitest 21/21 and smoke 3/3 still apply).
- § 7 Deployment: intentionally untouched (no deployment-shape
changed; same KV_REST_API_* env vars from Brief 4).
.cursor/rules/api-routes.mdc:
- The implementer extended § Rate limiting in PR #20 with the
per-class table + verbatim call shape + gate-ordering rules +
identifier-extraction + uniform 429 + fail-closed env-var
contract + fail-open Upstash-outage behavior. Doc-writer pass
verified completeness; added a one-sentence convoy-attribution
line at the top of § Rate limiting citing the two-convoy
lineage (fix-auth-bypass Brief 4 for the auth class +
add-rate-limiting for the other four classes and 7 newly-gated
routes), mirroring the post-cors-tighten § CORS attribution
shape. No other touch-ups needed.
No changes to: package.json, package-lock.json, lib/rate-limit.js,
pages/**, components/**, scripts/**, test/**, tests/**,
.github/workflows/**, README.md, TESTING_GUIDE.md, playwright.config.js,
eslint.config.mjs.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 00:09:07 -04:00
| P0 #6 — No rate limiting | **RESOLVED** 2026-05-24 | `fix-auth-bypass` Brief 4 (`297afca`, login + register) + `add-rate-limiting` (`708ef45`, the remaining surface) |
docs: post-convoy cleanup for cors-tighten
Reflects the merged cors-tighten convoy (PR #19, squash commit da50d78)
in repo documentation. Closes P0 #5 (Wildcard CORS on API surface) from
PARTIAL -> RESOLVED, leaving only P0 #6 (full add-rate-limiting) open
of the original P0 ship-blocker set. One brief in the convoy: Brief 1
shipped as planned with no scope expansions and no implementer deviations
from the verbatim spec.
.convoys/cors-tighten.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section: all 5 architect-self-ratifiable decisions
ratified verbatim (D1 Option B / D2 delete OPTIONS / D3 moot / D4
no new tests / D5 add CI lock); Pattern split (16 Pattern A + 8
Pattern B) per architect's 10-file audit + implementer's per-file
diff review; diff size (25 files, +29/-261); empirical CI metrics
from post-merge run 26378806555 (forbidden-cors-headers 4s PASS,
Playwright smoke 56s 3/3 in 3.3s, Screenshot diff continue-on-error
0 with the documented Decision-4 missing-baseline failure beneath);
cross-validation that Playwright smoke continues to pass post-CORS
removal (the auth + public surfaces don't depend on the wildcard
header); implementer subagent-retry footnote (HEAD already at
a843736 when retry woke up - transient retry, work is canonical);
operator-action-required-going-forward: none; What did NOT change
audit trail.
.convoys/ship-readiness.md:
- new ## Status summary at the top (right after the code-graph line):
P0 set is now 7/8 RESOLVED; only #6 (rate-limiting) remains. Table
lists each P0 with its resolving convoy + squash commit for a quick
scan of remaining work.
- P0 #5 marked RESOLVED 2026-05-24. Added the cors-tighten as-shipped
block (24 files swept, new CI job, 16/8 Pattern split, 5 decisions
ratified, diff stat, post-merge CI metrics, transient retry
footnote, operator-action: none). Brief 4's 2026-05-23 partial
is preserved as the prior as-shipped layer above the cors-tighten
layer to maintain the audit trail.
- Queued convoys: removed the cors-tighten entry (no longer queued).
Added a new tighten-visual-diff-path-filter entry (P3 polish) -
Screenshot diff workflow triggered on API-only PR #19 because its
paths: filter is pages/** which matches pages/api/** too. ~55s of
CI waste per API-only PR; one-line YAML tweak; verify GitHub
Actions' negated-glob semantics before merging.
.cursor/rules/api-routes.mdc:
- new ## CORS section near the existing ## Dev/test endpoints
(removed) section. Documents the no-CORS-by-default convention,
the brief-4 + cors-tighten lineage, the new forbidden-cors-headers
CI gate, and three forward-conventions (no setHeader for CORS,
no OPTIONS preflight handlers, design a proper middleware layer
if a future cross-origin caller is needed - not wildcards in
individual handlers).
AGENTS.md intentionally untouched. Gotcha #5 (the public
setup-database.js endpoint) is already RESOLVED by fix-auth-bypass
Brief 3 and unrelated to this convoy. The new convention belongs in
.cursor/rules/api-routes.mdc (where API conventions live) rather than
AGENTS.md; the convoy file + the new CI gate are sufficient
documentation for the audit trail. Per convoy spec, no new gotcha
entry needed.
No changes to: package.json, package-lock.json, pages/api/**, lib/**,
components/**, scripts/**, test/**, tests/**, .github/workflows/**,
README.md, TESTING_GUIDE.md, playwright.config.js.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 21:49:30 -04:00
| P0 #7 — Layout default-prop leaks email | **RESOLVED** 2026-05-24 | `fix-layout-default-user` (`ca302a8`) |
| P0 #8 — Next.js 15.4.3 vulnerable version | **RESOLVED** 2026-05-23 | `bump-next-js` (`e57ea17`) |
docs: post-convoy cleanup for add-rate-limiting — MILESTONE, last P0 closed
Reflects the merged add-rate-limiting convoy (PR #20, squash commit
708ef45) in repo documentation. **This is the milestone cleanup** —
add-rate-limiting closed P0 #6 (No rate limiting anywhere), the LAST
open P0 ship-blocker. `.convoys/ship-readiness.md`'s § Status summary
flips from "7 of 8 RESOLVED; 1 remains" to **"8 of 8 RESOLVED.
Launch-readiness P0 checklist is empty."** One brief in the convoy:
Brief 1 shipped as planned with no scope expansions and no implementer
deviations from the verbatim spec; all six architect decisions ratified
verbatim at gate 1 (D1 operator-ratified Option A; D2-D6
architect-self-ratified).
.convoys/add-rate-limiting.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section. Opens with the milestone language
pointing back at ship-readiness.md's flipped § Status summary.
Decisions section captures all 6 ratifications (D1 operator-
ratified Option A — Critical: WHY the atomic admin UI fix in
pages/admin/card-import.js was Decision 1's hidden coupling
requirement, since API gating alone would have broken every
"Import Cards" click; D2 hybrid named-limiter shape with
Map<className, Ratelimit> cache; D3 per-class table including
the two D3 tuning-evidence raises — search 30 -> 60/min because
ShareModal.handleSearch has no debounce so a 17-char email = 16
requests in <5s, and generate kept at 5/hour because DiceBear is
free not paid AI; D4 two-extractor shape with defensive THROW
on null/empty userId; D5 uniform 429 message; D6 no new vitest
or playwright specs deferred to fill-vitest-handler-coverage).
As-shipped surface broken into 4 layers (1 lib refactor + 6 route
gates + 1 atomic admin UI fix + 1 rule extension) mirroring the
cors-tighten cleanup's pattern-split shape. Empirical CI metrics
from post-merge run 26382185019 (Playwright smoke 59s 3/3 in
3.8s, forbidden-cors-headers pass, vitest 21/21, lint 128 baseline,
Screenshot diff continue-on-error swallow per Decision 4).
Cross-validation finding: smoke test 2 still passes against the
post-rate-limit preview — that's three convoys in a row (PR #15
Layout default-user, PR #19 CORS-tighten, PR #20 rate-limiting)
where the same 3-test smoke spec defended the auth surface
through sweeping changes. Operator-action-required: none. What
did NOT change audit trail.
.convoys/ship-readiness.md:
- § Status summary at the top flipped from 7/8 to 8/8 RESOLVED.
Header text updated to "Launch-readiness P0 checklist is empty."
P0 #6 row in the table flips from PARTIAL to RESOLVED with the
two-convoy lineage (fix-auth-bypass Brief 4 + add-rate-limiting).
Trailing paragraph rewritten as a milestone note: security gate
closed; remaining launch work is P1 quality bar + P2/P3 polish.
- P0 #6 entry flipped from PARTIAL to RESOLVED with the full
add-rate-limiting as-shipped block (8 sub-bullets covering the
lib refactor shape, the per-class table, the defensive THROW,
the three import routes' auth-gating, the atomic admin UI fix
and WHY, the rule extension, the 6 decisions, and the diff
breakdown). Brief 4's 2026-05-23 partial is preserved as the
prior as-shipped layer to maintain the audit trail.
- Launch sequence step 4 marked RESOLVED 2026-05-24 with the
squash commit + smoke metrics inline.
- Queued convoys: removed the add-rate-limiting entry (it shipped).
Added a new delete-dead-lorcana-import entry (P3 polish; the
Lorcana import route was gated defensively in PR #20 despite
zero current frontend callers — pages/admin/card-import.js's
<select> only offers mtg + pokemon — so if Lorcana stays
permanently out of the admin UI, this is the cleanup PR).
Added three "flagged but kept out of scope" follow-ups per the
convoy's § What did NOT change: harden-multipart-parser (P2;
5MB body still consumed before the 429 path on avatar.js),
god-function-split / refactor-cards-search-sql (P2; 240-line
7-branch SQL in cards/search.js), and withAdmin(handler) wrapper
extraction (P3 DX; the three import routes are call sites #3-5
in the codebase but uniform inline shape was preserved for
convoy atomicity). Updated tighten-visual-diff-path-filter to
note PR #20 also tripped the same false-positive.
AGENTS.md:
- Gotcha #12 extended end-to-end. Was the single-class auth-only
lib + the env-var contract; is now the 5-class reality with a
full per-class table (helper / limit-window / key / routes),
the defensive THROW pattern in extractUserIdentifier, the
gate-ordering rule for per-user limiters, and the
auth → admin-role → rate-limit ordering for the three import
routes. Prominent milestone line opens the new content:
"add-rate-limiting convoy (squash 708ef45, PR #20, 2026-05-24)
closed P0 #6 — all 8 P0s now RESOLVED." Original env-var
contract paragraph (KV_REST_API_URL / KV_REST_API_TOKEN,
fail-closed-in-prod / warn-and-noop-in-dev) is preserved
verbatim above the new content.
- § 6 Testing: intentionally untouched (no test surface changed;
vitest 21/21 and smoke 3/3 still apply).
- § 7 Deployment: intentionally untouched (no deployment-shape
changed; same KV_REST_API_* env vars from Brief 4).
.cursor/rules/api-routes.mdc:
- The implementer extended § Rate limiting in PR #20 with the
per-class table + verbatim call shape + gate-ordering rules +
identifier-extraction + uniform 429 + fail-closed env-var
contract + fail-open Upstash-outage behavior. Doc-writer pass
verified completeness; added a one-sentence convoy-attribution
line at the top of § Rate limiting citing the two-convoy
lineage (fix-auth-bypass Brief 4 for the auth class +
add-rate-limiting for the other four classes and 7 newly-gated
routes), mirroring the post-cors-tighten § CORS attribution
shape. No other touch-ups needed.
No changes to: package.json, package-lock.json, lib/rate-limit.js,
pages/**, components/**, scripts/**, test/**, tests/**,
.github/workflows/**, README.md, TESTING_GUIDE.md, playwright.config.js,
eslint.config.mjs.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 00:09:07 -04:00
**Milestone reached 2026-05-24:** `add-rate-limiting` (PR #20 , squash
commit `708ef45` ) closed P0 #6 — the last open P0 — flipping the
ship-blocker set from 7/8 to **8/8 RESOLVED** . The security gate is
closed; remaining launch work is P1 quality bar (single SQL client,
single auth provider, lint baseline cleanup, brand decision, test
coverage expansion) plus the P2 / P3 polish lanes in this file's
Queued convoys section. None of those are P0 ship-blockers.
docs: post-convoy cleanup for cors-tighten
Reflects the merged cors-tighten convoy (PR #19, squash commit da50d78)
in repo documentation. Closes P0 #5 (Wildcard CORS on API surface) from
PARTIAL -> RESOLVED, leaving only P0 #6 (full add-rate-limiting) open
of the original P0 ship-blocker set. One brief in the convoy: Brief 1
shipped as planned with no scope expansions and no implementer deviations
from the verbatim spec.
.convoys/cors-tighten.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section: all 5 architect-self-ratifiable decisions
ratified verbatim (D1 Option B / D2 delete OPTIONS / D3 moot / D4
no new tests / D5 add CI lock); Pattern split (16 Pattern A + 8
Pattern B) per architect's 10-file audit + implementer's per-file
diff review; diff size (25 files, +29/-261); empirical CI metrics
from post-merge run 26378806555 (forbidden-cors-headers 4s PASS,
Playwright smoke 56s 3/3 in 3.3s, Screenshot diff continue-on-error
0 with the documented Decision-4 missing-baseline failure beneath);
cross-validation that Playwright smoke continues to pass post-CORS
removal (the auth + public surfaces don't depend on the wildcard
header); implementer subagent-retry footnote (HEAD already at
a843736 when retry woke up - transient retry, work is canonical);
operator-action-required-going-forward: none; What did NOT change
audit trail.
.convoys/ship-readiness.md:
- new ## Status summary at the top (right after the code-graph line):
P0 set is now 7/8 RESOLVED; only #6 (rate-limiting) remains. Table
lists each P0 with its resolving convoy + squash commit for a quick
scan of remaining work.
- P0 #5 marked RESOLVED 2026-05-24. Added the cors-tighten as-shipped
block (24 files swept, new CI job, 16/8 Pattern split, 5 decisions
ratified, diff stat, post-merge CI metrics, transient retry
footnote, operator-action: none). Brief 4's 2026-05-23 partial
is preserved as the prior as-shipped layer above the cors-tighten
layer to maintain the audit trail.
- Queued convoys: removed the cors-tighten entry (no longer queued).
Added a new tighten-visual-diff-path-filter entry (P3 polish) -
Screenshot diff workflow triggered on API-only PR #19 because its
paths: filter is pages/** which matches pages/api/** too. ~55s of
CI waste per API-only PR; one-line YAML tweak; verify GitHub
Actions' negated-glob semantics before merging.
.cursor/rules/api-routes.mdc:
- new ## CORS section near the existing ## Dev/test endpoints
(removed) section. Documents the no-CORS-by-default convention,
the brief-4 + cors-tighten lineage, the new forbidden-cors-headers
CI gate, and three forward-conventions (no setHeader for CORS,
no OPTIONS preflight handlers, design a proper middleware layer
if a future cross-origin caller is needed - not wildcards in
individual handlers).
AGENTS.md intentionally untouched. Gotcha #5 (the public
setup-database.js endpoint) is already RESOLVED by fix-auth-bypass
Brief 3 and unrelated to this convoy. The new convention belongs in
.cursor/rules/api-routes.mdc (where API conventions live) rather than
AGENTS.md; the convoy file + the new CI gate are sufficient
documentation for the audit trail. Per convoy spec, no new gotcha
entry needed.
No changes to: package.json, package-lock.json, pages/api/**, lib/**,
components/**, scripts/**, test/**, tests/**, .github/workflows/**,
README.md, TESTING_GUIDE.md, playwright.config.js.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 21:49:30 -04:00
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
## P0 — ship-blockers (security)
These MUST land before any anonymous traffic touches the production URL.
docs: post-convoy cleanup for fix-auth-bypass
Closes out the fix-auth-bypass convoy (PRs #6–#11, merged through
1629afb) on the docs side. Code already on main; this PR is docs only.
Updates:
AGENTS.md
- §1 auth bullet refreshed (auth-secret SoT, 24h TTL, no synthetic
admin, login/register rate limit)
- §3 conventions point at lib/auth-secret.js + lib/rate-limit.js
- §4 gotchas #2/#3/#5 converted to "Resolved" notes in place
(NOT renumbered, to preserve cross-references)
- new #12 documents the KV_REST_API_* env-var convention
- §5 setup list adds the rate-limit env vars
- §6 testing rewritten for Vitest (16 unit tests, blocking CI gate)
.cursor/rules/auth-and-permissions.mdc
- canonical-surface table gains lib/auth-secret.js + lib/rate-limit.js
- token model now 24h (was 7d) with fail-loud explanation
- server-side authorization patterns lead with null → 401 contract
.cursor/rules/api-routes.mdc
- removes the "CRITICAL — known bug" callout (resolved by Brief 2)
- adds a "Rate limiting" section with verbatim shape + env-var notes
- "Dev/test endpoints" → "Removed" historical note so future agents
searching for test-db understand why it's gone
.convoys/fix-auth-bypass.md (restored — was on convoy branch only)
- frontmatter → status: shipped
- new "Convoy outcome" section: briefs + commits + resolved gotchas,
R1-R12 risk walk, env-var-rename deviation record, queued follow-up
convoys, lessons learned
.convoys/fix-auth-bypass/brief-{1..5}-*.md (restored from convoy branch)
- audit-trail completeness; convoy plan references them by name
- brief 4 additionally updated: UPSTASH_REDIS_REST_* → KV_REST_API_*
across init rules, smoke, pre-deploy checklist
- brief 4 has a new "Post-merge addendum" explaining the rename
.convoys/ship-readiness.md
- P0 #1, #2, #4 → RESOLVED with merge-commit citations
- P0 #5 (CORS), #6 (rate limit) → PARTIAL with deferral pointers
(cors-tighten and add-rate-limiting convoys)
- each item gains an "As-shipped" line for self-containment
README.md
- Next.js 15 → 16, TypeScript claim corrected to JS-with-devDep
- auth + rate-limit + testing bullets updated
- env-var template extended with KV_REST_API_*
- deleted dev-endpoints note added to the API list
- "Default Admin Account" section LEFT ALONE — drop-public-setup territory
Verified: build exit 0 (with JWT_SECRET set), 16/16 vitest tests pass,
lint baseline unchanged (128/81/47).
Convoy: fix-auth-bypass / role-doc-writer (closeout)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 12:27:48 -04:00
### 1. `getUserFromRequest` returns a hardcoded admin when no Bearer token is present — **RESOLVED 2026-05-23**
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
docs: post-convoy cleanup for fix-auth-bypass
Closes out the fix-auth-bypass convoy (PRs #6–#11, merged through
1629afb) on the docs side. Code already on main; this PR is docs only.
Updates:
AGENTS.md
- §1 auth bullet refreshed (auth-secret SoT, 24h TTL, no synthetic
admin, login/register rate limit)
- §3 conventions point at lib/auth-secret.js + lib/rate-limit.js
- §4 gotchas #2/#3/#5 converted to "Resolved" notes in place
(NOT renumbered, to preserve cross-references)
- new #12 documents the KV_REST_API_* env-var convention
- §5 setup list adds the rate-limit env vars
- §6 testing rewritten for Vitest (16 unit tests, blocking CI gate)
.cursor/rules/auth-and-permissions.mdc
- canonical-surface table gains lib/auth-secret.js + lib/rate-limit.js
- token model now 24h (was 7d) with fail-loud explanation
- server-side authorization patterns lead with null → 401 contract
.cursor/rules/api-routes.mdc
- removes the "CRITICAL — known bug" callout (resolved by Brief 2)
- adds a "Rate limiting" section with verbatim shape + env-var notes
- "Dev/test endpoints" → "Removed" historical note so future agents
searching for test-db understand why it's gone
.convoys/fix-auth-bypass.md (restored — was on convoy branch only)
- frontmatter → status: shipped
- new "Convoy outcome" section: briefs + commits + resolved gotchas,
R1-R12 risk walk, env-var-rename deviation record, queued follow-up
convoys, lessons learned
.convoys/fix-auth-bypass/brief-{1..5}-*.md (restored from convoy branch)
- audit-trail completeness; convoy plan references them by name
- brief 4 additionally updated: UPSTASH_REDIS_REST_* → KV_REST_API_*
across init rules, smoke, pre-deploy checklist
- brief 4 has a new "Post-merge addendum" explaining the rename
.convoys/ship-readiness.md
- P0 #1, #2, #4 → RESOLVED with merge-commit citations
- P0 #5 (CORS), #6 (rate limit) → PARTIAL with deferral pointers
(cors-tighten and add-rate-limiting convoys)
- each item gains an "As-shipped" line for self-containment
README.md
- Next.js 15 → 16, TypeScript claim corrected to JS-with-devDep
- auth + rate-limit + testing bullets updated
- env-var template extended with KV_REST_API_*
- deleted dev-endpoints note added to the API list
- "Default Admin Account" section LEFT ALONE — drop-public-setup territory
Verified: build exit 0 (with JWT_SECRET set), 16/16 vitest tests pass,
lint baseline unchanged (128/81/47).
Convoy: fix-auth-bypass / role-doc-writer (closeout)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 12:27:48 -04:00
- **Resolved by:** `fix-auth-bypass` Brief 2, commit `258e479` (PR #8 ). Follow-up Brief 6 hotfix `1fca3aa` added explicit 401 guards to the cards-collection POST/PUT/DELETE branches that previously masked the bug as 500s.
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
- **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://<host>/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).
docs: post-convoy cleanup for fix-auth-bypass
Closes out the fix-auth-bypass convoy (PRs #6–#11, merged through
1629afb) on the docs side. Code already on main; this PR is docs only.
Updates:
AGENTS.md
- §1 auth bullet refreshed (auth-secret SoT, 24h TTL, no synthetic
admin, login/register rate limit)
- §3 conventions point at lib/auth-secret.js + lib/rate-limit.js
- §4 gotchas #2/#3/#5 converted to "Resolved" notes in place
(NOT renumbered, to preserve cross-references)
- new #12 documents the KV_REST_API_* env-var convention
- §5 setup list adds the rate-limit env vars
- §6 testing rewritten for Vitest (16 unit tests, blocking CI gate)
.cursor/rules/auth-and-permissions.mdc
- canonical-surface table gains lib/auth-secret.js + lib/rate-limit.js
- token model now 24h (was 7d) with fail-loud explanation
- server-side authorization patterns lead with null → 401 contract
.cursor/rules/api-routes.mdc
- removes the "CRITICAL — known bug" callout (resolved by Brief 2)
- adds a "Rate limiting" section with verbatim shape + env-var notes
- "Dev/test endpoints" → "Removed" historical note so future agents
searching for test-db understand why it's gone
.convoys/fix-auth-bypass.md (restored — was on convoy branch only)
- frontmatter → status: shipped
- new "Convoy outcome" section: briefs + commits + resolved gotchas,
R1-R12 risk walk, env-var-rename deviation record, queued follow-up
convoys, lessons learned
.convoys/fix-auth-bypass/brief-{1..5}-*.md (restored from convoy branch)
- audit-trail completeness; convoy plan references them by name
- brief 4 additionally updated: UPSTASH_REDIS_REST_* → KV_REST_API_*
across init rules, smoke, pre-deploy checklist
- brief 4 has a new "Post-merge addendum" explaining the rename
.convoys/ship-readiness.md
- P0 #1, #2, #4 → RESOLVED with merge-commit citations
- P0 #5 (CORS), #6 (rate limit) → PARTIAL with deferral pointers
(cors-tighten and add-rate-limiting convoys)
- each item gains an "As-shipped" line for self-containment
README.md
- Next.js 15 → 16, TypeScript claim corrected to JS-with-devDep
- auth + rate-limit + testing bullets updated
- env-var template extended with KV_REST_API_*
- deleted dev-endpoints note added to the API list
- "Default Admin Account" section LEFT ALONE — drop-public-setup territory
Verified: build exit 0 (with JWT_SECRET set), 16/16 vitest tests pass,
lint baseline unchanged (128/81/47).
Convoy: fix-auth-bypass / role-doc-writer (closeout)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 12:27:48 -04:00
- **As-shipped:** The helper now returns `null` for any unauthenticated request. 16 unit tests in `test/lib/permission-middleware.test.js` lock in the contract (including a negative regression against the old synthetic-admin shape). `pages/api/auth/verify.js` returns 401 on the no-token branch instead of fetching the seed admin row.
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
- **Owns:** `role-architect` + `role-implementer` (one PR; small surface area in the helper, callers already check `!user` ).
docs: post-convoy cleanup for fix-auth-bypass
Closes out the fix-auth-bypass convoy (PRs #6–#11, merged through
1629afb) on the docs side. Code already on main; this PR is docs only.
Updates:
AGENTS.md
- §1 auth bullet refreshed (auth-secret SoT, 24h TTL, no synthetic
admin, login/register rate limit)
- §3 conventions point at lib/auth-secret.js + lib/rate-limit.js
- §4 gotchas #2/#3/#5 converted to "Resolved" notes in place
(NOT renumbered, to preserve cross-references)
- new #12 documents the KV_REST_API_* env-var convention
- §5 setup list adds the rate-limit env vars
- §6 testing rewritten for Vitest (16 unit tests, blocking CI gate)
.cursor/rules/auth-and-permissions.mdc
- canonical-surface table gains lib/auth-secret.js + lib/rate-limit.js
- token model now 24h (was 7d) with fail-loud explanation
- server-side authorization patterns lead with null → 401 contract
.cursor/rules/api-routes.mdc
- removes the "CRITICAL — known bug" callout (resolved by Brief 2)
- adds a "Rate limiting" section with verbatim shape + env-var notes
- "Dev/test endpoints" → "Removed" historical note so future agents
searching for test-db understand why it's gone
.convoys/fix-auth-bypass.md (restored — was on convoy branch only)
- frontmatter → status: shipped
- new "Convoy outcome" section: briefs + commits + resolved gotchas,
R1-R12 risk walk, env-var-rename deviation record, queued follow-up
convoys, lessons learned
.convoys/fix-auth-bypass/brief-{1..5}-*.md (restored from convoy branch)
- audit-trail completeness; convoy plan references them by name
- brief 4 additionally updated: UPSTASH_REDIS_REST_* → KV_REST_API_*
across init rules, smoke, pre-deploy checklist
- brief 4 has a new "Post-merge addendum" explaining the rename
.convoys/ship-readiness.md
- P0 #1, #2, #4 → RESOLVED with merge-commit citations
- P0 #5 (CORS), #6 (rate limit) → PARTIAL with deferral pointers
(cors-tighten and add-rate-limiting convoys)
- each item gains an "As-shipped" line for self-containment
README.md
- Next.js 15 → 16, TypeScript claim corrected to JS-with-devDep
- auth + rate-limit + testing bullets updated
- env-var template extended with KV_REST_API_*
- deleted dev-endpoints note added to the API list
- "Default Admin Account" section LEFT ALONE — drop-public-setup territory
Verified: build exit 0 (with JWT_SECRET set), 16/16 vitest tests pass,
lint baseline unchanged (128/81/47).
Convoy: fix-auth-bypass / role-doc-writer (closeout)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 12:27:48 -04:00
### 2. JWT_SECRET hardcoded fallback in 7 files — **RESOLVED 2026-05-23**
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
docs: post-convoy cleanup for fix-auth-bypass
Closes out the fix-auth-bypass convoy (PRs #6–#11, merged through
1629afb) on the docs side. Code already on main; this PR is docs only.
Updates:
AGENTS.md
- §1 auth bullet refreshed (auth-secret SoT, 24h TTL, no synthetic
admin, login/register rate limit)
- §3 conventions point at lib/auth-secret.js + lib/rate-limit.js
- §4 gotchas #2/#3/#5 converted to "Resolved" notes in place
(NOT renumbered, to preserve cross-references)
- new #12 documents the KV_REST_API_* env-var convention
- §5 setup list adds the rate-limit env vars
- §6 testing rewritten for Vitest (16 unit tests, blocking CI gate)
.cursor/rules/auth-and-permissions.mdc
- canonical-surface table gains lib/auth-secret.js + lib/rate-limit.js
- token model now 24h (was 7d) with fail-loud explanation
- server-side authorization patterns lead with null → 401 contract
.cursor/rules/api-routes.mdc
- removes the "CRITICAL — known bug" callout (resolved by Brief 2)
- adds a "Rate limiting" section with verbatim shape + env-var notes
- "Dev/test endpoints" → "Removed" historical note so future agents
searching for test-db understand why it's gone
.convoys/fix-auth-bypass.md (restored — was on convoy branch only)
- frontmatter → status: shipped
- new "Convoy outcome" section: briefs + commits + resolved gotchas,
R1-R12 risk walk, env-var-rename deviation record, queued follow-up
convoys, lessons learned
.convoys/fix-auth-bypass/brief-{1..5}-*.md (restored from convoy branch)
- audit-trail completeness; convoy plan references them by name
- brief 4 additionally updated: UPSTASH_REDIS_REST_* → KV_REST_API_*
across init rules, smoke, pre-deploy checklist
- brief 4 has a new "Post-merge addendum" explaining the rename
.convoys/ship-readiness.md
- P0 #1, #2, #4 → RESOLVED with merge-commit citations
- P0 #5 (CORS), #6 (rate limit) → PARTIAL with deferral pointers
(cors-tighten and add-rate-limiting convoys)
- each item gains an "As-shipped" line for self-containment
README.md
- Next.js 15 → 16, TypeScript claim corrected to JS-with-devDep
- auth + rate-limit + testing bullets updated
- env-var template extended with KV_REST_API_*
- deleted dev-endpoints note added to the API list
- "Default Admin Account" section LEFT ALONE — drop-public-setup territory
Verified: build exit 0 (with JWT_SECRET set), 16/16 vitest tests pass,
lint baseline unchanged (128/81/47).
Convoy: fix-auth-bypass / role-doc-writer (closeout)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 12:27:48 -04:00
- **Resolved by:** `fix-auth-bypass` Brief 1, commit `4a10dce` (PR #7 ).
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
- **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.
docs: post-convoy cleanup for fix-auth-bypass
Closes out the fix-auth-bypass convoy (PRs #6–#11, merged through
1629afb) on the docs side. Code already on main; this PR is docs only.
Updates:
AGENTS.md
- §1 auth bullet refreshed (auth-secret SoT, 24h TTL, no synthetic
admin, login/register rate limit)
- §3 conventions point at lib/auth-secret.js + lib/rate-limit.js
- §4 gotchas #2/#3/#5 converted to "Resolved" notes in place
(NOT renumbered, to preserve cross-references)
- new #12 documents the KV_REST_API_* env-var convention
- §5 setup list adds the rate-limit env vars
- §6 testing rewritten for Vitest (16 unit tests, blocking CI gate)
.cursor/rules/auth-and-permissions.mdc
- canonical-surface table gains lib/auth-secret.js + lib/rate-limit.js
- token model now 24h (was 7d) with fail-loud explanation
- server-side authorization patterns lead with null → 401 contract
.cursor/rules/api-routes.mdc
- removes the "CRITICAL — known bug" callout (resolved by Brief 2)
- adds a "Rate limiting" section with verbatim shape + env-var notes
- "Dev/test endpoints" → "Removed" historical note so future agents
searching for test-db understand why it's gone
.convoys/fix-auth-bypass.md (restored — was on convoy branch only)
- frontmatter → status: shipped
- new "Convoy outcome" section: briefs + commits + resolved gotchas,
R1-R12 risk walk, env-var-rename deviation record, queued follow-up
convoys, lessons learned
.convoys/fix-auth-bypass/brief-{1..5}-*.md (restored from convoy branch)
- audit-trail completeness; convoy plan references them by name
- brief 4 additionally updated: UPSTASH_REDIS_REST_* → KV_REST_API_*
across init rules, smoke, pre-deploy checklist
- brief 4 has a new "Post-merge addendum" explaining the rename
.convoys/ship-readiness.md
- P0 #1, #2, #4 → RESOLVED with merge-commit citations
- P0 #5 (CORS), #6 (rate limit) → PARTIAL with deferral pointers
(cors-tighten and add-rate-limiting convoys)
- each item gains an "As-shipped" line for self-containment
README.md
- Next.js 15 → 16, TypeScript claim corrected to JS-with-devDep
- auth + rate-limit + testing bullets updated
- env-var template extended with KV_REST_API_*
- deleted dev-endpoints note added to the API list
- "Default Admin Account" section LEFT ALONE — drop-public-setup territory
Verified: build exit 0 (with JWT_SECRET set), 16/16 vitest tests pass,
lint baseline unchanged (128/81/47).
Convoy: fix-auth-bypass / role-doc-writer (closeout)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 12:27:48 -04:00
- **As-shipped:** `lib/auth-secret.js` is the single source of truth and throws at module load if `JWT_SECRET` is unset. Canonical TTL is `JWT_TOKEN_TTL = '24h'` . All 7 literal fallback sites are converted to import-and-throw. `test/lib/auth-secret.test.js` (3 tests) covers the fail-loud path.
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
- **Owns:** `role-architect` + `role-implementer` .
docs: post-convoy cleanup for drop-public-setup
Reflects the merged drop-public-setup convoy (PR #13) in repo
documentation. Small focused pass; no behavior changes.
AGENTS.md:
- § 1 auth bullet: replace "seed admin row still ships in
setup-neon-db.js" claim with the new env-var-gated reality and
the R1 operator-rotation caveat.
- § 4 Gotcha #4: mark RESOLVED with commit refs (ff80753 + b63b509),
document the as-shipped behavior, the Brief 2 CJS→ESM Node 22.x
fix, and the R1 operator caveat. Entry kept (not renumbered) per
the same convention used for resolved gotchas #2, #3, #5.
- § 5 Running locally: add ADMIN_INITIAL_PASSWORD to the env-var
template list with a note that setup-db exits 1 if it's unset.
.convoys/ship-readiness.md:
- P0 #3: mark RESOLVED 2026-05-23 with commit refs, document the
full as-shipped behavior including Brief 2's CJS→ESM bonus,
the R1 operator caveat (Decision A — going-forward only),
and the deferred sibling weak-cred references queued for
purge-weak-creds-from-helpers.
.cursor/rules/no-go-zones.mdc:
- Editing rules of thumb: clarify the schema-vs-operational
distinction for scripts/setup-neon-db.js. drop-public-setup
set the precedent that operational changes (env-var gating,
pre-flight validation, module-system fixes) are allowed in
place, while DDL changes still need a separate migration
script. Documented so future agents don't have the same
confusion the drop-public-setup architect did (see Decision B
in .convoys/drop-public-setup.md).
No changes to: package.json, lib/**, pages/**, components/**,
scripts/**, .github/**, README.md (already updated in PR #13).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 18:04:35 -04:00
### 3. Default admin credentials in seed + README — **RESOLVED 2026-05-23**
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
docs: post-convoy cleanup for drop-public-setup
Reflects the merged drop-public-setup convoy (PR #13) in repo
documentation. Small focused pass; no behavior changes.
AGENTS.md:
- § 1 auth bullet: replace "seed admin row still ships in
setup-neon-db.js" claim with the new env-var-gated reality and
the R1 operator-rotation caveat.
- § 4 Gotcha #4: mark RESOLVED with commit refs (ff80753 + b63b509),
document the as-shipped behavior, the Brief 2 CJS→ESM Node 22.x
fix, and the R1 operator caveat. Entry kept (not renumbered) per
the same convention used for resolved gotchas #2, #3, #5.
- § 5 Running locally: add ADMIN_INITIAL_PASSWORD to the env-var
template list with a note that setup-db exits 1 if it's unset.
.convoys/ship-readiness.md:
- P0 #3: mark RESOLVED 2026-05-23 with commit refs, document the
full as-shipped behavior including Brief 2's CJS→ESM bonus,
the R1 operator caveat (Decision A — going-forward only),
and the deferred sibling weak-cred references queued for
purge-weak-creds-from-helpers.
.cursor/rules/no-go-zones.mdc:
- Editing rules of thumb: clarify the schema-vs-operational
distinction for scripts/setup-neon-db.js. drop-public-setup
set the precedent that operational changes (env-var gating,
pre-flight validation, module-system fixes) are allowed in
place, while DDL changes still need a separate migration
script. Documented so future agents don't have the same
confusion the drop-public-setup architect did (see Decision B
in .convoys/drop-public-setup.md).
No changes to: package.json, lib/**, pages/**, components/**,
scripts/**, .github/**, README.md (already updated in PR #13).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 18:04:35 -04:00
- **Resolved by:** `drop-public-setup` Brief 1 (commit `ff80753` ) + Brief 2 (commit `b63b509` ). PR #13 .
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
- **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".
docs: post-convoy cleanup for drop-public-setup
Reflects the merged drop-public-setup convoy (PR #13) in repo
documentation. Small focused pass; no behavior changes.
AGENTS.md:
- § 1 auth bullet: replace "seed admin row still ships in
setup-neon-db.js" claim with the new env-var-gated reality and
the R1 operator-rotation caveat.
- § 4 Gotcha #4: mark RESOLVED with commit refs (ff80753 + b63b509),
document the as-shipped behavior, the Brief 2 CJS→ESM Node 22.x
fix, and the R1 operator caveat. Entry kept (not renumbered) per
the same convention used for resolved gotchas #2, #3, #5.
- § 5 Running locally: add ADMIN_INITIAL_PASSWORD to the env-var
template list with a note that setup-db exits 1 if it's unset.
.convoys/ship-readiness.md:
- P0 #3: mark RESOLVED 2026-05-23 with commit refs, document the
full as-shipped behavior including Brief 2's CJS→ESM bonus,
the R1 operator caveat (Decision A — going-forward only),
and the deferred sibling weak-cred references queued for
purge-weak-creds-from-helpers.
.cursor/rules/no-go-zones.mdc:
- Editing rules of thumb: clarify the schema-vs-operational
distinction for scripts/setup-neon-db.js. drop-public-setup
set the precedent that operational changes (env-var gating,
pre-flight validation, module-system fixes) are allowed in
place, while DDL changes still need a separate migration
script. Documented so future agents don't have the same
confusion the drop-public-setup architect did (see Decision B
in .convoys/drop-public-setup.md).
No changes to: package.json, lib/**, pages/**, components/**,
scripts/**, .github/**, README.md (already updated in PR #13).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 18:04:35 -04:00
- **As-shipped:**
1. `pages/api/setup-database.js` already deleted by `fix-auth-bypass` Brief 3 (commit `fc0dd73` ); the `forbidden-endpoints` CI job blocks re-introduction.
2. `scripts/setup-neon-db.js` now reads `ADMIN_INITIAL_PASSWORD` from `process.env` ; if unset or empty, the script writes an actionable error (names the env var, points at `.env.local` , suggests `openssl rand -base64 24` , mentions CI-secret alternative, references README) and exits with code 1 **before** opening any DB connection. The bcrypt input is the env-var value, not the literal `admin123` . The two `console.log` lines that previously echoed `Admin User: admin@tcgvault.com` + `Admin Password: admin123` are deleted (R3 — stdout-leak prevention into CI logs); replaced with a single `Admin user ready (email: admin@tcgvault.com)` line that does NOT echo the password.
3. `README.md` 's "Default Admin Account" section replaced with "First-time admin setup" copy that documents the env-var requirement, the `openssl rand -base64 24` generation tip, the CI-secret alternative, and an operator-rotation note for envs that pre-date this convoy.
4. **Bonus (Decision D, Brief 2):** `scripts/setup-neon-db.js` converted from CommonJS to ESM so `npm run setup-db` actually executes on Node 22.x. The `bump-next-js` convoy added `"type": "module"` to `package.json` for ESLint v9 flat config; the seed script's `require()` calls were silently broken since that landed. Without Brief 2, Brief 1's env-var gate would have been theatrical (script throws `ReferenceError` before reaching the gate).
- **Operator caveat (R1, Decision A — going-forward only):** the seed is idempotent (`ON CONFLICT (email) DO NOTHING`); re-running `npm run setup-db` on an env that already has the admin row does NOT rotate the password. Any deployed env that ran setup before this convoy still has the weak `admin123` hash in its DB — operators must rotate manually via the app's profile settings, or wait for the queued `rotate-default-admin` follow-up convoy. Documented in `AGENTS.md` Gotcha #4 and the README's First-time admin setup blockquote.
- **Sibling weak-cred references deferred:** `scripts/reset-db.js` , `scripts/create-test-users.js` , and `TESTING_GUIDE.md` still hardcode `admin@tcgvault.com` / `admin123` — out of scope here per the no-go-zones rule (historical scripts) and the convoy spec. Queued for `purge-weak-creds-from-helpers` follow-up (or fold into `pick-a-name` since the email is also changing).
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
- **Owns:** `role-implementer` .
docs: post-convoy cleanup for fix-auth-bypass
Closes out the fix-auth-bypass convoy (PRs #6–#11, merged through
1629afb) on the docs side. Code already on main; this PR is docs only.
Updates:
AGENTS.md
- §1 auth bullet refreshed (auth-secret SoT, 24h TTL, no synthetic
admin, login/register rate limit)
- §3 conventions point at lib/auth-secret.js + lib/rate-limit.js
- §4 gotchas #2/#3/#5 converted to "Resolved" notes in place
(NOT renumbered, to preserve cross-references)
- new #12 documents the KV_REST_API_* env-var convention
- §5 setup list adds the rate-limit env vars
- §6 testing rewritten for Vitest (16 unit tests, blocking CI gate)
.cursor/rules/auth-and-permissions.mdc
- canonical-surface table gains lib/auth-secret.js + lib/rate-limit.js
- token model now 24h (was 7d) with fail-loud explanation
- server-side authorization patterns lead with null → 401 contract
.cursor/rules/api-routes.mdc
- removes the "CRITICAL — known bug" callout (resolved by Brief 2)
- adds a "Rate limiting" section with verbatim shape + env-var notes
- "Dev/test endpoints" → "Removed" historical note so future agents
searching for test-db understand why it's gone
.convoys/fix-auth-bypass.md (restored — was on convoy branch only)
- frontmatter → status: shipped
- new "Convoy outcome" section: briefs + commits + resolved gotchas,
R1-R12 risk walk, env-var-rename deviation record, queued follow-up
convoys, lessons learned
.convoys/fix-auth-bypass/brief-{1..5}-*.md (restored from convoy branch)
- audit-trail completeness; convoy plan references them by name
- brief 4 additionally updated: UPSTASH_REDIS_REST_* → KV_REST_API_*
across init rules, smoke, pre-deploy checklist
- brief 4 has a new "Post-merge addendum" explaining the rename
.convoys/ship-readiness.md
- P0 #1, #2, #4 → RESOLVED with merge-commit citations
- P0 #5 (CORS), #6 (rate limit) → PARTIAL with deferral pointers
(cors-tighten and add-rate-limiting convoys)
- each item gains an "As-shipped" line for self-containment
README.md
- Next.js 15 → 16, TypeScript claim corrected to JS-with-devDep
- auth + rate-limit + testing bullets updated
- env-var template extended with KV_REST_API_*
- deleted dev-endpoints note added to the API list
- "Default Admin Account" section LEFT ALONE — drop-public-setup territory
Verified: build exit 0 (with JWT_SECRET set), 16/16 vitest tests pass,
lint baseline unchanged (128/81/47).
Convoy: fix-auth-bypass / role-doc-writer (closeout)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 12:27:48 -04:00
### 4. Dev-only test endpoints shipped to production — **RESOLVED 2026-05-23**
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
docs: post-convoy cleanup for fix-auth-bypass
Closes out the fix-auth-bypass convoy (PRs #6–#11, merged through
1629afb) on the docs side. Code already on main; this PR is docs only.
Updates:
AGENTS.md
- §1 auth bullet refreshed (auth-secret SoT, 24h TTL, no synthetic
admin, login/register rate limit)
- §3 conventions point at lib/auth-secret.js + lib/rate-limit.js
- §4 gotchas #2/#3/#5 converted to "Resolved" notes in place
(NOT renumbered, to preserve cross-references)
- new #12 documents the KV_REST_API_* env-var convention
- §5 setup list adds the rate-limit env vars
- §6 testing rewritten for Vitest (16 unit tests, blocking CI gate)
.cursor/rules/auth-and-permissions.mdc
- canonical-surface table gains lib/auth-secret.js + lib/rate-limit.js
- token model now 24h (was 7d) with fail-loud explanation
- server-side authorization patterns lead with null → 401 contract
.cursor/rules/api-routes.mdc
- removes the "CRITICAL — known bug" callout (resolved by Brief 2)
- adds a "Rate limiting" section with verbatim shape + env-var notes
- "Dev/test endpoints" → "Removed" historical note so future agents
searching for test-db understand why it's gone
.convoys/fix-auth-bypass.md (restored — was on convoy branch only)
- frontmatter → status: shipped
- new "Convoy outcome" section: briefs + commits + resolved gotchas,
R1-R12 risk walk, env-var-rename deviation record, queued follow-up
convoys, lessons learned
.convoys/fix-auth-bypass/brief-{1..5}-*.md (restored from convoy branch)
- audit-trail completeness; convoy plan references them by name
- brief 4 additionally updated: UPSTASH_REDIS_REST_* → KV_REST_API_*
across init rules, smoke, pre-deploy checklist
- brief 4 has a new "Post-merge addendum" explaining the rename
.convoys/ship-readiness.md
- P0 #1, #2, #4 → RESOLVED with merge-commit citations
- P0 #5 (CORS), #6 (rate limit) → PARTIAL with deferral pointers
(cors-tighten and add-rate-limiting convoys)
- each item gains an "As-shipped" line for self-containment
README.md
- Next.js 15 → 16, TypeScript claim corrected to JS-with-devDep
- auth + rate-limit + testing bullets updated
- env-var template extended with KV_REST_API_*
- deleted dev-endpoints note added to the API list
- "Default Admin Account" section LEFT ALONE — drop-public-setup territory
Verified: build exit 0 (with JWT_SECRET set), 16/16 vitest tests pass,
lint baseline unchanged (128/81/47).
Convoy: fix-auth-bypass / role-doc-writer (closeout)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 12:27:48 -04:00
- **Resolved by:** `fix-auth-bypass` Brief 3, commit `fc0dd73` (PR #6 ).
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
- **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.
docs: post-convoy cleanup for fix-auth-bypass
Closes out the fix-auth-bypass convoy (PRs #6–#11, merged through
1629afb) on the docs side. Code already on main; this PR is docs only.
Updates:
AGENTS.md
- §1 auth bullet refreshed (auth-secret SoT, 24h TTL, no synthetic
admin, login/register rate limit)
- §3 conventions point at lib/auth-secret.js + lib/rate-limit.js
- §4 gotchas #2/#3/#5 converted to "Resolved" notes in place
(NOT renumbered, to preserve cross-references)
- new #12 documents the KV_REST_API_* env-var convention
- §5 setup list adds the rate-limit env vars
- §6 testing rewritten for Vitest (16 unit tests, blocking CI gate)
.cursor/rules/auth-and-permissions.mdc
- canonical-surface table gains lib/auth-secret.js + lib/rate-limit.js
- token model now 24h (was 7d) with fail-loud explanation
- server-side authorization patterns lead with null → 401 contract
.cursor/rules/api-routes.mdc
- removes the "CRITICAL — known bug" callout (resolved by Brief 2)
- adds a "Rate limiting" section with verbatim shape + env-var notes
- "Dev/test endpoints" → "Removed" historical note so future agents
searching for test-db understand why it's gone
.convoys/fix-auth-bypass.md (restored — was on convoy branch only)
- frontmatter → status: shipped
- new "Convoy outcome" section: briefs + commits + resolved gotchas,
R1-R12 risk walk, env-var-rename deviation record, queued follow-up
convoys, lessons learned
.convoys/fix-auth-bypass/brief-{1..5}-*.md (restored from convoy branch)
- audit-trail completeness; convoy plan references them by name
- brief 4 additionally updated: UPSTASH_REDIS_REST_* → KV_REST_API_*
across init rules, smoke, pre-deploy checklist
- brief 4 has a new "Post-merge addendum" explaining the rename
.convoys/ship-readiness.md
- P0 #1, #2, #4 → RESOLVED with merge-commit citations
- P0 #5 (CORS), #6 (rate limit) → PARTIAL with deferral pointers
(cors-tighten and add-rate-limiting convoys)
- each item gains an "As-shipped" line for self-containment
README.md
- Next.js 15 → 16, TypeScript claim corrected to JS-with-devDep
- auth + rate-limit + testing bullets updated
- env-var template extended with KV_REST_API_*
- deleted dev-endpoints note added to the API list
- "Default Admin Account" section LEFT ALONE — drop-public-setup territory
Verified: build exit 0 (with JWT_SECRET set), 16/16 vitest tests pass,
lint baseline unchanged (128/81/47).
Convoy: fix-auth-bypass / role-doc-writer (closeout)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-23 12:27:48 -04:00
- **As-shipped:** All four files deleted. `.github/workflows/ci.yml` has a new `forbidden-endpoints` job (blocking) that fails the build if any of the four paths reappear OR if a new `pages/api/test-*.js` file is added. Local simulation in the implementer PR confirmed clean → OK, with `test-fake.js` → FAIL, post-cleanup → OK.
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
- **Owns:** `role-implementer` .
docs: post-convoy cleanup for cors-tighten
Reflects the merged cors-tighten convoy (PR #19, squash commit da50d78)
in repo documentation. Closes P0 #5 (Wildcard CORS on API surface) from
PARTIAL -> RESOLVED, leaving only P0 #6 (full add-rate-limiting) open
of the original P0 ship-blocker set. One brief in the convoy: Brief 1
shipped as planned with no scope expansions and no implementer deviations
from the verbatim spec.
.convoys/cors-tighten.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section: all 5 architect-self-ratifiable decisions
ratified verbatim (D1 Option B / D2 delete OPTIONS / D3 moot / D4
no new tests / D5 add CI lock); Pattern split (16 Pattern A + 8
Pattern B) per architect's 10-file audit + implementer's per-file
diff review; diff size (25 files, +29/-261); empirical CI metrics
from post-merge run 26378806555 (forbidden-cors-headers 4s PASS,
Playwright smoke 56s 3/3 in 3.3s, Screenshot diff continue-on-error
0 with the documented Decision-4 missing-baseline failure beneath);
cross-validation that Playwright smoke continues to pass post-CORS
removal (the auth + public surfaces don't depend on the wildcard
header); implementer subagent-retry footnote (HEAD already at
a843736 when retry woke up - transient retry, work is canonical);
operator-action-required-going-forward: none; What did NOT change
audit trail.
.convoys/ship-readiness.md:
- new ## Status summary at the top (right after the code-graph line):
P0 set is now 7/8 RESOLVED; only #6 (rate-limiting) remains. Table
lists each P0 with its resolving convoy + squash commit for a quick
scan of remaining work.
- P0 #5 marked RESOLVED 2026-05-24. Added the cors-tighten as-shipped
block (24 files swept, new CI job, 16/8 Pattern split, 5 decisions
ratified, diff stat, post-merge CI metrics, transient retry
footnote, operator-action: none). Brief 4's 2026-05-23 partial
is preserved as the prior as-shipped layer above the cors-tighten
layer to maintain the audit trail.
- Queued convoys: removed the cors-tighten entry (no longer queued).
Added a new tighten-visual-diff-path-filter entry (P3 polish) -
Screenshot diff workflow triggered on API-only PR #19 because its
paths: filter is pages/** which matches pages/api/** too. ~55s of
CI waste per API-only PR; one-line YAML tweak; verify GitHub
Actions' negated-glob semantics before merging.
.cursor/rules/api-routes.mdc:
- new ## CORS section near the existing ## Dev/test endpoints
(removed) section. Documents the no-CORS-by-default convention,
the brief-4 + cors-tighten lineage, the new forbidden-cors-headers
CI gate, and three forward-conventions (no setHeader for CORS,
no OPTIONS preflight handlers, design a proper middleware layer
if a future cross-origin caller is needed - not wildcards in
individual handlers).
AGENTS.md intentionally untouched. Gotcha #5 (the public
setup-database.js endpoint) is already RESOLVED by fix-auth-bypass
Brief 3 and unrelated to this convoy. The new convention belongs in
.cursor/rules/api-routes.mdc (where API conventions live) rather than
AGENTS.md; the convoy file + the new CI gate are sufficient
documentation for the audit trail. Per convoy spec, no new gotcha
entry needed.
No changes to: package.json, package-lock.json, pages/api/**, lib/**,
components/**, scripts/**, test/**, tests/**, .github/workflows/**,
README.md, TESTING_GUIDE.md, playwright.config.js.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 21:49:30 -04:00
### 5. CORS `Access-Control-Allow-Origin: *` on auth endpoints — **RESOLVED 2026-05-24**
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
docs: post-convoy cleanup for cors-tighten
Reflects the merged cors-tighten convoy (PR #19, squash commit da50d78)
in repo documentation. Closes P0 #5 (Wildcard CORS on API surface) from
PARTIAL -> RESOLVED, leaving only P0 #6 (full add-rate-limiting) open
of the original P0 ship-blocker set. One brief in the convoy: Brief 1
shipped as planned with no scope expansions and no implementer deviations
from the verbatim spec.
.convoys/cors-tighten.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section: all 5 architect-self-ratifiable decisions
ratified verbatim (D1 Option B / D2 delete OPTIONS / D3 moot / D4
no new tests / D5 add CI lock); Pattern split (16 Pattern A + 8
Pattern B) per architect's 10-file audit + implementer's per-file
diff review; diff size (25 files, +29/-261); empirical CI metrics
from post-merge run 26378806555 (forbidden-cors-headers 4s PASS,
Playwright smoke 56s 3/3 in 3.3s, Screenshot diff continue-on-error
0 with the documented Decision-4 missing-baseline failure beneath);
cross-validation that Playwright smoke continues to pass post-CORS
removal (the auth + public surfaces don't depend on the wildcard
header); implementer subagent-retry footnote (HEAD already at
a843736 when retry woke up - transient retry, work is canonical);
operator-action-required-going-forward: none; What did NOT change
audit trail.
.convoys/ship-readiness.md:
- new ## Status summary at the top (right after the code-graph line):
P0 set is now 7/8 RESOLVED; only #6 (rate-limiting) remains. Table
lists each P0 with its resolving convoy + squash commit for a quick
scan of remaining work.
- P0 #5 marked RESOLVED 2026-05-24. Added the cors-tighten as-shipped
block (24 files swept, new CI job, 16/8 Pattern split, 5 decisions
ratified, diff stat, post-merge CI metrics, transient retry
footnote, operator-action: none). Brief 4's 2026-05-23 partial
is preserved as the prior as-shipped layer above the cors-tighten
layer to maintain the audit trail.
- Queued convoys: removed the cors-tighten entry (no longer queued).
Added a new tighten-visual-diff-path-filter entry (P3 polish) -
Screenshot diff workflow triggered on API-only PR #19 because its
paths: filter is pages/** which matches pages/api/** too. ~55s of
CI waste per API-only PR; one-line YAML tweak; verify GitHub
Actions' negated-glob semantics before merging.
.cursor/rules/api-routes.mdc:
- new ## CORS section near the existing ## Dev/test endpoints
(removed) section. Documents the no-CORS-by-default convention,
the brief-4 + cors-tighten lineage, the new forbidden-cors-headers
CI gate, and three forward-conventions (no setHeader for CORS,
no OPTIONS preflight handlers, design a proper middleware layer
if a future cross-origin caller is needed - not wildcards in
individual handlers).
AGENTS.md intentionally untouched. Gotcha #5 (the public
setup-database.js endpoint) is already RESOLVED by fix-auth-bypass
Brief 3 and unrelated to this convoy. The new convention belongs in
.cursor/rules/api-routes.mdc (where API conventions live) rather than
AGENTS.md; the convoy file + the new CI gate are sufficient
documentation for the audit trail. Per convoy spec, no new gotcha
entry needed.
No changes to: package.json, package-lock.json, pages/api/**, lib/**,
components/**, scripts/**, test/**, tests/**, .github/workflows/**,
README.md, TESTING_GUIDE.md, playwright.config.js.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 21:49:30 -04:00
- **Resolved by:** `fix-auth-bypass` Brief 4, commit `297afca` (PR #9 , login + register) + `cors-tighten` , squash commit `da50d78` (PR #19 , the remaining 24 handlers + CI regression-lock).
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
- **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).
docs: post-convoy cleanup for cors-tighten
Reflects the merged cors-tighten convoy (PR #19, squash commit da50d78)
in repo documentation. Closes P0 #5 (Wildcard CORS on API surface) from
PARTIAL -> RESOLVED, leaving only P0 #6 (full add-rate-limiting) open
of the original P0 ship-blocker set. One brief in the convoy: Brief 1
shipped as planned with no scope expansions and no implementer deviations
from the verbatim spec.
.convoys/cors-tighten.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section: all 5 architect-self-ratifiable decisions
ratified verbatim (D1 Option B / D2 delete OPTIONS / D3 moot / D4
no new tests / D5 add CI lock); Pattern split (16 Pattern A + 8
Pattern B) per architect's 10-file audit + implementer's per-file
diff review; diff size (25 files, +29/-261); empirical CI metrics
from post-merge run 26378806555 (forbidden-cors-headers 4s PASS,
Playwright smoke 56s 3/3 in 3.3s, Screenshot diff continue-on-error
0 with the documented Decision-4 missing-baseline failure beneath);
cross-validation that Playwright smoke continues to pass post-CORS
removal (the auth + public surfaces don't depend on the wildcard
header); implementer subagent-retry footnote (HEAD already at
a843736 when retry woke up - transient retry, work is canonical);
operator-action-required-going-forward: none; What did NOT change
audit trail.
.convoys/ship-readiness.md:
- new ## Status summary at the top (right after the code-graph line):
P0 set is now 7/8 RESOLVED; only #6 (rate-limiting) remains. Table
lists each P0 with its resolving convoy + squash commit for a quick
scan of remaining work.
- P0 #5 marked RESOLVED 2026-05-24. Added the cors-tighten as-shipped
block (24 files swept, new CI job, 16/8 Pattern split, 5 decisions
ratified, diff stat, post-merge CI metrics, transient retry
footnote, operator-action: none). Brief 4's 2026-05-23 partial
is preserved as the prior as-shipped layer above the cors-tighten
layer to maintain the audit trail.
- Queued convoys: removed the cors-tighten entry (no longer queued).
Added a new tighten-visual-diff-path-filter entry (P3 polish) -
Screenshot diff workflow triggered on API-only PR #19 because its
paths: filter is pages/** which matches pages/api/** too. ~55s of
CI waste per API-only PR; one-line YAML tweak; verify GitHub
Actions' negated-glob semantics before merging.
.cursor/rules/api-routes.mdc:
- new ## CORS section near the existing ## Dev/test endpoints
(removed) section. Documents the no-CORS-by-default convention,
the brief-4 + cors-tighten lineage, the new forbidden-cors-headers
CI gate, and three forward-conventions (no setHeader for CORS,
no OPTIONS preflight handlers, design a proper middleware layer
if a future cross-origin caller is needed - not wildcards in
individual handlers).
AGENTS.md intentionally untouched. Gotcha #5 (the public
setup-database.js endpoint) is already RESOLVED by fix-auth-bypass
Brief 3 and unrelated to this convoy. The new convention belongs in
.cursor/rules/api-routes.mdc (where API conventions live) rather than
AGENTS.md; the convoy file + the new CI gate are sufficient
documentation for the audit trail. Per convoy spec, no new gotcha
entry needed.
No changes to: package.json, package-lock.json, pages/api/**, lib/**,
components/**, scripts/**, test/**, tests/**, .github/workflows/**,
README.md, TESTING_GUIDE.md, playwright.config.js.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 21:49:30 -04:00
- **As-shipped (Brief 4, 2026-05-23):** `pages/api/auth/login.js` and `pages/api/auth/register.js` dropped the four `setHeader` calls + the OPTIONS preflight handler. `pages/api/setup-database.js` was deleted entirely by Brief 3.
- **As-shipped (`cors-tighten`, 2026-05-24, squash commit `da50d78` , PR #19 , architect-commit `ec22b70` , implementer-commit `a843736` ):**
1. **24 `pages/api/** ` handlers swept** — ` admin/index.js`, `auth/verify.js` , `cards/[id]/ownership.js` , `cards/owned.js` , `cards/search.js` , `collections.js` , `collections/[identifier].js` , `collections/[identifier]/activity.js` , `collections/[identifier]/cards.js` , `collections/[identifier]/permissions.js` , `collections/[identifier]/thumbnails.js` , `community/collections.js` , `favorites.js` , `invite/accept.js` , `invite/decline.js` , `public/collections.js` , `user/avatar.js` , `user/avatar/generate.js` , `user/delete.js` , `user/password.js` , `user/profile.js` , `user/settings.js` , `user/stats.js` , `users/search.js` . Each diff is a pure deletion of 9-11 lines (the leading `// Set CORS headers` comment + 3 `setHeader` calls + the leading `// Handle preflight requests` comment + the 4-line OPTIONS-if block + the trailing blank line). No additions per source file. **Pattern split: 16 Pattern A (top-level method gate after the CORS block) + 8 Pattern B (method-branched inside the `try` block).** Both shapes documented verbatim in `.convoys/cors-tighten/brief-1-sweep-wildcard-cors.md` .
2. **New blocking `forbidden-cors-headers` CI job** in `.github/workflows/ci.yml` , modeled verbatim on the existing `forbidden-endpoints` job (added by `fix-auth-bypass` Brief 3). Greps `pages/api/` for `Access-Control-Allow-(Origin|Methods|Headers)` , emits `::error file= line=::` annotations on hit, exits 1. No `continue-on-error` , no `|| true` wrapper. Sits between `forbidden-endpoints` and `test` in the YAML for logical grouping (both `forbidden-*` checks are static-source guards before the runtime test job). Runs in ~4 seconds; zero new dependencies.
3. **All five architect decisions self-ratified at gate 1** (no operator decisions needed) — D1 Option B (expanded sweep, all 24 files), D2 delete the OPTIONS preflight handler entirely (Option (a)), D3 `verify.js` `Allow-Methods` tightening moot (subsumed by D2), D4 no new per-route handler tests in this convoy (deferred to queued `fill-vitest-handler-coverage` ), D5 add the new CI regression-lock job.
4. **Diff: 25 files, +29 / -261** (pure deletion across 24 source files; 29 additions = the new CI job).
- **As-shipped metrics (post-merge run 26378806555 + subsequent runs):**
- `forbidden-cors-headers` (new) — PASS in **4s** . First live exercise of the regression-lock; greps clean against the post-sweep tree.
- `Playwright smoke` — PASS in **56s, 3/3 tests in 3.3s** against the post-CORS-removal Vercel preview. Cross-validates that CORS removal is safe for the auth surface (smoke's sign-in check still passes against `/login` ; `/api/health` still serves anonymously). Surfaced as a real CI signal even though Decision D4 deferred per-route handler tests — the existing smoke spec transitively defends the auth + public surfaces against this convoy's deletions.
- `Screenshot diff` — workflow exited 0 because of `continue-on-error: true` , but the actual visual test failed with the documented "snapshot doesn't exist" error (Decision-4 end state of `adopt-playwright-smoke` ). Triggered on PR #19 despite this being API-only because its `paths:` filter is `pages/**` which matches `pages/api/**` too — minor false-positive queued as `tighten-visual-diff-path-filter` (see § Queued convoys).
- All other gates (`Lint`, `Vitest` , `Schema map up to date` , `forbidden-endpoints` ) — green.
- **Implementer subagent-retry footnote (transient).** The implementer's PR report flagged that HEAD was already at the implementer commit (`a843736`) when its retry subagent woke up — a prior implementer run had completed the work, and the retry's "STOP per branch mismatch" rule kicked in; the retry then ran verification only (lint baseline, vitest 21/21, grep clean, YAML valid) and reported success. This is a transient subagent retry, not a process gap. The implementer commit `a843736` is canonical; the squash `da50d78` rolls up the architect plan + Brief 1 + the implementer's work without duplication.
- **Operator action required going forward:** **none.** No env vars to seed, no secrets to rotate, no infra changes. The `forbidden-cors-headers` job is self-contained (plain bash grep on the runner); future PRs that accidentally re-scaffold a wildcard CORS header will fail the build with a file-and-line pointer to the offending line.
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
- **Owns:** `role-implementer` .
docs: post-convoy cleanup for add-rate-limiting — MILESTONE, last P0 closed
Reflects the merged add-rate-limiting convoy (PR #20, squash commit
708ef45) in repo documentation. **This is the milestone cleanup** —
add-rate-limiting closed P0 #6 (No rate limiting anywhere), the LAST
open P0 ship-blocker. `.convoys/ship-readiness.md`'s § Status summary
flips from "7 of 8 RESOLVED; 1 remains" to **"8 of 8 RESOLVED.
Launch-readiness P0 checklist is empty."** One brief in the convoy:
Brief 1 shipped as planned with no scope expansions and no implementer
deviations from the verbatim spec; all six architect decisions ratified
verbatim at gate 1 (D1 operator-ratified Option A; D2-D6
architect-self-ratified).
.convoys/add-rate-limiting.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section. Opens with the milestone language
pointing back at ship-readiness.md's flipped § Status summary.
Decisions section captures all 6 ratifications (D1 operator-
ratified Option A — Critical: WHY the atomic admin UI fix in
pages/admin/card-import.js was Decision 1's hidden coupling
requirement, since API gating alone would have broken every
"Import Cards" click; D2 hybrid named-limiter shape with
Map<className, Ratelimit> cache; D3 per-class table including
the two D3 tuning-evidence raises — search 30 -> 60/min because
ShareModal.handleSearch has no debounce so a 17-char email = 16
requests in <5s, and generate kept at 5/hour because DiceBear is
free not paid AI; D4 two-extractor shape with defensive THROW
on null/empty userId; D5 uniform 429 message; D6 no new vitest
or playwright specs deferred to fill-vitest-handler-coverage).
As-shipped surface broken into 4 layers (1 lib refactor + 6 route
gates + 1 atomic admin UI fix + 1 rule extension) mirroring the
cors-tighten cleanup's pattern-split shape. Empirical CI metrics
from post-merge run 26382185019 (Playwright smoke 59s 3/3 in
3.8s, forbidden-cors-headers pass, vitest 21/21, lint 128 baseline,
Screenshot diff continue-on-error swallow per Decision 4).
Cross-validation finding: smoke test 2 still passes against the
post-rate-limit preview — that's three convoys in a row (PR #15
Layout default-user, PR #19 CORS-tighten, PR #20 rate-limiting)
where the same 3-test smoke spec defended the auth surface
through sweeping changes. Operator-action-required: none. What
did NOT change audit trail.
.convoys/ship-readiness.md:
- § Status summary at the top flipped from 7/8 to 8/8 RESOLVED.
Header text updated to "Launch-readiness P0 checklist is empty."
P0 #6 row in the table flips from PARTIAL to RESOLVED with the
two-convoy lineage (fix-auth-bypass Brief 4 + add-rate-limiting).
Trailing paragraph rewritten as a milestone note: security gate
closed; remaining launch work is P1 quality bar + P2/P3 polish.
- P0 #6 entry flipped from PARTIAL to RESOLVED with the full
add-rate-limiting as-shipped block (8 sub-bullets covering the
lib refactor shape, the per-class table, the defensive THROW,
the three import routes' auth-gating, the atomic admin UI fix
and WHY, the rule extension, the 6 decisions, and the diff
breakdown). Brief 4's 2026-05-23 partial is preserved as the
prior as-shipped layer to maintain the audit trail.
- Launch sequence step 4 marked RESOLVED 2026-05-24 with the
squash commit + smoke metrics inline.
- Queued convoys: removed the add-rate-limiting entry (it shipped).
Added a new delete-dead-lorcana-import entry (P3 polish; the
Lorcana import route was gated defensively in PR #20 despite
zero current frontend callers — pages/admin/card-import.js's
<select> only offers mtg + pokemon — so if Lorcana stays
permanently out of the admin UI, this is the cleanup PR).
Added three "flagged but kept out of scope" follow-ups per the
convoy's § What did NOT change: harden-multipart-parser (P2;
5MB body still consumed before the 429 path on avatar.js),
god-function-split / refactor-cards-search-sql (P2; 240-line
7-branch SQL in cards/search.js), and withAdmin(handler) wrapper
extraction (P3 DX; the three import routes are call sites #3-5
in the codebase but uniform inline shape was preserved for
convoy atomicity). Updated tighten-visual-diff-path-filter to
note PR #20 also tripped the same false-positive.
AGENTS.md:
- Gotcha #12 extended end-to-end. Was the single-class auth-only
lib + the env-var contract; is now the 5-class reality with a
full per-class table (helper / limit-window / key / routes),
the defensive THROW pattern in extractUserIdentifier, the
gate-ordering rule for per-user limiters, and the
auth → admin-role → rate-limit ordering for the three import
routes. Prominent milestone line opens the new content:
"add-rate-limiting convoy (squash 708ef45, PR #20, 2026-05-24)
closed P0 #6 — all 8 P0s now RESOLVED." Original env-var
contract paragraph (KV_REST_API_URL / KV_REST_API_TOKEN,
fail-closed-in-prod / warn-and-noop-in-dev) is preserved
verbatim above the new content.
- § 6 Testing: intentionally untouched (no test surface changed;
vitest 21/21 and smoke 3/3 still apply).
- § 7 Deployment: intentionally untouched (no deployment-shape
changed; same KV_REST_API_* env vars from Brief 4).
.cursor/rules/api-routes.mdc:
- The implementer extended § Rate limiting in PR #20 with the
per-class table + verbatim call shape + gate-ordering rules +
identifier-extraction + uniform 429 + fail-closed env-var
contract + fail-open Upstash-outage behavior. Doc-writer pass
verified completeness; added a one-sentence convoy-attribution
line at the top of § Rate limiting citing the two-convoy
lineage (fix-auth-bypass Brief 4 for the auth class +
add-rate-limiting for the other four classes and 7 newly-gated
routes), mirroring the post-cors-tighten § CORS attribution
shape. No other touch-ups needed.
No changes to: package.json, package-lock.json, lib/rate-limit.js,
pages/**, components/**, scripts/**, test/**, tests/**,
.github/workflows/**, README.md, TESTING_GUIDE.md, playwright.config.js,
eslint.config.mjs.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 00:09:07 -04:00
### 6. No rate limiting anywhere — **RESOLVED 2026-05-24** *(milestone — last P0 closed)*
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
docs: post-convoy cleanup for add-rate-limiting — MILESTONE, last P0 closed
Reflects the merged add-rate-limiting convoy (PR #20, squash commit
708ef45) in repo documentation. **This is the milestone cleanup** —
add-rate-limiting closed P0 #6 (No rate limiting anywhere), the LAST
open P0 ship-blocker. `.convoys/ship-readiness.md`'s § Status summary
flips from "7 of 8 RESOLVED; 1 remains" to **"8 of 8 RESOLVED.
Launch-readiness P0 checklist is empty."** One brief in the convoy:
Brief 1 shipped as planned with no scope expansions and no implementer
deviations from the verbatim spec; all six architect decisions ratified
verbatim at gate 1 (D1 operator-ratified Option A; D2-D6
architect-self-ratified).
.convoys/add-rate-limiting.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section. Opens with the milestone language
pointing back at ship-readiness.md's flipped § Status summary.
Decisions section captures all 6 ratifications (D1 operator-
ratified Option A — Critical: WHY the atomic admin UI fix in
pages/admin/card-import.js was Decision 1's hidden coupling
requirement, since API gating alone would have broken every
"Import Cards" click; D2 hybrid named-limiter shape with
Map<className, Ratelimit> cache; D3 per-class table including
the two D3 tuning-evidence raises — search 30 -> 60/min because
ShareModal.handleSearch has no debounce so a 17-char email = 16
requests in <5s, and generate kept at 5/hour because DiceBear is
free not paid AI; D4 two-extractor shape with defensive THROW
on null/empty userId; D5 uniform 429 message; D6 no new vitest
or playwright specs deferred to fill-vitest-handler-coverage).
As-shipped surface broken into 4 layers (1 lib refactor + 6 route
gates + 1 atomic admin UI fix + 1 rule extension) mirroring the
cors-tighten cleanup's pattern-split shape. Empirical CI metrics
from post-merge run 26382185019 (Playwright smoke 59s 3/3 in
3.8s, forbidden-cors-headers pass, vitest 21/21, lint 128 baseline,
Screenshot diff continue-on-error swallow per Decision 4).
Cross-validation finding: smoke test 2 still passes against the
post-rate-limit preview — that's three convoys in a row (PR #15
Layout default-user, PR #19 CORS-tighten, PR #20 rate-limiting)
where the same 3-test smoke spec defended the auth surface
through sweeping changes. Operator-action-required: none. What
did NOT change audit trail.
.convoys/ship-readiness.md:
- § Status summary at the top flipped from 7/8 to 8/8 RESOLVED.
Header text updated to "Launch-readiness P0 checklist is empty."
P0 #6 row in the table flips from PARTIAL to RESOLVED with the
two-convoy lineage (fix-auth-bypass Brief 4 + add-rate-limiting).
Trailing paragraph rewritten as a milestone note: security gate
closed; remaining launch work is P1 quality bar + P2/P3 polish.
- P0 #6 entry flipped from PARTIAL to RESOLVED with the full
add-rate-limiting as-shipped block (8 sub-bullets covering the
lib refactor shape, the per-class table, the defensive THROW,
the three import routes' auth-gating, the atomic admin UI fix
and WHY, the rule extension, the 6 decisions, and the diff
breakdown). Brief 4's 2026-05-23 partial is preserved as the
prior as-shipped layer to maintain the audit trail.
- Launch sequence step 4 marked RESOLVED 2026-05-24 with the
squash commit + smoke metrics inline.
- Queued convoys: removed the add-rate-limiting entry (it shipped).
Added a new delete-dead-lorcana-import entry (P3 polish; the
Lorcana import route was gated defensively in PR #20 despite
zero current frontend callers — pages/admin/card-import.js's
<select> only offers mtg + pokemon — so if Lorcana stays
permanently out of the admin UI, this is the cleanup PR).
Added three "flagged but kept out of scope" follow-ups per the
convoy's § What did NOT change: harden-multipart-parser (P2;
5MB body still consumed before the 429 path on avatar.js),
god-function-split / refactor-cards-search-sql (P2; 240-line
7-branch SQL in cards/search.js), and withAdmin(handler) wrapper
extraction (P3 DX; the three import routes are call sites #3-5
in the codebase but uniform inline shape was preserved for
convoy atomicity). Updated tighten-visual-diff-path-filter to
note PR #20 also tripped the same false-positive.
AGENTS.md:
- Gotcha #12 extended end-to-end. Was the single-class auth-only
lib + the env-var contract; is now the 5-class reality with a
full per-class table (helper / limit-window / key / routes),
the defensive THROW pattern in extractUserIdentifier, the
gate-ordering rule for per-user limiters, and the
auth → admin-role → rate-limit ordering for the three import
routes. Prominent milestone line opens the new content:
"add-rate-limiting convoy (squash 708ef45, PR #20, 2026-05-24)
closed P0 #6 — all 8 P0s now RESOLVED." Original env-var
contract paragraph (KV_REST_API_URL / KV_REST_API_TOKEN,
fail-closed-in-prod / warn-and-noop-in-dev) is preserved
verbatim above the new content.
- § 6 Testing: intentionally untouched (no test surface changed;
vitest 21/21 and smoke 3/3 still apply).
- § 7 Deployment: intentionally untouched (no deployment-shape
changed; same KV_REST_API_* env vars from Brief 4).
.cursor/rules/api-routes.mdc:
- The implementer extended § Rate limiting in PR #20 with the
per-class table + verbatim call shape + gate-ordering rules +
identifier-extraction + uniform 429 + fail-closed env-var
contract + fail-open Upstash-outage behavior. Doc-writer pass
verified completeness; added a one-sentence convoy-attribution
line at the top of § Rate limiting citing the two-convoy
lineage (fix-auth-bypass Brief 4 for the auth class +
add-rate-limiting for the other four classes and 7 newly-gated
routes), mirroring the post-cors-tighten § CORS attribution
shape. No other touch-ups needed.
No changes to: package.json, package-lock.json, lib/rate-limit.js,
pages/**, components/**, scripts/**, test/**, tests/**,
.github/workflows/**, README.md, TESTING_GUIDE.md, playwright.config.js,
eslint.config.mjs.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 00:09:07 -04:00
- **Resolved by:** `fix-auth-bypass` Brief 4, commit `297afca` (PR #9 , login + register only) + `add-rate-limiting` , squash commit `708ef45` (PR #20 , the remaining surface + 3-import-route gating + 1 atomic admin UI fix + 1 rule extension).
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
- **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).
docs: post-convoy cleanup for add-rate-limiting — MILESTONE, last P0 closed
Reflects the merged add-rate-limiting convoy (PR #20, squash commit
708ef45) in repo documentation. **This is the milestone cleanup** —
add-rate-limiting closed P0 #6 (No rate limiting anywhere), the LAST
open P0 ship-blocker. `.convoys/ship-readiness.md`'s § Status summary
flips from "7 of 8 RESOLVED; 1 remains" to **"8 of 8 RESOLVED.
Launch-readiness P0 checklist is empty."** One brief in the convoy:
Brief 1 shipped as planned with no scope expansions and no implementer
deviations from the verbatim spec; all six architect decisions ratified
verbatim at gate 1 (D1 operator-ratified Option A; D2-D6
architect-self-ratified).
.convoys/add-rate-limiting.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section. Opens with the milestone language
pointing back at ship-readiness.md's flipped § Status summary.
Decisions section captures all 6 ratifications (D1 operator-
ratified Option A — Critical: WHY the atomic admin UI fix in
pages/admin/card-import.js was Decision 1's hidden coupling
requirement, since API gating alone would have broken every
"Import Cards" click; D2 hybrid named-limiter shape with
Map<className, Ratelimit> cache; D3 per-class table including
the two D3 tuning-evidence raises — search 30 -> 60/min because
ShareModal.handleSearch has no debounce so a 17-char email = 16
requests in <5s, and generate kept at 5/hour because DiceBear is
free not paid AI; D4 two-extractor shape with defensive THROW
on null/empty userId; D5 uniform 429 message; D6 no new vitest
or playwright specs deferred to fill-vitest-handler-coverage).
As-shipped surface broken into 4 layers (1 lib refactor + 6 route
gates + 1 atomic admin UI fix + 1 rule extension) mirroring the
cors-tighten cleanup's pattern-split shape. Empirical CI metrics
from post-merge run 26382185019 (Playwright smoke 59s 3/3 in
3.8s, forbidden-cors-headers pass, vitest 21/21, lint 128 baseline,
Screenshot diff continue-on-error swallow per Decision 4).
Cross-validation finding: smoke test 2 still passes against the
post-rate-limit preview — that's three convoys in a row (PR #15
Layout default-user, PR #19 CORS-tighten, PR #20 rate-limiting)
where the same 3-test smoke spec defended the auth surface
through sweeping changes. Operator-action-required: none. What
did NOT change audit trail.
.convoys/ship-readiness.md:
- § Status summary at the top flipped from 7/8 to 8/8 RESOLVED.
Header text updated to "Launch-readiness P0 checklist is empty."
P0 #6 row in the table flips from PARTIAL to RESOLVED with the
two-convoy lineage (fix-auth-bypass Brief 4 + add-rate-limiting).
Trailing paragraph rewritten as a milestone note: security gate
closed; remaining launch work is P1 quality bar + P2/P3 polish.
- P0 #6 entry flipped from PARTIAL to RESOLVED with the full
add-rate-limiting as-shipped block (8 sub-bullets covering the
lib refactor shape, the per-class table, the defensive THROW,
the three import routes' auth-gating, the atomic admin UI fix
and WHY, the rule extension, the 6 decisions, and the diff
breakdown). Brief 4's 2026-05-23 partial is preserved as the
prior as-shipped layer to maintain the audit trail.
- Launch sequence step 4 marked RESOLVED 2026-05-24 with the
squash commit + smoke metrics inline.
- Queued convoys: removed the add-rate-limiting entry (it shipped).
Added a new delete-dead-lorcana-import entry (P3 polish; the
Lorcana import route was gated defensively in PR #20 despite
zero current frontend callers — pages/admin/card-import.js's
<select> only offers mtg + pokemon — so if Lorcana stays
permanently out of the admin UI, this is the cleanup PR).
Added three "flagged but kept out of scope" follow-ups per the
convoy's § What did NOT change: harden-multipart-parser (P2;
5MB body still consumed before the 429 path on avatar.js),
god-function-split / refactor-cards-search-sql (P2; 240-line
7-branch SQL in cards/search.js), and withAdmin(handler) wrapper
extraction (P3 DX; the three import routes are call sites #3-5
in the codebase but uniform inline shape was preserved for
convoy atomicity). Updated tighten-visual-diff-path-filter to
note PR #20 also tripped the same false-positive.
AGENTS.md:
- Gotcha #12 extended end-to-end. Was the single-class auth-only
lib + the env-var contract; is now the 5-class reality with a
full per-class table (helper / limit-window / key / routes),
the defensive THROW pattern in extractUserIdentifier, the
gate-ordering rule for per-user limiters, and the
auth → admin-role → rate-limit ordering for the three import
routes. Prominent milestone line opens the new content:
"add-rate-limiting convoy (squash 708ef45, PR #20, 2026-05-24)
closed P0 #6 — all 8 P0s now RESOLVED." Original env-var
contract paragraph (KV_REST_API_URL / KV_REST_API_TOKEN,
fail-closed-in-prod / warn-and-noop-in-dev) is preserved
verbatim above the new content.
- § 6 Testing: intentionally untouched (no test surface changed;
vitest 21/21 and smoke 3/3 still apply).
- § 7 Deployment: intentionally untouched (no deployment-shape
changed; same KV_REST_API_* env vars from Brief 4).
.cursor/rules/api-routes.mdc:
- The implementer extended § Rate limiting in PR #20 with the
per-class table + verbatim call shape + gate-ordering rules +
identifier-extraction + uniform 429 + fail-closed env-var
contract + fail-open Upstash-outage behavior. Doc-writer pass
verified completeness; added a one-sentence convoy-attribution
line at the top of § Rate limiting citing the two-convoy
lineage (fix-auth-bypass Brief 4 for the auth class +
add-rate-limiting for the other four classes and 7 newly-gated
routes), mirroring the post-cors-tighten § CORS attribution
shape. No other touch-ups needed.
No changes to: package.json, package-lock.json, lib/rate-limit.js,
pages/**, components/**, scripts/**, test/**, tests/**,
.github/workflows/**, README.md, TESTING_GUIDE.md, playwright.config.js,
eslint.config.mjs.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 00:09:07 -04:00
- **As-shipped (Brief 4, 2026-05-23):** `lib/rate-limit.js` (new) provides `checkAuthRateLimit(req)` via `@upstash/ratelimit@^2.0.8` + `@upstash/redis@^1.38.0` (5 attempts / 15-min sliding window per IP). Wired into login + register. Env vars are `KV_REST_API_URL` / `KV_REST_API_TOKEN` (auto-provisioned by Vercel's Upstash Marketplace integration — note this is a rename from the brief's original `UPSTASH_REDIS_REST_*` spec; see `.convoys/fix-auth-bypass/brief-4-tighten-auth-surface.md` § Post-merge addendum). Fails closed in prod when env vars are unset; warn-and-no-ops in dev. Search / import / avatar endpoints were unchanged at that point (the deferred surface that `add-rate-limiting` then closed).
- **As-shipped (`add-rate-limiting`, 2026-05-24, squash commit `708ef45` , PR #20 , architect-commit `60b842e` , implementer-commit `51a3a97` ):**
1. ** `lib/rate-limit.js` refactored** from a single auth-only `Ratelimit` instance into a `Map<className, Ratelimit>` cache with one shared Redis client and **5 `Ratelimit` instances** (one per class, distinct Redis prefix). Five exported functions: `checkAuthRateLimit(req)` (Brief 4 contract preserved byte-identical), `checkSearchRateLimit(req)` , `checkUploadRateLimit(req, userId)` , `checkGenerateRateLimit(req, userId)` , `checkImportRateLimit(req, userId)` . Internal `check(className, identifier)` shared helper. `LIMITER_CONFIG` is a top-level `const` map of `{ limit, window, prefix }` per class; adding a sixth class is a one-line addition + one new exported function.
2. **6 routes newly gated** with the appropriate per-class limiter at the correct ordering (auth before user-keyed limiter; method check first):
| Limiter | Limit/window | Key | Routes |
| --- | --- | --- | --- |
| `checkAuthRateLimit` (unchanged from Brief 4) | 5 / 15 min | IP | `auth/login.js` , `auth/register.js` |
| `checkSearchRateLimit` (new) | 60 / 1 min | IP | `users/search.js` , `cards/search.js` |
| `checkUploadRateLimit` (new) | 10 / 1 hour | user | `user/avatar.js` |
| `checkGenerateRateLimit` (new) | 5 / 1 hour | user | `user/avatar/generate.js` |
| `checkImportRateLimit` (new) | 5 / 1 hour | user (admin-only) | `cards/import-mtg.js` , `cards/import-pokemon.js` , `cards/import-lorcana.js` |
3. ** `extractUserIdentifier(userId)` THROWS** on `null` / `undefined` / `''` / `NaN` (Decision 4 defensive shape). Surfaces gate-ordering bugs at dev time rather than silently falling back to IP and converting a per-user limit into a per-IP limit — which would lock other household members out for one user's behavior. Numeric `0` is intentionally accepted (returns `'user:0'` ) for forward-compat.
4. **Three `pages/api/cards/import-*.js` routes newly auth-gated.** Each grew `getUserFromRequest` + `if (user.role !== 'admin') return 403` + `checkImportRateLimit(req, user.userId)` before the existing `try` block. Closes the publicly-callable anonymous-abuse vector the architect's pre-brief audit flagged (each handler hits Scryfall / Pokémon-TCG / Lorcana APIs and performs UPSERTs into `cards` with no caller throttling pre-convoy). Lorcana was gated defensively despite zero current frontend callers — see § Queued convoys for the `delete-dead-lorcana-import` cleanup follow-up.
5. **Atomic admin UI fix in `pages/admin/card-import.js`.** Added `'Authorization': \` Bearer ${localStorage.getItem('auth_token')}\`` to the import fetch's headers (one-line addition). **This was the architect's critical pre-brief discovery and the reason Decision 1 routed back to the operator** — gating the import APIs without this matching client fetch fix would have closed P0 #6 but introduced an immediate 401 on every "Import Cards" click, producing a visible UX regression on the only live admin tooling that exercises the gated routes. Shipping the API gate + the client fix in the same atomic PR is what made Decision 1 Option A viable.
6. ** `.cursor/rules/api-routes.mdc` § Rate limiting extended** with the per-class table + verbatim call shape + gate-ordering rules (method check first; auth before any user-keyed limiter; admin-role check goes between auth and rate-limit for the import routes) + identifier-extraction documentation + uniform 429 response shape + fail-closed env-var contract + fail-open Upstash-outage behavior. Doc-writer pass verified the implementer's extension is complete; no further touch-ups needed.
7. **All six architect decisions ratified at gate 1.** D1 operator-ratified (Option A — gate all three import routes plus the atomic admin UI fix); D2-D6 architect-self-ratified per the precedent established by `cors-tighten` D2-D5 and `fix-vercel-deployment-protection-in-ci` A/B/D (hybrid named-limiter shape; per-class limit values with tuning evidence; two-extractor shape with defensive THROW; uniform 429 message; no new vitest / playwright specs in this convoy).
8. **Diff: 12 files, +1612 / -23 in the squash.** The 1612-addition figure is dominated by the architect's convoy + brief files (676 + 751 lines) which the squash includes because the architect commit preceded the implementer commit on the same branch. Actual source-file diff is much smaller: `lib/rate-limit.js` +90/-23 (lib refactor); `.cursor/rules/api-routes.mdc` +41 (rule extension); 6 route files +69 total (3× +16 for import routes, 4× +7 for search/avatar/generate/users-search); `pages/admin/card-import.js` +1 (Bearer-header addition).
- **As-shipped metrics (post-merge run 26382185019 + subsequent runs):**
- `Playwright smoke` — PASS in **59s, 3/3 tests in 3.8s** against the post-rate-limit Vercel preview (home redirects ✓ 431ms / sign-in page renders ✓ 331ms / `/api/health` ✓ 193ms). **Critical cross-validation:** smoke calls `/api/health` once per run (well below the new search class's 60/min ceiling), and the home + sign-in routes don't touch any of the 6 newly-gated endpoints — so the new search limiter does NOT 429 the smoke spec. The cross-validation lineage now accumulates across three convoys: smoke test 2 still passes against post-PR-#15 Layout default-user + post-PR-#19 CORS-tighten + post-PR-#20 rate-limiting — the same 3-test spec has defended the auth surface through three sweeping changes without anyone writing a dedicated test.
- `forbidden-cors-headers` (from `cors-tighten` ) — PASS. The convoy is purely additive of rate-limit gate code; no CORS headers were reintroduced.
- `forbidden-endpoints` (from `fix-auth-bypass` Brief 3) — PASS. No new `pages/api/test-*.js` or deleted-endpoint shapes reintroduced.
- `Unit tests (vitest)` — PASS, **21/21 in 27s** . Decision 6 (no new vitest specs) verified at architect time (`rg 'rate-limit|@upstash' test/` returns zero matches; the existing 21 specs don't transitively import `lib/rate-limit.js` , so the lib refactor was strictly safer than the convoy file's stale § Known constraints implied).
- `Lint` — 128 problems (baseline preserved, no regression). Zero new lint problems from the lib refactor, the 6 route edits, or the admin UI one-liner.
- `Screenshot diff` — `continue-on-error: true` swallow per `adopt-playwright-smoke` Decision 4 (no baseline committed yet). Triggered on PR #20 because the `paths:` filter `pages/**` matches the 6 route edits under `pages/api/` ; same minor false-positive as PR #19 , tracked by the queued `tighten-visual-diff-path-filter` follow-up. Not a regression.
- All other gates (`Schema map up to date`, `Aggregate gate` ) — green.
- **Operator action required going forward:** **none.** All Upstash env vars (`KV_REST_API_URL` / `KV_REST_API_TOKEN` ) were already auto-provisioned via the Vercel Marketplace integration for Brief 4. No new secrets, no infra changes, no CI gates to enable. The fail-loud-in-prod predicate in `lib/rate-limit.js::init()` is self-defending: if a future deploy unsets either env var, every gated route fails closed on the first call (`throw new Error('[rate-limit] Upstash not configured...')`). If a follow-up tuning need surfaces (search 60/min too tight, generate 5/hour too tight), the fix is a single-line `LIMITER_CONFIG` edit; surface as `tune-search-rate-limit` or `tiered-rate-limits` only if real users 429.
- **Owns:** `role-architect` (pattern + Decision 1 routing) → `role-implementer` (per-route).
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
docs: post-convoy cleanup for fix-layout-default-user (+ queue CI-protection follow-up)
Reflects the merged fix-layout-default-user convoy (PR #15) and the
companion CI permissions fix (PR #16) in repo documentation. Also
queues the new fix-vercel-deployment-protection-in-ci convoy that
PR #16 exposed.
.convoys/ship-readiness.md:
- P0 #7: mark RESOLVED 2026-05-24 with squash commit ca302a8.
Document the as-shipped Layout default-null change, the 7-page
sweep, the 5 new regression-lock vitest assertions, and the
queued follow-ups (single-auth-provider, MobileNavigation
cleanup) that stayed explicitly out of scope.
- Queued convoys: add fix-vercel-deployment-protection-in-ci
(P2, CI infra) — PR #16's permissions fix exposed that Vercel
Deployment Protection 401s anonymous CI requests; needs a
bypass-secret plumb to land cleanly. New section also captures
other in-flight follow-ups (rotate-default-admin, cors-tighten,
add-rate-limiting, purge-weak-creds-from-helpers,
single-auth-provider, cleanup-mobile-nav-dead-props,
bump-eslint-10) so the audit trail is centralized.
AGENTS.md:
- § 4 Gotcha #8: mark RESOLVED with commit ref ca302a8. Mirror
the convention used by the prior cleanup commits for #2, #3,
#4, #5 (entry kept, not renumbered).
- § 3 Key conventions: add a new "Layout user prop" bullet
documenting the new default-null + logged-out-CTA contract so
the convention is discoverable from the conventions list, not
just the resolved-gotcha entry.
.convoys/fix-vercel-deployment-protection-in-ci.md (new):
- Queued scaffold. Operator must seed
VERCEL_AUTOMATION_BYPASS_SECRET as a repo secret before the
implementer can run. Decisions to ratify (query param vs.
header), known constraints, acceptance criteria, and
out-of-scope all enumerated.
No changes to: package.json, lib/**, pages/**, components/**,
scripts/**, .github/**, README.md, .cursor/rules/**.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 15:37:39 -04:00
### 7. Layout default-prop leaks maintainer email — **RESOLVED 2026-05-24**
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
docs: post-convoy cleanup for fix-layout-default-user (+ queue CI-protection follow-up)
Reflects the merged fix-layout-default-user convoy (PR #15) and the
companion CI permissions fix (PR #16) in repo documentation. Also
queues the new fix-vercel-deployment-protection-in-ci convoy that
PR #16 exposed.
.convoys/ship-readiness.md:
- P0 #7: mark RESOLVED 2026-05-24 with squash commit ca302a8.
Document the as-shipped Layout default-null change, the 7-page
sweep, the 5 new regression-lock vitest assertions, and the
queued follow-ups (single-auth-provider, MobileNavigation
cleanup) that stayed explicitly out of scope.
- Queued convoys: add fix-vercel-deployment-protection-in-ci
(P2, CI infra) — PR #16's permissions fix exposed that Vercel
Deployment Protection 401s anonymous CI requests; needs a
bypass-secret plumb to land cleanly. New section also captures
other in-flight follow-ups (rotate-default-admin, cors-tighten,
add-rate-limiting, purge-weak-creds-from-helpers,
single-auth-provider, cleanup-mobile-nav-dead-props,
bump-eslint-10) so the audit trail is centralized.
AGENTS.md:
- § 4 Gotcha #8: mark RESOLVED with commit ref ca302a8. Mirror
the convention used by the prior cleanup commits for #2, #3,
#4, #5 (entry kept, not renumbered).
- § 3 Key conventions: add a new "Layout user prop" bullet
documenting the new default-null + logged-out-CTA contract so
the convention is discoverable from the conventions list, not
just the resolved-gotcha entry.
.convoys/fix-vercel-deployment-protection-in-ci.md (new):
- Queued scaffold. Operator must seed
VERCEL_AUTOMATION_BYPASS_SECRET as a repo secret before the
implementer can run. Decisions to ratify (query param vs.
header), known constraints, acceptance criteria, and
out-of-scope all enumerated.
No changes to: package.json, lib/**, pages/**, components/**,
scripts/**, .github/**, README.md, .cursor/rules/**.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 15:37:39 -04:00
- **Resolved by:** `fix-layout-default-user` convoy (PR #15 , squash commit `ca302a8` ). Brief 1 (pre-squash `ddf8fd2` ) shipped the Layout default-null + logged-out branch + vitest lock-in; Brief 2 (pre-squash `8c7d127` , rebased to `0f6bfbb` pre-merge) swept the 7 pages that needed page-level fixes.
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
- **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).
docs: post-convoy cleanup for fix-layout-default-user (+ queue CI-protection follow-up)
Reflects the merged fix-layout-default-user convoy (PR #15) and the
companion CI permissions fix (PR #16) in repo documentation. Also
queues the new fix-vercel-deployment-protection-in-ci convoy that
PR #16 exposed.
.convoys/ship-readiness.md:
- P0 #7: mark RESOLVED 2026-05-24 with squash commit ca302a8.
Document the as-shipped Layout default-null change, the 7-page
sweep, the 5 new regression-lock vitest assertions, and the
queued follow-ups (single-auth-provider, MobileNavigation
cleanup) that stayed explicitly out of scope.
- Queued convoys: add fix-vercel-deployment-protection-in-ci
(P2, CI infra) — PR #16's permissions fix exposed that Vercel
Deployment Protection 401s anonymous CI requests; needs a
bypass-secret plumb to land cleanly. New section also captures
other in-flight follow-ups (rotate-default-admin, cors-tighten,
add-rate-limiting, purge-weak-creds-from-helpers,
single-auth-provider, cleanup-mobile-nav-dead-props,
bump-eslint-10) so the audit trail is centralized.
AGENTS.md:
- § 4 Gotcha #8: mark RESOLVED with commit ref ca302a8. Mirror
the convention used by the prior cleanup commits for #2, #3,
#4, #5 (entry kept, not renumbered).
- § 3 Key conventions: add a new "Layout user prop" bullet
documenting the new default-null + logged-out-CTA contract so
the convention is discoverable from the conventions list, not
just the resolved-gotcha entry.
.convoys/fix-vercel-deployment-protection-in-ci.md (new):
- Queued scaffold. Operator must seed
VERCEL_AUTOMATION_BYPASS_SECRET as a repo secret before the
implementer can run. Decisions to ratify (query param vs.
header), known constraints, acceptance criteria, and
out-of-scope all enumerated.
No changes to: package.json, lib/**, pages/**, components/**,
scripts/**, .github/**, README.md, .cursor/rules/**.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 15:37:39 -04:00
- **As-shipped:**
1. `components/Layout.js` default prop changed from hardcoded `{ email: 'me@randallstillwell.com', role: 'user' }` to `null` . `UserProfileDropdown` now branches on `user === null` and renders a `<Link href="/login">Sign in</Link>` CTA in place of the avatar + email + dropdown menu (`NavigationContent`'s `authenticatedNavigation` / `myCollectionNavigation` / `adminNavigation` were already null-safe via existing optional chains; no change there).
2. **7 pages swept** (Brief 2, 11 `<Layout>` call sites total). `pages/scanner.js` (× 1), `pages/decks.js` (× 3), `pages/deck-builder.js` (× 4), `pages/deck/[id].js` (× 3) now pass `user={user}` explicitly. `pages/profile.js` and `pages/settings.js` replaced their leaky `useState({ email: 'me@randallstillwell.com', role: 'admin' })` initializer with `useState(null)` (15 sync `user.*` reads in profile + 1 in settings got null-guards). `pages/card/[id].js` replaced its hardcoded `const user = { email: 'me@…', role: 'user' }` with `const { user } = useAuth()` from `lib/use-auth.js` .
3. **10 pages already correct** (architect's per-page audit, Decision B in `.convoys/fix-layout-default-user.md` ): `dashboard` , `my-cards` , `cards` , `collections` , `collection/[identifier]` , `community/collections` , `admin/card-import` , `admin/card-editor` , `invite/accept` , `invite/decline` . No changes there.
4. **Test coverage:** `test/components/Layout.test.js` (new) adds 5 regression-lock assertions — no maintainer email when `user` is `null` /omitted; "Sign in" link present when logged out; supplied email renders when supplied; no accidental `Guest` placeholder. Vitest 21/21 green at merge (16 pre-existing auth tests still green).
5. **New devDeps:** `jsdom@^29` + `@testing-library/react@^16` (test-only). `vitest.config.js` got a 3-line `esbuild` block to parse JSX in `.js` files (per-file `// @vitest-environment jsdom` directive — no global env change).
6. **Verification at merge:** `rg 'me@randallstillwell.com' pages/` → 0 hits; anonymous `curl /cards` returned HTTP 200 with no maintainer email; lint baseline match (128 problems, unchanged); CI Aggregate gate / Lint / Vitest / Vercel preview / forbidden-endpoints all green. `Playwright smoke` + `Screenshot diff` red but for an unrelated CI-infra reason — see CI infrastructure side-effect note below.
- **Flagged-but-deferred** (deliberately out of scope per the convoy spec):
1. 4 pages still import `useAuth` from `lib/auth-context.js` (`pages/scanner.js`, `pages/decks.js` , `pages/deck-builder.js` , `pages/deck/[id].js` ) — collapsing the three parallel client-side auth surfaces is the queued `single-auth-provider` convoy (P1 #9 in this file), not this one.
2. `components/MobileNavigation.js` still receives a dead `user` prop (it accepts `{ user, onMenuOpen }` but never reads `user.*` — the bottom-bar items are static). Queued as `cleanup-mobile-nav-dead-props` (or fold into `god-component-split` if that lands first).
3. `pages/card/[id].js` still imports `useIsAdmin` from `lib/admin-auth.js` — third parallel auth surface; same `single-auth-provider` convoy will collapse it.
- **CI infrastructure side-effect (not part of this convoy).** PR #16 (squash commit `7e97254` ) landed alongside as a CI permissions fix, adding scoped `permissions:` blocks to `.github/workflows/preview-smoke.yml` + `.github/workflows/visual-diff.yml` . That fixed the 5-second 403 "Resource not accessible by integration" failure on both workflows but exposed a second issue: with permissions correct, both now reach the actual deployment check and 10-min-timeout against Vercel Deployment Protection's 401 SSO challenge (anonymous GitHub runner GETs the preview URL). New queued convoy `fix-vercel-deployment-protection-in-ci` (`.convoys/fix-vercel-deployment-protection-in-ci.md`) tracks that follow-up.
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
- **Owns:** `role-implementer` .
docs: mark P0 #8 (Next.js bump) RESOLVED in ship-readiness.md
Doc-drift fix discovered after the fix-layout-default-user cleanup
pass. The bump-next-js convoy shipped on 2026-05-23 as commit
e57ea17, and the commit body explicitly states "Closes P0
ship-blocker #8" — but no dedicated doc-writer pass ever ran for
that convoy, so .convoys/ship-readiness.md still had P0 #8 listed
as open even though Vercel has been deploying main + every PR
successfully since.
Patches only P0 #8 to RESOLVED with the same shape used by P0
#1, #2, #3, #4, #7:
- Title: "— **RESOLVED 2026-05-23**"
- Resolved by: bullet citing e57ea17
- As-shipped: 7-point summary of the bump's actual deliverables
(next 15.4.3->16.2.6, ESLint v9-fallback, typescript devDep,
eslint.config.mjs, scripts.lint update, images.remotePatterns
migration, build verification)
- Side-effects deferred: bump-react, bump-eslint-10, bump-typescript-6,
fix-lint-baseline, App Router migration
- Doc drift note acknowledging this entry was added ~24h late
No code changes. No package.json / lib/** / pages/** / components/**
touched. AGENTS.md was already current (Gotchas #9, #10, #11 already
reflect Next 16 + ESLint v9 + Turbopack post-bump state).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 15:40:46 -04:00
### 8. Next.js 15.4.3 — Vercel platform blocks deploys (vulnerable version) — **RESOLVED 2026-05-23**
2026-05-23 00:24:11 -04:00
docs: mark P0 #8 (Next.js bump) RESOLVED in ship-readiness.md
Doc-drift fix discovered after the fix-layout-default-user cleanup
pass. The bump-next-js convoy shipped on 2026-05-23 as commit
e57ea17, and the commit body explicitly states "Closes P0
ship-blocker #8" — but no dedicated doc-writer pass ever ran for
that convoy, so .convoys/ship-readiness.md still had P0 #8 listed
as open even though Vercel has been deploying main + every PR
successfully since.
Patches only P0 #8 to RESOLVED with the same shape used by P0
#1, #2, #3, #4, #7:
- Title: "— **RESOLVED 2026-05-23**"
- Resolved by: bullet citing e57ea17
- As-shipped: 7-point summary of the bump's actual deliverables
(next 15.4.3->16.2.6, ESLint v9-fallback, typescript devDep,
eslint.config.mjs, scripts.lint update, images.remotePatterns
migration, build verification)
- Side-effects deferred: bump-react, bump-eslint-10, bump-typescript-6,
fix-lint-baseline, App Router migration
- Doc drift note acknowledging this entry was added ~24h late
No code changes. No package.json / lib/** / pages/** / components/**
touched. AGENTS.md was already current (Gotchas #9, #10, #11 already
reflect Next 16 + ESLint v9 + Turbopack post-bump state).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 15:40:46 -04:00
- **Resolved by:** `bump-next-js` convoy, single-brief PR commit `e57ea17` ("bump: next 15.4.3 -> 16.2.6, ESLint flat config (v9 fallback), typescript devDep"). The Vercel platform gate cleared with the first successful deploy on the same date; every subsequent PR (`fix-auth-bypass`, `drop-public-setup` , `fix-layout-default-user` , the CI permissions fix) has had a green Vercel preview.
2026-05-23 00:24:11 -04:00
- **Discovered:** 2026-05-22 during the bootstrap PR CI run. Vercel build completes successfully (~29s) but the deployment exits with status `Error` and `"Vulnerable version of Next.js detected, please update immediately"` .
- **Files:** `package.json` line 22 (`"next": "^15.4.2"` → locked at `15.4.3` ), `package-lock.json` .
- **Impact:** **Vercel will not deploy any branch — including `main` — until Next.js is bumped.** Preview URLs are unavailable, which means `preview-smoke.yml` and `visual-diff.yml` can't fire. The last successful deploy on `main` was 2025-08-01; production may already be running an outdated build.
- **CVE context:** Next.js shipped a middleware auth-bypass advisory (CVE-2025-29927) patched in 15.2.3, plus subsequent advisories. The exact CVE Vercel is flagging on 15.4.3 needs confirmation via `npm audit` and the Next.js security advisory page.
- **Fix:** Bump `next` to the latest secure 15.x (`npm install next@^15.5` and run smoke tests) OR the latest 16.x (`next@^16.2.6` — major bump; review breaking changes in [Next.js 16 release notes ](https://nextjs.org/blog/next-16 )).
docs: mark P0 #8 (Next.js bump) RESOLVED in ship-readiness.md
Doc-drift fix discovered after the fix-layout-default-user cleanup
pass. The bump-next-js convoy shipped on 2026-05-23 as commit
e57ea17, and the commit body explicitly states "Closes P0
ship-blocker #8" — but no dedicated doc-writer pass ever ran for
that convoy, so .convoys/ship-readiness.md still had P0 #8 listed
as open even though Vercel has been deploying main + every PR
successfully since.
Patches only P0 #8 to RESOLVED with the same shape used by P0
#1, #2, #3, #4, #7:
- Title: "— **RESOLVED 2026-05-23**"
- Resolved by: bullet citing e57ea17
- As-shipped: 7-point summary of the bump's actual deliverables
(next 15.4.3->16.2.6, ESLint v9-fallback, typescript devDep,
eslint.config.mjs, scripts.lint update, images.remotePatterns
migration, build verification)
- Side-effects deferred: bump-react, bump-eslint-10, bump-typescript-6,
fix-lint-baseline, App Router migration
- Doc drift note acknowledging this entry was added ~24h late
No code changes. No package.json / lib/** / pages/** / components/**
touched. AGENTS.md was already current (Gotchas #9, #10, #11 already
reflect Next 16 + ESLint v9 + Turbopack post-bump state).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 15:40:46 -04:00
- **As-shipped (Decision A in `.convoys/bump-next-js.md` — leapfrog to 16):**
1. `next` : `^15.4.2` → `^16.2.6` (resolves to `16.2.6` ).
2. `eslint-config-next` : `15.4.2` → `^16.2.6` . Config migrated from `.eslintrc.json` to `eslint.config.mjs` (eslint-config-next@16 is flat-config-only).
3. `eslint` : `^8` → `^9.39.4` (Decision D fallback — v10 surfaced Risk R15 empirically because `@typescript-eslint/scope-manager@8.59.4` bundled by `eslint-config-next@16` doesn't implement v10's new `addGlobals` API; v10 adoption deferred to a separate `bump-eslint-10` convoy, upstream-blocked on typescript-eslint).
4. `typescript` : newly added at `^5.9.3` as a devDep (Decision C — required by the typescript-eslint chain regardless of ESLint major; no project source migration to TS).
5. `scripts.lint` : `"next lint"` → `"eslint ."` (next lint removed in 16). Lint baseline grew from ~100 to **128 problems** (81 errors, 47 warnings) due to `eslint-plugin-react-hooks@7.1.1` + `@next/eslint-plugin-next@16.2.6` rule additions; CI tolerates this via the `|| true` wrapper in `.github/workflows/ci.yml` per P1 #11 .5 (`fix-lint-baseline`).
6. `next.config.js` : `images.domains` → `images.remotePatterns` (deprecated and removed in 16; preserves Scryfall, Pokémon TCG, Lorcana API hosts for eventual `next/image` adoption).
7. Verification at merge: `npm install` clean (no ERESOLVE), `npm run build` exit 0 with Turbopack (~1.4s compile, 23 static pages + 47 API routes), first green Vercel deploy on `main` since 2025-08-01.
- **Side-effects (deliberately deferred, not part of this convoy):**
- `bump-react` (React 18 → 19) — held until 18.x EOL or until a feature needs it.
- App Router migration — multi-month effort; queued indefinitely.
- `adopt-vitest` ✅ shipped as `fix-auth-bypass` Brief 5; `adopt-playwright-smoke` partially shipped via the Vercel-bound workflows (CI infra now blocked by `fix-vercel-deployment-protection-in-ci` ).
- `fix-lint-baseline` (P1 #11 .5) — drop the CI `|| true` wrapper once the 128-problem baseline is cleared.
- `bump-eslint-10` + `bump-typescript-6` — upstream-blocked on typescript-eslint shipping v10-tested releases.
- **Doc drift note:** this resolution was applied as part of the `fix-layout-default-user` post-convoy cleanup (commit reflecting `b7ddd08` 's sibling) — the `bump-next-js` convoy never ran a dedicated doc-writer pass, so this RESOLVED entry was added ~24h after the fix actually shipped.
2026-05-23 00:24:11 -04:00
- **Owns:** `role-architect` (pick target version + assess breaking changes) → `role-implementer` (bump + verify dev/build/start + smoke).
docs: mark P0 #8 (Next.js bump) RESOLVED in ship-readiness.md
Doc-drift fix discovered after the fix-layout-default-user cleanup
pass. The bump-next-js convoy shipped on 2026-05-23 as commit
e57ea17, and the commit body explicitly states "Closes P0
ship-blocker #8" — but no dedicated doc-writer pass ever ran for
that convoy, so .convoys/ship-readiness.md still had P0 #8 listed
as open even though Vercel has been deploying main + every PR
successfully since.
Patches only P0 #8 to RESOLVED with the same shape used by P0
#1, #2, #3, #4, #7:
- Title: "— **RESOLVED 2026-05-23**"
- Resolved by: bullet citing e57ea17
- As-shipped: 7-point summary of the bump's actual deliverables
(next 15.4.3->16.2.6, ESLint v9-fallback, typescript devDep,
eslint.config.mjs, scripts.lint update, images.remotePatterns
migration, build verification)
- Side-effects deferred: bump-react, bump-eslint-10, bump-typescript-6,
fix-lint-baseline, App Router migration
- Doc drift note acknowledging this entry was added ~24h late
No code changes. No package.json / lib/** / pages/** / components/**
touched. AGENTS.md was already current (Gotchas #9, #10, #11 already
reflect Next 16 + ESLint v9 + Turbopack post-bump state).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 15:40:46 -04:00
- **Convoy:** `bump-next-js` — ran before `fix-auth-bypass` . **Without this convoy, every L3 gate that depends on a Vercel preview was non-functional.**
2026-05-23 00:24:11 -04:00
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
## 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:**
docs: post-convoy cleanup for adopt-playwright-smoke
Reflects the merged adopt-playwright-smoke convoy (PR #18, squash
commit 7b6f751) in repo documentation. Closes the test-infrastructure
side of P1 #10 step 2 (launch sequence step 10). One commit in the
convoy: Brief 1, with two small lint-baseline-preserving deviations
from the brief's verbatim shape that the implementer report flagged.
.convoys/adopt-playwright-smoke.md:
- frontmatter status: in-progress -> shipped (added shipped:
2026-05-24)
- new ## As-shipped section: operator-ratified Decisions (D1 keep
.ts, D4 defer baselines, D6 simple scripts); two implementer
deviations (removed unused eslint-disable-no-console directive
that would have regressed lint 128 -> 129; placed @playwright/test
first in devDeps for alphabetical correctness); cross-validation
that smoke test 2 ("sign-in page renders") locks in PR #15's
logged-out CTA work in components/Layout.js; empirical metrics
from post-merge run 26376162598 (59s workflow, 3/3 in 2.9s,
0-leak); operator-action-required note pointing at the queued
seed-visual-baselines-on-linux follow-up; What did NOT change
audit trail.
.convoys/ship-readiness.md:
- Queued convoys: new entry seed-visual-baselines-on-linux (Linux-
Docker baseline generation per Decision 4 + Boot-the-brief
Finding 7; Mac-generated baselines would silently overwrite Linux
CI baselines because the custom snapshotPathTemplate has no
{platform} token).
- Queued convoys: new RESOLVED block for adopt-playwright-smoke
(PR #18, 7b6f751) — as-shipped surface, implementer deviations,
empirical metrics (59s workflow / 3/3 in 2.9s / 0 secret leaks),
the PR #15 cross-validation finding, operator-action-required
going forward (the seed-visual-baselines-on-linux follow-up),
flagged-but-deferred items, and ownership trail (3 architect-
self-ratified decisions + 3 operator-ratified).
- Launch sequence step 10: marked RESOLVED 2026-05-24 with the
commit + metrics inline.
- P1 #10 No tests Fix sequence: step 2 marked RESOLVED with the
convoy + metrics ref; step 3 (re-enable test: job in
ci.yml) called out as the next remaining task; step 5 (wire
preview-smoke.yml to the Vercel preview URL) marked RESOLVED
across PR #17 + PR #18 since both contributed.
AGENTS.md:
- Section 6 Testing: rewritten end-to-end. Was "E2E/smoke runner
still pending"; is now "@playwright/test@^1.60.0 wired, two
projects (smoke + visual), npm run test:smoke / test:visual /
test:visual:update". Documents the local-run convention (boot
next dev separately, then BASE_URL=... npm run test:smoke);
the one-time npx playwright install --with-deps chromium step;
the no-baselines-yet state + the Linux-Docker seed command +
the cross-platform mismatch reason (no {platform} token in
snapshotPathTemplate); the CI behavior split (vitest blocking,
smoke on every PR with pipeline:skip-smoke escape hatch,
Screenshot diff path-filtered with the first-red-on-missing-
baseline state documented). Updates vitest coverage count
16 -> 21 (the +5 Layout regression-lock tests from PR #15).
Notes TESTING_GUIDE.md is being eclipsed and will be renamed
to docs/MANUAL_QA.md in a future cleanup convoy.
- Section 7 Deployment: rewrites the Vercel-bypass paragraph from
a single "query param now / header reserved for future" bullet
into a two-shape audit ((1) query param on the wait-action's
path: input per PR #17; (2) HTTP header in playwright.config.js's
use.extraHTTPHeaders per PR #18). Documents the Decision-2
fail-loud-in-CI / warn-in-dev predicate and references Gotcha #12
as the established precedent (lib/rate-limit.js). Picked Section
7 over a new Gotcha because the bypass plumbing is operationally
a deployment concern, not an app pitfall.
No changes to: package.json, package-lock.json, playwright.config.js,
eslint.config.mjs, lib/**, pages/**, components/**, scripts/**,
.github/workflows/**, .cursor/rules/**, README.md,
tests/visual/homepage.spec.ts (JSDoc is already neutral-tense, no
future-tense references to clean up).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 20:33:43 -04:00
1. Install `vitest` . Add `npm run test:run` script. **RESOLVED** by `fix-auth-bypass` Brief 5, commit `1629afb` .
2. Install `@playwright/test` . Wire up `tests/smoke/app.smoke.spec.ts` (already drafted; needs `playwright.config.ts` ). **RESOLVED 2026-05-24** by `adopt-playwright-smoke` , PR #18 squash `7b6f751` — 3/3 smoke tests pass in 2.9s, full workflow 59s, zero secret leaks. See § Queued convoys and `.convoys/adopt-playwright-smoke.md` § As-shipped.
3. Re-enable the `test:` job in `.github/workflows/ci.yml` (commented out at install time). Next remaining step in this fix sequence.
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
4. Add unit tests for `lib/permission-middleware.js` , `lib/slug-utils.js` , `pages/api/auth-utils.js` .
docs: post-convoy cleanup for adopt-playwright-smoke
Reflects the merged adopt-playwright-smoke convoy (PR #18, squash
commit 7b6f751) in repo documentation. Closes the test-infrastructure
side of P1 #10 step 2 (launch sequence step 10). One commit in the
convoy: Brief 1, with two small lint-baseline-preserving deviations
from the brief's verbatim shape that the implementer report flagged.
.convoys/adopt-playwright-smoke.md:
- frontmatter status: in-progress -> shipped (added shipped:
2026-05-24)
- new ## As-shipped section: operator-ratified Decisions (D1 keep
.ts, D4 defer baselines, D6 simple scripts); two implementer
deviations (removed unused eslint-disable-no-console directive
that would have regressed lint 128 -> 129; placed @playwright/test
first in devDeps for alphabetical correctness); cross-validation
that smoke test 2 ("sign-in page renders") locks in PR #15's
logged-out CTA work in components/Layout.js; empirical metrics
from post-merge run 26376162598 (59s workflow, 3/3 in 2.9s,
0-leak); operator-action-required note pointing at the queued
seed-visual-baselines-on-linux follow-up; What did NOT change
audit trail.
.convoys/ship-readiness.md:
- Queued convoys: new entry seed-visual-baselines-on-linux (Linux-
Docker baseline generation per Decision 4 + Boot-the-brief
Finding 7; Mac-generated baselines would silently overwrite Linux
CI baselines because the custom snapshotPathTemplate has no
{platform} token).
- Queued convoys: new RESOLVED block for adopt-playwright-smoke
(PR #18, 7b6f751) — as-shipped surface, implementer deviations,
empirical metrics (59s workflow / 3/3 in 2.9s / 0 secret leaks),
the PR #15 cross-validation finding, operator-action-required
going forward (the seed-visual-baselines-on-linux follow-up),
flagged-but-deferred items, and ownership trail (3 architect-
self-ratified decisions + 3 operator-ratified).
- Launch sequence step 10: marked RESOLVED 2026-05-24 with the
commit + metrics inline.
- P1 #10 No tests Fix sequence: step 2 marked RESOLVED with the
convoy + metrics ref; step 3 (re-enable test: job in
ci.yml) called out as the next remaining task; step 5 (wire
preview-smoke.yml to the Vercel preview URL) marked RESOLVED
across PR #17 + PR #18 since both contributed.
AGENTS.md:
- Section 6 Testing: rewritten end-to-end. Was "E2E/smoke runner
still pending"; is now "@playwright/test@^1.60.0 wired, two
projects (smoke + visual), npm run test:smoke / test:visual /
test:visual:update". Documents the local-run convention (boot
next dev separately, then BASE_URL=... npm run test:smoke);
the one-time npx playwright install --with-deps chromium step;
the no-baselines-yet state + the Linux-Docker seed command +
the cross-platform mismatch reason (no {platform} token in
snapshotPathTemplate); the CI behavior split (vitest blocking,
smoke on every PR with pipeline:skip-smoke escape hatch,
Screenshot diff path-filtered with the first-red-on-missing-
baseline state documented). Updates vitest coverage count
16 -> 21 (the +5 Layout regression-lock tests from PR #15).
Notes TESTING_GUIDE.md is being eclipsed and will be renamed
to docs/MANUAL_QA.md in a future cleanup convoy.
- Section 7 Deployment: rewrites the Vercel-bypass paragraph from
a single "query param now / header reserved for future" bullet
into a two-shape audit ((1) query param on the wait-action's
path: input per PR #17; (2) HTTP header in playwright.config.js's
use.extraHTTPHeaders per PR #18). Documents the Decision-2
fail-loud-in-CI / warn-in-dev predicate and references Gotcha #12
as the established precedent (lib/rate-limit.js). Picked Section
7 over a new Gotcha because the bypass plumbing is operationally
a deployment concern, not an app pitfall.
No changes to: package.json, package-lock.json, playwright.config.js,
eslint.config.mjs, lib/**, pages/**, components/**, scripts/**,
.github/workflows/**, .cursor/rules/**, README.md,
tests/visual/homepage.spec.ts (JSDoc is already neutral-tense, no
future-tense references to clean up).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 20:33:43 -04:00
5. Wire `preview-smoke.yml` to run against the Vercel preview URL. **RESOLVED 2026-05-24** by `fix-vercel-deployment-protection-in-ci` (PR #17 , `9a3e077` ) + `adopt-playwright-smoke` (PR #18 , `7b6f751` ).
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
- **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).
2026-05-23 00:24:11 -04:00
### 11.5. Codebase has ~100 pre-existing ESLint errors
- **Discovered:** 2026-05-22 during the bootstrap PR. The repo had `"lint": "next lint"` in `package.json` but no `.eslintrc.json` — meaning lint was never run. Bootstrap added the config; lint now surfaces ~100 errors.
- **Most serious:** `react-hooks/rules-of-hooks` violations (hooks called conditionally) in several components. These are **real bugs** — React's hook ordering is undefined when hooks are called after early returns. They likely manifest as state-loss / stale-closure bugs in edge cases.
- **Less serious:** `react/no-unescaped-entities` (cosmetic), `react-hooks/exhaustive-deps` (warnings about missing useEffect deps), `@next/next/no-img-element` (cosmetic).
- **Impact:** The L3 CI lint job is currently `continue-on-error: true` (see `.github/workflows/ci.yml` ) so it doesn't block PRs. Lint output is visible in logs but PRs merge regardless of lint state until this is cleaned up.
- **Fix:** Triage each error. The rules-of-hooks ones need genuine code restructuring (move hooks before any early returns). The unescaped-entities are mechanical (`'` → `'` ). After cleanup, remove `continue-on-error: true` .
- **Convoy:** `fix-lint-baseline` — run after `fix-auth-bypass` and `drop-public-setup` . Multitask-safe: split into briefs by file group.
- **Owns:** `role-architect` (group strategy) → `role-implementer` (per-group fan-out).
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
### 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 `<Modal>` 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 `<CardGrid>` 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 `<a href="#main" class="sr-only focus:not-sr-only">` . 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 `<div>` s already have `onKeyDown` but a few don't (audit with `rg "onClick" components pages | rg -v "<button"` ).
### Role-doc-writer findings
- **README** — needs a public-facing rewrite. Currently mixes user docs + dev setup + admin credentials. Split into `README.md` (project landing) + `docs/DEVELOPMENT.md` (dev setup) + delete the admin credentials section entirely.
- **`docs/SCHEMA_MAP.md`** — installed at bootstrap (this convoy). Keep it fresh on every schema change.
- **CHANGELOG** — none yet. Adopt Keep-a-Changelog format. Backfill `[0.1.0] — initial private alpha` covering everything to date.
- **`TESTING_GUIDE.md`** — currently the only test doc; rename to `docs/MANUAL_QA.md` once `vitest` + `playwright` land.
- **`docs/API_REFERENCE.md`** — would help. Could be auto-generated by walking `pages/api/**/*.js` and extracting JSDoc; or hand-curated to start.
- **Privacy policy / Terms of service** — required before public launch. Use a template (Termly / Iubenda) and customize.
## Proposed launch sequence
Each phase is one Conductor-created convoy. Don't run more than two in parallel until tests exist.
2026-05-23 00:24:11 -04:00
0. ** `bump-next-js` ** (P0 #8 ). One PR. **MUST land first** — Vercel is currently blocking all deployments, which makes every other PR's preview-smoke / visual-diff gate non-functional. Trivial bump; risk is breaking changes if going to 16.x.
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
1. ** `fix-auth-bypass` ** (P0 #1 , #2 , #4 , #5 , #6 partial). One PR. Highest risk; needs human review.
2. ** `drop-public-setup` ** (P0 #3 , #4 ). One PR. Trivial; do as a hotfix.
3. ** `fix-layout-default-user` ** (P0 #7 ). One PR. Trivial.
2026-05-23 00:24:11 -04:00
3.5. ** `fix-lint-baseline` ** (P1 #11 .5). 2-4 PRs via multitask. Closes the lint gate (drops `continue-on-error` ).
docs: post-convoy cleanup for add-rate-limiting — MILESTONE, last P0 closed
Reflects the merged add-rate-limiting convoy (PR #20, squash commit
708ef45) in repo documentation. **This is the milestone cleanup** —
add-rate-limiting closed P0 #6 (No rate limiting anywhere), the LAST
open P0 ship-blocker. `.convoys/ship-readiness.md`'s § Status summary
flips from "7 of 8 RESOLVED; 1 remains" to **"8 of 8 RESOLVED.
Launch-readiness P0 checklist is empty."** One brief in the convoy:
Brief 1 shipped as planned with no scope expansions and no implementer
deviations from the verbatim spec; all six architect decisions ratified
verbatim at gate 1 (D1 operator-ratified Option A; D2-D6
architect-self-ratified).
.convoys/add-rate-limiting.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section. Opens with the milestone language
pointing back at ship-readiness.md's flipped § Status summary.
Decisions section captures all 6 ratifications (D1 operator-
ratified Option A — Critical: WHY the atomic admin UI fix in
pages/admin/card-import.js was Decision 1's hidden coupling
requirement, since API gating alone would have broken every
"Import Cards" click; D2 hybrid named-limiter shape with
Map<className, Ratelimit> cache; D3 per-class table including
the two D3 tuning-evidence raises — search 30 -> 60/min because
ShareModal.handleSearch has no debounce so a 17-char email = 16
requests in <5s, and generate kept at 5/hour because DiceBear is
free not paid AI; D4 two-extractor shape with defensive THROW
on null/empty userId; D5 uniform 429 message; D6 no new vitest
or playwright specs deferred to fill-vitest-handler-coverage).
As-shipped surface broken into 4 layers (1 lib refactor + 6 route
gates + 1 atomic admin UI fix + 1 rule extension) mirroring the
cors-tighten cleanup's pattern-split shape. Empirical CI metrics
from post-merge run 26382185019 (Playwright smoke 59s 3/3 in
3.8s, forbidden-cors-headers pass, vitest 21/21, lint 128 baseline,
Screenshot diff continue-on-error swallow per Decision 4).
Cross-validation finding: smoke test 2 still passes against the
post-rate-limit preview — that's three convoys in a row (PR #15
Layout default-user, PR #19 CORS-tighten, PR #20 rate-limiting)
where the same 3-test smoke spec defended the auth surface
through sweeping changes. Operator-action-required: none. What
did NOT change audit trail.
.convoys/ship-readiness.md:
- § Status summary at the top flipped from 7/8 to 8/8 RESOLVED.
Header text updated to "Launch-readiness P0 checklist is empty."
P0 #6 row in the table flips from PARTIAL to RESOLVED with the
two-convoy lineage (fix-auth-bypass Brief 4 + add-rate-limiting).
Trailing paragraph rewritten as a milestone note: security gate
closed; remaining launch work is P1 quality bar + P2/P3 polish.
- P0 #6 entry flipped from PARTIAL to RESOLVED with the full
add-rate-limiting as-shipped block (8 sub-bullets covering the
lib refactor shape, the per-class table, the defensive THROW,
the three import routes' auth-gating, the atomic admin UI fix
and WHY, the rule extension, the 6 decisions, and the diff
breakdown). Brief 4's 2026-05-23 partial is preserved as the
prior as-shipped layer to maintain the audit trail.
- Launch sequence step 4 marked RESOLVED 2026-05-24 with the
squash commit + smoke metrics inline.
- Queued convoys: removed the add-rate-limiting entry (it shipped).
Added a new delete-dead-lorcana-import entry (P3 polish; the
Lorcana import route was gated defensively in PR #20 despite
zero current frontend callers — pages/admin/card-import.js's
<select> only offers mtg + pokemon — so if Lorcana stays
permanently out of the admin UI, this is the cleanup PR).
Added three "flagged but kept out of scope" follow-ups per the
convoy's § What did NOT change: harden-multipart-parser (P2;
5MB body still consumed before the 429 path on avatar.js),
god-function-split / refactor-cards-search-sql (P2; 240-line
7-branch SQL in cards/search.js), and withAdmin(handler) wrapper
extraction (P3 DX; the three import routes are call sites #3-5
in the codebase but uniform inline shape was preserved for
convoy atomicity). Updated tighten-visual-diff-path-filter to
note PR #20 also tripped the same false-positive.
AGENTS.md:
- Gotcha #12 extended end-to-end. Was the single-class auth-only
lib + the env-var contract; is now the 5-class reality with a
full per-class table (helper / limit-window / key / routes),
the defensive THROW pattern in extractUserIdentifier, the
gate-ordering rule for per-user limiters, and the
auth → admin-role → rate-limit ordering for the three import
routes. Prominent milestone line opens the new content:
"add-rate-limiting convoy (squash 708ef45, PR #20, 2026-05-24)
closed P0 #6 — all 8 P0s now RESOLVED." Original env-var
contract paragraph (KV_REST_API_URL / KV_REST_API_TOKEN,
fail-closed-in-prod / warn-and-noop-in-dev) is preserved
verbatim above the new content.
- § 6 Testing: intentionally untouched (no test surface changed;
vitest 21/21 and smoke 3/3 still apply).
- § 7 Deployment: intentionally untouched (no deployment-shape
changed; same KV_REST_API_* env vars from Brief 4).
.cursor/rules/api-routes.mdc:
- The implementer extended § Rate limiting in PR #20 with the
per-class table + verbatim call shape + gate-ordering rules +
identifier-extraction + uniform 429 + fail-closed env-var
contract + fail-open Upstash-outage behavior. Doc-writer pass
verified completeness; added a one-sentence convoy-attribution
line at the top of § Rate limiting citing the two-convoy
lineage (fix-auth-bypass Brief 4 for the auth class +
add-rate-limiting for the other four classes and 7 newly-gated
routes), mirroring the post-cors-tighten § CORS attribution
shape. No other touch-ups needed.
No changes to: package.json, package-lock.json, lib/rate-limit.js,
pages/**, components/**, scripts/**, test/**, tests/**,
.github/workflows/**, README.md, TESTING_GUIDE.md, playwright.config.js,
eslint.config.mjs.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 00:09:07 -04:00
4. ** `add-rate-limiting` ** (P0 #6 full). One PR. Adds @upstash/ratelimit + applies to listed routes. **RESOLVED 2026-05-24** — PR #20 squash `708ef45` ; closes P0 #6 (last open P0), flipping the ship-blocker set to **8/8 RESOLVED** . 5 named limiters (auth/search/upload/generate/import), 6 routes newly gated + the 3 import routes auth-gated atomically with a `pages/admin/card-import.js` Bearer-header fix. Smoke 3/3 green in 3.8s post-merge — confirms the new 60/min search limiter doesn't 429 the smoke spec. See § Queued convoys and P0 #6 above for the full as-shipped block.
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
5. ** `pick-a-name` ** (P1 #12 ). Human decision first, then one or two PRs.
6. ** `adopt-vitest` ** (P1 #10 step 1). One PR. Enables testing every future change.
7. ** `migration-tool` ** (P1 #11 ). One PR. Backfill + first new migration.
8. ** `single-sql-client` ** (P1 #8 ). 2-3 PRs, fanned out via multitask once per-file briefs are written.
9. ** `single-auth-provider` ** (P1 #9 ). 3-5 PRs via multitask.
docs: post-convoy cleanup for adopt-playwright-smoke
Reflects the merged adopt-playwright-smoke convoy (PR #18, squash
commit 7b6f751) in repo documentation. Closes the test-infrastructure
side of P1 #10 step 2 (launch sequence step 10). One commit in the
convoy: Brief 1, with two small lint-baseline-preserving deviations
from the brief's verbatim shape that the implementer report flagged.
.convoys/adopt-playwright-smoke.md:
- frontmatter status: in-progress -> shipped (added shipped:
2026-05-24)
- new ## As-shipped section: operator-ratified Decisions (D1 keep
.ts, D4 defer baselines, D6 simple scripts); two implementer
deviations (removed unused eslint-disable-no-console directive
that would have regressed lint 128 -> 129; placed @playwright/test
first in devDeps for alphabetical correctness); cross-validation
that smoke test 2 ("sign-in page renders") locks in PR #15's
logged-out CTA work in components/Layout.js; empirical metrics
from post-merge run 26376162598 (59s workflow, 3/3 in 2.9s,
0-leak); operator-action-required note pointing at the queued
seed-visual-baselines-on-linux follow-up; What did NOT change
audit trail.
.convoys/ship-readiness.md:
- Queued convoys: new entry seed-visual-baselines-on-linux (Linux-
Docker baseline generation per Decision 4 + Boot-the-brief
Finding 7; Mac-generated baselines would silently overwrite Linux
CI baselines because the custom snapshotPathTemplate has no
{platform} token).
- Queued convoys: new RESOLVED block for adopt-playwright-smoke
(PR #18, 7b6f751) — as-shipped surface, implementer deviations,
empirical metrics (59s workflow / 3/3 in 2.9s / 0 secret leaks),
the PR #15 cross-validation finding, operator-action-required
going forward (the seed-visual-baselines-on-linux follow-up),
flagged-but-deferred items, and ownership trail (3 architect-
self-ratified decisions + 3 operator-ratified).
- Launch sequence step 10: marked RESOLVED 2026-05-24 with the
commit + metrics inline.
- P1 #10 No tests Fix sequence: step 2 marked RESOLVED with the
convoy + metrics ref; step 3 (re-enable test: job in
ci.yml) called out as the next remaining task; step 5 (wire
preview-smoke.yml to the Vercel preview URL) marked RESOLVED
across PR #17 + PR #18 since both contributed.
AGENTS.md:
- Section 6 Testing: rewritten end-to-end. Was "E2E/smoke runner
still pending"; is now "@playwright/test@^1.60.0 wired, two
projects (smoke + visual), npm run test:smoke / test:visual /
test:visual:update". Documents the local-run convention (boot
next dev separately, then BASE_URL=... npm run test:smoke);
the one-time npx playwright install --with-deps chromium step;
the no-baselines-yet state + the Linux-Docker seed command +
the cross-platform mismatch reason (no {platform} token in
snapshotPathTemplate); the CI behavior split (vitest blocking,
smoke on every PR with pipeline:skip-smoke escape hatch,
Screenshot diff path-filtered with the first-red-on-missing-
baseline state documented). Updates vitest coverage count
16 -> 21 (the +5 Layout regression-lock tests from PR #15).
Notes TESTING_GUIDE.md is being eclipsed and will be renamed
to docs/MANUAL_QA.md in a future cleanup convoy.
- Section 7 Deployment: rewrites the Vercel-bypass paragraph from
a single "query param now / header reserved for future" bullet
into a two-shape audit ((1) query param on the wait-action's
path: input per PR #17; (2) HTTP header in playwright.config.js's
use.extraHTTPHeaders per PR #18). Documents the Decision-2
fail-loud-in-CI / warn-in-dev predicate and references Gotcha #12
as the established precedent (lib/rate-limit.js). Picked Section
7 over a new Gotcha because the bypass plumbing is operationally
a deployment concern, not an app pitfall.
No changes to: package.json, package-lock.json, playwright.config.js,
eslint.config.mjs, lib/**, pages/**, components/**, scripts/**,
.github/workflows/**, .cursor/rules/**, README.md,
tests/visual/homepage.spec.ts (JSDoc is already neutral-tense, no
future-tense references to clean up).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 20:33:43 -04:00
10. ** `adopt-playwright-smoke` ** (P1 #10 step 2). One PR. **RESOLVED 2026-05-24** — PR #18 squash `7b6f751` ; smoke 3/3 green in 2.9s, full workflow 59s, zero secret leaks. See § Queued convoys for the full as-shipped block.
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
11. ** `schema-cleanup` ** (P2 #14 ). Multi-PR convoy via multitask.
12. ** `god-component-split` ** (P2 #13 ). One convoy per file; fan out via multitask once architect's `slice_dependencies` are written.
13. ** `launch-polish` ** (P3). UX/IA/a11y/docs convoy.
2026-05-23 00:24:11 -04:00
Total: ~14 convoys to get from current state to public-launch-ready. Estimate 4-8 weeks at one human-in-the-loop reviewer per convoy. Multitask + Cursor 3.2 worktrees compress steps 8-12 substantially.
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
docs: post-convoy cleanup for fix-layout-default-user (+ queue CI-protection follow-up)
Reflects the merged fix-layout-default-user convoy (PR #15) and the
companion CI permissions fix (PR #16) in repo documentation. Also
queues the new fix-vercel-deployment-protection-in-ci convoy that
PR #16 exposed.
.convoys/ship-readiness.md:
- P0 #7: mark RESOLVED 2026-05-24 with squash commit ca302a8.
Document the as-shipped Layout default-null change, the 7-page
sweep, the 5 new regression-lock vitest assertions, and the
queued follow-ups (single-auth-provider, MobileNavigation
cleanup) that stayed explicitly out of scope.
- Queued convoys: add fix-vercel-deployment-protection-in-ci
(P2, CI infra) — PR #16's permissions fix exposed that Vercel
Deployment Protection 401s anonymous CI requests; needs a
bypass-secret plumb to land cleanly. New section also captures
other in-flight follow-ups (rotate-default-admin, cors-tighten,
add-rate-limiting, purge-weak-creds-from-helpers,
single-auth-provider, cleanup-mobile-nav-dead-props,
bump-eslint-10) so the audit trail is centralized.
AGENTS.md:
- § 4 Gotcha #8: mark RESOLVED with commit ref ca302a8. Mirror
the convention used by the prior cleanup commits for #2, #3,
#4, #5 (entry kept, not renumbered).
- § 3 Key conventions: add a new "Layout user prop" bullet
documenting the new default-null + logged-out-CTA contract so
the convention is discoverable from the conventions list, not
just the resolved-gotcha entry.
.convoys/fix-vercel-deployment-protection-in-ci.md (new):
- Queued scaffold. Operator must seed
VERCEL_AUTOMATION_BYPASS_SECRET as a repo secret before the
implementer can run. Decisions to ratify (query param vs.
header), known constraints, acceptance criteria, and
out-of-scope all enumerated.
No changes to: package.json, lib/**, pages/**, components/**,
scripts/**, .github/**, README.md, .cursor/rules/**.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 15:37:39 -04:00
## Queued convoys
Follow-ups surfaced mid-convoy or mid-PR that didn't fit the original launch sequence but need to land before public traffic. Listed in priority order; not all will be P0/P1 — most are CI / DX / hygiene polish.
- **`rotate-default-admin`** (priority: P2 hygiene). Operator-rotation script for envs that ran `setup-neon-db.js` before `drop-public-setup` and still carry the weak `admin123` bcrypt hash. Surfaced in P0 #3 § Operator caveat. Optional: do nothing if no audit finds a deployed env with the weak hash.
docs: post-convoy cleanup for add-rate-limiting — MILESTONE, last P0 closed
Reflects the merged add-rate-limiting convoy (PR #20, squash commit
708ef45) in repo documentation. **This is the milestone cleanup** —
add-rate-limiting closed P0 #6 (No rate limiting anywhere), the LAST
open P0 ship-blocker. `.convoys/ship-readiness.md`'s § Status summary
flips from "7 of 8 RESOLVED; 1 remains" to **"8 of 8 RESOLVED.
Launch-readiness P0 checklist is empty."** One brief in the convoy:
Brief 1 shipped as planned with no scope expansions and no implementer
deviations from the verbatim spec; all six architect decisions ratified
verbatim at gate 1 (D1 operator-ratified Option A; D2-D6
architect-self-ratified).
.convoys/add-rate-limiting.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section. Opens with the milestone language
pointing back at ship-readiness.md's flipped § Status summary.
Decisions section captures all 6 ratifications (D1 operator-
ratified Option A — Critical: WHY the atomic admin UI fix in
pages/admin/card-import.js was Decision 1's hidden coupling
requirement, since API gating alone would have broken every
"Import Cards" click; D2 hybrid named-limiter shape with
Map<className, Ratelimit> cache; D3 per-class table including
the two D3 tuning-evidence raises — search 30 -> 60/min because
ShareModal.handleSearch has no debounce so a 17-char email = 16
requests in <5s, and generate kept at 5/hour because DiceBear is
free not paid AI; D4 two-extractor shape with defensive THROW
on null/empty userId; D5 uniform 429 message; D6 no new vitest
or playwright specs deferred to fill-vitest-handler-coverage).
As-shipped surface broken into 4 layers (1 lib refactor + 6 route
gates + 1 atomic admin UI fix + 1 rule extension) mirroring the
cors-tighten cleanup's pattern-split shape. Empirical CI metrics
from post-merge run 26382185019 (Playwright smoke 59s 3/3 in
3.8s, forbidden-cors-headers pass, vitest 21/21, lint 128 baseline,
Screenshot diff continue-on-error swallow per Decision 4).
Cross-validation finding: smoke test 2 still passes against the
post-rate-limit preview — that's three convoys in a row (PR #15
Layout default-user, PR #19 CORS-tighten, PR #20 rate-limiting)
where the same 3-test smoke spec defended the auth surface
through sweeping changes. Operator-action-required: none. What
did NOT change audit trail.
.convoys/ship-readiness.md:
- § Status summary at the top flipped from 7/8 to 8/8 RESOLVED.
Header text updated to "Launch-readiness P0 checklist is empty."
P0 #6 row in the table flips from PARTIAL to RESOLVED with the
two-convoy lineage (fix-auth-bypass Brief 4 + add-rate-limiting).
Trailing paragraph rewritten as a milestone note: security gate
closed; remaining launch work is P1 quality bar + P2/P3 polish.
- P0 #6 entry flipped from PARTIAL to RESOLVED with the full
add-rate-limiting as-shipped block (8 sub-bullets covering the
lib refactor shape, the per-class table, the defensive THROW,
the three import routes' auth-gating, the atomic admin UI fix
and WHY, the rule extension, the 6 decisions, and the diff
breakdown). Brief 4's 2026-05-23 partial is preserved as the
prior as-shipped layer to maintain the audit trail.
- Launch sequence step 4 marked RESOLVED 2026-05-24 with the
squash commit + smoke metrics inline.
- Queued convoys: removed the add-rate-limiting entry (it shipped).
Added a new delete-dead-lorcana-import entry (P3 polish; the
Lorcana import route was gated defensively in PR #20 despite
zero current frontend callers — pages/admin/card-import.js's
<select> only offers mtg + pokemon — so if Lorcana stays
permanently out of the admin UI, this is the cleanup PR).
Added three "flagged but kept out of scope" follow-ups per the
convoy's § What did NOT change: harden-multipart-parser (P2;
5MB body still consumed before the 429 path on avatar.js),
god-function-split / refactor-cards-search-sql (P2; 240-line
7-branch SQL in cards/search.js), and withAdmin(handler) wrapper
extraction (P3 DX; the three import routes are call sites #3-5
in the codebase but uniform inline shape was preserved for
convoy atomicity). Updated tighten-visual-diff-path-filter to
note PR #20 also tripped the same false-positive.
AGENTS.md:
- Gotcha #12 extended end-to-end. Was the single-class auth-only
lib + the env-var contract; is now the 5-class reality with a
full per-class table (helper / limit-window / key / routes),
the defensive THROW pattern in extractUserIdentifier, the
gate-ordering rule for per-user limiters, and the
auth → admin-role → rate-limit ordering for the three import
routes. Prominent milestone line opens the new content:
"add-rate-limiting convoy (squash 708ef45, PR #20, 2026-05-24)
closed P0 #6 — all 8 P0s now RESOLVED." Original env-var
contract paragraph (KV_REST_API_URL / KV_REST_API_TOKEN,
fail-closed-in-prod / warn-and-noop-in-dev) is preserved
verbatim above the new content.
- § 6 Testing: intentionally untouched (no test surface changed;
vitest 21/21 and smoke 3/3 still apply).
- § 7 Deployment: intentionally untouched (no deployment-shape
changed; same KV_REST_API_* env vars from Brief 4).
.cursor/rules/api-routes.mdc:
- The implementer extended § Rate limiting in PR #20 with the
per-class table + verbatim call shape + gate-ordering rules +
identifier-extraction + uniform 429 + fail-closed env-var
contract + fail-open Upstash-outage behavior. Doc-writer pass
verified completeness; added a one-sentence convoy-attribution
line at the top of § Rate limiting citing the two-convoy
lineage (fix-auth-bypass Brief 4 for the auth class +
add-rate-limiting for the other four classes and 7 newly-gated
routes), mirroring the post-cors-tighten § CORS attribution
shape. No other touch-ups needed.
No changes to: package.json, package-lock.json, lib/rate-limit.js,
pages/**, components/**, scripts/**, test/**, tests/**,
.github/workflows/**, README.md, TESTING_GUIDE.md, playwright.config.js,
eslint.config.mjs.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 00:09:07 -04:00
- **`delete-dead-lorcana-import`** (priority: P3 polish). Delete `pages/api/cards/import-lorcana.js` (and possibly `scripts/import-lorcana.js` ) if Lorcana stays out of the admin UI's `<select>` permanently. Surfaced 2026-05-24 in `add-rate-limiting` Decision 1: the architect ran `rg 'import-lorcana' pages/ components/` and found zero frontend callers — `pages/admin/card-import.js` 's `<select>` only offers `'mtg'` and `'pokemon'` . The route is gated defensively (auth + admin-role + rate-limit) as part of PR #20 so the future Lorcana admin UI path inherits protection automatically, but if Lorcana is never wired in, this is the cleanup convoy. Strictly easier than gating-then-deleting because the gating shape is uniform across all three import routes today (mtg + pokemon + lorcana); a future cleanup only needs to delete the lorcana file + remove the `'lorcana'` enum option from `.cursor/rules/api-routes.mdc` 's import-routes table. Do nothing if Lorcana support gets wired into the admin UI in a feature convoy; cancel the entry then.
- **`tighten-visual-diff-path-filter`** (priority: P3 polish). Tighten `.github/workflows/visual-diff.yml` 's `paths:` filter from `pages/**` to something like `pages/!(api)/**` so API-only PRs don't trigger the visual workflow. Surfaced on PR #19 (`cors-tighten`) which was API-only but triggered `Screenshot diff` anyway, wasting ~55s of CI time. PR #20 (`add-rate-limiting`) re-triggered the same false-positive — same shape, same swallow via `continue-on-error: true` . Low priority — works as designed today, just chatty. One-line YAML tweak; verify the negated-glob syntax against GitHub Actions' [path-filter semantics ](https://docs.github.com/en/actions/using-workflows/triggering-a-workflow#filter-pattern-cheat-sheet ) before merging (the `!()` extglob may need to be expressed differently if Actions uses minimatch without extglob support).
docs: post-convoy cleanup for fix-layout-default-user (+ queue CI-protection follow-up)
Reflects the merged fix-layout-default-user convoy (PR #15) and the
companion CI permissions fix (PR #16) in repo documentation. Also
queues the new fix-vercel-deployment-protection-in-ci convoy that
PR #16 exposed.
.convoys/ship-readiness.md:
- P0 #7: mark RESOLVED 2026-05-24 with squash commit ca302a8.
Document the as-shipped Layout default-null change, the 7-page
sweep, the 5 new regression-lock vitest assertions, and the
queued follow-ups (single-auth-provider, MobileNavigation
cleanup) that stayed explicitly out of scope.
- Queued convoys: add fix-vercel-deployment-protection-in-ci
(P2, CI infra) — PR #16's permissions fix exposed that Vercel
Deployment Protection 401s anonymous CI requests; needs a
bypass-secret plumb to land cleanly. New section also captures
other in-flight follow-ups (rotate-default-admin, cors-tighten,
add-rate-limiting, purge-weak-creds-from-helpers,
single-auth-provider, cleanup-mobile-nav-dead-props,
bump-eslint-10) so the audit trail is centralized.
AGENTS.md:
- § 4 Gotcha #8: mark RESOLVED with commit ref ca302a8. Mirror
the convention used by the prior cleanup commits for #2, #3,
#4, #5 (entry kept, not renumbered).
- § 3 Key conventions: add a new "Layout user prop" bullet
documenting the new default-null + logged-out-CTA contract so
the convention is discoverable from the conventions list, not
just the resolved-gotcha entry.
.convoys/fix-vercel-deployment-protection-in-ci.md (new):
- Queued scaffold. Operator must seed
VERCEL_AUTOMATION_BYPASS_SECRET as a repo secret before the
implementer can run. Decisions to ratify (query param vs.
header), known constraints, acceptance criteria, and
out-of-scope all enumerated.
No changes to: package.json, lib/**, pages/**, components/**,
scripts/**, .github/**, README.md, .cursor/rules/**.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 15:37:39 -04:00
- **`purge-weak-creds-from-helpers`** (priority: P2 hygiene). Sweep `scripts/reset-db.js` , `scripts/create-test-users.js` , and `TESTING_GUIDE.md` for the literal `admin@tcgvault.com` / `admin123` references. May fold into `pick-a-name` since the email itself is changing.
- **`single-auth-provider`** (priority: P1 quality, also listed as launch sequence step 9). Collapse `lib/auth-context.js` + `lib/admin-auth.js` into `lib/use-auth.js` . Surfaced again as a follow-up in P0 #7 § Flagged-but-deferred (4 pages still import the legacy `useAuth` ).
- **`cleanup-mobile-nav-dead-props`** (priority: P3 polish). `components/MobileNavigation.js` accepts a dead `user` prop; remove it. Surfaced in P0 #7 § Flagged-but-deferred. May fold into `god-component-split` (P2 #13 ) if that lands first.
- **`bump-eslint-10`** (priority: P2 hygiene; upstream-blocked). Bump ESLint from v9 to v10 once `typescript-eslint` ships a v10-tested release and `eslint-config-next` bundles it. Surfaced in `.convoys/bump-next-js.md` § Decisions D.
docs: post-convoy cleanup for add-rate-limiting — MILESTONE, last P0 closed
Reflects the merged add-rate-limiting convoy (PR #20, squash commit
708ef45) in repo documentation. **This is the milestone cleanup** —
add-rate-limiting closed P0 #6 (No rate limiting anywhere), the LAST
open P0 ship-blocker. `.convoys/ship-readiness.md`'s § Status summary
flips from "7 of 8 RESOLVED; 1 remains" to **"8 of 8 RESOLVED.
Launch-readiness P0 checklist is empty."** One brief in the convoy:
Brief 1 shipped as planned with no scope expansions and no implementer
deviations from the verbatim spec; all six architect decisions ratified
verbatim at gate 1 (D1 operator-ratified Option A; D2-D6
architect-self-ratified).
.convoys/add-rate-limiting.md:
- frontmatter status: in-progress -> shipped (added shipped: 2026-05-24)
- new ## As-shipped section. Opens with the milestone language
pointing back at ship-readiness.md's flipped § Status summary.
Decisions section captures all 6 ratifications (D1 operator-
ratified Option A — Critical: WHY the atomic admin UI fix in
pages/admin/card-import.js was Decision 1's hidden coupling
requirement, since API gating alone would have broken every
"Import Cards" click; D2 hybrid named-limiter shape with
Map<className, Ratelimit> cache; D3 per-class table including
the two D3 tuning-evidence raises — search 30 -> 60/min because
ShareModal.handleSearch has no debounce so a 17-char email = 16
requests in <5s, and generate kept at 5/hour because DiceBear is
free not paid AI; D4 two-extractor shape with defensive THROW
on null/empty userId; D5 uniform 429 message; D6 no new vitest
or playwright specs deferred to fill-vitest-handler-coverage).
As-shipped surface broken into 4 layers (1 lib refactor + 6 route
gates + 1 atomic admin UI fix + 1 rule extension) mirroring the
cors-tighten cleanup's pattern-split shape. Empirical CI metrics
from post-merge run 26382185019 (Playwright smoke 59s 3/3 in
3.8s, forbidden-cors-headers pass, vitest 21/21, lint 128 baseline,
Screenshot diff continue-on-error swallow per Decision 4).
Cross-validation finding: smoke test 2 still passes against the
post-rate-limit preview — that's three convoys in a row (PR #15
Layout default-user, PR #19 CORS-tighten, PR #20 rate-limiting)
where the same 3-test smoke spec defended the auth surface
through sweeping changes. Operator-action-required: none. What
did NOT change audit trail.
.convoys/ship-readiness.md:
- § Status summary at the top flipped from 7/8 to 8/8 RESOLVED.
Header text updated to "Launch-readiness P0 checklist is empty."
P0 #6 row in the table flips from PARTIAL to RESOLVED with the
two-convoy lineage (fix-auth-bypass Brief 4 + add-rate-limiting).
Trailing paragraph rewritten as a milestone note: security gate
closed; remaining launch work is P1 quality bar + P2/P3 polish.
- P0 #6 entry flipped from PARTIAL to RESOLVED with the full
add-rate-limiting as-shipped block (8 sub-bullets covering the
lib refactor shape, the per-class table, the defensive THROW,
the three import routes' auth-gating, the atomic admin UI fix
and WHY, the rule extension, the 6 decisions, and the diff
breakdown). Brief 4's 2026-05-23 partial is preserved as the
prior as-shipped layer to maintain the audit trail.
- Launch sequence step 4 marked RESOLVED 2026-05-24 with the
squash commit + smoke metrics inline.
- Queued convoys: removed the add-rate-limiting entry (it shipped).
Added a new delete-dead-lorcana-import entry (P3 polish; the
Lorcana import route was gated defensively in PR #20 despite
zero current frontend callers — pages/admin/card-import.js's
<select> only offers mtg + pokemon — so if Lorcana stays
permanently out of the admin UI, this is the cleanup PR).
Added three "flagged but kept out of scope" follow-ups per the
convoy's § What did NOT change: harden-multipart-parser (P2;
5MB body still consumed before the 429 path on avatar.js),
god-function-split / refactor-cards-search-sql (P2; 240-line
7-branch SQL in cards/search.js), and withAdmin(handler) wrapper
extraction (P3 DX; the three import routes are call sites #3-5
in the codebase but uniform inline shape was preserved for
convoy atomicity). Updated tighten-visual-diff-path-filter to
note PR #20 also tripped the same false-positive.
AGENTS.md:
- Gotcha #12 extended end-to-end. Was the single-class auth-only
lib + the env-var contract; is now the 5-class reality with a
full per-class table (helper / limit-window / key / routes),
the defensive THROW pattern in extractUserIdentifier, the
gate-ordering rule for per-user limiters, and the
auth → admin-role → rate-limit ordering for the three import
routes. Prominent milestone line opens the new content:
"add-rate-limiting convoy (squash 708ef45, PR #20, 2026-05-24)
closed P0 #6 — all 8 P0s now RESOLVED." Original env-var
contract paragraph (KV_REST_API_URL / KV_REST_API_TOKEN,
fail-closed-in-prod / warn-and-noop-in-dev) is preserved
verbatim above the new content.
- § 6 Testing: intentionally untouched (no test surface changed;
vitest 21/21 and smoke 3/3 still apply).
- § 7 Deployment: intentionally untouched (no deployment-shape
changed; same KV_REST_API_* env vars from Brief 4).
.cursor/rules/api-routes.mdc:
- The implementer extended § Rate limiting in PR #20 with the
per-class table + verbatim call shape + gate-ordering rules +
identifier-extraction + uniform 429 + fail-closed env-var
contract + fail-open Upstash-outage behavior. Doc-writer pass
verified completeness; added a one-sentence convoy-attribution
line at the top of § Rate limiting citing the two-convoy
lineage (fix-auth-bypass Brief 4 for the auth class +
add-rate-limiting for the other four classes and 7 newly-gated
routes), mirroring the post-cors-tighten § CORS attribution
shape. No other touch-ups needed.
No changes to: package.json, package-lock.json, lib/rate-limit.js,
pages/**, components/**, scripts/**, test/**, tests/**,
.github/workflows/**, README.md, TESTING_GUIDE.md, playwright.config.js,
eslint.config.mjs.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-25 00:09:07 -04:00
- **`harden-multipart-parser`** (priority: P2 quality). Surfaced 2026-05-24 in `add-rate-limiting` § Risk list. `pages/api/user/avatar.js` 's `parseMultipartFormData` consumes the 5MB multipart body via `req.on('data')` before any response is sent, so an attacker can still exhaust the 5MB body even on a 429 path from the new `checkUploadRateLimit` gate. Real defense requires moving the parse into a separate edge function or using `read-up-to` semantics. Not a release-blocker — the gate-ordering in PR #20 places the limiter BEFORE the method branches that call `parseMultipartFormData` , so when this hardening lands, the gate ordering is already correct. Surface as P1 only if a real abuse incident occurs.
- **`god-function-split` / `refactor-cards-search-sql` ** (priority: P2 refactor). Surfaced 2026-05-24 in `add-rate-limiting` § Files explicitly out of scope. `pages/api/cards/search.js` has a 240-line god-function shape with 7+ conditional `SELECT * FROM cards WHERE …` branches; the PR #20 rate-limit gate sits at the top of the handler and leaves the SQL byte-identical. Splitting is its own scope (probably one convoy per branch group with `slice_dependencies:` for safe multitask fan-out). Not security-critical; deferred to the P2 lane.
- **`withAdmin(handler)` wrapper extraction** (priority: P3 polish / DX). Surfaced 2026-05-24 in `add-rate-limiting` Decision 1 + § What did NOT change. `.cursor/rules/auth-and-permissions.mdc` notes *"check `user.role === 'admin'` directly; consider extracting `withAdmin()` if a third call site appears"* — the three `cards/import-*.js` routes are the third+fourth+fifth call sites in the codebase, but PR #20 kept the inline shape for uniformity across the three import routes and for the convoy's atomic-close-P0-#6 goal. A future convoy can extract `withAdmin(handler)` to `lib/permission-middleware.js` (or wherever the architect decides) and sweep all 5 admin-role check sites onto it. Pure refactor; no security delta either way.
docs: post-convoy cleanup for adopt-playwright-smoke
Reflects the merged adopt-playwright-smoke convoy (PR #18, squash
commit 7b6f751) in repo documentation. Closes the test-infrastructure
side of P1 #10 step 2 (launch sequence step 10). One commit in the
convoy: Brief 1, with two small lint-baseline-preserving deviations
from the brief's verbatim shape that the implementer report flagged.
.convoys/adopt-playwright-smoke.md:
- frontmatter status: in-progress -> shipped (added shipped:
2026-05-24)
- new ## As-shipped section: operator-ratified Decisions (D1 keep
.ts, D4 defer baselines, D6 simple scripts); two implementer
deviations (removed unused eslint-disable-no-console directive
that would have regressed lint 128 -> 129; placed @playwright/test
first in devDeps for alphabetical correctness); cross-validation
that smoke test 2 ("sign-in page renders") locks in PR #15's
logged-out CTA work in components/Layout.js; empirical metrics
from post-merge run 26376162598 (59s workflow, 3/3 in 2.9s,
0-leak); operator-action-required note pointing at the queued
seed-visual-baselines-on-linux follow-up; What did NOT change
audit trail.
.convoys/ship-readiness.md:
- Queued convoys: new entry seed-visual-baselines-on-linux (Linux-
Docker baseline generation per Decision 4 + Boot-the-brief
Finding 7; Mac-generated baselines would silently overwrite Linux
CI baselines because the custom snapshotPathTemplate has no
{platform} token).
- Queued convoys: new RESOLVED block for adopt-playwright-smoke
(PR #18, 7b6f751) — as-shipped surface, implementer deviations,
empirical metrics (59s workflow / 3/3 in 2.9s / 0 secret leaks),
the PR #15 cross-validation finding, operator-action-required
going forward (the seed-visual-baselines-on-linux follow-up),
flagged-but-deferred items, and ownership trail (3 architect-
self-ratified decisions + 3 operator-ratified).
- Launch sequence step 10: marked RESOLVED 2026-05-24 with the
commit + metrics inline.
- P1 #10 No tests Fix sequence: step 2 marked RESOLVED with the
convoy + metrics ref; step 3 (re-enable test: job in
ci.yml) called out as the next remaining task; step 5 (wire
preview-smoke.yml to the Vercel preview URL) marked RESOLVED
across PR #17 + PR #18 since both contributed.
AGENTS.md:
- Section 6 Testing: rewritten end-to-end. Was "E2E/smoke runner
still pending"; is now "@playwright/test@^1.60.0 wired, two
projects (smoke + visual), npm run test:smoke / test:visual /
test:visual:update". Documents the local-run convention (boot
next dev separately, then BASE_URL=... npm run test:smoke);
the one-time npx playwright install --with-deps chromium step;
the no-baselines-yet state + the Linux-Docker seed command +
the cross-platform mismatch reason (no {platform} token in
snapshotPathTemplate); the CI behavior split (vitest blocking,
smoke on every PR with pipeline:skip-smoke escape hatch,
Screenshot diff path-filtered with the first-red-on-missing-
baseline state documented). Updates vitest coverage count
16 -> 21 (the +5 Layout regression-lock tests from PR #15).
Notes TESTING_GUIDE.md is being eclipsed and will be renamed
to docs/MANUAL_QA.md in a future cleanup convoy.
- Section 7 Deployment: rewrites the Vercel-bypass paragraph from
a single "query param now / header reserved for future" bullet
into a two-shape audit ((1) query param on the wait-action's
path: input per PR #17; (2) HTTP header in playwright.config.js's
use.extraHTTPHeaders per PR #18). Documents the Decision-2
fail-loud-in-CI / warn-in-dev predicate and references Gotcha #12
as the established precedent (lib/rate-limit.js). Picked Section
7 over a new Gotcha because the bypass plumbing is operationally
a deployment concern, not an app pitfall.
No changes to: package.json, package-lock.json, playwright.config.js,
eslint.config.mjs, lib/**, pages/**, components/**, scripts/**,
.github/workflows/**, .cursor/rules/**, README.md,
tests/visual/homepage.spec.ts (JSDoc is already neutral-tense, no
future-tense references to clean up).
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 20:33:43 -04:00
- **`seed-visual-baselines-on-linux`** (priority: P2 CI infra; **operator action required** ). Generate Linux baselines for `tests/visual/__screenshots__/` in the `mcr.microsoft.com/playwright:v1.60.0-noble` Docker image and commit them in a small follow-up PR. Mac-generated baselines would silently overwrite Linux CI baselines because `playwright.config.js` 's custom `snapshotPathTemplate` has no `{platform}` token (Risk R3 + Boot-the-brief Finding 7 in `.convoys/adopt-playwright-smoke.md` ). Until this PR lands, every `Screenshot diff` run on a PR touching `pages/**` / `components/**` / `styles/**` / Tailwind/PostCSS config fails at the test step and posts a "Visual Diff — view run" comment with empty artifacts — that's the documented Decision-4 end state of `adopt-playwright-smoke` , not a regression. One small PR with just the PNG baseline(s). Surfaced 2026-05-24 as the follow-up to `adopt-playwright-smoke` (PR #18 ).
- **`adopt-playwright-smoke`** (priority: P1 quality, also listed as launch sequence step 10 / P1 #10 step 2) — **RESOLVED 2026-05-24** .
- **Resolved by:** squash commit `7b6f751` (PR #18 , architect-commit `3ac527e` , implementer-commit `c72d006` ). Brief 1 shipped as planned with two small lint-baseline-preserving deviations from the brief's verbatim shape (documented in the convoy file's § As-shipped).
- **As-shipped surface:** `@playwright/test@^1.60.0` added to `devDependencies` ; new `playwright.config.js` at repo root (ESM, two projects partitioned by `testMatch` — `smoke` + `visual` , CI-fail-loud / dev-warn predicate on `VERCEL_AUTOMATION_BYPASS_SECRET` per Decision 2, `snapshotPathTemplate: 'tests/visual/__screenshots__/{arg}{ext}'` aligned with `visual-diff.yml` 's artifact upload path); new `tests/visual/homepage.spec.ts` (1 test, no baseline committed per Decision 4); three new `package.json` scripts (`test:smoke`, `test:visual` , `test:visual:update` ); three new `.gitignore` entries (`/playwright-report/`, `/test-results/` , `/.playwright/` ). No `eslint.config.mjs` change (Decision 5 + Finding 2 verified clean empirically). No source touched under `pages/**` / `components/**` / `lib/**` .
- **Implementer deviations** (both behavior-neutral, both lint-baseline-preserving):
1. Removed Brief 1's `// eslint-disable-next-line no-console` directive on `playwright.config.js` 's `console.warn` branch — the current ESLint config does not flag `console.warn` at all, so the disable directive itself would have regressed lint from 128 → 129 as an "Unused eslint-disable directive" error.
2. Placed `@playwright/test` first in `devDependencies` for strict alphabetical correctness — the brief's prose was internally inconsistent on neighbors (`@playwright` sorts lexically before `@testing-library/react` ).
- **As-shipped metrics** (from post-merge `Playwright smoke` run 26376162598 on `main` ):
- `Playwright smoke` workflow total runtime: **59 seconds** , exit 0 (was: fast-fail at "playwright not installed" / "no config" before this convoy).
- `Run smoke tests` step: **3/3 tests pass in 2.9s** against the Vercel preview with `x-vercel-protection-bypass` header applied — `home redirects or renders without 5xx` ✓ 683ms / `sign-in page renders` ✓ 459ms / `public health endpoint responds` ✓ 571ms.
- `Screenshot diff` workflow: **not triggered on PR #18 itself** because its `paths:` filter excludes test-infra-only changes; first real trigger fires on the next PR touching `pages/**` / `components/**` / `styles/**` / `tailwind.config.js` / `postcss.config.js` . At that point the documented Decision-4 end state runs live (test fails on missing baseline → `continue-on-error: true` swallows → comment-on-PR step posts run link with empty artifacts).
- Bypass secret leak check: **0 matches** in the raw workflow log. GitHub Actions auto-masks registered secrets; our Decision-2 branches name the env var but never interpolate the value into any string.
- **Cross-validation finding** (not a planned AC; surfaced organically from CI green): smoke test 2 (`'sign-in page renders'`) asserts `await expect(page.getByRole('button', { name: /sign in/i })).toBeVisible()` against `/login` , which only passes because `components/Layout.js` renders the `<Link href="/login">Sign in</Link>` CTA on the logged-out branch that PR #15 (`fix-layout-default-user`, `ca302a8` ) introduced. P0 #7 's resolved state is now defended by a live CI signal — if a future PR reverts to a hardcoded default user or breaks the CTA wording, smoke fails the PR (in addition to the 5 vitest assertions in `test/components/Layout.test.js` ).
- **Operator action required going forward:** `seed-visual-baselines-on-linux` (above) is the follow-up. Until it lands, `Screenshot diff` runs post a "Visual Diff — view run" comment with empty artifacts on every UI-touching PR — that is the Decision-4 end state, not a regression. No operator action is required to keep `Playwright smoke` green.
- **Flagged-but-deferred** (deliberately out of scope per the convoy file, restated here for the audit trail):
1. `seed-visual-baselines-on-linux` — see above.
2. `adopt-test-smoke-local` (possible follow-up) — a `test:smoke:local` wrapper that auto-boots `next dev` . Explicitly rejected by Decision 6; queue only if dev friction proves out.
3. Deeper E2E coverage beyond the 3 existing smoke checks — per-feature work in feature convoys, not a test-infra concern.
- **Owns:** `role-architect` (3 of 6 decisions self-ratified — D2 CI predicate, D3 two-project shape, D5 no-eslint-change; 3 of 6 operator-ratified — D1 keep `.ts` , D4 defer baselines, D6 simple scripts) → `role-implementer` (Brief 1, plus the two deviations above).
docs: post-convoy cleanup for fix-vercel-deployment-protection-in-ci
Reflects the merged fix-vercel-deployment-protection-in-ci convoy
(PR #17, squash commit 9a3e077) in repo documentation. Closes the
CI-infra side-effect of P0 #7. Three commits in the convoy: Brief 1
(bypass plumbing per spec), plus two scope expansions discovered
during CI validation (shell-injection hardening, and a Decision-A
shape correction to drop the cookie-bypass param).
.convoys/ship-readiness.md:
- Queued convoys: mark fix-vercel-deployment-protection-in-ci as
RESOLVED 2026-05-24 with 9a3e077. Document the 3-commit reality
(365e9f0 Brief 1 bypass plumbing, b6f8688 shell-injection
hardening of the gate Decide step, 043a6ee dropping
&x-vercel-set-bypass-cookie=true), the empirical metrics
(wait-action: 10-min timeout -> 194ms; workflow runtime:
10+ min -> 59s), the documented expected red on Playwright
smoke (npx playwright test fails because playwright.config.js
doesn't exist yet -- adopt-playwright-smoke owns that), and the
operator-rotation caveat (R6).
AGENTS.md:
- Section 7 Deployment: correct the noun "header" -> "query param
on wait-action's path:" since that's what actually landed per
Decision A. Also document the without-cookie form (the cookie
variant 401s through axios's missing cookie jar) and the
operator re-seed runbook for token rotation.
- Section 7 Deployment: fold in a one-liner about the GitHub
Actions ${{ }}-in-shell-is-injection-vector pattern, with the
env: + quoted-shell fix shape. Picked Section 7 over a new
Gotcha #13 because the existing Gotchas list is dominated by
app-level pitfalls (auth, SQL clients, ESLint), and CI YAML
hardening is naturally co-located with deployment.
.convoys/fix-vercel-deployment-protection-in-ci.md:
- frontmatter status: in-progress -> shipped (added shipped:
2026-05-24)
- new ## As-shipped section: 3-commit reality, Decision-A shape
deviation (we shipped without &x-vercel-set-bypass-cookie=true),
empirical timings (194ms wait, 59s total), remaining-red
attribution to adopt-playwright-smoke, and the
operator-rotation caveat.
No changes to: package.json, lib/**, pages/**, components/**,
scripts/**, .github/workflows/**, .cursor/rules/**, README.md.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 17:32:45 -04:00
- **`fix-vercel-deployment-protection-in-ci`** (priority: P2 CI infra) — **RESOLVED 2026-05-24** .
- **Resolved by:** squash commit `9a3e077` (PR #17 ), comprising three commits, not one. Operator prereq seeded 2026-05-24T20:03:31Z (`gh secret set VERCEL_AUTOMATION_BYPASS_SECRET`; confirmed via `gh secret list` ); the implementer dispatch waited on that visibility per the convoy file's "Operator action required" gate.
- **Three-commit reality** (Brief 1 + two scope expansions found during CI validation):
1. `365e9f0` Brief 1 — bypass plumbing per spec. Both `.github/workflows/preview-smoke.yml` and `.github/workflows/visual-diff.yml` got the same shape change: `wait-for-vercel-preview@v1.3.2` 's `path:` input now carries `/?x-vercel-protection-bypass=${{ secrets.VERCEL_AUTOMATION_BYPASS_SECRET }}&x-vercel-set-bypass-cookie=true` (Decision A's original cookie-variant shape — later corrected in commit 3); `max_timeout: 600 → 120` (Decision B); the `gate:` job's Decide step short-circuits on `github.event.pull_request.head.repo.fork == true` with a `::notice::` annotation, before the existing PR-body skip directive runs (Decision D); and the Playwright/screenshot step exports `VERCEL_AUTOMATION_BYPASS_SECRET` as `env:` for forward-compat with `adopt-playwright-smoke` .
2. `b6f8688` shell-injection hardening (latent pre-existing bug surfaced by PR #17 's own CI validation). Decision D's gate step inlined `${{ github.event.pull_request.body }}` directly into bash, which broke when the PR body contained shell metacharacters like `(` or backticks. PR #17 's own description bit this with `"unexpected token \` ('"` because of phrasing like *"(was: 10-minute timeout)"* . Fix is the standard GitHub Actions hardening pattern (their official "Security hardening" guide flags inline `${{ }}` in shell as both a syntax-error risk and a shell-injection vector): route the body and the fork flag through the step's `env:` block as `PR_BODY` and `PR_IS_FORK` , then quote them as `"$PR_BODY"` / `"$PR_IS_FORK"` in the shell condition. Same change in both workflows; ~9 LOC each. Documented in commit message as technically beyond Brief 1's scope but bundled into the convoy because the bug actively blocked Brief 1's success criterion from being validated.
3. `043a6ee` drop `&x-vercel-set-bypass-cookie=true` from the wait-action `path:` — corrects Decision A's exact shape. With the cookie variant, Vercel responds **307 + Set-Cookie** , and axios in Node has no cookie jar — it follows the redirect to the bare URL without the cookie, which then 401s. Empirically confirmed by operator's local curl: bare `?x-vercel-protection-bypass=X` → HTTP/2 200, while `?x-vercel-protection-bypass=X&x-vercel-set-bypass-cookie=true` → HTTP/2 307 (the broken path). For a one-shot healthcheck the per-request bypass query is enough. The cookie variant stays reserved for the future Playwright config (`adopt-playwright-smoke`) where a real browser cookie jar exists. An inline comment in `preview-smoke.yml` explains this so the next agent doesn't accidentally re-add the cookie param.
- **As-shipped metrics** (from PR #17 's CI run, post-validation):
- `Wait for Vercel Preview deployment` step elapsed: **194 milliseconds** (was: 10-minute timeout before this convoy).
- `Playwright smoke` workflow total runtime: **59 seconds** (was: 10+ minutes).
- Step breakdown: `Wait for Vercel Preview deployment` → success in 194ms; `npm ci` , `setup-node` , `playwright install` → success; `Run smoke tests` → **failure** (expected — see next bullet).
- `Screenshot diff` workflow: not triggered on PR #17 itself because its path filter excludes workflow-only changes; will fire on the next PR touching `pages/**` / `components/**` / `styles/**` / Tailwind/PostCSS config.
- **Documented expected red**: `Playwright smoke` now reaches `npx playwright test` and fast-fails because `playwright.config.js` doesn't exist in the tree yet. That is `adopt-playwright-smoke` 's scope (P1 #10 step 2 / launch sequence step 10), not this convoy's. Per the convoy file's Test plan § and Brief 1 acceptance criterion #1 , a real downstream failure with the wait-action reaching `Received success status code` first counts as success for this convoy — the failure mode shifted from "401 timeout in the wait step" to "playwright not installed", which is precisely the target state.
- **Operator-rotation caveat** (R6 in the convoy file). The Vercel bypass token does not auto-expire. If/when it's rotated from the Vercel dashboard, the operator must re-seed the GitHub secret via `gh secret set VERCEL_AUTOMATION_BYPASS_SECRET --body "<new value>"` . Same human-responsibility pattern as `JWT_SECRET` rotation; not preventable from workflow YAML. No automation here.
- **Flagged-but-deferred** (from the convoy file's "Anything flagged but not acted on" section, unchanged at merge):
1. `replace-wait-for-vercel-preview` — the wait-action's last release was Mar 2024; could be replaced with a few lines of `gh api` + `curl` -loop. Out of scope for this convoy; queue if the action ages out further or gets a security advisory.
2. `adopt-playwright-smoke` — owns the actual `playwright.config.js` , `tests/smoke/` , and `@playwright/test` dep. The bypass plumbing here is forward-compat for that convoy (env var available on the smoke step). Listed in P1 #10 step 2 / launch sequence step 10 above.
3. `Screenshot diff` baseline authoring — orthogonal scope; the visual-diff workflow has nothing to compare against on its first real run.
- **Owns:** `role-architect` (3 Decisions ratified — A query-param, B 120s timeout, D fork-PR skip) → `role-implementer` (Brief 1) + two scope-expansion commits.
docs: post-convoy cleanup for fix-layout-default-user (+ queue CI-protection follow-up)
Reflects the merged fix-layout-default-user convoy (PR #15) and the
companion CI permissions fix (PR #16) in repo documentation. Also
queues the new fix-vercel-deployment-protection-in-ci convoy that
PR #16 exposed.
.convoys/ship-readiness.md:
- P0 #7: mark RESOLVED 2026-05-24 with squash commit ca302a8.
Document the as-shipped Layout default-null change, the 7-page
sweep, the 5 new regression-lock vitest assertions, and the
queued follow-ups (single-auth-provider, MobileNavigation
cleanup) that stayed explicitly out of scope.
- Queued convoys: add fix-vercel-deployment-protection-in-ci
(P2, CI infra) — PR #16's permissions fix exposed that Vercel
Deployment Protection 401s anonymous CI requests; needs a
bypass-secret plumb to land cleanly. New section also captures
other in-flight follow-ups (rotate-default-admin, cors-tighten,
add-rate-limiting, purge-weak-creds-from-helpers,
single-auth-provider, cleanup-mobile-nav-dead-props,
bump-eslint-10) so the audit trail is centralized.
AGENTS.md:
- § 4 Gotcha #8: mark RESOLVED with commit ref ca302a8. Mirror
the convention used by the prior cleanup commits for #2, #3,
#4, #5 (entry kept, not renumbered).
- § 3 Key conventions: add a new "Layout user prop" bullet
documenting the new default-null + logged-out-CTA contract so
the convention is discoverable from the conventions list, not
just the resolved-gotcha entry.
.convoys/fix-vercel-deployment-protection-in-ci.md (new):
- Queued scaffold. Operator must seed
VERCEL_AUTOMATION_BYPASS_SECRET as a repo secret before the
implementer can run. Decisions to ratify (query param vs.
header), known constraints, acceptance criteria, and
out-of-scope all enumerated.
No changes to: package.json, lib/**, pages/**, components/**,
scripts/**, .github/**, README.md, .cursor/rules/**.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-24 15:37:39 -04:00
bootstrap: agent pipeline v0.5.0 + ship-readiness review
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 <cursoragent@cursor.com>
2026-05-23 00:16:08 -04:00
## Self-analytics
After each convoy, `scripts/log-convoy-event.sh` emits a record to `.convoys/.metrics.jsonl` (gitignored). After 3-5 convoys, run the upstream `agent-pipeline/analytics/` aggregator to see where token spend goes — that data feeds whether to add or remove rules.
## How to start
Per `.cursor/agents/role-conductor.md` , start the next convoy with:
> *"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.