# Handoff Report — Explorer 2 (Milestone 2: Backend & DB) ## 1. Observation We observed the following definitions and implementations in the codebase: 1. **Prisma Schema (`apps/backend/prisma/schema.prisma`)**: - `Menu` model (lines 638-646): ```prisma model Menu { id Int @id @default(autoincrement()) name String @unique // e.g., "header", "footer" payload Json? isActive Boolean @default(true) items MenuItem[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } ``` - `MenuItem` model (lines 648-658): ```prisma model MenuItem { id Int @id @default(autoincrement()) menuId Int menu Menu @relation(fields: [menuId], references: [id], onDelete: Cascade) label String url String order Int @default(0) translations MenuItemTranslation[] createdAt DateTime @default(now()) updatedAt DateTime @updatedAt } ``` 2. **Shared Types (`packages/types/index.ts`)**: - Interfaces `MenuItem` and `Menu` (lines 195-208): ```typescript export interface MenuItem { id: number label: string url: string order: number translations?: any[] } export interface Menu { id: number name: string items: MenuItem[] payload?: any } ``` 3. **Backend Routes (`apps/backend/src/routes/menus.ts`)**: - Validation schema of `POST /` body (lines 50-59): ```typescript body: t.Object({ name: t.String(), items: t.Optional(t.Array(t.Object({ label: t.String(), url: t.String(), order: t.Number() }))) }) ``` - Validation schema of `PUT /:name` body (lines 132-147): ```typescript body: t.Object({ payload: t.Optional(t.Any()), isActive: t.Optional(t.Boolean()), items: t.Array(t.Object({ id: t.Optional(t.Number()), label: t.String(), url: t.String(), order: t.Number(), translations: t.Optional(t.Array(t.Object({ langCode: t.String(), label: t.String() }))) })) }) ``` - Database operations mapping in `PUT /:name` (lines 90-118): ```typescript if (item.id && existingItemIds.includes(item.id)) { await tx.menuItem.update({ where: { id: item.id }, data: { label: item.label, url: item.url, order: item.order } }); // ... } else { await tx.menuItem.create({ data: { menuId: menu!.id, label: item.label, url: item.url, order: item.order, translations: item.translations?.length ? { ... } : undefined } }); } ``` 4. **MCP Tools (`apps/mcp-server/menu-tools.ts` and `apps/mcp-server/menu-toolkit.ts`)**: - `validateMenuItems` helper (lines 158-184): ```typescript if (item.order !== undefined && typeof item.order !== "number") { issues.push(`items[${index}].order must be a number when provided`); } ``` - `upsert_menu` tool definition input schema (lines 34-74): ```json items: { type: "array", description: "Array of menu items", items: { type: "object", properties: { id: { type: "number", description: "ID of existing item (optional)" }, label: { type: "string", description: "Label of the menu item" }, url: { type: "string", description: "URL of the menu item" }, order: { type: "number", description: "Order sequence" }, translations: { ... } }, required: ["label", "url", "order"] } } ``` ## 2. Logic Chain - Adding the new columns to Prisma schema (Observation 1) as optional fields (`rootImage String?`, `columnImage String?`, `hoverImage String?`) ensures database schema compatibility. Running `prisma migrate dev` compiles a safe SQL statement that populates default `NULL` values for existing rows, which avoids downtime or data loss. - Exposing the fields on the shared interfaces (`packages/types/index.ts`, Observation 2) aligns the TypeScript contracts between package users. - In order to save these image fields, we must extend Elysia's schema validator body tags `t.Optional(t.Nullable(t.String()))` (Observation 3) and map the properties directly to the Prisma create/update calls, otherwise Elysia will strip them and Prisma won't save them. - Finally, updating `validateMenuItems` (Observation 4) and the JSON inputSchema of `upsert_menu` (Observation 4) allows external agents/clients to supply the image variables when working via MCP server endpoints. ## 3. Caveats - We assumed frontend (SPA/Website) changes are scheduled under Milestone 3 and thus out of scope for implementation/updates in this backend-only task, though we identified their locations (`apps/spa/src/store/menuStore.ts` and `apps/website/src/components/navigation/menuTypes.ts`). - We did not apply database changes or make code modifications as we are under a read-only constraint. ## 4. Conclusion We have mapped the precise locations, code structures, and type changes required to fully implement `rootImage`, `columnImage`, and `hoverImage` across the database, shared types, backend controllers, and MCP tool schemas. Detailed changes are saved to `/home/ti/git/homaroma/.agents/explorer_m2_2/analysis.md`. ## 5. Verification Method To verify the implementation once the implementer carries out the changes: 1. Run the build/check scripts to ensure zero compilation or type errors: ```bash npm run build ``` 2. Test menu endpoint updates by sending a HTTP PUT to `/api/menus/header` or calling the MCP tool `upsert_menu` with image URLs, then verify the response returns the fields and database contains the updated columns.