# Conventions

## Naming

- **Prefix:** All framework types use `Ac` prefix (e.g., `AcDalBase`, `AcBinarySerializer`, `AcLoggerBase`).
- **Interfaces:** Standard `I` prefix + `Ac` (e.g., `IAcDbContextBase`, `IAcUserDbSetBase`).
- **Extensions:** `{Domain}Extensions.cs` (e.g., `StringExtensions`, `CollectionExtensions`, `AcDbSessionExtension`).
- **Test bases:** `Ac{Domain}TestBase` or `AcBase_{TestName}` for inherited test methods.
- **Folder names — plural** (workspace-wide): source folder names plural to avoid type-vs-folder namespace collisions (`Serializers/AcBinarySerializer.cs`, NOT `Serializer/`). Namespace gets its own identity, separate from any single type.
- **English-only identifiers** (workspace-wide): all identifiers (types, members, namespaces, files, folders) use English. Native-language names (Hungarian or otherwise) forbidden even in product/consumer code — readability for LLM/human collaborators.

> **Workspace-wide note:** the framework-only **class prefix mandate** (`Ac` for AyCode.*, `Mg` for Mango.Nop.*; product/consumer repos un-prefixed) is an architectural rule — see `ARCHITECTURE.md#class-prefix--framework-only-mandate`. The first bullet above is the AyCode.Core-specific instance of that rule.

## XML Documentation

`<summary>` — brief, developer-facing, readable in VS IntelliSense tooltip. NO implementation details, NO wire-format / byte-level / perf specifics — those live in `docs/TOPIC/*.md`. Add `<example>` only when usage is non-obvious; otherwise omit.

## Patterns

- **Extension methods over instance methods** for CRUD — clean interfaces, composable impls.
- **Session/Transaction pattern** in DataLayers: `Session()` for reads, `Transaction()` for writes. Both are mutex-protected.
- **Generic interface hierarchy** for entities: Interface → Abstract Entity → Concrete (in consuming project).
- **Partial classes** for large serializers (AcBinarySerializer, AcToonSerializer split across 10+ files).
- **Source generation** for hot-path serialization. Runtime reflection only as fallback.

## Serialization Conventions

- Binary serializer properties are written in **alphabetical order** (matches source generator output).
- String interning is opt-in via `[AcStringIntern]` attribute or `enableInternString` flag.
- Chain API for multi-type deserialization with cross-reference resolution.
- Feature flags on `[AcBinarySerializable]` eliminate dead code from generated output.

## SignalR Conventions

See `AyCode.Services/docs/SIGNALR/README.md` for full architecture documentation.

- **Single dispatch method** — all communication goes through `OnReceiveMessage(int messageTag, int? requestId, SignalParams signalParams, object data)`. Do not add new hub methods.
- **Tag-based routing** — associate methods with integer tags via `[SignalR(tag)]` (server) or `[SignalRSendToClient(tag)]` (client). Tags must be unique across the entire system.
- **CRUD bundles** — entities use `SignalRCrudTags(getAllTag, getItemTag, addTag, updateTag, removeTag)` with 5 independent tag integers. Tags must be unique across the system. See `AyCode.Services/docs/SIGNALR_DATASOURCE/README.md`.
- **Binary protocol** — `AyCodeBinaryHubProtocol` (derived from `AcBinaryHubProtocol`) is the transport protocol. Zero-copy write: `AcBinarySerializer.Serialize(value, output)` directly to pipe. Zero-copy read: `SequenceReader<byte>` + type-aware deserialization via `SignalParams.SignalDataType`. Three read paths: byte[] fast-path (0x44 tag), IsRawBytesData (raw byte[]), typed deserialization.

### ⚠️ Temporary: JSON-in-Binary Request Parameters

Client→server request parameters use a JSON-inside-Binary envelope — cross-cutting tech debt, migration to pure Binary planned. Canonical: `AyCode.Core/AyCode.Core/docs/XCUT/XCUT_ISSUES.md#accore-xcut-i-x8q1`. Cross-refs: `BINARY_ISSUES.md` + `SIGNALR_ISSUES.md` (same ID). Migration: `BINARY_TODO.md#accore-bin-t-s8p4`. Do NOT attempt as a side-effect — requires coordinated client+server+consuming-project changes.

## Testing

- ⚠️ **Release config skips test projects (verification trap)** — `AyCode.Core.sln`'s **Release** configuration leaves several MSTest projects unmapped (no `Build.0` in the `.sln` `ProjectConfigurationPlatforms`), so a **Release-only build/publish does NOT compile the tests**. A test-side break (stale `using`, moved/renamed type, broken reference) therefore stays **hidden in Release** and surfaces only in **Debug**. When a change touches types the tests reference (e.g. relocating a class across assemblies — see `LLMP-DEC-68`), build/verify in **Debug** too — a green Release build alone is not proof the tests compile. (The same `.sln` per-project mapping governs the `SGenOnly`/`Product` configs — see `Directory.Build.props`.)
- **MSTest** framework across all test projects.
- **Abstract test bases** with `AcBase_` prefixed methods for reusable test logic.
- **TestDataFactory** for centralized test data creation with ID sequencing.
- **Testable infrastructure** for SignalR: `TestableSignalRClient2`, `TestableSignalRHub2` bypass real connections.

## Framework-First Placement

Every new type — *generic or consumer-specific?*

| Trait | Verdict |
|-------|---------|
| Contains a consumer's product name, tenant, or URL | **REJECT** from framework |
| Same pattern appears in 2+ consumers | **PROMOTE** to framework |
| Abstract/virtual hooks for consumer customization | **ACCEPT** in framework |
| Requires a specific concrete type from a consumer | **REDESIGN** — use generic/options/extension pattern |

Pre-commit check:
- No consumer-name string in any identifier, namespace, docstring, or doc
- No hardcoded consumer-specific values
- Extension methods / base classes / options classes cover the N-consumer use case

Full doctrine: `ARCHITECTURE.md#framework-vs-consumer-boundary`
