> ## Documentation Index
> Fetch the complete documentation index at: https://docs.internal.sevencanyon.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Backend AI ruleset

> Enforceable rules for AI agents working in the Symfony (PHP) backend

Machine-readable rules for AI assistants and agents working in **`sevendays_backend`**
(Symfony, PHP 8.3). These are the enforceable form of the
[backend coding standards](/development/backend/coding-standards) and the shared
[AI manifesto](/development/ai/manifesto). Each rule has a stable ID so it can be cited in
review.

```yaml theme={null}
ruleset-id: 7days-backend
applies-to: sevendays_backend
language: PHP 8.3
framework: Symfony
id-scheme: BE-<AREA>-NN
derived-from: /development/backend/coding-standards
```

## Rule IDs

Rules use **category-scoped IDs**: `BE-<AREA>-NN`, where `<AREA>` is the (stable, uppercase)
section code below and `NN` is a zero-padded number **within that area** — for example
`BE-ASYNC-01`.

* **To add a rule, take the next free number in the relevant area** (a second async rule is
  `BE-ASYNC-02`), so each section grows independently and the ID always tells you the
  category.
* **IDs are stable.** Never renumber an existing rule or reuse the ID of a retired one —
  mark a dropped rule as retired instead.

## Language & tooling

* **BE-LANG-01** — Add `declare(strict_types=1);` at the top of **every new PHP file**. The
  codebase is mid-migration, so new code always opts in.
* **BE-LANG-02** — Target **PHP 8.3**. Use modern features: backed enums, `readonly`
  properties, constructor property promotion, first-class attributes.
* **BE-LANG-03** — Before proposing a change as done, run the per-file diff gates:
  `bin/phpstan-diff.sh`, `bin/phpcs-diff.sh`, `bin/static.sh`. Never claim a change passes
  CI without running them.

## Coding style

* **BE-STYLE-01** — Follow **PSR-12** plus the Symfony (risky) ruleset. Do not hand-format;
  let the fixer decide. Run `vendor/bin/ecs check src --fix` before finishing.
* **BE-STYLE-02** — Respect the baked-in house conventions: short array syntax `[]`, strict
  comparison `===`, Yoda style for equality checks, ordered class elements, and **no arrow
  functions**. Do not emit `fn() =>` closures.

## Static analysis

* **BE-STATIC-01** — New files must pass **PHPStan level 4 as a hard floor**; aim as high as
  possible toward the level 8 project goal.
* **BE-STATIC-02** — **Never lower the PHPStan level on a file that already passes higher**,
  and never suppress errors with baseline entries or `@phpstan-ignore` to make a check pass.
  Fix the underlying issue instead.

## Structure & modules

* **BE-MOD-01** — Keep controllers thin. Business logic lives in services, not controllers.
* **BE-MOD-02** — Put a bigger feature in its own module directory under `src/` (e.g.
  `src/Raffle`), owning its own `config/services.yaml` and `config/packages.yaml`. Follow
  `src/IGaming` as the reference DDD layout (`Domain` / `Application` / `Infrastructure`).
* **BE-MOD-03** — Communication between modules is **minimal and through a facade**. Do not
  depend on another module's entities, repositories, or internal services directly.

## Multi-project & configuration

* **BE-CONFIG-01** — Keep application code **project-agnostic**. Never branch on the brand:
  no `if ($projectCode === '7days')`, no `match (ProjectCodeEnum::UKCC)`, no per-brand
  conditionals that fork behavior. A brand check buried in logic is a bug waiting for the
  other brand.
* **BE-CONFIG-02** — Drive per-brand differences through **config or data, not control
  flow**. Values belong in `config/project/{project_code}_parameters.yaml` (over
  `default_parameters.yaml`); differing implementations are swapped as services in
  `config/project/{project_code}_config.yaml`; operational differences are DB-driven. Inject
  the parameter/binding and let config select it — adding or changing a brand should be a
  config/data change, not a logic edit. See [Projects: 7days & UKCC](/development/projects).

## Services & DI

* **BE-DI-01** — Use **constructor injection and autowiring**. Do not pull services from the
  container manually or add service-locator patterns without reason.

## Doctrine, entities & migrations

* **BE-DB-01** — There are **four separate databases** (`draw`, `user`, `ticket`, `event`),
  each with its own connection and entity manager. Put a new entity in the directory
  matching its database/mapping prefix; the `draw` EM is the default.
