Skip to content

fix(windows): scope quota checks to hosted chat turns - #10631

Merged
kodjima33 merged 1 commit into
BasedHardware:mainfrom
tianmind-studio:codex/windows-chat-quota-trigger
Sep 1, 2026
Merged

kodjima33 merged 1 commit into
BasedHardware:mainfrom
tianmind-studio:codex/windows-chat-quota-trigger

Conversation

@tianmind-studio

@tianmind-studio tianmind-studio commented Jul 26, 2026 •

Copy link
Copy Markdown
Contributor

What changed and why

Addresses the Stream-1 usage-limit chat integration follow-up in #10240. Current main already owns quota enforcement in the canonical mainChatQuotaGate, but its post-reply refresh still infers a hosted completion from the app-wide chat.sending busy-to-idle edge. That flag is also controlled by local automation, coding-agent, cancellation, and reset paths, so unrelated work can refresh the quota snapshot and clear its optimistic send count.

This PR replaces that inference with an explicit hosted transport boundary:

  • pi-mono emits hosted_request_started only after its provider command is written successfully;
  • the kernel and main-chat IPC project that marker to the matching renderer request, while generic coding-agent tasks filter the internal event;
  • useChat advances a monotonic quotaCheckSeq only when a real pi-mono or legacy fetch dispatch settles (including the watchdog path), never for pre-dispatch failures or reset/cancelled generations;
  • UsageLimitTriggerHost syncs the existing canonical quota gate from that sequence instead of watching shared busy state.

No second quota gate, popup latch, or probe queue is introduced; this rebased version reuses the implementation now on main from #12203.

Product invariants affected

  • INV-CHAT-1

How it was verified

  • Focused Vitest run: 7 files, 164 tests passed.
  • typecheck:node and typecheck:web passed with Node 22.23.1.
  • ESLint passed for all 14 touched TypeScript/TSX files.
  • Prettier 3.8.3 passed for all 15 touched files.
  • PR metadata preflight passed all 13 selected repository checks against current main (219.11s).
  • Deep architecture and security review found no remaining high-confidence issue; the only preflight notices are existing large-file/long-function advisories.

Tests

  • UsageLimitTriggerHost.test.tsx proves generic busy-to-idle transitions do not sync quota and an explicit hosted completion does.
  • piMono.test.ts, mainChat.test.ts, and useChat.pimono.test.tsx cover the managed-cloud event boundary, pre-dispatch failure, retry, and watchdog terminalization.
  • taskRunner.test.ts proves the internal lifecycle event cannot leak into public coding-agent events.
  • useChat.test.tsx covers legacy fetch dispatch, synchronous pre-dispatch failure, reset suppression, and normal completion.
  • Existing chatQuotaGate.test.ts remains green, covering the canonical gate's in-flight dedupe, refresh throttle, and optimistic count.

Failure class (fixes)

Failure-Class: new

Failure-class transition narrative

The violated contract was that post-chat quota refresh inferred hosted-request completion from a shared busy flag with several non-hosted owners. The canonical prevention is an adapter-owned hosted-dispatch marker, a monotonic terminal signal in the chat hook, and host consumption through the existing main-chat quota gate. PR #10236 is the supporting evidence because it introduced the temporary spinner inference and recorded this replacement as deferred work.

@Git-on-my-level Git-on-my-level left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for tightening this up. The direction looks sound to me: the quota check is now driven by an explicit hosted-request boundary instead of the shared sending busy flag, the internal hosted_request_started event is filtered out of the public coding-agent stream, and the added tests cover the important ownership/race cases including pre-dispatch failure, watchdog timeout, and serialized quota probes.

I’m not formally approving from automation because this touches quota/billing-sensitive chat behavior and the current automation context still has non-green Hygiene/Formatting/Detect Changes entries. Please get human maintainer review before merge, but I did not find a concrete code blocker in the static review.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@Git-on-my-level Git-on-my-level added security-review Touches auth, provider routing, secrets, or security-sensitive surfaces needs-maintainer-review Needs a human maintainer to sign off before merge labels Jul 26, 2026
@undivisible
undivisible force-pushed the codex/windows-chat-quota-trigger branch from 25ce3df to f775498 Compare July 27, 2026 05:05
@undivisible undivisible added human Human-authored pull request desktop javascript JavaScript or TypeScript implementation work workflow-review Needs maintainer review for workflow, automation, hooks, or CI behavior labels Aug 10, 2026
@Git-on-my-level Git-on-my-level added windows Windows desktop work and removed desktop javascript JavaScript or TypeScript implementation work workflow-review Needs maintainer review for workflow, automation, hooks, or CI behavior labels Aug 11, 2026
@Git-on-my-level

