feat(web): fence dart:ffi behind web stubs so the WASM build compiles (T-438, D-100)

`flutter build web --wasm` had been broken since the tree-sitter/PTY dart:ffi
pivot. Per D-100 (resolving Q-50: keep the web "happy accident" alive), every
native binding now sits behind a `dart.library.ffi` conditional import with a
graceful web stub. Desktop builds are unchanged — no fidelity loss; the web
target degrades (no terminal, native git, or syntax highlighting).

Discriminator is `dart.library.ffi`, not `dart.library.io` — dart2wasm provides
dart:io, so FFI is the only blocker.

Fences:
- PTY: pty_session → pty_backend_io / pty_backend_web (stub throws).
- tree-sitter: pure types → syntax_result.dart; tree_sitter_service is now a
  facade over _ffi/_stub; tree_sitter_boot_io/stub fences TreeSitterLib.init().
- watchdog: watchdog_windows_stub (all -1 sampler).
- claude ABI probe: native_abi_io/stub (was `dart:ffi show Abi`).
- testmode fd-check: fd_check_io/stub.

Also dart2js-safe: the 64-bit FNV literals in session_naming.dart + paths.dart
(the dual JS fallback rejected them) — split into 32-bit halves, dropped a
no-op 64-bit mask. Desktop/wasm hash values unchanged.

CI: added a `web-wasm` job (flutter build web --wasm) so the fence can't rot.
Two FFI-constructing tree-sitter tests import _ffi.dart directly (the analyzer
resolves the conditional facade to the stub branch).

Verified: `flutter build web --wasm` → built; `flutter analyze` clean;
`make test` green. Full Playwright e2e harness wiring is the tracked follow-on.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-15 17:31:24 +02:00
co-authored by Claude Opus 4.8
parent 967db2f8d9
commit ca08c2a17d
28 changed files with 796 additions and 380 deletions
@@ -8,7 +8,9 @@ import 'dart:ffi';
import 'dart:typed_data';
import 'package:clide/kernel/src/syntax/tree_sitter_ffi.dart';
import 'package:clide/kernel/src/syntax/tree_sitter_service.dart';
// The FFI impl directly (not the facade): these tests inject a fake
// TreeSitterLib via the FFI-only constructor params (T-438).
import 'package:clide/kernel/src/syntax/tree_sitter_service_ffi.dart';
import 'package:ffi/ffi.dart';
import 'package:flutter_test/flutter_test.dart';
@@ -13,7 +13,9 @@ import 'dart:ffi';
import 'dart:io';
import 'package:clide/kernel/src/syntax/tree_sitter_ffi.dart';
import 'package:clide/kernel/src/syntax/tree_sitter_service.dart';
// The FFI impl directly (not the facade): the smoke test injects a fake
// TreeSitterLib via the FFI-only constructor params (T-438).
import 'package:clide/kernel/src/syntax/tree_sitter_service_ffi.dart';
import 'package:flutter_test/flutter_test.dart';
const _libPath = 'native/linux-x64/libtree-sitter.so';