Files
suaveplan/openspec/archive/2026-05-09-i18n-backend-fs-multifile/tasks.md
2026-05-27 23:29:53 +01:00

93 lines
9.5 KiB
Markdown

# Tasks — `@suaveplan/i18n-backend-fs-multifile` (Node fs/promises multi-file translation backend, server-tier)
> Every checkbox MUST reflect work actually done in this change. Do not retroactively tick.
## 1. Spec authoring
- [x] 1.1 Read `openspec/cross-cutting-designs/2026-05-05-i18n-modular-split.md` end-to-end (especially §3, §6, §6.1 — the only Wave 5 backend on a different tier)
- [x] 1.2 Read `openspec/AGENTS.md` Sections 0, 0a, 6a, 7, 9, 13
- [x] 1.3 Read source: `packages/i18n/i18n/src/core/backend/multi-file-fs/multi-file-fs-backend.ts`, `multi-file-fs-backend.test.ts`, `multi-file-fs-backend.schemas.ts`, `multi-file-fs-backend.md`, plus `packages/i18n/i18n/src/core/backend/backend.types.ts`
- [x] 1.4 Draft `proposal.md` from `openspec/templates/proposal.md`
- [x] 1.5 Document the tier split rationale in proposal.md (`### Tier split rationale (server-only, NOT universal)` section)
- [x] 1.6 Fill `## Source mapping` table with verified line ranges from monolith
- [x] 1.7 Draft `specs/i18n-backend-fs-multifile/spec.md` from `openspec/templates/spec-delta.md`
- [x] 1.8 Every requirement carries a `**Verified by:**` line naming concrete `it(...)` titles
- [x] 1.9 `openspec change validate i18n-backend-fs-multifile` exits with no errors
## 2. Package scaffold
- [x] 2.1 `mkdir -p packages/i18n/i18n-backend-fs-multifile/src`
- [x] 2.2 Author `package.json` — name `@suaveplan/i18n-backend-fs-multifile`, version `0.1.0`, type `module`, sideEffects `false`. **Server tier:** declare `"engines": { "node": ">=20" }`; OMIT `"browser"` field. Deps: `@suaveplan/i18n-shared workspace:^`, `@suaveplan/types workspace:^`, `@suaveplan/error workspace:^`, `@opentelemetry/api catalog:observability`, `tslib catalog:build`, `zod catalog:forms`. devDeps: `@suaveplan/testing workspace:^`, `@suaveplan/typescript-config workspace:^`, `@vitest/coverage-istanbul catalog:testing`, `vitest catalog:testing`. Optional peerDeps: `yaml`, `json5` (markedOptional via `peerDependenciesMeta`).
- [x] 2.3 Author `tsconfig.json` extending `@suaveplan/typescript-config/library-node.json` (server-tier)
- [x] 2.4 Author `vite.config.ts` using `createTestConfig()` from `@suaveplan/testing/config`
- [x] 2.5 Author `README.md` (≥200 words, includes Modules table linking to each `.md`; opens with a server-tier marker callout)
- [x] 2.6 Add `package.json` `exports` map with subpaths `.`, `./backend`, `./factory`, `./path-resolver`, `./payload-codec`, `./fs-error-classifier`
## 3. Implementation
- [x] 3.1 Author `src/backend.types.ts` — port `MultiFileFsBackendOptions`, `MultiFileFsLayout`, `MultiFileFsLike` from monolith L15-L51 verbatim
- [x] 3.2 Author `src/backend.schemas.ts` — port `MultiFileFsBackendOptionsSchema` Zod schema verbatim (15 lines)
- [x] 3.3 Author `src/path-resolver.ts` — extract `#resolvePath` body into a standalone `resolvePath(root, layout, extension, globPattern, locale, namespace): string` plus `joinPath` helper
- [x] 3.4 Author `src/payload-codec.ts` — extract `parsePayload`, `parseJson`, `parseWithPeer`, `encodePayload` from monolith L313-L400 as standalone exports
- [x] 3.5 Author `src/fs-error-classifier.ts` — extract `classifyFsError` from monolith L297-L311 as a standalone export
- [x] 3.6 Author `src/observability.ts` — inline equivalents of `recordRead` / `recordWrite`; export histograms and counters with `i18n.backend.fs_multifile.` prefix; include `layout` attribute on counters
- [x] 3.7 Author `src/backend.ts` — port `MultiFileFsBackend` class from monolith L67-L284; rewire imports; use helpers from `src/path-resolver.ts`, `src/payload-codec.ts`, `src/fs-error-classifier.ts`; emit metrics via `src/observability.ts`; add optional `signal?: AbortSignal` parameter on `read` / `save` and throw `I18nError(I18N.BACKEND_TIMEOUT)` when signal aborts; LEADING SOURCE COMMENT: `// Server-tier package — uses node:fs/promises. Browser consumers: use @suaveplan/i18n-backend-http or @suaveplan/i18n-backend-storage.`
## 4. Factory function
- [x] 4.1 Author `src/factory.ts` — `multiFileFsBackend(options: MultiFileFsBackendOptions): MultiFileFsBackend` returning `new MultiFileFsBackend(options)`
## 5. Documentation (sibling `.md` per implementation file)
- [x] 5.1 `src/backend.md` — port from `packages/i18n/i18n/src/core/backend/multi-file-fs/multi-file-fs-backend.md`; ≥200 words; opens with server-tier marker; basic + advanced examples (per layout, with file-watch, with YAML peer)
- [x] 5.2 `src/factory.md` — ≥200 words; basic + advanced examples
- [x] 5.3 `src/path-resolver.md` — per-layout path shapes; glob substitution rules
- [x] 5.4 `src/payload-codec.md` — supported extensions; peer-detection contract; missing-peer error shape
- [x] 5.5 `src/fs-error-classifier.md` — errno → I18N code mapping table
- [x] 5.6 `src/observability.md` — emitted metrics + units + cardinality
- [x] 5.7 `README.md` `## Modules` section links every `.md`; opens with `> **Server-tier package** — uses`node:fs/promises`. For browser consumers, use`@suaveplan/i18n-backend-http` or `@suaveplan/i18n-backend-storage`.`
## 6. Tests (Vitest, runner-neutral imports per CLAUDE.md §11)
- [x] 6.1 `src/backend.test.ts` — port every test from `packages/i18n/i18n/src/core/backend/multi-file-fs/multi-file-fs-backend.test.ts` (556 lines); same `it(...)` titles for conformance traceability; cover `nested`, `flat`, `glob` layouts; `read` hit/miss/parse-failure; `save` round-trip; `lastModified`; `init` / `dispose` lifecycle; YAML / JSON5 peer-detect happy path AND missing-peer path; `fs.watch` integration via injected `MultiFileFsLike`
- [x] 6.2 `src/backend.spec-conformance.test.ts` — one `it(...)` per spec requirement; titles match `**Verified by:**` lines exactly
- [x] 6.3 `src/factory.test.ts` — `multiFileFsBackend()` returns `MultiFileFsBackend` instance; default `name === "multi-file-fs"`
- [x] 6.4 `src/path-resolver.test.ts` — `nested` produces `<root>/<locale>/<namespace>.<ext>`; `flat` produces `<root>/<locale>.<namespace>.<ext>`; `glob` substitutes both placeholders
- [x] 6.5 `src/payload-codec.test.ts` — JSON parse; YAML peer-detect happy + missing; JSON5 peer-detect happy + missing; unknown extension → `BACKEND_READ_FAILED`; non-object payload → `BACKEND_READ_FAILED`; encode for unsupported extension → `BACKEND_WRITE_FAILED`
- [x] 6.6 `src/fs-error-classifier.test.ts` — `ENOENT` and `ENOTDIR` → `BACKEND_LOCALE_NOT_AVAILABLE`; `EACCES` and `EBUSY` → `BACKEND_READ_FAILED`
- [x] 6.7 `src/index.test.ts` — every documented public export resolves through `.`, `./backend`, `./factory`, `./path-resolver`, `./payload-codec`, `./fs-error-classifier`; `package.json` declares `engines.node >=20` AND omits `browser` field
- [x] 6.8 Add an AbortSignal cancellation test — pre-aborted signal causes `read` to throw `I18nError(I18N.BACKEND_TIMEOUT)`
- [x] 6.9 Every `it(...)` carries `{ timeout: 5000 }`
- [x] 6.10 All test imports go through `@suaveplan/testing/runner` (CLAUDE.md §11) — verified by `grep -rnE "from ['\"](vitest|bun:test)['\"]" packages/i18n/i18n-backend-fs-multifile/src/` returning zero hits
- [x] 6.11 No `// biome-ignore` suppressions (CLAUDE.md §9)
- [x] 6.12 No `export *` in any non-root barrel (CLAUDE.md §10)
## 7. Pre-archive verification (5-gate triple)
- [x] 7.1 `bun run check` — Biome 0/0/0 across the monorepo
- [x] 7.2 `bunx turbo lint typecheck test --filter=@suaveplan/i18n-backend-fs-multifile` — green
- [x] 7.3 `bunx turbo test:coverage --filter=@suaveplan/i18n-backend-fs-multifile` — 100/100/100/100
- [x] 7.4 `bun scripts/openspec-conformance.ts @suaveplan/i18n-backend-fs-multifile` — exit 0
- [x] 7.5 `bunx turbo build --filter=@suaveplan/i18n-backend-fs-multifile` — green
## 8. Pre-archive 7-row checklist (AGENTS.md Section 13)
- [x] 8.1 Generics threaded — `MultiFileFsLike` injection contract typed; layout discriminator union exhaustively narrowed
- [x] 8.2 Zod schemas wired at the public boundary — `MultiFileFsBackendOptionsSchema` parses constructor input; `globPattern` required when `layout: "glob"` enforced via `INVALID_OPTIONS`
- [x] 8.3 Every error code thrown by at least one production path: `INVALID_OPTIONS` (constructor), `BACKEND_LOCALE_NOT_AVAILABLE` (ENOENT/ENOTDIR), `BACKEND_READ_FAILED` (other fs errors / parse failure / unknown extension / missing peer), `BACKEND_WRITE_FAILED` (writeFile failure / unsupported extension), `BACKEND_TIMEOUT` (signal aborted)
- [x] 8.4 Strict-mode tested — exhaustive switch on `MultiFileFsLayout` enforced via `expectTypeOf`
- [x] 8.5 OTel histograms emitted from production paths: `i18n.backend.fs_multifile.read_duration_ms`, `i18n.backend.fs_multifile.write_duration_ms`; counters with `layout` attribute
- [x] 8.6 Lifecycle dedup — `init` is idempotent (re-calling does not start a second watcher); `dispose` is idempotent (re-calling is a no-op)
- [x] 8.7 No retroactive tick — every box reflects work done in this change
## 9. Manifest + ecosystem awareness
- [x] 9.1 Add `@suaveplan/i18n-backend-fs-multifile` entry to root `CLAUDE.md` package listing under the i18n cluster with a `(server-tier)` marker
- [x] 9.2 Run `bun scripts/folder-reorg/generate-manifest.ts` to refresh `MANIFEST.md`
- [x] 9.3 Append `@suaveplan/i18n-backend-fs-multifile` to `STATUS.md` Completed list with a server-tier marker
## 10. Archive
- [x] 10.1 Fold spec delta into `openspec/specs/i18n-backend-fs-multifile/spec.md` (NEW capability spec — first delta becomes canonical)
- [x] 10.2 Move `openspec/changes/i18n-backend-fs-multifile/` → `openspec/archive/<yyyy-mm-dd>-i18n-backend-fs-multifile/`