agentleFS
versaticaversatica/mediasoup/worker/AGENTS.md

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
  1. Very specific:

    Never use npm install / npm ci to build the worker: without MEDIASOUP_LOCAL_DEV their postinstall deletes the build directory, the …

    line 8
  2. Very specific:

    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 …

    line 21
  3. Very specific:

    Before making a member const, check that it is not written from subclasses, not passed by non-const reference, and not mutated through …

    line 22
  4. Very specific:

    Do not make members const in value types that are copied, assigned, swapped or stored in containers that need assignment (std::vector …

    line 23
  5. Very specific:

    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 …

    line 29
  6. Very specific:

    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 …

    line 30
  7. Very specific:

    Do not call Stop() on a timer right before destroying it: its destructor already closes it.

    line 38
  8. Very specific:

    Every .hpp includes "common.hpp".

    line 43
  9. Very specific:

    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 …

    line 60
  10. Very specific:

    Never &foo to take an address: use std::addressof(foo).

    line 65
  11. Very specific:

    Never reach through an iterator (it->second.field): bind a named reference or copy first.

    line 66
  12. Very specific:

    Never use .first / .second inline: bind them to a named variable first (const bool inserted = set.insert(x).second;).

    line 67
  13. Very specific:

    Never write the struct keyword before a variable, parameter or return type (RttBackoff rttBackoff;), except where a function hides the type …

    line 69
  14. Very specific:

    Every lambda states its return type explicitly, -> void included.

    line 70
  15. Very specific:

    Write Utils::Foo, never ::Utils::Foo.

    line 72
  16. Very specific:

    Use the std::ranges version of an algorithm whenever there is one, passing the container itself when the algorithm covers all of it …

    line 75
  17. Very specific:

    No input coming from the API or the network may crash the worker.

    line 79
  18. Distinctive:

    All code text is in English.

    line 83
  19. Distinctive:

    No semicolons in log messages or comments: use a comma in logs and split into sentences in comments.

    line 86
  20. Very specific:

    Use ASCII ->, never the Unicode arrow.

    line 87
  21. Very specific:

    Prefer MS_WARN_TAG() / MS_WARN_2TAGS() over MS_DEBUG_DEV() for conditions worth seeing in production.

    line 88
  22. Very specific:

    Never using namespace RTC;: fully qualify names.

    line 93
  23. Very specific:

    Use the existing mocks (mocks::MockShared, mocks::MockTimerHandle, mocks::MockBackoffTimerHandle) instead of writing new ones.

    line 96

≈ “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.

Keep what fits and drop the rest. Your version, ready for your agents.
Files
AGENTS.md

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 make and its tests with MEDIASOUP_LOCAL_DEV=true make test.
  • Never use npm install / npm ci to build the worker: without MEDIASOUP_LOCAL_DEV their postinstall deletes 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 .hpp and 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 x for values, T* const x for pointers (including listener and shared), 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 const in value types that are copied, assigned, swapped or stored in containers that need assignment (std::vector erase/insert, ankerl::unordered_dense maps, std::optional reassignment, etc.), nor in wire-format structs (RTCP, SCTP, STUN headers).
  • Making a member const never moves it: it stays where it was in the class, with const added and nothing else.

Ownership

  • An object owned by a class is held in a std::unique_ptr, never as a raw pointer with new/delete.
  • 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 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_ptr is not const and is set with reset() or std::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 the delete would have been.
  • Exceptions: libuv handles (uv_*_t), which are freed in the uv_close() callback, and raw buffers created with new[].

Timers

  • TimerHandleInterface and BackoffTimerHandleInterface instances are owned through std::unique_ptr, created with shared->CreateTimer() / shared->CreateBackoffTimer(), preferably in the initializer list.
  • Compare the timer received in OnTimer() against this->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 (inactivityCheckPeriodicTimer in RtpStreamRecv -> "rtp-stream-recv-inactivity-check"). A label never ends in -timer, -timeout or -periodic, except to tell apart two timers of the same class.

Includes

  • Every .hpp includes "common.hpp".
  • A .cpp includes "common.hpp" only if it has no paired .hpp (tests, main.cpp, fuzzers).
  • A .cpp never re-includes a header that its paired .hpp already 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 const members always starts with its own public: or private: 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 .cpp starts with MS_TRACE() (MS_TRACE_STD() in the Channel classes and other code that uses the _STD variants).
  • Local declarations inside a function: constexpr first, then const, 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/REQUIRE that checks it, and a check is not glued to the next action.
  • Everything that can be const is const, including const auto locals and const T& parameters. But never replace an explicit type with auto when the right-hand side does not show the type.
  • Value getters are named GetXxx(). Boolean getters are named IsXxx() / HasXxx().
  • Never &foo to take an address: use std::addressof(foo).
  • Never reach through an iterator (it->second.field): bind a named reference or copy first.
  • Never use .first / .second inline: 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 struct keyword 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, -> void included. Hence a lambda never takes auto parameters (clang-tidy fuchsia-trailing-return rejects a trailing return type on a generic lambda): spell out the parameter type, e.g. const std::pair<const uint16_t, OutgoingStream>& entry for a std::map entry.
  • Integer literals have no u suffix (0, not 0u), unless the value does not fit in an int.
  • Write Utils::Foo, never ::Utils::Foo.
  • Access std::optional with .value(), not *opt. In production code, a .value() already guarded by the surrounding flow is left alone (no NOLINT).
  • A function whose result the caller must check (e.g. a bool telling 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::ranges version of an algorithm whenever there is one, passing the container itself when the algorithm covers all of it (std::ranges::fill(container, value), not std::fill(std::begin(container), std::end(container), value)).

Robustness

  • No input coming from the API or the network may crash the worker. MS_ASSERT aborts in release builds too, so it is only for invariants that hold by construction. Inputs are validated at the boundary and rejected with MS_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() over MS_DEBUG_DEV() for conditions worth seeing in production.

Tests (Catch2)

  • Helpers are lambdas defined inside the SCENARIO body, never file-level static functions.
  • Never using namespace RTC;: fully qualify names.
  • A SCENARIO or SECTION name is a single string literal, never split into adjacent literals.
  • Accessing a std::optional after REQUIRE(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 single NOLINT.
  • 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.

Download now (ZIP)

  • 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.

  • In this repository2 other files it gives its agents.

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.