surface kernel notifications as toasts (T-382)

The Notifications service had zero widget consumers — anything pushed
through ctx.notify (cli_install's dogfood warnings, install results)
accumulated in a list nothing rendered. The service now takes the
kernel MessageBus and publishes each notification to the toast
channel with mapped severity, so the existing ToastOverlay renders
them; the active list stays for API compatibility. Chose routing over
building a notifications tray nobody asked for.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
2026-06-12 01:21:16 +02:00
co-authored by Claude Fable 5
parent 01c3de37e1
commit d9ae2d7585
6 changed files with 92 additions and 1 deletions
+31
View File
@@ -272,5 +272,36 @@ void main() {
expect(calls, 1);
n.dispose();
});
// T-382: the in-memory list had no widget consumer — notifications
// vanished silently. Wired to the bus, each one now raises a toast.
test('a bus-wired notification surfaces as a rendered toast (T-382)', () async {
final messages = MessageBus();
final toasts = ToastService(messages: messages);
addTearDown(toasts.dispose);
final n = Notifications(messages: messages);
addTearDown(n.dispose);
n.warn('clide CLI not on PATH', title: 'dogfood');
await Future<void>.delayed(Duration.zero);
expect(toasts.entries, hasLength(1));
expect(toasts.entries.single.message, 'dogfood — clide CLI not on PATH');
expect(toasts.entries.single.severity, ToastSeverity.warning);
});
test('error and success levels map to their toast severities (T-382)', () async {
final messages = MessageBus();
final toasts = ToastService(messages: messages);
addTearDown(toasts.dispose);
final n = Notifications(messages: messages);
addTearDown(n.dispose);
n.error('boom');
n.success('done');
await Future<void>.delayed(Duration.zero);
expect(toasts.entries.map((e) => e.severity), [ToastSeverity.error, ToastSeverity.success]);
});
});
}