| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
fix: release completed stateless HTTP children | 8 天前 | |
fix: clean up owned stdio process groups on termination | 8 天前 | |
test: two properties over gaps a real parse found Ran a scope analysis over src/ (TypeScript checker, not grep — grep missed the bindings that mattered twice before) to get the actual state surface, then asked which parts of it no test exercises. 1. stdout framing. Every gateway accumulates the child's stdout and splits on newlines; the same four lines appear in all four. They exist because a write is not a message — the pipe splits a large result. Every peer in this suite writes one whole line per call, so that branch never ran with a partial message in it. The lines were covered; the behaviour was not. The new peer replies in generated pieces. Verified load-bearing: change `buffer = lines.pop() ?? ''` to `buffer = ''` in the compiled gateway and the property fails, shrinking to [[[1]]] — one reply, one byte at a time. 2. id round-tripping. GW-018 is a *type* bug: parseInt applied to an id that was a string. No example test with a numeric id can see it, which is why it sat there. The generator is the whole point, so the shapes that break naive handling are in it explicitly — a colon, digits-as-a-string, empty, zero. SSE passes and is now pinned, which matters because the GW-017 fix is expected to add per-client routing here and routing by id is the tempting way — it is what WebSocket does, and what GW-018 is. Verified by injecting that coercion into the compiled SSE gateway: the property fails, shrinking to [[""]]. Harness gains an env argument and dispose(), so a property can launch a gateway per generated case and tear it down instead of leaving a dozen running. | 9 天前 | |
test: audit MC/DC reachability and cover startup edge cases | 18 天前 | |
fix: report HTTP child exits as protocol errors | 8 天前 | |
test: run the portable suite on every supported Node, and on macOS Node 18's three failures were harness, not gateway: they fail inside MockTimers.enable before any product code runs, because Node 20.4 changed `enable(['setTimeout'])` to `enable({ apis: [...] })` and 18 rejects the object form with ERR_INVALID_ARG_TYPE. A helper tries the modern signature and falls back, which takes the mock-free subset on Node 18 from 84 passed / 3 failed to 87 passed / 0 failed — identical to 20, 22 and 24. What remains version-specific is the runner's module mocking. Neither the deprecated `namedExports` form nor the modern `exports` form works on 20 or 22, so those twenty files cannot run below 24 for reasons unrelated to this project. The other thirty-five can, and they include every end-to-end test that spawns the compiled CLI — the part that catches platform differences, as GW-015 showed. So `test:compat` runs that subset, selected by grep rather than a hand-kept list so a new mocking test excludes itself, and a new mocking helper surfaces as a failure instead of silently narrowing what is checked. A compat job runs it across 18, 20 and 22 on both ubuntu and macOS: six combinations that previously had no coverage at all, all green. The full suite still runs on 24, and the Alpine smoke job still covers musl. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> | 9 天前 | |
test: reproduce child I/O and slow-reader failures | 8 天前 | |
fix: clean up owned stdio process groups on termination | 8 天前 | |
test: reproduce child I/O and slow-reader failures | 8 天前 | |
test: two more that had no coverage — stdout volume and credentials GW-028: all four gateways read the child with an uncapped `buffer += chunk` that only drains on a newline. A peer emitting 512 MB with no newline in it pushed resident memory to about 2 GB, and with the heap capped every gateway died the same way — SIGABRT, heap out of memory. No malice needed: a large stack trace or a base64 blob on stdout does it, and an abort is not something the gateway can report. GW-029: --oauth2Bearer puts the token verbatim in the startup log at the default level, which in a container means the platform's log store. The same line is GW-006 in the other three gateways, which write `Object(headers).length` — always undefined, so they report configured headers as absent. One missing `.keys` causes both, in opposite directions. surface-peer.mjs backs a probe that found nothing: resources, prompts and resource-update notifications relay correctly on all four transports, WebSocket included. | 8 天前 | |
fix: refuse a descendant walk that cannot date an exited Windows root Run 35309815902 cleared the canary for the first time, then lost the first lane in 40 seconds: native process enumeration detects nine real descendants and their removal + [3628, 1756, 7336, 7404] - [] Those four were not the fixture's. It kills its nine children and awaits every one of their exits before exiting itself, and the test had already asserted the parent exited 0. They were stale claimants of its recycled PID — the same defect #207 fixed for leak-check, at a call site #207 did not touch, which is independent confirmation of that diagnosis. Passing `since` there fixes this instance. The general problem is that omitting it is silent: descendantsOf answers with whoever holds the PID now and nothing says the answer was unverifiable. This file already refuses an unreadable table and missing creation times rather than guessing, so refuse this too — but only when something actually claims the absent root, since a PID with no claimants has no descendants on any reading and erroring there would break an ordinary empty query. Every remaining caller now supplies it: the resource sampler and its shutdown path in soak-release, and the session-pairing property test. launchGateway exposes the spawn time it already records. The pinned "an exited ancestor can still have real living children" case keeps its meaning and moves to real tick values, because `since` is a wall-clock time and has to be comparable to what Win32_Process reports. | 6 天前 | |
feat: bridge modern HTTP requests while retaining Node 18 support | 7 天前 | |
test: cover initialization failures and document constrained branches | 18 天前 | |
test: defer gateway fixes and record known regressions as TODOs | 18 天前 | |
fix: stop attributing a recycled Windows PID's process tree to a dead gateway Soak run 35305652456 died in the canary preflight, before any cycle: Processes accumulated beyond the budget of 8: gateway --stdio node .../signed-continuation-peer.mjs (pid 736) has 128 live descendants The gateway never spawned 128 of anything. The test's own assertions had all passed first, including that every continuation child was dead and that no expired handle started a new process; the throw came from the leak check afterwards. continuation-lifetime shuts its gateway down inside the test body, so by the time the leak check runs the root has exited and is absent from the process table. descendantsOf only dated an edge when the parent was in that table, so an absent root disabled the check entirely — and Windows keeps ParentProcessId after a parent dies and recycles low PIDs hard, so every stale claimant of 736 was counted, subtrees included. A table of 128 such claimants reproduces it exactly; the walk returns 133. Returning nothing for an exited root would have been wrong: an exited ancestor can still have real living children, and a test already pins that. The evidence that outlives the root is when we spawned it, which the harness already records, so pass it through and date the root's edges against that instead. Real orphans still count; claimants that predate the gateway no longer do. A two-second tolerance absorbs the difference between Date.now() and CIM CreationDate, erring toward counting so a real leak is never hidden by a rounding difference. Unset, the behaviour is exactly as before, so nothing that does not know its own spawn time changes. | 6 天前 | |
test: name the step that hangs in the lifecycle tests The previous commit guessed wrong. It gave the wait on the control server a deadline, and CI still reported only "test timed out after 15000ms" with no rejection from that deadline — proving the hang is somewhere else, and that the idle-timeout theory behind the 300ms to 1000ms change was not the cause. That timing change is reverted: it rested on a disproven hypothesis and added about two seconds to tests already exceeding their budget. Reading both tests for waits that cannot expire leaves one in common, `once(held, 'close')` after the session ends. Everything else is bounded: rpc and the raw fetches carry AbortSignal timeouts, waitFor has its own deadline. Rather than guess a second time, a `within` helper names each remaining unbounded wait, and the two tests get 30s so a 10s named deadline fires and reports instead of being cut off. The next run should say which step hangs. The deadline on the control server stays. It rules out one cause for good, and an unbounded wait there was worth removing on its own. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> | 9 天前 | |
test: commit the component and lifecycle suites left untracked These files predate this session and were never committed, although the coverage numbers recorded in MCDC_E2E_PROGRESS.md and MCDC_REACHABILITY.md were all measured with them in place. They are the component-level half of the suite: an observed-gateway harness that holds express, the MCP SDK transports, child processes and signals at controlled boundaries while the real routing, parsing, callbacks and session state run, plus the SSE, HTTP, WebSocket, bridge, configuration and session-counter tests built on it. Committing them so the recorded results and the checked-in tests agree. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> | 10 天前 | |
test: exercise disconnect child-exit and timeout lifecycles | 18 天前 | |
Remove StreamableHttp aliases and improve tests | 1 年前 | |
test: prepare manual RC.1 soak and continuation lifetime checks | 6 天前 | |
feat: bridge modern HTTP requests while retaining Node 18 support | 7 天前 | |
test: reproduce child I/O and slow-reader failures | 8 天前 | |
fix: preserve bridge results and stateless batches with regression coverage | 18 天前 | |
fix: route a stateful child's notifications through the request in flight Soak run 35410255256 lost a progress notification on macOS: the caller saw [2, 3] where the tool had emitted 1, 2 and 3, 150ms apart. Not the batching defect GW-027 documents in that file. That one loses the notifications batched *with the result* — the later ones — and the test that failed spaces them out precisely to avoid it. This lost the first and kept the rest, which is the opposite shape and a different cause. `StreamableHTTPServerTransport.send` routes anything without a `relatedRequestId` to the standalone GET stream, and when that stream is not connected it returns without sending: const standaloneSse = this._streamMapping.get(this._standaloneSseStreamId) if (standaloneSse === undefined) { // Stream is disconnected - event is stored for replay, nothing more to do return } Nothing throws, so the gateway's `.catch` never sees it and the notification is simply gone. A client opens that GET stream asynchronously after initialize, so the window where it is missing is exactly the start of a call — where a tool emits its first progress notification. The later two arrived 150ms and 300ms on and found the stream up. The stateful bridge already tracked its in-flight requests, for the replies it sends on shutdown. It just never routed by them. The stateless bridge has always passed `relatedRequestId` from the same set, and so does the modern HTTP path; this was the one send that did not. Taking the oldest pending request is a heuristic when several are in flight, and it is the one the stateless bridge already uses. It is also harmless: a client demultiplexes progress by token, not by which stream carried it, so riding a sibling's stream still delivers. Only the standalone stream can swallow it. Lines 100%, branches 100%, MC/DC 100% (1358/1358, 646/646, 292/292). | 5 天前 | |
test: verify Windows descendants with native process enumeration | 6 天前 | |
test: retry a timed-out process query instead of believing it Soak run 35395011603 died at cycle 32, two hours in and otherwise healthy, with stateful: a supported protocol version in the header is accepted spawnSync powershell.exe ETIMEDOUT That test has nothing to do with process trees. It inherited the leak check's `afterEach`, which cold-starts PowerShell after every test on Windows to read the process table — roughly a thousand queries per lane across 32 cycles, any one of which can stall past the 30s budget on a loaded runner, and whichever test is in front of it takes the blame. A query that timed out observed nothing. That is not evidence about the process tree, so read once more before believing it. A second timeout still throws: an unreadable table must never pass as a clean one, which is what the surrounding code has always been careful about. Failures that are not timeouts are not retried, because a missing binary will not appear on a second try. This is mitigation, not a cure. The underlying cost is a PowerShell process per test on Windows; sharing one snapshot across the gateways watched in a single pass, or reading the table by some cheaper means, would remove the exposure rather than halve it. Harness only — no src change, so the published 4.0.0-rc.2 artifact the soak is measuring stays valid and needs no new candidate. Lines 100%, branches 100%, MC/DC 100% (1357/1357, 646/646, 292/292). | 5 天前 | |
style: prettier the two peers added in #180 | 8 天前 | |
test: verify session lifetimes and reproduce UTF-8 chunk corruption | 8 天前 | |
fix: clean up owned stdio process groups on termination | 8 天前 | |
fix: isolate continuation ownership and preserve explicit retries | 6 天前 | |
fix: release completed stateless HTTP children | 8 天前 | |
fix: handle server messages in stateless HTTP mode | 8 天前 | |
style: prettier the two peers added in #180 | 8 天前 | |
test: exercise transparent modern relay compatibility and HTTP gaps | 7 天前 | |
test: mark the malformed-envelope check as an expected upstream failure SDK 1.25.3 and 1.26.0 answer 200 to a malformed envelope they should reject with 400; 1.24.3 rejects it and 1.27.1 rejects it again. The regression is the SDK's and it fixed it itself. Neither obvious response was right. Teaching the test to expect 200 on those two releases would put someone else's regression in our suite permanently, and skipping it would drop the check on every version, including the ones where it is the only thing asserting that behaviour. So the check runs everywhere, and on the affected band it is declared an expected upstream failure. Unlike a skip or a TODO the expectation is enforced both ways: on an affected version the test must still fail, and if it starts passing the marker fails loudly, naming the SDK version and saying that either the dependency is fixed, the range is wrong, or the fault was never upstream. Verified by widening the band over a version where the test passes. Only for defects in a dependency. A test failing because this gateway is wrong belongs in knownBugTest, or gets fixed — GW-016 was the latter. Measured: 1.24.3 118 passing 0 failing with the marker inactive, 1.26.0 117/1 with it active, 1.27.1 117/1 with it inactive again. The single remaining failure from 1.25 upward is cluster C. | 9 天前 | |
test: verify session lifetimes and reproduce UTF-8 chunk corruption | 8 天前 | |
Reject reused Windows parent PIDs and stop concurrent soak commands on failure | 6 天前 | |
test: exercise upstream wire errors and record response preservation bugs | 18 天前 |
| 文件 | 最后提交记录 | 最后更新时间 |
|---|---|---|
| 8 天前 | ||
| 8 天前 | ||
| 9 天前 | ||
| 18 天前 | ||
| 8 天前 | ||
| 9 天前 | ||
| 8 天前 | ||
| 8 天前 | ||
| 8 天前 | ||
| 8 天前 | ||
| 6 天前 | ||
| 7 天前 | ||
| 18 天前 | ||
| 18 天前 | ||
| 6 天前 | ||
| 9 天前 | ||
| 10 天前 | ||
| 18 天前 | ||
| 1 年前 | ||
| 6 天前 | ||
| 7 天前 | ||
| 8 天前 | ||
| 18 天前 | ||
| 5 天前 | ||
| 6 天前 | ||
| 5 天前 | ||
| 8 天前 | ||
| 8 天前 | ||
| 8 天前 | ||
| 6 天前 | ||
| 8 天前 | ||
| 8 天前 | ||
| 8 天前 | ||
| 7 天前 | ||
| 9 天前 | ||
| 8 天前 | ||
| 6 天前 | ||
| 18 天前 |