chore: adopt Dart 3.9 toolchain — honest floor + tall-style reformat (T-353)

Raise the declared minimums in pubspec.yaml to what our deps already
require: Flutter >=3.35.0 / Dart >=3.9.0 (was 3.19.0 / 3.5.0). alchemist
0.12 needs Flutter 3.32; Dart 3.9 first ships in Flutter 3.35, so 3.35 is
the binding floor. Pin the exact build toolchain in .fvmrc (Flutter
3.44.1).

Moving to the Dart 3.9 language level switches `dart format` to the new
"tall" style and enables two new lints. This commit is the resulting
mechanical churn, isolated from any behaviour change:
  - whole-tree `dart format` reformat (tall style)
  - `dart fix` for unnecessary_underscores + use_null_aware_elements

No runtime behaviour change; `make test` green.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-11 12:11:53 +02:00
co-authored by Claude Opus 4.8
parent bcea5f15b7
commit 6d0ebab721
444 changed files with 7587 additions and 12849 deletions
+4 -1
View File
@@ -109,7 +109,10 @@ void main() {
group('normalize (argv shape → named args)', () {
const schema = CommandSchema(
positional: ['slot'],
args: {'slot': ArgSpec(), 'to': ArgSpec(type: ArgType.number)},
args: {
'slot': ArgSpec(),
'to': ArgSpec(type: ArgType.number),
},
);
test('positional[i] maps to the i-th declared name', () {
+6 -32
View File
@@ -4,11 +4,7 @@ import 'package:test/test.dart';
void main() {
group('IpcRequest', () {
test('encodes and decodes round-trip', () {
final req = IpcRequest(
id: '42',
cmd: 'git.status',
args: const {'path': '.'},
);
final req = IpcRequest(id: '42', cmd: 'git.status', args: const {'path': '.'});
final line = req.encode();
final decoded = IpcMessage.decode(line);
expect(decoded, isA<IpcRequest>());
@@ -49,32 +45,18 @@ void main() {
test('encodes with error payload, no data', () {
final r = IpcResponse.err(
id: '9',
error: IpcError(
code: IpcExitCode.notFound,
kind: IpcErrorKind.notFound,
message: 'missing',
hint: 'try --help',
),
error: IpcError(code: IpcExitCode.notFound, kind: IpcErrorKind.notFound, message: 'missing', hint: 'try --help'),
);
final json = r.toJson();
expect(json['ok'], false);
expect(json['error'], {
'code': 3,
'kind': 'not_found',
'message': 'missing',
'hint': 'try --help',
});
expect(json['error'], {'code': 3, 'kind': 'not_found', 'message': 'missing', 'hint': 'try --help'});
expect(json.containsKey('data'), isFalse);
});
test('decode preserves error fields', () {
final original = IpcResponse.err(
id: '9',
error: IpcError(
code: IpcExitCode.conflict,
kind: IpcErrorKind.conflict,
message: 'race',
),
error: IpcError(code: IpcExitCode.conflict, kind: IpcErrorKind.conflict, message: 'race'),
);
final r = IpcMessage.decode(original.encode()) as IpcResponse;
expect(r.ok, false);
@@ -89,12 +71,7 @@ void main() {
group('IpcEvent', () {
test('serializes subsystem + kind + ts + data', () {
final ts = DateTime.utc(2026, 4, 21, 12, 0, 0);
final e = IpcEvent(
subsystem: 'git',
kind: 'status-changed',
timestamp: ts,
data: const {'staged': 3},
);
final e = IpcEvent(subsystem: 'git', kind: 'status-changed', timestamp: ts, data: const {'staged': 3});
final json = e.toJson();
expect(json['type'], 'event');
expect(json['subsystem'], 'git');
@@ -110,10 +87,7 @@ void main() {
});
test('throws on unknown type', () {
expect(
() => IpcMessage.decode('{"type":"bogus","id":"1"}'),
throwsA(isA<FormatException>()),
);
expect(() => IpcMessage.decode('{"type":"bogus","id":"1"}'), throwsA(isA<FormatException>()));
});
test('throws on malformed JSON', () {
+23 -22
View File
@@ -20,11 +20,7 @@ void main() {
setUp(() async {
discoveryDir = await Directory.systemTemp.createTemp('clide-mcp-disc-');
server = McpServer(
workspaceRoot: '/var/mnt/test/clide-fixture',
log: _silent(),
discoveryDirOverride: discoveryDir.path,
);
server = McpServer(workspaceRoot: '/var/mnt/test/clide-fixture', log: _silent(), discoveryDirOverride: discoveryDir.path);
await server.start();
});
@@ -155,7 +151,7 @@ void main() {
'method': 'tools/call',
'params': {
'name': 'mcp__ide__executeCode',
'arguments': {'code': 'print(1)'}
'arguments': {'code': 'print(1)'},
},
});
final reply = jsonDecode(await replyFuture.timeout(const Duration(seconds: 2))) as Map<String, Object?>;
@@ -214,12 +210,7 @@ void main() {
dispatcher.register('echo', (req) async => IpcResponse.ok(id: req.id, data: {'echo': req.args['text']}));
// A poor MCP fit — withheld from the tool surface (D-86).
dispatcher.register('pane.tail', (req) async => IpcResponse.ok(id: req.id, data: const {}), mcpExpose: false);
srv = McpServer(
workspaceRoot: '/x',
log: _silent(),
discoveryDirOverride: disc.path,
dispatcher: dispatcher,
);
srv = McpServer(workspaceRoot: '/x', log: _silent(), discoveryDirOverride: disc.path, dispatcher: dispatcher);
await srv.start();
});
@@ -234,8 +225,12 @@ void main() {
final client = HttpClient();
addTearDown(client.close);
final resp = await (await client.getUrl(Uri.parse('http://127.0.0.1:${srv.port}/sse'))).close();
final dataLines =
resp.transform(utf8.decoder).transform(const LineSplitter()).where((l) => l.startsWith('data: ')).map((l) => l.substring(6)).asBroadcastStream();
final dataLines = resp
.transform(utf8.decoder)
.transform(const LineSplitter())
.where((l) => l.startsWith('data: '))
.map((l) => l.substring(6))
.asBroadcastStream();
final endpoint = Completer<String>();
final sub = dataLines.listen((d) {
final m = RegExp(r'sessionId=([\w-]+)').firstMatch(d);
@@ -312,8 +307,12 @@ void main() {
final client = HttpClient();
addTearDown(client.close);
final resp = await (await client.getUrl(Uri.parse('http://127.0.0.1:${srv.port}/sse'))).close();
final data =
resp.transform(utf8.decoder).transform(const LineSplitter()).where((l) => l.startsWith('data: ')).map((l) => l.substring(6)).asBroadcastStream();
final data = resp
.transform(utf8.decoder)
.transform(const LineSplitter())
.where((l) => l.startsWith('data: '))
.map((l) => l.substring(6))
.asBroadcastStream();
final ep = Completer<String>();
final sub = data.listen((d) {
final m = RegExp(r'sessionId=([\w-]+)').firstMatch(d);
@@ -324,12 +323,14 @@ void main() {
final replyFuture = data.firstWhere((s) => s.contains('"id":13'));
final post = await client.postUrl(Uri.parse('http://127.0.0.1:${srv.port}/messages?sessionId=$sid'));
post.headers.contentType = ContentType.json;
post.write(jsonEncode({
'jsonrpc': '2.0',
'id': 13,
'method': 'tools/call',
'params': {'name': 'mcp__clide__echo', 'arguments': const {}},
}));
post.write(
jsonEncode({
'jsonrpc': '2.0',
'id': 13,
'method': 'tools/call',
'params': {'name': 'mcp__clide__echo', 'arguments': const {}},
}),
);
await post.close();
final reply = jsonDecode(await replyFuture.timeout(const Duration(seconds: 2))) as Map<String, Object?>;
expect((reply['result'] as Map)['isError'], isTrue);
+2 -8
View File
@@ -15,17 +15,11 @@ void main() {
});
test('is deterministic for the same input', () {
expect(
workspaceSocketPath('/home/me/repo'),
workspaceSocketPath('/home/me/repo'),
);
expect(workspaceSocketPath('/home/me/repo'), workspaceSocketPath('/home/me/repo'));
});
test('different workspace roots hash to different paths', () {
expect(
workspaceSocketPath('/home/me/repo-a'),
isNot(workspaceSocketPath('/home/me/repo-b')),
);
expect(workspaceSocketPath('/home/me/repo-a'), isNot(workspaceSocketPath('/home/me/repo-b')));
});
test('socketDirectory uses XDG_RUNTIME_DIR on Linux when set', () {
+1 -6
View File
@@ -16,12 +16,7 @@ void main() {
});
test('error kinds are unique strings', () {
final kinds = <String>{
IpcErrorKind.userError,
IpcErrorKind.toolError,
IpcErrorKind.notFound,
IpcErrorKind.conflict,
};
final kinds = <String>{IpcErrorKind.userError, IpcErrorKind.toolError, IpcErrorKind.notFound, IpcErrorKind.conflict};
expect(kinds.length, 4);
});
});
+6 -10
View File
@@ -20,10 +20,7 @@ import 'package:test/test.dart';
Logger _silent() => Logger(minLevel: LogLevel.error, sinks: const []);
Future<Socket> _connect(IpcServer s) async => Socket.connect(
InternetAddress(s.socketPath, type: InternetAddressType.unix),
0,
);
Future<Socket> _connect(IpcServer s) async => Socket.connect(InternetAddress(s.socketPath, type: InternetAddressType.unix), 0);
void _emit(DaemonBus bus, String sub, String kind, [Map<String, Object?> data = const {}]) =>
bus.emit(DaemonEvent(subsystem: sub, kind: kind, data: data, ts: DateTime.now().toUtc()));
@@ -62,12 +59,11 @@ void main() {
try {
final lines = s.cast<List<int>>().transform(utf8.decoder).transform(const LineSplitter());
final it = StreamIterator(lines);
s.write('${IpcRequest(id: 'e', cmd: 'events', args: {
'flags': {
if (since != null) 'since': since,
if (filter != null) 'filter': filter,
},
}).encode()}\n');
s.write(
'${IpcRequest(id: 'e', cmd: 'events', args: {
'flags': {'since': ?since, 'filter': ?filter},
}).encode()}\n',
);
await s.flush();
if (!await it.moveNext().timeout(const Duration(seconds: 2))) {
throw StateError('no response');
+22 -18
View File
@@ -19,10 +19,7 @@ import 'package:test/test.dart';
Logger _silent() => Logger(minLevel: LogLevel.error, sinks: const []);
Future<Socket> _connect(IpcServer s) async => Socket.connect(
InternetAddress(s.socketPath, type: InternetAddressType.unix),
0,
);
Future<Socket> _connect(IpcServer s) async => Socket.connect(InternetAddress(s.socketPath, type: InternetAddressType.unix), 0);
/// Wrap a Socket in a line iterator backed by a single broadcast
/// stream so the same connection can read multiple framed lines.
@@ -50,15 +47,12 @@ Future<void> _send(Socket s, IpcRequest req) async {
}
IpcRequest _tailReq({String? filter, String id = 't'}) => IpcRequest(
id: id,
cmd: 'tail',
args: {
'flags': {
'events': true,
if (filter != null) 'filter': filter,
},
},
);
id: id,
cmd: 'tail',
args: {
'flags': {'events': true, 'filter': ?filter},
},
);
void main() {
late Directory ws;
@@ -70,8 +64,13 @@ void main() {
ws = await Directory.systemTemp.createTemp('clide-stream-test-');
dispatcher = DaemonDispatcher();
bus = DaemonBus();
server =
IpcServer(dispatcher: dispatcher, workspaceRoot: '${ws.path}/${DateTime.now().microsecondsSinceEpoch}', log: _silent(), events: bus, replayDepth: 4);
server = IpcServer(
dispatcher: dispatcher,
workspaceRoot: '${ws.path}/${DateTime.now().microsecondsSinceEpoch}',
log: _silent(),
events: bus,
replayDepth: 4,
);
await server.start();
});
@@ -213,10 +212,15 @@ void main() {
final r = _lineReader(s);
final q = _Lines(r.lines);
await _send(
s,
IpcRequest(id: 'a', cmd: '_argv', args: {
s,
IpcRequest(
id: 'a',
cmd: '_argv',
args: {
'argv': ['tail', '--events', '--filter', 'pane'],
}));
},
),
);
final ack = IpcMessage.decode(await q.next()) as IpcResponse;
expect(ack.ok, isTrue);
expect(ack.data['streaming'], isTrue);
+5 -20
View File
@@ -94,10 +94,7 @@ void main() {
test('malformed JSON line surfaces a userError', () async {
server = IpcServer(dispatcher: dispatcher, workspaceRoot: workRoot, log: _silentLog());
await server.start();
final c = await Socket.connect(
InternetAddress(server.socketPath, type: InternetAddressType.unix),
0,
);
final c = await Socket.connect(InternetAddress(server.socketPath, type: InternetAddressType.unix), 0);
c.write('{not json\n');
await c.flush();
final line = await c.cast<List<int>>().transform(utf8.decoder).transform(const LineSplitter()).first.timeout(const Duration(seconds: 2));
@@ -175,10 +172,7 @@ void main() {
test('stop closes an in-flight client connection', () async {
server = IpcServer(dispatcher: dispatcher, workspaceRoot: workRoot, log: _silentLog());
await server.start();
final c = await Socket.connect(
InternetAddress(server.socketPath, type: InternetAddressType.unix),
0,
);
final c = await Socket.connect(InternetAddress(server.socketPath, type: InternetAddressType.unix), 0);
c.write('${IpcRequest(id: 'q', cmd: 'ping').encode()}\n');
await c.flush();
await c.cast<List<int>>().transform(utf8.decoder).transform(const LineSplitter()).first;
@@ -192,10 +186,7 @@ void main() {
test('multiple sequential requests on the same connection each get a reply', () async {
server = IpcServer(dispatcher: dispatcher, workspaceRoot: workRoot, log: _silentLog());
await server.start();
final c = await Socket.connect(
InternetAddress(server.socketPath, type: InternetAddressType.unix),
0,
);
final c = await Socket.connect(InternetAddress(server.socketPath, type: InternetAddressType.unix), 0);
final replies = c.cast<List<int>>().transform(utf8.decoder).transform(const LineSplitter());
final iter = StreamIterator(replies);
for (var i = 0; i < 3; i++) {
@@ -239,10 +230,7 @@ void main() {
test('a non-request message (e.g. event) surfaces a userError', () async {
server = IpcServer(dispatcher: dispatcher, workspaceRoot: workRoot, log: _silentLog());
await server.start();
final c = await Socket.connect(
InternetAddress(server.socketPath, type: InternetAddressType.unix),
0,
);
final c = await Socket.connect(InternetAddress(server.socketPath, type: InternetAddressType.unix), 0);
final evt = IpcEvent(subsystem: 'test', kind: 'wrong-shape', timestamp: DateTime.now().toUtc());
c.write('${evt.encode()}\n');
await c.flush();
@@ -258,10 +246,7 @@ void main() {
Logger _silentLog() => Logger(minLevel: LogLevel.error, sinks: const []);
Future<IpcResponse> _roundTrip(String socketPath, IpcRequest req) async {
final c = await Socket.connect(
InternetAddress(socketPath, type: InternetAddressType.unix),
0,
);
final c = await Socket.connect(InternetAddress(socketPath, type: InternetAddressType.unix), 0);
c.write('${req.encode()}\n');
await c.flush();
final line = await c.cast<List<int>>().transform(utf8.decoder).transform(const LineSplitter()).first.timeout(const Duration(seconds: 2));