Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
55 commits
Select commit Hold shift + click to select a range
06ead1c
feat(desktop): observe daemon and always-on state
roodboi Nov 14, 2026
333dc26
merge: incorporate core observation and refresh safety fixes
roodboi Nov 14, 2026
412781f
fix(desktop): preserve independent daemon version evidence
roodboi Nov 14, 2026
bef1940
merge: unify machine recovery gate with core unavailable states
roodboi Nov 14, 2026
d0e552f
fix(desktop): gate daemon recovery through ordered fresh reads
roodboi Nov 14, 2026
dd7c11a
merge: retain exited replica failures and shared recovery gate
roodboi Nov 14, 2026
aa53b94
docs(desktop): show daemon and always-on fixture evidence
roodboi Nov 14, 2026
cc93d28
fix(desktop): accept API-running daemon without tracked PID
roodboi Nov 14, 2026
e8ad96c
merge: integrate setup with shared daemon machine-action gate
roodboi Nov 14, 2026
7c0a59e
fix(desktop): verify unified setup and daemon action admission
roodboi Nov 14, 2026
36397ca
merge: integrate setup (Doctor, next e47681ea) into daemon-state
roodboi Nov 15, 2026
5b69dda
fix(desktop): describe read-only always-on without internal request ids
roodboi Nov 15, 2026
4b0cb71
merge: integrate setup (Doctor interpreter check) into daemon-state
roodboi Nov 15, 2026
86256cf
merge: integrate setup (Doctor shebang argument check) into daemon-state
roodboi Nov 15, 2026
ed5fac2
merge: integrate setup (Doctor shebang allowlist) into daemon-state
roodboi Nov 15, 2026
3383741
merge: integrate setup (Doctor interpreter option check) into daemon-…
roodboi Nov 15, 2026
76b12d1
merge: integrate setup (narrowed Doctor shebang rule) into daemon-state
roodboi Nov 15, 2026
c0abb7e
fix(desktop): run daemon actions through setup's checked context and …
roodboi Nov 15, 2026
3265f85
merge: integrate setup (CLI pinning, XDG_CONFIG_HOME, Mutagen interpr…
roodboi Nov 15, 2026
d4959d6
merge: integrate setup (CLI interpreter check) into daemon-state
roodboi Nov 15, 2026
860506a
fix(desktop): keep a failed Setup action's message through its recheck
roodboi Nov 15, 2026
b19cdc0
merge: integrate setup (next 345dc0d1: #835, #831) into daemon-state
roodboi Nov 15, 2026
c52572c
test(desktop): answer the version --json envelope in fake CLIs
roodboi Nov 15, 2026
77f8786
merge: integrate setup (Doctor pin, Docker selectors, adapter check) …
roodboi Nov 15, 2026
58e0e96
fix(desktop): wait for daemon responses with poll instead of SO_RCVTIMEO
roodboi Nov 15, 2026
ee1d5ca
fix(desktop): end the daemon read wait at its deadline across interrupts
roodboi Nov 15, 2026
1416a3e
merge: integrate setup (terminal temp root check) into daemon-state
roodboi Nov 15, 2026
ba9ae8c
merge: integrate setup (canonical terminal temp root) into daemon-state
roodboi Nov 15, 2026
c221d91
merge: integrate setup (Doctor startup-variable strip) into daemon-state
roodboi Nov 15, 2026
e6ad605
merge: integrate setup (loader-variable strip) into daemon-state
roodboi Nov 15, 2026
3086600
merge: integrate setup (loader-variable test) into daemon-state
roodboi Nov 15, 2026
5cabf17
merge: integrate setup (next 34b50534: Env tab) into daemon-state
roodboi Nov 15, 2026
beb76b1
merge: integrate setup (explicit CLI check, native selectors) into da…
roodboi Nov 15, 2026
32063b9
merge: integrate setup (verified checked runner) into daemon-state
roodboi Nov 15, 2026
75120bf
merge: integrate setup (Doctor local-endpoint check) into daemon-state
roodboi Nov 15, 2026
065c2bc
merge: integrate setup (Doctor preflight selector fix) into daemon-state
roodboi Nov 15, 2026
23b2abb
merge: integrate setup (exact Docker context lookup) into daemon-state
roodboi Nov 15, 2026
ccb9e19
merge: integrate setup (context ACL check) into daemon-state
roodboi Nov 15, 2026
7b041c8
merge: integrate setup (daemon self-invocation, global config selecto…
roodboi Nov 15, 2026
24a641c
merge: integrate setup (unreadable-tool refusal) into daemon-state
roodboi Nov 15, 2026
f67625b
fix(desktop): keep the CLI's stdout diagnostics when a daemon action …
roodboi Nov 15, 2026
0e52ea6
merge: integrate setup (dirname wrapper, pinned copied commands) into…
roodboi Nov 15, 2026
1d0ca4c
Merge commit '77f25083871dfc52f450a1a68934858cac57b611' into HEAD
roodboi Nov 15, 2026
b9d8ca0
Merge commit '865df7f8563c06b80afef14a7fa45af17081c048' into HEAD
roodboi Nov 15, 2026
7ad07fb
Merge commit '8f0cd942c733b54d0f60a9fedba3590565168b55' into HEAD
roodboi Nov 15, 2026
710053c
Merge commit '3ee37641ade9b4c0b2a358931301a7bff27d5d00' into HEAD
roodboi Nov 15, 2026
77986a3
Merge commit 'd6eb90d974b3b1c4485efec771a38524396cf835' into HEAD
roodboi Nov 15, 2026
f35cf02
Merge commit 'db5642f93d51de523551b60a798d4d205c94322d' into HEAD
roodboi Nov 15, 2026
f6b2e14
Merge commit '6f47a1cbc44b9a005d40f46038a0b4a0060454cd' into HEAD
roodboi Nov 15, 2026
de04bb6
Merge commit 'b15338c609a8d98fb5ae03ee3597eaff3efa465f' into HEAD
roodboi Nov 15, 2026
90be8d0
Merge commit '20e79a96646bdd80e1bd140c188618aef1e0f4bf' into HEAD
roodboi Nov 15, 2026
e4d6b6d
Merge commit 'ac40fbbe9298ccc4af5cfde2724e1014419004fc' into HEAD
roodboi Nov 15, 2026
afaff59
Merge commit 'f601833c583f32cdeba98b034e60d3186660d57c' into HEAD
roodboi Nov 15, 2026
97f369a
Merge commit '52cef18415d494b77a032449c7788845bd9a09f7' into HEAD
roodboi Nov 15, 2026
d838bb1
Merge commit 'c886c226374e4f9762a5dfbbde99ca48325fe844' into HEAD
roodboi Nov 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
66 changes: 62 additions & 4 deletions apps/desktop/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -215,9 +215,67 @@ observations, not locks against concurrent same-user changes.

## What v0 does not do yet

The Activity tab, CPU and memory, always-on, broader daemon controls and
upgrades come in later units. The design and the CLI/daemon requests they need
are in `DESIGN.md` (PR #800).
The Activity tab, CPU and memory, and upgrades come in later units. The design
and the CLI/daemon requests they need are in `DESIGN.md` (PR #800).

## Daemon and always-on preview (DESIGN §5–6)

The daemon card observes `hack daemon status --json`, retaining a valid raw report
on exit 1. Missing or malformed status is **unknown**; an omitted
`service.installed` is not evidence that the login service is absent. Version
mismatch comes from the read-only socket `/v1/status`, compared with the CLI;
the socket is the absolute `socket_path` in the CLI's own status answer, never a
guessed path. Daemon reads use the same pinned CLI and allowlisted environment
as project reads.
Explicit R8 `service.unit_stale: true` shows an outdated login service; missing
R8 metadata never triggers that diagnosis.

Start, Restart and Install login service run today's `hack daemon
start|restart|install --no-interactive` as the current user, through setup's
checked CLI and execution context (never the app's own environment). Install
re-checks its guards and a fresh `daemon status` immediately before running.
Without a checked setup report no daemon action runs. They require no
terminal adapter and never request a password. Only one daemon action runs at a
time, including its fresh read after command exit. Exit 0 alone does not mean
running or installed. Failure keeps observed state and an error; timeout shows
unknown until another read. Missing CLI disables actions. Reads have deadlines;
actions have a two-minute deadline. The app never edits units, signals a daemon,
or starts one during observation. Only the CLI owns those effects.

Setup Run, the daemon card and recovery share the same machine-action gate and
fixed CLI adapter. Project actions and terminal launches check that gate; daemon
actions also check setup's launch gate. Each result refreshes ordered project
status and setup (queuing a fresh setup check if an older check is in flight).
The existing setup terminal adapter remains owned by setup.

Always-on consumes proposed project `autostart` and global `autostart_global`
fields when present, including manual-stop pause, failure count/limit/services,
global pause and login progress. Missing fields remain unknown. All always-on
switches are read-only: live R12a command capability is not established, and
current payloads lack R12b metadata. No proposed autostart command, local policy,
persistence or retry loop is implemented. No login service additionally explains
`hack daemon install`; missing `service_ready` never enables a switch.

Additional fixtures: `daemon-not-installed`, `daemon-stopped`, `daemon-starting`,
`daemon-incompatible`, `daemon-stale`, `daemon-stale-unit`, `daemon-unknown`,
`always-off`, `always-on`, `always-paused`, `always-attention`,
`always-global-paused`, `always-login`, `always-no-login`,
`always-command-failed` (failure presentation only; no command runs).
Fixture buttons never mutate a live system. To capture one fixture in both themes:

```sh
cargo run -p hack-desktop --features screenshots -- \
--fixtures daemon-stale-unit --screenshots /tmp/daemon-shots
```

The daemon card is independent of project reads, so a project error does not
hide its observed status. Automatic daemon reads run at most every ten seconds;
post-action reads are immediate. Existing project polling remains unchanged.
R8 and R12 remain CLI/daemon work outside this desktop-only slice. This Linux
preview does not qualify native macOS service behavior or a shipped artifact.

Fixture views: [daemon repair (light)](screenshots/daemon-stale-unit-light.png)
and [always-on needs attention (dark)](screenshots/always-attention-dark.png).

## Setup

Expand Down Expand Up @@ -258,7 +316,7 @@ Needs attention. Missing service-manager availability never permits Install.
Start and Install, and the project pane's Start/Restart hackd recovery, pass the
selected CLI as the CLI's self-invocation selector (`HACK_MCP_COMMAND`), so the daemon they start or install runs that CLI, not
whatever `hack` is first on PATH. Only the fixed `daemon start` and supported `daemon install` actions offer **Run**, with
bounded, memory-only output followed by fresh checks. Install requires explicit
bounded, memory-only exit/error feedback followed by fresh checks. Install requires explicit
`service.available: true`, an ordinary home, and no launchd/systemd-disable guard;
otherwise the card explains why and links to documentation. Availability is checked
again immediately before install; an unavailable or unconfirmed manager is refused
Expand Down
10 changes: 7 additions & 3 deletions apps/desktop/app/src/screenshots.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ use hack_desktop_core::logs::fixtures::LogsFixture;

use crate::view::HackApp;

pub fn run(dir: &Path, fixture: Option<Fixture>) {
pub fn run(dir: &Path, selected: Option<Fixture>) {
std::fs::create_dir_all(dir).expect("create screenshot dir");
let mut cx = HeadlessAppContext::with_platform(
gpui_kit::platform::current_platform(true).text_system(),
Expand All @@ -35,7 +35,11 @@ pub fn run(dir: &Path, fixture: Option<Fixture>) {
shots.push((l.name(), base, Some(l)));
}
for mode in [ThemeMode::Light, ThemeMode::Dark] {
for name in hack_desktop_core::setup_fixtures::NAMES {
for name in hack_desktop_core::setup_fixtures::NAMES
.iter()
.copied()
.filter(|_| selected.is_none())
{
cx.update(|cx| Theme::change(mode, None, cx));
let mut opts = crate::window_options();
opts.focus = false;
Expand Down Expand Up @@ -83,7 +87,7 @@ pub fn run(dir: &Path, fixture: Option<Fixture>) {
}
for &(name, f, logs) in shots
.iter()
.filter(|(_, f, logs)| fixture.is_none_or(|selected| selected == *f && logs.is_none()))
.filter(|(_, f, logs)| selected.is_none_or(|s| s == *f && logs.is_none()))
{
cx.update(|cx| Theme::change(mode, None, cx));
let opts = WindowOptions {
Expand Down
30 changes: 9 additions & 21 deletions apps/desktop/app/src/setup_controller.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ impl HackApp {
self.setup.refresh_again = true;
return;
}
if self.setup.action_busy || self.recovery_busy {
if self.setup.action_busy || self.daemon.busy.is_some() {
return;
}
let generation = self.setup.begin();
Expand Down Expand Up @@ -64,7 +64,7 @@ impl HackApp {
if self.setup.report.cli_ready()
&& !self.setup.loading
&& !self.setup.action_busy
&& !self.recovery_busy
&& !self.daemon.busy.is_some()
{
self.pane = Pane::Project;
cx.notify();
Expand Down Expand Up @@ -93,25 +93,13 @@ impl HackApp {
cx.notify();
return;
}
self.setup.result = None;
self.setup.action_busy = true;
let source = self.source.clone();
let checked = self.setup.report.clone();
cx.spawn(async move |this, cx| {
let result = cx
.background_executor()
.spawn(async move { source.setup_action(action, &checked) })
.await;
let _ = this.update(cx, |this, cx| {
this.setup.action_busy = false;
this.setup.result = Some(result);
this.setup.pending_fix = true;
this.refresh_now_async(cx);
this.load_setup(cx);
});
})
.detach();
cx.notify();
self.run_daemon_action(
match action {
hack_desktop_core::setup::SafeAction::StartDaemon => DaemonAction::Start,
hack_desktop_core::setup::SafeAction::InstallDaemon => DaemonAction::Install,
},
Comment thread
roodboi marked this conversation as resolved.
Resolved
cx,
);
}
pub fn open_setup_terminal(&mut self, args: Vec<String>, cx: &mut Context<Self>) {
use hack_desktop_core::terminal::{Platform, Script, SystemLauncher, linux_adapter};
Expand Down
Loading
Loading