Skip to content

test(sdk): deterministic host steer response handshake - #4643

Merged
probepark merged 2 commits into
devfrom
fix/issue-4641-sdk-steer-ci
Aug 18, 2026
Merged

test(sdk): deterministic host steer response handshake#4643
probepark merged 2 commits into
devfrom
fix/issue-4641-sdk-steer-ci

Conversation

@Yeachan-Heo

@Yeachan-Heo Yeachan-Heo commented Aug 17, 2026

Copy link
Copy Markdown
Owner

Closes #4641

Root cause

Dev CI run 32059470791 (job 95479277528, shard 6-of-8) failed both tests in packages/coding-agent/test/sdk-host-steer-integration.test.ts at the harness transport's fixed 100 × Bun.sleep(1) pollhost did not respond. The same run/head passed on rerun (job 95485461325). Reproduced locally at exact head 6696988b6dd757f898ab7b33984ba830b6344109 only under fresh-process load; both merge parents (80a0c0974d #4631, 6696988b6d #4613) carry the same poll loop. Not a product regression — an invalid wall-clock bound on the test transport: the host answers asynchronously, so any scheduling stall > ~100ms starves the poll while the response is still in flight.

Fix (causal contract, not a bigger sleep)

Per-emission promise handshake resolved by the response frame itself, correlated by request id:

  • sendFrame resolves the live emission only when frame.id === pending.id; anything else is fenced into lateResponses (a stale frame can never satisfy a later emission).
  • Bounded safety timeout (RESPONSE_TIMEOUT_MS = 5000) fails a lost response instead of hanging to the harness timeout; dangling sleep timer does not delay process exit.
  • Exact cleanup: stop() clears pending delayed-delivery timers.
  • Injected responseDelayMs for deterministic race-contract tests.

New race-contract tests

  1. Delayed response (250ms, past the retired 100ms bound) resolves through the handshake.
  2. Timeout then fenced late response: emission fails at the bound, the late frame is recorded as stale, a subsequent emission under a different id resolves only against its own response.
  3. Sequential controls in the durable-replay test now assert lateResponses === [].

Preserved verbatim: durable replay correlation, restart process_restart_uncertain, no redispatch (dispatches counts), undefined-session-file state-root persistence.

Evidence (exact head d0a1397472, parent 6696988b6dd757f898ab7b33984ba830b6344109)

Check Result
Focused file bun test ×3 4 pass / 0 fail each
Focused file fresh-process (CI sandbox env: HOME/XDG_*/TMPDIR/GJC_HOME + test-preload.ts) ×3 4 pass / 0 fail each
Adjacent SDK suites (session-runtime, sdk-surface-parity, sdk-broker-restart, sdk-reconciliation-recovery, sdk-q26-prompt-status, sdk-session-isolation) 105 pass / 0 fail
bun --cwd=packages/coding-agent run check (biome + tsc) green
Default definitions gate + check-visible-definitions green

Unrelated shard evidence (not absorbed in this lane)

Shard-6-of-8 full run locally fails packages/coding-agent/test/tools/lsp-regressions.test.ts (3 tests: tlaplus detection, csharp-ls preferred, omnisharp fallback) only under the sandboxed HOME used by run-bun-test-files.ts; verified at exact dev head 6696988b6dd757f898ab7b33984ba830b6344109 with this patch stashed (clean tree) → identical failures. Unsandboxed: 24/24 pass. Environment-sensitive path-trust/$which interaction with the synthetic ~/.gjc-lsp-* binaries — pre-existing, out of #4641 scope, needs its own issue.

GJC verdict

gajae.pr-review-verdict.v1 merge-approved sha256:1f8551a57d68e0f83f06c296c3ccadcf1998761060b6098bc608cd2a5fbcc015 reviewer:human reviewer-id:probepark evidence:independent-architect-review-test-only-request-id-correlated-handshake-replaces-flaky-polling-250ms-delayed-and-late-response-cases-fail-on-retired-harness


