# Spec Delta — react-ui-i18n deferred tooling ## ADDED Requirements ### Requirement: Stories ship for every i18n primitive Every primitive added by the `react-ui-i18n` capability SHALL ship a sibling `.stories.tsx` alongside its implementation. Each story file SHALL contain a `default` export plus a `playground` and at least one variants story (the chassis-wide stories convention). Stories SHALL render inside `` + `` + `` so the rendered surface matches production-runtime composition. Story-snapshot tests SHALL run as part of the package's test suite. The covered surfaces are: ``, ``, ``, ``, ``, ``, ``, ``, `formattedCell`, `useValidationMessageBridge`, and ``. **Verified by:** per-feature story-snapshot tests (one `*.stories.test.tsx` per primitive) plus a presence check in `scripts/check-i18n-stories.test.ts` (or equivalent). #### Scenario: every i18n primitive has a `.stories.tsx` sibling - GIVEN the directory `src/i18n//` - WHEN the chassis `*.stories.tsx` walker runs - THEN every public primitive listed in the capability spec has a matching `.stories.tsx` file ### Requirement: bun run check:i18n-keys catches translation-key drift The package SHALL ship `scripts/check-i18n-keys.ts` invoked via `bun run check:i18n-keys`. The script SHALL AST-walk every `*.{ts,tsx}` under `src/components/**` and `src/i18n/**` looking for translation-key references — `t("react-ui.…")`, `tDynamic("react-ui.…")`, ``, `` — and SHALL diff the discovered key set against `src/locales/en-GB.json`. The script SHALL exit non-zero when any key is referenced in code but absent from the bundle (`MISSING`) or present in the bundle but referenced nowhere in code (`ORPHAN`). The script SHALL be wired into `bunx turbo lint` so CI fails on drift. **Verified by:** `scripts/check-i18n-keys.test.ts` — fixture directory exercising the missing, orphan, and clean paths. #### Scenario: missing key in code fails the gate - GIVEN a source file referencing `t("react-ui.foo.absent")` and `en-GB.json` lacking that key - WHEN `bun run check:i18n-keys` runs - THEN the script exits non-zero and prints `MISSING: react-ui.foo.absent` with the source path #### Scenario: orphan key in bundle fails the gate - GIVEN `en-GB.json` containing `react-ui.foo.unused` and no source reference - WHEN `bun run check:i18n-keys` runs - THEN the script exits non-zero and prints `ORPHAN: react-ui.foo.unused` #### Scenario: clean source + bundle pair passes - GIVEN every key in `en-GB.json` is referenced in code AND every code-referenced key is in the bundle - WHEN `bun run check:i18n-keys` runs - THEN the script exits zero with no diagnostics ### Requirement: bun run check:registry-eject verifies shadcn ejection The package SHALL ship `scripts/check-registry-eject.ts` invoked via `bun run check:registry-eject`. For every `registry/r/.json` entry the script SHALL: 1. Resolve the listed source files. 2. Apply the registry transform (rewriting `@suaveplan/react-ui` imports to local relative paths, preserving `@suaveplan/react-i18n` imports). 3. Run `tsc --noEmit` against the transformed tree using a synthesized consumer `tsconfig.json` that declares `@suaveplan/react-i18n` and React peers via `compilerOptions.paths`. The script SHALL exit non-zero when any registry entry's transformed source fails to type-check, surfacing the underlying tsc diagnostics with the entry name as context. The script SHALL also detect missing registry entries — public components / primitives in `src/components/**` and `src/i18n/**` lacking a corresponding `registry/r/.json` SHALL trigger a `MISSING REGISTRY ENTRY` diagnostic. The script SHALL be wired into `bunx turbo lint`. **Verified by:** `scripts/check-registry-eject.test.ts` — fixture-driven happy-path + failure-path tests. #### Scenario: ejected primitive type-checks against react-i18n - GIVEN the registry entry `formatted-number.json` - WHEN the transform is applied to its files and the result is type-checked - THEN `tsc --noEmit` exits 0 and the import `import { useFormatter } from "@suaveplan/react-i18n"` resolves #### Scenario: missing registry entry for a public primitive fails the gate - GIVEN a public primitive at `src/i18n//.tsx` with no corresponding `registry/r/.json` - WHEN `bun run check:registry-eject` runs - THEN the script exits non-zero with `MISSING REGISTRY ENTRY: src/i18n//.tsx` #### Scenario: corrupted transform output fails the gate - GIVEN a registry entry whose source references a symbol the transform fails to rewrite correctly - WHEN the transformed source is type-checked - THEN the script exits non-zero with `EJECT FAILURE: ` and the underlying tsc diagnostics