remove dissolved daemon, retire ptyc, fix golden cross-platform

Complete three overdue cleanups discovered during macOS health check:

D-56 daemon dissolution: delete bin/clide.dart, DaemonServer,
and orphaned tests (test/cli/, subprocess_test, in_process_test).
Update stale "clide --daemon" references in i18n catalogs, error
messages, editor_commands, CI scripts, and decision records.

ptyc retirement: delete ptyc/ source tree, PtySession, scm_rights.
Remove from Toolchain resolution, ToolCheck gate, backend
serialization, testmode harness, Makefile, CI, and sandbox
entitlements. PTY spawning uses NativePty (Dart FFI forkpty) since
the terminal was absorbed in-tree. D-5 amended.

Golden tests: wire the existing but never-applied clideGoldenConfig
via flutter_test_config.dart. Disable CI goldens (Skia anti-aliasing
differs between macOS/Linux even with Ahem). Keep platform-keyed
goldens only — goldens/linux/ and goldens/macos/ each run on their
own OS.

Test suite: 826 pass, 0 fail on macOS (was 829 pass, 11 fail).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Jeroen Schweitzer
2026-05-07 18:40:01 +02:00
co-authored by Claude Opus 4.6
parent 6b7290dc42
commit a6eca2561b
51 changed files with 265 additions and 2719 deletions
-102
View File
@@ -34,7 +34,6 @@ import 'src/daemon/pane_commands.dart';
import 'src/ipc/envelope.dart';
import 'src/panes/event_sink.dart';
import 'src/panes/registry.dart';
import 'src/pty/session.dart';
import 'src/daemon/dispatcher.dart';
import 'src/pty/env.dart' show expandedPath;
@@ -124,7 +123,6 @@ class _ClideTestAppState extends State<ClideTestApp> {
_log('toolchain.git', tc.git);
_log('toolchain.pql', tc.pql);
_log('toolchain.tmux', tc.tmux);
_log('toolchain.ptyc', tc.ptyc);
_log('toolchain.shell', tc.shell);
_log('toolchain.missing', tc.missing.isEmpty ? 'none' : tc.missing.join(', '));
_say('');
@@ -132,14 +130,12 @@ class _ClideTestAppState extends State<ClideTestApp> {
await _testExists('git', tc.git);
await _testExists('pql', tc.pql);
await _testExists('tmux', tc.tmux);
await _testExists('ptyc', tc.ptyc);
await _testExists('shell', tc.shell);
_say('');
await _testExec('git --version', tc.git, ['--version'], workDir);
await _testExec('pql --version', tc.pql, ['--version'], workDir);
await _testExec('tmux -V', tc.tmux, ['-V'], workDir);
await _testExec('ptyc (no args)', tc.ptyc, [], workDir);
await _testExec('shell --version', tc.shell, ['--version'], workDir);
_say('');
@@ -191,17 +187,6 @@ class _ClideTestAppState extends State<ClideTestApp> {
return 'exit=${r.exitCode} ${(r.stdout as String).trim()}';
});
// ptyc stdin/stdout test — send a valid request, verify JSON response
await _testAsync('ptyc spawn echo', () async {
final proc = await Process.start(tc.ptyc, []);
// Send a request for /bin/echo — simplest possible child
proc.stdin.write('{"argv":["/bin/echo","hello"],"cwd":"/tmp","env":{},"cols":80,"rows":24}');
await proc.stdin.close();
final stdout = await proc.stdout.transform(const SystemEncoding().decoder).join();
final exitCode = await proc.exitCode;
return 'exit=$exitCode stdout=${stdout.trim().split('\n').first}';
});
_say('');
}
@@ -395,93 +380,6 @@ class _ClideTestAppState extends State<ClideTestApp> {
return 'exit=$exit stderr=${stderr.trim()}';
});
// Direct PtySession test — bypasses IPC, tests fd transfer + reader.
await _testAsync('PtySession.spawn direct', () async {
final session = await PtySession.spawn(
argv: [tc.shell, '-c', 'echo DIRECT_PTY_TEST'],
cwd: workDir,
ptycPath: tc.ptyc,
);
_say(' session pid=${session.pid} masterFd exists');
final bytes = <int>[];
final done = Completer<void>();
session.output.listen(
(chunk) {
bytes.addAll(chunk);
_say(' got ${chunk.length} bytes');
},
onDone: () {
_say(' stream done');
if (!done.isCompleted) done.complete();
},
onError: (e) => _say(' stream error: $e'),
);
await done.future.timeout(const Duration(seconds: 5), onTimeout: () {
_say(' timeout waiting for output, got ${bytes.length} bytes so far');
});
await session.close();
final output = utf8.decode(bytes, allowMalformed: true);
final ok = output.contains('DIRECT_PTY_TEST');
return ok ? 'output=$output' : 'no marker in ${bytes.length} bytes: ${output.substring(0, output.length.clamp(0, 100))}';
});
if (!Platform.isMacOS) {
// Additional direct PtySession tests (Linux only — no merged thread).
// Test 1: spawn /bin/echo via PtySession, read output
await _testAsync('pty spawn echo', () async {
final session = await PtySession.spawn(
argv: ['/bin/echo', 'CLIDE_PTY_TEST_OK'],
cwd: workDir,
ptycPath: tc.ptyc,
);
final bytes = <int>[];
final done = Completer<void>();
session.output.listen(bytes.addAll, onDone: () => done.complete());
await done.future.timeout(const Duration(seconds: 5));
await session.close();
final output = utf8.decode(bytes, allowMalformed: true);
final ok = output.contains('CLIDE_PTY_TEST_OK');
return ok ? 'output contains marker' : 'marker not found in ${output.length} bytes';
});
// Test 2: spawn shell, write a command, verify output
await _testAsync('pty spawn shell', () async {
final session = await PtySession.spawn(
argv: [tc.shell, '-c', 'echo CLIDE_SHELL_TEST'],
cwd: workDir,
ptycPath: tc.ptyc,
);
final bytes = <int>[];
final done = Completer<void>();
session.output.listen(bytes.addAll, onDone: () => done.complete());
await done.future.timeout(const Duration(seconds: 5));
await session.close();
final output = utf8.decode(bytes, allowMalformed: true);
final ok = output.contains('CLIDE_SHELL_TEST');
return ok ? 'shell output contains marker' : 'marker not found in ${output.length} bytes';
});
// Test 3: spawn interactive shell, write to stdin, verify file creation
await _testAsync('pty write to child', () async {
final marker = '/tmp/clide-pty-test-${DateTime.now().millisecondsSinceEpoch}';
final session = await PtySession.spawn(
argv: [tc.shell],
cwd: workDir,
ptycPath: tc.ptyc,
);
session.write(utf8.encode('touch $marker && exit\n'));
final bytes = <int>[];
final done = Completer<void>();
session.output.listen(bytes.addAll, onDone: () => done.complete());
await done.future.timeout(const Duration(seconds: 5));
await session.close();
final fileCreated = File(marker).existsSync();
if (fileCreated) File(marker).deleteSync();
return fileCreated ? 'file created + cleaned up' : 'file not created';
});
} // end !Platform.isMacOS
_say('');
}