# AGENTS.md Guidance for AI coding agents working in this repository. See for the file convention. ## Coding guidelines These rules apply repository-wide. They are normative: **MUST** = required, **MUST NOT** = prohibited, and **PREFER** = the default unless a concrete reason justifies otherwise. Preserve the snapshot's behavior and compatibility contracts; consistency with established project conventions beats introducing a second style. ## Lifetime and RAII - **MUST** make it evident when every object and resource starts and stops existing. - **MUST** bind files, sockets, processes, handles, locks, subscriptions, and library resources to move-only RAII owners. Destructors must be safe, deterministic, and non-throwing. - **PREFER** automatic storage and the Rule of Zero. Do not use manual cleanup or process exit as resource management. - **MUST NOT** return or retain a pointer, reference, iterator, span, view, callback, or coroutine state that can outlive its referent. - **MUST** give every asynchronous task, timer, callback, process, and subscription an owner plus explicit completion and cancellation paths. No detached work. - **MUST NOT** capture local references in asynchronous callbacks unless completion before scope exit is structurally guaranteed. - **MUST** declare members so reverse destruction order is safe when one member depends on another. ## Ownership - **MUST** give every resource one identifiable owner unless shared lifetime is an essential domain requirement. - **PREFER** values. Use `std::unique_ptr` for exclusive dynamic or polymorphic ownership. - **MUST NOT** use `std::shared_ptr` to avoid deciding ownership. When independent shared owners are necessary, document why and break cycles with `std::weak_ptr`. - **MUST** treat raw pointers and references as non-owning. Never delete through them or assume they extend lifetime. - **MUST** express ownership transfer in the type: factories return values or `std::unique_ptr`; consuming parameters take a value or `std::unique_ptr`. - **MUST NOT** store borrowed data unless the owner's longer lifetime is both guaranteed and locally obvious. ## Copying, movement, and borrowing - **MUST** make value flow clear in every interface: - small, cheap values: pass by value; - required borrows: `T&` or `const T&`; - nullable borrows: `T*`; - call-bounded contiguous/text views: `std::span` or `std::string_view`; - values retained by the callee: accept by value and move into place; - explicit ownership transfer: `std::unique_ptr`. - **MUST NOT** persist a span or view unless the containing type encodes and guarantees the backing storage lifetime. - **MUST** use `std::move` only for intentional transfer. After a move, only destroy, reassign, or perform operations guaranteed for the moved-from state. - **MUST** avoid accidental copies in loops, structured bindings, callbacks, variants, and return paths. Keep inexpensive copies when they make code clearer. - **MUST** explicitly default or delete copy/move operations for resource-owning types when the Rule of Zero cannot apply. - **MUST NOT** return `const` values; it obstructs movement without adding safety. ## Modeling data - **PREFER** values, enums, and `std::variant` over classes with optional fields or mode booleans. Make illegal states unrepresentable when that improves clarity. - **MUST** handle variants and state enums exhaustively so adding a case produces a compile error at every required decision point. - **PREFER** strong types for identifiers, paths, units, money, token counts, and states that would otherwise be easy to confuse. - **PREFER** immutable values. Keep mutation narrow, deliberate, and protected by an invariant. - **MUST** validate external input once at its boundary, then preserve the validated invariant internally. ## Compile-time and zero-cost abstraction - **PREFER** values, templates, concepts, `constexpr`, enums, and `std::variant` when behavior is known at compile time and the result remains readable. - **MUST** use runtime polymorphism only at genuine runtime boundaries such as providers, storage backends, platform services, frontends, and integrations. - **MUST NOT** add allocation, virtual dispatch, type erasure, reference counting, or synchronization unless the required runtime flexibility justifies it. - **MUST** keep templates small, constrained, and domain-named. Do not create a generic framework before the shared invariant and variation points are known. - **MUST NOT** use macros for ordinary abstraction. Contain unavoidable platform, build, and feature macros at narrow boundaries. - **MUST** verify performance-sensitive zero-cost claims with an optimized build, compiler output, or a benchmark—not intuition. ## Abstractions and classes - **MUST NOT** introduce an abstraction unless it materially improves readability, flexibility, correctness, or maintainability. If direct code is clearer, use it. - **MUST NOT** use a class when a value or function suffices. A class must have an identity or resource, mutable state, an invariant to protect, and behavior. - **MUST** name the invariant protected by a class. If no invalid instance state can be described, prefer a value type or free function. - **MUST NOT** perform I/O or expected-failure work in constructors. Use a named factory returning `Result` when construction can fail. - **PREFER** composition over inheritance. Use inheritance only for genuinely substitutable runtime behavior. - **MUST** keep public surfaces minimal; implementation details remain private. ## Functions and dependencies - **MUST** keep functions cohesive and side-effect-free where practical. Push I/O and mutation to adapters and keep domain transformations pure. - **MUST** inject clocks, IDs, filesystems, processes, transports, stores, and other external capabilities. Do not construct them inside domain logic. - **MUST** define dependency interfaces from the consumer's view and expose only the operations that consumer needs. - **PREFER** returned values and typed events over mutation of distant shared state. - **MUST NOT** use service locators, hidden global state, or implicit environment reads in domain code. Resolve configuration at the composition root. ## DRY and modularization - **MUST** keep one authoritative representation of each rule, invariant, protocol, and piece of domain knowledge. - **MUST NOT** extract superficial repeated syntax when doing so hides intent, couples unrelated modules, or creates flags and branches. DRY applies to knowledge, not every repeated line. - **MUST** organize modules around cohesive responsibilities and stable boundaries; each module should have one clear reason to change. - **MUST** keep domain logic independent of UI, transport, storage, platform, and third-party SDK types. Convert representations at adapter boundaries. - **MUST** keep dependencies acyclic and inward: delivery/adapters → application → domain. The domain must not import an outer layer. - **MUST NOT** create `utils`, `helpers`, or `misc` grab-bags. Name modules after their domain responsibility. - **PREFER** a helper beside its only caller. Share it only when cohesive callers depend on the same semantics rather than merely similar syntax. - **MUST** avoid both oversized files that mix concepts and tiny fragments that force readers to cross many files to understand one operation. ## Naming and comments - **MUST** use precise domain names that expose purpose, units, ownership, and state. Prefer `request_timeout` to `timeout` and `message_count` to `count` when the distinction matters. - **MUST NOT** use unexplained abbreviations, single-letter names outside tiny conventional scopes, or vague names such as `data`, `manager`, `helper`, and `util` when a domain name exists. - **MUST** keep terminology consistent across code, tests, logs, documentation, wire formats, and user-facing concepts. - **MUST** write comments for rationale, invariants, ownership/lifetime assumptions, compatibility constraints, and non-obvious tradeoffs—not to narrate syntax. - **MUST** update or remove comments when behavior changes. A stale comment is a defect. - **MUST** document public-interface ownership, borrowing lifetime, executor/thread affinity, cancellation, ordering, and errors when the types do not make them obvious. - **MUST** cite the authoritative behavior or fixture beside byte-level, ordering, or compatibility code that a future cleanup could accidentally break. ## Locality and orthogonality - **MUST** keep related state, invariants, transformations, and error handling near one another. Declare and initialize variables in the narrowest useful scope. - **MUST** handle errors at the layer with enough context to act. Do not repeatedly catch only to log and rethrow. - **MUST** isolate platform code, third-party types, serialization quirks, and compatibility shims behind narrow adapters. - **MUST** keep components independently understandable, testable, and replaceable. - **MUST NOT** combine model conversion, domain decisions, permissions, tool execution, persistence, and presentation in one object. - **MUST** model independent dimensions independently. Avoid boolean clusters and modes that create invalid combinations. - **MUST** use typed events/results across subsystem boundaries, not callbacks that mutate another subsystem's internals. - **MUST** ensure adding a provider, tool, command, backend, or frontend does not require unrelated edits across the repository. ## Errors and invariants - **PREFER** typed `Result` values for expected operational failures. Reserve exceptions for programming errors and library boundaries where exceptions are the natural contract. - **MUST** model domain failure modes in the return type and preserve original context while adding actionable domain context. - **MUST NOT** ignore an error without an explicit reason. Destructors must not throw. - **MUST** use assertions only for programmer invariants, never input validation or behavior required in release builds. ## Verification - **MUST** test success, expected failure, cancellation, early return, partial initialization, shutdown, boundary values, ownership transfer, and compatibility behavior relevant to the change. - **MUST** run formatting, warnings-as-errors, focused tests, and relevant ASan/ UBSan builds before declaring C++ work complete. - **MUST** use byte golden tests—not parsed-value equality—for byte-compatible JSONL behavior. - **MUST** review every stored reference and asynchronous callback for lifetime, every resource for one clear owner, and every interface for explicit copy/move/ borrow semantics. - **MUST NOT** approve code merely because it compiles or passes a narrow test. ## Meta - **MUST** prefer an established convention over introducing a "better" second one. Predictability is the point. - **MUST** delete an abstraction when removing it makes the code easier to read, change, and verify without losing a real invariant. ## Git/PR Workflow - Recent human commit format: (type) imperative summary. - Examples from this repo: (feat) add deployment script, (fix) add shebang to index.ts for execution in github actions, (chore) bump cli version. - AI-created commit format when the user asks for a commit: (type) (openai/gpt-5.5, reviewed T|F, tested T|F) imperative summary. - Before creating an AI commit, ask the user whether a human reviewed the changes so reviewed T|F is accurate. - Mark tested T only after the relevant checks have run successfully. Otherwise use tested F. - Before committing, inspect git status --short, git diff, and git log --oneline -10; stage only intended files. - Run git diff --check before commit/PR handoff.