refactor(platform): consolidate build-channel config into one table + fail closed - #385
Open
pnewsam wants to merge 2 commits into
Open
refactor(platform): consolidate build-channel config into one table + fail closed#385pnewsam wants to merge 2 commits into
pnewsam wants to merge 2 commits into
Conversation
This was referenced Jul 10, 2026
pnewsam
added a commit
that referenced
this pull request
Jul 16, 2026
Address the three Major review comments on the channel model plus their build-time mirror: - normalizeBuildKind FAILS CLOSED on an explicitly present but unrecognized build kind (throws) instead of silently resolving to prod — a typo could otherwise point non-prod code at the production data home / API. An absent value still means prod (legacy builds). resolveBuildKind reads build-config.json in a guard that swallows only FILE errors, so a present-but-invalid value still aborts. - checkChannelConsistency compares full API origins, not the env slug, so an unknown/mistyped host (e.g. https://example.com) is no longer indistinguishable from prod. An empty host stays the intentional prod default. gen-build-channel.mjs mirrors both changes at build time. - minds-urls derives the main-process API host from the resolved channel (CHANNELS[buildKind()].apiHost) as its final fallback, so a clean `npm run dev` talks to the dev backend instead of silently falling back to prod while the renderer is on dev. prod is unchanged in every case. - server-source sources the per-kind server ref from CHANNELS[kind] .serverRef (the table is now authoritative). This also fixes rename fallout: the old `kind === 'stable'` check no longer compiled, and a `staging` build would have fallen back to installing the server from main. - buildKind() tolerates a missing electron `app` (tests/tooling) and resolves dev instead of throwing; prod is byte-for-byte identical. - Stale-home migration failures (~/.cowork-stable → ~/.cowork-staging) are logged instead of silently swallowed, via a testable migrateHomeDir helper. New/updated tests: channels (fail-closed + unknown-host), minds-urls (clean-dev resolution), cowork-home (filesystem migration incl. rename-failure), server-source (stable→staging). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pnewsam
force-pushed
the
paul/developer-platform-channels
branch
from
July 16, 2026 16:32
106200d to
8092821
Compare
This comment was marked as outdated.
This comment was marked as outdated.
pnewsam
added a commit
that referenced
this pull request
Jul 16, 2026
Address the three Major review comments on the channel model plus their build-time mirror: - normalizeBuildKind FAILS CLOSED on an explicitly present but unrecognized build kind (throws) instead of silently resolving to prod — a typo could otherwise point non-prod code at the production data home / API. An absent value still means prod (legacy builds). resolveBuildKind reads build-config.json in a guard that swallows only FILE errors, so a present-but-invalid value still aborts. - checkChannelConsistency compares full API origins, not the env slug, so an unknown/mistyped host (e.g. https://example.com) is no longer indistinguishable from prod. An empty host stays the intentional prod default. gen-build-channel.mjs mirrors both changes at build time. - minds-urls derives the main-process API host from the resolved channel (CHANNELS[buildKind()].apiHost) as its final fallback, so a clean `npm run dev` talks to the dev backend instead of silently falling back to prod while the renderer is on dev. prod is unchanged in every case. - server-source sources the per-kind server ref from CHANNELS[kind] .serverRef (the table is now authoritative). This also fixes rename fallout: the old `kind === 'stable'` check no longer compiled, and a `staging` build would have fallen back to installing the server from main. - buildKind() tolerates a missing electron `app` (tests/tooling) and resolves dev instead of throwing; prod is byte-for-byte identical. - Stale-home migration failures (~/.cowork-stable → ~/.cowork-staging) are logged instead of silently swallowed, via a testable migrateHomeDir helper. New/updated tests: channels (fail-closed + unknown-host), minds-urls (clean-dev resolution), cowork-home (filesystem migration incl. rename-failure), server-source (stable→staging). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pnewsam
force-pushed
the
paul/developer-platform-channels
branch
from
July 16, 2026 16:44
8092821 to
ba6e6f4
Compare
hamishfagg
approved these changes
Jul 17, 2026
hamishfagg
left a comment
Contributor
There was a problem hiding this comment.
approving devops changes
pnewsam
added a commit
that referenced
this pull request
Jul 21, 2026
Address the three Major review comments on the channel model plus their build-time mirror: - normalizeBuildKind FAILS CLOSED on an explicitly present but unrecognized build kind (throws) instead of silently resolving to prod — a typo could otherwise point non-prod code at the production data home / API. An absent value still means prod (legacy builds). resolveBuildKind reads build-config.json in a guard that swallows only FILE errors, so a present-but-invalid value still aborts. - checkChannelConsistency compares full API origins, not the env slug, so an unknown/mistyped host (e.g. https://example.com) is no longer indistinguishable from prod. An empty host stays the intentional prod default. gen-build-channel.mjs mirrors both changes at build time. - minds-urls derives the main-process API host from the resolved channel (CHANNELS[buildKind()].apiHost) as its final fallback, so a clean `npm run dev` talks to the dev backend instead of silently falling back to prod while the renderer is on dev. prod is unchanged in every case. - server-source sources the per-kind server ref from CHANNELS[kind] .serverRef (the table is now authoritative). This also fixes rename fallout: the old `kind === 'stable'` check no longer compiled, and a `staging` build would have fallen back to installing the server from main. - buildKind() tolerates a missing electron `app` (tests/tooling) and resolves dev instead of throwing; prod is byte-for-byte identical. - Stale-home migration failures (~/.cowork-stable → ~/.cowork-staging) are logged instead of silently swallowed, via a testable migrateHomeDir helper. New/updated tests: channels (fail-closed + unknown-host), minds-urls (clean-dev resolution), cowork-home (filesystem migration incl. rename-failure), server-source (stable→staging). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pnewsam
force-pushed
the
paul/developer-platform-channels
branch
from
July 21, 2026 00:16
fe8cfc9 to
ac55d19
Compare
pnewsam
added a commit
that referenced
this pull request
Jul 28, 2026
Address the three Major review comments on the channel model plus their build-time mirror: - normalizeBuildKind FAILS CLOSED on an explicitly present but unrecognized build kind (throws) instead of silently resolving to prod — a typo could otherwise point non-prod code at the production data home / API. An absent value still means prod (legacy builds). resolveBuildKind reads build-config.json in a guard that swallows only FILE errors, so a present-but-invalid value still aborts. - checkChannelConsistency compares full API origins, not the env slug, so an unknown/mistyped host (e.g. https://example.com) is no longer indistinguishable from prod. An empty host stays the intentional prod default. gen-build-channel.mjs mirrors both changes at build time. - minds-urls derives the main-process API host from the resolved channel (CHANNELS[buildKind()].apiHost) as its final fallback, so a clean `npm run dev` talks to the dev backend instead of silently falling back to prod while the renderer is on dev. prod is unchanged in every case. - server-source sources the per-kind server ref from CHANNELS[kind] .serverRef (the table is now authoritative). This also fixes rename fallout: the old `kind === 'stable'` check no longer compiled, and a `staging` build would have fallen back to installing the server from main. - buildKind() tolerates a missing electron `app` (tests/tooling) and resolves dev instead of throwing; prod is byte-for-byte identical. - Stale-home migration failures (~/.cowork-stable → ~/.cowork-staging) are logged instead of silently swallowed, via a testable migrateHomeDir helper. New/updated tests: channels (fail-closed + unknown-host), minds-urls (clean-dev resolution), cowork-home (filesystem migration incl. rename-failure), server-source (stable→staging). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
pnewsam
added a commit
that referenced
this pull request
Jul 28, 2026
… code Rebasing #385 onto staging surfaced three references to the pre-rename `stable` build kind in code that only exists on staging (added by #461's PyPI flooring and #503's start-budget work, which the PR branched before): - server-updater.ts `includePrereleases()` gated the rc pre-release stream on `kind === 'stable'`; after the rename buildKind() never returns that, so staging builds would silently stop tracking rc wheels (also a TS2367). - server-process.test.ts mocked `./uv-paths` with `getCoworkServerBinary`, but the merged server-process.ts resolves the binary via the PR's `coworkServerBinCandidates()` — the stale mock broke 15 Windows-reap tests. - minds-urls.workflows.test.ts `HOST_FOR_KIND` keyed the staging ring under `stable`, so the renamed `build_kind: staging` failed its host guard. Behaviour-only alignment to the canonical `staging` kind; no logic change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pnewsam
force-pushed
the
paul/developer-platform-channels
branch
from
July 28, 2026 18:45
51d6d1b to
98766c3
Compare
pnewsam
added a commit
that referenced
this pull request
Jul 28, 2026
…rget (rescope) Rescopes #385 to what it uniquely adds on top of today's staging — the canonical CHANNELS table, fail-closed build-kind resolution, and the build-script drift-guard — and backs out two decisions that the merged #312/#461/#493 work made unnecessary or contentious: - Keep the build kind named `stable` (not `staging`). staging already ships `stable` (workflows, ~/.cowork-stable home, docs); the rename was pure churn plus a data-home migration, for a naming-consistency win with no user-facing meaning. CHANNELS is simply keyed `stable`; env references (api.staging, envSlug 'staging', serverRef 'staging', the `staging` branch) are unchanged. Removes LEGACY_KIND_ALIASES and the ~/.cowork-stable→~/.cowork-staging migration (migrateStaleChannelHome / migrateHomeDir) with them. - Keep `dev` builds pointed at the staging API (CHANNELS.dev.apiHost), matching staging's existing behaviour, instead of retargeting them at api.dev. The renderer mirror and its tests revert to staging's version as a result. Kept from #385: the CHANNELS single source of truth (drift-proofing the scattered build-kind switches that this very rebase caught drifting), the fail-closed policy on a present-but-broken build kind, and the channel-origins.mjs mirror + drift test. Full suite green (629 tests), typecheck + cowork-purity clean. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pnewsam
force-pushed
the
paul/developer-platform-channels
branch
3 times, most recently
from
July 28, 2026 20:08
8788d47 to
4466efd
Compare
…ble (fail closed) The build "kind" (dev / preview / stable / prod) drives five things — the isolated data home, the MindsHub API host, the env slug stamped on the server, the default cowork-server ref, and the PyPI channel — and each was resolved by its own `switch (buildKind())` in a different file, free to drift out of sync (this branch's own rebase caught exactly that drift). Replace those scattered sites with a single source of truth. - channels.ts: the CHANNELS table (one row per kind → home dir, API host, env slug, server ref) plus normalizeBuildKind / checkChannelConsistency. Build-kind resolution now FAILS CLOSED: a present-but-broken or unrecognized kind throws instead of silently resolving to prod (which would point a non-prod build at the production data home and API). A genuinely absent config still resolves to prod, so legacy releases keep working. - cowork-home, minds-urls, server-source, uv-paths, ui-updater, index, server-process, minds-auth: resolve their per-kind value through CHANNELS rather than a local switch. - channel-origins.mjs (+ .d.mts): a plain-JS mirror of the table for build scripts that can't import the TS, guarded by a drift unit test in channels.test.ts so the mirror can't silently go stale. - gen-build-channel.mjs: validates the baked build_kind ↔ minds_api_url pairing at build time against that same mirror. The build kind stays named `stable` and `dev` keeps targeting the staging API: the stable→staging rename and the dev→api.dev retarget are intentionally NOT part of this change (staging already ships per-build data isolation, build-kind → environment resolution, and renderer origin-derivation). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…odel - build-macos-pkg.yml / build-windows-installer.yml pass COWORK_BUILD_KIND so gen-build-channel.mjs's build-time guard fails a build whose kind and minds_api_url target different environments (e.g. preview baked against prod). - README documents the four build kinds and the OTA/build-kind gating. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
pnewsam
force-pushed
the
paul/developer-platform-channels
branch
from
July 28, 2026 20:14
4466efd to
6c9db65
Compare
pnewsam
added a commit
that referenced
this pull request
Jul 28, 2026
…, dock icon Give each non-prod build kind a distinct RUNTIME identity so build kinds on one machine don't collide, building on the CHANNELS table from #385: - channels.ts: add per-kind `appName` (the Electron app name → userData dir) and `iconName` (the window/dock icon). prod is frozen to `anton` / `icon.png` so shipped installs keep their data and look unchanged. - app-identity.ts: a side-effect module imported FIRST in index.ts — calls app.setName for non-prod kinds only (before token-store reads userData at load) so localStorage, terms consent, and the token store isolate per channel. prod's app.name is never touched → byte-for-byte unchanged. - keychain-service.ts: namespace the keychain service per channel (`cowork-oauth-<kind>`); prod keeps the historical `cowork-oauth`. - app-icon.ts: pick the badged window/dock icon at runtime (the packaged bundle icon alone doesn't govern the running app's dock icon), falling back to the base icon.png if the asset is missing. The `stable` kind targets the staging env (see #385), so its user-facing labels read "Staging"; its invisible appId stays keyed to the kind. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What this does
Consolidates the build-"channel" configuration that had drifted across four files into a single source of truth —
src/main/channels.ts— and hardens build-kind resolution to fail closed.Before, each concern resolved the build kind on its own:
cowork-home.ts→ per-kind data home (~/.cowork-<kind>)minds-urls.ts→ MindsHub API hostserver-source.ts/server-updater.ts→ cowork-server ref + PyPI channelgen-build-channel.mjs(CI) → baked envNothing tied them together, so they could — and during this branch's own rebase, demonstrably did — drift out of sync. This PR replaces those scattered
switch (buildKind())sites with oneCHANNELStable that every layer reads and that CI validates against.Contents
CHANNELStable (channels.ts) — one row per kind (dev/preview/stable/prod) mapping home dir, API host, env slug, and server ref. Both the runtime and CI derive from it.build-config.jsonnow throws instead of silently falling back to prod (which would point a non-prod build at the production data home / API). A genuinely absent config still resolves to prod, so legacy releases keep working.gen-build-channel.mjsvalidates the bakedbuild_kind↔minds_api_urlpairing;channel-origins.mjsis a plain-JS mirror of the table (build scripts can't import the TS) with a drift-guard unit test so the mirror can't silently go stale.Context: overlap with already-merged work
The original "distinct envs / correct APIs / complete data isolation" goals were largely delivered independently while this PR was open — per-build data isolation (#312), build-kind → environment + PyPI channel (#461), renderer origin-derivation (#493). This PR is rescoped to what it still uniquely adds on top of that: the single-table consolidation, the fail-closed policy, and the drift guard.
Test plan
npm run typecheck(main + renderer + test) ✅npm test— 629 passing ✅npm run check:cowork-purity✅🤖 Generated with Claude Code