Skip to content

refactor(nix): fetch dependencies from bun.lock - #59403

Open
jerome-benoit wants to merge 33 commits into
anomalyco:devfrom
jerome-benoit:nix-lockfile-deps
Open

jerome-benoit wants to merge 33 commits into
anomalyco:devfrom
jerome-benoit:nix-lockfile-deps

Conversation

@jerome-benoit

@jerome-benoit jerome-benoit commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor

Issue for this PR

Closes #49063
Closes #59882
Closes #59601

Related: #51796 (NixOS helper loading only; generic glibc portability and diagnostics are not addressed).

Type of change

  • Bug fix
  • New feature
  • Refactor / code improvement
  • Documentation

What does this PR do?

Fetch Nix dependencies from bun.lock text formats 1–3 instead of maintaining platform-specific node_modules hashes.

  • Fetch locked archives with Nix; Bun installs the frozen workspace graph offline, preserving peer contexts and patches.
  • Remove aggregate hash maintenance, its bot and post-install normalization.
  • Patch the Linux PTY helper before embedding it and stage the Desktop CLI at the production builder's expected path.
  • Resolve the Linux Desktop version manifest from its packaged resource directory.

How did you verify your code works?

Linux x86_64 at 122ead5: CLI/Desktop builds, packaged TUI welcome and embedded PTY shell passed. bun run check passed (35/35 cached tasks), as did lock import checks and nine offline fixtures under each Bun consumer (1.4.2/1.4.4).

All three configured targets passed evaluation. CI remains evaluation-only with a 15-minute timeout. Desktop startup requires the separate service-discovery fix in #60683. ARM Linux/macOS runtimes are untested.

Commands, exact results and limits.

Checklist

  • I have tested my changes locally
  • I have not included unrelated changes in this PR

@jerome-benoit
jerome-benoit requested a review from a team October 26, 2026 21:05

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The approach looks sound, but I could not verify it: this environment has no nix, so I could not evaluate or build anything on the base or this branch. I checked by reading the code. Reading per-package URLs and integrity from bun.lock and installing with bun install --frozen-lockfile --offline removes the hash that only matched one Bun version. The CLI channel change prod → latest keeps the same database file (opencode.db) and moves the service config to service.json on the shared port, which bundled CLI discovery in Desktop needs. Desktop stays on prod, so its behaviour is unchanged. The desktop → opencode-desktop rename in check-nix.yml also fixes an attribute name the flake does not define.

Public API: only Nix packaging changes. The node_modules_updater flake output and the hash argument of nix/node_modules.nix are removed, a checks.<system>.runtime output is added, and the stable Nix CLI now reports channel latest instead of prod. The new bun2nix flake input needs no changes in downstream flakes. Anyone overriding hash on node_modules.nix would break, which is expected since there is no aggregate hash any more.

Concerns worth addressing (details inline):

  • Every package entry in bun.lock is fetched, including about 305 of 2,876 that are for other platforms (win32, darwin, musl and so on). Before, bun install --os/--cpu only fetched packages for the host.
  • The GitHub dependency is downloaded as a GitHub-generated archive and checked against the integrity in bun.lock. GitHub does not guarantee those archives stay byte-identical over time.
  • The new CI step builds the CLI and Desktop in full on every PR that touches any package.json, bun.lock, nix/ or patches/, with the timeout raised from 15 to 60 minutes. That is a large CI cost for most dependency bumps, and it covers only x86_64-linux.

Related: #52863, #52121, #52122

