Initial Commit
This commit is contained in:
74
openspec/archive/2026-05-19-add-react-auth-forms/proposal.md
Normal file
74
openspec/archive/2026-05-19-add-react-auth-forms/proposal.md
Normal file
@@ -0,0 +1,74 @@
|
||||
# Add @suaveplan/react-auth — forms (Wave 2)
|
||||
|
||||
**Package**: `@suaveplan/react-auth`
|
||||
**Status**: PROPOSED
|
||||
**Type**: feature
|
||||
|
||||
- **Spec delta**: ./specs/react-auth/spec.md
|
||||
|
||||
## Why
|
||||
|
||||
Wave 1 shipped the `@suaveplan/react-auth` core (client, provider, context, hooks, guard, translations data) but explicitly deferred the form components to Wave 2. Downstream apps need shipping login/register/forgot-password/reset-password/email-verification surfaces to wire up the Better Auth flow. Wave 2 layers the five pure-UI form components on top of Wave 1's core, consuming the existing `enGb`/`cyGb`/`zhCn` translation packs (which Wave 1 sized for this exact purpose) and `@suaveplan/react-ui` primitives. No Wave 1 surface is modified — these forms are purely additive. The upstream enterprise audit (`../suaveplan/enterprise/react-auth.md` §2.32, §3.4, §5.5) called out the absence of `.test.tsx` coverage for these five components as the dominant test gap in the package; Wave 2 closes that gap by shipping 100% covered, accessible, schema-validated forms.
|
||||
|
||||
## What changes
|
||||
|
||||
- ADDED: `LoginForm` — pure UI form with email/password/remember-me, password show/hide, react-hook-form + Zod, "forgot password?" + "sign up" links.
|
||||
- ADDED: `RegisterForm` — pure UI form with email/username/password+confirm/optional name/dob/sex/gender/company, password show/hide on both fields, react-hook-form + Zod with cross-field `confirmPassword` refinement.
|
||||
- ADDED: `ForgotPasswordForm` — pure UI form with email field, success view (with "send another" + "back to login"), react-hook-form + Zod.
|
||||
- ADDED: `ResetPasswordForm` — pure UI form with new password + confirm, success view, invalid-token view, react-hook-form + Zod with cross-field refinement.
|
||||
- ADDED: `EmailVerificationView` — pure UI status view rendering `verifying` / `success` / `expired` / `error` states with appropriate icon, copy, CTA, and `back-to-login` link.
|
||||
- ADDED: Form-scoped subpath exports under `./components/<form>` for every form module, mirroring Wave 1's per-component subpath convention.
|
||||
- ADDED: New translation keys consumed by the forms — every form accepts an optional `translations?: Partial<AuthLoginStrings | …>` prop defaulting to the Wave 1 `enGb.auth.<group>` table so existing `cyGb` / `zhCn` packs work end-to-end with zero new strings (the Wave 1 packs were sized to cover Wave 2 form copy).
|
||||
|
||||
## Out of scope
|
||||
|
||||
- Page components (`Login`, `Register`, `ForgotPassword`, `ResetPassword`, `EmailVerify`, `Profile`) — deferred to Wave 3.
|
||||
- `PermissionGate` component — deferred to Wave 3.
|
||||
- MFA / passkey / magic-link flows — enterprise audit §4 enhancements, not in any current wave.
|
||||
|
||||
### Excluded from this migration
|
||||
|
||||
- **Page components** (`pages/login`, `pages/register`, `pages/forgot-password`, `pages/reset-password`, `pages/email-verification`, `pages/profile`) — Deferred to Wave 3 — scope-bounded migration to keep ship velocity at 100/100/100/100 quality. Pages wrap the Wave 2 forms with `useAuth` action wiring + `useNavigate` redirects.
|
||||
- **`PermissionGate` component** — Deferred to Wave 3 — scope-bounded migration; ships next to the Wave 3 pages because the page surfaces are its primary consumer.
|
||||
- **MFA / passkey / magic-link surfaces** (`MfaChallengePage`, `MfaEnrolmentPage`, `PasskeyRegistrationPage`, `PasskeyLoginPage`) — Enterprise audit §4 enhancements, never part of upstream — out of scope for every current wave.
|
||||
|
||||
## Source mapping
|
||||
|
||||
This change derives from `../suaveplan/enterprise/react-auth.md` (§1, §2.25, §2.30–32, §3.4, §5.5) and `../suaveplan/packages/react/react-auth/src/components/`.
|
||||
|
||||
| Requirement | Source lines | Notes |
|
||||
|---|---|---|
|
||||
| `LoginForm` shape | `components/login-form/login-form.tsx:1-210` | Verbatim semantics; `resolver: zodResolver(...) as any` cast removed via typed resolver narrowing — no `any` survives §2. |
|
||||
| `RegisterForm` shape | `components/register-form/register-form.tsx:1-366` | Verbatim semantics; same `any` removal; split into `register-form.tsx` + `register-form-fields.tsx` to stay under the 300-line hard cap (§3). |
|
||||
| `ForgotPasswordForm` shape | `components/forgot-password-form/forgot-password-form.tsx:1-190` | Verbatim semantics; same `any` removal. |
|
||||
| `ResetPasswordForm` shape | `components/reset-password-form/reset-password-form.tsx:1-277` | Verbatim semantics; same `any` removal. |
|
||||
| `EmailVerificationView` shape | `components/email-verification-view/email-verification-view.tsx:1-220` | Reworded: upstream consumed `useTranslation` from `@suaveplan/react-translate`; Wave 1 settled this package as no-react-translate (`enterprise/react-auth.md §5.5` migration impact note in Wave 1 proposal). Wave 2 follows that precedent — the view accepts a `translations?: Partial<AuthEmailVerificationStrings>` prop defaulting to `enGb.auth.emailVerification`. Callers wanting `cyGb` / `zhCn` pass that pack's slice. |
|
||||
| 5 sibling `.test.tsx` files | `enterprise/react-auth.md §3.4 / §5.5` | Upstream gap — these tests do not exist in upstream and are added in this wave per the audit. |
|
||||
|
||||
### Requirements with no source line
|
||||
|
||||
| Requirement | Justification |
|
||||
|---|---|
|
||||
| Zod-validated `<FormName>Props` boundary on every form | Genesis rule §0a — Zod boundary on every public option bundle. Upstream had `Props` interfaces but no runtime validation. |
|
||||
| `react-hook-form` typed resolver, no `as any` cast | Genesis rule §2 — `any` is banned. Upstream's `resolver: zodResolver(schema) as any` is replaced with a typed wrapper. |
|
||||
| 100% statement / branch / function / line coverage | Genesis-wide policy, not package-specific. |
|
||||
| Sibling `<form>.md` per implementation file | Genesis rule §4. |
|
||||
| Sibling `<form>.stories.tsx` per component | CLAUDE.md: "Every React component needs `.stories.tsx`." Upstream ships stories for pages / guards but not for these forms. |
|
||||
| `translations?: Partial<...>` prop on every form, defaulting to `enGb.auth.<group>` | Genesis-specific — Wave 1 dropped `@suaveplan/react-translate` and parked translations as static packs. Wave 2 wires those packs in via prop injection to preserve the three-locale surface without re-introducing the dropped dep. |
|
||||
|
||||
## Verification approach
|
||||
|
||||
Every form gets a sibling `.test.tsx` that uses `@suaveplan/testing/runner` and `@testing-library/react` with real `react-hook-form` + real Zod. No mocks. Each test file covers:
|
||||
|
||||
- `src/components/login-form/login-form.test.tsx` — submit happy path, Zod validation errors (empty email, short password, bad email), password show/hide toggle, `error` prop renders alert, `isLoading` disables submit, `translations` prop overrides English defaults.
|
||||
- `src/components/register-form/register-form.test.tsx` — same matrix + username regex, `confirmPassword` mismatch, optional field flow, sex select changes.
|
||||
- `src/components/forgot-password-form/forgot-password-form.test.tsx` — submit happy path, Zod email error, `isSuccess` view renders email + `onReset` button, `error` alert, `translations` override.
|
||||
- `src/components/reset-password-form/reset-password-form.test.tsx` — submit happy path, short password Zod error, `confirmPassword` mismatch, `isSuccess` view, `hasToken=false` invalid-token view with `onNavigateToForgotPassword` CTA, password toggle, `translations` override.
|
||||
- `src/components/email-verification-view/email-verification-view.test.tsx` — `verifying` / `success` / `error` / `expired` branches, custom `message` overrides default copy, `onTryAgain` fires on error CTA, `isLoading` disables retry, `translations` override.
|
||||
- `src/index.test.ts` — extended to assert the 5 new symbols (`LoginForm`, `RegisterForm`, `ForgotPasswordForm`, `ResetPasswordForm`, `EmailVerificationView`) appear in the root barrel.
|
||||
|
||||
## Migration impact
|
||||
|
||||
- `package.json` gains three new dependencies — `@suaveplan/react-ui` (`workspace:^`), `react-hook-form` and `@hookform/resolvers` (both `catalog:forms`). The five form subpath exports are appended to the `exports` block. Vite `lib.entry` gains five new entries; rollup `external` gains `@suaveplan/react-ui`, `react-hook-form`, `@hookform/resolvers`.
|
||||
- No Wave 1 file is modified except `src/index.ts` (named re-exports for the 5 new components added — strictly additive) and `src/index.test.ts` (asserts the new symbols are present).
|
||||
- No downstream consumers yet — `@suaveplan/react-auth-whitelabel` is the only sibling and it ships independently in parallel. Verified by `bunx turbo ls --filter=...@suaveplan/react-auth` (empty list).
|
||||
@@ -0,0 +1,148 @@
|
||||
<!-- markdownlint-disable MD024 -->
|
||||
|
||||
# Capability Spec Delta: react-auth (add-react-auth-forms)
|
||||
|
||||
**Package**: `@suaveplan/react-auth`
|
||||
**Status**: PROPOSED
|
||||
|
||||
> Every requirement carries a `**Verified by:**` line authored at draft time.
|
||||
|
||||
## ADDED Requirements
|
||||
|
||||
### Requirement: LoginForm renders a Zod-validated sign-in form with email, password, and remember-me
|
||||
|
||||
The package SHALL expose a `LoginForm` component that renders a card-layout sign-in form with email + password + remember-me fields. The form SHALL validate via `react-hook-form` + `zodResolver` against an internal `loginSchema` requiring email to be a non-empty valid email and password to be at least 8 characters. Submit SHALL invoke the caller-supplied `onSubmit({ email, password, rememberMe })`. The component SHALL render a destructive `Alert` when `error` is non-null, disable the submit button when `isLoading` or `formState.isSubmitting` is true, and accept a `translations` prop overriding the default `enGb.auth.login` strings.
|
||||
|
||||
#### Scenario: invalid email shows Zod error on blur
|
||||
|
||||
- **WHEN** the user types `not-an-email` into the email field and blurs out
|
||||
- **THEN** the text "Invalid email address" appears
|
||||
|
||||
#### Scenario: short password rejected
|
||||
|
||||
- **WHEN** the user submits with password `"abc"`
|
||||
- **THEN** the text "Password must be at least 8 characters" appears and `onSubmit` is NOT called
|
||||
|
||||
#### Scenario: happy path submit
|
||||
|
||||
- **WHEN** the user fills `alice@example.com` + `correcthorsebatterystaple` and clicks Sign in
|
||||
- **THEN** `onSubmit({ email: "alice@example.com", password: "correcthorsebatterystaple", rememberMe: true })` is called
|
||||
|
||||
#### Scenario: error prop renders alert
|
||||
|
||||
- **WHEN** `error={new Error("Invalid credentials")}` is passed
|
||||
- **THEN** an alert containing "Invalid credentials" is in the document
|
||||
|
||||
#### Scenario: translations prop overrides defaults
|
||||
|
||||
- **WHEN** `translations={{ submit: "Connexion" }}` is passed
|
||||
- **THEN** the submit button displays "Connexion"
|
||||
|
||||
**Verified by:** `src/components/login-form/login-form.test.tsx::"shows Zod error when email is invalid"` AND `src/components/login-form/login-form.test.tsx::"shows Zod error when password is shorter than 8 characters"` AND `src/components/login-form/login-form.test.tsx::"calls onSubmit with the typed payload on happy path"` AND `src/components/login-form/login-form.test.tsx::"renders the error prop in a destructive Alert"` AND `src/components/login-form/login-form.test.tsx::"overrides the submit copy when translations prop is supplied"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: RegisterForm renders a Zod-validated sign-up form with cross-field password match
|
||||
|
||||
The package SHALL expose a `RegisterForm` component rendering email, username, password, confirm-password, optional first-name, surname, date-of-birth, sex (4-option select), gender, company fields. The Zod schema SHALL enforce email validity, username `^[a-zA-Z0-9_]+$` with length 3–30, password ≥ 8 chars, and a `confirmPassword` cross-field refinement matching `password`. Submit SHALL fire only when validation passes. The component SHALL accept `error`, `isLoading`, and `translations` props mirroring `LoginForm` semantics.
|
||||
|
||||
#### Scenario: confirmPassword mismatch blocks submit
|
||||
|
||||
- **WHEN** password `"longenough1"` and confirmPassword `"longenough2"` are submitted
|
||||
- **THEN** "Passwords don't match" appears under the confirm field and `onSubmit` is NOT called
|
||||
|
||||
#### Scenario: username regex enforced
|
||||
|
||||
- **WHEN** username `"has space"` is submitted
|
||||
- **THEN** the Zod regex error appears and `onSubmit` is NOT called
|
||||
|
||||
#### Scenario: happy path submit
|
||||
|
||||
- **WHEN** every required field is valid and the form is submitted
|
||||
- **THEN** `onSubmit` receives the full typed payload including the default `sex: "prefer-not-to-say"`
|
||||
|
||||
**Verified by:** `src/components/register-form/register-form.test.tsx::"blocks submit when passwords do not match"` AND `src/components/register-form/register-form.test.tsx::"rejects usernames that do not match the regex"` AND `src/components/register-form/register-form.test.tsx::"calls onSubmit with the typed payload on happy path"` AND `src/components/register-form/register-form.test.tsx::"renders the error prop in a destructive Alert"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: ForgotPasswordForm renders an email-only Zod-validated form with success view
|
||||
|
||||
The package SHALL expose a `ForgotPasswordForm` component rendering an email-only form. When `isSuccess` is true the component SHALL switch to a confirmation card displaying `emailSent`, a "send another" button wired to `onReset`, and a "back to login" link. Otherwise it SHALL render the email form with Zod validation, a destructive alert when `error` is non-null, and a submit button disabled while `isLoading` or `isSubmitting`. A `translations` prop SHALL override `enGb.auth.forgotPassword` defaults.
|
||||
|
||||
#### Scenario: invalid email blocked
|
||||
|
||||
- **WHEN** the user submits "not-email"
|
||||
- **THEN** "Invalid email address" appears and `onSubmit` is NOT called
|
||||
|
||||
#### Scenario: success view rendered
|
||||
|
||||
- **WHEN** `isSuccess` is true and `emailSent="alice@example.com"`
|
||||
- **THEN** the document contains "alice@example.com" and a button whose click calls `onReset`
|
||||
|
||||
**Verified by:** `src/components/forgot-password-form/forgot-password-form.test.tsx::"blocks submit when email is invalid"` AND `src/components/forgot-password-form/forgot-password-form.test.tsx::"calls onSubmit with the typed payload on happy path"` AND `src/components/forgot-password-form/forgot-password-form.test.tsx::"renders the success view with emailSent and a working onReset button"` AND `src/components/forgot-password-form/forgot-password-form.test.tsx::"renders the error prop in a destructive Alert"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: ResetPasswordForm renders new+confirm password with success and invalid-token views
|
||||
|
||||
The package SHALL expose a `ResetPasswordForm` component supporting three view modes: form (default), success (`isSuccess=true`), and invalid token (`hasToken=false`). The schema SHALL require both `password` and `confirmPassword` ≥ 8 chars with a cross-field match refinement. The success view SHALL render a "sign in" CTA wired to `onNavigateToLogin`; the invalid-token view SHALL render a "request new link" CTA wired to `onNavigateToForgotPassword`. The form view SHALL accept `error`, `isLoading`, and `translations` props.
|
||||
|
||||
#### Scenario: password mismatch blocks submit
|
||||
|
||||
- **WHEN** password `"longenough1"` and confirm `"longenough2"` are submitted
|
||||
- **THEN** "Passwords don't match" appears and `onSubmit` is NOT called
|
||||
|
||||
#### Scenario: success view renders CTA
|
||||
|
||||
- **WHEN** `isSuccess=true`
|
||||
- **THEN** clicking the Sign in CTA calls `onNavigateToLogin`
|
||||
|
||||
#### Scenario: invalid token view renders CTA
|
||||
|
||||
- **WHEN** `hasToken=false`
|
||||
- **THEN** clicking the Request new reset link CTA calls `onNavigateToForgotPassword`
|
||||
|
||||
**Verified by:** `src/components/reset-password-form/reset-password-form.test.tsx::"blocks submit when passwords do not match"` AND `src/components/reset-password-form/reset-password-form.test.tsx::"calls onSubmit with the typed payload on happy path"` AND `src/components/reset-password-form/reset-password-form.test.tsx::"renders the success view with a working onNavigateToLogin CTA"` AND `src/components/reset-password-form/reset-password-form.test.tsx::"renders the invalid-token view with a working onNavigateToForgotPassword CTA"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: EmailVerificationView renders four status modes with caller-supplied translations
|
||||
|
||||
The package SHALL expose an `EmailVerificationView` component accepting `status: "verifying" | "success" | "expired" | "error"`. Each status SHALL render a Card with an icon, title, description, and (where applicable) a CTA wired to one of `onContinueToLogin` / `onRegisterAgain` / `onTryAgain`. The `error` status SHALL render a destructive Alert; the `error` retry CTA SHALL be disabled while `isLoading`. A `translations` prop SHALL override `enGb.auth.emailVerification` defaults; an optional `message` prop SHALL override the default status description.
|
||||
|
||||
#### Scenario: verifying state renders spinner and copy
|
||||
|
||||
- **WHEN** `status="verifying"`
|
||||
- **THEN** a `role="status"` spinner is present and the default English "Verifying email..." copy is visible
|
||||
|
||||
#### Scenario: success CTA fires onContinueToLogin
|
||||
|
||||
- **WHEN** `status="success"` and the CTA is clicked
|
||||
- **THEN** `onContinueToLogin` is called once
|
||||
|
||||
#### Scenario: error CTA fires onTryAgain and respects isLoading
|
||||
|
||||
- **WHEN** `status="error"` and `isLoading=false`
|
||||
- **THEN** the retry CTA is enabled and clicking it calls `onTryAgain`
|
||||
|
||||
#### Scenario: expired CTA fires onRegisterAgain
|
||||
|
||||
- **WHEN** `status="expired"` and the CTA is clicked
|
||||
- **THEN** `onRegisterAgain` is called once
|
||||
|
||||
**Verified by:** `src/components/email-verification-view/email-verification-view.test.tsx::"renders the verifying status with a spinner"` AND `src/components/email-verification-view/email-verification-view.test.tsx::"fires onContinueToLogin from the success CTA"` AND `src/components/email-verification-view/email-verification-view.test.tsx::"fires onTryAgain from the error CTA"` AND `src/components/email-verification-view/email-verification-view.test.tsx::"fires onRegisterAgain from the expired CTA"` AND `src/components/email-verification-view/email-verification-view.test.tsx::"overrides the default copy when translations prop is supplied"`
|
||||
|
||||
---
|
||||
|
||||
### Requirement: Form components are reachable from the root barrel
|
||||
|
||||
The `@suaveplan/react-auth` root entry SHALL re-export the 5 Wave 2 form components as named exports (`LoginForm`, `RegisterForm`, `ForgotPasswordForm`, `ResetPasswordForm`, `EmailVerificationView`) with no `export *`. Each form SHALL also be reachable via a dedicated subpath export (`./components/login-form`, etc.) for tree-shaking.
|
||||
|
||||
#### Scenario: root barrel surface
|
||||
|
||||
- **WHEN** the root-barrel test enumerates `Object.keys(import("@suaveplan/react-auth"))`
|
||||
- **THEN** every Wave 2 form symbol is present alongside the Wave 1 surface
|
||||
|
||||
**Verified by:** `src/index.test.ts::"the root barrel exports exactly the documented public surface"`
|
||||
|
||||
<!-- markdownlint-enable MD024 -->
|
||||
66
openspec/archive/2026-05-19-add-react-auth-forms/tasks.md
Normal file
66
openspec/archive/2026-05-19-add-react-auth-forms/tasks.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# Tasks — Add @suaveplan/react-auth — forms (Wave 2)
|
||||
|
||||
> No retroactive ticks. Each box reflects work done in this change.
|
||||
|
||||
## 1. Spec authoring
|
||||
|
||||
- [ ] 1.1 Read `../suaveplan/enterprise/react-auth.md` end-to-end
|
||||
- [ ] 1.2 Read `openspec/specs/react-auth/spec.md` (Wave 1 canonical)
|
||||
- [ ] 1.3 Read `openspec/AGENTS.md` §§ 0c, 2a, 6a, 7, 12, 13, 14
|
||||
- [ ] 1.4 Draft `proposal.md` from template with `## Source mapping` + `### Excluded`
|
||||
- [ ] 1.5 Draft `specs/react-auth/spec.md` delta with `## ADDED Requirements`, one per form
|
||||
- [ ] 1.6 Every requirement carries a `**Verified by:**` line at draft time
|
||||
|
||||
## 2. Implementation
|
||||
|
||||
- [ ] 2.1 Add `@suaveplan/react-ui` + `react-hook-form` + `@hookform/resolvers` deps to `package.json`
|
||||
- [ ] 2.2 Add 5 subpath exports to `package.json` `exports` block
|
||||
- [ ] 2.3 Extend `vite.config.ts` `lib.entry` with 5 new entries; extend `rollupOptions.external`
|
||||
- [ ] 2.4 Ship `src/components/login-form/` (`login-form.tsx`, `.types.ts`, `.md`, `.stories.tsx`, `index.ts`)
|
||||
- [ ] 2.5 Ship `src/components/register-form/` (split into `register-form.tsx` + `register-form-fields.tsx` to honour the 300-line cap)
|
||||
- [ ] 2.6 Ship `src/components/forgot-password-form/`
|
||||
- [ ] 2.7 Ship `src/components/reset-password-form/`
|
||||
- [ ] 2.8 Ship `src/components/email-verification-view/`
|
||||
- [ ] 2.9 Add `src/components/index.ts` barrel with explicit named re-exports for every form symbol
|
||||
- [ ] 2.10 Extend `src/index.ts` with the 5 new components (named re-exports only)
|
||||
- [ ] 2.11 Extend `src/index.test.ts` expected-keys list with the 5 new symbols
|
||||
|
||||
## 3. Tests
|
||||
|
||||
- [ ] 3.1 `src/components/login-form/login-form.test.tsx` — Zod errors, happy path, password toggle, `error`, `isLoading`, `translations` override
|
||||
- [ ] 3.2 `src/components/register-form/register-form.test.tsx` — same matrix + username regex + `confirmPassword` refinement + sex select
|
||||
- [ ] 3.3 `src/components/forgot-password-form/forgot-password-form.test.tsx`
|
||||
- [ ] 3.4 `src/components/reset-password-form/reset-password-form.test.tsx`
|
||||
- [ ] 3.5 `src/components/email-verification-view/email-verification-view.test.tsx`
|
||||
- [ ] 3.6 Every `it(...)` carries `{ timeout: 5000 }`
|
||||
- [ ] 3.7 All tests import from `@suaveplan/testing/runner`
|
||||
|
||||
## 4. Documentation
|
||||
|
||||
- [ ] 4.1 Sibling `.md` for every form (≥200 words, Basic + Advanced examples, full API reference)
|
||||
- [ ] 4.2 No README changes (Wave 1's README already lists components/ subpaths generically)
|
||||
|
||||
## 5. Pre-archive 5-gate verification
|
||||
|
||||
- [ ] 5.1 `bun run check` — Biome clean across the monorepo
|
||||
- [ ] 5.2 `bunx turbo lint typecheck test --filter=@suaveplan/react-auth --force` — green
|
||||
- [ ] 5.3 `bunx turbo test:coverage --filter=@suaveplan/react-auth` — 100/100/100/100 istanbul
|
||||
- [ ] 5.4 `bun scripts/openspec-conformance.ts @suaveplan/react-auth` — exit 0
|
||||
- [ ] 5.5 `bunx turbo build --filter=@suaveplan/react-auth --force` — green
|
||||
- [ ] 5.6 `bun scripts/lint/check-test-runner-imports.ts` — exit 0
|
||||
|
||||
## 6. Pre-archive 7-row checklist
|
||||
|
||||
- [ ] 6.1 Generics threaded — `useForm<TForm>` typed end-to-end, no `as any`
|
||||
- [ ] 6.2 Zod schemas wired at every form's submit boundary
|
||||
- [ ] 6.3 No new error codes introduced (Wave 1's registry unchanged); Zod parse failures surface through react-hook-form's `formState.errors`
|
||||
- [ ] 6.4 N/A — no strict-mode behaviour toggle in this wave
|
||||
- [ ] 6.5 N/A — no new OTel instruments in this wave (forms are pure UI; metrics live on the Wave 1 actions)
|
||||
- [ ] 6.6 N/A — no lifecycle dedup claims on forms
|
||||
- [ ] 6.7 No retroactive ticks
|
||||
|
||||
## 7. Archive
|
||||
|
||||
- [ ] 7.1 Fold spec delta — APPEND new requirements to `openspec/specs/react-auth/spec.md` under `## ADDED Requirements`; do NOT modify Wave 1 requirements
|
||||
- [ ] 7.2 Move `openspec/changes/add-react-auth-forms/` → `openspec/archive/2026-05-19-add-react-auth-forms/`
|
||||
- [ ] 7.3 No MANIFEST.md / STATUS.md updates (not a new package)
|
||||
Reference in New Issue
Block a user