* Speed up Studio startup path
* Studio: recheck managed binary executability on preflight cache hit and ignore stale unauthenticated platform fetches
Preflight: a matching capability cache fingerprint no longer skips the
runnability check when the managed binary's executable bit was cleared
(size and mtime unchanged, since chmod bumps ctime not mtime). The cache
fast path now confirms the binary is still executable, otherwise it falls
back to the CLI help probe so preflight reports Stale and can repair,
instead of returning Ready and failing later at backend start. Adds a
regression test.
Frontend: now that first render is no longer gated on fetchDeviceType,
the initial unauthenticated health call can resolve after an
authenticated platform fetch. Guard the store so a late unauthenticated
or failed non-forced response cannot overwrite an already authoritative
device type, tunnel URL, or secure flag. Forced refreshes and the first
unauthenticated load are unaffected.
* Studio: use access(X_OK) for the preflight cache executability guard
A mode bitmask treats any execute bit as launchable, but the executable
bits can be set only for another owner or group, or be denied by an ACL,
so the current user could still hit PermissionDenied at launch and the
cached fast path would wrongly return Ready. access(X_OK) checks real
executability for the calling user, so an ownership or permission change
correctly falls back to the CLI help probe and the Stale repair path.
* Studio: ignore any stale non-forced platform fetch once authoritative
Extend the platform store guard so a non-forced health response never
overwrites an already authoritative result, not only unauthenticated
ones. With a saved token the post-render non-forced request can be
authenticated but older than a later forced refresh that already picked
up the tunnel URL and secure flag; if that earlier request resolves last
it would null those fields. Now any non-forced response is dropped once
the store holds a server-reported platform. Forced refreshes and the
first authoritative write are unaffected.
* Studio: run the managed CLI help probe before trusting the preflight cache
Restore running the managed CLI help probe before returning Ready from
the desktop capability cache, so a managed install whose venv interpreter
or a runtime dependency is broken (while path, size, mtime, and markers
are unchanged) is reported Stale for repair rather than proceeding to a
backend start that cannot spawn. The capability cache still skips the
heavier desktop-capabilities probe on a hit, so a warm cache runs one
probe instead of two. Removes the executable-access shortcut, which the
help probe now subsumes.
---------
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
* Speed up Studio desktop startup
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Address Studio startup review findings
* Keep orphaned run cleanup before readiness
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
---------
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>
* studio: expose launcher capability bits on unauth /api/health
PR #5375 reduced the unauthenticated /api/health response to {status,
timestamp} only, on the theory that the rest of the payload was useful
fingerprinting. That was too aggressive: the Tauri watchdog reads
`service == "Unsloth UI Backend"` and `studio_root_id` to re-adopt its
own backend across restarts (src-tauri/src/desktop_backend_owner.rs
and commands.rs), and the SPA bootstrap fetches the same payload
unauth to detect chat-only mode and native path lease support before
any token is available (frontend src/config/env.ts and
features/native-intents/use-native-readiness.ts). With the post-#5375
shape, the watchdog kills its own healthy backend, the SPA never
flips out of "full Studio" mode on chat-only Linux/Windows, and the
About tab shows "dev" in place of the real version.
The actual fingerprint-ish fields are `version` / `studio_version` /
`device_type` (and to a lesser extent the hostname inside
`device_type`). `service`, `studio_root_id` (already a hex digest of
the install path, not the raw path), `chat_only`, the desktop_*
capability flags, and `native_path_leases_supported` do not leak the
install path or version.
This patch keeps the auth gate but rebalances which fields sit on
each side of it:
unauth service, studio_root_id, chat_only, desktop_protocol_version,
desktop_manageability_version, supports_desktop_auth,
supports_desktop_backend_ownership, native_path_leases_supported,
desktop_owner (when present)
authed + version, studio_version, device_type
Existing must-change-password sessions still fall through to the base
payload because get_current_subject (strict) rejects them; that
matches prior behaviour.
test_middleware.py is updated to pin the new contract: launcher bits
present unauth, fingerprint fields present only with a valid bearer.
* studio: complete launcher-bits health unauth contract on Tauri + About tab
Reviewer follow-ups to the unauth /api/health launcher bits split.
Tauri preflight:
backend_capability_stale_reason() fell through to
backend_version_stale_reason(health.version.as_deref()) when capability
bits were present but version was absent. With the unauth payload now
exposing service + studio_root_id + desktop_* bits but gating version
behind a bearer, the desktop watchdog was reading the new payload,
parsing all capability bits, then classifying the same-root backend
as desktop_backend_version_missing and refusing to adopt it.
A backend that exposes desktop_protocol_version=1,
desktop_manageability_version>=1, supports_desktop_auth=true and
supports_desktop_backend_ownership=true was introduced together with
MIN_DESKTOP_BACKEND_VERSION=2026.5.3 in #5341, so a present capability
bitset is itself a version-compatibility signal. Skip the version
sub-check when version is None/empty; keep it for non-empty values
so genuinely-too-old backends that do echo a version still get
desktop_backend_version_too_old.
About tab:
fetchStudioVersions() did a bare fetch(apiUrl("/api/health")), which
the unauth payload no longer carries version/studio_version for, so
Settings -> About kept rendering "dev"/"dev" for any logged-in user.
Attach Authorization: Bearer <token> when getAuthToken() returns one;
fall back to bare fetch (still 200, just truncated payload) for the
not-logged-in case. No new endpoint.
Comment:
studio_root_id is no longer a hex digest of the install path; it is
an opaque per-install id written by the launcher. Updated the inline
comment to match.
Test:
- python -m pytest studio/backend/tests/test_middleware.py::TestHealthAuthGate studio/backend/tests/test_desktop_auth.py -q
-> 29 passed
- npm run typecheck clean, npm run build produces fresh dist
* Trigger CI rerun for flaky Mac Chat UI step
* Harden Tauri backend preflight and startup
Require managed Studio root IDs to match before attaching to existing backends, close the concurrent backend-start window, and tighten frontend Tauri detection to Tauri-specific signals.
* Add Tauri backend manageability guards
Gate desktop backend compatibility on explicit manageability fields, add external-conflict handling for unsafe backend states, and protect update/repair paths from mutating active non-owned Studio backends. Track Tauri-owned backends with local owner metadata for verified orphan cleanup only.
* Split Tauri preflight probes into modules
Move preflight types, version checks, managed install probing, and backend probing into focused submodules while preserving behavior and keeping implementation files under the release-readiness size target.
* Use desktop-specific Tauri updater channel
Point the desktop updater at a same-repo desktop-latest manifest and publish that channel from non-draft desktop releases after validating the Tauri-generated latest.json.
* Add Linux desktop update policy
* Add owned backend lifecycle guards
* Adopt verified desktop-owned backends
* Validate desktop backend readiness
* Trim Tauri release hardening code
* Require desktop backend 2026.5.3
* Handle desktop backend edge cases
* Fail stalled desktop backend startup
* Fix desktop update edge cases
* Avoid secret-gating adopted watchdog
* Fix desktop update comparison guards
* Automate desktop release versioning
* Serialize desktop release workflow
* tests: follow preflight.rs split into preflight/{backend,managed,types,version}.rs
PR #5341 splits studio/src-tauri/src/preflight.rs into a directory of
submodules. The cmd.env_remove("UNSLOTH_STUDIO_HOME") + STUDIO_HOME
calls now live in preflight/managed.rs instead of preflight.rs, so
test_tauri_preflight_scrubs_studio_home_env counted zero matches in
the old single-file location and failed with "assert 0 >= 2".
Read whichever shape is on disk: preflight.rs at the old path plus
every *.rs under preflight/ (current PR has 2 occurrences in
preflight/managed.rs). The guard intent is unchanged: at least 2
env_remove calls covering run_cli_probe and probe_cli_capability,
plus the single commands.rs scrub in check_install_status. Verified
locally: pytest tests/test_studio_install_workspace_guard.py::test_tauri_preflight_scrubs_studio_home_env passes.
* [pre-commit.ci] auto fixes from pre-commit.com hooks
for more information, see https://pre-commit.ci
* Avoid browser Tauri hostname detection
* Restore shutdown flag after failed stop
---------
Co-authored-by: Daniel Han <danielhanchen@gmail.com>
Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com>