From a81af2d71abd779fb84b5f682a4a2b864e785c82 Mon Sep 17 00:00:00 2001 From: simoleo89 Date: Sun, 2 Aug 2026 13:13:58 +0200 Subject: [PATCH] docs: plan production furni asset fix --- .../2026-08-02-production-furni-assets.md | 126 ++++++++++++++++++ 1 file changed, 126 insertions(+) create mode 100644 docs/superpowers/plans/2026-08-02-production-furni-assets.md diff --git a/docs/superpowers/plans/2026-08-02-production-furni-assets.md b/docs/superpowers/plans/2026-08-02-production-furni-assets.md new file mode 100644 index 0000000000..bbe4be4b9a --- /dev/null +++ b/docs/superpowers/plans/2026-08-02-production-furni-assets.md @@ -0,0 +1,126 @@ +# Production Furni Assets Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Make every furni import write the icon, Nitro bundle, and FurnitureData entry into the directories served by the production client under `/var/www/Gamedata`. + +**Architecture:** Extend the central furni asset resolver with a separate Gamedata layout while preserving CMS-local and Nitro-Files targets. Both import paths consume the same resolved targets; FurnitureData resolves the same Gamedata root independently so metadata and binary assets stay aligned. + +**Tech Stack:** TypeScript 7, Node.js filesystem APIs, Vitest 4, Next.js 16. + +## Global Constraints + +- Preserve all existing `nitro_files_root`, `furni_*_dir`, and `furni_data_mirror_path` behavior. +- Auto-detect `/var/www/Gamedata` only when the directory exists; allow `gamedata_root` to override it. +- Do not copy source SWFs into the Gamedata tree. +- Report live mirror failures in the import warnings. +- Use test-first development and run the production build before completion. + +--- + +### Task 1: Resolve the production Gamedata layout + +**Files:** +- Create: `src/lib/services/furni-asset-dirs.test.ts` +- Modify: `src/lib/services/furni-asset-dirs.ts` +- Modify: `src/lib/services/furni-data.ts` +- Modify: `src/app/admin/settings/cms-settings-config.ts` + +**Interfaces:** +- Produces: `getGamedataRoot(): Promise`. +- Produces: Gamedata mirror entries from `getFurniAssetWriteTargets()` with `/icons` and `/bundled/furniture`. +- Consumes: `siteSettings.get("gamedata_root", "")` and `existsSync(DEFAULT_GAMEDATA_ROOT)`. + +- [ ] **Step 1: Write failing resolver tests** + +Mock `siteSettings.get` and `existsSync`, then assert that a configured Gamedata root produces: + +```ts +expect(targets.mirrorDirs).toContainEqual({ + swfDir: targets.swfDir, + iconDir: path.join(gamedataRoot, "icons"), + nitroDir: path.join(gamedataRoot, "bundled/furniture"), +}); +``` + +Also assert that an absent unconfigured root adds no Gamedata mirror and that a duplicate primary destination is de-duplicated. + +- [ ] **Step 2: Run the resolver test and verify RED** + +Run: `pnpm test src/lib/services/furni-asset-dirs.test.ts` + +Expected: FAIL because `gamedata_root` is not read and the Gamedata mirror is absent. + +- [ ] **Step 3: Implement the Gamedata resolver** + +Add `DEFAULT_GAMEDATA_ROOT`, `getGamedataRoot()`, and a pure Gamedata mapping that uses the primary `swfDir` while mapping icons and Nitro bundles to the live locations. Merge this candidate with the existing Nitro-Files candidate and remove identical directory triples. + +- [ ] **Step 4: Extend FurnitureData and the settings form** + +Make `getFurnitureDataWritePaths()` include `/config/FurnitureData.json`, after any explicit `furni_data_mirror_path`. Add a documented `gamedata_root` text setting with `/var/www/Gamedata` as the placeholder. + +- [ ] **Step 5: Run resolver tests and verify GREEN** + +Run: `pnpm test src/lib/services/furni-asset-dirs.test.ts` + +Expected: PASS. + +- [ ] **Step 6: Commit Task 1** + +```text +fix: map furni imports to production gamedata +``` + +### Task 2: Mirror manual Nitro uploads + +**Files:** +- Create: `src/lib/services/upload-import.test.ts` +- Modify: `src/lib/services/upload-import.ts` + +**Interfaces:** +- Consumes: `getFurniAssetWriteTargets(): Promise`. +- Produces: manual upload copies at every unique `mirrorDirs[].iconDir` and `mirrorDirs[].nitroDir`. + +- [ ] **Step 1: Write a failing manual-upload test** + +Mock the database and metadata dependencies, provide temporary primary and Gamedata directories, call `uploadSingleFurni`, and assert: + +```ts +await expect(fs.readFile(path.join(liveNitroDir, "chair.nitro"))).resolves.toEqual(nitroBuffer); +await expect(fs.readFile(path.join(liveIconDir, "chair_icon.png"))).resolves.toEqual(iconBuffer); +``` + +- [ ] **Step 2: Run the upload test and verify RED** + +Run: `pnpm test src/lib/services/upload-import.test.ts` + +Expected: FAIL because manual uploads currently write only to the primary CMS directories. + +- [ ] **Step 3: Implement manual mirroring** + +Resolve all write targets, create their directories through the existing `ensureDirectories()`, and copy the successfully written primary Nitro and optional icon files to each unique mirror. Catch each copy error separately and append a warning containing the destination path. + +- [ ] **Step 4: Run the upload test and verify GREEN** + +Run: `pnpm test src/lib/services/upload-import.test.ts` + +Expected: PASS. + +- [ ] **Step 5: Run focused and full verification** + +Run: + +```text +pnpm test src/lib/services/furni-asset-dirs.test.ts src/lib/services/upload-import.test.ts src/lib/services/furni-import.test.ts +pnpm typecheck +pnpm test +pnpm build +``` + +Expected: every command exits with status 0. + +- [ ] **Step 6: Commit Task 2** + +```text +fix: mirror manual furni uploads to live assets +```