Comment thread nix/bun-lock.nix Resolved
Comment thread nix/bun-lock.nix Outdated
{
key = "github:${builtins.elemAt entry 2}";
value = {
url = "https://api.github.com/repos/${builtins.elemAt github 0}/${builtins.elemAt github 1}/tarball/${builtins.elemAt github 2}";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This downloads the archive GitHub generates for the commit and checks it against the integrity Bun recorded in bun.lock. GitHub doesn't promise those generated archives stay byte-identical (they changed in 2023), so this fetch can start failing with a hash mismatch without any change in the repo. Could this source be fetched as unpacked source (for example fetchFromGitHub, with its own hash) instead, so the build doesn't depend on the exact archive bytes?

@jerome-benoit jerome-benoit Oct 26, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

GitHub sources use the direct legacy codeload endpoint and retain the SRI from bun.lock. For the current locked Ghostty archive, paired API/codeload downloads are byte-identical and match that SRI. This removes the REST request and its rate-limit cost, not the archive-regeneration risk. For a retained commit in an unchanged repository, GitHub guarantees extracted contents but not compressed bytes. Different compression changes the archive hash; the retained SRI then rejects it and the build fails. An unpacked-source hash is not interchangeable with Bun's archive checksum.

Comment thread .github/workflows/check-nix.yml Outdated
if: steps.dependencies.outputs.changed == 'true'
run: nix develop --command bun nix/check-lock.ts

- name: Check CLI runtime and Desktop build

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This builds the CLI and Desktop in full, sandboxed, on every PR that touches any package.json, bun.lock, nix/ or patches/, so the timeout goes from 15 to 60 minutes. Most dependency bumps would pay for that. Maintainers may want it limited, for example to pushes to dev or PRs that touch nix//flake.*, or the Desktop build made optional.

@jerome-benoit jerome-benoit Oct 26, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The Nix workflow evaluates flake outputs only, with a 15-minute timeout. It checks CLI, Desktop and devShell derivations for x86_64-linux, aarch64-linux and aarch64-darwin. It does not install workspace dependencies, build packages or execute runtime checks; those remain explicit local verification.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not verify this push: there is still no nix in this environment, so I compared the new commit with the last one I reviewed instead. Apart from the rebase, only nix/desktop.nix changed. It now stages the Nix CLI in packages/cli/dist/<package>/, where script/build.ts on dev looks for it, so the production missing-CLI check passes with the real Nix binary. It also calls electron-builder directly, since the package script was removed on dev. Both changes match the current Desktop build and look correct.

Public API: unchanged from my previous review (Nix flake outputs and the Nix CLI channel only).

The three earlier inline points (fetching other platforms' packages, the GitHub archive download, and the full CI build on dependency changes) are not addressed by this push and still apply. Nothing new to raise.

Use Bun's recorded source integrities instead of hashing its installed
layout. Keep native Bun responsible for patches and peer contexts.

Remove hash calibration and manual lock synchronization.

Closes anomalyco#49063

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not verify this push: there is still no nix in this environment, so I compared it with the last commit I reviewed. The new os/cpu filter in nix/bun-lock.nix addresses the point about fetching other platforms' packages. It reads the metadata from the right position for each kind of source, and node_modules.nix feeds it the same bunOs/bunCpu that bun install gets. I couldn't run the new matcher's edge cases (any, none, negated and unknown values) or the fixtures in nix/check-lock.ts, so a CI run of those would be good to see.

The GitHub archive point is now documented in CONTRIBUTING.md but the fetch is unchanged, and check-nix.yml is unchanged, so those two threads stay open. Public API: unchanged since my previous review. Nothing new to raise.

Preserve the former prod service configuration and discovery during
the shared-channel cutover without overwriting existing shared files.
Keep live incumbents available to the normal version replacement policy
and retain explicit listener overrides.

Exercise compiled upgrade and incumbent behavior in Nix runtime checks.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not verify the Nix side of this push (there is still no nix here). The new migration tests pass with bun test test/service.test.ts in packages/cli.

Public API: packages/client now also exports Service.same (backwards compatible). Otherwise unchanged from my previous review.

The new commits move a Nix-only concern into core CLI service code that every release user runs. Every latest build now looks for service-prod.json in the config and state directories, and can adopt a service still running from it. migrateConfig now also accepts legacy-format files for every migration, not just this one. That code isn't Nix packaging, so I'd suggest moving the channel change (OPENCODE_CHANNEL in nix/opencode.nix) and this migration to their own pull request, where service maintainers can review them; details inline. The new service.test.ts cases are long multi-step scenarios. A smaller test of the adoption rule would be easier to follow.

The os/cpu filter from the earlier push is still in place. The GitHub archive and CI build threads stay open.

legacyConfigFile: legacy ? path.join(global.config, legacy) : undefined,
// Preserve settings from Nix's former channel without replacing existing shared settings.
legacyConfigFile:
OPENCODE_CHANNEL === "latest"

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This applies to every latest build, including npm and curl installs, not just Nix. Each service start now reads service-prod.json from the config and state directories, and may adopt a service still running from it (with the matching early return in server-process.ts). The migrateConfig change also accepts legacy-format files for every migration, not just this one. This is core service behaviour for a Nix-only channel rename, so I'd suggest splitting it into its own PR together with the OPENCODE_CHANNEL change in nix/opencode.nix. That keeps this PR about fetching dependencies and lets the service changes be reviewed on their own.

@jerome-benoit jerome-benoit Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR changes no packages/desktop, CLI, client or TUI sources and introduces no service, configuration or storage migration. Nix retains its existing prod channel. Desktop channel alignment remains outside this dependency-fetching change.

Comment thread packages/cli/test/service.test.ts Resolved
@jerome-benoit

jerome-benoit commented Oct 26, 2026 •

Copy link
Copy Markdown
Contributor Author

Verified on Linux x86_64 at 122ead5. Full package builds use the flake's Bun 1.4.2.

Results

Verification Observed result
CLI/Desktop build Exit 0; dependency tree, PTY helper, CLI and Desktop rebuilt successfully
bun run check with Bun 1.4.4 35/35 typecheck tasks passed (cached); lint: 1,353 warnings, 0 errors
bun nix/check-lock.ts Exit 0; formats 1–3, JSONC, source contexts, updates, conflicts and platform filtering passed
Actual Bun-generated locks Both Bun 1.4.2/1.4.4 produced v2 on fresh installation and v3 after a v1 ranged override; all four imported with URL/SRI unchanged
Offline cache fixtures 9/9 executed under each Bun 1.4.2/1.4.4 consumer; installed package executed and original lock bytes unchanged
nix flake check --no-build --all-systems Exit 0, all checks passed! on all three configured targets
Packaged TUI Ask anything, version 2.2.3+122ead5
Embedded PTY HTTP 200 creation/snapshot; real /bin/sh produced REVIEW122_PTY_OK, without an external helper override
Desktop app / bundled CLI manifest Both 2.2.3+122ead5
Desktop UI startup Failed: Timed out waiting for the background service to start

The Desktop backend answered /api/info with HTTP 200 and the expected version. Its isolated state contained service-prod.json, but no service.json. The existing CLI/Desktop discovery contracts use different filenames; These standalone packaging results exclude the service-discovery fix in #60683. Combined-runtime verification covers the pinned commit pair listed there, not the current Desktop head.

Commands and native probes

Repository root, with Bun 1.4.4 selected for bun run check:

bun run check
bun nix/check-lock.ts
nix flake check --no-build --all-systems
nix build .#opencode .#opencode-desktop .#checks.x86_64-linux.bun-cache --no-link --print-out-paths -L

The nine fixture outputs per consumer were explicitly rerun with nix build --rebuild …^out. Each executed bun install --frozen-lockfile --offline, imported its installed package, and compared the original lock bytes. Bun 1.4.4 used the same fixture module's bun argument with the official Linux x86_64 executable, prepared for the Nix sandbox. Its unpacked source hash is sha256-gHN554d/7j2JkXgn64rFRtbSi+Zt/fetRppilio/scw=.

Runtime probes used termctrl/Xvfb and private HOME/XDG/TMPDIR/database paths. The foreground CLI server created a terminal through POST /api/experimental/session/ses_review122/terminal; its snapshot contained the shell-generated marker. Both private PTY services returned HTTP 204 on shutdown. All owned applications and helpers were stopped; no user services or configuration were modified.

Dependency graph

  • Seven OpenTUI-consuming workspaces plus the Solid integration resolve one physical @opentui/[email protected] entry. Constructor identity and cross-consumer instanceof checks passed. Workspace links remain relative in the Nix store.
  • Two @ai-sdk/[email protected] instances retain distinct Zod 3.25.76 and 4.6.7 contexts. Archive sharing does not merge the installed peer graphs.

Limits

  • Native execution covers Linux x86_64 only. ARM Linux/macOS were evaluated, not run.
  • Bun 1.4.4 coverage includes generated locks and offline fixtures, not a full CLI/Desktop build with that consumer.
  • GitHub archive regeneration can invalidate Bun's recorded SRI. Direct codeload avoids the REST request, not that risk.
  • Bun2nix master remains pinned at 0f2a1f0b6f42cebe3b149bf62d38754c5e0e9729; no native Bun 1.4.4 HTTP cache-key producer is available there. Unknown future formats are not guaranteed.
  • CI remains evaluation-only with a 15-minute timeout. Hosted check-nix, check, check-test and check-bot-scope report action_required; no hosted Nix success or cold-cache timing is claimed.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This push only changes tests. The long multi-step migration test is now split into small cases, each with its own temporary directory: adoption, the rejection cases, and an existing shared registration being left alone. They are easier to follow now. bun test test/service.test.ts in packages/cli passes (41 pass, 1 skip). Public API: unchanged since my previous review.

No source changes, so the open threads on the GitHub archive fetch, the CI build scope and the prod → latest service migration in core CLI code still apply.

Preserve shared, named prod, then hashed prod migration precedence.
Replace conflicting saved listeners through stop/handoff/ensure without
changing explicit CLI overrides.

Keep archive URLs and SRI while separating archive hosts from registry
namespaces. Override fetch names only when extraction requires it.

Cover offline native-Bun consumers and compiled single-owner upgrades.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not verify the Nix side of this push: there is still no nix here, so the cache-namespace fix, the .tgz filename change and the new bun-cache check went unevaluated. bun test test/service.test.ts in packages/cli passes (44 pass, 1 skip).

Public API: adds the checks.<system>.bun-cache flake output. Otherwise unchanged from my previous review.

The new commit grows the core CLI migration: it now also covers hashed prod files, and the service startup path can stop the old service and start a replacement (inline). That makes the case for moving the prod → latest channel change and its migration into its own PR stronger. The open threads on the GitHub archive fetch, the CI build scope and that split still apply.

Comment thread packages/cli/src/server-process.ts Outdated
)
return
// A successor must inherit the handoff prepared after this process started.
yield* Service.stop({ file: serviceOptions.file, pty: legacy.state === "ready" ? "handoff" : "clear" })

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A starting serve --service process now stops another live service, then calls Service.ensure, which launches yet another serve --service process, and exits without serving. This runs for every stable install that has an old prod registration and saved hostname/port settings, regardless of the old service's version. It is a new step in the core service lifecycle, added for a Nix-only channel rename, and I don't think it should land inside a dependency-fetching refactor. Could it go with the rest of the prod → latest migration into its own PR, where the service maintainers can weigh it against the normal client version policy?

@jerome-benoit jerome-benoit Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR changes neither server-process.ts nor service-config.ts and introduces no channel cutover or additional service startup path. Native verification starts a real shell with the packaged CLI's embedded PTY helper.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This push only changes comments and CONTRIBUTING.md, with no behaviour change, so there was nothing to run. Public API: unchanged from my previous review. The open threads on the GitHub archive fetch, the CI build scope, and splitting out the prod → latest service migration and its lifecycle change still apply. The new comment in service-config.ts says released versions can also own a prod registration, which affects stable installs well beyond Nix. That is one more reason to review it in its own PR.

Keep HTTP cache keys aligned with the consuming Bun so nixpkgs can
move from 1.4.2 to 1.4.4 without breaking frozen offline installs.

Preserve prod TUI state during the latest channel cutover. Replace
legacy service owners in the requesting process and adopt the
prepared terminal handoff before announcing readiness.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not verify the Nix side of this push (there is still no nix here). The new TUI storage tests (8 pass in packages/tui) and the CLI service tests (44 pass, 1 skip in packages/cli) pass.

Public API: adds a ./pty-handoff export to @opencode/client (backwards compatible), on top of the earlier Service.same export and the Nix flake changes listed before.

None of the open threads is fixed. The service no longer launches a second process after stopping the old one, but it still stops a live service from core startup code (thread 6). The push also adds a third core migration, prod → latest TUI state, and changes the handoff flow for every service start. New concerns are inline: TUI startup can now fail on migration errors, the server duplicates the client's handoff step, and bun-cache.nix depends on Bun's internal cache key format. With the TUI, CLI service, client and Nix all involved, this PR no longer reads as a Nix dependency refactor. Splitting the channel cutover into its own PR would let it be reviewed properly.

Comment thread packages/tui/src/context/storage.tsx Outdated
}
}

