Repository navigation
[agent-cli] The OTA verdict keeps expo config on every run, and skips it for a project with no app config - #70
Merged
Conversation
expo-tuft Bot
added a commit
that referenced
this pull request
Sep 15, 2026
…list, and add the CHANGELOG entry for #70 Co-authored-by: kudo <46429+kudo@users.noreply.github.com>
Kudo
added this pull request to stack #78
September 16, 2026 06:07
expo-tuft Bot
added a commit
that referenced
this pull request
Sep 16, 2026
…list, and add the CHANGELOG entry for #70 Co-authored-by: kudo <46429+kudo@users.noreply.github.com>
expo-tuft
Bot
force-pushed
the
kudo/status-2-ota-cache
branch
from
September 16, 2026 19:25
23b789c to
c138c61
Compare
expo-tuft Bot
added a commit
that referenced
this pull request
Sep 24, 2026
…list, and add the CHANGELOG entry for #70 Co-authored-by: kudo <46429+kudo@users.noreply.github.com>
expo-tuft
Bot
force-pushed
the
kudo/status-2-ota-cache
branch
from
September 24, 2026 14:28
eb1317c to
5076ba9
Compare
expo-tuft
Bot
force-pushed
the
kudo/status-2-ota-cache
branch
2 times, most recently
from
October 15, 2026 15:03
bde1c76 to
d77a632
Compare
Kudo
reviewed
Oct 15, 2026
expo-tuft
Bot
force-pushed
the
kudo/status-2-ota-cache
branch
2 times, most recently
from
October 15, 2026 19:34
75b7e6a to
0cd6a51
Compare
vonovak
requested changes
Oct 16, 2026
vonovak
left a comment
Contributor
There was a problem hiding this comment.
as discussed on slack we might take another approach first but posting anyway :)
expo-tuft
Bot
force-pushed
the
kudo/status-2-ota-cache
branch
from
October 16, 2026 10:29
0cd6a51 to
b796b20
Compare
…ict, and remembers the evaluation of a dynamic one The OTA verdict of `status --explain` spawned `expo config --json --type public` on every run, to be told what `app.json` says. For a static config that file *is* the public config, so the spawn — about a second — learned nothing. It is now one file read (`readStaticExpoConfigAsync`). A dynamic `app.config.js`/`.ts` still has to be evaluated, by the project's own Expo CLI (llp/0001 §Constraints item 5). The answer is now remembered in `.expo/agent-cli-app-config.json` (`src/project/evaluatedAppConfig.ts`), revalidated against the same size-and-mtime manifest the fingerprint record uses, under the same ten-minute expiry — the gaps are the same: a dynamic config can read `process.env` or the date, and no stamp sees that. The report says when the answer came out of the record (`runtimeVersion.cache`; `cached 2m ago` in the text), and `--no-fingerprint-cache` refuses it, the way it refuses the fingerprint record and the remembered EAS answer. `parseLastJsonObject` moves beside the subprocess it reads; `runtimeVersion.ts` re-exports it for its callers. Co-authored-by: kudo <46429+kudo@users.noreply.github.com>
…list, and add the CHANGELOG entry for #70 Co-authored-by: kudo <46429+kudo@users.noreply.github.com>
Co-authored-by: kudo <46429+kudo@users.noreply.github.com>
A plain package.json reaches the OTA verdict through status, and evaluating it meant npx expo fetching a package to answer a question nothing asked — a 300 s hang on the Windows tier0 shard. No app config is no runtimeVersion. Co-authored-by: kudo <46429+kudo@users.noreply.github.com>
Co-authored-by: kudo <46429+kudo@users.noreply.github.com>
…n it parses, and pin Expo's file order Co-authored-by: kudo <46429+kudo@users.noreply.github.com>
…ig does not check the environment, and drop the unused clear Co-authored-by: kudo <46429+kudo@users.noreply.github.com>
…rop the app config cache A dynamic config can read env vars and .env files, which the file stamps do not see. A static app.json can list config plugins that set runtimeVersion.
Kudo
force-pushed
the
kudo/status-2-ota-cache
branch
from
October 18, 2026 18:11
b796b20 to
f11b3dd
Compare
vonovak
reviewed
Oct 19, 2026
| if (!staticConfig.source || !staticConfig.config) { | ||
| return { policy: null, literal: null, source: null }; | ||
| } | ||
| return readRuntimeVersion(staticConfig.config.runtimeVersion, staticConfig.source); |
Contributor
There was a problem hiding this comment.
[💡 suggestion] When expo config fails, this falls back to runtimeVersion from the static file.
Could a failed evaluation return source: null, so the verdict is unknown?
Collaborator
There was a problem hiding this comment.
Agreed, done in 5eb90b0. A failed expo config now gives source: null, so the verdict is unknown. The static file is not a fallback any more, for the same reason we stopped reading it directly: its plugins or a dynamic config can change the runtimeVersion the app sees. The e2e for the failed subprocess on #71 now expects source: null (5c5340b).
…tead of reading the static file Its plugins, or a dynamic config, can change the runtimeVersion the app sees.
Kudo
added a commit
that referenced
this pull request
Oct 19, 2026
…t changed, the OTA verdict, and the EAS answer (#71) Stack 3/4 of the plan to remove `status --explain`. Based on #70 (which is based on #69). ## Why `--explain` gated three things — the per-source change list, the OTA verdict, the EAS build lookup — because two of them cost a second or more. "That's not friendly to use." #69 and #70 made those costs rememberable, so the flag has nothing left to gate. ## How - `--explain` is removed with no alias (an unknown option, exit 1), the way `--cloud` was in #34. - Every run carries `changedSources`, `ota`, and the EAS lookup. `--build <id>` no longer needs `--explain`. - The two per-platform fingerprints of a linked project start at the top of `collectStatusReportAsync`, beside the probe's own, and the lookup joins them through the in-process memo (LLP 0023 Layer 1). A cold run of a linked project costs the wall clock one `fingerprint:generate` instead of two. An unlinked project starts none. - `BuildsStatus.askedEas` now means "this run called EAS for at least one platform" (false when every platform was answered from the record or gated: signed out, not linked, no fingerprint). Same key, same type; the `cli:status` event's `easBuildsAsked` follows. - The `eas build` line prints whenever EAS has answered (`found` or `none`) or was called; it stays out when every platform was gated, because the freshness rows carry that reason once. - The change list prints platforms with identical source lists once: `changed ios, android: 3 sources`. - `--no-fingerprint-cache` help text now says what it refuses: fingerprints, the evaluated app config, the remembered EAS answer. - Prose that named `status --explain`: help, `AGENTS.md` content, workflow help, the `--assert` refusal, the `change-ota-unsafe` follow-up, `.expo/README.md`, code comments. LLP 0004 §Status, 0011, 0016, 0022, 0023, 0027 updated. ## Test Plan - Unit: `statusAsync-test` "what every run carries" (classification, OTA + EAS on every run, refused cache threaded to both, section isolation); `easBuilds-test` without `lookUp` (asks both platforms, answers from the record per platform, `askedEas` false when nothing was asked, asks again over a stale key, remembers nothing without a project hash); `resolveOptions-test` for `--build` alone; `format-test` / `sections-test` strings. - E2E `status-test.ts`: the former `--explain` cases run as plain runs; `--explain` exits 1; `--build build-1` alone is taken and echoed; a linked plain run asks EAS twice and prints `eas build … ios: none`; an unlinked plain run prints no `eas build` line and names `eas init`; the impact headline carries `changedSources`; three fingerprints on a linked run, one on an unlinked one. - `help-test`, `eas-runner-test`, `eas-shim-test`, `e2e-live/live-eas-test` updated for the missing flag. - Ran: `bun run typecheck`, `bun run lint`, `bun run test` (4228 pass), `bun run test:e2e -- status-test eas-runner-test eas-shim-test help-test` (all pass). --------- Co-authored-by: expo-tuft[bot] <288127324+expo-tuft[bot]@users.noreply.github.com> Co-authored-by: kudo <46429+kudo@users.noreply.github.com> Co-authored-by: Kudo <kudo@expo.dev>
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.
Stack 2/4 of the plan to remove
status --explain. Based on #69.Why
The OTA verdict runs
expo config --json --type publicto learn theruntimeVersion. Once--explaingoes away (#71), everystatusrun pays for that call.We tried reading a static
app.jsonas a file and caching a dynamic config's answer. Review showed that this is not safe:.envfiles, and no file stamp sees them. A cache can report an oldruntimeVersion.app.jsoncan list config plugins, andexpo config --type publicruns them. A plugin can setruntimeVersion, so the file alone is not the answer.So this PR keeps
expo configas the source on every run, with no cache. An env-aware cache can come later in its own PR.How
resolveRuntimeVersionAsyncstill runsexpo config --json --type publicfirst, for static and dynamic configs.npx expocould fetch a package for it. It reportssource: null.source: nulland the verdict isunknown. The static file is not a fallback, for the same reasons as above.--no-fingerprint-cachealso refuses the EAS answer record.Test Plan
src/impact/__tests__/runtimeVersion-test.ts:expo configanswers for static and dynamic configs with the pinned argv; a staticapp.jsonis evaluated too; no app config file spawns nothing;source: nullwhen the subprocess fails (even beside a staticruntimeVersion), prints something unparsable, or fails on a dynamic config.bun run test(all pass),bun run test:e2e -- status-test(96 pass).🤖 Generated with Claude Code