Conventions

Find your footing in the repository and its workflow.

Scope and Precedence

  • Follow the repository AGENTS.md, then the conventions in this file.
  • Keep changes narrow and preserve the style of nearby legacy code.
  • Treat .c files under src/ as C++20 sources compiled with g++.
  • Preserve unrelated worktree changes and never expose local credentials or player data.

Guiding Principles

  • The game thread is the sole owner of mutable P_char and P_obj state.
  • Normal mutation paths and per-pulse callbacks perform no external I/O.
  • Workers receive immutable typed values, never live game-object pointers.
  • Memory is the authority; one writer copies it to the database in capture order.
  • Give every non-idempotent economy, ownership, reward, and audit action a unique ID.
  • Bound time, bytes, retries, and shutdown behavior at every external boundary.
  • Preserve proven InnoDB, transaction, dedupe, pool, and locker-worker foundations.
  • Base schema and performance changes on measured repository or development-clone evidence.

Naming

  • Match nearby names and established domain terms such as PID, epic, frag, rent, and locker.
  • Keep existing public type names such as P_char and P_obj stable unless a scoped refactor requires otherwise.
  • New functions should describe one action; state fields should identify their domain.
  • Boolean and result names should make success, retryability, dirty state, and acknowledgement explicit.
  • Stable operation, event, and query-site IDs must be unambiguous in code and telemetry.

Files and Structure

  • src/ owns server implementation and its Makefile.
  • tests/async/ owns focused regression, runtime, source-contract, and DB-backed tests.
  • migrations/ is the authoritative location for schema changes and schema verification.
  • scripts/ owns startup, formatting, backup, debugging, and maintenance helpers.
  • docs/ owns architecture, database, configuration, testing, and operational guidance.
  • bin/ contains all compiled artifacts and must not be committed.
  • Put changes in the existing domain module unless a new boundary is required by the persistence design.

C++20 and Legacy Code

  • Compile touched server code as C++20; do not rely on C-only compilation behavior.
  • Follow .clang-format for touched C/C++ lines and avoid formatting unrelated legacy code.
  • Keep functions focused, but do not broaden a persistence fix into unrelated modernization.
  • Check all allocation, conversion, query, and serialization bounds using existing safe helpers where available.
  • Initialize every output on every success and failure path before it can influence gameplay.
  • Avoid mutating equipment or affects solely to build a persistence snapshot.

Concurrency and Ownership

  • Build immutable snapshot DTOs on the game thread before enqueueing worker work.
  • Key ordered work by player, account, item, or other owning entity. The one writer applies it in capture order, so no revision fence is needed.
  • Apply worker completions on the game thread and ignore stale acknowledgements safely.
  • Do not call fork() from the running multithreaded server for player or world persistence.
  • Make worker lifecycle states, operation deadlines, retry timing, pool healing, drain, and termination explicit.
  • Never hold a game lock or queue mutex across database, Redis, filesystem, or allocator-heavy work.

Error Handling and Recovery

  • Classify external failures as retryable, terminal, ambiguous commit, or overload outcomes.
  • Memory is the authority: change state in memory at once and let the save write it. Never make the game wait for, or refuse on, the database.
  • A terminal save is queued and the character leaves at once; a save that cannot be queued is reported, never a reason to hold the character.
  • Do not convert Redis or queue failure into a synchronous full save on the game thread.
  • Alerts and counters must report actual durable success, failure, retry, spill, and recovery state.
  • Recovery records require schema versions, checksums, unique IDs, atomic checkpoints, and idempotent replay.
  • Shutdown and copyover stop new mutations and drain to a documented deadline; shutdown always exits, and copyover is called off when the drain fails.

Database Layer

Connections

  • Read DB_HOST, optional DB_PORT, DB_USER, DB_PASSWD, and DB_NAME from the environment; never hardcode or log their values.
  • MySQL or MariaDB with InnoDB is the durable system of record.
  • Set and verify utf8mb4, time zone, isolation level, SQL mode, and bounded connect/read/write deadlines on every connection.
  • Require native TLS or a protected local socket/tunnel for non-local database hosts.
  • Heal failed pool slots asynchronously and expose acquisition, execution, retry, and oldest-work age.

Migrations

  • Put additive schema changes and their verification under migrations/.
  • Keep migrations guarded and re-runnable where practical; never alter production or run operational scripts against it.
  • Use bootstrap_multithread_safe.sql only for an empty database.
  • Read an operational script's argument handling before running it, even on a development database: several have no --help or dry-run mode and start work at once.
  • Apply migrations/run_migration.sh only to a backed-up development clone before promotion.
  • Give new migrations immutable IDs and checksums and record success only after the operation completes.
  • Validate required schema and connection invariants before boot writes lookup data.

Schema and Models

  • A save advances its owner's revision; revisions order history, they do not fence writes.
  • Keep current balance or ownership rows reconcilable with immutable ledger or audit rows.
  • Use foreign keys and uniqueness constraints to enforce dedupe and ownership contracts where applicable.
  • Define explicit limits and errors instead of silently truncating player, inventory, or pet data.
  • Add retention or archival only with documented season, reconciliation, and audit ownership.

Queries

  • Prefer typed repositories and prepared statements for new hot or sensitive paths.
  • Never concatenate untrusted values into SQL; use the database client's parameter or escaping contract consistently.
  • Include mysql/mysql.h and derive statement null-indicator types from MYSQL_BIND, not MariaDB-only my_bool, so the source builds against both clients.
  • Keep hot-path lookups in memory when the source defines game memory as authoritative.
  • Batch related rows and avoid N+1 queries, repeated linear scans, and per-recipient query fan-out.
  • Keep predicates sargable and validate candidate indexes with representative clone data and EXPLAIN ANALYZE.
  • Log stable site ID, error code, duration, and operation ID, never raw SQL or bound private values.