* **BE-DB-02** — **Never query or join across connections/databases in a single query.**
  Coordinate at the service layer or through a module facade.
* **BE-DB-03** — Mapping is **attribute-based** with the underscore naming strategy.
* **BE-DB-04** — Generate and apply migrations with `bin/db_diff` and `bin/db_migrate` (all
  four namespaces). **Do not run bare `d:m:migrate`.**
* **BE-DB-05** — After `bin/db_diff`, **review the generated migration**: confirm it landed
  in the expected namespace and touches only that DB. Clean up auto-generated boilerplate
  comments, leftover `@todo`s, and empty `down()` scaffolding.
* **BE-DB-06** — **Never edit a committed migration** — create a new one. Always provide a
  meaningful `getDescription()`.

## API conventions

* **BE-API-01** — Use **plain Symfony controllers** (no API Platform). API controllers
  extend `App\Controller\Api\ApiController`.
* **BE-API-02** — Declare routes with `#[Route]` and document every endpoint with accurate
  **Nelmio / OpenAPI attributes** (`#[OA\Get]`, `#[OA\Parameter]`, `#[OA\Response]`,
  `#[Security]`) — they are the published API docs.
* **BE-API-03** — In new code use the **Symfony Serializer** with `#[Groups]`. **JMS
  Serializer is legacy** — only touch it in endpoints that already depend on it; never
  introduce it in new code.
* **BE-API-04** — Validate input with the **Symfony Validator** (constraints on request
  DTOs/models in `src/App/Model`) or Form types for complex requests.
* **BE-API-05** — Throw from the **`App\Exception` hierarchy**; let `ApiExceptionSubscriber`
  produce the standard JSON shape. Do not build error responses ad hoc.
* **BE-API-06** — Guard endpoints with `#[IsGranted]` / voters. Session auth is the default;
  JWT is used by the iGaming module.
* **BE-API-07** — Mark cacheable endpoints with the custom `#[CachedEndpoint]` attribute.

## Sensitive data

* **BE-SEC-01** — Store PII / secrets with the custom `encrypted_data` Doctrine type
  (`App\Security\Crypto`). **Never store sensitive columns as plaintext**, and never place
  secrets or customer data in a prompt or agent context.

## Testing

<Warning>
  **BE-TEST-01** — Any code that calculates something or contains non-trivial logic
  (pricing, scoring, date/time math, business rules, state machines, parsers) **ships with
  PHPUnit tests**. Do not mark such a change complete without them.
</Warning>

* **BE-TEST-02** — Cover meaningful branches and edge cases (zero, negative, empty,
  boundary), not just the happy path. Keep unit tests fast and free of I/O; use functional
  tests for HTTP/DB integration. We do **not** target 100% coverage — skip trivial glue.
* **BE-TEST-03** — Build test state with **Zenstruck Foundry** factories (`tests/Factory`)
  and **Stories** (`tests/Story`), not hand-rolled fixtures. Name test classes `*Test.php`,
  data providers `provider*`, and tag with `@group unit` / `@group integration`.

## Async & messaging

* **BE-ASYNC-01** — Async currently runs on **RabbitMQ** (`OldSoundRabbitMqBundle`); Symfony
  Messenger is not in use yet. Keep consumer/handler classes **thin and
  framework-agnostic** — put real logic in a service the consumer calls — so the planned
  move to Messenger is an adapter swap, not a rewrite.

## Design principles & hygiene

* **BE-DESIGN-01** — Aim to comply with **SOLID** and the relevant **PHP-FIG PSRs**; follow
  HTTP semantics (RFC 9110), RFC 9457 Problem Details for errors, and ISO 8601 / UTC for
  dates. Prefer composition over inheritance; avoid premature abstraction.
* **BE-DESIGN-02** — **Remove unnecessary comments** — comment the *why*, not the *what*;
  delete commented-out code and generated placeholders.
* **BE-DESIGN-03** — Use **early returns / guard clauses** over deep nesting.
* **BE-DESIGN-04** — **Leave it nicer than you found it**: clear names, small methods,
  sensible shape. If a change makes a file harder to read, rethink it.

<Note>
  When any of these rules changes, update it here **and** in the
  [backend coding standards](/development/backend/coding-standards) in the same pull
  request, so the AI ruleset never drifts from the human docs.
</Note>
