main: boot the daemon at the last project, not HOME (T-352)

Confirmed root cause of the sidebar failure: a desktop launch starts in
HOME, which isn't a git repo, so resolveWorkspaceRoot returns HOME and
the daemon's pql/git/files all target HOME. pql then finds a stale
~/.pql/pql.db (left from earlier HOME-workdir runs) and errors
"pql.db is from an earlier schema" — exactly what the sidebars showed.
A manual refresh worked only because by then the workspace had swapped
to the repo. (Verified directly: `cd $HOME && pql ticket list` reproduces
the schema error against ~/.pql/pql.db.)

Settings confirmed the user is in the picker path (no startupSticky on
any recent), so nothing auto-opens the repo at boot and the daemon sits
on HOME until the project is opened.

Fix: resolve the startup workspace before boot — prefer the launch CWD
when it's a git repo, else fall back to app.lastProject (the repo). The
daemon now points at the real workspace from its first request, so the
sidebars load immediately without depending on swap/refetch timing. The
ProjectOpened refetch and swap serialization from 2.3.x stay in place for
genuine mid-session project switches.

Pure resolveStartupWorkspace() helper is unit-tested; app boot green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 12:32:18 +02:00
co-authored by Claude Opus 4.8
parent 46f84728d2
commit fc4021e98b
6 changed files with 86 additions and 3 deletions
+17 -1
View File
@@ -83,6 +83,22 @@ Future<void> main() async {
final appDir = await _resolveAppDir();
final themes = await _loadBundledThemes();
// Resolve the workspace to boot the daemon at. A desktop launch starts in
// HOME (not a git repo), so atCwd would point pql/git/files at HOME — where
// pql hits a stale ~/.pql/pql.db and the sidebars error on first load. Boot
// at the last project instead so the daemon targets the real repo from the
// first request. (T-352)
Directory startupWorkRoot = resolveWorkspaceRoot(Directory.current);
if (!kIsWeb) {
final bootSettings = SettingsStore(appDir: appDir);
await bootSettings.load();
startupWorkRoot = resolveStartupWorkspace(
cwdRoot: startupWorkRoot,
lastProject: bootSettings.get<String>('app.lastProject'),
isGitRepo: (d) => Directory('${d.path}/.git').existsSync(),
);
}
// Resolve toolchain + boot daemon inline — same as Linux.
// With proper signing (Developer ID), no sandbox or isolate needed.
final toolchain = Toolchain();
@@ -297,7 +313,7 @@ Future<void> main() async {
daemonBus = events;
kernelArrangement = arrangement;
kernelPanels = panels;
final workRoot = FilesService.atCwd(events: _BusEventSink(events)).root;
final workRoot = startupWorkRoot;
final dispatcher = buildDispatcher(events, toolchain, workRoot, arrangement, panels);
// Build the client at the workspace's socket path. The
// server is started below (swapIpcServer) which the
+20 -2
View File
@@ -33,7 +33,7 @@ class FilesService {
/// Build from the current working directory, walking up to the git
/// root if present. Falls back to CWD otherwise.
factory FilesService.atCwd({required DaemonEventSink events}) {
final root = _resolveWorkspaceRoot(Directory.current);
final root = resolveWorkspaceRoot(Directory.current);
return FilesService(root: root, events: events);
}
@@ -148,7 +148,7 @@ void registerFilesCommands(DaemonDispatcher d, FilesService files) {
// ---------------------------------------------------------------------------
Directory _resolveWorkspaceRoot(Directory start) {
Directory resolveWorkspaceRoot(Directory start) {
Directory cur = start.absolute;
for (var i = 0; i < 64; i++) {
final g = Directory('${cur.path}/.git');
@@ -160,6 +160,24 @@ Directory _resolveWorkspaceRoot(Directory start) {
return start.absolute;
}
/// Pick the workspace root to boot the daemon at.
///
/// Prefer the launch CWD when it's inside a git repo. Otherwise — desktop
/// launches start in HOME, which isn't a repo — fall back to the last opened
/// project so pql/git/files target the real workspace from the very first
/// request. Booting at HOME instead makes pql run there and hit a stale
/// `~/.pql/pql.db`, so the ticket/decision sidebars error on first load and
/// only recover once the project is (re)opened. Falls back to [cwdRoot] when
/// there's no valid last project. (T-352)
Directory resolveStartupWorkspace({required Directory cwdRoot, required String? lastProject, required bool Function(Directory) isGitRepo}) {
if (isGitRepo(cwdRoot)) return cwdRoot;
if (lastProject != null && lastProject.isNotEmpty) {
final dir = Directory(lastProject);
if (isGitRepo(dir)) return dir;
}
return cwdRoot;
}
/// Build the default IgnoreSet: clide's always-hide list layered under
/// the `ignore_files:` chain from `.pql/config.yaml` (per D-4), in
/// order, later files winning. clide owns that config key (D-3) and