code-quality batch (T-112)
test / unit + widget + golden + a11y (push) Failing after 31s
test / integration_test (xvfb) (push) Has been skipped
test / bundle smoke (xvfb 5s) (push) Has been skipped
test / daemon subprocess + web WASM smoke (push) Has been skipped
test / dart doc (lib API) (push) Failing after 1m7s

Seven small consultant findings, one commit:

1. TreeSitterLib stores last dlopen error + path in static fields
   instead of swallowing them. Callers that observe a null instance
   can now read the diagnostic.

2. Drop the Cmsghdr alias in libc.dart — back-compat shim with no
   callers; CLAUDE.md forbids those in a solo repo.

3. Drop EditorController._events field + the unused_field
   suppression. The constructor still subscribes via `events.on<...>`
   for _eventSub; the field itself was speculative retention.

4. Replace inline hex / errno literals in native_pty.dart with
   PosixErrno.{eintr,ebadf,epipe} and new libc.{pollin, pollAnyErr,
   sighup, sigkill, sigwinch}. PosixErrno gains eintr.

5. ExtensionManager records activate/deactivate exceptions in a
   `_failed` map exposed as `failedExtensions` + `didFail(id)`.
   Listeners are notified on entry/exit; cleared on a clean
   activate. UI surfaces the degraded state instead of pretending
   everything is fine.

6. file_tree_view imports FileEntry via the clide.dart barrel
   instead of `package:clide/src/files/listing.dart` directly — the
   leak the consultant flagged (barrel already re-exports it).

7. test_app branch in main.dart wrapped in `if (kDebugMode)` so
   release tree-shaker elides the test harness from shipping
   binaries. Source import stays; tree-shake handles the rest.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-05-17 22:11:23 +02:00
co-authored by Claude Opus 4.7
parent b66e8f6cc0
commit d4f8f89016
11 changed files with 98 additions and 27 deletions
+14
View File
@@ -81,6 +81,9 @@ class ExtensionManager extends ChangeNotifier {
final Map<String, ClideExtension> _known = {};
final Set<String> _activated = {};
// Extensions whose activate() / deactivate() threw — exposed so the
// UI can show a "degraded" status next to them.
final Map<String, Object> _failed = {};
void register(ClideExtension ext) {
if (_known.containsKey(ext.id)) {
@@ -94,6 +97,12 @@ class ExtensionManager extends ChangeNotifier {
Iterable<ClideExtension> get all => _known.values;
bool isActivated(String id) => _activated.contains(id);
/// Map of extension id → most recent activate/deactivate error.
/// Cleared for an extension when it activates cleanly. UI surfaces
/// read this for the "degraded" indicator.
Map<String, Object> get failedExtensions => Map.unmodifiable(_failed);
bool didFail(String id) => _failed.containsKey(id);
bool isEnabled(String id) {
final v = settings.get<bool>('app.extensions.$id.enabled');
return v ?? true;
@@ -138,11 +147,14 @@ class ExtensionManager extends ChangeNotifier {
_applyContribution(c);
}
_activated.add(id);
_failed.remove(id);
events.emit(ExtensionActivated(id: id));
notifyListeners();
log.info('extensions', 'activated $id');
} catch (e, st) {
_failed[id] = e;
log.error('extensions', 'activate failed for $id', error: e, stackTrace: st);
notifyListeners();
}
}
@@ -160,7 +172,9 @@ class ExtensionManager extends ChangeNotifier {
notifyListeners();
log.info('extensions', 'deactivated $id');
} catch (e, st) {
_failed[id] = e;
log.error('extensions', 'deactivate failed for $id', error: e, stackTrace: st);
notifyListeners();
}
}
+30 -5
View File
@@ -243,6 +243,15 @@ class TreeSitterLib {
@visibleForTesting
static TreeSitterLib fromDynamicLibrary(DynamicLibrary lib) => TreeSitterLib._(lib);
/// Most recent error raised while trying to dlopen the tree-sitter
/// library, paired with the path that was attempted. Cleared on a
/// successful open. Null means "no attempt failed yet."
///
/// Callers that observe a null `instance` after `init()` should read
/// this for the diagnostic instead of guessing.
static Object? lastOpenError;
static String? lastOpenErrorPath;
static DynamicLibrary? _openLibrary() {
final libName = Platform.isLinux
? 'libtree-sitter.so'
@@ -251,12 +260,22 @@ class TreeSitterLib {
: Platform.isWindows
? 'tree-sitter.dll'
: null;
if (libName == null) return null;
if (libName == null) {
lastOpenError = 'unsupported platform ${Platform.operatingSystem}';
lastOpenErrorPath = null;
return null;
}
// Try standard dlopen path first (works when lib is in bundle/lib/).
try {
return DynamicLibrary.open(libName);
} catch (_) {}
final lib = DynamicLibrary.open(libName);
lastOpenError = null;
lastOpenErrorPath = null;
return lib;
} catch (e) {
lastOpenError = e;
lastOpenErrorPath = libName;
}
// Try next to executable.
final exe = File(Platform.resolvedExecutable).parent.path;
@@ -264,8 +283,14 @@ class TreeSitterLib {
final path = '$dir/$libName';
if (File(path).existsSync()) {
try {
return DynamicLibrary.open(path);
} catch (_) {}
final lib = DynamicLibrary.open(path);
lastOpenError = null;
lastOpenErrorPath = null;
return lib;
} catch (e) {
lastOpenError = e;
lastOpenErrorPath = path;
}
}
}
return null;