Skip to content

Complete immediate signed Stable/Developer transitions with rollback-safe shared data #152

Open

Description

@NOirBRight

Problem

The shared-state channel foundation in #148 can define ReleaseChannel, signed feed selection, and Developer/Stable release artifacts, but it deliberately cannot expose a channel selector that only changes the next feed. The accepted product contract requires an explicit channel choice to replace and restart the installed app immediately, including a signed Developer → Stable transition when the Stable SemVer is lower.

CodexHub currently has no minimal shared-data compatibility declaration, pre-install recovery backup, exact-target pending-transition check, or narrowly scoped downgrade authorization. Enabling the selector before all of those boundaries exist would create a half-enabled and potentially irreversible rollback path.

Accepted outcome

Enable the user-selectable Stable/Developer control only when CodexHub can complete an immediate signed transition safely. Stable → Developer installs the latest complete signed Developer candidate for the current BuildFlavor; Developer → Stable installs the signed supported Stable rollback target even when its SemVer is lower. Both preserve the one shared runtime/data identity and finish with exactly one app/Gateway owner.

Scope and invariants

  • Consume the typed shared-state ReleaseChannel and signed ReleaseChannel × BuildFlavor feed contract from Establish shared-state Stable/Developer channel feeds and release artifacts #148.
  • Expose Stable and Developer in Settings, show the effective channel/version, and require explicit confirmation that the app will replace/restart and active Codex Tasks may be interrupted.
  • Treat selection as an immediate transition. Do not persist a completed channel change merely as a preference for a later update check.
  • Fetch and verify the requested channel's signed manifest and reject mismatched channel, BuildFlavor, platform, target version, URL, signature metadata, or artifact identity before replacement.
  • Permit a lower version only for an explicit user-selected transition to the signed supported Stable rollback target. Normal checks, Developer transitions, and every other caller keep normal downgrade rejection.
  • Use Tauri updater's supported custom version-comparator/allow_downgrades seam without bypassing signature verification or adding an arbitrary-version installer.
  • Define the minimal versioned shared-data compatibility metadata required to prove that both the running build and the target Stable reader can use the shared state. Fail closed if either side is unknown or incompatible.
  • Keep Developer-written state readable by the currently supported Stable rollback target. Reject an irreversible Developer-only migration before candidate publication.
  • Create and verify an atomic recovery backup of the shared state before installation. Preserve that backup and the original data on download, validation, install, restart, or confirmation failure.
  • Make pending-transition completion exact-target-aware and channel-aware. A generic current_version >= target_version comparison is not success for a rollback.
  • Route update installation and channel transitions through Cancel active Gateway work within two seconds on explicit lifecycle stops #143 and Stop CodexHub-managed Gateway when the app fully exits #112: stop admissions, cancel active upstream work, allow no more than two seconds for connection close/diagnostic flush, then force-terminate only the ownership-verified current-session Gateway before replacement/restart.
  • Preserve eligible legacy Beta client backups through one bounded, idempotent migration or read-only compatibility fallback. Do not reactivate a legacy Beta home as a second live runtime, silently merge conflicting settings, or delete the legacy source.
  • Keep application identifier, installation path, runtime home, settings, Providers/models, history, client backups, ports, and Gateway ownership shared across channels.
  • Document that the first pre-channel-capable 0.1.6-dev.N builds require manual installation from v0.1.5.

Non-goals

  • Side-by-side Stable/Developer applications, separate active homes, separate ports, or concurrent Gateways.
  • Reintroducing Beta as a product, BuildFlavor, or runtime owner.
  • An arbitrary version picker, unsigned installer, generalized downgrade privilege, or custom signature bypass.
  • The full revisioned settings patch API or generated cross-language JSON Schema program; this Issue owns only the minimal compatibility metadata required for safe rollback.
  • Changing Provider routing, model semantics, Responses/Chat conversion, or diagnostic BuildFlavor behavior.
  • Publishing an incomplete Developer candidate instead of HOLD.

Acceptance criteria

  • The Stable/Developer selector is enabled only with the complete immediate-transition implementation; selecting a channel never leaves only a future-feed preference behind.
  • Stable → Developer installs the latest complete signed Developer candidate for the current BuildFlavor and confirms the exact channel/version after restart.
  • Developer → Stable immediately installs the manifest-declared signed supported Stable artifact for the current BuildFlavor even when Stable has a lower SemVer, then confirms the exact Stable channel/version.
  • A lower version is rejected unless the request is an explicit user-selected Stable transition and the signed manifest, BuildFlavor, platform, artifact, compatibility metadata, and supported rollback target all match.
  • Tampered, unsigned, cross-channel, cross-flavor, wrong-platform, stale-target, and incompatible-data manifests/artifacts fail before replacement with sanitized diagnostics.
  • A verified atomic shared-state backup exists before install; injected download/install/restart/confirmation failures preserve the backup and original shared data without creating a second active home.
  • Deterministic fixtures prove a Developer-written state remains readable by the supported Stable reader and a simulated irreversible migration is refused before publication.
  • Existing eligible legacy Beta client backups remain recoverable without continuing dual active roots or deleting the legacy source.
  • The transition uses the approved Cancel active Gateway work within two seconds on explicit lifecycle stops #143 warning/cancellation/two-second ownership-safe shutdown contract and Stop CodexHub-managed Gateway when the app fully exits #112 terminal path.
  • Stable and Developer retain one application identifier, installation, runtime home, settings, Providers/models, history, client backups, ports, and Gateway ownership.
  • Packaged restart leaves exactly one application/Gateway owner and listener, with exact pending-transition state reconciled.
  • Normal/debug remain BuildFlavors of each channel and cross-flavor artifacts are rejected.
  • Release documentation states that v0.1.5 users must manually install the early 0.1.6-dev.N builds until a channel-capable Stable is available.

Expected hotset

Dependencies and relationships

Blocked by #148. That foundation is itself blocked by #112, which consumes #143 after #139.

Related, not duplicate: #135 preserves the legacy Beta-backup reporter evidence but is closed because its dual-active-root premise is superseded. This Issue owns only the bounded compatibility input needed for one shared runtime.

Verification

  • cd src-tauri; cargo test --locked
  • cd src-tauri; cargo clippy --locked --all-targets -- -D warnings
  • cd frontend; npm run build; npm run test:ui-contract
  • python -m pytest -q
  • powershell -File scripts/e2e-app-update.ps1 -Flavor normal -ValidateOnly, extended with Stable/Developer, exact-target, lower-version rollback, compatibility, backup, and failure-injection cases
  • python scripts/report_quality_gates.py (report-only; non-blocking)

Execution contract

Execution-Contract: v2
Verification-Class: strict
Verification-Commands: cd src-tauri; cargo test --locked; cd src-tauri; cargo clippy --locked --all-targets -- -D warnings; cd frontend; npm run build; npm run test:ui-contract; python -m pytest -q; powershell -File scripts/e2e-app-update.ps1 -Flavor normal -ValidateOnly; python scripts/report_quality_gates.py (report-only)
Manual-Evidence: Signed packaged Windows Normal smoke preserving a known shared-state fixture across Stable → Developer → lower-version Stable; verify manifest/signature/channel/flavor/compatibility decisions, backup existence, exact post-restart target confirmation, and exactly one application/Gateway owner and listener after each transition.
Architecture-Decision: resolved
Review-Owner: orchestrator

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or requestready-for-agentFully specified, ready for an AFK agentwayfinder:taskWayfinder implementation task

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions