/** * Phase 7.0 — HMR perf marker names (B4 fix) * * Purpose: give Agent B (incremental bundled import), Agent A (reliability), * and Agent F (perf validation) a single vocabulary for marker names passed * to `mark()` / `measure()` / `withPerf()`. * * Why a shared module: in Phase 7.0 diagnostics we found that * `cli/commands/dev.ts:322-363`'s `handleSSRChange` chain has NO perf * markers — `dev:rebuild` wraps only `_doBuild`, so the true SSR rebuild * walltime (1.5~2 s vs 200ms target) is invisible. Without consistent * marker naming across agents, each would pick ad-hoc strings and * Agent F's benchmark script would have to grep for a hundred variants. * * When adding new markers: * 1. Add the constant here. * 2. Use it in production code as `mark(HMR_PERF.SSR_HANDLER_RELOAD)` * (not a string literal). * 3. Update `docs/bun/phase-7-benchmarks.md` (Agent F owns it). * * References: * docs/bun/phase-7-diagnostics/performance-reliability.md §2 B4 * docs/bun/phase-7-team-plan.md §3.2 */ /** * All HMR-related perf markers. String literals are frozen — do not * mutate. Marker names follow the `:` convention so grep * and log aggregation work predictably. */ export const HMR_PERF = { // ─── File detection → build invocation ───────────────────────────────── /** Raw fs event received (watcher). */ FILE_DETECT: "hmr:file-detect", /** After per-file debounce, before `handleFileChange()` runs. */ DEBOUNCE_FLUSH: "hmr:debounce-flush", /** Coalesced batch dispatched to the rebuild path. */ BATCH_DISPATCH: "hmr:batch-dispatch", // ─── Rebuild outer frame ─────────────────────────────────────────────── /** Wall-clock from batch dispatch → WS broadcast complete. The P95 * target (≤50 ms island / ≤200 ms SSR / ≤500 ms cold) is measured on * this marker. */ REBUILD_TOTAL: "hmr:rebuild-total", /** `_doBuild` body (covers both `buildClientBundles` + SSR path). */ DO_BUILD: "hmr:do-build", // ─── SSR handler reload chain (B4 newly-instrumented) ────────────────── /** `handleSSRChange` mutex section — from enter to exit. */ SSR_HANDLER_RELOAD: "ssr:handler-reload", /** `bundledImport` call — the single largest SSR cost today. Incremental * path (Agent B) should target near-zero on cache hits. */ SSR_BUNDLED_IMPORT: "ssr:bundled-import", /** `clearDefaultRegistry` + `registeredLayouts.clear` (fast). */ SSR_CLEAR_REGISTRY: "ssr:clear-registry", /** `registerManifestHandlers(manifest, true)` — per-route re-registration. */ SSR_REGISTER_HANDLERS: "ssr:register-handlers", /** Prerender regeneration (issue #188 fix). Measures only the prerender * re-run, not the HTML delivery. */ PRERENDER_REGEN: "prerender:regen", // ─── Client bundle path ──────────────────────────────────────────────── /** Per-island rebuild. Sub-marker of DO_BUILD when kind === "islands-only". */ ISLAND_REBUILD: "island:rebuild", /** Framework bundle (runtime/router/vendor/devtools). Should be SKIPPED * on common-dir rebuild (fire when skipFrameworkBundles === false). */ FRAMEWORK_REBUILD: "framework:rebuild", /** Vendor shim build. Sub-marker of FRAMEWORK_REBUILD. */ VENDOR_SHIM_BUILD: "framework:vendor-shim", // ─── HMR transport ───────────────────────────────────────────────────── /** Time to serialize + send to all connected clients. */ HMR_BROADCAST: "hmr:broadcast", /** Replay buffer enqueue (B8). */ HMR_REPLAY_ENQUEUE: "hmr:replay-enqueue", /** Client reconnect — `?since=` processing. */ HMR_REPLAY_FLUSH: "hmr:replay-flush", // ─── Incremental bundled import internals (Agent B) ──────────────────── /** Import graph lookup for a root path. */ INCR_GRAPH_LOOKUP: "incr:graph-lookup", /** Cache hit (no rebuild needed — changed file not in descendants). */ INCR_CACHE_HIT: "incr:cache-hit", /** Cache miss (rebuild required). */ INCR_CACHE_MISS: "incr:cache-miss", /** Graph rebuild after new build — updates descendants map. */ INCR_GRAPH_UPDATE: "incr:graph-update", // ─── Cold boot path (Phase 7.1 B_gap — R0.3 diagnostic identified 9 // unmeasured stages accounting for 150~210 ms of the 626 ms cold // start. Instrumenting these unlocks Tier 1 / Tier 2 optimizations.) ─ /** `validateAndReport(rootDir)` — mandu.config.ts load + schema check. */ BOOT_VALIDATE_CONFIG: "boot:validate-config", /** `validateRuntimeLockfile` — bun.lock check + advisory warnings. */ BOOT_LOCKFILE_CHECK: "boot:lockfile-check", /** `loadEnv({ rootDir, env: "development" })` — .env / .env.development. */ BOOT_LOAD_ENV: "boot:load-env", /** `startSqliteStore(rootDir)` — observability store (optional). Should * become fire-and-forget in Tier 1 so it doesn't block ready. */ BOOT_SQLITE_START: "boot:sqlite-start", /** `checkDirectory(guardConfig, rootDir)` — Architecture Guard preflight. */ BOOT_GUARD_PREFLIGHT: "boot:guard-preflight", /** `resolveAvailablePort(desiredPort, ...)` — port probe (may be slow on * Windows due to TIME_WAIT; Phase 0 already added retry). */ BOOT_RESOLVE_PORT: "boot:resolve-port", /** `createHMRServer(port, options?)` — Bun.serve + WebSocket + replay * buffer setup. Phase 7.0.R4 added Origin allowlist + rate limit. */ BOOT_HMR_SERVER: "boot:hmr-server", /** `startServer(manifest, ...)` — Bun.serve for the actual app. */ BOOT_START_SERVER: "boot:start-server", /** `watchFSRoutes(...)` — chokidar watcher for spec/slots + app/ routes. * Phase 7.1.A may not be able to remove this; it tracks new-route * creation which `_doBuild` does not. */ BOOT_WATCH_FS_ROUTES: "boot:watch-fs-routes", // ─── Phase 7.2 ───────────────────────────────────────────────────────── /** Tier 2 vendor shim disk-cache hit — all 4 shim outputs loaded from * `.mandu/vendor-cache/` instead of rebuilt. Target: >95% on warm dev. */ VENDOR_CACHE_HIT: "vendor:cache-hit", /** Tier 2 miss — any reason (no manifest / version mismatch / tamper / * first boot). Full `buildVendorShims` runs. */ VENDOR_CACHE_MISS: "vendor:cache-miss", /** Tier 2 cache write — after a successful rebuild, persist to disk. * Only fires on miss paths. */ VENDOR_CACHE_WRITE: "vendor:cache-write", /** HDR (Hot Data Revalidation) — slot change triggered a loader * refetch without a React tree remount. Measures WS broadcast → * client loader settled → props applied. Target: ≤ 150 ms P95. */ HDR_REFETCH: "hdr:refetch", // ─── Phase 7.3 ───────────────────────────────────────────────────────── /** JIT pre-warm — `mandu dev` boot kicks off fire-and-forget imports * of the hot SSR modules (react, react-dom, react-dom/server) so * Bun's JITTier compiler has them ready before the user's first edit. * * This marker measures the prewarm Promise's total wall-clock from * boot-start to all `import()` settling — it is NOT on the critical * path to "ready" (see `startPrewarmIdle`), so values here are * informational for benchmarks only. Phase 7.2 F observed first-iter * cold +41 ms vs warm steady-state; the goal is to absorb that into * prewarm before the user hits a file save. */ JIT_PREWARM: "boot:jit-prewarm", /** Phase 11 C — Deep-path JIT prewarm extension. Phase 7.3 A closed the * first-iter gap from 41 ms to 25 ms by prewarming the React hot set; * R0.3 diagnostics traced the remaining ~15 ms to the * `registerManifestHandlers` deep-path (cli `util/handlers` + * `util/bun` bundledImport + `@mandujs/core/bundler/safe-build` * internals) which only execute on the FIRST SSR reload. This marker * measures the settling time of the deep-import Promise — still * fire-and-forget, still NOT on the critical path, so values are * informational only. Target: first-iter ≤ 15 ms (hard) / ≤ 20 ms * (soft). See `packages/cli/src/util/jit-prewarm.ts`. */ JIT_PREWARM_DEEP: "boot:jit-prewarm-deep", /** API route handler reload (`handleAPIChange`) — `.route.ts` / * `.route.tsx` change. Symmetric to `SSR_HANDLER_RELOAD` for page / * layout reloads. Phase 7.2 §7.4 flagged that `handleAPIChange` was * missing a top-level `withPerf()` wrap so Agent D's MANDU_PERF=1 * trace couldn't attribute API reload walltime. * * Semantically API reloads are a subset of "route handler reload" * (`SSR_HANDLER_RELOAD`), but a distinct marker lets benchmark scripts * separate page vs API reload populations when computing P95. */ API_HANDLER_RELOAD: "api:handler-reload", } as const; /** * Union of all marker names — useful when a function accepts an arbitrary * marker as a parameter and you want compile-time exhaustiveness. */ export type HMRPerfMarker = (typeof HMR_PERF)[keyof typeof HMR_PERF]; /** * Human-readable target thresholds. Source of truth for Agent F's hard * assertion pass/fail logic. Values in milliseconds. */ export const HMR_PERF_TARGETS = { /** Cold dev start (`mandu dev` → "ready" log). */ COLD_START_MS: 500, /** Island-only rebuild P95, measured on REBUILD_TOTAL. */ ISLAND_REBUILD_P95_MS: 50, /** SSR page rebuild P95, measured on REBUILD_TOTAL when kind === "ssr-only". */ SSR_REBUILD_P95_MS: 200, /** Common-dir rebuild P95 (fan-out across multiple islands/SSR modules). */ COMMON_DIR_REBUILD_P95_MS: 400, /** CSS-only rebuild P95. */ CSS_REBUILD_P95_MS: 100, } as const;