175 lines
23 KiB
Markdown
175 lines
23 KiB
Markdown
# Tasks: enterprise-react-ui-pack-auth
|
|
|
|
**Change ID**: enterprise-react-ui-pack-auth
|
|
**Package**: `@suaveplan/react-ui-pack-auth`
|
|
**Target path**: `packages/react/react-ui-pack-auth`
|
|
**Prerequisite changes (must be archived before this change ships)**: `enterprise-react-i18n`, `enterprise-react-ui-v2`, `enterprise-react-forms-v2`
|
|
**Prerequisite packages on Verdaccio**: `@suaveplan/react-i18n`, `@suaveplan/react-ui`, `@suaveplan/react-forms`, `@suaveplan/api-schemas`, `@suaveplan/error`, `@suaveplan/types`, `@suaveplan/testing`, `@suaveplan/testing-dom`, `@suaveplan/testing-e2e`
|
|
|
|
## 0. Pre-flight (non-negotiable)
|
|
|
|
- [ ] 0.1 Confirm clean working tree (`git status` reports nothing) and no in-flight worktrees with uncommitted work (`git worktree list` then `git status` in each)
|
|
- [ ] 0.2 If any worktrees exist under `.claude/worktrees/`, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (`git worktree remove`) before opening a new one — code that has been lost twice on main is the failure mode this guards against.
|
|
- [ ] 0.3 Confirm the package set in `MANIFEST.md` matches `find packages -maxdepth 3 -name package.json` count. Drift here means a prior change archived without regenerating the manifest.
|
|
|
|
## 1. Spec authoring
|
|
|
|
- [ ] 1.1 Read `proposal.md` and `specs/react-ui-packs/spec.md` end-to-end
|
|
- [ ] 1.2 Read `openspec/cross-cutting-designs/2026-05-05-react-ui-react-i18n-react-forms.md` §5 (component-pack contract), §9 (`createDomainForm`), §11 (`TranslationKey`)
|
|
- [ ] 1.3 Read the in-flight `enterprise-react-ui-v2` proposal (specifically the `ComponentPack` / `PackRegistryProvider` / `usePackComponent` surface) and confirm the augmentation pattern this pack uses matches that proposal verbatim
|
|
- [ ] 1.4 Read the in-flight `enterprise-react-forms-v2` proposal (specifically `createDomainForm`'s field-config types and the `TranslationKey`-typed label / placeholder / helperText / errorMessage constraint)
|
|
- [ ] 1.5 `openspec change validate enterprise-react-ui-pack-auth --strict` exits with no errors
|
|
|
|
## 2. Adjacent api-schemas extension
|
|
|
|
- [ ] 2.1 In `packages/api/api-schemas/src/auth/`, add `password-reset-request.ts` exporting `PasswordResetRequestInput` (`{ email }`) and `PasswordResetRequestOutput` (`{ requestedAt }`) plus inferred types and sibling `.md`
|
|
- [ ] 2.2 Add `password-reset-confirm.ts` exporting `PasswordResetConfirmInput` (`{ token, newPassword }`) and `PasswordResetConfirmOutput` (`{ confirmedAt }`) plus inferred types and sibling `.md`
|
|
- [ ] 2.3 Add `two-factor-verify.ts` exporting `TwoFactorVerifyInput` (`{ code }`, six-digit string with `regex(/^\d{6}$/)` refinement) and `TwoFactorVerifyOutput` (`{ token, refreshToken, user: LoginUser }`) plus inferred types and sibling `.md`
|
|
- [ ] 2.4 Add `magic-link-request.ts` exporting `MagicLinkRequestInput` (`{ email }`) and `MagicLinkRequestOutput` (`{ requestedAt }`) plus inferred types and sibling `.md`
|
|
- [ ] 2.5 Add `magic-link-verify.ts` exporting `MagicLinkVerifyInput` (`{ token }`) and `MagicLinkVerifyOutput` (`{ token, refreshToken, user: LoginUser }`) plus inferred types and sibling `.md`
|
|
- [ ] 2.6 Update `packages/api/api-schemas/src/auth/index.ts` with explicit named exports for all five new modules (no `export *`, per CLAUDE.md §10)
|
|
- [ ] 2.7 Add a sibling `.test.ts` per new schema asserting parse success on a happy-path payload, parse failure on every refinement violation (empty, malformed email, invalid token, wrong code length), and idempotent round-trip through `safeParse`
|
|
- [ ] 2.8 `bunx turbo lint typecheck test --filter=@suaveplan/api-schemas` green
|
|
- [ ] 2.9 `bunx turbo test:coverage --filter=@suaveplan/api-schemas` at 100/100/100/100 across the touched files
|
|
|
|
## 3. Scaffold
|
|
|
|
- [ ] 3.1 Create `packages/react/react-ui-pack-auth/` with `package.json` at version `0.1.0`, `tsconfig.lib.json` (extends `@suaveplan/typescript-config/react-library.json`), `vitest.config.ts` (uses `createTestConfig()` from `@suaveplan/testing/config`, `provider: 'istanbul'`, thresholds `100/100/100/100`), `vite.config.ts` (vite-plugin-dts), `playwright.config.ts` (uses `createPlaywrightConfig()` from `@suaveplan/testing-e2e`)
|
|
- [ ] 3.2 Dependencies: `@suaveplan/react-forms workspace:^`, `@suaveplan/api-schemas workspace:^`, `@suaveplan/error workspace:^`, `@suaveplan/types workspace:^`, `@opentelemetry/api catalog:observability`, `tslib catalog:build`, `zod catalog:forms`
|
|
- [ ] 3.3 Peer dependencies: `react ^18.0.0 || ^19.0.0`, `@suaveplan/react-ui workspace:^`, `@suaveplan/react-i18n workspace:^`
|
|
- [ ] 3.4 Dev dependencies: `@suaveplan/react-ui workspace:^`, `@suaveplan/react-i18n workspace:^`, `@suaveplan/testing workspace:^`, `@suaveplan/testing-dom workspace:^`, `@suaveplan/testing-e2e workspace:^`, `@vitest/coverage-istanbul catalog:testing`, `react`, `react-dom`, `@types/react`, `@types/react-dom`, `vitest`, `vite`, `vite-plugin-dts`, `fast-check catalog:testing`
|
|
- [ ] 3.5 NO direct imports of `vitest` / `bun:test` (CLAUDE.md §11) — all test-runner primitives via `@suaveplan/testing/runner`
|
|
- [ ] 3.6 NO direct imports of `react-hook-form`, `@radix-ui/*`, `i18next`, `react-i18next` — all consumed transitively through `@suaveplan/react-forms`, `@suaveplan/react-ui`, `@suaveplan/react-i18n`
|
|
- [ ] 3.7 `"sideEffects": ["./src/translations/augmentation.js"]` — only the augmentation file has a side effect (registering the bundle); everything else is pure
|
|
- [ ] 3.8 Subpath exports: `.`, `./pack`, `./components/login-form/login-form`, `./components/register-form/register-form`, `./components/password-reset-form/password-reset-form`, `./components/two-factor-form/two-factor-form`, `./components/magic-link-form/magic-link-form`, `./errors`, `./translations`, `./types/auth-submit-client`
|
|
|
|
## 4. Errors and shared types
|
|
|
|
- [ ] 4.1 Create `src/errors/errors.ts` with `defineCodes("REACT_UI_PACK_AUTH", ["SUBMIT_CLIENT_MISSING", "PACK_VERSION_MISMATCH", "UNKNOWN_FIELD_KIND", "MODE_REQUIRED"])`; export `REACT_UI_PACK_AUTH_CODES` and `ReactUiPackAuthError extends SuaveplanError<ReactUiPackAuthErrorCode>`
|
|
- [ ] 4.2 Sibling `errors.test.ts` — every code resolves through the registry, error chain preserves `cause`, JSON wire format round-trips
|
|
- [ ] 4.3 Sibling `errors.md`
|
|
- [ ] 4.4 Create `src/types/auth-submit-client.types.ts` defining the `AuthSubmitClient` interface — one method per form, each typed against the matching `<Form>Input` / `<Form>Output` from `@suaveplan/api-schemas/auth`
|
|
- [ ] 4.5 Sibling `auth-submit-client.md` (no `.test.ts` — pure type file)
|
|
- [ ] 4.6 Create `src/types/auth-submit-client-provider.tsx` exporting `<AuthSubmitClientProvider>` and `useAuthSubmitClient()` (returns the provider's value, throws `SUBMIT_CLIENT_MISSING` when no provider AND no prop is passed at the form level)
|
|
- [ ] 4.7 Sibling `auth-submit-client-provider.test.tsx` and `auth-submit-client-provider.md`
|
|
|
|
## 5. Translations
|
|
|
|
- [ ] 5.1 Create `src/translations/en-GB.json` — every key the five forms reference: `react-ui-pack-auth.<form>.title`, `.subtitle`, `.<field>.label`, `.<field>.placeholder`, `.<field>.helperText`, `.<field>.errors.<code>`, `.submit.label`, `.submit.busyLabel`, `.cancel.label`, `.success.title`, `.success.body`, `.error.<code>.title`, `.error.<code>.body`. Add the OTP plural string with full Welsh six-form coverage (`{remaining, plural, zero {…} one {…} two {…} few {…} many {…} other {…}}`).
|
|
- [ ] 5.2 Create `src/translations/zh-CN.json` mirroring every key
|
|
- [ ] 5.3 Create `src/translations/ar-AE.json` mirroring every key (RTL — translation values are LTR-marked correctly via Unicode bidi where required)
|
|
- [ ] 5.4 Create `src/translations/cy-GB.json` mirroring every key (Welsh six-form plural for the OTP attempts string)
|
|
- [ ] 5.5 Create `src/translations/ru-RU.json` mirroring every key (Russian three-form plural where applicable)
|
|
- [ ] 5.6 Create `src/translations/translations.schema.ts` — Zod schema constraining the shape every locale JSON must satisfy (used by the locale-completeness gate to fail fast on missing keys)
|
|
- [ ] 5.7 Create `src/translations/augmentation.d.ts` (generated) — `declare module "@suaveplan/react-i18n" { interface ContributedTranslationKeys { ... } }` registering every key from `en-GB.json` into the brand union
|
|
- [ ] 5.8 Create `src/translations/augmentation.ts` — runtime side-effect entry that imports each locale JSON statically and exports `bundles: Record<SupportedLocale, Record<string, string>>`
|
|
- [ ] 5.9 Create `src/translations/translations.test.ts` — locale-completeness gate (every key in en-GB.json exists in every other locale), parse-against-Zod test for every locale, augmentation-mirrors-en-GB test
|
|
- [ ] 5.10 Create `src/translations/translations.md` (the only `.md` for the directory; per-JSON sibling docs not required since JSON files have no exports)
|
|
- [ ] 5.11 Wire `bun scripts/generate-translation-types.ts` for this package so re-running it produces a byte-identical `augmentation.d.ts`
|
|
|
|
## 6. ComponentPack export
|
|
|
|
- [ ] 6.1 Create `src/pack/pack.ts` exporting `authPack: ComponentPack<'auth'>` with `name: 'auth'`, `version: '0.0.0'`, `peerRange: '^0.1.0'`, `components: { LoginForm, RegisterForm, PasswordResetForm, TwoFactorForm, MagicLinkForm }`, `translations: bundles`, `onRegister`, `onUnregister`
|
|
- [ ] 6.2 `onRegister(ctx)` — calls `ctx.i18n.addResourceBundle('react-ui-pack-auth', bundles[ctx.i18n.locale])`, fires `pack.auth.lifecycle_total{phase=register, outcome=success}` counter, logs structured `pack.version_mismatch` warning via `ctx.logger` when the runtime `@suaveplan/react-ui` version falls outside `peerRange`
|
|
- [ ] 6.3 `onUnregister(ctx)` — calls `ctx.i18n.removeResourceBundle('react-ui-pack-auth', ctx.i18n.locale)` and fires the matching counter
|
|
- [ ] 6.4 Create `src/pack/pack.augmentation.d.ts` — `declare module "@suaveplan/react-ui/pack" { interface PackComponents { auth: { LoginForm: ComponentType<LoginFormProps>; RegisterForm: ComponentType<RegisterFormProps>; PasswordResetForm: ComponentType<PasswordResetFormProps>; TwoFactorForm: ComponentType<TwoFactorFormProps>; MagicLinkForm: ComponentType<MagicLinkFormProps> } } }`
|
|
- [ ] 6.5 Create `src/pack/pack.contract.test.tsx` — `expectTypeOf(authPack).toMatchTypeOf<ComponentPack<'auth'>>()`; registering it through `<PackRegistryProvider packs={[authPack]}>` makes `usePackComponent('auth', '<form>')` return the same component reference (`Object.is`) for all five forms
|
|
- [ ] 6.6 Create `src/pack/pack.lifecycle.test.tsx` — `onRegister` is called with `{ i18n, theme, logger, signal }` on mount; `onUnregister` is called on unmount; `addResourceBundle` and `removeResourceBundle` were called with the matching arguments; `pack.version_mismatch` fires only when `peerRange` is unsatisfied
|
|
- [ ] 6.7 Create `src/pack/pack.md`
|
|
|
|
## 7. LoginForm
|
|
|
|
- [ ] 7.1 Create `src/components/login-form/login-form.ts` building the form via `createDomainForm({ schema: LoginInput, fields: { email: { kind: 'email', label: 'react-ui-pack-auth.login.email.label', placeholder: 'react-ui-pack-auth.login.email.placeholder', autoComplete: 'email' }, password: { kind: 'password', label: 'react-ui-pack-auth.login.password.label', autoComplete: 'current-password' } }, layout: 'vertical', submit: { label: 'react-ui-pack-auth.login.submit.label', busyLabel: 'react-ui-pack-auth.login.submit.busyLabel', onSubmit: async (values, ctx) => { const result = await ctx.submitClient.login(values); ctx.onSuccess(result); } } })`
|
|
- [ ] 7.2 Export `LoginForm` and `LoginFormProps`
|
|
- [ ] 7.3 Sibling `login-form.test.tsx` — renders under all 5 locales (`it.each`); submits the typed payload exactly once; calls `onSuccess` with the `LoginOutput`-typed value; renders the typed key from the catalog (no string literal); maps a thrown `SuaveplanError("AUTH.INVALID_CREDENTIALS")` to the correct `react-ui-pack-auth.login.error.AUTH.INVALID_CREDENTIALS.body` key
|
|
- [ ] 7.4 Sibling `login-form.stories.tsx` — Default, Playground, WithI18n (parametric over the 5 locales), WithTheme (parametric over light/dark/highContrast/dracula), WithError (server-error mapping), WithDefaultEmail (the magic-link-prefilled flow)
|
|
- [ ] 7.5 Sibling `login-form.md`
|
|
|
|
## 8. RegisterForm
|
|
|
|
- [ ] 8.1 Create `src/components/register-form/register-form.ts` building the form via `createDomainForm({ schema: RegisterInput.extend({ confirmPassword: z.string().min(8), terms: z.literal(true) }), fields: { email, name, password, confirmPassword: { kind: 'password', label: 'react-ui-pack-auth.register.confirmPassword.label', validate: { matches: 'password' } }, terms: { kind: 'checkbox', label: 'react-ui-pack-auth.register.terms.label', required: true } }, layout: 'vertical', submit: { ... onSuccess wires onto AuthSubmitClient.register ... } })`
|
|
- [ ] 8.2 Export `RegisterForm` and `RegisterFormProps`
|
|
- [ ] 8.3 Sibling `register-form.test.tsx` — under all 5 locales; cross-field validator fires when `confirmPassword !== password` and renders `react-ui-pack-auth.register.confirmPassword.errors.mismatch`; the `terms` checkbox is required and submit stays disabled until checked; submit returns a `RegisterOutput`-typed value
|
|
- [ ] 8.4 Sibling `register-form.stories.tsx` — Default, Playground, WithI18n, WithTheme, WithMismatchedPasswords, WithUncheckedTerms, WithExistingEmailError
|
|
- [ ] 8.5 Sibling `register-form.md`
|
|
|
|
## 9. PasswordResetForm
|
|
|
|
- [ ] 9.1 Create `src/components/password-reset-form/password-reset-form.ts` exporting a single component whose `mode: 'request' | 'confirm'` prop selects between two factory-built forms — `mode = 'request'` builds against `PasswordResetRequestInput`, `mode = 'confirm'` builds against `PasswordResetConfirmInput`
|
|
- [ ] 9.2 When `mode` is missing or invalid, throw `ReactUiPackAuthError(MODE_REQUIRED)` at render time
|
|
- [ ] 9.3 Export `PasswordResetForm` and `PasswordResetFormProps` (the props discriminated union over `mode`)
|
|
- [ ] 9.4 Sibling `password-reset-form.test.tsx` — both modes render under all 5 locales; request-mode submit calls `submitClient.requestPasswordReset` once; confirm-mode submit calls `submitClient.confirmPasswordReset` once with `{ token, newPassword }`; missing `mode` throws `MODE_REQUIRED`
|
|
- [ ] 9.5 Sibling `password-reset-form.stories.tsx` — RequestDefault, ConfirmDefault, Playground, WithI18n, WithTheme, RequestSuccess, ConfirmSuccess, RequestRateLimitError, ConfirmInvalidTokenError
|
|
- [ ] 9.6 Sibling `password-reset-form.md`
|
|
|
|
## 10. TwoFactorForm
|
|
|
|
- [ ] 10.1 Create `src/components/two-factor-form/two-factor-form.ts` building via `createDomainForm({ schema: TwoFactorVerifyInput, fields: { code: { kind: 'otp', length: 6, label: 'react-ui-pack-auth.twoFactor.code.label', helperText: 'react-ui-pack-auth.twoFactor.code.helperText' } }, layout: 'vertical', submit: { ... } })` — the `kind: 'otp'` field renders the `react-ui` `InputOTP` primitive
|
|
- [ ] 10.2 Add a "send new code" sub-component gated by a 60-second countdown; the countdown is driven by an injectable `clock?: Clock` prop (default `Date.now`-based) so tests can use a `FakeClock` from `@suaveplan/testing`
|
|
- [ ] 10.3 The remaining-attempts string uses the `react-ui-pack-auth.twoFactor.code.helperText` key with a `count` argument, exercising Welsh six-form plurals under `cy-GB`
|
|
- [ ] 10.4 Export `TwoFactorForm` and `TwoFactorFormProps`
|
|
- [ ] 10.5 Sibling `two-factor-form.test.tsx` — renders under all 5 locales; six-digit input pattern is enforced (submit disabled until `/^\d{6}$/`); submit calls `submitClient.verifyTwoFactor` with `{ code }`; "send new code" is disabled while the countdown is active and re-enables when the FakeClock advances 60 s; Welsh plural categories `zero / one / two / few / many / other` all resolve to the right string
|
|
- [ ] 10.6 Sibling `two-factor-form.stories.tsx` — Default, Playground, WithI18n, WithTheme, WithCountdownActive, WithCountdownExpired, WithInvalidCode, WithWelshPlurals (one story per Welsh plural category)
|
|
- [ ] 10.7 Sibling `two-factor-form.md`
|
|
|
|
## 11. MagicLinkForm
|
|
|
|
- [ ] 11.1 Create `src/components/magic-link-form/magic-link-form.ts` exporting a component whose `mode: 'request' | 'verify'` prop selects between two factory-built forms — `mode = 'request'` builds against `MagicLinkRequestInput`, `mode = 'verify'` builds against `MagicLinkVerifyInput`
|
|
- [ ] 11.2 In `verify` mode, accept an optional `token` prop — when present, auto-submit on mount; when absent, render the manual fallback form
|
|
- [ ] 11.3 Throw `ReactUiPackAuthError(MODE_REQUIRED)` if `mode` is missing
|
|
- [ ] 11.4 Export `MagicLinkForm` and `MagicLinkFormProps`
|
|
- [ ] 11.5 Sibling `magic-link-form.test.tsx` — both modes render under all 5 locales; request-mode submit calls `submitClient.requestMagicLink`; verify-mode auto-submit fires `submitClient.verifyMagicLink` exactly once on mount when `token` is provided; manual-fallback button submits the typed `{ token }` payload otherwise
|
|
- [ ] 11.6 Sibling `magic-link-form.stories.tsx` — RequestDefault, VerifyAutoSubmit, VerifyManualFallback, Playground, WithI18n, WithTheme, RequestRateLimitError, VerifyExpiredTokenError
|
|
- [ ] 11.7 Sibling `magic-link-form.md`
|
|
|
|
## 12. Telemetry
|
|
|
|
- [ ] 12.1 Create `src/telemetry/telemetry.ts` exporting the three instruments (`pack.auth.submit_total`, `pack.auth.submit.duration_ms`, `pack.auth.lifecycle_total`); enforce the `outcome` and `phase` allowlists at the recording site so unbounded label cardinality is impossible
|
|
- [ ] 12.2 Wire each form's submit pipeline through the counter + histogram (success path increments `outcome=success`, validation failures `outcome=validation_error`, thrown errors `outcome=submit_error`)
|
|
- [ ] 12.3 Sibling `telemetry.test.ts` — counters fire with the right attributes; histogram records duration; allowlist rejects unknown values
|
|
- [ ] 12.4 Sibling `telemetry.md`
|
|
|
|
## 13. Public root + README
|
|
|
|
- [ ] 13.1 Create `src/index.ts` with explicit named re-exports (no `export *`, per CLAUDE.md §10) for `authPack`, the five `<Form>` components, the matching `<Form>Props` types, `AuthSubmitClient`, `AuthSubmitClientProvider`, `useAuthSubmitClient`, `ReactUiPackAuthError`, `REACT_UI_PACK_AUTH_CODES`. Side-effect import `./translations/augmentation.js` and `./pack/pack.augmentation.js` so type augmentation and runtime bundle registration happen on root import.
|
|
- [ ] 13.2 Create `README.md` at the package root with: package name, one-paragraph description, install snippet (`bun add @suaveplan/react-ui-pack-auth`), quick-start (registering the pack via `<PackRegistryProvider packs={[authPack]}>` and rendering `<LoginForm submitClient={mySubmitClient} onSuccess={...} />`), Modules section linking to every co-located `.md`
|
|
- [ ] 13.3 Run the per-package doc-coverage gate: every implementation `.ts`/`.tsx` (excluding tests, types, stories, barrels) has a sibling `.md` ≥ 200 words with simple + advanced examples + API reference
|
|
|
|
## 14. End-to-end + a11y
|
|
|
|
- [ ] 14.1 Create `e2e/auth-submit-success.spec.ts` — for each of the five forms, fill required fields and submit against a stub `AuthSubmitClient`; assert `onSuccess` payload matches the API-schema shape
|
|
- [ ] 14.2 Create `e2e/auth-submit-error.spec.ts` — submit against a stub that throws a structured `SuaveplanError`; assert the form maps the error code to the correct translation key and renders next to the offending field (or as a form-level alert when no field maps)
|
|
- [ ] 14.3 Create `e2e/auth-rtl.spec.ts` — load each form under `ar-AE`; assert `<html dir="rtl">`; assert OTP cells flow right-to-left; assert the password show/hide eye icon mirrors
|
|
- [ ] 14.4 Create `e2e/auth-themes.spec.ts` — load `LoginForm` under each built-in theme + the dracula example theme; assert no console errors; assert axe-core finds zero serious or critical violations
|
|
- [ ] 14.5 Create `e2e/pack-lifecycle.spec.ts` — mount the `<PackRegistryProvider>` with `authPack`; assert bundles are added; unmount; assert bundles are removed and `usePackComponent('auth', 'LoginForm')` now returns `undefined`
|
|
|
|
## 15. Pre-publish gate
|
|
|
|
- [ ] 15.1 `bunx turbo lint typecheck test --filter=@suaveplan/react-ui-pack-auth` green
|
|
- [ ] 15.2 `bunx turbo test:coverage --filter=@suaveplan/react-ui-pack-auth` at 100/100/100/100
|
|
- [ ] 15.3 `bunx turbo build --filter=@suaveplan/react-ui-pack-auth` green
|
|
- [ ] 15.4 Banned-pattern scan zero matches: `grep -rn "@deprecated\|TODO\|FIXME\|ts-ignore\|ts-expect-error\|\.skip\|\.only\|\bany\b\|console\.log" packages/react/react-ui-pack-auth/src/ --include="*.ts" --include="*.tsx"`
|
|
- [ ] 15.5 Test-runner-import scan zero matches: `grep -rnE "from ['\"](vitest|bun:test)['\"]" packages/react/react-ui-pack-auth/src/ --include="*.ts" --include="*.tsx"`
|
|
- [ ] 15.6 Barrel-shape scan zero matches in feature barrels: `grep -rn "^\s*export\s\+\(type\s\+\)\?\*\s\+from" packages/react/react-ui-pack-auth/src/ --include="index.ts" | grep -v "/src/index\.ts:"`
|
|
- [ ] 15.7 Hardcoded-string scan against the §11 watchlist (label, placeholder, helperText, errorMessage, aria-label, title, alt) zero matches: every value resolves to a `TranslationKey`
|
|
- [ ] 15.8 Locale-completeness scan green — every key in `en-GB.json` exists in `zh-CN.json`, `ar-AE.json`, `cy-GB.json`, `ru-RU.json`
|
|
- [ ] 15.9 Translation-key-existence scan green — every `t('...')` call references a key that exists in `en-GB.json`
|
|
- [ ] 15.10 React single-version check: `find . -path "*/node_modules/react/package.json" | head -5` returns one path
|
|
- [ ] 15.11 Subpath-export count matches feature module count (per CLAUDE.md §6)
|
|
- [ ] 15.12 Dependent sweep `bunx turbo test --filter=...@suaveplan/react-ui-pack-auth` (expect zero dependents on first ship; future packs that consume this will be picked up automatically)
|
|
|
|
## 16. Manifest + STATUS refresh
|
|
|
|
- [ ] 16.1 Add `@suaveplan/react-ui-pack-auth` to the package listing in `CLAUDE.md` under the `react-ui` plugin's package list (immediately after `react-ui` and `react-ui-theme-dracula`)
|
|
- [ ] 16.2 Run `bun scripts/folder-reorg/generate-manifest.ts` to refresh `MANIFEST.md`
|
|
- [ ] 16.3 Run `bun scripts/generate-missing.ts` to refresh `MISSING.yml`
|
|
- [ ] 16.4 Update `STATUS.md` to move `@suaveplan/react-ui-pack-auth` (if it appears in Pending) to the Completed section, or add a new Completed row if it does not appear in Pending
|
|
- [ ] 16.5 Update `SUMMARY.md` with a per-package paragraph reflecting the shipped surface (five forms, pack contract proof, five locales, AuthSubmitClient injection)
|
|
- [ ] 16.6 `bunx markdownlint-cli2 --fix` — re-stage modified files; `bunx markdownlint-cli2` exits 0
|
|
|
|
## 17. OpenSpec archival
|
|
|
|
- [ ] 17.1 Fold `specs/react-ui-packs/spec.md` into `openspec/specs/react-ui-packs/spec.md` (creating the canonical spec file since this is the first change against the capability)
|
|
- [ ] 17.2 Move `openspec/changes/enterprise-react-ui-pack-auth/` → `openspec/archive/<yyyy-mm-dd>-enterprise-react-ui-pack-auth/`
|
|
- [ ] 17.3 Confirm `git status` shows only intentional changes; no scratch / debug artifacts remain
|