Copy link
Copy Markdown
Member

Thanks for the update — the current direction still looks sound to me, and I don’t see a code-level blocker in this head. This now scopes the chat quota probe to an explicit hosted-dispatch/settled path rather than the shared sending flag, which is the right ownership boundary for this Windows billing surface.

File-by-file notes from this head:

  • .github/failure-classes/FC-chat-quota-trigger-ownership.json records the failure class and ties the prevention artifacts back to the quota-trigger ownership contract.
  • desktop/windows/src/main/agentKernel/kernelSupport.ts maps the internal adapter event to hosted.request_started, keeping the new lifecycle signal explicit in the kernel projection.
  • desktop/windows/src/main/codingAgent/interface.ts adds HostedRequestStartedEvent with a narrow contract: only after provider transport dispatch, not for local setup failures.
  • desktop/windows/src/main/codingAgent/piMono.ts emits the signal immediately after sendCommand(cmd), so pre-session/pre-dispatch errors do not falsely count as hosted chat attempts.
  • desktop/windows/src/main/codingAgent/piMono.test.ts covers both sides of that boundary: no event on send failure and the hosted event first on the successful sink path.
  • desktop/windows/src/main/codingAgent/taskRunner.ts filters hosted_request_started out of public coding-agent events, avoiding leakage of this main-chat/billing lifecycle marker into the agent-task stream.
  • desktop/windows/src/main/codingAgent/taskRunner.test.ts verifies that filter by having the fake adapter emit the boundary while public CodingAgentEvents remain unchanged.
  • desktop/windows/src/main/ipc/mainChat.ts projects hosted.request_started to the renderer-facing hosted_request_started event while keeping local run-start/running events as status-only.
  • desktop/windows/src/main/ipc/mainChat.test.ts verifies the new projection and confirms adapter-not-registered failures do not emit the hosted boundary.
  • desktop/windows/src/renderer/src/components/settings/billing/UsageLimitTriggerHost.tsx now watches quotaCheckSeq instead of chat.sending, which removes the old false-positive path from coding-agent/local busy transitions.
  • desktop/windows/src/renderer/src/components/settings/billing/UsageLimitTriggerHost.test.tsx directly guards that regression by asserting a generic busy-to-idle edge does not probe quota, while a sequence increment does.
  • desktop/windows/src/renderer/src/hooks/useChat.ts adds quotaCheckSeq and increments it only after a hosted request has started and the turn reaches a terminal/timeout path; the legacy fetch lane similarly marks actual fetch invocation before completion.
  • desktop/windows/src/renderer/src/hooks/useChat.pimono.test.tsx updates pi_mono fixtures to emit the hosted boundary and checks success, retry, watchdog, and pre-dispatch failure behavior around quotaCheckSeq.
  • desktop/windows/src/renderer/src/hooks/useChat.test.tsx adds coverage that normal completed chat increments the sequence while agent reset/not-ready paths do not.
  • desktop/windows/src/renderer/src/lib/usageLimit.ts serializes quota probes behind a promise tail and generation guard, which prevents concurrent completions from racing the once-per-session popup latch.
  • desktop/windows/src/renderer/src/lib/usageLimit.test.ts adds regression coverage for serialized exhausted probes and queued allowed-then-exhausted probes.
  • desktop/windows/src/shared/types.ts extends MainChatEvent with the hosted-request boundary and documents why it is distinct from generic status events.

CI is green on the relevant Windows/typecheck jobs in the context I reviewed. I’m leaving this for human maintainer sign-off because it touches quota/billing-sensitive behavior, but from this pass the implementation and tests look coherent.

— Automated maintainer review (glm-5.2)


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@aryanorastar aryanorastar left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Community review (COMMENT):

Replacing app-wide sending busy→idle inference with an explicit managed-cloud quotaCheckSeq / hosted-dispatch contract is the correct fix for INV-CHAT-1 and the once-per-session latch races called out in #10240. The Vitest coverage for “local/BYOK/cancel must not bump seq” and “managed dispatch does” is the right shape.