Transactions and Idempotency

  • Critical commands (auctions, the collector, item grants, boons, artifacts, combat outcomes, zone touches) commit in the database, one transaction each on the one writer. Items, wallets, banks, epics and frags change in memory and are saved.
  • The #58 branch also applies epic-derived artifact/guild rewards in memory and saves current snapshots. Its type-9 command records history only; local qualification is recorded in log-server-review.md.
  • Put state owned by a database command and its ledger/outbox in one transaction. Memory-authoritative snapshots and reward history have separate durability; a history receipt does not certify saved gameplay state.
  • Use mandatory unique command or event IDs for ambiguous commits and replay.
  • Store outbox or inbox delivery state in the same transaction as the domain mutation.
  • Publish committed values only for domains whose command owns the state change. History-only completion must not reapply a reward or overwrite current memory.

Redis and Caching

  • Use Redis only for reconstructible cross-process or report cache data.
  • Keep player dirty state in game memory, not in Redis membership.
  • Give Redis connect and command operations bounded timeouts and guard every context before use.
  • Use namespaced and versioned keys, finite TTL with jitter, explicit invalidation, and single-flight rebuilds.
  • Monitor cache, dirty-state, and world-recovery domains independently.
  • Never put a Redis round trip in regeneration, XP, or another per-pulse hot path.

Queues

  • Database writes go to the one persistence writer, in capture order; do not add a second writer or let a job overtake one queued before it.
  • Use typed bounded queues with byte limits, age limits, high/low watermarks, and explicit overload behavior.
  • Preallocate compact queue storage away from the hot producer path and batch compatible operations.
  • Acknowledge only after the destination commits.
  • Track pending, oldest age, high-water mark, retries, drops, and end-to-end latency.

Testing

  • Add or update a focused regression in tests/async/ whenever behavior changes.
  • Run the smallest relevant python3 tests/async/test_<feature>.py or run_<feature>.sh wrapper first.
  • Run make -C src after every C/C++ change; the executable is bin/server/dms_new.
  • Use isolated MySQL or a backed-up development clone for schema and persistence integration tests.
  • Run make test-db only for isolated database suites and make test-all for the complete handoff gate.
  • Test success, retryable failure, ambiguous commit, stale acknowledgement, duplicate replay, bounds, and shutdown paths.
  • Use representative history sizes for load plans; tiny local tables are not capacity evidence.
  • Run the 25-to-200-client workloads and defined fault injections only on non-production ports and data.

Formatting and Build Verification

  • Run ./scripts/format.sh for changed C/C++ lines and ./scripts/format.sh --check to verify.
  • Do not commit generated world output, binaries, object files, logs, archives, or runtime player/account data.
  • Keep compiler warnings at zero and use the repository sanitizer or Valgrind workflows when the risk warrants them.
  • Install or respect ./scripts/install-hooks.sh; do not bypass repository checks.

Observability and Privacy

  • Record query and Redis counts, latency histograms, and errors by stable site ID without values.
  • Correlate external-I/O time with pulse and event-budget metrics.
  • Expose queue age, retries, failures, reconnect state, deadlocks, and lock waits.
  • Remove unconditional /tmp and per-save traces from normal persistence paths.
  • Any diagnostic trace must be explicit, sampled, redacted, non-blocking, size-bounded, and rotated.
  • Never commit or disclose credentials, private keys, IPs, password hashes, player descriptions, logs, or player/account data.

Documentation

  • Update docs/reference/DATABASE.md, docs/reference/ARCHITECTURE.md, README configuration guidance, and the runbook when implemented behavior changes.
  • Describe actual execution boundaries, durability guarantees, deadlines, degraded modes, recovery, and operator actions.
  • Keep source evidence links and acceptance commands current as code moves.

Git and Review

  • Preserve unrelated worktree changes and avoid destructive Git commands.
  • Keep each commit to one logical, reviewable change.
  • Never add co-authors, attributions, Claude-Session, or signed-off-by lines.
  • Review behavior, failure semantics, data migration safety, and observability together.

Local Development Tools

Category Tool Config or Command
Formatter clang-format .clang-format, ./scripts/format.sh
Linter g++ strict warning profile src/Makefile, tests/async/test_compiler_warning_profile.py
Type safety and compiler g++ C++20 with -Werror make -C src
Focused tests Python/Bash tests/async/test_*.py, tests/async/run_*.sh
Full tests Make make test-all
Observability Bounded persistence telemetry and redacted logs src/persistence/persistence_observability.*, log-hygiene regressions
Database tests Isolated MySQL/Docker make test-db and DB-specific wrappers
Git hooks Repository hook installer ./scripts/install-hooks.sh
Database MySQL/MariaDB .env, migrations/run_migration.sh
Dev server DurisMUD supervisor scripts/start_mud.sh --dev

Verification gate

Verification is local; no hosted pipeline runs. The gate is ./scripts/format.sh --all --check, make test-all and make test-db (TESTING.md). The workflow files under .github/workflows/ are kept as the recipe for the checks that are replayed by hand when a change touches them: backup recovery, CodeQL and Trivy, and the site tests.

Infrastructure

Component Provider Details
Hosting externally managed PRODUCTION_DEPLOYMENT.md describes the production host
Database MySQL/MariaDB Required at boot; pooled readiness is exposed without a blocking query
Health DurisMUD WebSocket listener GET /health on DURIS_WEBSOCKET_PORT (default 4050); JSON status; scripts/healthcheck.sh probe

When In Doubt

  • Decide from repository and measured development-clone evidence and record material assumptions.
  • Preserve live state and retryability over optimistic destructive completion.
  • Prefer one durable ordered command over loosely timed cross-system side effects.
  • Report any validation that could not be run.