lazy backend boot and rename workspace to project

Backend isolate now boots in two phases: resolve toolchain on spawn
(binary availability only), initialize services on project.open. The
dispatcher stays empty until a project activates — IPC requests before
that return "No project active".

Scheduler ticker only runs while a project is open. Fires an immediate
first cycle on ProjectOpened so sidebar panels refresh without waiting
for the next interval. Stops on ProjectClosed.

Renamed workspace → project throughout backend messages
(project.validate, project.open, project.ready), callbacks
(onProjectOpen, onValidateProject), and methods
(openProject, validateProject, resolveProject).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jeroen Schweitzer
2026-04-26 14:16:46 +02:00
co-authored by Claude Opus 4.6
parent a89910dc36
commit 5515d88407
9 changed files with 367 additions and 204 deletions
+30 -7
View File
@@ -36,9 +36,27 @@ class SchedulerService {
Isolate? _isolate;
ReceivePort? _port;
StreamSubscription<dynamic>? _sub;
StreamSubscription<dynamic>? _projectSub;
/// Listen for project lifecycle events. The periodic ticker only runs
/// while a project is open — no wasted cycles on the welcome screen.
void start() {
if (_isolate != null) return;
_projectSub = _events.on<ProjectOpened>().listen((_) => _startTicker());
_events.on<ProjectClosed>().listen((_) => _stopTicker());
}
/// Start the periodic ticker and fire an immediate first cycle so
/// all panels refresh without waiting for the first interval.
void _startTicker() {
_stopTicker();
// Immediate first tick for all tiers.
for (final tier in SchedulerTier.values) {
if (tier == SchedulerTier.midnight) continue;
_events.emit(SchedulerTick(tier: tier));
}
// Then start the periodic isolate.
_port = ReceivePort();
_sub = _port!.listen((msg) {
if (msg is String) {
@@ -49,6 +67,15 @@ class SchedulerService {
Isolate.spawn(_isolateEntry, _port!.sendPort).then((iso) => _isolate = iso);
}
void _stopTicker() {
_sub?.cancel();
_port?.close();
_isolate?.kill(priority: Isolate.immediate);
_isolate = null;
_port = null;
_sub = null;
}
static void _isolateEntry(SendPort send) {
int lastDay = DateTime.now().day;
for (final tier in SchedulerTier.values) {
@@ -67,11 +94,7 @@ class SchedulerService {
}
void dispose() {
_sub?.cancel();
_port?.close();
_isolate?.kill(priority: Isolate.immediate);
_isolate = null;
_port = null;
_sub = null;
_projectSub?.cancel();
_stopTicker();
}
}