fix: embed altimate-core in standalone binary and rename to altimate (match npm primary)#820
fix: embed altimate-core in standalone binary and rename to altimate (match npm primary)#820mdesmet wants to merge 9 commits into
Conversation
|
Note Reviews pausedIt looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the Use the following commands to manage reviews:
Use the checkboxes below for quick actions:
📝 WalkthroughWalkthroughThe PR embeds a single target .node into each Bun binary, restricts builds to platforms with available prebuilds, renames artifacts from altimate-code-* to altimate-*, adds musl/Win-ARM gating in installer and runtime wrapper, updates publish/formula names, and changes smoke tests to run with NODE_PATH cleared and add a binary-content guard. ChangesStandalone NAPI Embedding and Binary Release
sequenceDiagram
participant BuildScript
participant StageDir
participant BunBuild
participant BinaryArchive
participant Publish
BuildScript->>StageDir: locate platform .node and write staged shim
StageDir->>BunBuild: register onResolve -> redirect `@altimateai/altimate-core` to shim
BunBuild->>BinaryArchive: produce single-binary altimate (embed .node)
BinaryArchive->>Publish: create altimate-<target> artifact and compute SHA256
Estimated code review effort🎯 4 (Complex) | ⏱️ ~45 minutes Poem
🚥 Pre-merge checks | ✅ 3 | ❌ 2❌ Failed checks (2 warnings)
✅ Passed checks (3 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
94b714b to
5947f93
Compare
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
…(match npm primary)
The curl install at the repo root produced a binary that crashed on first
run with `Cannot find module '@altimateai/altimate-core'`. Root cause:
script/build.ts marked altimate-core as `external` (NAPI native modules
can't live inside Bun's single-file bunfs), and the release archive
shipped only the raw Bun binary — no companion node_modules, no
NODE_PATH-aware wrapper. The CI smoke tests hid the bug by pre-setting
NODE_PATH against the developer checkout before invoking the binary.
The fix has two halves:
1. Self-contained binary (build-level fix)
- Drop `@altimateai/altimate-core` from requiredExternals.
- In the per-target build loop, stage a copy of the loader package and
replace its NAPI-RS dispatcher with a one-line shim:
module.exports = require('./altimate-core.<platform>.node')
Co-locate the target's `.node` file. Add a Bun.build onResolve plugin
that redirects `@altimateai/altimate-core` to the staged shim. Bun
statically sees a single require() and embeds that one .node into
bunfs. Result: ~192 MB self-contained binary, no companion files.
- Ensure every altimate-core NAPI prebuild is installed before the
target loop via `bun install --os=* --cpu=*`.
- Drop targets without an altimate-core prebuild from allTargets:
linux-*-musl and win32-arm64. release.yml's build matrix updated to
match. Re-add if/when altimate-core publishes those prebuilds.
- CI smoke tests now run the binary with NODE_PATH explicitly cleared
(`env -u NODE_PATH ...`). This turns the smoke test into a real
regression guard for the curl-install class of bug.
2. Rename the curl-install binary altimate-code → altimate
The npm package already exposes `altimate` as the primary bin and
`altimate-code` as the alias (see packages/opencode/package.json bin
block, and the postinstall banner). The curl install was the odd one
out, installing only `altimate-code`. Align it with the npm contract:
the standalone install puts `altimate` on $PATH. Drop the alias from
the curl-install path — users who want `altimate-code` get it from
npm or Homebrew.
- `install`: APP=altimate (was altimate-code). Cascades to archive
filename `altimate-<target>.{zip,tar.gz}`, INSTALL_DIR
`~/.altimate/bin`, `mv $tmp_dir/altimate $INSTALL_DIR`, and every
log line / tmp-basename / check_version reference. Final
getting-started block matches upstream opencode's shape
(cd <project> / altimate). Branding-only diff against
anomalyco/opencode's install is now 6 non-branding lines: the
file-marker line, 4 ASCII banner lines (removed), and one cosmetic
getting-started line.
- `script/build.ts`: archive step renames `@altimateai/altimate-code-<target>`
→ `altimate-<target>` and packs exactly one file (the renamed
`altimate` binary), not the sourcemaps and not the `altimate-code`
alias. The `altimate-code` copy stays inside the platform package
dir because the npm wrapper (`packages/opencode/bin/altimate`)
looks for `bin/altimate-code` to locate the platform binary —
that flow is unchanged.
- `script/publish.ts`: Homebrew + AUR formula URLs and the sha256sum
paths swap to the new `altimate-<target>` archive names. The brew
formula still installs the binary as `altimate` and creates the
`altimate-code` alias inside the Cellar — Homebrew users keep both
names.
- Tests (`branding.test.ts`, `brew-formula.test.ts`,
`smoke-test-binary.test.ts`) updated to match the new binary name
and archive prefix. Things that intentionally did NOT change:
GitHub repo URL `AltimateAI/altimate-code`, npm package name
`altimate-code`, the npm wrapper package's `bin` block.
Verification on darwin-arm64:
$ ls packages/opencode/dist/*.zip
altimate-darwin-arm64.zip # was altimate-code-darwin-arm64.zip
$ unzip -l packages/opencode/dist/altimate-darwin-arm64.zip
192306704 altimate # single file, no altimate-code
# Regression test: NODE_PATH unset, from /tmp
$ cd /tmp && env -u NODE_PATH .../bin/altimate --version
0.0.0-test
# E2E install via --binary
$ HOME=$SCRATCH bash ./install --binary .../bin/altimate
$ ls $SCRATCH/.altimate/bin
altimate
$ env -u NODE_PATH $SCRATCH/.altimate/bin/altimate --version
0.0.0-test
# Install diff vs upstream opencode — non-branding lines
$ diff -u <(curl -s https://raw.githubusercontent.com/anomalyco/opencode/dev/install) install \
| grep -E '^[-+]' | grep -vE 'opencode|altimate|anomalyco|AltimateAI|OpenCode|Altimate'
--- /tmp/upstream-install ...
-echo -e "${MUTED} ${NC} ▄ "
-echo -e "${MUTED}█▀▀█ █▀▀█ █▀▀█ █▀▀▄ ${NC}█▀▀▀ █▀▀█ █▀▀█ █▀▀█"
-echo -e "${MUTED}█░░█ █░░█ █▀▀▀ █░░█ ${NC}█░░░ █░░█ █░░█ █▀▀▀"
-echo -e "${MUTED}▀▀▀▀ █▀▀▀ ▀▀▀▀ ▀ ▀ ${NC}▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀"
+echo -e "${MUTED}To start:${NC}"
# → file marker + ASCII banner removal + cosmetic "To start:" line.
$ bun test --timeout 60000 test/branding/ test/install/
400 pass / 0 fail
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
5947f93 to
0e83773
Compare
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
Local verification on darwin-arm64Ran from a fresh clone of 1. BuildThe 183 MB size reflects the embedded 2. The regression — original failure modeNo 3. The exact user-reported failing commandPreviously this errored out at startup with the missing-module crash. Now it boots into the TUI. 4. End-to-end install via
|
There was a problem hiding this comment.
Claude Code Review
This repository is configured for manual code reviews. Comment @claude review to trigger a review and subscribe this PR to future pushes, or @claude review once for a one-time review.
Tip: disable this comment in your organization's Code Review settings.
|
@claude review |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
There was a problem hiding this comment.
🧹 Nitpick comments (1)
packages/opencode/script/build.ts (1)
249-277: ⚡ Quick winUse the installed loader version for
.bunpath resolution.
locatePlatformPackageDirconstructs.bunstore paths using the dependency spec string frompkg.dependencies. If that spec becomes a range or tag (e.g.^0.3.1), the lookup will fail even when the package is installed, since Bun stores packages by their resolved installed version, not the spec string.Proposed fix
-const altimateCoreVersion = pkg.dependencies["`@altimateai/altimate-core`"] +const altimateCoreVersion = JSON.parse( + fs.readFileSync(altimateCoreLoaderPkgJson, "utf8"), ).version as string🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/opencode/script/build.ts` around lines 249 - 277, The code currently uses the dependency spec string (altimateCoreVersion from pkg.dependencies) when composing Bun store paths, which fails for ranges/tags; instead read the actual installed version from the on-disk loader package and use that resolved version when building candidates. In locatePlatformPackageDir, derive the installed version by reading/parsing the loader's package.json (located via altimateCoreLoaderDir, e.g. fs.readFileSync(path.join(altimateCoreLoaderDir, "package.json")) and JSON.parse(...).version) and replace uses of altimateCoreVersion with that installedVersion when forming `${flatName}@${installedVersion}` so the lookup matches Bun's store naming.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Nitpick comments:
In `@packages/opencode/script/build.ts`:
- Around line 249-277: The code currently uses the dependency spec string
(altimateCoreVersion from pkg.dependencies) when composing Bun store paths,
which fails for ranges/tags; instead read the actual installed version from the
on-disk loader package and use that resolved version when building candidates.
In locatePlatformPackageDir, derive the installed version by reading/parsing the
loader's package.json (located via altimateCoreLoaderDir, e.g.
fs.readFileSync(path.join(altimateCoreLoaderDir, "package.json")) and
JSON.parse(...).version) and replace uses of altimateCoreVersion with that
installedVersion when forming `${flatName}@${installedVersion}` so the lookup
matches Bun's store naming.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 21bd2d09-97fc-4877-a307-ffd25776fb17
📒 Files selected for processing (7)
.github/workflows/release.ymlinstallpackages/opencode/script/build.tspackages/opencode/script/publish.tspackages/opencode/test/branding/branding.test.tspackages/opencode/test/install/brew-formula.test.tspackages/opencode/test/install/smoke-test-binary.test.ts
There was a problem hiding this comment.
1 issue found across 7 files
Reply with feedback, questions, or to request a fix.
Re-trigger cubic
The `name` builder at build.ts:283 substitutes `win32 → windows` when
constructing the per-target directory name, so the keys in the
`binaries` map (e.g. `@altimateai/altimate-code-windows-x64`) never
contain the literal substring `win32`. The archive step at line 461
was using `key.includes("win32")` to pick `altimate.exe` vs
`altimate`, which evaluated false on every Windows target and caused
the zip step to look for a non-existent `altimate` file on Windows.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
Code review is billed via overage credits. To resume reviews, an organization admin can raise the monthly limit at claude.ai/admin-settings/claude-code.
Once credits are available, reopen this pull request to trigger a review.
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
1 similar comment
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
packages/opencode/script/build.ts (1)
92-183:⚠️ Potential issue | 🟠 Major | ⚡ Quick winFail fast when target selection resolves to an unsupported standalone build.
After removing musl and
win32-arm64fromallTargets, this path can now either pick the wrong Linux artifact or silently build nothing.--singlestill matches the glibclinux-*entry on Alpine because it only checksprocess.platform/process.arch, and a stale--target-index(or--singleonwin32-arm64) collapses to[]and exits 0 after producing no binary. Please reject unsupported host ABIs/arches andtargets.length === 0before entering the build loop.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/opencode/script/build.ts` around lines 92 - 183, The build selection can silently produce an empty targets list or pick an incompatible host artifact (e.g., glibc vs musl) — update the logic around target resolution (look at allTargets, targetsFlag, targetIndexFlag, targets, singleFlag, baselineFlag) to validate and fail fast: (1) when singleFlag is used, explicitly verify the host ABI/arch/OS combination is present in allTargets (reject unsupported host ABIs/arches such as musl or removed win32-arm64) and exit non‑zero with an error if not; (2) after computing targets, check if targets.length === 0 and emit a clear error + non‑zero exit instead of continuing; perform these checks immediately after targets is computed and before entering the build loop.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Outside diff comments:
In `@packages/opencode/script/build.ts`:
- Around line 92-183: The build selection can silently produce an empty targets
list or pick an incompatible host artifact (e.g., glibc vs musl) — update the
logic around target resolution (look at allTargets, targetsFlag,
targetIndexFlag, targets, singleFlag, baselineFlag) to validate and fail fast:
(1) when singleFlag is used, explicitly verify the host ABI/arch/OS combination
is present in allTargets (reject unsupported host ABIs/arches such as musl or
removed win32-arm64) and exit non‑zero with an error if not; (2) after computing
targets, check if targets.length === 0 and emit a clear error + non‑zero exit
instead of continuing; perform these checks immediately after targets is
computed and before entering the build loop.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: ad407437-8d1f-4b60-84ae-e655fd065818
📒 Files selected for processing (1)
packages/opencode/script/build.ts
Multi-Model Code Review — PR #820Verdict: REQUEST CHANGES Critical IssuesC1. Install script doesn't handle the Windows
|
| Issue | Origin | Type |
|---|---|---|
C1 — Install script doesn't handle Windows .exe suffix |
Claude (CRIT), GPT (MAJ), Gemini (MIN) | Consensus |
| C2 — Musl install-script cryptic 404→tar failure | GPT, Kimi, Qwen, GLM-5, Gemini, Grok, MiniMax, MiMo | Consensus (7 of 8 panelists; severity converged on CRITICAL via GLM-5/Qwen/Gemini) |
| C2 — npm wrapper still references dropped musl/win32-arm64 package names | GPT, MiMo | Convergence fix (split out of C2 after MiniMax's correction that the npm wrapper does not construct URLs) |
| M1 — Smoke test isn't hermetic (Bun's filesystem-walk fallback hides regression) | Claude | Unique; diagnosis tightened in convergence per Gemini's correction |
M2 — Shim drops _requiredExports validation |
Grok, Qwen, GPT, MiniMax, Kimi (CRIT); Gemini (safe) | Consensus with disagreement; downgraded CRIT→MAJOR via convergence on Gemini's "build-time pinning kills version-drift class" argument |
M3 — locatePlatformPackageDir couples to Bun flat-layout + literal version string |
Gemini (MAJ), Claude (MIN, version-only subset) | Consensus |
m1 — CI smoke covers only linux-x64 |
Kimi, Grok, MiniMax, Qwen | Consensus |
m2 — Smoke test only calls --version, not core API |
Grok, MiniMax (Phase 1 MAJ); demoted to MIN via convergence per GPT + MiniMax round-1 objection | Convergence fix |
m3 — Dead requiredExternals block |
Claude, MiniMax | Consensus |
| m4 — Staging cleanup not in try/finally | Kimi | Unique |
| N1 — "~80–100 MB" comment wrong (actual 183 MB) | Claude | Unique |
N2 — archiveName regex hard-coded prefix |
Claude | Unique |
N3 — Document intentional strict onResolve filter |
Qwen (CRIT), Grok (MAJ), MiniMax (NIT), MiMo (MIN), Kimi-on-reflection (NIT), Gemini-on-reflection (safe) | Convergence fix (downgraded to NIT after grep confirmed zero subpath imports) |
Reviewed by 9 models: Claude, GPT 5.4 Codex, Gemini 3.1 Pro, Kimi K2.5, Grok 4 (4.20), MiniMax M2.7, GLM-5, Qwen 3.5 Plus, MiMo V2 Pro. Convergence: 1 round (6 APPROVE / 2 CHANGES NEEDED). All round-1 corrections incorporated textually; no round 2 needed.
C1 from PR #820 review. The install script hard-coded `mv "$tmp_dir/altimate"` and `chmod ... "$INSTALL_DIR/altimate"` for every platform, but the Windows release archive contains `altimate.exe`. Result on Windows: the script tries to move a file that doesn't exist (download path) or installs a file under the wrong name with no `.exe` extension (--binary path). Fix: - Set `binary_name="$APP"` (with `.exe` suffix on windows) right after computing the target triple, then route mv/chmod through `$binary_name` in `download_and_install`. - In `install_from_binary` use the caller's basename so the installed file matches what was supplied — also handles the `.exe` case naturally and doesn't break the existing non-windows callers. Local verification (darwin-arm64 host, simulating windows path): $ binary_name="$APP"; [ "$os" = "windows" ] && binary_name="$APP.exe" $ cp /tmp/c1-test/$binary_name "${INSTALL_DIR}/$binary_name" $ ls "${INSTALL_DIR}" altimate.exe # ← extension preserved end-to-end $ bash ./install --binary /tmp/c1-test/altimate.exe --no-modify-path $ ls $HOME/.altimate/bin altimate.exe # ← --binary path preserves the .exe basename $ bash ./install --binary /tmp/c1-test/altimate --no-modify-path $ ls $HOME/.altimate/bin altimate # ← non-.exe path unchanged Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
C2 from PR #820 review. The previous PR dropped linux-*-musl and win32-arm64 from the build matrix (no @altimateai/altimate-core NAPI prebuild exists), but left three runtime paths that still tried to fetch / resolve archives for those platforms and emitted misleading errors: 1. The curl install script's musl detection sets is_musl=true and constructs `altimate-linux-x64-musl.tar.gz`. The release doesn't ship that archive, so the download 404s. curl without `--fail` writes the GitHub 404 HTML to disk; tar -xzf then dies with "not in gzip format". Fail fast with an actionable message (`apk add gcompat`, or use npm) before URL construction. 2. The npm wrapper at packages/opencode/bin/altimate has a names[] array that fans out to ${base}-musl / ${base}-baseline-musl as fallbacks. Those packages are no longer published. When findBinary() exhausts the list it prints "your package manager failed to install the right version of the altimate-code CLI" — wrong diagnosis. Hard-error on musl and win32-arm64 before findBinary() runs, with the same actionable message. 3. The npm postinstall script's findBinary() emits a generic "Could not find package @altimateai/altimate-code-<plat>-<arch>" via the require catch path on these systems. Add the same explicit musl/win32-arm64 detection there so npm install fails with a clear message. Also add `curl --fail` to both download paths (the trace-enabled progress fetch and the simpler `-#` fallback). Without it, a future 404 on any unknown target archive silently writes the error page to disk and bombs later in tar/unzip. Verified locally (sed-patched is_musl=true to force the path on a darwin host): $ bash /tmp/install-musl-sim --version 0.1.0 Alpine Linux (musl) is not currently supported by the standalone install. altimate-core has no NAPI prebuild for musl yet. Workarounds: • apk add gcompat # run glibc binaries on Alpine • Use npm: npm install -g altimate-code $ echo $? 1 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
M1 from PR #820 review. The previous standalone-mode smoke test spawned the binary with NODE_PATH="" but inherited the worktree as cwd. Bun's compiled binary, when resolving an unbundled require, walks the filesystem from process.execPath upward — it doesn't actually need NODE_PATH if there's a node_modules anywhere in the parent chain. So if the staged-shim onResolve plugin ever silently fails to redirect @altimateai/altimate-core, the binary falls back to filesystem resolution and finds altimate-core in the worktree's node_modules. Test passes for the wrong reason. Two fixes: 1. Make the runtime test hermetic. Set cwd=os.tmpdir() in spawnSync so the binary cannot walk upward into the worktree node_modules tree. Mirror the change in release.yml smoke tests (build-time and pre-publish): resolve $BINARY to an absolute path first, then `cd "${RUNNER_TEMP:-/tmp}"` before invoking the binary. 2. Add an independent content-level assertion. Run `strings` against the binary and count distinct altimate-core.<platform>.node references (after collapsing the bunfs hash suffix). If the staged shim ever silently fails and Bun pulls in the upstream multi- platform NAPI-RS loader, every platform's .node name leaks into bunfs and this test catches it independently of any runtime path. The bunfs hash-suffix regex needed a wider character class than the review's `[a-f0-9]+`: Bun uses an alphanumeric (not hex) suffix. With `[a-f0-9]+` the suffix `darwin-arm64-ptxrnv5e.node` doesn't strip (contains p/t/x/r/n/v) and the test sees 2 distinct names. Use `[a-z0-9]{6,}` instead — Bun's hash is 7+ chars; real platform last-segments (arm64/x64/gnu/msvc) are all ≤5 chars so the length bound is unambiguous. Local verification on darwin-arm64: $ bun test test/install/smoke-test-binary.test.ts 4 pass / 0 fail (was 3; added "binary embeds exactly one .node") $ strings .../bin/altimate | grep -oE 'altimate-core\\.(...).*\\.node' | sort -u altimate-core.darwin-arm64-ptxrnv5e.node # bunfs entry altimate-core.darwin-arm64.node # require() string # → collapse to 1 distinct after hash strip. Test passes. The Windows path in the content-level assertion no-ops because the `strings` binutil isn't on a stock Windows runner; Linux + macOS CI (where the build matrix actually runs) is fully covered. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
M2 from PR #820 review. The single-line shim (`module.exports = require('./altimate-core.<platform>.node')`) dropped the upstream NAPI-RS loader's `_requiredExports` correctness check. If the embedded .node ever ships missing an export (truncated artifact, mismatched napi version, etc.), the binary would crash later with a confusing "TypeError: X is not a function" instead of a clear startup error pointing at the broken binding. Extract the `_requiredExports` literal from the real loader at build time and inline it into the generated shim. If the upstream loader format ever changes and the regex stops matching, abort the build rather than ship a shim with no validation — replacing the review's suggested `?? "[]"` fallback with a hard `throw`. Verification: the embedded `strings` of the compiled binary now show the _requiredExports array (40 method names: analyzeMigration ... validate) and the throw branch. The binary still starts cleanly: $ env -u NODE_PATH .../bin/altimate --version 0.0.0-fix/curl-install-bundle-altimate-core-202605181339 $ strings .../bin/altimate | grep -c _requiredExports 6 # ← validation present (twice — main chunk + worker chunk) $ bun test test/install/smoke-test-binary.test.ts 4 pass / 0 fail Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
M3 from PR #820 review. The old locatePlatformPackageDir walked the filesystem hand-checking three different prefix shapes for bun's flat `.bun/<scope>+<name>@<version>/node_modules/<scope>/<name>` layout and interpolated `altimateCoreVersion` (read from packages/opencode/package.json dependencies) into the path literally. Both fragile: - pkg.dependencies values can carry semver prefixes (`^0.3.1`, `~0.3.1`, `>=0.3.1`) — the literal interpolation would miss the on-disk dir, which is keyed by the exact resolved version. - bun could change its flat-layout dir name format at any time; we'd silently start failing to find the prebuild. Replace with `createRequire(path.join(loaderDir, "index.js"))` then `req.resolve(`${pkgName}/package.json`)`. Node's resolver walks parent node_modules from the require base, which (in this project's bun-hoisted layout) finds the prebuild via the loader's `.bun/@AltimateAI+altimate-core@.../node_modules/@altimateai/altimate-core-<platform>` sibling entries without us having to know what the surrounding store looks like. Verified all 5 platform packages resolve to the same `.bun` paths the old function returned: darwin-arm64 -> .../node_modules/.bun/@AltimateAI+altimate-core-darwin-arm64@0.3.1/node_modules/@altimateai/altimate-core-darwin-arm64 darwin-x64 -> .../node_modules/.bun/@AltimateAI+altimate-core-darwin-x64@0.3.1/node_modules/@altimateai/altimate-core-darwin-x64 linux-arm64 -> .../node_modules/.bun/@AltimateAI+altimate-core-linux-arm64-gnu@0.3.1/node_modules/@altimateai/altimate-core-linux-arm64-gnu linux-x64 -> .../node_modules/.bun/@AltimateAI+altimate-core-linux-x64-gnu@0.3.1/node_modules/@altimateai/altimate-core-linux-x64-gnu win32-x64 -> .../node_modules/.bun/@AltimateAI+altimate-core-win32-x64-msvc@0.3.1/node_modules/@altimateai/altimate-core-win32-x64-msvc Build still succeeds, binary still starts hermetically: $ rm -rf packages/opencode/dist && \ bun run packages/opencode/script/build.ts --single --skip-install building @altimateai/altimate-code-darwin-arm64 $ cd /tmp && env -u NODE_PATH .../bin/altimate --version 0.0.0-fix/curl-install-bundle-altimate-core-202605181341 $ bun test test/install/ test/branding/ 401 pass / 0 fail altimateCoreVersion is no longer used and is dropped. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
1 similar comment
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
There was a problem hiding this comment.
Actionable comments posted: 1
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
install (1)
123-126:⚠️ Potential issue | 🟠 Major | ⚡ Quick win
pipefailbreaks musl detection—ldd --versionreturns exit code 1 on musl systems.Under
set -o pipefail(line 2), whenldd --versionexits with code 1 on musl systems (which it does by design), the pipeline fails immediately andgrepnever executes, even though the output contains "musl". This causesis_muslto remain false on musl systems, skipping the early error message (lines 134–139) and leading to confusing downstream failures when the glibc binary fails to extract.Suggested fix
if command -v ldd >/dev/null 2>&1; then - if ldd --version 2>&1 | grep -qi musl; then + ldd_out="$(ldd --version 2>&1 || true)" + if printf '%s' "$ldd_out" | grep -qi musl; then is_musl=true fi fi🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@install` around lines 123 - 126, The musl detection pipeline fails under set -o pipefail because ldd --version exits with code 1 on musl, preventing grep from running; update the detection around the ldd check so the ldd output is captured regardless of ldd's exit status (e.g., run ldd --version and pipe its stdout/stderr into grep using a subshell or capture output into a variable), then test that output for "musl" and set is_musl=true accordingly; modify the block that calls ldd --version and grep (the logic that sets is_musl) so it does not rely on pipefail-sensitive pipelines and always inspects the command output before deciding.
🧹 Nitpick comments (1)
packages/opencode/test/install/smoke-test-binary.test.ts (1)
97-111: ⚡ Quick winUse
tmpdir()fixture instead ofos.tmpdir()for hermetic cwd.Switch this test to
await using tmp = await tmpdir(...)and setcwd: tmp.pathso it follows the repo’s temp-dir lifecycle and cleanup pattern.As per coding guidelines,
packages/opencode/test/**/*.test.ts: "Use thetmpdirfunction fromfixture/fixture.tsto create temporary directories for tests with automatic cleanup" and "Always useawait usingsyntax withtmpdir()for automatic cleanup when the variable goes out of scope".🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@packages/opencode/test/install/smoke-test-binary.test.ts` around lines 97 - 111, Replace the use of os.tmpdir() for the test cwd with the repo fixture tmpdir(): import and call tmpdir() using the recommended pattern (await using tmp = await tmpdir(...)) at the start of the "binary succeeds with NODE_PATH cleared (standalone mode)" runTest block, then set spawnSync's cwd to tmp.path instead of os.tmpdir(); ensure the tmpdir fixture is imported from fixture/fixture.ts and used with await using so the directory is automatically cleaned up when the test scope ends.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@packages/opencode/script/postinstall.mjs`:
- Around line 58-62: The current detection uses execSync which throws on
non-zero exit and only returns stdout, so replace the execSync call in
isMuslPlatform (postinstall.mjs) with child_process.spawnSync('ldd',
['--version'], { encoding: 'utf8' }) and inspect the spawnSync result
(result.stdout and result.stderr) without relying on thrown errors; treat either
stdout or stderr content (concatenate both) and check
toLowerCase().includes('musl') and return true if found, and fall back to false
on missing output or other failures while preserving the existing try/catch
behavior.
---
Outside diff comments:
In `@install`:
- Around line 123-126: The musl detection pipeline fails under set -o pipefail
because ldd --version exits with code 1 on musl, preventing grep from running;
update the detection around the ldd check so the ldd output is captured
regardless of ldd's exit status (e.g., run ldd --version and pipe its
stdout/stderr into grep using a subshell or capture output into a variable),
then test that output for "musl" and set is_musl=true accordingly; modify the
block that calls ldd --version and grep (the logic that sets is_musl) so it does
not rely on pipefail-sensitive pipelines and always inspects the command output
before deciding.
---
Nitpick comments:
In `@packages/opencode/test/install/smoke-test-binary.test.ts`:
- Around line 97-111: Replace the use of os.tmpdir() for the test cwd with the
repo fixture tmpdir(): import and call tmpdir() using the recommended pattern
(await using tmp = await tmpdir(...)) at the start of the "binary succeeds with
NODE_PATH cleared (standalone mode)" runTest block, then set spawnSync's cwd to
tmp.path instead of os.tmpdir(); ensure the tmpdir fixture is imported from
fixture/fixture.ts and used with await using so the directory is automatically
cleaned up when the test scope ends.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 2b3d22e0-4384-4e96-8d57-645837391f64
📒 Files selected for processing (6)
.github/workflows/release.ymlinstallpackages/opencode/bin/altimatepackages/opencode/script/build.tspackages/opencode/script/postinstall.mjspackages/opencode/test/install/smoke-test-binary.test.ts
🚧 Files skipped from review as they are similar to previous changes (2)
- .github/workflows/release.yml
- packages/opencode/script/build.ts
There was a problem hiding this comment.
2 issues found across 6 files (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name=".github/workflows/release.yml">
<violation number="1" location=".github/workflows/release.yml:263">
P1: Pre-publish smoke test still looks for the old `altimate-code-linux-x64` artifact path, which can break release verification after the binary/archive rename to `altimate`.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| run: | | ||
| BINARY=$(find packages/opencode/dist -path '*altimate-code-linux-x64/bin/altimate' -type f | head -1) | ||
| # Resolve to an absolute path before we cd away from the workspace. | ||
| BINARY=$(find "$(pwd)/packages/opencode/dist" -path '*altimate-code-linux-x64/bin/altimate' -type f | head -1) |
There was a problem hiding this comment.
P1: Pre-publish smoke test still looks for the old altimate-code-linux-x64 artifact path, which can break release verification after the binary/archive rename to altimate.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/release.yml, line 263:
<comment>Pre-publish smoke test still looks for the old `altimate-code-linux-x64` artifact path, which can break release verification after the binary/archive rename to `altimate`.</comment>
<file context>
@@ -256,14 +259,17 @@ jobs:
run: |
- BINARY=$(find packages/opencode/dist -path '*altimate-code-linux-x64/bin/altimate' -type f | head -1)
+ # Resolve to an absolute path before we cd away from the workspace.
+ BINARY=$(find "$(pwd)/packages/opencode/dist" -path '*altimate-code-linux-x64/bin/altimate' -type f | head -1)
if [ -z "$BINARY" ]; then
echo "::error::No linux-x64 binary found in artifacts — cannot verify release"
</file context>
| BINARY=$(find "$(pwd)/packages/opencode/dist" -path '*altimate-code-linux-x64/bin/altimate' -type f | head -1) | |
| BINARY=$(find "$(pwd)/packages/opencode/dist" -path '*altimate-linux-x64/bin/altimate' -type f | head -1) |
Tip: Review your code locally with the cubic CLI to iterate faster.
There was a problem hiding this comment.
Not a bug — false positive. The path glob matches the dist subdirectory layout, not the release archive name. The build script writes binaries to dist/@altimateai/altimate-code-<target>/bin/altimate (per build.ts:280-291, the per-target dir name is pkg.name + os + arch + ... = @altimateai/altimate-code-<target>). That layout is unchanged by this PR — only the archive filename produced at build.ts:459-468 was renamed to altimate-<target>.{zip,tar.gz}. The find pattern *altimate-code-linux-x64/bin/altimate correctly resolves the dist binary; locally verified with ls packages/opencode/dist/@altimateai/altimate-code-darwin-arm64/bin/ → contains altimate.
There was a problem hiding this comment.
Thanks for the feedback! I've saved this as a new learning to improve future reviews.
… musl execSync throws on non-zero exit AND only returns stdout. ldd --version on musl exits 1 and prints to stderr — so isMuslPlatform() would silently return false on every non-Alpine musl distro (Void musl, custom builds), falling through to the generic "Could not find package" error instead of the actionable apk-add-gcompat message. packages/opencode/bin/altimate already used spawnSync correctly; this brings postinstall.mjs in line with it. Flagged by cubic-dev-ai (P2) and coderabbitai (Major) in PR #820 review. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
Review comment dispositionWalked through every review comment on the PR. Status:
PR now at HEAD |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
1 similar comment
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
There was a problem hiding this comment.
1 issue found across 1 file (changes from recent commits).
Prompt for AI agents (unresolved issues)
Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.
<file name="install">
<violation number="1" location="install:500">
P2: Only show the shell-reload hint when PATH was actually persisted; otherwise instruct manual PATH export. Current logic can tell users to reload even when no rc file was updated.</violation>
</file>
Tip: Review your code locally with the cubic CLI to iterate faster.
Re-trigger cubic
| ash|sh) reload_cmd="exec \$SHELL" ;; | ||
| *) reload_cmd="exec \$SHELL" ;; | ||
| esac | ||
| print_message info "${MUTED}Reload your shell to pick up the updated PATH:${NC}" |
There was a problem hiding this comment.
P2: Only show the shell-reload hint when PATH was actually persisted; otherwise instruct manual PATH export. Current logic can tell users to reload even when no rc file was updated.
Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At install, line 500:
<comment>Only show the shell-reload hint when PATH was actually persisted; otherwise instruct manual PATH export. Current logic can tell users to reload even when no rc file was updated.</comment>
<file context>
@@ -465,6 +475,33 @@ if [ -n "${GITHUB_ACTIONS-}" ] && [ "${GITHUB_ACTIONS}" == "true" ]; then
+ ash|sh) reload_cmd="exec \$SHELL" ;;
+ *) reload_cmd="exec \$SHELL" ;;
+ esac
+ print_message info "${MUTED}Reload your shell to pick up the updated PATH:${NC}"
+ print_message info " $reload_cmd ${MUTED}# or open a new terminal${NC}"
+ fi
</file context>
There was a problem hiding this comment.
Orphaned — this comment was posted on install:500 after I'd already force-pushed away the commit (322788bcd) that introduced the reload hint. The current PR HEAD (2898d9f01) has only 478 lines of install; line 500 doesn't exist. The reload-hint code referenced no longer ships on the branch.
There was a problem hiding this comment.
Thanks for the heads-up—understood, this comment is stale after the force-push and no longer applies.
|
@mdesmet — circling back on the cubic P2 on The finding is real:
Upstream comparison: Recommended fix: delete the block (matches upstream)Removes lines 478-503 of our @@ -475,29 +475,3 @@
print_message info "Added $INSTALL_DIR to \$GITHUB_PATH"
fi
-
-# Shell-reload hint. The script can modify rc files but cannot mutate the
-# parent shell's environment (UNIX process isolation — `export` in a child
-# never reaches the parent). When the parent's PATH lacks $INSTALL_DIR we
-# print a shell-specific reload command; otherwise altimate is already
-# usable in the current session and no hint is needed.
-#
-# Skip the hint inside GitHub Actions — the $GITHUB_PATH write above is
-# picked up automatically by subsequent steps.
-if [[ "$parent_path_has_install_dir" != "true" ]] \
- && { [[ -z "${GITHUB_ACTIONS-}" ]] || [[ "${GITHUB_ACTIONS}" != "true" ]]; }; then
- echo -e ""
- if [[ "$no_modify_path" == "true" ]]; then
- print_message info "${MUTED}--no-modify-path was set. Add this to your shell rc, or run it now:${NC}"
- print_message info " export PATH=$INSTALL_DIR:\$PATH"
- else
- case ${current_shell:-} in
- fish) reload_cmd="exec fish" ;;
- zsh) reload_cmd="exec zsh" ;;
- bash) reload_cmd="exec bash" ;;
- ash|sh) reload_cmd="exec \$SHELL" ;;
- *) reload_cmd="exec \$SHELL" ;;
- esac
- print_message info "${MUTED}Reload your shell to pick up the updated PATH:${NC}"
- print_message info " $reload_cmd ${MUTED}# or open a new terminal${NC}"
- fi
-fiAlternative: gate on actual rc-file writeIf you want to keep the hint, the correct gating is to track whether
Happy to push either variant. The delete-the-block option is simpler and removes a divergence, so I'd lean that way unless there's a specific reason to keep it. |
322788b to
bfe430f
Compare
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
❌ Tests — Failures DetectedTypeScript — 15 failure(s)
Next StepPlease address the failing cases above and re-run verification. cc @mdesmet |
|
@anandgupta42 I have removed the latest commit. |
…leanup Three issues surfaced by coderabbitai's outside-diff review. 1. install: capture ldd output before grep `set -o pipefail` (line 2) made `ldd --version 2>&1 | grep -qi musl` evaluate false on every non-Alpine musl distro — ldd exits 1 by design, pipefail propagates that, the if-block never fires, and is_musl stays false. The C2 fail-fast logic at line 134-139 then doesn't trigger, and Void/Adelie/custom musl users hit the cryptic 404→tar failure C2 was supposed to fix. Capture ldd output into a variable with `|| true`, grep on the captured string. Sanity-tested with `false` simulating musl ldd — new form returns is_musl=true, old form returns false. 2. build.ts: refuse to silently produce no artifact + reject musl host for --single `--target-index=N` for an index that no longer exists after the musl/ win32-arm64 cull yielded `[allTargets[N]].filter(Boolean) === []`, which ran the build loop zero times and exited 0 — looked like success in CI. `--single` on Alpine matches process.platform=linux/x64, builds the glibc target, and produces a binary the host can't load. Add a targets.length === 0 guard with a reason-specific error, and a musl host check for --single mode. 3. smoke-test-binary.test.ts: use the repo's tmpdir() fixture `cwd: os.tmpdir()` works but doesn't auto-clean. Repo coding guidelines require `await using tmp = await tmpdir(...)` for tests. Functional behavior unchanged; cleanup is now deterministic when the scope closes. Tests: 397 pass / 0 fail / 5 skip (the local-binary smoke tests skip when no build is present). typecheck clean. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
👋 This PR was automatically closed by our quality checks. Common reasons:
If you believe this was a mistake, please open an issue explaining your intended contribution and a maintainer will help you. |
Round 2 disposition — coderabbit outside-diff reviewThe earlier inline reviews are resolved (commit
PR HEAD: |
Convergence round 1 fixes applied
The multi-model code-review pass on this PR surfaced 5 actionable items
(review comment: #820 (comment)).
All five are addressed as separate commits stacked on top of
32a17c015:18dd4c6f1installhandlesaltimate.exeon windows —binary_nameresolved from$os,install_from_binarypreserves caller's basename4fc156e34install,packages/opencode/bin/altimate, andpackages/opencode/script/postinstall.mjs;curl --failon both download paths154cb6c96cwd: os.tmpdir()inspawnSync; new content-level assertion that the binary embeds exactly one.node(catches a silent onResolve miss); samecd "${RUNNER_TEMP:-/tmp}"shape in release.yml2564abfef_requiredExportsvalidation in the staged shim — extracted from the upstream loader at build time, hard-throw if the regex stops matching85dd35164locatePlatformPackageDir's bun-flat-layout walk withcreateRequirerooted at the loaderVerification (re-run after all 5 commits)
One review note that needed adjusting in practice
M1's suggested hash-strip regex
/-[a-f0-9]+(?=\.node$)/didn't actuallystrip bunfs's suffix. Bun's embedded-resource hash is alphanumeric (not hex)
and the real example
darwin-arm64-ptxrnv5e.nodecontainsp/t/x/r/n/v—none of which match
[a-f]. Used/-[a-z0-9]{6,}(?=\.node$)/instead: thelength bound is unambiguous because real platform last-segments
(
arm64,x64,gnu,msvc) are all ≤5 chars and Bun's hash is ≥7. Thetest was failing 1 vs 2 distinct names before this fix.
Why
The curl install at the repo root produced a binary that crashed on first run with
Cannot find module '@altimateai/altimate-core'. Root cause:script/build.tsmarked altimate-core asexternal(NAPI native modules can't live inside Bun's single-file bunfs), and the release archive shipped only the raw Bun binary — no companionnode_modules, no NODE_PATH-aware wrapper. The CI smoke tests hid the bug by pre-settingNODE_PATH="$(pwd)/packages/opencode/node_modules:..."against the developer checkout before invoking the binary.History: commit
a15dbfecforiginally moved altimate-core toexternalto avoid 4x binary bloat (bundling brought in 5 platforms' worth of.nodefiles, ~250 MB total). Subsequent commits made altimate-core a runtime npm dep so the npm flow worked. Neither commit accounted for curl-install / Homebrew / AUR.A second mismatch: the npm package exposes
altimateas the primary bin andaltimate-codeas the alias (packages/opencode/package.jsonbin block, postinstall banner, README). The curl install was installing onlyaltimate-code. This PR aligns the standalone install with the npm contract.What
Two halves, in one commit, in this PR.
1. Self-contained binary (build-level fix)
In each per-target build loop:
@altimateai/altimate-coreand locate the target's NAPI prebuild (@altimateai/altimate-core-darwin-arm64for darwin-arm64, etc.).dist/${name}/.altimate-core-staged/@altimateai/altimate-core/..nodeand platform package) with a one-line shim:.nodefile next to the shim.onResolveplugin that redirects@altimateai/altimate-coreto the staged shim. Without this, Bun resolves the import via the workspacenode_modulesand uses the full multi-platform dispatcher.@altimateai/altimate-corefromrequiredExternals. Bun statically sees the singlerequire()and embeds that one.nodeinto bunfs.Result: ~192 MB self-contained binary.
stringson it shows only target-platform.nodereferences, no leakage from the other platforms.Caveat: targets without an altimate-core prebuild
@altimateai/altimate-corehas no NAPI prebuilds for linux-musl variants or win32-arm64. Those targets are removed fromallTargetsinbuild.tsand from the GitHub Actions release matrix. Re-add if/when altimate-core ships those prebuilds upstream. Dropping was preferred over shipping a broken artifact. Affects Alpine Linux container users and Windows ARM users specifically — should be called out in release notes.2. Rename curl-install binary
altimate-code→altimateThe npm package already exposes
altimateas the primary bin. The standalone install was installing onlyaltimate-code, making it the odd one out. Aligned:install—APP=altimate(wasaltimate-code). That single change cascades to: archive filenamealtimate-<target>.{zip,tar.gz},INSTALL_DIR=$HOME/.altimate/bin,mv "$tmp_dir/altimate" "$INSTALL_DIR", every log line / tmp-basename /check_versionreference. The final getting-started block matches upstream opencode's shape (cd <project>/altimate). No fallback, no symlink, no alias — users who wantaltimate-codeget it from npm or Homebrew.script/build.ts— archive step renames@altimateai/altimate-code-<target>→altimate-<target>and packs exactly one file (thealtimatebinary), not the sourcemaps and not thealtimate-codealias. Thealtimate-codecopy stays inside the platform package dir because the npm wrapper (packages/opencode/bin/altimate) looks forbin/altimate-codeto locate the platform binary — npm flow unchanged.script/publish.ts— Homebrew + AUR formula URLs and thesha256sumpaths swap to the newaltimate-<target>archive names. Brew + AUR still install the binary asaltimateAND create analtimate-codealias inside the Cellar //usr/bin— those package managers keep both names.Things intentionally NOT changed:
AltimateAI/altimate-code(the repo isn't renamed)@altimateai/altimate-code(not renamed)binblock (still exposes bothaltimateandaltimate-code)packages/opencode/bin/altimateandpackages/opencode/bin/altimate-code(unchanged — keep working as today)Files changed
Verification
All run on darwin-arm64 against this branch:
Release-critical test suites: 400 pass / 0 fail across
test/branding/+test/install/. Typecheck clean.Binary contains no other-platform
.noderefs:Test plan
env -u NODE_PATH.env -u NODE_PATH.curl -fsSL .../install | bash -s -- --version <prerelease>in a clean container. Expectaltimate --versionto work;~/.altimate/bin/altimateis on PATH; noaltimate-codesymlink (use npm/brew if you need that name).npm i -g @altimateai/altimate-code@<prerelease>and verify bothaltimate --versionandaltimate-code --versionwork (npm still exposes both).brew install AltimateAI/tap/altimate-codeafter the prerelease formula is regenerated. Verify bothaltimateandaltimate-codework.altimate-code→altimate; install dir moved from~/.altimate-code/binto~/.altimate/bin. Existing curl-install users will need to re-runinstall(the old binary in~/.altimate-code/bincontinues to work but won't be upgraded).🤖 Generated with Claude Code
Summary by CodeRabbit
New Features
Bug Fixes
Chores
Tests