| 1 | import { readFileSync, readdirSync, statSync } from "node:fs"; |
| 2 | import { basename, resolve } from "node:path"; |
| 3 | import { gzipSync } from "node:zlib"; |
| 4 | |
| 5 | const distDir = resolve("dist"); |
| 6 | const indexPath = resolve(distDir, "index.html"); |
| 7 | const html = readFileSync(indexPath, "utf8"); |
| 8 | |
| 9 | function gzipBytes(path) { |
| 10 | return gzipSync(readFileSync(path), { level: 9 }).byteLength; |
| 11 | } |
| 12 | |
| 13 | function initialAssetPaths(extension) { |
| 14 | const pattern = extension === ".js" |
| 15 | ? /<(?:script|link)\b[^>]+(?:src|href)=["']([^"']+\.js)["'][^>]*>/g |
| 16 | : /<link\b[^>]+href=["']([^"']+\.css)["'][^>]*>/g; |
| 17 | return [...new Set([...html.matchAll(pattern)].map((match) => resolve(distDir, match[1])))]; |
| 18 | } |
| 19 | |
| 20 | function formatKiB(bytes) { |
| 21 | return `${(bytes / 1024).toFixed(1)} KiB`; |
| 22 | } |
| 23 | |
| 24 | function assertBudget(label, actual, budget) { |
| 25 | if (actual > budget) { |
| 26 | throw new Error(`${label} is ${formatKiB(actual)} (${actual} B); budget is ${formatKiB(budget)} (${Math.floor(budget)} B)`); |
| 27 | } |
| 28 | process.stdout.write(` PASS ${label}: ${formatKiB(actual)} / ${formatKiB(budget)}\n`); |
| 29 | } |
| 30 | |
| 31 | const initialJS = initialAssetPaths(".js"); |
| 32 | const initialCSS = initialAssetPaths(".css"); |
| 33 | if (!initialJS.length) throw new Error("no initial JavaScript assets found in dist/index.html"); |
| 34 | // initial CSS 允许为空:styles.css 走 ?url 延迟加载,feature 样式走 lazy chunk。 |
| 35 | |
| 36 | // main.tsx intentionally loads styles.css before mounting React so the inline |
| 37 | // boot shell can paint without waiting for the full application stylesheet. |
| 38 | // Vite emits that entry as styles-<hash>.css; keep it in the startup budget |
| 39 | // while also proving it never drifts back into the render-blocking HTML path. |
| 40 | const appShellCSS = readdirSync(resolve(distDir, "assets")) |
| 41 | .filter((name) => /^styles-.+\.css$/.test(name)) |
| 42 | .map((name) => resolve(distDir, "assets", name)); |
| 43 | if (appShellCSS.length !== 1) { |
| 44 | throw new Error(`expected exactly one deferred app-shell stylesheet, found ${appShellCSS.length}`); |
| 45 | } |
| 46 | if (initialCSS.some((path) => appShellCSS.includes(path))) { |
| 47 | throw new Error("app-shell stylesheet must not block the inline boot shell's first paint"); |
| 48 | } |
| 49 | |
| 50 | const initialJSGzip = initialJS.reduce((total, path) => total + gzipBytes(path), 0); |
| 51 | const initialCSSGzip = initialCSS.reduce((total, path) => total + gzipBytes(path), 0); |
| 52 | const appShellCSSGzip = appShellCSS.reduce((total, path) => total + gzipBytes(path), 0); |
| 53 | const largestInitialJS = Math.max(...initialJS.map(gzipBytes)); |
| 54 | const largestInitialJSRaw = Math.max(...initialJS.map((path) => statSync(path).size)); |
| 55 | const localeChunks = readdirSync(resolve(distDir, "assets")) |
| 56 | .filter((name) => /^(?:zh|zh-TW)-.+\.js$/.test(name)) |
| 57 | .map((name) => resolve(distDir, "assets", name)); |
| 58 | |
| 59 | console.log("\nbundle budgets"); |
| 60 | // React Virtuoso replaces the transcript's custom measurement/anchor engine. |
| 61 | // Its production runtime adds 16.9 KiB gzip (4.2%) over the 402 KiB baseline. |
| 62 | // This exceptional overrun is locally attributable and trades ~1400 lines of |
| 63 | // competing state machines for a maintained library. Native-tail finish helpers |
| 64 | // then sat on the 423.5 KiB gate (Windows CI: 423.5 / 423.5); this 0.5 KiB |
| 65 | // raise (0.12%) absorbs that leave-cancel / remasure-once code without |
| 66 | // widening the original Virtuoso exception. The project-tree archive race |
| 67 | // guards add 611 bytes gzip over main-v2's 423.988 KiB startup path after the |
| 68 | // blank-project flow landed; project-topic sort invalidation and request |
| 69 | // ordering add another bounded 0.2 KiB. Retain both owner boundaries with a |
| 70 | // narrowly rounded 1 KiB ratchet. |
| 71 | // Diagnostic builds intentionally keep content-free row geometry and scroll |
| 72 | // transition probes in the initial transcript path. Stable builds retain the |
| 73 | // existing production ratchet. Per-row measurement versions and a bounded |
| 74 | // recovery probe add less than 0.1% gzip; retain them with a 0.5 KiB (0.118%) |
| 75 | // production ratchet rather than weakening either recovery contract. The |
| 76 | // bounded allowance also covers small gzip drift from the embedded build SHA. |
| 77 | // Reader extent stabilization adds 1.2 KiB gzip (0.28%) in production for its |
| 78 | // bounded input, collapse, rebound, and ownership transaction. Retain it with |
| 79 | // a 1.5 KiB (0.35%) ratchet instead of weakening the Windows scroll invariant. |
| 80 | // Complete-history navigation adds 0.3 KiB gzip (0.070%) to that production |
| 81 | // path while keeping its 1.68 KiB question rail lazy-loaded. Test diagnostics |
| 82 | // plus the navigation owner add 0.7 KiB gzip (0.164%) over the merged test gate. |
| 83 | // DingTalk channel status and locale wiring move the current-base production |
| 84 | // build from 427.2 to 427.7 KiB and test from 428.6 to 429.1 KiB. The unified |
| 85 | // state-aware geometry contract, session diagnostics counters, and guarded |
| 86 | // native-scroll probes add 2.4 KiB gzip to the initial path. The current |
| 87 | // main-v2 merge adds another 0.3 KiB of deterministic shared startup code. |
| 88 | // Keep the increase explicit and bounded instead of hiding it in a broad |
| 89 | // percentage ratchet. |
| 90 | // The retained-transcript surface adds a small, bounded navigation owner to |
| 91 | // the startup path (overlay state + stale-completion guard). Keep the increase |
| 92 | // explicit and narrow; the measured build is 431.1 KiB gzip. |
| 93 | // The web-search tool card now resolves the same display projection lazily so |
| 94 | // its filtered count matches the assistant Sources panel. The measured build |
| 95 | // is 431.509 KiB gzip; keep 0.1 KiB of explicit headroom for hash/toolchain |
| 96 | // drift instead of relying on a rounded equality. |
| 97 | // Remote onboarding [0.5/3] adds project-group and credential-chain wiring on |
| 98 | // top of the lazy wizard. Exact-turn routing, the extracted event-gap |
| 99 | // projector, checkpoint resets, and the navigation surface transaction bring |
| 100 | // the current main-v2 path to 437.36 KiB gzip. |
| 101 | // The full remote-session surface adds the lazy transcript bridge and tab |
| 102 | // lifecycle on top of [0.5/3]. Keep the measured stack's narrow ratchet. |
| 103 | // Remote approval hardening adds authoritative composer-profile hydration, |
| 104 | // scoped rewind dispatch, and attachment/inbox fences to the always-mounted |
| 105 | // remote hook. The measured production path is 438.38 KiB gzip after keeping |
| 106 | // the integration modules below repolint's ownership ceilings; retain 0.12 KiB |
| 107 | // toolchain headroom with a bounded 1.4 KiB ratchet. |
| 108 | // Remote status isolation keeps the always-mounted status bar on the active |
| 109 | // remote transcript and routes job cancellation to that host. Parsers and |
| 110 | // retry policy remain lazy; the measured selector adds under 0.1 KiB gzip. |
| 111 | // Remote runtime parity adds scoped approvals, status-only reconciliation, |
| 112 | // session quality-floor routing, dropped-frame reconciliation, and remote |
| 113 | // runtime-command dispatch. The measured initial path is 439.60 KiB; |
| 114 | // retain 0.10 KiB of bounded toolchain headroom. |
| 115 | // Closing the remaining review gaps adds generation-fenced hydration plus |
| 116 | // remote-only tool payload, Todo, and terminal isolation. The measured path is |
| 117 | // 439.74 KiB; retain 0.06 KiB of headroom with a 0.1 KiB ratchet. |
| 118 | // The final remote-runtime parity pass adds remote run-strip telemetry, |
| 119 | // explicit session verbs, and specialized plan decisions. The measured path |
| 120 | // is 440.02 KiB. The current main-v2 turn-event, finish-protocol, and session |
| 121 | // repair runtime then moves the combined path to 445.097 KiB; retain 0.103 KiB |
| 122 | // of bounded build/toolchain headroom. |
| 123 | // Atomic remote profile changes, exact approval draining, and generation-safe |
| 124 | // history handoff bring the measured path to 445.228 KiB. Retain 0.072 KiB of |
| 125 | // headroom with the smallest existing decimal ratchet. |
| 126 | // Direct pending-prompt recovery and authoritative remote Goal state bring the |
| 127 | // measured path to 445.473 KiB. Retain 0.027 KiB of bounded headroom. |
| 128 | // Restored remote shells now activate their backend session immediately and |
| 129 | // keep disconnected state out of the mounted surface. The merged production |
| 130 | // path measures 445.614 KiB; retain 0.086 KiB of bounded build/toolchain |
| 131 | // headroom with the smallest existing decimal ratchet. |
| 132 | // Runtime-aware Todo presentation plus exact-tab continuation adds 0.3 KiB gzip |
| 133 | // to the always-mounted footer path. Keep the state/routing guard with a narrow |
| 134 | // ratchet rather than showing idle restored work as actively running. The |
| 135 | // combined path measures 445.9 KiB; retain 0.1 KiB of toolchain headroom. |
| 136 | // Transcript surface ownership and the token-fenced unloaded-question commit |
| 137 | // move the exact main-v2 baseline from 445.865 to 447.587 KiB gzip (+0.39%). |
| 138 | // The final 0.266 KiB retains jump ownership through paint-ready instead of |
| 139 | // allowing a native scrollend to release it. Keep only 0.213 KiB headroom; |
| 140 | // native validation hosts and test fixtures stay outside the production graph. |
| 141 | // Cross-platform shell inventory, current-session vs after-reload rows, |
| 142 | // manual repair guidance, and exact download-host allowlisting move the merged |
| 143 | // path from 448.692 to 449.758 KiB (+1.066 KiB). Retain 0.142 KiB of bounded |
| 144 | // build/toolchain headroom. |
| 145 | // The reader transaction contract (geometry revisions, generation-fenced |
| 146 | // writer requests, gesture travel proof, stabilized-shrink extent acceptance, |
| 147 | // and the blank-rebound prepaint lane) adds a measured 3.978 KiB gzip on the |
| 148 | // merged main-v2 baseline. MCP elicitation and the inline Apps lifecycle remain |
| 149 | // on that startup graph; the combined path measures 455.0 KiB. Retain 0.2 KiB |
| 150 | // of bounded build/toolchain headroom. |
| 151 | // Generic elicitation validation adds field-specific localized accessibility |
| 152 | // copy to the English startup dictionary. The interaction code and CSS remain |
| 153 | // lazy; the measured path is 455.437 KiB. Retain 0.163 KiB of headroom. |
| 154 | // Stream-failure visibility (#9560) adds the last-discard reason and one |
| 155 | // terminal-notice dedupe flag, while provider no_proxy copy now states the |
| 156 | // custom-proxy precedence. The merged path measures 455.9 KiB; retain 0.1 KiB |
| 157 | // of bounded build/toolchain headroom. |
| 158 | // Exhausted tail repair now releases ownership so jump-bottom remains usable |
| 159 | // after a stranded native WebView extent. The WebView2 reachable-tail clamp |
| 160 | // then absorbs a second post-quiet extent without an unbounded write loop. |
| 161 | // The combined path measures 456.316 KiB; retain 0.084 KiB with the smallest |
| 162 | // one-decimal ratchet. |
| 163 | // The generation-bound history-prepend lease adds stable-key reader anchoring, |
| 164 | // full mounted coverage, and one final arbiter-owned correction. The measured |
| 165 | // path is 457.406 KiB after extracting the lease owner to satisfy repolint. |
| 166 | // Latest-base transcript settle ownership measures 457.523 KiB with this UX. |
| 167 | // Isolated conversation forks and their extracted browser mock adapter bring |
| 168 | // the combined tree to 458.158 KiB; completion uncertainty adds a terminal |
| 169 | // outcome and notice without exposing evaluator audits to the frontend, |
| 170 | // measuring 458.287 KiB gzip. |
| 171 | // Transactional Ask resolution and authoritative rejected-submit recovery add |
| 172 | // 0.3 KiB gzip to the initial controller path. Retain the exact turn fence, |
| 173 | // bounded ListTabs retry, and stale-prompt guard. |
| 174 | // Session-catalog repair presentation stays in the lazy project-tree chunk; |
| 175 | // compact shared helpers keep the combined initial path within the same gate. |
| 176 | // Merge-Back adds identity-bound inspection, navigation, and retained-recovery |
| 177 | // orchestration on top. The merged stable build measures 461.338 KiB and the |
| 178 | // test channel measures 461.323 KiB. Deferring selection ownership until a |
| 179 | // real range exists (#9703/#9711) and adding the session takeover banners |
| 180 | // move the combined path to 462.2 KiB. Local spectator reclaim adds the |
| 181 | // desktop-vs-remote command branch. Sticky Context's session-scoped file chips |
| 182 | // bring the merged stable path to 462.587 KiB. Windows' embedded build metadata |
| 183 | // lands just above the rounded 462.6 KiB boundary; retain one cross-platform |
| 184 | // decimal step without widening any chunk or raw gate. |
| 185 | // Reading the applied item-list transform (instead of the remembered offset) |
| 186 | // keeps the reader/anchor visual guards from compounding under reduced-motion |
| 187 | // WebView2; the merged path measures 462.827 KiB. Retain one decimal step. |
| 188 | // Generation-bound native-thumb transactions and the rebased custom-scrollbar |
| 189 | // drag add 0.3 KiB gzip; the merged path measures 463.102 KiB. |
| 190 | // Absorbing content-preserving block-window prepends into the active reader |
| 191 | // transaction adds 0.2 KiB gzip on top; the merged path measures 463.292 KiB, |
| 192 | // 8 bytes under the next decimal. Retain one cross-platform decimal step. |
| 193 | // The subagent outcome envelope, partial-state card, and history hydration add |
| 194 | // 0.5 KiB gzip on the initial path. The model-capability resolver and its |
| 195 | // read-only provider badges add a measured 0.2 KiB including gzip/toolchain |
| 196 | // rounding. The integrated management shell, image capability controls, and |
| 197 | // upstream updater refresh measure 465.4 KiB gzip (base: 464.7 KiB). |
| 198 | // Keep the next decimal ceiling and leave feature editors lazy. |
| 199 | // Durable protocol recovery controls and search-source status add 1.2 KiB |
| 200 | // over the same-environment main-v2 build (465.4 -> 466.6 KiB gzip). |
| 201 | // Keep one decimal of cross-platform headroom for this measured shell change. |
| 202 | // Integrating main-v2 rich-link menus measures 466.905 KiB combined. |
| 203 | // The AskCard session-draft wiring adds a bounded 30-byte gzip drift on the |
| 204 | // initial route. The session-runtime ordering fence adds 56 bytes and |
| 205 | // cross-platform zlib rounding reaches the same startup path; retain the |
| 206 | // explicit budget rather than failing on a rounded 467.0 KiB display value. |
| 207 | // The latest main-v2 session-runtime fence and exact prompt protocol measure |
| 208 | // 468.2 KiB here; retain a 0.1 KiB ceiling for platform zlib rounding. |
| 209 | // Mainline provider/settings integration measures 469.230 KiB gzip. |
| 210 | const initialJSBudgetKiB = 469.3; |
| 211 | assertBudget("initial JavaScript gzip", initialJSGzip, initialJSBudgetKiB * 1024); |
| 212 | assertBudget("largest initial JavaScript chunk gzip", largestInitialJS, 280 * 1024); |
| 213 | // Render-blocking CSS is intentionally absent: styles.css loads deferred via |
| 214 | // ?url, and feature styles (heartbeat) live in lazy chunks loaded on demand. |
| 215 | // An empty initial CSS list is the desired state, not a build error. |
| 216 | if (initialCSS.length > 0) { |
| 217 | assertBudget("render-blocking CSS gzip", initialCSSGzip, 4 * 1024); |
| 218 | } else { |
| 219 | process.stdout.write(" PASS render-blocking CSS: none (all styles deferred)\n"); |
| 220 | } |
| 221 | // Extension surfaces, Task Monitor, and compact decision receipts share the |
| 222 | // application stylesheet loaded before React mounts. Keep their combined |
| 223 | // allowance bounded even though the file is no longer render-blocking. |
| 224 | // Navigation overlay styles add a bounded 0.1 KiB to the deferred shell. |
| 225 | // The cleaned source panel adds 0.1 KiB gzip to the deferred shell on top of |
| 226 | // the retained-transcript navigation allowance; keep the ratchet explicit. |
| 227 | // The navigation mask's stable composer footprint and remote tab/surface |
| 228 | // states bring the merged shell to roughly 115.7 KiB gzip. |
| 229 | // The one-row model configuration list, responsive stacking, Automation's |
| 230 | // shared title-safe shell, and the shared harness decision surface measure |
| 231 | // 116.9 KiB gzip while reusing existing layout primitives. Retain a bounded |
| 232 | // 0.1 KiB headroom ratchet. |
| 233 | // Workbench welcome and recovery styles measure 122869 B gzip on main-v2. |
| 234 | // Turn result styles add 388 B after removing obsolete metrics (123257 B). |
| 235 | // The dock launcher, tab container and tab picker add ~0.6 KiB (the ported |
| 236 | // card, branch switcher, tab strip, add menu, empty-state picker and the |
| 237 | // spacer that starts the panel below the topic bar). |
| 238 | assertBudget("deferred app-shell CSS gzip", appShellCSSGzip, 121.2 * 1024); |
| 239 | if (localeChunks.length !== 2) { |
| 240 | throw new Error(`expected 2 on-demand Chinese locale chunks, found ${localeChunks.length}`); |
| 241 | } |
| 242 | for (const path of localeChunks) { |
| 243 | const name = basename(path); |
| 244 | // Task Monitor, billing, indexed history, Task Center, Extension UI, and |
| 245 | // runtime controls plus execution-setting receipts add localized copy. The |
| 246 | // write-access approval card adds four scoped actions and a home-risk |
| 247 | // warning (~0.15 KiB gzip, +0.27% over the old 54.75 gate). Context |
| 248 | // compaction settings add 40 bytes gzip of policy guidance to simplified |
| 249 | // Chinese, while scheduled billing adds compact rate-band labels/tooltips. |
| 250 | // The three StepFun presets add localized names/descriptions (~0.1 KiB |
| 251 | // gzip); the two pay-as-you-go presets add the same again. The delivery |
| 252 | // floor segmented control adds two labels plus one explanatory tooltip, |
| 253 | // measured at 23 B gzip for zh and 8 B for zh-TW. Completion receipts add |
| 254 | // six short status labels in each locale, requiring another 0.2 KiB per |
| 255 | // language. DingTalk setup and mention guidance add at most 0.2 KiB more |
| 256 | // (0.36%); retain the complete security and group-chat copy instead of |
| 257 | // abbreviating user-facing instructions to fit the old locale ratchet. |
| 258 | // Recovery-copy and catalog-only sidebar labels can move the simplified |
| 259 | // Chinese chunk across the rounded 55.9 KiB boundary on CI's Node/zlib; |
| 260 | // retain a narrow 0.1 KiB headroom rather than making gzip output a |
| 261 | // platform-dependent gate. The OpenCode one-key setup adds product-level |
| 262 | // connection, fallback, and legacy-state copy while removing protocol |
| 263 | // choices from the primary UI; keep that complete guidance with a bounded |
| 264 | // 0.4–0.5 KiB locale-only ratchet. |
| 265 | // Git-Bash installation guidance adds localized copy across dialects. |
| 266 | // MCP elicitation adds fourteen short labels per locale (~40 B gzip). |
| 267 | // Generic schema validation adds complete field-error, privacy, and safe- |
| 268 | // fallback copy. Measured chunks are 58.574 KiB zh and 59.368 KiB zh-TW; |
| 269 | // retain roughly 0.13 KiB of platform headroom for each. |
| 270 | // Stream-failure diagnostics add five strings per dialect. Together with the |
| 271 | // reachable-tail recovery copy, the merged chunks measure 58.923 KiB zh and |
| 272 | // 59.710 KiB zh-TW. The isolated-fork guidance brings the measured chunks |
| 273 | // to 59.1 KiB zh and 59.9 KiB zh-TW; retain a narrow one-decimal ratchet. |
| 274 | // Merge-Back lifecycle and recovery guidance measure 59.819 KiB zh and |
| 275 | // 60.612 KiB zh-TW; retain only the next one-decimal ceiling for each. |
| 276 | // The retained-recovery receipt and copy action move zh to 59.911 KiB; |
| 277 | // session-catalog recovery guidance on the merged base moves zh-TW to |
| 278 | // 60.757 KiB; retain only its exact one-decimal ceiling. |
| 279 | // Session takeover adds ~20 locale keys per dialect (banners, dialog, |
| 280 | // reclaim), while Sticky Context adds file-state and limit diagnostics. The |
| 281 | // merged stable chunks measure 60.395 KiB zh and 61.232 KiB zh-TW; retain |
| 282 | // only the next one-decimal ceiling for each dialect. |
| 283 | // The outcome card adds one short localized status label per dialect. CI's |
| 284 | // Windows zlib measured zh at 60.4 KiB exactly; capability-status copy adds |
| 285 | // a small 0.1 KiB ratchet, so retain the next decimal ceiling rather than |
| 286 | // dropping the unknown-state explanation. |
| 287 | // Image input mode, provenance and unknown-state guidance measure 60.724 KiB |
| 288 | // zh and 61.570 KiB zh-TW. Keep the next decimal ceiling per locale. |
| 289 | // Protocol recovery and source-availability copy measure 60.927 KiB zh |
| 290 | // and 61.789 KiB zh-TW (base: 60.8 / 61.6 rounded). |
| 291 | // Rich-link action copy on the current base brings these to |
| 292 | // 61.027/61.881 KiB; retain bounded cross-platform headroom. |
| 293 | // Recovery retry copy reaches the rounded 61.1 KiB boundary on Node/zlib |
| 294 | // toolchains; keep the next one-decimal ceiling for cross-platform CI. |
| 295 | // The #9889/#9890 series adds recovery-wait, dialog-failure, and stall copy: |
| 296 | // zh-TW measures 63492 B (62.004 KiB) with the four PRs merged together. |
| 297 | // Integrated settings and ownership copy measures 61.415 / 62.212 KiB. |
| 298 | // Session-log head versions (head kinds, current/covered wording, and the |
| 299 | // three head notices) measure 61.7 / 62.4 KiB; keep the next one-decimal |
| 300 | // ceiling for cross-platform CI. |
| 301 | // Search assignment copy adds 239 / 231 B over main-v2 (63147 / 63920 B). |
| 302 | // Measured result: 63386 / 64151 B; retain bounded cross-platform headroom. |
| 303 | // Saved/pending/apply-failure guidance adds 233 / 245 B: 63619 / 64396 B |
| 304 | // with gzip level 9. Keep the next decimal ceiling for these four keys. |
| 305 | // Session recovery guidance adds 173 / 156 B over main-v2, measuring |
| 306 | // 62.173 / 62.887 KiB. Keep only the next one-decimal ceiling. |
| 307 | // Combined recovery and model-application copy measures 63791 / 64557 B. |
| 308 | // Turn result copy adds 554 / 566 B to the latest-base chunks, measuring |
| 309 | // 64219 / 64964 B with recovery guidance included. Round to the next tenth. |
| 310 | // Combined turn-result and model-application copy measures 64342 / 65119 B. |
| 311 | // Runtime/receipt confirmation copy adds 89 / 99 B to the integrated |
| 312 | // turn-result base (64219 / 64964 B). Measured: 64308 / 65063 B. |
| 313 | // Read-pause copy merges on top of that base: the combined chunks measure |
| 314 | // 64606 / 65349 B, so both dialect ceilings ratchet to the next tenth. |
| 315 | // Model-application copy on the read-pause base measures 64734 / 65499 B, |
| 316 | // adding 128 / 150 B. Retain only the next one-decimal ceiling. |
| 317 | const budget = name.startsWith("zh-TW-") ? 64.0 * 1024 : 63.3 * 1024; |
| 318 | assertBudget(`${name} gzip`, gzipBytes(path), budget); |
| 319 | } |
| 320 | |
| 321 | const rawInitialBytes = [...initialJS, ...initialCSS, ...appShellCSS] |
| 322 | .reduce((total, path) => total + statSync(path).size, 0); |
| 323 | // The maintained Virtuoso engine adds 49.1 KiB raw (2.2%) over the previous |
| 324 | // 2268.7 KiB gate. Navigation remains inside the 2341 KiB production ceiling; |
| 325 | // its combined diagnostic wiring adds 2.2 KiB (0.094%) to the test channel. |
| 326 | // DingTalk startup wiring moves current-base production from 2341.0 to 2343.6 |
| 327 | // KiB and test from 2346.2 to 2348.8 KiB; the pinned heading adds 0.5 KiB raw |
| 328 | // (0.021%). The workspace panel rework (change-row hover/revert, status badges, |
| 329 | // More menu, completion summary) makes the latest-base merge 2353.1 KiB in |
| 330 | // production and test channels both measure 2357.92 KiB after project-group |
| 331 | // wiring. Exact-turn routing, checkpoint resets, and failure-atomic navigation |
| 332 | // bring the current main-v2 path to 2379.22 KiB. The remote approval |
| 333 | // fences, extracted ownership modules, and remote status-bar isolation bring |
| 334 | // the measured initial payload to 2380.9 KiB; retain 0.1 KiB of bounded |
| 335 | // raw/toolchain headroom. Scoped remote approvals, status reconciliation, and |
| 336 | // runtime command dispatch bring the measured payload to 2382.9 KiB. The |
| 337 | // remaining review fences measure 2383.2 KiB; retain 0.1 KiB of headroom. |
| 338 | // Final remote-runtime parity measures 2384.4 KiB raw. The current main-v2 |
| 339 | // runtime additions bring the combined path to 2404.364 KiB. The final merged |
| 340 | // restored-shell activation and disconnected-state revival path measures |
| 341 | // 2404.898 KiB; retain 0.102 KiB of bounded headroom alongside the gzip |
| 342 | // ratchet above. |
| 343 | // Runtime-aware Todo status and exact-tab continuation then add to the same |
| 344 | // initial path. The combined payload measures 2406.2 KiB; retain 0.1 KiB of |
| 345 | // raw/toolchain headroom for both owners. |
| 346 | // The same transcript transaction measures 2413.012 KiB raw (+0.28%) against |
| 347 | // the 2406.204 KiB baseline. Retain 0.188 KiB of bounded headroom. |
| 348 | // The notification-volume control adds one persisted master gain, per-source |
| 349 | // loudness trims, and its accessible Settings surface. Current main-v2 moves |
| 350 | // from 2413.183 to 2414.879 KiB raw (+1.696 KiB); retain 0.121 KiB of bounded |
| 351 | // headroom. |
| 352 | // Owner-lifecycle reasoning disclosure, pre-paint tail pinning, and the live |
| 353 | // footer growth floor then add 2.390 KiB after extracting ownership modules |
| 354 | // below repolint's source ceilings. Lifecycle fencing adds 0.258 KiB; the |
| 355 | // combined path measures 2417.526 KiB. Retain 0.074 KiB while preventing |
| 356 | // phase-boundary reverse flashes and cross-surface floor leaks. |
| 357 | // The same shell-support surface moves the merged path from 2417.526 to |
| 358 | // 2422.371 KiB raw (+4.845 KiB). Retain 0.129 KiB of bounded headroom without |
| 359 | // widening unrelated chunk ceilings. |
| 360 | // The WebView2 extent rebound prepaint handoff adds 0.204 KiB raw so a native |
| 361 | // scroll delivery can restore mounted coverage before the next visible frame. |
| 362 | // Retain 0.096 KiB of headroom without widening gzip or chunk ceilings. |
| 363 | // The reader transaction contract then adds a measured 15.317 KiB raw on the |
| 364 | // merged main-v2 baseline (including its own prepaint port). MCP elicitation |
| 365 | // and Apps add their bounded payload on the shared graph; the combined path |
| 366 | // measures 2442.6 KiB. Retain 0.4 KiB of bounded build/toolchain headroom. |
| 367 | // The browser MCP interaction preview adds 0.6 KiB of route wiring while its |
| 368 | // 0.75 KiB form fixture and lifecycle remain lazy. The combined path measures |
| 369 | // 2443.2 KiB; retain 0.1 KiB of bounded build/toolchain headroom. |
| 370 | // Generic field copy adds 1.134 KiB raw to the startup dictionary; all schema |
| 371 | // parsing, rendering, and CSS remain lazy. The measured path is 2444.334 KiB; |
| 372 | // retain 0.166 KiB of bounded build/toolchain headroom. |
| 373 | // The off-flow composer measurement mirror adds 0.472 KiB raw while removing |
| 374 | // live-textarea layout mutation. The merged path measures 2444.806 KiB; retain |
| 375 | // 0.194 KiB of bounded toolchain headroom without widening gzip/chunk gates. |
| 376 | // Stream-failure visibility and corrected proxy guidance bring the merged path |
| 377 | // to 2446.6 KiB; retain the smallest existing decimal ratchet. |
| 378 | // The stranded-tail recovery transition plus the WebView2 reachable-tail clamp |
| 379 | // bring the measured initial payload to 2447.953 KiB. Retain 0.047 KiB with |
| 380 | // the smallest one-decimal ratchet. |
| 381 | // The extracted history-prepend owner adds 3.953 KiB of bounded transaction |
| 382 | // state and stable-key coverage checks. Together with the compact |
| 383 | // session-version host, they measure 2452.7 KiB; the recovery coordinator and |
| 384 | // dialog remain lazy. Completion uncertainty adds a distinct terminal notice |
| 385 | // and localized startup copy without collapsing into recovery-paused UX. |
| 386 | // 2454.719 KiB on the release toolchain. Completion uncertainty brings the |
| 387 | // final merged payload to 2455.154 KiB. |
| 388 | // Ask turn fencing, rejection reconciliation, and the localized submit-failure |
| 389 | // notice measure 2456.044 KiB raw; retain 0.056 KiB of one-decimal headroom. |
| 390 | // Merge-Back's startup ownership and failure-atomic navigation fence add the |
| 391 | // remaining bounded payload. The retained recovery receipt makes the stable |
| 392 | // path 2465.105 KiB raw; the merged test channel measures 2464.979 KiB. |
| 393 | // Session takeover banners and #9703/#9711's provisional-selection handoff |
| 394 | // combine with Sticky Context's pinned-file state at 2469.125 KiB raw on the |
| 395 | // merged stable path. Retain only the next one-decimal ceiling. |
| 396 | // The passive reader-anchor lease for delayed WebView2 range commits measures |
| 397 | // 2469.347 KiB raw (+0.222 KiB, +0.009%). Retain only the next one-decimal |
| 398 | // ceiling; gzip and largest-chunk budgets remain unchanged. |
| 399 | // Reading the applied item-list transform for the reader/anchor visual guards |
| 400 | // adds 0.5 KiB raw on top; the merged path measures 2469.815 KiB. |
| 401 | // The scrollbar generation fence and drag rebase add 1.1 KiB raw; the merged |
| 402 | // path measures 2470.932 KiB. |
| 403 | // The reader-transaction offset absorption adds 0.8 KiB raw on top; the merged |
| 404 | // path measures 2471.741 KiB. Controller-owned management dispositions and |
| 405 | // optimistic management settlement add 0.6 KiB raw; retain the smallest |
| 406 | // one-decimal ceiling with bounded headroom. |
| 407 | // The outcome card and history hydration add 2.3 KiB raw on the initial path |
| 408 | // (2474.0 KiB measured in CI). Keep this narrowly attributable ratchet rather |
| 409 | // than removing persisted-result visibility or changing chunk ownership. |
| 410 | // On the current main-v2 base, the combined measured path is 2474.6 KiB; |
| 411 | // the model-capability helper and localized status copy add 0.9 KiB; retain |
| 412 | // the smallest bounded cross-platform ceiling. |
| 413 | // Retain the upstream updater ceiling and independent chunk gates. |
| 414 | // Recovery controls add 3.6 KiB raw over the measured 2480.9 KiB base; |
| 415 | // current payload is 2484.509 KiB. Retain only bounded toolchain headroom. |
| 416 | // With the current-base rich-link menus: 2485.715 KiB raw. |
| 417 | // The shared harness decision surface adds a bounded startup stylesheet |
| 418 | // payload. The current base plus exact prompt identity and stale-card recovery |
| 419 | // measure 2496.4 KiB locally; retain the smallest bounded ceiling. |
| 420 | // The context truncation-rescue notice and its three locale strings measure |
| 421 | // 2496.6 KiB; retain the smallest bounded ceiling. |
| 422 | // Deferred presentation measured 2381.4 KiB before first-materialization |
| 423 | // preloading. Mainline Stop brings the payload to 2381.8 KiB; |
| 424 | // Mainline recovery UI brings it to 2384.5 KiB, and tool elapsed/liveness |
| 425 | // UI to 2384.9 KiB. Retain 0.2 KiB headroom. |
| 426 | // Mainline provider/settings integration measures 2398.2 KiB in the |
| 427 | // extracted shell. Retain the same bounded 0.2 KiB build headroom. |
| 428 | // The head-version dialog copy and its covered-version cleanup control |
| 429 | // measure 2399.2 KiB; retain the same bounded 0.2 KiB build headroom. |
| 430 | // Search-assignment bridge and metadata add 1.0 KiB over the measured |
| 431 | // main-v2 baseline (2399.3 KiB); result 2400.3 KiB plus 0.2 KiB headroom. |
| 432 | // Workbench welcome plus toolbar-refresh integration measures 2401.108 KiB |
| 433 | // against the 2398.0 KiB base; retain only the next one-decimal ceiling. |
| 434 | // Shared availability, visible recovery and retry controls measure 2407.215 KiB |
| 435 | // (+6.107 KiB, 0.25% over the prior welcome head). Retain the next tenth. |
| 436 | // Integrated model settings and bounded receipt mock measure 2468523 B |
| 437 | // (2410.667 KiB); retain 0.2 KiB headroom on the combined startup payload. |
| 438 | // Turn results add 12585 B (0.51%) over main-v2's 2464923 B: bounded receipt |
| 439 | // projection, status presentation and view bindings. Result: 2477508 B. |
| 440 | // Combined turn-result and model-settings startup payload is 2481108 B |
| 441 | // (2422.957 KiB), retaining the same bounded 0.2 KiB build headroom. |
| 442 | // The integrated turn-result base measures 2477492 B. Runtime state and |
| 443 | // session-bound receipt confirmation and its mock session contract add |
| 444 | // 5944 B (0.240%): 2483436 B total. |
| 445 | // Durable session isolation and missed-completion reconciliation add 1232 B |
| 446 | // (0.050% over that head), measuring 2484668 B total. |
| 447 | // The read-status line, read-pause card and their host wiring merge on top and |
| 448 | // measure 2488853 B. Keep the next tenth; gzip, CSS, and chunk limits unchanged. |
| 449 | // Combined model-settings and read-evidence integration measures 2492541 B, |
| 450 | // adding 3688 B (0.148%) over the base. Retain the next one-decimal ceiling. |
| 451 | // SessionRef bridge methods, active-row identity and the mock hydration event |
| 452 | // contract measure 2439.8 KiB. Extracting exact-tab mock rebinding from the |
| 453 | // startup bridge measures 2439.6 KiB; retain 0.3 KiB bounded toolchain headroom. |
| 454 | // History preparation and cancellation across startup and paging add 884 B |
| 455 | // (0.035%) to the 2498286 B base. Measured 2499170 B; retain 0.1 KiB headroom. |
| 456 | // Independent-session identity, organization CAS and unread/lifecycle guards |
| 457 | // measure 2443.0 KiB against the same-toolchain main-v2 base of 2440.7 KiB |
| 458 | // (+2.3 KiB, 0.095%). Retain one tenth; all other limits stay unchanged. |
| 459 | // Complete export execution/rendering remains lazy; only bounded progress, |
| 460 | // lifecycle observation, and cross-page tool-state hydration enter startup. |
| 461 | // A full 40-character identity measures 2064513 B in the browser build and |
| 462 | // 2064459 B in Electron, versus 2064077 B on current main-v2 (+436 B, 0.021%). |
| 463 | // Retain the smallest one-decimal ceiling; all other limits stay unchanged. |
| 464 | // Reasoning capability recovery copy moves the same-toolchain local build from |
| 465 | // 2064327 B to 2064614 B (+287 B, 0.014%). The stable build with a full source |
| 466 | // identity measures 2064746 B; retain the next one-decimal ceiling. |
| 467 | // The React error-family field adds 177 B to the same production build. The |
| 468 | // measured 2064923 B payload keeps the existing gzip, CSS, and chunk limits. |
| 469 | // Approval outcome recovery measures 2065149 B on current main-v2, versus the |
| 470 | // 2015.6 KiB base. Retain 0.15 KiB headroom; all other limits stay unchanged. |
| 471 | // Live-default draft fencing and frozen-model recovery measure 2066040 B |
| 472 | // (2017.617 KiB). Retain 0.183 KiB; gzip, chunk, CSS, and locale gates stay fixed. |
| 473 | // Session-id migration and cross-client takeover measure 2073794 B merged onto |
| 474 | // that main-v2 payload, 7754 B (0.375%) over it. The startup-path growth is |
| 475 | // attributable to this branch's remote session identity work: the remote |
| 476 | // telemetry and status modules become static imports of useRemoteSession (they |
| 477 | // leave their lazily loaded surface chunk), plus the canonical session-id |
| 478 | // plumbing, the spectator reconcile loop and its ownership classification, the |
| 479 | // pre-activation history prime, the rebased optimistic-submission settlement, |
| 480 | // and the project tree's canonical row identity. The CI Linux stable build |
| 481 | // measures 2073980 B (2025.4 KiB), 186 B above the same-toolchain macOS build; |
| 482 | // the ceiling follows the CI producer. Retain 0.13 KiB headroom at the next |
| 483 | // one-decimal ceiling; gzip, CSS, and chunk limits are unchanged. |
| 484 | // Combining that migration with restored historical-tab preparation measures |
| 485 | // 2074127 B locally. Preserve the measured 186 B Linux producer difference |
| 486 | // above (2074313 B combined), with 0.11 KiB headroom. Historical preparation |
| 487 | // remains lazy; compressed, chunk, CSS, and locale limits stay unchanged. |
| 488 | // Native window ownership, stopping-state handling and bounded transcript crash |
| 489 | // context measure 2075238 B locally (+1111 B, 0.054%). Preserve the same 186 B |
| 490 | // Linux producer difference (2075424 B) and retain the next one-decimal ceiling; |
| 491 | // gzip, chunk, CSS, and locale limits remain unchanged. |
| 492 | const rawInitialBudgetKiB = 2_027.0; |
| 493 | assertBudget("initial raw JavaScript and CSS", rawInitialBytes, rawInitialBudgetKiB * 1024); |
| 494 | assertBudget("largest initial JavaScript chunk raw", largestInitialJSRaw, 1_000 * 1024); |
| 495 |