perf: resolve a commission's render projects with a by-id batch (#4148) - #4254
Conversation
Add a `?ids=a,b,c` batch filter to GET /creative-director (backed by a getProjectsByIds read on both the PG and file project backends) and switch the Creative Commission detail page to it, so resolving a commission's render history costs one bounded round trip instead of fetching every Creative Director project on the install.
Review follow-ups: extract csvIdsParam into lib/sharedSchemas.js (catalog keeps its silent-truncate contract via the truncate flag), move the client's by-ids fetch into services/apiBatch.js so the catalog and Creative Director helpers share one implementation, and cover the wrapper's dedupe / empty-list short-circuit / envelope unwrap with a service test.
Review round 1 —
|
…ds (#4148) Codex review follow-ups: - csvIdsParam now normalizes the repeated ?ids=a&ids=b array form through the same trim / blank-drop / cap path as the CSV form, instead of passing arrays through unnormalized. - The Creative Director batch accepts 120-char ids (the per-record peer-sync recordId bound) rather than the helper's 64-char default, so a project that arrived from a peer is still resolvable. - fetchByIds trims ids and drops non-strings, so a whitespace-only id can no longer serialize to a blank ?ids= that the server reads as absent and answers with the full unfiltered list. normalizeIds is shared with apiCatalog's request-order re-sort so both index the same normalized list.
Review round 2 —
|
Reviewer status
In place of the blocked confirmation pass, the three fixes are each pinned by a test that fails without them: the array-form normalization and the 120-char peer id in |
Summary
client/src/pages/CreativeCommissionDetail.jsxfetched every Creative Director project (non-slim) on load just to index the ones itsruns[].projectIdreferenced — cost scaled with the install's total project count rather than with the commission.This adds a by-id batch resolver and points the page at it:
GET /creative-director?ids=a,b,c— a batch filter on the existing list route, mirroring the catalog'sGET /catalog/ingredients?ids=precedent (no new path, so nothing can shadow/:id). Validated by a newcreativeDirectorProjectQuerySchemainserver/lib/creativeDirectorValidation.js.getProjectsByIds(ids, { includeDeleted })on both project backends (projectsDB.jsusesWHERE id = ANY($1);projectsFile.jsfilters the loaded array) plus thelocal.jsdispatcher — one round trip, tombstones excluded by default, unknown ids simply absent.getCreativeDirectorProjectsByIds()inclient/src/services/apiCreativeDirector.js— de-dupes/drops blank ids and short-circuits an empty list with no request.{ silent: true }since it owns a.catchthat degrades to status-only cards (clientCLAUDE.md: custom catch ⇒ silent).Decisions made while implementing
CREATIVE_DIRECTOR_IDS_BATCH_MAX), 2x the Creative CommissionMAX_PERSISTED_RUNS = 50, so the real consumer never has to chunk.Test plan
cd server && NODE_ENV=test npm test— 1389 files / 29065 tests pass. The only 4 failures (routes/health.test.js,services/updateExecutor.test.js) reproduce identically on a cleanorigin/maincheckout and are environmental (an ambientPORTOS_FORCE_CLEAN_WORKSPACESenv var + gh/forge reachability), not from this change.cd client && NODE_ENV=test npm test— 644 files / 7842 tests pass.server/routes/creativeDirector.test.js—?ids=dispatches togetProjectsByIdsand never callslistProjects; blank/whitespace ids trim and an all-blank CSV falls back to the full list; an over-cap batch 400s without touching either service.server/services/creativeDirector/local.test.js— file-backend batch: only requested live projects, tombstones excluded unlessincludeDeleted, empty/blank list short-circuits without reading the store.server/services/creativeDirector/projectsDB.test.js— PG round-trip batch case (skips without a test DB; runs undernpm run test:db).client/src/pages/CreativeCommissionDetail.test.jsx(new) — only the referenced ids go out (de-duplicated), the whole-list route is never called, no request at all when no run references a project, and an unresolved id degrades to the "render unavailable" card. Verified 2 of the 3 cases fail against the pre-fix page.Closes #4148