Repository navigation
feat(fix): fetch the Agent SDK on the first fix, never fall back silently (UPT-108) - #237
Merged
Merged
Conversation
…UPT-108) `npx uptide fix` never installed @anthropic-ai/claude-agent-sdk, an optional peer, so the Anthropic default quietly ran the patch executor while the notice and docs described the SDK. - The SDK is an optionalDependency (pinned to the tested 0.3.296): npm, pnpm and yarn install it with uptide, binary included. It stays external and loads only through import(). - sdkUnavailable() says why the SDK cannot run: not installed, failed to load, or no Claude Code binary for the platform. A fallback is the first line of the output and of the PR body, with the reason and how to get the SDK executor. `--agent sdk` stops before any clone. - report.json records llm.executor (sdk or patch) and the fallback reason in llm.sdkMissing. - The privacy notice names the SDK only when it will run; the LLM line names the executor. - ADR 0016 records the new decision with measured install sizes; fix, privacy and architecture docs follow. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
…t (UPT-108) As an optional dependency the SDK made every `npx uptide` 323.6 MB, `check` included, which never uses it. The package goes back to 13.6 MB and an optional peer: - An SDK resolvable next to uptide wins (the Action's install path is unchanged). - Otherwise the first fix that would use it installs the pinned SDK_VERSION into <cache>/agent-sdk/<version> (npm install into a temporary directory, scripts off, secrets stripped, renamed into place), prints one line with its size, and loads it from there. Later runs reuse it. - If that install fails (offline, a proxy), the run falls back to the patch executor loudly, as before, with the reason and the remedy. - A package test pins SDK_VERSION to the CLI's devDependency and peer range. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
… throwing (UPT-108) Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Signed-off-by: Lucas Braum <lucasbraum@gmail.com>
Merged
lucasbraum
pushed a commit
that referenced
this pull request
Oct 18, 2026
This PR was opened by the [Changesets release](https://github.com/changesets/action) GitHub action. When you're ready to do a release, you can merge this and publish to npm yourself or [setup this action to publish automatically](https://github.com/changesets/action#with-publishing). If you're not ready to do a release yet, that's fine, whenever you add more changesets to main, this PR will be updated. # Releases ## uptide@0.16.0 ### Minor Changes - [#237](#237) [`717468a`](717468a) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `npx uptide fix` gets the Agent SDK executor. The package stays as small as before: `@anthropic-ai/claude-agent-sdk` is not installed with Uptide. An SDK installed next to Uptide is used as it is; otherwise the first `fix` that needs it installs the version Uptide is tested with into Uptide's cache (`~/.cache/uptide/agent-sdk/<version>`), says so in one line with its size, and later runs reuse it. When that install fails (offline, a proxy) or the SDK cannot run, `fix` runs the patch executor and says so on the first line of its output and at the top of the PR body, with the reason and how to get the SDK executor. `report.json` records `llm.executor` and, after a fallback, the reason in `llm.sdkMissing`. The privacy notice names the SDK only when the SDK will run, and the LLM line names the executor. `--agent sdk` without a usable SDK stops before any work instead of failing mid-run. - [#239](#239) [`e1e8c40`](e1e8c40) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `uptide fix` runs only with the Claude Agent SDK; if it can't, it doesn't run. Before the first download, an interactive terminal asks once: `uptide fix needs the Claude Agent SDK (about 300 MB, downloaded once to <cache>). Download now? [Y/n]` (default yes). With `--yes`, or without a terminal, it downloads and says so in one line. Once cached, it never asks again. Answering `n`, an SDK that can't be fetched or loaded (offline, a proxy, no binary for the platform), a missing `ANTHROPIC_API_KEY`, another provider, or `llm.enabled: false` / `executor: patch` in `uptide.yml` all stop `fix` with exit 2 before any clone or spending. The run prints one message with the reason and the fix, and `--json` returns it as `error.code: "SDK_UNAVAILABLE"`. There is no fallback to the patch executor anywhere: the Uptide Action stops the run with the same message. `--no-llm`, `--agent` and `--provider` are gone from help and docs. `llm.sdkMissing` is gone from `report.json`, and `uptide init` no longer writes `executor`. ### Patch Changes - [#236](#236) [`9b35b9d`](9b35b9d) Thanks [@lucasbraum](https://github.com/lucasbraum)! - A repository build that fails on the final head is no longer published as a ready PR. When it fails, `fix` builds the parent commit in the same clone to compare. If the parent fails too, the build line says "already failing before this upgrade" and nothing is blocked. If the parent passes, the upgrade broke the build: verification fails, and the PR opens only as a draft with "Build broken by this upgrade" at the top. If the parent can't be checked out, installed or built, verification still fails and the PR says "Build fails on this upgrade; the parent build could not be checked" with the reason, never that the parent passed. The GitHub Action opens these runs as draft PRs, the same way it opens agent-led runs. The head build also skips workspaces already built on that head, so a workspace whose output a consumer reads is no longer built twice. - [#233](#233) [`1ad8071`](1ad8071) Thanks [@lucasbraum](https://github.com/lucasbraum)! - The PR description's build check now gives the build's actual result: passed, failed with its first error, or not run and why. When a migration pack doesn't require a build, `fix` still runs the repository's build once on the final head, and `uptide verify` runs it again, and reports what happened. "Not required by the pack" now appears next to that result instead of replacing it. Before, the line read "build not required" even when the repository's build failed. A build the pack doesn't require still doesn't block verification or publishing. - [#238](#238) [`6f07d3f`](6f07d3f) Thanks [@lucasbraum](https://github.com/lucasbraum)! - An SDK session's edits are no longer thrown away because of one flaky test. Before the gate rejects a session on test failures, it runs the failing test files once more, alone, with their runner and the same isolation as the full run. A file that passes then is recorded as flaky (`llm.tasks[].flaky` in `report.json`, and a line in the PR body's review list) and the edits stay. A file that fails twice still rejects them, and a failure that names no test file (a build, a timeout) is never rerun. Final verification keeps its full gate: it runs the flaky files again, and if one fails there, verification fails and the PR body says so. `fix` also stops starting verification feedback sessions when what is left of `--max-cost` is below the mean cost of the run's sessions so far, so it no longer starts a session it can't afford; the PR's feedback line says when this happened. - [#234](#234) [`066deab`](066deab) Thanks [@lucasbraum](https://github.com/lucasbraum)! - `fix` on npm no longer loses a package the repo imports without declaring it when the ERESOLVE retry re-resolves the lockfile. If the retry drops such a package from the top of `node_modules`, it runs again keeping that package and the locked packages that require it at their old versions; if that conflicts, the first retry stands. Either way the summary and the PR body name the package under "Imported but not declared" and suggest adding it to package.json. `fix` never adds it for you. Co-authored-by: uptide-release[bot] <338861287+uptide-release[bot]@users.noreply.github.com>
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.
UPT-108.
npx uptide@0.15.1 fixnever installed@anthropic-ai/claude-agent-sdk(an optional peer), so the Anthropic default quietly ran the patch executor while the notice and docs described the SDK (pilot round 3).What changes
fixthat needs it, not shipped. The package stays at 13.6 MB and keeps the SDK as an optional peer. Lookup order:<cache>/agent-sdk/<version>, usingdefaultCacheDir(), soUPTIDE_CACHE_DIRandXDG_CACHE_HOMEapply.fixthat would use it runsnpm install --prefix <temp> --no-save --include=optional @anthropic-ai/claude-agent-sdk@0.3.296. That goes throughcommand(), so scripts are off and secrets are stripped from the env. The temp dir is renamed into place: the cache holds a complete install or nothing. One line reports it, with the size. Later runs reuse it.SDK_VERSIONis pinned by a package test to the CLI's devDependency and peer range.--agent sdkwithout a usable SDK stops before any clone instead of failing mid-run.report.jsongainsllm.executor(sdk|patch, set whenever assisted fixes are on).llm.sdkMissingis now the reason string, where it used to betrue.LLM:line names the executor.fix.md,privacy.mdandarchitecture.md, plus a new dated decision in ADR 0016 (the 2026-10-12 optional-peer decision is marked superseded). The changeset isminor.What the first lines look like:
The PR body of that run starts with the same note, after the title, as
> ⚠️ ….Sizes
node:22 on linux arm64, the packed CLI.
npm installof the tarball / plainnpx uptide checkfixthat needs the SDKdu: 310 MB)fixrunsI tried an
optionalDependenciesversion first (77bfe7c, then reverted). Every install grew to 323.6 MB and 110 packages,checkincluded: binary 250 MB, SDK 5.5 MB, its peers 36 MB. That's why it's fetched on demand.Smoke (clean node:22 container, packed tarball, fake key, the npm smoke fixture)
The zod sites there are all mechanical, so no model call is made.
npx -y file:<tgz> checkclaude-agent-sdk*directory on disk, noagent-sdkcachenpxfix, first runexecutor sdk;report.jsonhasexecutor: "sdk"; exit 0 in 13 snpxfix, second runexecutor sdk; exit 0 in 8 sexecutor sdk, nothing written underagent-sdk.npmrcsends@anthropic-aito127.0.0.1:9)executor patch;report.jsonhasexecutor: "patch"andsdkMissingwith the reason; PR body line 3 is the note; no partial cache left; exit 0UPTIDE_CACHE_DIR=/etc/passwd/cache)ENOTDIRin the reason, no crash (df41ef8). The run then exits 2 because Uptide's registry cache can't be written either. That is pre-existing and unrelated.An earlier round, on the optional-dependency build, also confirmed that npm, pnpm 10, yarn 1 and yarn 4
dlxresolve and run the SDK from where they install it.pnpm turbo run typecheck testforuptideand@uptide/corepasses.biome check .has only the pre-existing warning and info.Pilot rerun: Uptide arm only, packed tarball via plain
npx -y file:<tgz>Same harness as round 3,
claude-sonnet-5-5, $1 (--max-costdefault), human references from round 3. All three runs reportexecutor: sdk.npm audit(×2) anddependency-reviewfail, both environmentalTool surfaceanddocker³ The MongoMemoryServer flake:
private-admission.test.tstimed out at 5 s.@react-types/datepickerimport with@entur/datepicker's ownTimeValue. That was round 3's lockfile finding. The scorer counts those 2 hunks as "unrelated" only because the human left that file alone.tests fail: packages/dms-saas; the local check shows that same flaky test failing. The fourth stopped at the budget. Every edit was discarded, so the branch is only the version bump, the formatter and the lockfile. That gives 0/51 coverage and the full set of upstream type errors. The gate letting one known flaky test throw away correct-looking sessions is its own follow-up, not part of this PR.error_max_budget_usdafter crossing it.a2408063…). The executor path didn't change after that: same SDK 0.3.296, same binary, same session code. Only where the SDK is imported from changed, and the smoke above covers that.uptide-benchonly: calmcp#9, dms-saas#11, enki#8. The harness change (anUPTIDE_SPECoverride, plus executor read fromllm.executor) was applied to a copy ofbench/, not committed.For review
npm.cmdwithout a shell, which is untested. If it fails there, the run falls back loudly.UPTIDE_CACHE_DIRunder/procmakescheckandfixspin forever before any output.🤖 Generated with Claude Code