Please confirm / keep explicit:

  1. Serialization — concurrent completions cannot race past the once-per-session popup latch (called out in the body — worth one dedicated assertion if not already isolated).
  2. Cancel / reset / coding-agent paths never publish a quota-check signal (tests appear to cover this — keep them green on tip).
  3. Latest PR Metadata Preflight is green on the tip (older CANCELLED rollups are noise if the newest run passed).

Solid Windows chat cost/UX correctness work. Not requesting changes unless (1) is missing on tip.

Replace shared busy-state edge inference with an explicit hosted-chat completion sequence owned by useChat.

Failure-Class: new
@tianmind-studio
tianmind-studio force-pushed the codex/windows-chat-quota-trigger branch from f775498 to 90aff16 Compare August 28, 2026 07:11
@tianmind-studio

Copy link
Copy Markdown
Contributor Author

Rebased this onto current main and force-pushed 90aff16. The refresh drops the superseded duplicate probe/popup machinery and now feeds the canonical gate from #12203; focused Vitest is 164/164, both Windows typechecks and touched-file ESLint pass, and current-main PR preflight passes all 13 selected checks.

@Git-on-my-level

Copy link
Copy Markdown
Member

Verified on the rebased head 90aff160 — thanks for dropping the superseded probe/popup machinery and feeding the canonical gate from #12203. That simplification resolves the main structural note from the earlier automated pass, and the checks that were non-green then (Hygiene/Formatting/Detect Changes) are green on this head.

What I verified statically on this head:

  • piMono.ts — onDispatched fires strictly after sendCommand(cmd) writes the provider command, sendCommand throws synchronously when the pi-mono process/stdin is not writable, and both the session check and the activePromptGeneration serialization guard run before the write. Local binding/session/concurrency failures genuinely cannot emit the dispatch marker, so the boundary the new failure-class record describes holds in code.
  • kernelSupport.ts / mainChat.ts / types.ts — the canonical hosted.request_started mapping is what carries the event through the kernel event store; projectKernelEvent is the only switch over these kernel event types and handles the new case, so no other consumer meets an unhandled variant.
  • taskRunner.ts — the internal event is filtered from the public CodingAgentEvent wire, with a regression test asserting it never leaks into the generic agent stream.
  • useChat.ts — quotaCheckSeq advances exactly once per hosted turn: the pi_mono terminal handler is isCurrent()-gated, the watchdog path records before invalidating the generation, and the legacy doFetch marks the boundary only after fetch() accepts the request so a synchronous setup throw stays pre-dispatch (tested). The seq is part of the UseChat shape that AppStateProvider publishes, so the trigger host reads a real field rather than only a test mock.
  • UsageLimitTriggerHost.tsx — busy-to-idle edges no longer probe; only an explicit seq advance syncs, with a dedicated test, and mainChatQuotaGate.sync() keeps its in-flight dedupe and per-interval floor so a burst of turns cannot hammer /usage-quota.
  • FC-chat-quota-trigger-ownership.json — matches the established failure-class schema and the PR-template requirement.

On the three asks from @aryanorastar: cancel/reset/coding-agent paths never publish a quota signal — covered by the new assertions (both reset suites expect seq 0 and the taskRunner filter test holds); the PR Metadata Preflight is green on this tip. For the popup-latch serialization question, that latch now lives in the machinery this rebase adopted from #12203 rather than in this diff — the seq is a functional state update and only the current generation's terminal bumps it, so I don't see a new race here; if the maintainer still wants a dedicated concurrent-completion assertion, it would sit most naturally beside that latch.

No code-level blocker found in static review of this head. Not formally approving from automation because this drives quota/billing-sensitive refresh behavior; keeping security-review + needs-maintainer-review so a human maintainer signs off on the quota-trigger ownership decision before merge.


by AI on behalf of David — if you need David’s attention urgently, please @Git-on-my-level and escalate with need human response.

@kodjima33 kodjima33 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Desktop bug-fix fast lane: title fix(windows), scopes quota-check refresh to real hosted chat dispatches instead of a shared busy flag, well-tested (164 tests), CI green, no abuse signals. Owner-override merge.

@kodjima33
kodjima33 merged commit 8da225c into BasedHardware:main Sep 1, 2026
33 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

human Human-authored pull request needs-maintainer-review Needs a human maintainer to sign off before merge security-review Touches auth, provider routing, secrets, or security-sensitive surfaces windows Windows desktop work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants