Source: ddx-documentation/research/rq11-dev-latency.md
**Question** (`ddx-wip/entity-kernel/INPUT.md` §6): why does every first compile take 5-14 s, and why does next-server reach ~6 GB RSS? This page is the **pre-change baseline** (entity-kernel task 001), taken before task 004 edits the `@/inventory` barrel. Task 010 re-runs the same commands and compares against these numbers. **Answer (baseline):** a cold route costs **5.5-16.0 s of Next compile time**, and next-server holds **~6.0 GB RSS 51 s after a clean start** (a compile peak: 466 MB idle at ~3 min). Both login and the projects page reach the full UI graph: the `(auth)` layout imports `SiteShell` from the full `@/inventory` barrel (15 `export *` entry files), and the login client imports `EntityForm` from `@/inventory/client`, which pulls Tiptap. ## Setup | Item | Value | |---|---| | Date / host | 2026-10-23, dudoxxmac | | Next.js | 16.4.2 (Turbopack dev), `cacheComponents`, `reactCompiler`, `typedRoutes` | | Dev cache | `turbopackFileSystemCacheForDev: false`, `turbopackGc: true` (`ddx-web/next.config.ts`) | | Start | `./ddx-manage.sh restart --clean` at 13:33:17; `.next` wiped (was 745 MB) | | ddx-api | already up on 16310 (not managed by the script); not restarted | | Seed user | `doctor@acme-health.test`, org `acme-health`, role segment `doctor` | ## Cold compile per route (from `logs/ddx-web.log`) The log line is the measurement; curl only triggers it. In the Next 16 log, `next.js:` = framework time (compile + RSC render), `application-code:` = our render code, `generate-params:` = params. | Route | Total | next.js (compile) ms | application-code (render) ms | Status | Who triggered | |---|---|---|---|---|---| | `/en/auth/register` | 16.5 s | 15100 ms | 1049 ms | 200 | owner browser (first route after restart, concurrent with gallery) | | `/en/gallery/ConnectionBanner` | 17.3 s | 16000 ms | 496 ms | 200 | owner browser (concurrent with register) | | `/en/auth/login` | 6.0 s | 5500 ms | 93 ms | 200 | probe 1 (curl) | | `POST /api/v1/auth/sign-in/email` | 103 ms | 3 ms | 101 ms | 200 | probe 2 (curl, sign-in for the cookie jar) | | `/en/portal/acme-health/doctor/projects` | 9.5 s | 8600 ms | 578 ms | 200 | probe 3 (curl, signed in) | | `/en/auth/register` (2nd hit) | 240 ms | 176 ms | 60 ms | 200 | probe 4 (curl) — already warm | | `/en/gallery` (index) | — | not measured | — | — | probe budget (max 4) spent; the gallery entry row above is the gallery cold number | Raw log lines (ANSI stripped): ``` GET /en/auth/register 200 in 16.5s (next.js: 15.1s, proxy.ts: 6ms, generate-params: 419ms, application-code: 1049ms) GET /en/gallery/ConnectionBanner 200 in 17.3s (next.js: 16.0s, proxy.ts: 55ms, generate-params: 728ms, application-code: 496ms) GET /en/auth/login 200 in 6.0s (next.js: 5.5s, proxy.ts: 3ms, generate-params: 366ms, application-code: 93ms) POST /api/v1/auth/sign-in/email 200 in 103ms (next.js: 3ms, application-code: 101ms) GET /en/portal/acme-health/doctor/projects 200 in 9.5s (next.js: 8.6s, proxy.ts: 2ms, generate-params: 410ms, application-code: 578ms) GET /en/auth/register 200 in 240ms (next.js: 176ms, proxy.ts: 1.8ms, generate-params: 1.7ms, application-code: 60ms) ``` Read the numbers with care: - The owner's browser hit register + a gallery entry at the same time, right after the restart. Those two compiled in parallel and also built the shared graph (locale layout, `(auth)` layout, full barrel). So their 15-16 s includes the shared graph; login's 5.5 s is "cold route, warm shared modules". A login hit as the very first request would cost more. - The projects page compiled the `(app)` layout + portal shell graph on top: 8.6 s. ## next-server RSS and `.next` size `ps -o rss=` on the pid that listens on 16300 (KiB; MB = KiB / 1024). | Sample | Uptime | RSS | `.next` | |---|---|---|---| | Before restart (warm, owner browsing) | 3 min 59 s | 2,078,544 KiB = **2030 MB** | 745 MB | | After the 4 probes (+ owner's 2 routes) | 51 s | 6,158,400 KiB = **6014 MB** | 348 MB | | Second sample, idle (after the heavy-dep map) | ~3 min (2 min 42 s) | 477,232 KiB = **466 MB** | 348 MB | - The 6014 MB sample is a **peak during the cold compiles** (3 routes compiling, 2 in parallel). With `turbopackGc: true` the process fell to 466 MB about 2 min later with no new request. - This baseline has **no long-uptime sample**. Task 010 takes it: RSS after 15 min idle-with-HMR (or after a wave of edits lands), with the same `ps -o rss=` command. INPUT.md §6 reports 6.3 GB after ~15 min; that number is not reproduced here. ## Module graph reachable from `/en/auth/login` - `rg -c 'export \* from' ddx-web/src/inventory/index.ts` → **15** (`index.ts:83-97`). The same file also imports all 15 entry arrays statically to build the `inventory` registry (`index.ts:19-33`), so any import of `@/inventory` evaluates every entry file. - Login route chain: `app/[locale]/(auth)/auth/layout.tsx:18` imports `SiteShell` from `@/inventory` (full barrel) → all 15 entries. `auth/login/login-client.tsx:21` imports `EntityForm` from `@/inventory/client` → `components/forms/EntityForm.tsx:38` `Field` → `components/forms/Field.tsx:28` `RichTextEditor` → `@tiptap/react` (the client slice also reaches Tiptap). - Projects route: `portal/[slug]/[role]/projects/page.tsx:18` imports from `@/inventory`. ### Heavy dependency → component → inventory entry | Dependency | Importing component | Inventory entry that makes it reachable | |---|---|---| | `pdfjs-dist` | `components/media/PdfViewer.tsx:40` (lazy `await import()`, own chunk) | `inventory/entries/i-media.ts:21,36` (also via `components/media/DocumentViewer.tsx:25`) | | `@react-pdf/renderer` | `components/media/PdfDocument.tsx` (static) | `inventory/entries/i-media.ts:20,35` | | `@tiptap/react` (+ `@tiptap/*`) | `components/forms/RichTextEditor.tsx`, `forms/editor/{SlashMenu.tsx,EditorToolbar.tsx,presets.ts}` | `inventory/entries/d-forms.ts:24,161`; also `forms/Field.tsx:28` → `EntityForm` → `inventory/client.ts:30` | | `recharts` | `components/data/Chart.tsx` | `inventory/entries/c-data.ts:27,69` (also `components/portal/templates/PortalPageDashboard.tsx:16`) | | `streamdown` | `components/realtime/StreamingText.tsx`, `realtime/types.ts` | `inventory/entries/f-realtime.ts:19,182` (also `components/chat/ChatMessage.tsx:18` → g-chat, `realtime/PortalPageStream.tsx:25`) | ## Re-runnable commands Run from the repo root. One request per route; no loops (owner rule). ```sh ./ddx-manage.sh status ./ddx-manage.sh restart --clean # wipes ddx-web/.next, cold compile per route until rg -q 'Ready in' logs/ddx-web.log; do sleep 1; done B=http://localhost:16300; J=/tmp/ek-jar.txt curl -s -o /dev/null -w '%{http_code} %{time_total}\n' $B/en/auth/login # one sign-in through the BFF; password read from ddx-api/.env SEED_USER_PASSWORD into a shell var jq -nc --arg e doctor@acme-health.test --arg p "$PW" '{email:$e,password:$p}' \ | curl -s -o /dev/null -c $J -H 'Content-Type: application/json' -H "Origin: $B" \ --data-binary @- -w '%{http_code} %{time_total}\n' $B/api/v1/auth/sign-in/email curl -s -o /dev/null -b $J -w '%{http_code} %{time_total}\n' $B/en/portal/acme-health/doctor/projects curl -s -o /dev/null -w '%{http_code} %{time_total}\n' $B/en/auth/register # or /en/gallery sed 's/\x1b\[[0-9;]*m//g' logs/ddx-web.log | rg ' (GET|POST) ' P=$(lsof -nP -tiTCP:16300 -sTCP:LISTEN); ps -o rss=,etime= -p $P; du -sh ddx-web/.next # graph rg -c 'export \* from' ddx-web/src/inventory/index.ts for d in pdfjs-dist @react-pdf/renderer @tiptap/react recharts streamdown; do rg -l --fixed-strings "from '$d" ddx-web/src; done ``` For a clean comparison in task 010: ask the owner to keep the browser closed until the probes finish, so no route compiles in parallel with the probes. ## After barrel split + lazy families (task 010) Re-run of the commands above on 2026-10-23, 14:08-14:30, dudoxxmac, after task 004 (per-family `@/inventory/<family>` subpaths) and task 007 (`next/dynamic` bodies for Tiptap, recharts, pdfjs-dist). Same 4 probes per run, one per route. ddx-api was not restarted by this task (its watch rebuilt itself at 14:12:07 after a sibling edit; that made the run-E sign-in a 502). **Non-probe traffic.** An open browser tab on `/en/auth/register` reloaded after every restart and sent `GET /en/auth/register` + `GET /api/v1/auth/get-session`. In runs A, B and D it compiled in parallel with the login probe. Run C is the clean comparison: the tab's register hit came first, then login compiled alone — the same order as the baseline (owner routes first, then login). During the 15-min watch, task 011 sent 6 BFF API calls (`/api/v1/orgs…`, `/api/v1/me/profile`) and sibling edits caused 2 HMR compiles (`✓ Compiled in 1781ms`, `1103ms`). ### Cold compile, `turbopackFileSystemCacheForDev: false`, `.next` wiped (run C) | Route | Baseline next.js ms (001) | Now next.js ms | Change | |---|---|---|---| | `/en/auth/login` | 5500 | **5200** | -5 % | | `/en/portal/acme-health/doctor/projects` | 8600 | **7100** | -17 % | | `/en/auth/register` (cold, tab hit) | 15100 (parallel with gallery) | 8800 | -42 % (not same load) | | `/en/auth/register` (2nd hit, warm) | 176 | 154 | — | ``` GET /en/auth/register 200 in 9.4s (next.js: 8.8s, proxy.ts: 50ms, generate-params: 222ms, application-code: 393ms) GET /en/auth/login 200 in 5.5s (next.js: 5.2s, proxy.ts: 3ms, generate-params: 214ms, application-code: 74ms) POST /api/v1/auth/sign-in/email 200 in 91ms (next.js: 2ms, application-code: 88ms) GET /en/portal/acme-health/doctor/projects 200 in 7.8s (next.js: 7.1s, proxy.ts: 2ms, generate-params: 250ms, application-code: 415ms) GET /en/auth/register 200 in 217ms (next.js: 154ms, proxy.ts: 1.9ms, generate-params: 1.8ms, application-code: 59ms) ``` Other cold runs (same flag, `.next` wiped), skewed by the parallel tab compile: run A login 6100 ms / projects 7300 ms; run B login 10900 ms / projects 7100 ms. **Stretch target (login cold < 1500 ms): NOT met cold** — 5200 ms. Met only on a warm restart with the fs cache (1409 ms, below). The login graph still compiles the whole client slice: `inventory/client.ts` brings DataTable, PageWizard, PageDialog, PageForm, FileDropzone, ConfirmDialog and SseProvider into the login SSR chunk (`chunks/ssr/ddx-web_src_1ow-ogk8v5sjt._.js`). A smaller login import set (EntityForm + Button only) is the next lever. ### Module trace for `/en/auth/login` (run A build, `.next/dev/server/app/[locale]/(auth)/auth/login/`) | Check | Result | |---|---| | `page_client-reference-manifest.js` (208,290 bytes): `pdfjs-dist`, `@tiptap`, `recharts`, `react-pdf`, `streamdown`, `components/chat/`, `components/call/`, `components/voice/` | **0** hits each | | `RichTextEditorBody` in the client manifest | 19 hits — the `next/dynamic` loader stub chunk only | | `page/react-loadable-manifest.json` | the ONE lazy entry: `RichTextEditorBody.tsx (next/dynamic entry)` → `0r31_@tiptap_core_dist_…js`, `0eb9_prosemirror-view_dist_…js` + 2 more. Loaded only when a rich-text field mounts; login has none. | | 30 server chunks named in `page.js`, scanned for the same 6 strings | 1 hit: `recharts` in `chunks/ssr/ddx-web_src_1ow-ogk8v5sjt._.js` — a **code comment** in the `inventory/client.ts` header, not a module | So Tiptap, recharts, pdfjs-dist, chat and call code are not in the login route's compiled graph. ### fs dev cache trial (`turbopackFileSystemCacheForDev: true`, `turbopackGc: true`) | Run | Start | login ms | projects ms | register ms | `.next` | listener RSS | |---|---|---|---|---|---|---| | D control: flag OFF, `--keep-cache` | warm `.next`, no fs cache | 10700 (parallel tab) | 6800 | 133 (2nd hit) | 259 MB → 260 MB | 5017 MB @21 s | | E: flag ON, `.next` wiped (fills cache) | cold | 8800 | 7700 (E2) | 5000 | 192 MB → 824 MB | 5337 MB @41 s | | **F: flag ON, `--keep-cache` (warm restart)** | warm | **1409** | **421** | **243** | 1.1 GB | 2650 MB @6 s | | G: flag ON, new default restart (cache kept) | warm, after sibling edits | 5800 | 353 | 234 | 1.2 GB | 3995 MB @10 s | ``` F GET /en/auth/login 200 in 2.1s (next.js: 1409ms, proxy.ts: 48ms, generate-params: 242ms, application-code: 354ms) F GET /en/portal/acme-health/doctor/projects 200 in 1100ms (next.js: 421ms, proxy.ts: 3ms, generate-params: 240ms, application-code: 436ms) F GET /en/auth/register 200 in 538ms (next.js: 243ms, proxy.ts: 2ms, generate-params: 223ms, application-code: 70ms) G GET /en/auth/login 200 in 6.5s (next.js: 5.8s, proxy.ts: 49ms, generate-params: 236ms, application-code: 389ms) ``` - Warm restart vs cold (run C): login **-73 %** (1409 vs 5200), projects **-94 %** (421 vs 7100). - Run D shows that keeping `.next` without the fs cache gives nothing (projects 6800 ms). - Run G: login compiled cold again because sibling tasks edited files in its graph during the watch (`components/forms/EntityForm.tsx`, `lib/di/client-factory.ts`, `components/providers/SseProvider.tsx`). Projects and register stayed warm. The cache helps every route whose imports did not change. ### RSS and `.next` over 15 min (flag ON, from run F, `/tmp/ek010/watch.sh`, 60 s samples) | Uptime | listener RSS | tree RSS (`./ddx-manage.sh mem web`) | `.next` | |---|---|---|---| | 17 s | 2797 MB | 2969 MB (8 proc) | 1116 MB | | 1 min 17 s – 4 min 20 s | 2475 MB | 2646 MB | 1116 MB | | 5 min 21 s – 9 min 24 s | 922-925 MB | 1018-1021 MB | 1142 MB | | 10 min 25 s – 12 min 27 s | 1047 MB | 1143 MB | 1145 MB | | 13 min 28 s | 1135 MB | 1359 MB (9 proc) | 1145 MB | | 14 min 29 s | 1925 MB | 2310 MB (10 proc) | 1212 MB | | **15 min 30 s** | **2073 MB** | **2444 MB (10 proc)** | **1178 MB** | - Peaks: `.next` 1212 MB, tree RSS 2969 MB. Both under the thresholds (2 GB, 3 GB). - Seconds after a COLD compile the listener RSS is higher with either flag value (run C, flag OFF: 5024 MB @24 s; run G, flag ON, login recompiling: 3995 MB @10 s). That is the transient compile peak, not the steady state; the 3 GB threshold is judged on the 60 s samples above. - The climb at 13-15 min follows the 2 HMR compiles from sibling edits (light load). - **Long-uptime sample with the flag OFF** (before this task's first restart, 31 min 20 s uptime, owner browsing + sibling edits): listener **6163 MB** (tree 7115 MB), `.next` 565 MB. So the 466 MB idle drop that task 001 saw at ~3 min was not lasting: memory climbs back with HMR over time. With the flag ON this run reached 2073 MB at 15 min 30 s; the loads differ, so this is not a like-for-like RSS comparison. ### Decision **Keep `turbopackFileSystemCacheForDev: true`.** All three task thresholds pass: warm-restart compile -73 % (login) / -94 % (projects) ≥ 40 %; `.next` max 1212 MB < 2 GB; RSS max 2969 MB < 3 GB. `turbopackGc: true` stays. `ddx-manage.sh` `start` / `restart` now KEEP `.next` and wipe it only when it passes `WEB_CACHE_CAP_MB=2048` (the old 7.9 GB growth, task 041, is the case the cap catches); `--clean` forces a wipe, `--keep-cache` never wipes. Caveat: the 15-min watch ran under light load (2 HMR compiles); task 041's growth came from heavy parallel edits. If a later wave pushes `.next` past 2 GB in one session, the cap wipes it on the next restart — re-measure then. Commands this section used (from the repo root): `./ddx-manage.sh restart web`, `./ddx-manage.sh restart web --keep-cache`, `/tmp/ek010/probe.sh` (the 4 rq11 probes + `ps -o rss=,etime=` + `du -sh ddx-web/.next` + the log filter), `/tmp/ek010/trace.sh` and `trace2.sh` (manifest `rg -F` counts), `/tmp/ek010/watch.sh` (16 × 60 s samples). ## Reverted (2026-10-23, lead, same day) The fs dev cache went back to `turbopackFileSystemCacheForDev: false`. The 15-min watch above ran under light load (2 HMR compiles). Under a real working day with parallel workers editing: | Evidence | Where | |---|---| | `[Server HMR] Update failed, re-evaluating modules: … conflicting effects for the same key` on every save | `logs/ddx-web.log` (owner paste) | | `.next` 2262 MB after ~1 h (cap 2048 MB); 1.1 GB again ~10 min after a `--clean` | `./ddx-manage.sh restart web --clean` output, `du -sh` | | Stale route module: `profileDisplayName is not a function` on `/profile/print` while the export existed and the sibling `/profile` page used it fine | `logs/ddx-web.log`, jev-browser | Decision: correct modules beat a faster warm restart. Cold first compile stays ~5.2 s (login); the barrel split and lazy heavy families (tasks 004, 007) keep the compiled graph small. ## 16.4.3 re-trial (2026-10-24) Plan `ddx-web-speed-ui`: Next 16.4.2 → 16.4.3, all ddx-web deps to the latest stable (majors included), the fs dev cache back to its default (ON) with three Turbopack flags, plus the dev-graph cuts of task 002 (`@/inventory/client` split into `@/inventory/client/<family>` slices, `KanbanBoard`, `streamdown` and the heavy `Field.tsx` controls loaded lazily, `PdfDocument` moved to `@/inventory/media-server`). The per-lever split was not measured separately; the table is the whole plan, one probe before and one after. Method: `./ddx-manage.sh restart web --clean`, then `sh scripts/dev-latency-probe.sh <label>` (`baseline-16.4.2`, `after-16.4.3`). Source: `findings/ddx-web-speed-ui/baseline.md`. Both runs had an owner browser tab compiling another route in parallel (16.1 s before, 2.4 s after), so the numbers carry that noise. | Route | Before (16.4.2) compile | After (16.4.3) compile | Change | |---|---|---|---| | login | 8400 ms | 2600 ms | -69 % | | projects | 6300 ms | 1540 ms | -76 % | | register | 4500 ms | 901 ms | -80 % | | next-server RSS | 6324 MB @ 36 s | 2269 MB @ 9 s, 3538 MB @ 34 s | — | Owner target (cold login and projects each < 2 s, RSS < 3 GB): projects and register meet it, login does not (2.6 s). The plan gate (each route >= 40 % faster) is met. The RSS gate was defined at the 60 s sample; the recorded samples are 9 s and 34 s, and the 34 s value is above 3 GB. ### Flags now set (`ddx-web/next.config.ts`) | Key | Value | |---|---| | `turbopackFileSystemCacheForDev` | not set (default ON); `false` is forbidden by the plan acceptance | | `turbopackGc` | `true` | | `turbopackLazyDynamicImports` | `true` | | `turbopackRustReactCompiler` | `true` | `optimizePackageImports` is not used (Turbopack does not need it). ### Revert rule The 2026-10-23 trial failed under a real working day (see §Reverted). If the same signs return, set `turbopackFileSystemCacheForDev: false` again and record it here: - `[Server HMR] Update failed, re-evaluating modules: … conflicting effects for the same key` in `logs/ddx-web.log`; - a stale route module (an export that exists on disk is reported missing). Whether those signs return under 16.4.3 is not established by this probe: the probe is a cold-start measurement, and the jev-browser working-day pass is the judge for the revert rule. Last reconciled: 2026-10-24 · plan ddx-web-speed-ui
Question (ddx-wip/entity-kernel/INPUT.md §6): why does every first compile take 5-14 s,
and why does next-server reach ~6 GB RSS? This page is the pre-change baseline (entity-kernel
task 001), taken before task 004 edits the @/inventory barrel. Task 010 re-runs the same
commands and compares against these numbers.
Answer (baseline): a cold route costs 5.5-16.0 s of Next compile time, and next-server holds
~6.0 GB RSS 51 s after a clean start (a compile peak: 466 MB idle at ~3 min). Both login and the projects page reach the full UI graph:
the (auth) layout imports SiteShell from the full @/inventory barrel (15 export * entry
files), and the login client imports EntityForm from @/inventory/client, which pulls Tiptap.
logs/ddx-web.log)The log line is the measurement; curl only triggers it. In the Next 16 log, next.js: = framework
time (compile + RSC render), application-code: = our render code, generate-params: = params.
Raw log lines (ANSI stripped):
Read the numbers with care:
(auth) layout,
full barrel). So their 15-16 s includes the shared graph; login's 5.5 s is "cold route, warm
shared modules". A login hit as the very first request would cost more.(app) layout + portal shell graph on top: 8.6 s..next sizeps -o rss= on the pid that listens on 16300 (KiB; MB = KiB / 1024).
turbopackGc: true the process fell to 466 MB about 2 min later with no new request.ps -o rss= command. INPUT.md §6 reports
6.3 GB after ~15 min; that number is not reproduced here./en/auth/loginrg -c 'export \* from' ddx-web/src/inventory/index.ts → 15 (index.ts:83-97). The same
file also imports all 15 entry arrays statically to build the inventory registry
(index.ts:19-33), so any import of @/inventory evaluates every entry file.app/[locale]/(auth)/auth/layout.tsx:18 imports SiteShell from
@/inventory (full barrel) → all 15 entries. auth/login/login-client.tsx:21 imports
EntityForm from @/inventory/client → components/forms/EntityForm.tsx:38 Field →
components/forms/Field.tsx:28 RichTextEditor → @tiptap/react (the client slice also
reaches Tiptap).portal/[slug]/[role]/projects/page.tsx:18 imports from @/inventory.Run from the repo root. One request per route; no loops (owner rule).
For a clean comparison in task 010: ask the owner to keep the browser closed until the probes finish, so no route compiles in parallel with the probes.
Re-run of the commands above on 2026-10-23, 14:08-14:30, dudoxxmac, after task 004 (per-family
@/inventory/<family> subpaths) and task 007 (next/dynamic bodies for Tiptap, recharts,
pdfjs-dist). Same 4 probes per run, one per route. ddx-api was not restarted by this task (its
watch rebuilt itself at 14:12:07 after a sibling edit; that made the run-E sign-in a 502).
Non-probe traffic. An open browser tab on /en/auth/register reloaded after every
restart and sent GET /en/auth/register + GET /api/v1/auth/get-session. In runs A, B and D it
compiled in parallel with the login probe. Run C is the clean comparison: the tab's register
hit came first, then login compiled alone — the same order as the baseline (owner routes first,
then login). During the 15-min watch, task 011 sent 6 BFF API calls (/api/v1/orgs…,
/api/v1/me/profile) and sibling edits caused 2 HMR compiles (✓ Compiled in 1781ms, 1103ms).
turbopackFileSystemCacheForDev: false, .next wiped (run C)Other cold runs (same flag, .next wiped), skewed by the parallel tab compile: run A login
6100 ms / projects 7300 ms; run B login 10900 ms / projects 7100 ms.
Stretch target (login cold < 1500 ms): NOT met cold — 5200 ms. Met only on a warm restart
with the fs cache (1409 ms, below). The login graph still compiles the whole client slice:
inventory/client.ts brings DataTable, PageWizard, PageDialog, PageForm, FileDropzone, ConfirmDialog
and SseProvider into the login SSR chunk (chunks/ssr/ddx-web_src_1ow-ogk8v5sjt._.js). A smaller
login import set (EntityForm + Button only) is the next lever.
/en/auth/login (run A build, .next/dev/server/app/[locale]/(auth)/auth/login/)So Tiptap, recharts, pdfjs-dist, chat and call code are not in the login route's compiled graph.
turbopackFileSystemCacheForDev: true, turbopackGc: true).next without the fs cache gives nothing (projects 6800 ms).components/forms/EntityForm.tsx, lib/di/client-factory.ts,
components/providers/SseProvider.tsx). Projects and register stayed warm. The cache helps
every route whose imports did not change..next over 15 min (flag ON, from run F, /tmp/ek010/watch.sh, 60 s samples).next 1212 MB, tree RSS 2969 MB. Both under the thresholds (2 GB, 3 GB)..next 565 MB. So the
466 MB idle drop that task 001 saw at ~3 min was not lasting: memory climbs back with HMR over
time. With the flag ON this run reached 2073 MB at 15 min 30 s; the loads differ, so this is
not a like-for-like RSS comparison.Keep turbopackFileSystemCacheForDev: true. All three task thresholds pass: warm-restart
compile -73 % (login) / -94 % (projects) ≥ 40 %; .next max 1212 MB < 2 GB; RSS max 2969 MB < 3 GB.
turbopackGc: true stays. ddx-manage.sh start / restart now KEEP .next and wipe it only
when it passes WEB_CACHE_CAP_MB=2048 (the old 7.9 GB growth, task 041, is the case the cap
catches); --clean forces a wipe, --keep-cache never wipes. Caveat: the 15-min watch ran under
light load (2 HMR compiles); task 041's growth came from heavy parallel edits. If a later wave
pushes .next past 2 GB in one session, the cap wipes it on the next restart — re-measure then.
Commands this section used (from the repo root): ./ddx-manage.sh restart web,
./ddx-manage.sh restart web --keep-cache, /tmp/ek010/probe.sh (the 4 rq11 probes + ps -o rss=,etime= + du -sh ddx-web/.next + the log filter), /tmp/ek010/trace.sh and trace2.sh
(manifest rg -F counts), /tmp/ek010/watch.sh (16 × 60 s samples).
The fs dev cache went back to turbopackFileSystemCacheForDev: false. The 15-min watch above ran
under light load (2 HMR compiles). Under a real working day with parallel workers editing:
Decision: correct modules beat a faster warm restart. Cold first compile stays ~5.2 s (login); the barrel split and lazy heavy families (tasks 004, 007) keep the compiled graph small.
Plan ddx-web-speed-ui: Next 16.4.2 → 16.4.3, all ddx-web deps to the latest stable (majors included),
the fs dev cache back to its default (ON) with three Turbopack flags, plus the dev-graph cuts of
task 002 (@/inventory/client split into @/inventory/client/<family> slices, KanbanBoard,
streamdown and the heavy Field.tsx controls loaded lazily, PdfDocument moved to
@/inventory/media-server). The per-lever split was not measured separately; the table is the
whole plan, one probe before and one after.
Method: ./ddx-manage.sh restart web --clean, then sh scripts/dev-latency-probe.sh <label>
(baseline-16.4.2, after-16.4.3). Source: findings/ddx-web-speed-ui/baseline.md. Both runs had an owner browser
tab compiling another route in parallel (16.1 s before, 2.4 s after), so the numbers carry that noise.
Owner target (cold login and projects each < 2 s, RSS < 3 GB): projects and register meet it, login does not (2.6 s). The plan gate (each route >= 40 % faster) is met. The RSS gate was defined at the 60 s sample; the recorded samples are 9 s and 34 s, and the 34 s value is above 3 GB.
ddx-web/next.config.ts)optimizePackageImports is not used (Turbopack does not need it).
The 2026-10-23 trial failed under a real working day (see §Reverted). If the same signs return,
set turbopackFileSystemCacheForDev: false again and record it here:
[Server HMR] Update failed, re-evaluating modules: … conflicting effects for the same key in logs/ddx-web.log;Whether those signs return under 16.4.3 is not established by this probe: the probe is a cold-start measurement, and the jev-browser working-day pass is the judge for the revert rule.
Last reconciled: 2026-10-24 · plan ddx-web-speed-ui
adr/
deploy/
research/
GET /en/auth/register 200 in 16.5s (next.js: 15.1s, proxy.ts: 6ms, generate-params: 419ms, application-code: 1049ms)GET /en/gallery/ConnectionBanner 200 in 17.3s (next.js: 16.0s, proxy.ts: 55ms, generate-params: 728ms, application-code: 496ms)GET /en/auth/login 200 in 6.0s (next.js: 5.5s, proxy.ts: 3ms, generate-params: 366ms, application-code: 93ms)POST /api/v1/auth/sign-in/email 200 in 103ms (next.js: 3ms, application-code: 101ms)GET /en/portal/acme-health/doctor/projects 200 in 9.5s (next.js: 8.6s, proxy.ts: 2ms, generate-params: 410ms, application-code: 578ms)GET /en/auth/register 200 in 240ms (next.js: 176ms, proxy.ts: 1.8ms, generate-params: 1.7ms, application-code: 60ms)./ddx-manage.sh status./ddx-manage.sh restart --clean # wipes ddx-web/.next, cold compile per routeuntil rg -q 'Ready in' logs/ddx-web.log; do sleep 1; doneB=http://localhost:16300; J=/tmp/ek-jar.txtcurl -s -o /dev/null -w '%{http_code} %{time_total}\n' $B/en/auth/login# one sign-in through the BFF; password read from ddx-api/.env SEED_USER_PASSWORD into a shell varjq -nc --arg e doctor@acme-health.test --arg p "$PW" '{email:$e,password:$p}' \ | curl -s -o /dev/null -c $J -H 'Content-Type: application/json' -H "Origin: $B" \ --data-binary @- -w '%{http_code} %{time_total}\n' $B/api/v1/auth/sign-in/emailcurl -s -o /dev/null -b $J -w '%{http_code} %{time_total}\n' $B/en/portal/acme-health/doctor/projectscurl -s -o /dev/null -w '%{http_code} %{time_total}\n' $B/en/auth/register # or /en/gallerysed 's/\x1b\[[0-9;]*m//g' logs/ddx-web.log | rg ' (GET|POST) 'P=$(lsof -nP -tiTCP:16300 -sTCP:LISTEN); ps -o rss=,etime= -p $P; du -sh ddx-web/.next# graphrg -c 'export \* from' ddx-web/src/inventory/index.tsfor d in pdfjs-dist @react-pdf/renderer @tiptap/react recharts streamdown; do rg -l --fixed-strings "from '$d" ddx-web/src; doneGET /en/auth/register 200 in 9.4s (next.js: 8.8s, proxy.ts: 50ms, generate-params: 222ms, application-code: 393ms)GET /en/auth/login 200 in 5.5s (next.js: 5.2s, proxy.ts: 3ms, generate-params: 214ms, application-code: 74ms)POST /api/v1/auth/sign-in/email 200 in 91ms (next.js: 2ms, application-code: 88ms)GET /en/portal/acme-health/doctor/projects 200 in 7.8s (next.js: 7.1s, proxy.ts: 2ms, generate-params: 250ms, application-code: 415ms)GET /en/auth/register 200 in 217ms (next.js: 154ms, proxy.ts: 1.9ms, generate-params: 1.8ms, application-code: 59ms)F GET /en/auth/login 200 in 2.1s (next.js: 1409ms, proxy.ts: 48ms, generate-params: 242ms, application-code: 354ms)F GET /en/portal/acme-health/doctor/projects 200 in 1100ms (next.js: 421ms, proxy.ts: 3ms, generate-params: 240ms, application-code: 436ms)F GET /en/auth/register 200 in 538ms (next.js: 243ms, proxy.ts: 2ms, generate-params: 223ms, application-code: 70ms)G GET /en/auth/login 200 in 6.5s (next.js: 5.8s, proxy.ts: 49ms, generate-params: 236ms, application-code: 389ms)