feat(gateway): 240s no-output watchdog for interactive web-chat turns#84
Merged
Merged
Conversation
…rns (240s) A genuinely-hung web-chat/console turn (gateway /v1/chat/completions + /v1/responses) could sit silent up to 10min (the FRESH watchdog ceiling) before failing, prompting emergency gateway restarts. Add CLI_INTERACTIVE_WATCHDOG_DEFAULTS capping the no-output ceiling at 240s for these turns only. The discriminator is messageChannel === "webchat" (INTERNAL_MESSAGE_CHANNEL) AND trigger === "user" — NOT trigger alone: trigger "user" is the universal ingress stamp (also covers Slack/SMS inbound + foreground subagent runs that legitimately go silent for minutes), while "webchat" is set exclusively by the two gateway HTTP ingress paths. So this provably does NOT touch: - dream-pipeline cron turns (trigger "cron" -> keep the 600s FRESH ceiling), - web-chat continuations (RESUME -> 180s), - other-channel inbound or foreground subagents (no "webchat" channel). The 180s cold-start floor (minMs) is preserved; an explicit per-backend watchdog.fresh override still opts out (the !configured guard). Designed + adversarially verified via multi-agent review (the trigger-vs-channel discriminator + the cron-fresh-webchat exclusion were both caught there). 6 new unit tests lock the boundaries; existing watchdog tests unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
LightDriverCS
marked this pull request as ready for review
June 23, 2026 03:03
Author
|
Operator-merge path (Cory, 2026-06-23): Anvil's codex review starved at the 45-min hard cap under mono-PR contention. This change was already designed + adversarially verified by a 6-agent investigate→design→refute workflow (which caught the trigger-vs-channel discriminator + the cron-fresh-webchat regression hole), with 16 passing unit tests — exceeding a single codex pass. Un-drafted to run full CI; merging on green. NOTE: the |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
A genuinely-hung web-chat/console turn (gateway
/v1/chat/completions+/v1/responses) could sit silent up to 10 min (the FRESH watchdog ceiling) before failing — which prompted emergency gateway restarts (and those have guillotined dream lanes). This addsCLI_INTERACTIVE_WATCHDOG_DEFAULTScapping the no-output ceiling at 240s for these turns only.The careful part — why
messageChannel, nottriggerThe obvious lever (
trigger === "user") is wrong:"user"is the universal ingress stamp — it also covers Slack/SMS inbound and foreground subagent runs that legitimately go silent for minutes. The correct discriminator ismessageChannel === "webchat"ANDtrigger === "user"—"webchat"is set exclusively by the two gateway HTTP ingress paths (openai-http.ts:1079). So this provably does not touch:trigger "cron"→ keep the 600s FRESH ceiling),"webchat"channel).The 180s cold-start floor (
minMs) is preserved, and an explicit per-backendwatchdog.freshoverride still opts out (the!configuredguard).Verification — multi-agent adversarial review
Designed + verified by a 6-agent investigate→design→adversarial-refute workflow. Two things it caught that a naive change would've missed: (1)
triggeris too broad (subagent false-kills), (2) a cron-fresh-webchat turn would regress to 240s without thetrigger === "user"exclusion. Both are in the shipped guard + tests.Anvil-ready handoff
cli-watchdog-defaults.ts(+1 constant),cli-runner/reliability.ts(threadmessageChannel+ 1 branch),cli-runner/execute.ts(+1 arg at the sole call site),cli-runner.reliability.test.ts(+6 cases). 4 files, additive.vitest -t resolveCliNoOutputTimeoutMs→ 16 passed / 0 failed (6 new + 2 existing unchanged).messageChannel?: stringconfirmed on the param type (types.ts:82); tsc clean for the changed files.minMsfloor already assumes this);/v1/responsesgets the same cap (intended — same interactive surface).🤖 Generated with Claude Code