# SimCord

> SimCord is an offline integration-testing framework for discord.py bots. It runs a real discord.py Client, Bot, AutoShardedClient, or AutoShardedBot against an in-memory Discord implementation. Tests need no bot token, network connection, or Discord test server.

Use SimCord when an AI coding agent creates or changes a discord.py bot. It gives the agent deterministic behavioral feedback through pytest instead of relying on guessed mocks.

## Start here

- [AI coding agents](https://simcord.readthedocs.io/guides/ai-coding-agents/): Agent workflow, project instructions, and acceptance checks.
- [Quickstart](https://simcord.readthedocs.io/quickstart/): Install SimCord and run the first test.
- [Core concepts](https://simcord.readthedocs.io/concepts/): Builders arrange state, actors perform user actions, and queries inspect real discord.py objects.
- [API reference](https://simcord.readthedocs.io/api/): Public Python interfaces.
- [Parity matrix](https://simcord.readthedocs.io/parity-matrix/): Exact implemented and unsupported Discord behavior.

## Common tasks

- [Test a discord.py bot with pytest](https://simcord.readthedocs.io/guides/testing-discord-py-bots/)
- [Test without a token or test server](https://simcord.readthedocs.io/guides/test-without-token/)
- [Test slash commands and interactions](https://simcord.readthedocs.io/guides/testing-slash-commands/): actors enforce command visibility; use `available_commands()` to inspect visible leaf invocations, and `UserHandle.slash()` / `.autocomplete()` for bot DMs.
- [Choose simulation instead of mocks](https://simcord.readthedocs.io/guides/mocks-vs-simulation/)
- [Component preview and screenshots](https://simcord.readthedocs.io/guides/preview/): Protocol 3, the Conversation-layout slash-command picker (`window.simcordPreview.commandPicker`), the page-authorized `GET /api/commands` catalog, bounded authorized navigation, typed receipts, and capture geometry.
- [Recipes](https://simcord.readthedocs.io/cookbook/)

## Installation

```bash
python -m pip install "simcord[pytest]"
```

The tested bot must be constructible without calling `bot.run()`. Provide a pytest fixture named `simcord_bot` that returns a fresh bot. Use the built-in `simcord_env` fixture in tests.

## Ground rules for agents

- Exercise observable bot behavior through SimCord actors rather than directly calling command callbacks.
- Assert on real objects in the bot cache or SimCord handles.
- Keep tests offline. Never request or invent a Discord token.
- Check the parity matrix before assuming a Discord route or event is implemented.
- Treat `RouteNotImplemented` as a visible parity gap, not as permission to fake success.
- SimCord requires Python >=3.11, tests Python 3.11–3.14, and declares discord.py
  >=2.7.1,<3. Locked CI tests discord.py 2.7.1; a separate weekly workflow runs
  against upstream `master`, not every released 2.x version.
- In 2.0, dispatched handlers join runnable bot work. Declare intentional external
  waits with `await env.external_wait(awaitable, reason="...")`; unknown waits time
  out with diagnostics and can be recovered by a later operation or `env.settle()`.
  Public operations reject overlap before mutating state.
- Preview snapshots use protocol 3: `messageIndex` is a separate authorized page of at most 50
  summaries; `messages` holds full authorized projections. Queries are NFC/case-folded plain text
  (at most 128 Unicode code points); denied and deleted exact IDs have the same unavailable result.
  Entity candidates are control-scoped, paged to at most 50, and keep separately authorized selected
  values; raw entity `component.default_values` are not projected. Protocol 2 has no compatibility
  shim. Read `window.simcordPreview` as immutable schema 1 / protocol 3 status; readiness, completion,
  publication, transport, actual visibility and projected IDs are separate. Never share private
  snapshots, recipes, captures, capability URLs or reference images; Copy/Download support reports
  use an allowlist. Preview is not calibration/accessibility-certified; 3.0 releases with an explicit maintainer evidence-gate waiver.

## Migrating from 1.x

- Replace implicit or coroutine-name parking with `env.external_wait(..., reason="...")`.
- To avoid production coupling, inject the test environment's wait adapter only in
  tests and use the normal awaitable in production, or keep indefinite waits outside
  dispatched handlers in your application's supervisor.
