deckhearth/.convoys
Randall Stillwell 3022e80c04 refactor(auth): collapse lib/auth-context.js + lib/admin-auth.js onto lib/use-auth.js
`lib/use-auth.js` is now the sole client-side auth surface (P1 §9 of
`.convoys/ship-readiness.md`). The legacy `lib/auth-context.js`
(`AuthProvider` + `useAuth`) and `lib/admin-auth.js` (`AdminProvider` +
`useAdmin` + `useIsAdmin`) are deleted; every importer is migrated to
the canonical hook. Pre-convoy a worst-case page mount issued THREE
identical `GET /api/auth/verify` requests (one per provider/hook); the
post-convoy floor is one verify per page mount (3 → 1 on
`pages/card/[id].js`, 2 → 1 elsewhere).

Importer inventory swept (7 source files):
- `pages/_app.js` — removed `<AuthProvider>` wrapper; `<ThemeProvider>`
  is now the only top-level provider. `lib/use-auth.js` is hook-only,
  no replacement provider needed.
- `pages/index.js`, `pages/scanner.js`, `pages/decks.js`,
  `pages/deck/[id].js`, `pages/deck-builder.js` — `import { useAuth }`
  path swap from `../lib/auth-context` to `../lib/use-auth`. All five
  pages destructured only `{ user }` or `{ user, loading }`; verified
  no consumer reads `login` / `register` from useAuth (those flows are
  in `pages/login.js` / `pages/signup.js` which call the API directly),
  so no shape-parity gap on `lib/use-auth.js`.
- `pages/card/[id].js` — replaced `useIsAdmin()` (the only consumer of
  `lib/admin-auth.js` anywhere in the tree) with synchronous
  `user?.role === 'admin'` derived from the existing `useAuth()` call.
  Render condition at line 524 stays byte-identical.

Decisions documented in `.convoys/single-auth-provider.md`:
- D1: no extension to `lib/use-auth.js` (zero call sites for `login` /
  `register` from useAuth — those flows are direct fetches in
  `login.js` / `signup.js`).
- D2: `useIsAdmin()` collapses onto `useAuth()`; no separate hook.
- D3: provider tree `<ThemeProvider><AuthProvider>{children}</AuthProvider></ThemeProvider>`
  → `<ThemeProvider>{children}</ThemeProvider>`.
- D4: 3 → 1 verify roundtrip on `card/[id].js`; 2 → 1 on every other
  page-load.
- D5: zero test files modified; the 21-test vitest suite is server-
  side or prop-driven (`Layout.test.js` passes `user` as a prop, never
  imports the legacy hooks).

Doc / config updates so the deletion lands cleanly:
- `.github/CODEOWNERS` — drop the two CODEOWNERS lines for the deleted
  files.
- `AGENTS.md` § 2 architecture row + § 3 "Auth (client)" bullet —
  rewritten for the post-convoy single-surface state.
- `.cursor/rules/auth-and-permissions.mdc` — § "Legacy" reframed to
  "deleted by this convoy"; § "Authentication state on the client"
  updated to the post-convoy `useAuth()` shape and the direct-fetch
  login flow used by `login.js` / `signup.js`.
- `.cursor/rules/no-go-zones.mdc` — auth-refactors bullet drops the
  deleted files from the canonical list.
- `.cursor/skills/add-page/SKILL.md` — checklist + anti-pattern row
  refer to the deletion.

Verification:
- `rg "lib/auth-context|lib/admin-auth" --type js` → 0 hits in source.
- `npm run lint` → 128 → 125 problems (3 fewer errors from the deleted
  unused-import lines; no regression).
- `npm run test:run` → 21/21 pass (including the 5 Layout regression
  locks from `fix-layout-default-user`, which are prop-driven and
  unaffected).
- `npm run build` → all 26 pages compile end-to-end; no SSR / static-
  generation breakage that would have surfaced if a page tried to use
  the legacy context hook unwrapped.
- Manual smoke deferred to operator post-merge per convoy doc.

Risks (full discussion in convoy file):
- R1 shape parity gap — verified zero consumers of legacy-only
  surface; mitigated.
- R2 SSR mismatch from removing `<AuthProvider>` — `useEffect`-
  guarded `localStorage` read; identical SSR shape pre/post; build
  passes.
- R3 missed importer — post-delete grep + build pass would surface
  any miss.
- R5 stale `useAuth` cache across components — pre-existing
  pattern, called out as follow-up rather than addressed here.

Out of scope: any change to `lib/permission-middleware.js` (server-
side; resolved P0 #1), `lib/auth-secret.js` (resolved P0 #2),
`pages/api/**` route handlers, login / register API contracts, or
the seeded admin account flow.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-05-26 22:54:35 -05:00
..
add-rate-limiting feat(security): rate-limit search/upload/import + gate import routes (P0 #6 - closes last P0) 2026-05-24 22:59:59 -05:00
adopt-playwright-smoke feat(test): adopt @playwright/test + ship playwright.config.js + visual scaffold (P1 #10 step 2) 2026-05-24 19:25:18 -05:00
bump-next-js convoy(bump-next-js): plan + brief 1 (Decisions A-D) 2026-05-23 02:31:26 -05:00
cors-tighten fix(security): drop wildcard CORS + redundant OPTIONS from 24 API routes (P0 #5) 2026-05-24 20:41:38 -05:00
drop-public-setup architect(drop-public-setup): expand scope with brief 2 (CJS→ESM) 2026-05-23 17:02:44 -05:00
fix-auth-bypass docs: post-convoy cleanup for fix-auth-bypass 2026-05-23 12:22:50 -05:00
fix-layout-default-user fix(layout+pages): default user=null + page audit sweep (P0 #7) (#15) 2026-05-24 14:31:37 -05:00
fix-vercel-deployment-protection-in-ci fix(ci): plumb VERCEL_AUTOMATION_BYPASS_SECRET into preview-smoke + visual-diff (#17) 2026-05-24 16:26:22 -05:00
pick-a-name feat(brand): unify on Deck Hearth across in-repo strings + infra (P1 brand decision) 2026-05-25 02:28:29 -05:00
add-rate-limiting.md docs: post-convoy cleanup for add-rate-limiting — MILESTONE, last P0 closed 2026-05-24 23:09:07 -05:00
adopt-playwright-smoke.md docs: post-convoy cleanup for adopt-playwright-smoke 2026-05-24 19:33:43 -05:00
bump-next-js.md convoy(bump-next-js): plan + brief 1 (Decisions A-D) 2026-05-23 02:31:26 -05:00
cors-tighten.md docs: post-convoy cleanup for cors-tighten 2026-05-24 20:49:30 -05:00
drop-public-setup.md architect(drop-public-setup): expand scope with brief 2 (CJS→ESM) 2026-05-23 17:02:44 -05:00
fix-auth-bypass.md docs: post-convoy cleanup for fix-auth-bypass 2026-05-23 12:22:50 -05:00
fix-layout-default-user.md fix(layout+pages): default user=null + page audit sweep (P0 #7) (#15) 2026-05-24 14:31:37 -05:00
fix-reset-db-script.md docs: post-convoy cleanup for fix-reset-db-script 2026-05-26 22:10:13 -05:00
fix-vercel-deployment-protection-in-ci.md docs: post-convoy cleanup for fix-vercel-deployment-protection-in-ci 2026-05-24 16:32:45 -05:00
pick-a-name.md docs: post-convoy cleanup for pick-a-name — first post-P0 P1 convoy 2026-05-25 04:10:12 -05:00
README.md bootstrap: agent pipeline v0.5.0 + ship-readiness review 2026-05-23 02:31:26 -05:00
ship-readiness.md docs: post-convoy cleanup for fix-reset-db-script 2026-05-26 22:10:13 -05:00
single-auth-provider.md refactor(auth): collapse lib/auth-context.js + lib/admin-auth.js onto lib/use-auth.js 2026-05-26 22:54:35 -05:00

Convoys

A convoy is a multi-PR work-stream coordinated by an agent pipeline. One convoy = one feature, bug fix, or epic. Each convoy is a Markdown file in this directory plus an optional sub-directory of implementer briefs.

File layout

.convoys/
├── README.md                              (this file)
├── <slug>.md                              (the convoy file — written by role-conductor)
└── <slug>/
    ├── brief-1-<kebab-title>.md           (written by role-architect)
    ├── brief-2-<kebab-title>.md
    └── ...

Convoy file format

Frontmatter (set by role-conductor, then appended-to by other roles):

---
name: <kebab-slug>
classification: feature | hotfix | docs-only | infra-only | server-only | config-only
success_metric: <one sentence>
skip:
  - <flag1>
status: open | in-progress | merged | shipped | abandoned
created: <YYYY-MM-DD>
---

Body sections (added in order by the pipeline roles):

  1. ## Why (Conductor)
  2. ## Scope (Conductor)
  3. ## Roles invoked (Conductor)
  4. ## Todos (Conductor → refined by Architect)
  5. ## IA (IA Architect)
  6. ## UX (UX Reviewer)
  7. ## Architecture (Architect)

After Architect, briefs live in .convoys/<slug>/brief-N-*.md. Implementers read only their brief, not the whole convoy.

Skip flags

The Conductor sets skip: based on classification. These flags map to pipeline stages that no-op when set:

Flag Skips
ia IA Architect
ux UX Reviewer
arch Architect
test Component tests
review Reviewer
visual Visual diff
a11y A11y auditor
design Design-system auditor
smoke Staging smoke
qa Manual QA
docs Doc Writer
flag Flag rollout

Never skipped (mandatory human gates): plan-approval, pr-merge, prod-promote.

Status lifecycle

  • open — Conductor created the convoy; no work started.
  • in-progress — At least one brief has an open or merged PR.
  • merged — All briefs merged to umbrella; release PR to develop pending.
  • shipped — Release to main complete; flag rollout (if any) underway.
  • abandoned — Convoy closed without shipping; reason in convoy body.

Update status by editing the convoy frontmatter as you progress.

Adding a new convoy

  1. Open Cursor in this repo.
  2. Prompt: "Start a new convoy: . Success = ."
  3. The role-conductor subagent writes .convoys/<slug>.md.
  4. Run subsequent roles in order per the convoy's Roles invoked list.

See .cursor/agents/role-conductor.md for the Conductor's full spec.

Multitask + worktrees (Cursor 3.2+)

Cursor 3.2 (Apr 24, 2026) added /multitask async subagents and native worktree management in the Agents Window. The pipeline uses both:

Audit fan-out — after an implementer ships a PR draft:

/multitask role-reviewer + role-design-system-auditor + role-a11y-auditor

All three read the same diff and emit independent comments. Use group id audit-<convoy>-<pr> so analytics can compute wall-clock savings.

Implementer fleet — after architect's plan is approved (gate 1), if slice_dependencies: declares parallel-safe briefs (depends_on: [], disjoint files:):

/multitask role-implementer briefs 1, 2, 3

Use Cursor's Agents Window to create a worktree per brief — one click each. The legacy scripts/wt.sh is now a deprecation stub.

See the multitask playbook for the full guardrail set.

Self-analytics

Each L2 role appends one event to .convoys/.metrics.jsonl via scripts/log-convoy-event.sh. The file is gitignored by default — events stay local. To opt-in to commit team-shared metrics, remove .convoys/.metrics.jsonl from .gitignore.

Aggregate across repos and render a dashboard with the agent-pipeline analytics scripts:

cd ~/code/agent-pipeline/analytics
npx tsx analyze-convoys.ts <repo-path> [<repo-path>...]
npx tsx render-dashboard.ts
open ~/agent-pipeline-data/dashboard.html

Schema: analytics/schemas/convoy-event.json.