11 KiB
status, created, owner
| status | created | owner |
|---|---|---|
| shipped | 2026-07-31 | kandev |
npm nightly channel
Why
Users who want fixes from main currently have to build Kandev themselves or wait for the next
stable release. Maintainers also lack one supported prerelease path that exercises the same npm
launcher and native runtime packages users install in production.
What
- Kandev publishes an npm nightly from
mainat 12:00 UTC whenmaincontains commits after the latest stable release and that exact commit has not already been published. - Maintainers can run the same Nightly path manually from
main. A manual Nightly dry run performs metadata and registry preflight only, with no platform builds or npm writes. - A nightly for stable
X.Y.Zand commitabcdef123456...has versionX.Y.(Z+1)-nightly.shaabcdef123456. - The 12-hex abbreviation is an accepted compactness trade-off. Before an existing version can skip publication, Git must resolve that abbreviation to the exact scheduled commit; an ambiguous prefix or identity mismatch fails closed for maintainer resolution.
- Every nightly publishes
kandevand all five@kdlbs/runtime-*packages at the same immutable version under the npmnightlydist-tag. The stablelatestdist-tag does not move. - Users persistently install the channel with
npm install -g kandev@nightly. The commandnpx -y kandev@nightlyruns a transient Nightly copy without changing a global installation. - Settings > System > Updates exposes Stable and Nightly for a verified, Kandev-managed npm or npx user service. Stable is the default and the setting is install-wide.
- An npx run is managed only after it installs the service (for example,
npx -y kandev@nightly service install); a transientnpx -y kandev@nightlyrun alone neither creates a managed service nor persists an update-channel selection. - Desktop, Homebrew, Scoop, local-checkout, unknown, unmanaged, and system-service installations remain on the Stable channel.
- Stable update discovery continues to use GitHub Releases. Nightly discovery follows npm's
kandev@nightlydist-tag. - Applying an npm/npx update submits the exact version shown in the Updates page. The backend
accepts it only while it still matches the selected channel's cached target, then installs that
immutable version; a changed cache returns
409 Conflictand requires a refresh. - Nightly publication creates no Git tag, GitHub Release, changelog commit, desktop update, container tag, Homebrew update, or Scoop update. Scoop remains a Stable-only channel.
Decision: ADR-2026-07-31-npm-nightly-release-channel.
Implementation plan: npm nightly channel.
Data model
The install-wide settings table contains key updates_channel. Its value is the UTF-8 string
stable or nightly; a missing or invalid value reads as stable.
The existing stable target cache remains in these kandev_meta keys:
latest_versionlatest_version_urllatest_version_checked_at
Nightly uses an isolated cache:
latest_version_nightlylatest_version_nightly_urllatest_version_nightly_checked_at
Changing channel never reinterprets cached data from the other source.
API surface
GET /api/v1/system/updates and POST /api/v1/system/updates/check retain their current fields
and add:
{
"channel": "stable",
"channel_editable": true,
"channel_unsupported_reason": ""
}
PATCH /api/v1/system/updates/channel accepts:
{ "channel": "nightly" }
It returns the complete updates response for the selected channel. Invalid channel names return
400. Selecting Nightly for an unsupported installation returns 409. Persistence or resolver
failures return 500 or 502 using the existing System handler error conventions.
The npm registry contract is the public metadata document for package kandev. The resolver reads
dist-tags.nightly, requires a valid SemVer, and requires the same version to exist in versions.
Permissions
Anyone allowed to view System settings may read update status. The existing admin guard applies to manual checks, channel changes, and update application. Channel choice is shared by all users of the installation.
Failure modes
- A GitHub or npm discovery failure preserves that channel's previous cache and surfaces the stale checked time plus the request error.
- Apply never re-resolves a moving channel tag. It locks the selected-channel cache, rejects a
submitted
target_versionthat no longer matches, and writes the matching exact version into the update intent. - A malformed or missing npm
nightlytag fails closed; it is never offered or installed. - A failed channel save keeps the draft dirty and surfaces that save failure instead of retaining a stale manual-check error or retry countdown.
- A scheduled run with no commits after the stable tag exits successfully without building.
- A manual Nightly dispatch from a ref other than
mainfails before resolving metadata. - Manual Nightly rejects the Stable-only desktop-validation and backfill modes. The workflow's
required bump selector is ignored for Nightly, while
dry_runremains supported. - A scheduled retry for an already-published commit exits successfully only when the main package
and
nightlytag agree. - Before building, the workflow resolves the commit prefix in the current
nightlytag. A rerun behind a newer published commit is superseded and exits without building. The same commit skips only when every package exists and itsnightlytag matches; an incomplete current target proceeds to repair. Divergent or unresolvable history fails closed. - A 12-hex collision makes Git abbreviation resolution ambiguous, so the run fails closed instead of treating the colliding commit as already published.
- Stable and Nightly workflow runs share one non-cancelling release-wide concurrency group. This covers Stable tag creation through npm publication, so Nightly cannot derive a version while a newly tagged Stable release is still pending on npm.
- Before building and again before publishing, Nightly requires the highest stable Git tag to match
kandev@latest. A pending Stable tag or a scheduled commit superseded by the current Stable tag exits successfully without publishing. - Before publishing, a Nightly run rechecks
kandev@latest. If the baseline moved while Nightly was building, the stale run exits without publishing. - The same locked preflight requires
kandev@nightlyto equal the value observed before building; an overlapping run that already moved the tag supersedes the stale run. - Runtime packages publish before
kandev. If any runtime fails, the main launcher is not published, so no visible launcher references missing exact dependencies. - Trusted-publisher OIDC is used only by
npm publish --tag nightly. An existing version whosenightlytag does not match fails with recovery guidance because OIDC cannot runnpm dist-tag add. - GitHub's scheduled start may be later than 12:00 UTC; delayed execution does not change the deterministic version.
Persistence guarantees
- Channel choice and both target caches survive backend restarts.
- One source failure never overwrites the other source's cache.
- Each full commit deterministically maps to one npm version, so a retry never creates a second version for the same source state. The accepted 12-hex collision case halts automatic publication rather than mapping a second commit to the existing package.
- npm nightly versions are immutable and retained; this feature performs no automated deletion.
Scenarios
- GIVEN stable
0.82.0andmaincommitabc123def456..., WHEN the nightly schedule runs, THEN all six packages publish as0.82.1-nightly.shaabc123def456undernightlyandkandev@latestis unchanged. - GIVEN an eligible
maincommit, WHEN a maintainer manually dispatches the Nightly channel, THEN the same metadata, shared builds, safeguards, and npm publication run as the schedule. - GIVEN a manual Nightly with
dry_run=true, WHEN its preflight resolves an eligible target, THEN the run reports that exact version and commit without building bundles or changing npm. - GIVEN a manual Nightly dispatched from a non-
mainref or combined with desktop validation or backfill, WHEN validation runs, THEN it fails with an actionable input error. - GIVEN
mainpoints at the latest stable tag's commit, WHEN the nightly schedule runs, THEN it exits successfully without a platform build or npm publication. - GIVEN the highest Stable Git tag is newer than npm
latest, WHEN Nightly prepares or publishes, THEN it exits successfully rather than deriving from the stale npm baseline. - GIVEN a Nightly schedule was queued before a Stable release superseded its commit, WHEN that Nightly starts after Stable completes, THEN it exits successfully without building.
- GIVEN the current
mainnightly already exists andkandev@nightlypoints to it, WHEN the schedule runs again, THEN it exits successfully without rebuilding. - GIVEN a previous run published only some runtime packages, WHEN the same commit retries,
THEN matching packages are accepted, missing packages publish, and
kandevpublishes last. - GIVEN an older run left only some runtime tags advanced, WHEN a later
maincommit runs, THEN ancestor-tagged partial packages are repaired by the later publish without manual tag edits. - GIVEN no channel setting, WHEN a user opens Updates, THEN Stable is selected and the target comes from GitHub Releases.
- GIVEN a verified npm managed user service, WHEN an admin selects and saves Nightly,
THEN the setting survives reload and the target resolves from
kandev@nightly. - GIVEN a Nightly save is pending, WHEN the admin changes the draft back to Stable before the response arrives, THEN the returned Nightly state becomes the saved baseline while the newer Stable draft remains selected, dirty, and available for a follow-up save.
- GIVEN a Homebrew, Desktop, unmanaged, system-service, local, or unknown installation, WHEN Updates renders, THEN Nightly is unavailable with a visible reason and Stable stays effective.
- GIVEN an installed nightly whose SHA sorts lexically after the new target SHA, WHEN npm's
nightlytag changes, THEN the new unequal target is offered; SHA text is not treated as a chronological counter. - GIVEN a user running a nightly selects Stable, WHEN a valid stable target differs, THEN the UI offers an explicit return to that exact stable version without announcing it as a normal upgrade notification.
- GIVEN the Updates page shows target A and discovery changes the selected-channel cache to B,
WHEN the user submits Apply for A, THEN the backend returns
409 Conflictand installs neither target. - GIVEN a Pixel 5 viewport, WHEN the user selects Nightly and saves, THEN the same persisted outcome is reachable through 44px rows with no horizontal document overflow.
Out of scope
- Homebrew
HEAD, a nightly formula, or a second tap - Desktop nightly updater feeds
- GHCR nightly tags
- Nightly GitHub Releases or Git tags
- Automatic update application
- Per-user channels
- Additional beta/canary channels
- Timestamped versions