fix(environment-provider): refuse a command result that carries no exit status - #241
Merged
Conversation
tangletools
approved these changes
Aug 21, 2026
tangletools
left a comment
There was a problem hiding this comment.
✅ Auto-approved drewstone PR — aa9e798e
This PR was opened by the trusted drewstone account.
This approval is provisional and was applied by the local stand-in because the pr-reviewer webhook host is unreachable (2026-08-21). CI on this head is fully green. The full PR reviewer audit re-runs via the resweep when the service returns and will publish findings if it detects issues.
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.
Plan
Problem -
execResultFromUnknownread an absent exit status as0. Exit zero is the one value that means a command succeeded, so a sandbox SDK answering with captured output and noexitCodeorcodeproducedexec()success for a command whose outcome was never measured, and the turn over it finishedstatus: "completed".Change - a result with no finite exit status is refused, naming the SDK call whose answer could not be read.
Why long-term right - it is the house rule these repos already hold: no fallbacks, fail loud, missing data stays unknown rather than becoming a default. Round 1 collapsed three copies of this reader into one owner, so the defect that needed three fixes now needs one, and no adapter can drift back to it.
Cost - 7 files.
execResultFromUnknowngains a requiredsourceargument; it is unreleased (npm servesagent-interface@1.4.0; it ships first in the pending1.5.0), so no consumer can be holding the old signature. Rollback is a revert.Proof
The bug, in a real adapter flow. An E2B sandbox whose
commands.runreports a failed build and no exit status, run throughprovider.create()thenenvironment.stream():A failing build read as a build that worked. That is the whole defect.
The round-1 test caught the change before I did.
execResultFromUnknownhad a test asserting the silent zero (expect(...).toEqual({ exitCode: 0, ... })). It went red on the first run of this change, which is what that test was for; it is now rewritten to assert the refusal.pnpm buildpnpm check-typespnpm testorigin/main)pnpm check:package-artifactspnpm check:control-artifactsAll three adapters' conformance runs (
runAgentEnvironmentProviderConformance) pass unchanged - their SDKs do return an exit status, which is why this stayed invisible.Simplification
Simplification: none - this is a behavior fix on an owner that round 1 had already collapsed. The three adapters each pass the name of their own SDK call, so the refusal names the fix site rather than the shared reader.
Net: +63 / -19 lines, 7 files, 0 copies removed (the copies were removed in #234; this is the one-line fix that collapse made possible).
Not done here:
commandTurnEventsstill readsexitCode: result?.exitCode ?? 1andstdout ?? ""for the case where an adapter exposes noexecat all. That path is a different question - an environment that cannot run commands should arguably refuse the turn at the top rather than synthesize a failed result - and it needs its own decision about what a workspace-only adapter withoutexecis for.Tests: +2 net, 1 rewritten (that a result with no exit status is refused, covering nothing at all, output-without-status, a non-numeric status, and a non-finite status - the failure mode is a failed command reported as a completed turn; that a zero exit status is still accepted, because zero is a measured success and must not be caught by the same check; the wrong-type test kept, narrowed to captured output only), -0 deleted.