Skip to content

Commit 6e0e2db

Browse files
pblazejclaude
andcommitted
chore: add AGENTS.md and CLAUDE.md
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent a0adcb6 commit 6e0e2db

3 files changed

Lines changed: 67 additions & 0 deletions

File tree

.changes/add-agents-md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
patch type="docs" "Add AGENTS.md with agent/contributor guidelines"

AGENTS.md

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
# LiveKit Flutter SDK
2+
3+
Flutter client SDK for LiveKit (`livekit_client` on pub.dev), built on top of `flutter_webrtc`. Supported platforms: iOS, Android, macOS, Windows, Linux, Web (including WASM).
4+
5+
## Commands
6+
7+
Requires Dart >= 3.6 / Flutter >= 3.27.
8+
9+
```sh
10+
flutter pub get
11+
flutter analyze # static analysis
12+
flutter test # unit tests
13+
dart format . --set-exit-if-changed # format (page width 120)
14+
dart run import_sorter:main --no-comments --exit-if-changed # import order (CI-enforced)
15+
dart run scripts/check_version.dart # version consistency (CI-enforced)
16+
dart run build_runner build # json_serializable codegen
17+
make proto # regen protobufs (needs ../protocol checkout)
18+
dart compile js ./web/e2ee.worker.dart -o ./example/web/e2ee.worker.dart.js # E2EE web worker
19+
```
20+
21+
CI (`build.yaml`) runs all of the above plus example-app builds for every platform including web/WASM.
22+
23+
## Repo layout
24+
25+
- `lib/livekit_client.dart` — public API surface; implementation lives in `lib/src/`.
26+
- `lib/src/core/``Room`, `Engine`, `SignalClient`, `Transport`; `lib/src/participant/`, `lib/src/track/`, `lib/src/publication/` — participants, tracks, publications; `lib/src/events.dart` + `lib/src/managers/event.dart` — event system.
27+
- `lib/src/proto/` — protoc-generated code (from the sibling `protocol` repo); excluded from analysis/format, never edit by hand. Same goes for `*.g.dart` files (json_serializable output in `lib/src/json/` and `lib/src/token_source/`).
28+
- `lib/src/e2ee/` + top-level `web/` — E2EE; the web worker (`web/e2ee.worker.dart`) must be recompiled to JS (command above) when changed.
29+
- `example/` — example app, built for all platforms in CI.
30+
- `test/` — unit tests with mock infrastructure in `test/mock/` (mock peer connection, data channel, websocket; `e2e_container.dart` for room-level tests).
31+
- Platform code in `android/`, `ios/`, `macos/`, `windows/`, `linux/`, `shared_cpp/`, `shared_swift/`.
32+
33+
Web/native divergence is handled with conditional imports (e.g. `track/processor_native.dart` vs `processor_web.dart`) — new platform-specific code should follow that pattern.
34+
35+
## Common pitfalls (from issue history)
36+
37+
- `flutter_webrtc` is pinned to an exact version on purpose: livekit_client and flutter_webrtc must agree on the same WebRTC-SDK native pods, and mismatches break user builds (CocoaPods conflicts). Bump it only in sync with a matching WebRTC-SDK version.
38+
- iOS `AVAudioSession` handling and reconnection-after-network-loss are the most frequent user-reported problem areas — change that code conservatively.
39+
- Don't hand-edit version numbers: `scripts/create_version.dart` propagates the version to `.version`, `pubspec.yaml`, `README.md`, `lib/src/livekit.dart`, and the podspecs; `check_version.dart` fails CI on mismatch.
40+
41+
## Error-prone areas & anti-patterns (from bug/review history)
42+
43+
Most regressions live in `lib/src/core/` (`room.dart`, `engine.dart`, `signal_client.dart`) and `participant/local.dart` — connection lifecycle and async event plumbing. Recurring mistake classes:
44+
45+
- Un-awaited async state updates: callers observed half-initialized participants/publications because `updateFromInfo()`/`updateTrack()` weren't awaited. The `unawaited_futures`/`discarded_futures` lints exist for exactly this — write `unawaited(...)` only as a deliberate choice. Never emit events from constructors or factories; return data and let the caller sequence emissions.
46+
- Iterating a collection across an `await`: event handlers mutate participants/publications/listener lists mid-iteration (`ConcurrentModificationError` — one fix swept 5 files). Snapshot with `.toList()` before iterating; for queues, copy-then-clear before processing.
47+
- The reconnect/disconnect state machine runs on mutable flags (`_isClosed`, `_isReconnecting`, ...) and has historically produced duplicate or missing lifecycle events (double `DisconnectedEvent`, flags not reset on re-connect). When touching engine/room event flow: use `Room.emitWhenConnected(...)`, `createListener(synchronized: true)`, mind emit-vs-cleanup ordering, and add a regression test (see `test/core/disconnect_event_test.dart`).
48+
- Events or media tracks arriving before their dependent state exists: don't throw or drop — queue and flush (see `lib/src/core/pending_track_queue.dart`, TTL'd and flushed on connect/participant updates).
49+
- Use-after-close on `StreamController`s crashed data streams: wrap controllers with `isClosed` guards (see `DataStreamController`), and when erroring a stream out, also close it.
50+
- Don't rely on protobuf field defaults for state: republish-after-reconnect silently unmuted tracks because `muted` defaults to `false`. Pass prior state explicitly and reconcile local flags with server responses.
51+
- Duplicate in-flight async operations: cache the future (`_future ??= ...`) and clear it on failure/disconnect (how `ensurePublisherConnected` is debounced), or use `lib/src/support/reusable_completer.dart`.
52+
- Cleanup: extend `Disposable` and register teardown via `onDispose()` (run LIFO, each guarded so one failing disposer can't abort the rest); create event listeners through `createListener()` so they're cancelled with their owner.
53+
- Native boundary: methods in `Native` swallow errors and `logger.warning` by default — if a failure must propagate (e.g. a mode change the caller depends on), document why. Platform-channel crossings are expensive in hot paths; batch data instead of calling per-frame/per-sample.
54+
55+
## Code style
56+
57+
- Lints: `package:lints/recommended` plus repo rules in `analysis_options.yaml` — single quotes, `prefer_final_locals`, `unawaited_futures`, `discarded_futures`, `avoid_print`.
58+
- `dart format` with `page_width: 120`; imports sorted with `import_sorter`.
59+
- Every source file carries the Apache-2.0 license header.
60+
61+
## Releases
62+
63+
Every PR needs a changeset file in `.changes/` (format: `patch|minor|major type="fixed|added|changed|..." "description"`); CI checks for it and runs `dart-apitool` against `main` to require a `major` changeset for breaking public-API changes. Releases are tag-driven (`vX.Y.Z`) and publish to pub.dev.

CLAUDE.md

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# CLAUDE.md
2+
3+
@AGENTS.md

0 commit comments

Comments
 (0)