Naming & Style
Namespaces, class names, method names, and field names follow the SET: 3D Edition Naming Conventions
No
#region directives anywhere in the changed filesNo public mutable fields on any entity, value object, or DTO — only properties
All new classes are
sealed unless they are explicitly documented as base classesAll boolean members use an
Is, Has, or Can prefix (IsValid, HasPenalty, CanClaim)Method length does not exceed ~40 lines (document any FSM exhaustive-switch exception)
No method accepts more than 3 parameters without a parameter object
Architecture & Layer Boundaries
No
using UnityEngine; in SET.Domain or SET.Application assembliesNo
using Nakama; in SET.Domain or SET.Application assembliesNo
FindObjectOfType, GameObject.Find, or GetComponent calls outside the Presentation layerNo static mutable fields anywhere in the Domain layer
Infrastructure classes implement interfaces declared in Application or Domain — not the other way around
SET.Presentation accesses Infrastructure only through injected interfaces, never by direct assembly referenceThe fastest way to verify these rules: check the
.asmdef dependency graph and grep for using UnityEngine in the Domain and Application folders. CI will also catch violations via the Roslyn analyzer.Dependency Injection
No
new ConcreteClass() inside Application or Domain methods (factories are the only exception)All dependencies are injected as interface types via the constructor
Any new binding is registered in the Bootstrap scene’s VContainer composition root
No service-locator calls (
ServiceLocator.Get<T>(), Container.Resolve<T>() from inside a class) — resolution happens only at the composition rootReactive UI
No game state polling in any
Update() method — use R3 reactive subscriptionsAll
Subscribe(...) return values are stored in a CompositeDisposable fieldCompositeDisposable is disposed in OnDestroy() (for MonoBehaviours) or in the class’s Dispose() methodDistinctUntilChanged() applied on ViewModel properties bound to UI elementsAny Nakama callback that touches Unity objects or ViewModel state is marshalled to the main thread via
.ObserveOnMainThread()Game Logic
All match state changes go through
GameSession.HandleCommand() — no direct mutation of Board, Player, or Deck from outside GameSessionAnySetExists() is called after every board mutation (card removal, card addition, board expansion)End-game condition is evaluated after every state transition
Input events are discarded (not queued) while
GameSession is in an animation lock state (Validating, Refilling, MatchEnd)Penalty logic uses the
PenaltyMode from GameRules — no hard-coded penalty valuesMultiplayer (complete only if this PR touches multiplayer code)
The client does not call
SetValidator.Validate() on a multiplayer code path — claim validation is server-authoritative onlyThe client sends claim intent only:
SendClaim(cardSlotIds) — not a pre-validated resultServer state is applied to
GameSession only via ApplyServerState() — not via direct board mutationDisconnect and reconnect scenarios handled: grace period countdown shown, full state sync on rejoin
No Nakama types (
IMatch, IMatchState, ISocket) referenced outside NakamaMultiplayerServiceTests
All new Domain and Application logic has corresponding unit tests in
SET.Tests.EditModeAll new test methods follow the
MethodName_Scenario_ExpectedBehavior naming patternAll tests follow the Arrange-Act-Assert (AAA) structure with a blank line separating each section
All tests pass locally (
Run All Tests in Unity Test Runner) before pushingNo tests are commented out or marked
[Ignore] without a linked ticket number in a commentTests assert on observable behaviour (return values, emitted events, state snapshots) — not on internal call counts or implementation details
Definition of Done (Per Feature)
A feature is not done until every item below is true. “Done” does not mean “the happy path works in my dev build.”Code compiles with zero errors and zero new warnings on the CI build server
All CI checks pass:
dotnet format, Roslyn analyzer, unit tests (EditMode), integration tests (PlayMode)At least one reviewer has approved the PR with no unresolved blocking comments
The Hard Boundaries document confirms this feature is in scope for v1.0 — no out-of-scope work has been introduced
Every new public interface and every non-obvious public method has an XML-doc comment (
/// <summary>...)The PR description explains what changed and why — not just a list of files edited
If this feature introduces a new third-party SDK or package, the package version is pinned and documented
Common Reasons PRs Are Returned
These are the most frequent violations. If you catch yourself doing any of these, fix them before submitting.Related Pages
Coding Conventions
The naming, formatting, and class design rules this checklist enforces.
Approved Patterns
The architectural patterns and banned anti-patterns checked in the Architecture section.
Testing Standards
Unit test naming, AAA structure, and coverage targets referenced in the Tests section.
Phase Breakdown
Per-phase Definitions of Done that feed into this checklist.