|
1 | 1 | # AGENTS.md |
2 | 2 |
|
3 | | -Guidance for code agents working in this repository. |
| 3 | +This file provides guidance to ai agents when working with code in this repository. |
4 | 4 |
|
5 | 5 | ## Project Overview |
6 | 6 |
|
7 | | -Loro Protocol is a transport‑agnostic synchronization protocol for collaborative real‑time data structures (CRDTs). This monorepo contains: |
8 | | - |
9 | | -- TypeScript protocol encoder/decoder (`packages/loro-protocol`) |
10 | | -- WebSocket client and a minimal Node server (`packages/loro-websocket`) |
11 | | -- Adaptors that bridge the protocol to the Loro CRDT (`packages/loro-adaptors`) |
12 | | -- A Rust workspace with protocol/client/server equivalents under `rust/` |
13 | | - |
14 | | -No Cloudflare/DO code lives in this repo. |
| 7 | +Loro Protocol is a transport‑agnostic synchronization protocol for collaborative CRDTs. This monorepo contains a TypeScript implementation (protocol, WebSocket client/server, adaptors) and Rust counterparts. It supports Loro, Yjs, and other CRDT systems over WebSocket and P2P connections. An optional end‑to‑end encrypted flow for Loro documents ("%ELO") is included. |
15 | 8 |
|
16 | 9 | ## Common Development Commands |
17 | 10 |
|
18 | 11 | ```bash |
19 | 12 | # Install dependencies |
20 | 13 | pnpm install |
21 | 14 |
|
22 | | -# Build / test / lint across packages |
23 | | -pnpm -r build |
24 | | -pnpm -r test |
25 | | -pnpm -r typecheck |
26 | | -pnpm -r lint |
| 15 | +# Run development watch mode |
| 16 | +pnpm dev |
| 17 | + |
| 18 | +# Run tests |
| 19 | +pnpm test |
27 | 20 |
|
28 | | -# Dev/watch (where supported) |
29 | | -pnpm -r dev |
| 21 | +# Build all packages |
| 22 | +pnpm build |
| 23 | + |
| 24 | +# Type checking |
| 25 | +pnpm typecheck |
| 26 | + |
| 27 | +# Linting |
| 28 | +pnpm lint |
30 | 29 |
|
31 | 30 | # Clean build artifacts |
32 | | -pnpm -r clean |
| 31 | +pnpm clean |
33 | 32 | ``` |
34 | 33 |
|
35 | 34 | ## Architecture |
36 | 35 |
|
37 | 36 | ### Monorepo Structure |
38 | 37 |
|
39 | | -- `packages/loro-protocol`: Core wire encoding/decoding logic (TS) |
40 | | -- `packages/loro-websocket`: WebSocket client and `SimpleServer` (TS) |
41 | | -- `packages/loro-adaptors`: Adaptors for `loro-crdt` docs and ephemeral state (TS) |
42 | | -- `examples/excalidraw-example`: React demo using `SimpleServer` |
43 | | -- `rust/`: Rust workspace with `loro-protocol`, `loro-websocket-client`, `loro-websocket-server` |
| 38 | +- **packages/loro-protocol**: Protocol types and binary encoders/decoders, bytes utilities, and `%ELO` container/crypto helpers (TypeScript). Key files: `src/{protocol,encoding,bytes,e2ee}.ts` with tests under `src/`. |
| 39 | +- **packages/loro-websocket**: WebSocket client and a `SimpleServer` (TypeScript). |
| 40 | + - Features: message fragmentation/reassembly (≤256 KiB), connection‑scoped keepalive frames (`"ping"/"pong"` text), permission hooks, optional persistence hooks. |
| 41 | +- **packages/loro-adaptors**: Adaptors that connect the WebSocket client to `loro-crdt` (`LoroAdaptor`, `LoroEphemeralAdaptor`) and `%ELO` (`EloLoroAdaptor`). |
| 42 | +- **examples/excalidraw-example**: React demo using `SimpleServer`; syncs a Loro doc and ephemeral presence. |
| 43 | +- **rust/**: Rust workspace mirroring the TS packages: |
| 44 | + - `rust/loro-protocol`: Encoder/decoder parity with JS (snapshot tests included). |
| 45 | + - `rust/loro-websocket-client`: Minimal client. |
| 46 | + - `rust/loro-websocket-server`: Async server with workspace isolation, auth hooks, and persistence example (see `examples/simple-server.rs`). |
| 47 | + |
| 48 | +### Protocol Overview |
| 49 | + |
| 50 | +- **CRDT magic bytes**: `%LOR` (Loro), `%EPH` (Ephemeral), `%YJS`, `%YAW`, `%ELO` (E2EE Loro). |
| 51 | +- **Messages**: JoinRequest/JoinResponseOk/JoinError, DocUpdate, DocUpdateFragmentHeader/Fragment, UpdateError, Leave. |
| 52 | +- **Limits**: 256 KiB max per message; large payloads are fragmented and reassembled. |
| 53 | +- **Keepalive**: Text frames `"ping"`/`"pong"` are connection‑scoped and bypass the envelope. |
| 54 | +- **%ELO**: DocUpdate payload is a container of encrypted records (DeltaSpan/Snapshot). Each record has a plaintext header (peer/version metadata, `keyId`, 12‑byte IV) and AES‑GCM ciphertext (`ct||tag`). Servers route/broadcast without decrypting. |
| 55 | + |
| 56 | +### Testing |
| 57 | + |
| 58 | +- **TypeScript**: `vitest` across packages via `vitest.workspace.ts` (unit + e2e in `packages/loro-websocket`). |
| 59 | +- **Rust**: `cargo test` in each crate; server/client e2e and auth tests under `rust/loro-websocket-server/tests`. |
| 60 | + |
| 61 | +## Important Design Documents |
| 62 | + |
| 63 | +**When implementation behavior doesn't match expectations, these documents are the source of truth:** |
| 64 | + |
| 65 | +- `/protocol.md`: Wire protocol specification - defines message formats and syncing process |
| 66 | +- `/protocol-e2ee.md`: End-to-end encryption protocol |
| 67 | + |
| 68 | +These documents represent the ground truth design. If there are inconsistencies between code and these specs, follow the specifications. |
| 69 | + |
| 70 | +# Development Guidelines |
| 71 | + |
| 72 | +## Philosophy |
| 73 | + |
| 74 | +### Core Beliefs |
| 75 | + |
| 76 | +- **Incremental progress over big bangs** - Small changes that compile and pass tests |
| 77 | +- **Learning from existing code** - Study and plan before implementing |
| 78 | +- **Pragmatic over dogmatic** - Adapt to project reality |
| 79 | +- **Clear intent over clever code** - Be boring and obvious |
| 80 | + |
| 81 | +### Simplicity Means |
| 82 | + |
| 83 | +- Single responsibility per function/class |
| 84 | +- Avoid premature abstractions |
| 85 | +- No clever tricks - choose the boring solution |
| 86 | +- If you need to explain it, it's too complex |
| 87 | +- Avoid over-engineering, don't write low-value docs/comments/tests. They'll increase the maintenance cost and make code review harder. |
| 88 | +- Your changes should be easy to review. Please address the part that you want human to focus on by adding `TODO: REVIEW [reason]`. |
| 89 | +- Don't test obvious things. |
| 90 | + |
| 91 | +## Process |
| 92 | + |
| 93 | +### 1. Planning & Staging |
| 94 | + |
| 95 | +Break complex work into 3-5 stages. Document in `IMPLEMENTATION_PLAN.md`: |
| 96 | + |
| 97 | +```markdown |
| 98 | +## Stage N: [Name] |
| 99 | + |
| 100 | +**Goal**: [Specific deliverable] |
| 101 | +**Success Criteria**: [Testable outcomes] |
| 102 | +**Tests**: [Specific test cases] |
| 103 | +**Status**: [Not Started|In Progress|Complete] |
| 104 | +``` |
| 105 | + |
| 106 | +- Update status as you progress |
| 107 | +- Remove file when all stages are done |
| 108 | + |
| 109 | +### 2. Implementation Flow |
| 110 | + |
| 111 | +1. **Understand** - Study existing patterns in codebase |
| 112 | +2. **Test** - Write test first (red) |
| 113 | +3. **Implement** - Minimal code to pass (green) |
| 114 | +4. **Refactor** - Clean up with tests passing |
| 115 | +5. **Commit** - With clear message linking to plan |
| 116 | + |
| 117 | +### 3. When Stuck (After 3 Attempts) |
| 118 | + |
| 119 | +**CRITICAL**: Maximum 3 attempts per issue, then STOP. |
| 120 | + |
| 121 | +1. **Document what failed**: |
| 122 | + - What you tried |
| 123 | + - Specific error messages |
| 124 | + - Why you think it failed |
| 125 | + |
| 126 | +2. **Research alternatives**: |
| 127 | + - Find 2-3 similar implementations |
| 128 | + - Note different approaches used |
| 129 | + |
| 130 | +3. **Question fundamentals**: |
| 131 | + - Is this the right abstraction level? |
| 132 | + - Can this be split into smaller problems? |
| 133 | + - Is there a simpler approach entirely? |
| 134 | + |
| 135 | +4. **Try different angle**: |
| 136 | + - Different library/framework feature? |
| 137 | + - Different architectural pattern? |
| 138 | + - Remove abstraction instead of adding? |
| 139 | + |
| 140 | +## Technical Standards |
| 141 | + |
| 142 | +### Architecture Principles |
| 143 | + |
| 144 | +- **Composition over inheritance** - Use dependency injection |
| 145 | +- **Interfaces over singletons** - Enable testing and flexibility |
| 146 | +- **Explicit over implicit** - Clear data flow and dependencies |
| 147 | +- **Test-driven when possible** - Never disable tests, fix them |
| 148 | + |
| 149 | +### Code Quality |
| 150 | + |
| 151 | +- **Every commit must**: |
| 152 | + - Compile successfully |
| 153 | + - Pass all existing tests |
| 154 | + - Include tests for new functionality |
| 155 | + - Follow project formatting/linting |
| 156 | + |
| 157 | +- **Before committing**: |
| 158 | + - Run formatters/linters |
| 159 | + - Self-review changes |
| 160 | + - Ensure commit message explains "why" |
| 161 | + |
| 162 | +### Error Handling |
44 | 163 |
|
45 | | -### Protocol Essentials |
| 164 | +- Fail fast with descriptive messages |
| 165 | +- Include context for debugging |
| 166 | +- Handle errors at appropriate level |
| 167 | +- Never silently swallow exceptions |
46 | 168 |
|
47 | | -See `/protocol.md`. |
| 169 | +## Decision Framework |
48 | 170 |
|
49 | | -- Message envelope: 4‑byte CRDT magic, varBytes roomId (≤128B), 1‑byte type, payload |
50 | | -- Types: JoinRequest/JoinResponseOk/JoinError, DocUpdate, DocUpdateFragmentHeader/Fragment, UpdateError, Leave |
51 | | -- Limit: 256 KiB max per message; large payloads must be fragmented |
52 | | -- Keepalive: connection‑scoped text frames "ping"/"pong" (out‑of‑band) |
| 171 | +When multiple valid approaches exist, choose based on: |
53 | 172 |
|
54 | | -Key TS files: |
| 173 | +1. **Testability** - Can I easily test this? |
| 174 | +2. **Readability** - Will someone understand this in 6 months? |
| 175 | +3. **Consistency** - Does this match project patterns? |
| 176 | +4. **Simplicity** - Is this the simplest solution that works? |
| 177 | +5. **Reversibility** - How hard to change later? |
55 | 178 |
|
56 | | -- `packages/loro-protocol/src/{bytes,encoding,protocol}.ts` |
| 179 | +## Project Integration |
57 | 180 |
|
58 | | -## Build & Test |
| 181 | +### Learning the Codebase |
59 | 182 |
|
60 | | -- TypeScript: `vitest` tests are co‑located with sources (`*.test.ts`). The websocket package includes e2e tests spinning up `SimpleServer`. |
61 | | -- Rust: run with `cargo` under `rust/` (tests in crates and `tests/`). |
| 183 | +- Find 3 similar features/components |
| 184 | +- Identify common patterns and conventions |
| 185 | +- Use same libraries/utilities when possible |
| 186 | +- Follow existing test patterns |
62 | 187 |
|
63 | | -Useful references: |
| 188 | +### Tooling |
64 | 189 |
|
65 | | -- TS E2E: `packages/loro-websocket/src/e2e.test.ts` |
66 | | -- Rust server example: `rust/loro-websocket-server/examples/simple-server.rs` |
| 190 | +- Use project's existing build system |
| 191 | +- Use project's test framework |
| 192 | +- Use project's formatter/linter settings |
| 193 | +- Don't introduce new tools without strong justification |
67 | 194 |
|
68 | | -## Implementation Notes |
| 195 | +## Quality Gates |
69 | 196 |
|
70 | | -- Fragmentation: servers/clients reassemble using `DocUpdateFragmentHeader` + `DocUpdateFragment` with an 8‑byte batch id |
71 | | -- Rooms: one WS connection can join multiple rooms (CRDT+roomId pair) |
72 | | -- CRDTs: Loro (`%LOR`), Loro Ephemeral (`%EPH`), Yjs (`%YJS`), Yjs Awareness (`%YAW`) |
73 | | -- Errors: explicit small codes for join/update failures |
74 | | -- Auth: `SimpleServer` exposes `authenticate` and snapshot load/save hooks |
| 197 | +### Definition of Done |
75 | 198 |
|
76 | | -## Development Guidelines |
| 199 | +- [ ] Tests written and passing |
| 200 | +- [ ] Code follows project conventions |
| 201 | +- [ ] No linter/formatter warnings |
| 202 | +- [ ] Commit messages are clear |
| 203 | +- [ ] Implementation matches plan |
| 204 | +- [ ] No TODOs without issue numbers |
77 | 205 |
|
78 | | -Principles: |
| 206 | +### Test Guidelines |
79 | 207 |
|
80 | | -- Incremental, boring, and obvious code |
81 | | -- Single responsibility; avoid premature abstractions |
82 | | -- Tests for new behavior; don’t disable existing tests |
| 208 | +- Test behavior, not implementation |
| 209 | +- One assertion per test when possible |
| 210 | +- Clear test names describing scenario |
| 211 | +- Use existing test utilities/helpers |
| 212 | +- Tests should be deterministic |
83 | 213 |
|
84 | | -Process: |
| 214 | +## Important Reminders |
85 | 215 |
|
86 | | -1. Understand existing patterns |
87 | | -2. Add/adjust tests |
88 | | -3. Implement minimally to pass |
89 | | -4. Refactor with tests passing |
| 216 | +**NEVER**: |
90 | 217 |
|
91 | | -Quality gates: |
| 218 | +- Use `--no-verify` to bypass commit hooks |
| 219 | +- Disable tests instead of fixing them |
| 220 | +- Commit code that doesn't compile |
| 221 | +- Make assumptions - verify with existing code |
92 | 222 |
|
93 | | -- All packages build and tests pass |
94 | | -- Lint/format clean |
95 | | -- Clear commit messages (explain “why”) |
| 223 | +**ALWAYS**: |
96 | 224 |
|
97 | | -When stuck (≤3 attempts): document failures, explore alternatives, question assumptions, try a simpler angle. |
| 225 | +- Commit working code incrementally |
| 226 | +- Update plan documentation as you go |
| 227 | +- Learn from existing implementations |
| 228 | +- Stop after 3 failed attempts and reassess |
0 commit comments