mediasoup / worker
AGENTS.md7.4k starsChanged 5 days agoISCView on GitHub
These rules apply to every C++ source file and header of the worker, that is, the files matched by the CLANGFORMATPATHS constant in scripts/clang-scripts.mjs (src/, include/, test/, fuzzer/ and mocks/).
We split this AGENTS.md into the rules it gives an agent
23 rules. About 0 are common, 2 are distinctive, and 21 are very specific to this project.
≈0
Common · estimate
Many files like this have a rule of this kind.
≈2
Distinctive · estimate
This author's own choices. Worth a look.
21
Very specific
Tied to their project's names, files or tools. Probably skip.
- Common
- Distinctive
- Very specific
- Very specific: line 8
Never use npm install / npm ci to build the worker: without MEDIASOUP_LOCAL_DEV their postinstall deletes the build directory, the …
- Very specific: line 21
Every member that is set in the constructor and never written again is const: const T x for values, T* const x for pointers (including …
- Very specific: line 22
Before making a member const, check that it is not written from subclasses, not passed by non-const reference, and not mutated through …
- Very specific: line 23
Do not make members const in value types that are copied, assigned, swapped or stored in containers that need assignment (std::vector …
- Very specific: line 29
If it is created once in the constructor and never replaced, the std::unique_ptr is const and is created in the initializer list (a ternary …
- Very specific: line 30
If it is created lazily, conditionally with complex logic, or replaced during the object's life, the std::unique_ptr is not const and is …
- Very specific: line 38
Do not call Stop() on a timer right before destroying it: its destructor already closes it.
- Very specific: line 43
Every .hpp includes "common.hpp".
- Very specific: line 60
Every function and method defined in a .cpp starts with MS_TRACE() (MS_TRACE_STD() in the Channel classes and other code that uses the _STD …
- Very specific: line 65
Never &foo to take an address: use std::addressof(foo).
- Very specific: line 66
Never reach through an iterator (it->second.field): bind a named reference or copy first.
- Very specific: line 67
Never use .first / .second inline: bind them to a named variable first (const bool inserted = set.insert(x).second;).
- Very specific: line 69
Never write the struct keyword before a variable, parameter or return type (RttBackoff rttBackoff;), except where a function hides the type …
- Very specific: line 70
Every lambda states its return type explicitly, -> void included.
- Very specific: line 72
Write Utils::Foo, never ::Utils::Foo.
- Very specific: line 75
Use the std::ranges version of an algorithm whenever there is one, passing the container itself when the algorithm covers all of it …
- Very specific: line 79
No input coming from the API or the network may crash the worker.
- Distinctive: line 83
All code text is in English.
- Distinctive: line 86
No semicolons in log messages or comments: use a comma in logs and split into sentences in comments.
- Very specific: line 87
Use ASCII ->, never the Unicode arrow.
- Very specific: line 88
Prefer MS_WARN_TAG() / MS_WARN_2TAGS() over MS_DEBUG_DEV() for conditions worth seeing in production.
- Very specific: line 93
Never using namespace RTC;: fully qualify names.
- Very specific: line 96
Use the existing mocks (mocks::MockShared, mocks::MockTimerHandle, mocks::MockBackoffTimerHandle) instead of writing new ones.
≈ “Common” is an estimate: at least 1 in 5 of the 36,368 AGENTS.md files we read have a rule of the same kind, not the same words. “Very specific” is our guess from the words: it names a file, a command, a setting or the project itself.
Files
AGENTS.md
These rules apply to every C++ source file and header of the worker, that is, the files matched by the CLANG_FORMAT_PATHS constant in scripts/clang-scripts.mjs (src/, include/, test/, fuzzer/ and mocks/).
Building
- Build the worker with
MEDIASOUP_LOCAL_DEV=true makeand its tests withMEDIASOUP_LOCAL_DEV=true make test. - Never use
npm install/npm cito build the worker: withoutMEDIASOUP_LOCAL_DEVtheirpostinstalldeletes the build directory, the downloaded subprojects and the local PIP/Meson/Ninja (cleanWorkerArtifacts()in../npm-scripts.mjs).
Class members and constructors
- A member that the constructor must set gets no default initializer in the
.hpp, so that the compiler complains if a constructor forgets it. - A member whose value comes verbatim from a constructor argument is set in the member initializer list, never assigned in the constructor body.
- A member that the constructor normalizes, validates or computes before assigning keeps its default initializer in the
.hppand is assigned in the body. - A member that not every constructor sets (several constructors, or a default constructor) keeps its default initializer.
- A member whose initial value is a constant that does not depend on any argument gets that value as its initializer in the
.hpp, not in the initializer list. - The initializer list follows the declaration order of the members. Never reorder members to fit the initializer list.
const members
- Every member that is set in the constructor and never written again is
const:const T xfor values,T* const xfor pointers (includinglistenerandshared),const std::unique_ptr<T>for owned objects,const std::function<...>for callbacks. - Before making a member
const, check that it is not written from subclasses, not passed by non-const reference, and not mutated through non-const methods. - Do not make members
constin value types that are copied, assigned, swapped or stored in containers that need assignment (std::vectorerase/insert,ankerl::unordered_densemaps,std::optionalreassignment, etc.), nor in wire-format structs (RTCP, SCTP, STUN headers). - Making a member
constnever moves it: it stays where it was in the class, withconstadded and nothing else.
Ownership
- An object owned by a class is held in a
std::unique_ptr, never as a raw pointer withnew/delete. - If it is created once in the constructor and never replaced, the
std::unique_ptrisconstand is created in the initializer list (a ternary is fine when it is optional and the condition is known at construction). - If it is created lazily, conditionally with complex logic, or replaced during the object's life, the
std::unique_ptris notconstand is set withreset()orstd::make_unique(). - Where destruction order matters (for example, a DTLS transport that must go before the sockets), keep an explicit
reset()in the destructor at the place where thedeletewould have been. - Exceptions: libuv handles (
uv_*_t), which are freed in theuv_close()callback, and raw buffers created withnew[].
Timers
TimerHandleInterfaceandBackoffTimerHandleInterfaceinstances are owned throughstd::unique_ptr, created withshared->CreateTimer()/shared->CreateBackoffTimer(), preferably in the initializer list.- Compare the timer received in
OnTimer()againstthis->timer.get(). - Do not call
Stop()on a timer right before destroying it: its destructor already closes it. - Timer labels are derived, never invented: the file's class name in kebab-case, then the purpose taken from the member name without
Timer/Periodic(inactivityCheckPeriodicTimerinRtpStreamRecv->"rtp-stream-recv-inactivity-check"). A label never ends in-timer,-timeoutor-periodic, except to tell apart two timers of the same class.
Includes
- Every
.hppincludes"common.hpp". - A
.cppincludes"common.hpp"only if it has no paired.hpp(tests,main.cpp, fuzzers). - A
.cppnever re-includes a header that its paired.hppalready includes. - Standard headers already provided by
common.hpp(<algorithm>,<cinttypes>,<cstddef>,<cstdint>,<functional>,<memory>,<optional>,<utility>) are never included again. Others (<vector>,<map>,<string>...) are included directly where used. - A type newly used in a file gets its own
#include. Never rely on transitive includes (except through the paired.hpp). - Whenever code is touched, check the includes of the file: none missing and none left over.
- A standard header that provides functions or utilities rather than a type of its own name (
<cstring>,<cstdlib>,<cmath>,<limits>,<sstream>...) carries a comment listing what is used from it, with()for functions:#include <cstring> // std::memcmp(), std::memcpy(). Headers named after the type they provide (<vector>,<string>,<map>...) take no comment.
Class layout
- Nested types (
struct,class,enum,using) go at the very top of the class body, before any method, under their own access specifier. - A block of
static constexpr/static constmembers always starts with its ownpublic:orprivate:line, even when the previous block has the same access. - Struct fields that need documentation use multiline
/** ... */comments, never//. - Public methods and class constants are documented with
/** ... */. Parameter docs take a dash:@param nowMs - Current time.
Functions and statements
- Every function and method defined in a
.cppstarts withMS_TRACE()(MS_TRACE_STD()in theChannelclasses and other code that uses the_STDvariants). - Local declarations inside a function:
constexprfirst, thenconst, then non-const, with a blank line between groups. This does NOT apply to the private/protected member declarations of a class: there members are grouped by meaning with their group comment. - Code is split into paragraphs with blank lines: a declaration is not glued to the first thing done with it, an action is not glued to the assert/
REQUIREthat checks it, and a check is not glued to the next action. - Everything that can be
constisconst, includingconst autolocals andconst T¶meters. But never replace an explicit type withautowhen the right-hand side does not show the type. - Value getters are named
GetXxx(). Boolean getters are namedIsXxx()/HasXxx(). - Never
&footo take an address: usestd::addressof(foo). - Never reach through an iterator (
it->second.field): bind a named reference or copy first. - Never use
.first/.secondinline: bind them to a named variable first (const bool inserted = set.insert(x).second;). - Aggregates are built with designated initializers:
{ .ssrc = ssrc, .seq = seq }. - Never write the
structkeyword before a variable, parameter or return type (RttBackoff rttBackoff;), except where a function hides the type (struct stat). - Every lambda states its return type explicitly,
-> voidincluded. Hence a lambda never takesautoparameters (clang-tidyfuchsia-trailing-returnrejects a trailing return type on a generic lambda): spell out the parameter type, e.g.const std::pair<const uint16_t, OutgoingStream>& entryfor astd::mapentry. - Integer literals have no
usuffix (0, not0u), unless the value does not fit in anint. - Write
Utils::Foo, never::Utils::Foo. - Access
std::optionalwith.value(), not*opt. In production code, a.value()already guarded by the surrounding flow is left alone (noNOLINT). - A function whose result the caller must check (e.g. a
booltelling whether it did its job) is declared[[nodiscard]], so that ignoring it fails at compile time instead of relying on the caller to remember. - Use the
std::rangesversion of an algorithm whenever there is one, passing the container itself when the algorithm covers all of it (std::ranges::fill(container, value), notstd::fill(std::begin(container), std::end(container), value)).
Robustness
- No input coming from the API or the network may crash the worker.
MS_ASSERTaborts in release builds too, so it is only for invariants that hold by construction. Inputs are validated at the boundary and rejected withMS_THROW_TYPE_ERROR.
Comments and logs
- All code text is in English.
- A comment must help whoever reads the file cold. Do not write comments that only record a discussion, and do not reference other projects the code may have been ported from.
- Never turn a point raised while working on a change into a comment: why a header was chosen over another, why something is measured one way and not another, what an earlier version did wrong, what a lint check would say. If the code is right, it needs no note defending it.
- No semicolons in log messages or comments: use a comma in logs and split into sentences in comments.
- Use ASCII
->, never the Unicode arrow. - Prefer
MS_WARN_TAG()/MS_WARN_2TAGS()overMS_DEBUG_DEV()for conditions worth seeing in production.
Tests (Catch2)
- Helpers are lambdas defined inside the
SCENARIObody, never file-levelstaticfunctions. - Never
using namespace RTC;: fully qualify names. - A
SCENARIOorSECTIONname is a single string literal, never split into adjacent literals. - Accessing a
std::optionalafterREQUIRE(opt.has_value())needs// NOLINTNEXTLINE(bugprone-unchecked-optional-access). When there are several accesses, bind the value once (const auto& value = opt.value();) with a singleNOLINT. - Use the existing mocks (
mocks::MockShared,mocks::MockTimerHandle,mocks::MockBackoffTimerHandle) instead of writing new ones. - A test expectation states what the code must do. Never change an expected value, widen a tolerance or loosen an assertion just because the code now produces something else.
Where files are headingNew AGENTS.md files have more rules about keeping secrets out of the code, and fewer about code style.
Of the AGENTS.md files we read, the 28,693 first seen in the last six months, against the older ones: the kind of rule that gained most share, and the kind that lost most. About files like this in general, not this one, and an estimate, since a kind is a pattern in the words.
Does it work?
Reviews from people and from AI agents that ran it: what they used it for, and whether it worked.
Reviews can't be read right now.
Your trail
Files you open will collect here, so you can find them again.
Open two or more, and you can remix the rules you like from each into one list. Kept in this browser only.
Now do this for your own.
Bring your CLAUDE.md, AGENTS.md or skills. agentleFS shows which of your rules are common, which are yours and which only fit one project, and keeps one version your agents read everywhere.

