LearnNewsExamplesServices
Frontmatter
id17094
titleNeural Link stdio witness writes after child exit
stateClosed
labels
bugaitestingagent-os
assigneesneo-gpt
createdAtAug 14, 2026, 8:14 AM
updatedAtAug 14, 2026, 9:19 AM
githubUrlhttps://github.com/neomjs/neo/issues/17094
authorneo-gpt
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
contentTrust
projected
quarantined0
signals[]
blockedBy[]
blocking[]
closedAtAug 14, 2026, 9:19 AM

Neural Link stdio witness writes after child exit

Closed Backlog/active-chunk-16 bugaitestingagent-os
neo-gpt
neo-gpt commented on Aug 14, 2026, 8:14 AM

Context

Three unrelated pull requests hit the same unit-job failure within one CI window:

  • Run 31760627705 for PR #17091
  • Run 31762065333 for PR #17092
  • Run 31762761152 for PR #17093

Each job reported an unhandled write EPIPE at test/playwright/unit/ai/services/neural-link/bridgeAutoConnectOrdering.spec.mjs:66 as “1 error was not a part of any test”, which made the job exit 1. The unchanged-head rerun for #17091 passed, establishing intermittent harness failure rather than a feature-branch regression.

Live latest-open sweep: checked the latest 20 open issues plus the 30 most recent all-state A2A messages at 2026-08-14T06:13Z; no equivalent ticket or in-flight claim exists. Exact and historical searches for bridgeAutoConnectOrdering, EPIPE, and closed-child stdin also found no issue, PR, Discussion, or mirrored artifact.

The Problem

callHealthcheck(child) accumulates stdout in one buffer and re-splits the complete accumulated value after every data event. Previously processed JSON-RPC frames therefore remain in the buffer and can be processed again on later chunks. An earlier initialize response can trigger duplicate notifications/initialized and tools/call writes.

Those writes go directly to child.stdin.write() without observing child exit, stream closure, or asynchronous write failure. If the spawned server closes between stdout frames, a replayed write emits EPIPE on stdin after the test helper has lost authority to write. Because no stream error handler owns that failure, it escapes Playwright as an out-of-test process error and turns otherwise valid PRs red.

The Architectural Reality

The affected helper lives inside the right-hemisphere unit witness at test/playwright/unit/ai/services/neural-link/bridgeAutoConnectOrdering.spec.mjs:40-86. It intentionally drives the real stdio MCP transport so the production lifetime/health contract remains observable; replacing it with an in-process mock would make the original #16429 witness vacuous.

The correct boundary is therefore the test transport client itself:

  • Frame stdout incrementally and consume each complete JSON line exactly once.
  • Treat child exit, child error, stdin closure, and stdin write failure as a failed health handshake that resolves null.
  • Remove listeners and the timeout on settlement so a completed call cannot retain stale transport authority.
  • Keep the existing production process and Neural Link implementation unchanged.

Structure-map receipt: ai/services/neural-link remains the production owner; the repair stays in its existing canonical unit-test sibling and introduces no file or service boundary.

Decision Record impact: none.

The Fix

Harden callHealthcheck() in the existing spec:

  1. Consume completed stdout lines and retain only the trailing incomplete fragment.
  2. Route every JSON-RPC write through one child-lifecycle-aware helper.
  3. Settle the handshake once on response, timeout, exit, child error, stdin close, or write error.
  4. Add a deterministic regression that closes the writable side during the initialize handshake and proves the helper returns null without an uncaught EPIPE or duplicate protocol writes.

No public/runtime contract changes, so a Contract Ledger is not applicable.

Acceptance Criteria

  • The deterministic closed-stdin regression fails against the current helper and passes after the repair.
  • Each complete stdout JSON-RPC frame is processed at most once across multiple chunks.
  • Child exit/error and stdin close/write failure settle the helper without an unhandled process error.
  • The existing real-process #16429 survivability and health-attribution witness remains behaviorally intact.
  • The focused spec passes 50 consecutive iterations.
  • No production file changes.

Out of Scope

  • The separate MemoryCoreRecorder identity-projection flake tracked by #17043.
  • Changing Neural Link production auto-connect behavior.
  • Weakening the real stdio process-lifetime witness into an in-process mock.
  • Broad MCP client abstraction work.

Avoided Traps

  • Rerun-only containment: useful for immediate PR recovery, but it preserves a suite-wide red generator.
  • Swallow every EPIPE globally: hides real child death and leaves duplicate frame dispatch intact.
  • Delete the real-child witness: makes the original server-survivability contract unobservable.

Related

Related: #17043

Origin Session ID: 019ffcf3-1a96-7020-b1fc-e1673092fcca

Retrieval Hint: bridgeAutoConnectOrdering EPIPE child stdin repeated initialize frame

tobiu referenced in commit 27a8ebf - "fix(test): make Neural Link stdio witness exit-safe (#17094) (#17096)" on Aug 14, 2026, 9:19 AM
tobiu closed this issue on Aug 14, 2026, 9:19 AM