[repo owner's gaebal-gajae (clawdbot) 🦞]

Dev CI shard-6 (run 32059470791, job 95479277528) failed both
sdk-host-steer-integration tests at the harness transport's fixed
100 x Bun.sleep(1) poll with "host did not respond", while the same
head passed on rerun (job 95485461325): a wall-clock poll bound, not
a host response regression from #4631/#4613.

Replace the poll loop with a per-emission promise handshake resolved
by the response frame correlated to the live request id, fenced
against stale/late frames (recorded, never resolving a later await),
with a bounded safety timeout instead of a hang. Transport now also
supports an injectable response delay for race-contract tests:
delayed response beyond the old 100ms bound, timeout then fenced late
response, and sequential control isolation. Durable replay/restart,
no-redispatch, and undefined-session-file state-root contracts are
preserved verbatim.

Sandbox-only unrelated evidence recorded in the PR: tools/lsp-regressions
C#-LSP/tlaplus tests fail under the run-bun-test-files sandboxed HOME
at this head both with and without this patch (environment-sensitive,
not absorbed in this lane).

Closes #4641

Lore-id: 4641-steer-sync
Constraint: no blind sleep inflation; handshake must be tied to actual host response
Constraint: keep synchronous delivery semantics of the memory transport
Rejected: raising the poll budget to e.g. 500 iterations | still wall-clock dependent under fresh-process load
Rejected: unbounded await on response promise | a lost response would hang the file to the harness timeout
Tested: focused file 3x (4/0), 3x fresh-process sandboxed (4/0), adjacent SDK suites 105/0, package check green
Not-tested: none; CI shard-6 rerun on the PR head
Confidence: high
Scope-risk: narrow
Reversibility: trivial
@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.
To continue using code reviews, add credits to your account and enable them for code reviews in your settings.

@Yeachan-Heo

Copy link
Copy Markdown
Owner Author

Signed exact-head evidence — #4643

Field Value
Base 6696988b6dd757f898ab7b33984ba830b6344109 (exact dev head from issue #4641, run 32059470791)
Head d0a1397472449783f4745784a1c4702e0ede268a
Base…head diff sha256 09e52d502657854e6e4cd859d43bcf5399e1c1a6ee94c63a370d6878656ca242
Diff scope 1 file, +127/−9 — packages/coding-agent/test/sdk-host-steer-integration.test.ts only

Root cause (not a product regression)

Fix — causal handshake, not a bigger sleep

  • Per-emission promise resolved by the response frame correlated to the live request id (frame.id === pending.id); mismatched frames are fenced into lateResponses and can never satisfy a later await.
  • Bounded safety timeout (5s) fails a lost response instead of hanging to the harness timeout; dangling sleep timer verified not to delay process exit.
  • Exact cleanup: stop() clears pending delayed-delivery timers.
  • New race contracts: delayed response (250ms > retired bound), timeout-then-fenced-late-response, sequential-control isolation (lateResponses === []).
  • Pinned unchanged: durable replay correlation, restart process_restart_uncertain, no redispatch (dispatches), undefined-session-file state-root persistence.

Verification at exact head

Check Result
Focused file ×3 4/0 each
Focused file fresh-process, CI sandbox env ×3 4/0 each
Adjacent SDK suites (6 files) 105/0
bun --cwd=packages/coding-agent run check green
Default-definitions gate + check-visible-definitions green

Unrelated shard attribution (explicitly not absorbed)

packages/coding-agent/test/tools/lsp-regressions.test.ts — 3 tests (tlaplus detection, csharp-ls preferred, omnisharp fallback) fail only under the sandboxed HOME used by scripts/run-bun-test-files.ts. Verified at exact dev head 6696988b6d with this patch stashed (clean tree) → identical failures; unsandboxed 24/24 pass. Environment-sensitive path-trust/$which interaction with synthetic ~/.gjc-lsp-* binaries. Pre-existing, out of #4641 scope, needs its own issue.

Reviewers requested: @probepark @snowykr (independent review on this exact head; verdict stays needs-human until an authenticated approving review lands).


[repo owner's gaebal-gajae (clawdbot) 🦞]

@probepark probepark 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.

Independent maintainer review at exact head 2d35eb7. independent-architect-review-test-only-request-id-correlated-handshake-replaces-flaky-polling-250ms-delayed-and-late-response-cases-fail-on-retired-harness

@probepark
probepark merged commit b72f125 into dev Aug 18, 2026
21 of 39 checks passed
pull Bot pushed a commit to nenyatech-mirror/gajae-code that referenced this pull request Aug 18, 2026
…eachan-Heo#4643)

Dev CI shard-6 (run 32059470791, job 95479277528) failed both
sdk-host-steer-integration tests at the harness transport's fixed
100 x Bun.sleep(1) poll with "host did not respond", while the same
head passed on rerun (job 95485461325): a wall-clock poll bound, not
a host response regression from Yeachan-Heo#4631/Yeachan-Heo#4613.

Replace the poll loop with a per-emission promise handshake resolved
by the response frame correlated to the live request id, fenced
against stale/late frames (recorded, never resolving a later await),
with a bounded safety timeout instead of a hang. Transport now also
supports an injectable response delay for race-contract tests:
delayed response beyond the old 100ms bound, timeout then fenced late
response, and sequential control isolation. Durable replay/restart,
no-redispatch, and undefined-session-file state-root contracts are
preserved verbatim.

Sandbox-only unrelated evidence recorded in the PR: tools/lsp-regressions
C#-LSP/tlaplus tests fail under the run-bun-test-files sandboxed HOME
at this head both with and without this patch (environment-sensitive,
not absorbed in this lane).

Closes Yeachan-Heo#4641

Lore-id: 4641-steer-sync
Constraint: no blind sleep inflation; handshake must be tied to actual host response
Constraint: keep synchronous delivery semantics of the memory transport
Rejected: raising the poll budget to e.g. 500 iterations | still wall-clock dependent under fresh-process load
Rejected: unbounded await on response promise | a lost response would hang the file to the harness timeout
Tested: focused file 3x (4/0), 3x fresh-process sandboxed (4/0), adjacent SDK suites 105/0, package check green
Not-tested: none; CI shard-6 rerun on the PR head
Confidence: high
Scope-risk: narrow
Reversibility: trivial

Co-authored-by: Yeachan Heo <yeachan.heo@gmail.com>
Co-authored-by: probe <re2rar@gmail.com>
(cherry picked from commit b72f125)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants