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>
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>
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>
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>
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>
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>
The throwaway bootstrap PR exposed three pre-existing issues that
weren't visible before the pipeline was installed:
1. ESLint had no config (`.eslintrc.json` missing) even though the
`lint` script and deps were both present. `next lint` was prompting
interactively in CI. Added `.eslintrc.json` extending
`next/core-web-vitals` (Next.js Strict).
2. Running lint surfaced ~100 pre-existing errors, including several
real bugs (conditional React hook calls in components/pages).
Marked the CI lint job `continue-on-error: true` with an explicit
TODO so PRs aren't blocked while a follow-up convoy
(fix-lint-baseline) cleans up the codebase. Lint output is still
visible in PR logs.
3. Vercel is platform-blocking every deployment with "Vulnerable
version of Next.js detected" — locked at 15.4.3, latest is 16.2.6.
The last successful Vercel deploy on main was 2025-08-01. Until
Next.js is bumped, every preview-smoke / visual-diff gate is
non-functional. Added as P0 #8 with a new `bump-next-js` convoy at
the front of the launch sequence.
Updated `.convoys/ship-readiness.md`:
- P0 #8: Vercel deploy blocked by Next.js CVE
- P1 #11.5: pre-existing lint baseline
- Launch sequence: prepend `bump-next-js` at step 0, add
`fix-lint-baseline` at step 3.5
Co-authored-by: Cursor <cursoragent@cursor.com>