function migrateProdStorage(root: string, destination: string) {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This runs on every TUI start for every stable install, not only Nix users moving off prod. Any error other than ENOENT/EEXIST is rethrown from createStorage, so an unreadable or odd prod/tui entry (EACCES, ENOTDIR, a failed copy) stops the TUI from starting. The new test asserts exactly that. Also, because the copy runs on every start, a latest state file the user deleted comes back from prod the next time. A migration should not be able to crash startup: could failures be logged and ignored? And this channel cutover is a TUI behaviour change for all users that would be better reviewed in its own PR.

@jerome-benoit jerome-benoit Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR changes no TUI storage code. Nix keeps its existing prod channel; no legacy-state import, migration marker or additional startup error handling is included.

Comment thread packages/cli/src/server-process.ts Outdated
const url = HttpServer.formatAddress(server.address)
if (serviceOptions !== undefined)
yield* Effect.tryPromise(() =>
PtyHandoff.complete(serviceOptions.file, { id: instanceID, pid: process.pid, url }),

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Every service start now clears the terminal handoff ticket itself, not just the legacy-migration path. The client already does this after ensure (client/src/effect/service.ts:95, promise/service.ts:77), so the step now happens in two places, and the server clears it before the client confirms the handoff. That changes the normal restart flow for every install. It would be better confined to the migration path, or reviewed separately with the service owners.

@jerome-benoit jerome-benoit Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR changes no server or client lifecycle code. Terminal handoff completion remains with the existing client lifecycle; no additional server-side completion is included.

Comment thread nix/bun-cache.nix
};
in
{
name = "@T@${algorithm}-${builtins.substring 0 32 digest}@@@1";

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This builds Bun's internal cache folder names by hand (@T@<algo>-<first 32 hex>@@@1) and chooses the format with a bun.version check, to work around the pinned bun2nix. The build then depends on undocumented Bun internals that can change in any Bun release, and the next format change would only show up as a cache miss when installing offline. Could bun2nix be bumped to a version that supports Bun 1.4.4 instead, or the Bun version pinned, rather than copying its key format here?

@jerome-benoit jerome-benoit Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pinned bun2nix master remains 0f2a1f0b6f42cebe3b149bf62d38754c5e0e9729. Its cache-entry creator receives neither archive integrity nor the selected consumer Bun, so a pin bump cannot provide Bun 1.4.4 HTTP cache keys. The adapter covers only HTTP tarball entries; registry/GitHub entries remain bun2nix-produced.

Nine offline fixtures passed under each Bun consumer (1.4.2/1.4.4), including lockfile v2 and scoped-override v3 cases, on Linux x86_64 (verification). Unknown future cache formats are not guaranteed. The upstream producer request remains open; this maintenance risk is not resolved.

Keep legacy TUI filesystem errors nonfatal and record completed imports so deleted latest state stays deleted. Failed imports remain retryable.

Complete PTY handoff inside the server only on the local legacy replacement path. Normal ensures retain ready-acknowledgement cleanup.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This push addresses two earlier points. The TUI prod → latest storage copy now logs failures instead of crashing startup, and a marker stops deleted latest state from coming back. The server now clears the terminal handoff ticket only after it has stopped an old prod service, so the normal restart flow is unchanged. The TUI storage tests (10 pass) and CLI service tests (44 pass, 1 skip) pass. I still couldn't run the Nix side.

Public API: unchanged from my previous review.

The remaining open threads still apply: stopping the old service from core startup and splitting the channel cutover into its own PR, the hand-built Bun cache keys, the GitHub archive fetch, and the CI build scope.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This push only merges dev. The files this PR changes are identical to the previous head, so there was nothing new to run or review. Public API: unchanged from my previous review. The remaining open threads still apply: the GitHub archive fetch, the CI build scope, splitting the prod → latest cutover (including the service stop at startup) into its own PR, and the hand-built Bun cache keys.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I could not verify this push. One of its new client tests fails consistently here. In packages/client, bun test test/service.test.ts test/service-reconnect.test.ts gives 44 pass and 1 fail: the new "an incumbent observation cannot retarget an owner published during its probe" fails 3 out of 3 runs on its own, after about 2s each (inline).

Public API: @opencode/client gains StopOptions.expected, and the value Service.incumbent returns now includes info. Both are additive and backwards compatible, on top of the earlier ./pty-handoff, Service.same and Nix flake changes.

Checking that the expected service still owns the registration before stopping it closes a real race. But it changes how ensure replaces services for every client, so this PR now spans the Nix build, the CLI service, the TUI and the client package. That reinforces the open request to move the prod → latest cutover and the service changes into their own PR. The remaining open threads (GitHub archive fetch, CI build scope, hand-built Bun cache keys) still apply.

Comment thread packages/client/test/service.test.ts Outdated
expect(await Bun.file(fixture.registration).exists()).toBe(false)
})

