chore(master): release 0.23.0 - #433
Conversation
There was a problem hiding this comment.
✅ No new issues found.
Reviewed changes — the full generated release-please diff for the 0.23.0 desktop release, checked against the actual commit range since v0.22.0 and against the release contracts in docs/RELEASE.md.
- Manifest bump —
.release-please-manifest.json"."moves0.22.0→0.23.0. - Desktop version bump —
apps/desktop/package.jsonversion→0.23.0, written through the config'sextra-filesjsonpath$.version. - Changelog entry — a new
## [0.23.0]section listingfeat(config): generate branded desktop and mobile identity(#428,a50ea0f).
Three things I checked rather than assumed:
- The range since
v0.22.0(6d8f9dac) is exactly two commits — #427 (test:) and #428 (feat:).test:has no default changelog section, so its omission is correct, and a minor bump is right for afeatunderbump-minor-pre-majoron 0.x. - Invariant 5 holds:
apps/desktop/package.jsonreads0.23.0and the tag will bev0.23.0, which is whatbuild-desktop.yml's "Assert package version matches release tag" step compares. - #428's brand work does not re-brand this release.
assertStagedConfigMatchesGeneratedmarks a build branded only when the config bundle'sbrandId !== 'linkcode', so the default release keepselectron-builder.yml'sappId: com.arcboxlabs.linkcode.desktop,productName: LinkCode, and the base publish feed — artifact names and updater feed continuity for 0.22.0 clients are unaffected.
ℹ️ This is the first tag cut since the desktop packaging path was reworked
Nothing in this diff is wrong, but v0.23.0 is the first release to run #428's reworked packaging scripts for real. Signed builds set LINKCODE_REQUIRE_CONFIG_BUNDLE=1, so the new hard-fail assertStagedConfigMatchesGenerated gate executes on a real signed release for the first time on this tag; today it has unit coverage only. A failure there aborts packaging after the tag already exists, which is the awkward state to unwind.
Technical details
# First signed build on the reworked desktop packaging path
## Affected sites
- `apps/desktop/scripts/package-config.mts` — `assertStagedConfigMatchesGenerated` is a new hard-fail gate comparing `apps/desktop/generated` against `apps/desktop/out/config`; it throws rather than degrading.
- `apps/desktop/scripts/package-app.mts` — calls that gate before every pack, and additionally refuses `-c.appId` / `-c.productName` / `-c.protocols` passthrough overrides on branded builds.
- `.github/workflows/build-desktop.yml` — sets `LINKCODE_REQUIRE_CONFIG_BUNDLE: 1` whenever `sign` is true, so the config-bundle staging path is mandatory on exactly this release.
## Required outcome
- Confidence that a signed desktop package succeeds end-to-end on `a259c3f` before the `v0.23.0` tag is pushed.
## Suggested approach
- Dispatch `release-desktop.yml` with `dry_run: true` against this branch or the prospective merge SHA — it builds and signs but publishes nothing, which is precisely the rehearsal this situation calls for.
## Open questions for the human
- Does the configured `CONFIG_RELEASE_MANIFEST_DESKTOP` for this release resolve to `brandId: linkcode`? That is the value that keeps `branded` false and preserves `appId` / `productName` / the publish feed; it is an org-level repo variable and not verifiable from the repo.Claude Opus | 𝕏
lucas77778
left a comment
There was a problem hiding this comment.
Reviewed the complete release-please diff and release contracts. Verified the manifest and desktop package both advance from 0.22.0 to 0.23.0, the changelog accurately reflects the feat commit since v0.22.0 while correctly omitting the test-only commit, and the minor bump matches bump-minor-pre-major. The tag/package version contract remains aligned, and all current CI and CodeQL checks pass. No blocking findings.
a259c3f to
9540bb5
Compare
There was a problem hiding this comment.
Important
The regenerated changelog lists #426 as a plain feature, but it moves MIN_COMPATIBLE_WIRE_VERSION from 68 to 76 — this release refuses every peer built before it, and nothing in the notes says so.
Reviewed changes — the delta since the prior pullfrog review of a259c3f. release-please regenerated the release commit on top of the new master (084933c), so a second feature joined the release; the version itself did not move.
- Added #426 to the 0.23.0 changelog —
feat(providers,schema,engine,workbench): retire the default model(084933c) now sits under### Featuresalongside #428. - Re-dated the release section —
2026-08-08→2026-08-10. - Left the version untouched —
.release-please-manifest.jsonandapps/desktop/package.jsonstill read0.23.0. Both entries arefeatandbump-minor-pre-major: truemakes even a breaking change a minor bump on 0.x, so the minor is correct either way and Invariant 5'sv0.23.0tag pairing still holds.
⚠ Nothing coordinates the peers this release locks out
MIN_COMPATIBLE_WIRE_VERSION now equals WIRE_PROTOCOL_VERSION (76), so there is zero backward tolerance in either direction, and the desktop app does not always own the daemon it talks to: it stands down for an externally-running daemon and dials whatever runtime.json advertises, and settings.daemonUrl bypasses spawning entirely. apps/mobile is shipped separately to TestFlight / Play internal and is outside this release train, so a mobile build predating #426 stops connecting to a 0.23.0 daemon at the 5s handshake timeout with nothing on screen naming the cause.
Technical details
# v0.23.0 severs the wire with pre-#426 peers, with no announcement and no diagnosis
## Affected sites
- `packages/foundation/schema/src/wire/message.ts:12,16` — #426 moved `WIRE_PROTOCOL_VERSION` 73 → 76 and `MIN_COMPATIBLE_WIRE_VERSION` 68 → 76. The floor jumped eight versions in one release and now sits at the current version, so no older peer is accepted.
- `apps/desktop/src/main/daemon-supervisor.ts:29,124-127` — on `DAEMON_EXIT_ALREADY_RUNNING` the supervisor stands down rather than replacing the incumbent, and `isDaemonManaged()` is false whenever `settings.daemonUrl` is set. Combined with `apps/desktop/src/main/daemon-discovery.ts:14-24` (`settings.daemonUrl ?? discoverRuntimeUrl() ?? default`), a 0.23.0 renderer can end up dialing a daemon it did not build.
- `.github/workflows/build-mobile.yml:6-13,38-41` — mobile ships on its own `workflow_dispatch(submit)` to TestFlight and Play internal testing, decoupled from `release-please`. A build cut before `084933c` speaks wire ≤ 75.
- `packages/client/core/src/client.ts:190-199,651-655` — the diagnosis only exists in one direction. New client vs old host: `pong` arrives and `wireIncompatibility` returns `host speaks wire v73, older than the v76 this build needs`, but `packages/client/workbench/src/app/connection-state.tsx` renders only the generic `error`/`errorManaged` copy, so the string is log-only. Old client vs new host: `parseWireMessage` refuses the sub-floor `ping` before anything answers, so no `pong` ever comes and the connection dies at `HANDSHAKE_TIMEOUT_MS` — the CODE-447 shape, where only an out-of-band probe can name the cause.
- `apps/daemon/src/config.ts` (`withEnabledAccounts`, `withPickedModels`) and `apps/daemon/drizzle/0009_add_session_run_pin.sql` — legacy config fields are folded forward and dropped on the next write, and the migration has no down step. Reinstalling 0.22.0 after 0.23.0 does not restore the previous model / default-account state.
## Required outcome
- A decision, before the tag is pushed, on whether anything outside this release train has to ship alongside `v0.23.0` — and release notes that let a user whose client stops connecting understand why.
## Open questions for the human
- Is a mobile build predating `084933c` currently in testers' hands via TestFlight or Play internal? That is not verifiable from this repo, and it is the difference between "no field exposure" and "testers hit an unexplained 5s timeout the moment their daemon updates".
- Is the desktop side worth threading `wireIncompatibility`'s message into the connection screen as a follow-up issue? The string already exists and is already correct; only desktop discards it. Mobile renders it, but in the direction that cannot fire here.Claude Opus | 𝕏
| ## [0.23.0](https://github.com/arcboxlabs/linkcode/compare/v0.22.0...v0.23.0) (2026-08-10) | ||
|
|
||
|
|
||
| ### Features |
There was a problem hiding this comment.
#426 is listed as a plain feature, but it removes ProviderConfig.activeAccountId/model, drops config.account.create-and-bind, and moves both wire versions to 76 — its own squashed body says so (feat(schema,engine,daemon)!: … BREAKING: … both wire versions move to 76). release-please could not see it: the squash subject carries no !, and the footer is spelled BREAKING:, which is not one of conventional-commits' BREAKING CHANGE / BREAKING-CHANGE note keywords.
The version is unaffected (bump-minor-pre-major makes a breaking change a minor bump on 0.x anyway), but these notes are what finalize-releases.yml turns into the draft release, so the break reaches users unannounced. Adding the section by hand here is the only fix left for this tag — the commit convention is the fix for the next one.
| ### Features | |
| ### ⚠ BREAKING CHANGES | |
| * **schema,engine,daemon:** `ProviderConfig` loses `activeAccountId` and `model`, `config.account.create-and-bind` is gone, and both wire versions move to 76 — a client or daemon below wire v76 can no longer connect ([#426](https://github.com/arcboxlabs/linkcode/issues/426)) | |
| ### Features |

🤖 I have created a release beep boop
0.23.0 (2026-08-10)
Features
This PR was generated with Release Please. See documentation.