feat(log): live verbosity toggle — dock Level chip + clide log level (T-433)

The boot-time toggle (CLIDE_LOG / app.log.level) existed; this makes it
adjustable at runtime, from the two surfaces that fit — D-6 parity — and NOT a
Claude-composer slash command (log verbosity isn't a Claude-session concept).

- Output dock Level chip: was a view-only filter; now also drives the kernel
  Logger.minLevel and persists app.log.level, so the choice is real (changes
  what's captured) and survives restart. Initialized from the logger's current
  level so it reflects a CLI change. A null callback keeps it a pure view
  filter (tests / no kernel).
- `clide log level [<level>]` (lib/src/daemon/log_commands.dart): no arg reports
  the level + vocabulary; a valid level sets the live logger and persists;
  unknown → bad_arg (code 64), logger untouched. Wired in buildDispatcher with
  the kernel logger + settings (captured post-boot).

Tested: the command (get/set/case-insensitive/bad-arg, live + persist) and the
controller (initial level, callback fires, same-level no-op, filter-only when
unwired). Coverage gate 95.05%.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 12:32:56 +02:00
co-authored by Claude Opus 4.8
parent f8477816c4
commit 432a5f2d5a
8 changed files with 192 additions and 6 deletions
+12 -3
View File
@@ -10,15 +10,23 @@ import 'package:clide/kernel/src/log_ring.dart';
import 'package:flutter/foundation.dart';
class OutputController extends ChangeNotifier {
OutputController(this.ring) {
OutputController(this.ring, {LogLevel? initialLevel, this.onMinLevelChanged}) : minLevel = initialLevel ?? LogLevel.debug {
_sub = ring.changes.listen((_) => notifyListeners());
}
final LogRing ring;
late final StreamSubscription<void> _sub;
/// Minimum level shown. Defaults to debug (trace is firehose-noise).
LogLevel minLevel = LogLevel.debug;
/// Invoked when the Level chip changes the level — the dock chip is the live
/// dev/prod verbosity toggle (T-433), not just a view filter. The owner wires
/// this to set the kernel `Logger.minLevel` and persist `app.log.level`. Null
/// in tests / when no kernel is attached, leaving the chip a pure view filter.
final void Function(LogLevel)? onMinLevelChanged;
/// Minimum level shown — initialized from the kernel logger's level so the
/// chip reflects the real verbosity (which a `clide log level` CLI may have
/// already set), defaulting to debug (trace is firehose-noise).
LogLevel minLevel;
/// Source filter; null = all sources.
String? source;
@@ -29,6 +37,7 @@ class OutputController extends ChangeNotifier {
void setMinLevel(LogLevel level) {
if (minLevel == level) return;
minLevel = level;
onMinLevelChanged?.call(level);
notifyListeners();
}