test("an incumbent observation cannot retarget an owner published during its probe", async () => {

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fails consistently for me. Run on its own (bun test test/service.test.ts -t "incumbent observation" in packages/client) it failed 3 out of 3 times, each after about 2.02s, with Observed owner did not finish its probe: Service.incumbent gives up before the probe-gated fixture releases its first request. It depends on two spawned services, file-based gates, sleep loops in the fixture and a 15s deadline, so it is also timing-sensitive. Could it be made deterministic, for example by testing stop({ expected }) directly with a registration that differs from expected, instead of racing a live probe?

@jerome-benoit jerome-benoit Oct 27, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This PR changes no packages/client code. It introduces no conditional-stop API, ownership-race test or probe-gated fixture change.

@opencode-agent opencode-agent Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This push fixes the client test that failed every time: it no longer depends on file gates and sleep loops, and it passes reliably here. Run on its own it passed 3 out of 3 times (about 31ms each), and the full service.test.ts + service-reconnect.test.ts run in packages/client gives 45 pass, 0 fail. The probe-gated fixture mode in test/fixture/service.ts is no longer used and can be dropped.

Public API: unchanged from my previous review. The other open threads (GitHub archive fetch, CI build scope, splitting the prod → latest cutover and service changes into their own PR, hand-built Bun cache keys) still apply.

Test User and others added 2 commits October 27, 2026 19:37
Keep CLI, client and TUI behavior unchanged. Select the Nix prod service
through Desktop's existing channel setting, including its early probe.
Use the shared build channel for service discovery and preserve the existing runtime override.
Keep executable reuse and cleanup within the raw build channel while preserving version and architecture identity.
@rekram1-node

Copy link
Copy Markdown
Collaborator

Thanks for tackling #49063, getting rid of nix/hashes.json and the hash bot is a great direction.

Right now this PR has grown pretty large though, so I'd like to narrow the scope before merging:

  1. Please drop the packages/desktop changes from this PR. Moving staged CLIs under cli/channel-<ch>/ leaves existing cli/<ver>-<arch> binaries (~200 MB) orphaned on disk for every Desktop user since cleanStages only cleans inside the channel subfolder. (prod also isn't a real CLI release channel, stable builds use latest, so aligning nix/opencode.nix or handling Desktop channels can be a small follow-up PR.)
  2. Please drop nix/check-tui-colors.ts and nix/check-service.ts. Reaching into internal packages/tui modules from nix/ will break on unrelated refactors, and cutting those keeps this PR focused on the lockfile fetch. (Keeping nix/opencode-pty.nix and the nix/desktop.nix build fix is fine, though sidecar-probe.ts also uses process.resourcesPath on Linux.)
  3. In nix/bun-lock.nix, switch the GitHub tarball URL from api.github.com/repos/.../tarball/... to https://codeload.github.com/<owner>/<repo>/legacy.tar.gz/<rev>. api.github.com has a 60 req/hr unauthenticated IP limit inside the Nix sandbox and just 302s to codeload.github.com with the same bytes.
  4. Please rebase on dev to resolve the nix/hashes.json conflict.

(Automated review)

@jerome-benoit

jerome-benoit commented Oct 30, 2026 •

Copy link
Copy Markdown
Contributor Author

@rekram1-node The diff is limited to Nix packaging and contributor guidance.

  • packages/desktop is unchanged from dev; only dependency-cache flake checks remain.
  • GitHub sources use direct legacy codeload with Bun's original SRI.
  • The Linux resource substitution includes sidecar-probe.ts; native tracing verifies the packaged manifest lookup.
  • dev through da87c47 is integrated; GitHub reports no conflicts.

Verification and limits: builds, TUI and embedded PTY pass. Standalone Desktop startup hits the existing registration mismatch; #60683 provides the separate runtime fix. Nix keeps prod, with no channel or storage migration.

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

2 participants