diff --git a/.project-docs/30-worklog/tasks/20260819-disable-http2-package-b84c7e.md b/.project-docs/30-worklog/tasks/20260819-disable-http2-package-b84c7e.md new file mode 100644 index 0000000..3eb9cea --- /dev/null +++ b/.project-docs/30-worklog/tasks/20260819-disable-http2-package-b84c7e.md @@ -0,0 +1,56 @@ +# Task: Build HTTP2-disabled diagnostic package + +## Identity + +- Task ID: 20260819-disable-http2-package-b84c7e +- Mode: Feature +- Branch: codex/20260819-disable-http2-package-b84c7e-disable-http2-package +- Worktree: D:\mk-disable-http2-b84c7e +- Base commit: 9ff79e96813ebadade0aecb8f407edf5aee0144a +- Owner: codex-root +- Status: Completed + +## Scope + +- Build a Windows x64 diagnostic installer from local `main` base `9ff79e9` with Chromium HTTP/2 disabled globally so the user can A/B the observed request fan-out stall against HTTP/1.1. +- Keep the source change limited to Electron Main startup and a focused ordering regression test; do not change Quote, authentication, retry, timeout, Renderer, or server behavior. + +## Intent And Constraints + +- Apply Chromium's `disable-http2` command-line switch before `app.whenReady()`, window creation, or network initialization. +- Treat the installer as diagnostic evidence, not a permanent protocol policy or a fix for the independent unbounded shared authentication flights. +- Build with the exact pnpm version pinned by `packageManager`; do not commit generated packages, downloaded runtimes, caches, or secrets. +- Keep all work in `D:\mk-disable-http2-b84c7e`; the main worktree remains owned by a superseded packaging record and is not modified. +- The user previously prohibited sub-agents; implementation and validation are performed directly without delegated or reviewer agents. + +## Outcome + +- Added `app.commandLine.appendSwitch('disable-http2')` at Electron Main bootstrap, before the single-instance lock and `app.whenReady()`. +- Added a focused regression test that guards the switch and its startup ordering. +- Built the Windows x64 diagnostic installer `release/Makelore-2.0.0-win-x64.exe` without installing it or interrupting the user's running client. +- The package reuses the installed client's repository-validated, versioned Learning Player (`htmlSha256` `748d6d7c74d9d0ba444e0c051a50010f7070e7d6e46442b1e60f1e2be80fd020`) so the transport switch remains the intended A/B variable. + +## Verification + +- Red test: `pnpm exec vitest run tests/unit/http2-diagnostic-bootstrap.test.ts --reporter=verbose` failed before implementation because the switch was absent. +- Green test: the same focused Vitest command passed after implementation (1 test). +- `pnpm run typecheck` passed. +- `pnpm exec eslint electron/main/index.ts tests/unit/http2-diagnostic-bootstrap.test.ts` passed. +- `pnpm run build:vite` passed; only pre-existing Vite chunk/dynamic-import warnings were emitted. +- Packaged Main inspection found `disable-http2` in `dist-electron/main/index-Cn0aQm66.js` at byte offset 500965, before `requestSingleInstanceLock` at 501868 and `whenReady` at 511598. +- `pnpm run verify:publish-runtime` passed (`npm 11.6.2`). +- `pnpm run verify:artifact:win -- --allow-dirty --installer .\\release\\Makelore-2.0.0-win-x64.exe` passed for Electron 43.4.0, Node 24.18.1, OpenCode 1.18.9, Python, uv 0.10.0, npm 11.6.2, native modules, and Unicode-copy proof. +- Packaged `package.json` reports version `2.0.0`; installer FileVersion and ProductVersion are both `2.0.0`. +- Installer size: 302,025,645 bytes; SHA-256: `1C4E4E67F29A58E81705CA258E171200576BE41B313B4CD794D49CD296B479EE`. +- Authenticode status is `NotSigned`, consistent with the local unsigned packaging configuration. +- `git diff --check` passed. + +## Follow-ups + +- The user should exit the currently running Makelore instance, install the diagnostic package, reproduce the same Quote PATCH flow, and compare whether global pending requests and `net::ERR_HTTP2_PROTOCOL_ERROR` disappear. +- If the stall remains under forced HTTP/1.1, resume diagnosis at the shared authentication/request single-flight path rather than treating HTTP/2 as the root cause. +- If the stall disappears, capture server/proxy and Chromium NetLog evidence before deciding whether to keep a protocol workaround or repair the HTTP/2 path. + +## Promotion Candidates + +- Keep the startup switch temporary until the A/B result is known; do not promote it as a permanent release policy without evidence. diff --git a/electron/main/index.ts b/electron/main/index.ts index e5cd58a..8482bd8 100644 --- a/electron/main/index.ts +++ b/electron/main/index.ts @@ -95,6 +95,9 @@ import { import { WorksSquareDesignWorkspace } from '../image-workspace/works-square-workspace'; import type { DesignWorkspaceModule } from '../image-workspace/module'; +// Diagnostic package: force Chromium networking onto HTTP/1.1 for transport A/B testing. +app.commandLine.appendSwitch('disable-http2'); + const WINDOWS_APP_USER_MODEL_ID = 'app.niancode.desktop'; const isE2EMode = process.env.NIANCODE_E2E === '1'; const requestedUserDataDir = process.env.NIANCODE_USER_DATA_DIR?.trim(); diff --git a/tests/unit/http2-diagnostic-bootstrap.test.ts b/tests/unit/http2-diagnostic-bootstrap.test.ts new file mode 100644 index 0000000..d233f63 --- /dev/null +++ b/tests/unit/http2-diagnostic-bootstrap.test.ts @@ -0,0 +1,16 @@ +import { readFileSync } from 'node:fs'; +import { resolve } from 'node:path'; +import { describe, expect, it } from 'vitest'; + +describe('HTTP/1.1 diagnostic bootstrap', () => { + it('disables HTTP/2 before Electron startup and network initialization', () => { + const source = readFileSync(resolve(process.cwd(), 'electron/main/index.ts'), 'utf8'); + const disableHttp2 = source.indexOf("app.commandLine.appendSwitch('disable-http2')"); + const singleInstanceLock = source.indexOf('app.requestSingleInstanceLock()'); + const ready = source.indexOf('app.whenReady()'); + + expect(disableHttp2).toBeGreaterThan(-1); + expect(disableHttp2).toBeLessThan(singleInstanceLock); + expect(disableHttp2).toBeLessThan(ready); + }); +});