Skip to content

test(node-sdk): stop comparing killed-task exitCode across engines - #2857

Open
rajathpi wants to merge 3 commits into
MoonshotAI:mainfrom
rajathpi:deflake-killed-task-exitcode-parity
Open

test(node-sdk): stop comparing killed-task exitCode across engines#2857
rajathpi wants to merge 3 commits into
MoonshotAI:mainfrom
rajathpi:deflake-killed-task-exitcode-parity

Conversation

@rajathpi

Copy link
Copy Markdown

Related Issue

No existing issue — the problem is explained below. (Searched open issues and PRs for this flake first; nothing covers it.)

Problem

pnpm test intermittently fails the background-task lifecycle parity test:

FAIL |kimi-sdk| test/v1-v2-parity.test.ts > v1↔v2 background task parity > task lifecycle matches: list / detach / output / stop
-     "exitCode": -1      <- expected (v1)
+     "exitCode": 143     <- received (v2)

This is a flaky test, not an engine divergence. The killed-task step stops sh -c "echo bg-out; sleep 30", and stopping signals the whole process group (process.kill(-pid, 'SIGTERM') — v1 packages/kaos/src/local.ts, v2 packages/agent-core-v2/src/os/backends/node-local/hostProcessService.ts). The shell then settles one of two ways depending on which group member observes the SIGTERM first:

  • reaped by the signal itself → exit fires with code=null, signal=SIGTERM, which both backends map through the same code ?? -1 to -1;
  • it outlives sleep and exits with the shell's 128+15 convention → code=143, signal=null143.

Both backends' child.on('exit') handlers are line-for-line identical, and so is the settle logic in v1 ProcessBackgroundTask and v2 ProcessTask — so a 143 can only come from the shell itself. I reproduced the race outside the engines with a bare spawn of the same command (detached, group-SIGTERM after 60ms, 40 trials): 38x code=null signal=SIGTERM, 2x code=143 signal=null on an idle machine. Each engine independently lands on either side of that coin flip, and the parity comparison fails whenever they disagree.

What changed

Test-only, one file (packages/node-sdk/test/v1-v2-parity.test.ts):

  • projectBackgroundTask() now deletes exitCode only when status === 'killed', with a comment explaining the OS race. Completed and failed tasks keep comparing exitCode in full — the drain-mode test's exitCode: 0 assertion is deliberately untouched.
  • The KNOWN_DIFFS.listBackgroundTasks doc comment is updated to match (that block documents every projected-away field and why).
  • At the killed-task call site, two new assertions keep the field covered instead of dropping it silently: each engine must still settle on a number exit code. Not asserting -1 | 143 specifically, because those values are POSIX-specific and the suite also runs on Windows.

This follows the file's existing pattern: KNOWN_DIFFS projections remove per-run nondeterminism (pids, timestamps, task-id suffixes) while call-site assertions pin the invariants that do hold. An engine-side fix was considered — mapping signal deaths to 128+N would make the reported code deterministic here — but that changes the SDK-visible exitCode for every signal-killed task on both engines, which is a behavior change deserving its own discussion, and the engines already agree exactly on how they map the exit payload today, so there is no cross-engine bug for this parity test to catch.

Verification:

  • pnpm vitest run packages/node-sdk/test/v1-v2-parity.test.ts → 84 passed, several consecutive runs.
  • Forced-divergence control: with the killed-task infos hard-coded to exitCode: 143 vs -1, the fixed projection passes; with the new projection line disabled, it fails with exactly the original error signature.
  • pnpm lint → 0 errors, warning count identical to main.
  • Package-scoped tsc --noEmit on @moonshot-ai/kimi-code-sdk passes.

Checklist

  • I have read the CONTRIBUTING document.
  • I have linked a related issue, or explained the problem above.
  • I have added tests that prove my feature works.
  • Ran gen-changesets skill, or this PR needs no changeset.
  • Ran gen-docs skill, or this PR needs no doc update.

Stopping a background task signals the whole process group, and the
shell settles two ways depending on which group member observes the
SIGTERM first: reaped by the signal itself (exit code null, mapped to
-1 by both engines) or outliving its child and exiting 128+15 (143).
Both engines map the raw exit payload identically, so each lands on
its own side of that OS race and the lifecycle parity test fails
whenever the two runs disagree.

Project exitCode away for killed tasks only, and assert instead that
each engine still settles on a concrete numeric code. Completed and
failed tasks keep comparing exitCode in full.
@changeset-bot

changeset-bot Bot commented Aug 12, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 7b4a63e

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@rajathpi

Copy link
Copy Markdown
Author

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 9f0fbae4e5

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@rajathpi

Copy link
Copy Markdown
Author

@chatgpt-codex-connector review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. You're on a roll.

Reviewed commit: f485960647

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@rajathpi

Copy link
Copy Markdown
Author

@liruifengv could you please take a look when you have time 😄

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.

1 participant