Files
makelore/.project-docs/30-worklog/tasks/20260819-design-freeze-live-6e2c.md
T

9.0 KiB

Task: Diagnose live AI design freeze after HTTP1 package

Identity

  • Task ID: 20260819-design-freeze-live-6e2c
  • Mode: Feature
  • Branch: codex/20260819-design-freeze-live-6e2c-design-freeze-live
  • Worktree: D:\mk-design-freeze-live-6e2c
  • Base commit: 1ba68a9e41
  • Owner: codex-root
  • Status: Completed

Scope

  • Inspect the live installed Makelore process after the combined native-login and HTTP/2-disabled diagnostic package froze in AI Design.
  • Verify package provenance, Chromium transport flags, process health, TCP state, local Host API responsiveness, session-refresh eligibility, Works service health, and the matching request implementation.
  • Implement the directly authorized fix in the same task: bound finite AI Design and Works session-refresh requests, prevent implicit replay of non-idempotent requests, and add focused regressions.
  • Build a Windows diagnostic package that retains native password/SMS login and disable-http2 so this behavior change can be isolated in the user's next smoke test.

Intent And Constraints

  • The user explicitly requested direct execution without sub-agents.
  • Treat the observed 409, global Loading state, and previous ERR_HTTP2_PROTOCOL_ERROR as separate signals until live evidence connects them.
  • Read the encrypted session only to project expiration/activity metadata and to issue bounded read-only requests; never print or persist access/refresh tokens.
  • Do not replay the Quote PATCH because its idempotency and server-side commit state are not proven.
  • Remove every temporary harness and isolated Chromium profile before completion.

Outcome

Confirmed facts

  • The installed app.asar SHA-256 exactly matched the combined diagnostic package: 80948DDBBD5F9BDE9B4C75CB40E515E63200AA79582BF16CF88347DECCD864C6.
  • The live Network Service process had --disable-http2; HTTP/2 was genuinely disabled.
  • Main, Renderer, GPU, and Network Service remained Windows-responsive. A three-second sample showed zero Main/Renderer CPU growth and only 0.0156 seconds in Network Service, so the process was passively waiting rather than spinning or blocking the UI thread.
  • While the UI remained stuck, three rounds of 20 concurrent requests to the live Host API all completed with the expected unauthenticated 401 in 47/16/14 ms. Main's HTTP event loop and listener were not globally blocked.
  • The stored access token was more than 157 minutes from expiry and outside the 30-second refresh skew. The shared refreshFlight was not active for this incident.
  • With the same authenticated session, read-only Node probes to /api/auth/me, Design capabilities, Workspace list, the affected Workspace, and its two Conversations returned 200 in 31-159 ms. Works, the account, and that Workspace were not globally locked.
  • A fresh Electron 43.4.0 Network Service with disable-http2 returned 200 for the same three read-only endpoints in 1764-1853 ms. HTTP/1.1 itself remained functional.
  • Product code gives net.fetch, its global-fetch fallback, session refresh, design authorizedFetch, IPC Host API requests, Workspace/Conversation loads, and Quote repricing no common deadline or abort contract. Their UI cleanup runs only after the Promise settles.
  • proxyAwareFetch catches every Electron net.fetch exception and unconditionally replays the same request with global fetch. That is unsafe for mutation methods such as Quote PATCH when the first request may have committed before its response transport failed.

Conclusion

  • Disabling HTTP/2 did not fix the freeze and is not the root cause.
  • The live symptom is an unbounded in-process network/IPC Promise in the current Makelore network context. The process remains healthy but a request can wait forever, leaving Renderer loading/busy state waiting for finally.
  • The observed 409 is a completed conflict response and cannot itself block the JavaScript thread. It may be a secondary symptom of the unconditional transport fallback replay after an earlier protocol failure, but that specific mutation was not replayed during this diagnostic task.
  • The user's “one request hangs, then everything stays Loading” report is consistent with missing deadlines/cancellation plus page-level state ownership, not with a dead request thread.

