Skip to content

Commit 67cfee2

Browse files
kixelatedclaude
andauthored
docs: split CLAUDE.md into per-directory guides (#1846)
Co-authored-by: Claude <noreply@anthropic.com>
1 parent 6893b1c commit 67cfee2

9 files changed

Lines changed: 232 additions & 89 deletions

File tree

CLAUDE.md

Lines changed: 26 additions & 84 deletions
Original file line numberDiff line numberDiff line change
@@ -39,64 +39,29 @@ Key architectural rule: The CDN/relay does not know anything about media. Anythi
3939

4040
## Project Structure
4141

42-
```
43-
/rs/ # Rust crates
44-
moq-net/ # Core networking layer (published as moq-net; negotiates moq-lite or moq-transport)
45-
moq-native/ # QUIC/WebTransport connection helpers for native apps; clock example lives in examples/clock.rs
46-
moq-relay/ # Clusterable relay server (binary: moq-relay)
47-
moq-token/ # JWT authentication library
48-
moq-token-cli/ # JWT token CLI tool (binary: moq-token-cli)
49-
moq-cli/ # CLI tool for media operations (binary: moq)
50-
moq-bench/ # Load generator for benchmarking relays (binary: moq-bench)
51-
moq-mux/ # Media muxers/demuxers (fMP4, CMAF, HLS)
52-
moq-audio/ # Native PCM ↔ Opus encode/decode on top of moq-mux
53-
hang/ # Media encoding/streaming (catalog/container format)
54-
libmoq/ # C bindings (staticlib)
55-
moq-ffi/ # UniFFI bindings for Python/Swift/Kotlin (cdylib + staticlib)
56-
moq-boy/ # MoQ Boy emulator publisher (binary: moq-boy)
57-
moq-gst/ # GStreamer plugin (moqsink/moqsrc elements)
58-
59-
/js/ # TypeScript/JavaScript packages
60-
net/ # Core networking layer for browsers (published as @moq/net)
61-
signals/ # Reactive signals library (published as @moq/signals)
62-
token/ # JWT token generation (published as @moq/token)
63-
clock/ # Clock example (published as @moq/clock)
64-
hang/ # Core media layer: catalog, container, support (published as @moq/hang)
65-
watch/ # Watch/subscribe to streams + UI (published as @moq/watch)
66-
publish/ # Publish media to streams + UI (published as @moq/publish)
67-
moq-boy/ # MoQ Boy web viewer (published as @moq/boy)
68-
69-
/py/ # Python packages (uv workspace)
70-
moq-ffi/ # Maturin project: rs/moq-ffi cdylib + uniffi bindings.
71-
# Distribution `moq-ffi` (PyPI); import `moq_ffi`. One
72-
# wheel covers every crate exposed via moq-ffi because
73-
# uniffi-linked libraries can't be split across separate
74-
# wheels. Version tracks rs/moq-ffi (release-py-ffi.yml
75-
# fires on moq-ffi-v* tags). Most callers want `moq-rs`.
76-
moq-rs/ # Pure-python ergonomic wrapper. Distribution `moq-rs`
77-
# (PyPI, since `moq` is taken); import `moq`. Depends on
78-
# moq-ffi via a compatible-release pin (~=0.2.x) so it
79-
# floats to the latest moq-ffi patch. Versioned
80-
# independently: bump py/moq-rs/pyproject.toml by hand; on
81-
# merge to main release-py.yml publishes to PyPI if that
82-
# version isn't already there (registry is the gate).
83-
84-
/swift/ # Swift wrapper over rs/moq-ffi (SwiftPM)
85-
/kt/ # Kotlin wrapper over rs/moq-ffi (Gradle, KMP)
86-
/go/ # Go wrapper over rs/moq-ffi (uniffi-bindgen-go)
87-
# swift/kt/go are in-tree source skeletons.
88-
# CI mirrors them to moq-dev/moq-{swift,kotlin,go}
89-
# on each moq-ffi-v* tag.
90-
91-
/demo/ # Demos and test media
92-
boy/ # MoQ Boy demo (ROM hosting, orchestration justfile)
93-
relay/ # Relay server configs (relay.toml, root.toml, leaf*.toml)
94-
pub/ # Media hosting (vid.moq.dev)
95-
web/ # Web demo (watch/publish examples)
96-
throttle/ # Network throttle script for testing
97-
98-
/doc/ # Documentation site (VitePress, deployed via Cloudflare)
99-
```
42+
Top-level layout only. Per-crate and per-package detail lives in the nested guides (see [Per-Directory Guides](#per-directory-guides)), which sit next to the code and don't rot here.
43+
44+
- `/rs/` - Rust crates: core networking (`moq-net`), native helpers, the relay, CLIs, media muxing/codecs, and the FFI/C bindings. See `rs/CLAUDE.md`.
45+
- `/js/` - TypeScript/JavaScript packages for the browser, published as `@moq/*`. See `js/CLAUDE.md`.
46+
- `/py/`, `/swift/`, `/kt/`, `/go/` - language wrappers over `rs/moq-ffi` (see [Language Bindings](#language-bindings)). `/py/` has `py/CLAUDE.md`; the others defer to their `README.md`.
47+
- `/demo/` - demos and test media: relay configs, the web demo, MoQ Boy, media hosting, and a network throttle script.
48+
- `/doc/` - documentation site (VitePress, deployed via Cloudflare).
49+
50+
## Language Bindings
51+
52+
`rs/moq-ffi` is the single UniFFI core that every non-Rust binding is generated from. The wrappers under `/py`, `/swift`, `/kt`, and `/go` are thin layers over it, and `rs/libmoq` exposes the same core as a C staticlib. So one `moq-ffi` change ripples out to all of them (and their docs) per the [Cross-Package Sync](#cross-package-sync) table. CI mirrors the `swift`/`kt`/`go` source skeletons to `moq-dev/moq-{swift,kotlin,go}` on each `moq-ffi-v*` tag. For Python, most callers want the ergonomic `moq-rs` wrapper rather than the generated `moq-ffi` bindings directly.
53+
54+
## Per-Directory Guides
55+
56+
Language-specific conventions, crate/package maps, and patterns live in nested `CLAUDE.md` files that load automatically when you work under that directory. Before writing code in one of these areas, read its guide (your editor loads it for you, but check it explicitly if you are reasoning about the area without opening a file in it):
57+
58+
- **`rs/CLAUDE.md`** - Rust workspace: crate map, Producer/Consumer model, `poll_*` plumbing, error handling, config/TOML merge, Version matching, testing.
59+
- **`js/CLAUDE.md`** - TypeScript/JS workspace: package map, the signals + Effect reactivity model and its lifecycle rules, Web Components UI, `bun`/Biome tooling.
60+
- **`py/CLAUDE.md`** - Python wrappers: the `moq-ffi` (generated bindings) vs `moq-rs` (ergonomic) split and the `moq` public surface.
61+
62+
The `swift/`, `kt/`, and `go/` directories are thin wrappers over `rs/moq-ffi` (mirrored to external repos); see each directory's `README.md` rather than a dedicated guide.
63+
64+
This root file holds only cross-cutting rules that apply everywhere (writing style, branch targeting, cross-package sync, public-API scrutiny, comment/doc conventions).
10065

10166
## Dependencies
10267

@@ -110,30 +75,10 @@ Key architectural rule: The CDN/relay does not know anything about media. Anythi
11075
3. For JS/TS development, bun workspaces are used with configuration in the root `package.json`
11176
4. Consult `doc/` for documentation and the [IETF datatracker](https://datatracker.ietf.org/doc/draft-lcurley-moq-lite/) for specification drafts when working on protocol-level code
11277

113-
## Version Matching Convention
114-
115-
When matching on `Version` enums, default to the **newest** draft behavior so future versions default forward. Explicitly list older versions:
116-
117-
```rust
118-
// CORRECT: future versions get draft-17+ behavior
119-
match version {
120-
Version::Draft14 | Version::Draft15 | Version::Draft16 => { /* old behavior */ }
121-
_ => { /* newest/draft-17 behavior */ }
122-
}
123-
```
124-
12578
## Writing Style
12679

12780
- **No em dashes (—)** in code, comments, doc comments, commit messages, or any prose. Use a period and start a new sentence, or use a comma/parenthesis if the clauses are tightly bound.
12881

129-
## Rust Conventions
130-
131-
- **Error handling**: Use `thiserror` with `#[from]` for library crates, `anyhow` for binaries. Always add `#[non_exhaustive]` to public `thiserror` enums.
132-
- Use `anyhow::Context` (`.context("msg")`) instead of `.map_err(|_| anyhow::anyhow!("msg"))` for error conversion
133-
- **Config flags + TOML merge**: For any `#[arg]` field on a TOML-loadable config, use `Option<T>` (not bare `bool` / `String` / etc.). The TOML→CLI merge clobbers bare fields with their `Default` when the flag is absent, silently overwriting TOML values. See `rs/moq-relay/src/config.rs::tests` for the regression test; add one for any new flag.
134-
- **Prefer `if let` / `let ... else` over an unwrapping `match`**: a `match` whose only job is to unwrap (`Ok(v) => v` / `Some(v) => v`) reads cleaner as `if let Some(v) = x { ... }` or `let Some(v) = x else { ... };`. Matching on an `Option`/`Result` just to bind the inner value is the tell. Keep `match` when both arms do real work or you need the `Err` / `None` payload.
135-
- **`poll_*` plumbing**: a `Poll::Pending => Poll::Pending` arm usually means `ready!(...)` will collapse the match. And `.map_err(Into::into)` on a fallible result is usually better as `Ok(x?)` (the `?` does the `From` conversion). These compose: `let v = ready!(inner.poll_next(cx))?;` in a `fn -> Poll<Result<...>>` both unwraps the `Poll` and converts the error.
136-
13782
## Comment Conventions
13883

13984
- Keep things brief and avoid comments if the code is self-explanatory. Reserve comments for the non-obvious WHY: a hidden constraint, a subtle invariant, a workaround for a specific bug, behavior that would surprise a reader. This is about *implementation* comments inside function bodies and on private items.
@@ -163,15 +108,12 @@ This applies whenever you add or widen a `pub` item, especially in library crate
163108

164109
## Tooling
165110

166-
- **TypeScript**: Always use `bun` for all package management and script execution (not npm, yarn, or pnpm)
111+
Language-specific tooling (TypeScript/`bun`/Biome, JS async patterns, Web Components UI, Rust/`cargo`) lives in the per-directory guides. See [Per-Directory Guides](#per-directory-guides).
112+
167113
- **Common**: Use `just` for common development tasks
168-
- **Rust**: Use `cargo` for Rust-specific operations
169-
- **Formatting/Linting**: Biome for JS/TS formatting and linting
170-
- **UI**: Plain Web Components in `@moq/watch/ui` and `@moq/publish/ui`, built directly on `@moq/signals`
171114
- **Builds**: Nix flake for reproducible builds (optional)
172115
- **Local-first**: When work can live in a `just` recipe (invoked via `nix develop --command`) or as logic in a GitHub Actions workflow step, prefer the recipe. The same code then runs reproducibly on a developer machine and in CI, and is debuggable locally without pushing commits. Workflow YAML should mostly delegate to `just`; reach for plugins (`dorny/paths-filter`, custom actions, etc.) only when a recipe genuinely can't express the logic.
173116
- **CI**: Prefer building release artifacts inside Nix (`nix build .#pkg`) over relying on runner-provided toolchains and `apt`/`brew` packages. Pinning the build environment in `flake.lock` makes artifacts deterministic and decouples them from drift in GitHub Actions runner images. Reach for the runner-native toolchain only when Nix doesn't fit (e.g. Windows runners).
174-
- **JS async patterns**: Use `Effect.interval()`, `Effect.timer()`, and `Effect.event()` helpers from `@moq/signals` instead of raw `setInterval`, `setTimeout`, `addEventListener`. These handle cleanup automatically when the Effect is closed.
175117

176118
## Testing Approach
177119

@@ -225,7 +167,7 @@ When making changes to the codebase:
225167

226168
## PR Reviews
227169

228-
CodeRabbit reviews PRs automatically, but it has an hourly quota and runs out of org credits. If a PR shows a "Review limit reached" / "out of usage credits" message instead of an actual review, run the `/review` skill locally against the PR to get review feedback without waiting for the quota to refill.
170+
CodeRabbit reviews PRs automatically, but it has an hourly quota and runs out of org credits. If a PR shows a "Review limit reached" / "out of usage credits" message instead of an actual review (or CodeRabbit otherwise fails to produce one), run the `/review` skill locally against the PR to get review feedback without waiting for the quota to refill. Then act on the findings the same way you would CodeRabbit's: push the high-confidence, unambiguous fixes directly, and escalate anything ambiguous, architectural, or open to interpretation by asking first rather than guessing.
229171

230172
When reviewing a PR, always include a list of the public API changes (new/renamed/removed/signature-changed `pub` items in `rs/moq-*` and `js/*`), and call out anything that is breaking per [Branch Targeting](#branch-targeting). Distinguish genuinely public surface from `pub(crate)` / private items so the breaking-change and branch-targeting rules are applied to the right things.
231173

0 commit comments

Comments
 (0)