Repository navigation
[agent-cli] one reading of a failed eas run, starting with the EAS session start and stop - #126
Conversation
a832502 to
b986f33
Compare
b986f33 to
68f889e
Compare
…ssion start and stop (llp/0021 rules 11 and 14) On a cold scratch directory bunx writes its install progress to stderr before the EAS CLI writes there. A session start that failed this way reported `Resolving dependencies` as what EAS said. `readEasFailure` in src/utils/easFailure.ts reads a failed eas run once: a recognised EAS sentence first, then the first line that is not the runner's, and "the runner did not deliver the CLI" only when the runner printed everything. `easFailureReason` puts that in one reason line. The session start and stop use it. The other callers move to it in the next PR. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
68f889e to
16f6e39
Compare
…1 rule 11) `npx --yes eas-cli@latest` on an empty npm cache writes `npm warn deprecated …` lines before the EAS CLI runs and `npm notice …` lines after it [observed — npm 10.9.4, 2026-10-07]. The runner list matched only `npm warn exec`, so a failed run quoted a deprecation warning as what EAS said. The list now matches every `npm warn` and `npm notice` line. `npm error` stays out: that is the runner failing. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
| } | ||
| const line = | ||
| firstLine(withoutRunnerNoise(stderr), EAS_COMMAND_FAILED) ?? | ||
| firstLine(stdout) ?? |
There was a problem hiding this comment.
[bug] A failed simulator start used to quote the first stderr line. With no runner noise that line was Error: simulator command failed. It now skips that closing line and quotes the first stdout line. spawnCaptureAsync uses stdio: ['ignore', ...], so the child stdin is not a TTY. eas-cli's ora() then disables the spinner and writes it to stdout (packages/eas-cli/src/ora.ts, ora 5.1.0). start() prints - 🚀 Creating simulator session before succeed() prints Simulator session created, and the timeout sentence is a later stdout line (Log.error in EasCommand.catch, after pollSpinner.fail). The reason becomes exited 1: - 🚀 Creating simulator session. The timeout is dropped. Stop does the same with - 🛑 Stopping simulator session …. The new test puts the timeout on stderr, so it stays green. A captured run does not.
Suggestion: Skip the disabled-spinner lines (they start with - ) and the Simulator session created line, on stdout as well as stderr. The next line is the failure (✖ Timed out…, then the Timed out after … sentence). Add a fixture with that stdout/stderr split.
| | { kind: 'nothing' }; | ||
|
|
||
| export function readEasFailure({ stdout, stderr }: { stdout: string; stderr: string }): EasSaid { | ||
| const cause = classifyEasFailure(`${stdout}\n${stderr}`); |
There was a problem hiding this comment.
[bug] Session start and stop now classify the whole capture before they pick a line. A GraphQL UNAUTHORIZED_ERROR is not "signed out". EasCommand.catch logs You don't have the required permissions… Run eas whoami… Run eas login to change the account. That text contains eas login, so the eas-login signature matches and the reason says this machine is not signed in. Before, the same failure quoted stderr's Error: GraphQL request failed. and did not claim the account was missing. The real signed-out sentence (log in with "eas login" plus EXPO_TOKEN) still needs to match.
Suggestion: Do not treat every eas login as signed out. Keep the signed-out wording (not logged in, must be logged in, or eas login next to EXPO_TOKEN). Leave the permissions sentence to be quoted as a line.
| * `Error: build:list command failed.` names no cause. The EAS CLI prints it on stderr after it put the | ||
| * explanation on stdout [observed — live against an unlinked project, 2026-08-26]. | ||
| */ | ||
| const EAS_COMMAND_FAILED = /^Error: \S+ command failed\.?$/; |
There was a problem hiding this comment.
[bug] The skip only matches Error: <word> command failed. EasCommand.catch uses a second closing line for every GraphQL failure: it console.logs the explanation, then throws GraphQL request failed., which oclif prints as Error: GraphQL request failed. The unlinked build:list case works because its closing line matches the regex. A simulator failure in getContextAsync happens before any spinner, so stderr is only that GraphQL line. The reader quotes Error: GraphQL request failed. and drops the stdout sentence. That is the same bug the skip was added to fix.
Suggestion: Skip Error: GraphQL request failed. the same way, then read stdout. Pin it with a captured split, explanation on stdout and that one line on stderr.
| const said = readEasFailure(result); | ||
| if (said.kind === 'runner-only') { | ||
| return runnerNoiseReason( | ||
| { tool: 'eas', exitCode: result.exitCode }, |
There was a problem hiding this comment.
[suggestion] The other branch says exited on a signal when exitCode is null. The runner-only branch passes result.exitCode through, so a killed bunx (session deadline, stderr still Resolving dependencies) says it exited null. null !== 0, so session start and stop both reach this.
Suggestion: Use the same on a signal wording when there is no exit code.
…eadEasFailure (#127) Stacked on #126. ## What was wrong `looksLikeRunnerNoise` read only the first stderr line. On a cold scratch directory, bunx prints its progress first and the EAS CLI writes its error after it. The guard then said that the runner printed everything, and the EAS line was lost. Two independent reviews of #126 found this. The other callers had their own versions of the same reading: - The build lookup of `status --explain` (`src/impact/buildCache.ts`) used the guard. - The fingerprint compare (`src/impact/compare.ts`) used the guard, and otherwise quoted the tail of stderr (rule 14). - The session listing (`src/device/cloudSimulator.ts`) quoted the first stderr line. - The cloud reload (`src/runtime/reload/cloudReload.ts`) quoted the first stderr line. ## What changes All four use `readEasFailure` from #126. `looksLikeRunnerNoise` and `runnerNoiseLine` are deleted. Their test cases move to the `readEasFailure` tests. `runnerNoiseReason` now says "exited on a signal" when there is no exit code. The reload and the listing also get the recognised-sentence answers (`classifyEasFailure`), the same as the build lookup. ## Tests - `src/impact/__tests__/buildCache-test.ts` "quotes the CLI's line after the runner's progress". It fails on #126 alone. - Local: typecheck, lint, `bun run test`, and the e2e files `cloud-simulator-test dev-eas-test eas-preflight-test eas-runner-test eas-shim-test runtime-reload-test status-installed-test status-test` (209 passed). 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
What was wrong
An EAS Simulator session start through
bunx eas-cli@latestexited 1, and the report quotedResolving dependenciesas what EAS said [observed live, 2026-10-05]. On a cold scratch directory, bunx writes its install progress to stderr before the EAS CLI writes there. So the first line of stderr is the runner's line. This breaks llp/0021 rule 11 ("The runner is not the service").Each caller that quotes a failed
easrun reads its output in a different way. Some read the first stderr line, one reads stdout first, and one quotes the tail. The first-linelooksLikeRunnerNoiseguard has the same problem: when EAS writes after the runner, the guard says that the runner printed everything.What changes
readEasFailureinsrc/utils/easFailure.tsreads a failedeasrun in one way:classifyEasFailure), with its fix.Error: <command> command failed.line.runnerNoiseReason).easFailureReasonwrites that as one reason line. In this PR, the session start and the session stop use it. #127, stacked on this PR, moves the other callers and deleteslooksLikeRunnerNoise.The order uses the two observations. The session start error was on stderr. The
build:listexplanation for an unlinked project was on stdout, with only the generic closing line on stderr.The second commit covers
npx. On an empty npm cache,npx --yes eas-cli@latestwritesnpm warn deprecated …lines before the EAS CLI runs andnpm notice …lines after it [observed — npm 10.9.4, 2026-10-07]. The runner list matched onlynpm warn exec, so a deprecation warning was quoted as what EAS said. The list now matches everynpm warnandnpm noticeline.npm errorstays out, because that is the runner failing.This PR replaces the error-output part of #112. #112 quoted the last 20 lines of stderr, and llp/0021 rule 14 ("A message is not a tail") prohibits that.
Tests
src/utils/__tests__/easFailure-test.ts: each order rule, a recognised sentence after the runner, and the reason text.src/dev/__tests__/openAppEas-test.ts: runner then error, runner only, and runner then a created session. The first and third cases fail on the earlier head of this PR.bun run format:check,bun run test.🤖 Generated with Claude Code