Implemented fix

  • Added a reusable deadline primitive that aborts the active transport and still rejects deterministically when a transport ignores AbortSignal.
  • Bounded the complete AI Design JSON request lifecycle to 30 seconds, including token lookup/refresh, response headers, and response-body parsing. The adapter returns a stable 504 DESIGN_WORKSPACE_REQUEST_TIMEOUT with AI 设计服务响应超时,请重试.
  • Bounded the shared Works access-token refresh to 30 seconds. All coalesced callers settle together, refreshFlight is released by its existing finally, and a later refresh can retry without clearing a still-valid refresh token.
  • Restricted Electron-to-Node fetch fallback to safe reads (GET, HEAD, OPTIONS). POST, PATCH, PUT, and DELETE transport failures now propagate without implicitly replaying the mutation.
  • Preserved native password/SMS login and the temporary global disable-http2 switch in the diagnostic installer so the user can isolate this request-lifetime/replay fix before HTTP/2 policy is revisited.

Verification

  • Live installed-package hash comparison — PASS, exact match.
  • Live process command-line projection — PASS, Network Service had disable-http2.
  • Live process responsiveness/CPU sampling — PASS, responsive passive wait.
  • Host API concurrency probe — PASS, 60/60 requests completed within 47 ms per round maximum.
  • Encrypted-session metadata projection — PASS, no refresh window and no token output.
  • Authenticated read-only Works differential probes — PASS, account/Design/Workspace/Conversation endpoints responsive.
  • Fresh Electron HTTP/1.1 differential probe — PASS.
  • Static request-path inspection — confirmed missing deadlines/AbortSignals and unconditional fallback replay.
  • Temporary scripts, metadata output, and isolated Electron profile — removed.
  • Red-phase regression run — PASS as evidence: the new mutation-replay, stuck request, and stuck shared-refresh tests all failed against the previous implementation.
  • Focused unit tests — PASS, 59/59.
  • TypeScript check — PASS, pnpm run typecheck.
  • Lint check — PASS with zero errors and seven pre-existing unrelated warnings.
  • Full unit suite — PASS on clean rerun, 184 files and 2190 tests. The first run had one unrelated Windows port-release timing failure; its focused rerun passed before the clean full rerun.
  • Production compile — PASS, pnpm run build:vite.
  • Windows package build — PASS after the verified prior uv binary and verified Learning Player artifact were reused when GitHub download timed out.
  • Packaged Learning Player verification — PASS, SHA-256 748d6d7c74d9d0ba444e0c051a50010f7070e7d6e46442b1e60f1e2be80fd020.
  • Packaged app.asar marker inspection — PASS for disable-http2, DESIGN_WORKSPACE_REQUEST_TIMEOUT, and the request-deadline implementation.
  • Packaged Electron runtime verification — PASS, including Electron 43.4.0, Node 24.18.1, OpenCode, Python, uv, npm, and native addons.
  • Installer SHA-256 — FB3BDA4F464770E58EA0AA6E989366685976D5FA7B76E6944B92879B423D83D4.
  • Packaged app.asar SHA-256 — A752255B43694814CC3D3FB86C75E024D39E67BBF87BE273960A1FA9FA0366A6.

Follow-ups

  • Smoke the original Quote edit/retry/confirm sequence in the diagnostic package. A stalled request should now leave Loading within 30 seconds and show the explicit timeout message; a transport exception must not produce a second Quote PATCH.
  • After the user confirms the bounded-request fix, restore HTTP/2 in a separate package and repeat the same regression smoke. disable-http2 remains a diagnostic variable, not the root-cause fix.
  • Add privacy-safe request lifecycle instrumentation (route kind, transport, phase, elapsed time, deadline outcome, correlation id) so a future live freeze identifies the exact pending boundary without DevTools or credentials.

Promotion Candidates

  • Target: .project-docs/20-architecture/data-flow.md and the AI Design transport boundary.
  • Proposal: document the implemented bounded AbortSignal/deadline contract for Main-owned finite Works requests and the prohibition on implicit mutation replay without an explicit idempotency contract.
  • Evidence: this live differential diagnosis, the focused/full regression results, electron/utils/proxy-fetch.ts, electron/services/works-square-session.ts, electron/image-workspace/works-square-workspace.ts, src/lib/host-api.ts, and src/stores/image-workspace.ts.
  • Future impact: network failures become terminal and recoverable instead of leaving permanent Loading state or ambiguously duplicating Quote mutations.
  • Semantic conflicts: none with ADR-001; Workspace/Conversation ownership remains unchanged.
  • Human confirmation required: no for a bounded bug fix, but permanent HTTP protocol policy should be decided separately after the fixed package is validated.