Session artifact · final
Game Architecture
An architecture document full with details about core systems, implementation patterns and much more..
- Document
- TestGame Production-Chain Strategy PoC
- Created
- 2 August 2026
- Updated
- 2 August 2026
Game Architecture
Executive Summary
TestGame uses Unity 6.5 Update (6000.5.6f1) for a local Windows desktop
strategy PoC.
Authoritative gameplay lives in a Unity-independent, session-scoped C# domain model. Unity and uGUI act as explicit engine and presentation adapters, while a thin Bootstrap assembly composes the application.
Key architectural decisions:
- Commands validate completely and commit atomically before publishing events.
- ScriptableObjects are immutable authoring inputs converted through a strict definition-to-session boundary.
- Production uses exact destination reservations and full restoration on cancel.
- Persistent snapshots and selective typed changes make player-visible causality part of correctness.
- Explicit state machines, typed IDs, injected registries, and focused factories prevent incompatible implementations.
- Edit Mode domain tests carry most rule coverage; focused Play Mode tests verify Unity integration and complete journeys.
Project structure: Hybrid organization with four runtime boundaries and 15 mapped core systems.
Implementation patterns: Three novel and four standard patterns define transactions, feedback, configuration conversion, communication, creation, state transitions, and data access.
Ready for: Implementation-readiness validation and story implementation.
Document Status
This architecture document was completed through the GDS Architecture Workflow.
Steps Completed: 9 of 9 (Complete)
Project Context
Game Overview
TestGame is a real-time fantasy strategy proof of concept about building small, readable production chains whose outputs create immediate and measurable combat or Quest effects.
The intended experience progresses through comprehension, anticipation, agency, and payoff. The project is a bounded learning experiment for its solo creator, not a vertical slice or commercial release candidate.
Technical Scope
Engine: Unity
Platform: Local-only Windows desktop, keyboard and mouse
Reference Layout: 1600×900
Genre: Real-time fantasy strategy / production sandbox
Project Level: Medium complexity, tightly bounded scope
Session Model: Fresh, non-persistent single-player session
Networking: None
Core Systems
| System | Complexity | Architectural significance |
|---|---|---|
| World interaction and camera | Medium | Selection, box selection, control groups, contextual commands, bounded camera, and world/UI focus must coexist cleanly. |
| Building placement | Medium | Pad validation, route preservation, placement previews, and immutable placement require deterministic validation. |
| Mythical Storage | Medium | Shared authoritative resource state feeds concurrent production chains and must provide exact accounting. |
| Production jobs | High | Independent timers, one-job-per-building rules, committed inputs, cancellation refunds, and visible background progress form a transactional state system. |
| Recipes and crafting | High | Crafting crosses storage, job, and inventory boundaries and must commit or reject changes atomically. |
| Inventory reservation | High | Ten slots support reservation, cancellation, upgrades, equipment, eligibility, and full-capacity blockers without item loss or duplication. |
| Equipment and effects | High | Permanent and temporary percentage-based HP modifiers must stack, round, expire, and clamp current HP consistently. |
| Unit commands and navigation | Medium | Five controllable units require selection, movement, attacks, collision avoidance, and navigation around placed buildings. |
| Combat and recovery | High | Deterministic attacks, pursuit limits, defeat, encounter reset, delayed player recovery, and persistent session progression interact closely. |
| Deer and Monster AI | Medium | Separate bounded behavior profiles need predictable acquisition, pursuit, return, fleeing, and unstuck behavior. |
| Morale Flag | High | Deployment validation, spatial membership, visible duration, non-stacking effects, and cleanup require coordinated world and stat state. |
| Hero skills and Scrolls | Medium | Drop, learn, equip, target-preview, execute, and cooldown states must remain distinct and observable. |
| Quests and session counters | Medium | Retroactive progress, independent acceptance, exactly-once updates, and persistent completion require event-driven tracking. |
| Persistent HUD and feedback | High | Storage, timers, selection, Quests, blockers, remedies, effects, and causal values must remain legible across world interactions. |
| Baseline evaluation | Medium | Repeatable sessions, tuning records, performance evidence, and end-to-end checks must support an evidence-based continue/stop decision. |
Technical Requirements
- Sustain an average of at least 60 FPS and a 1% low of at least 55 FPS during the specified ten-minute reference workload on Tim’s development PC.
- Support the 1600×900 reference layout and window resizing without hiding Storage, selection context, active Quest progress, or active job timers.
- Complete three consecutive end-to-end sessions without blockers, corrupted state, or external inspection or mutation through development tools.
- Keep gameplay deterministic enough that exact production and combat consequences can be explained from the game surface.
- Treat invalid actions as side-effect-free operations that identify both the blocker and its remedy.
- Keep all tuning values lightweight and externally configurable without changing player-facing rules.
- Preserve full visual comprehension without requiring audio, hover-only information, networking, accounts, telemetry, or persistence.
- Communicate state through text or iconography as well as color.
- Record performance configuration and tuning changes for repeatable evaluation.
Complexity Drivers
Transactional state across systems
Production and crafting operations span Storage, Building availability, timers, Inventory reservations, and Quest progress. Start, cancel, and completion paths must be atomic to prevent duplication, loss, or contradictory presentation.
Concurrent timed activity
Several Buildings can work independently while the player commands units and fights. Timers and results must continue correctly when contextual panels close.
Unified effect calculation
Equipment and temporary Flag modifiers affect current and maximum HP using specific independent rounding, stacking, removal, and clamping rules. One authoritative calculation path is required.
Event ordering and exactly-once consequences
Monster defeats drive loot and Quest progress; crafting completion drives Inventory delivery and Quest progress. Events must be processed exactly once, including when Quests are accepted after qualifying actions.
World state and UI state synchronization
The game’s success depends on causal readability. Every authoritative gameplay transition must produce a corresponding visible state without making UI objects the owners of gameplay rules.
Recoverable combat state
Player defeat, full-party defeat, Monster reset, delayed respawn, persistent equipment, retained progression, and temporary-effect cleanup must resolve without deadlock.
Runtime navigation constraints
Buildings are placed during play yet cannot obstruct required routes. Navigation and placement validation must share a reliable representation of traversability.
Novel Architectural Elements
- Production as an observable proof system: combat and Quests are validation surfaces for economic choices, so observability is part of game correctness rather than presentation polish.
- Reservation-preserving item upgrade: crafting T2 Armor consumes T1 Armor, reuses its slot reservation, and must restore that exact item and slot on cancellation.
- Baseline-versus-extension boundary: architecture must permit one bounded experiment to be added without prematurely introducing systems excluded from the Baseline.
Technical Risks
- Resource or item duplication/loss across cancellation and completion paths.
- Divergence between authoritative game state and persistent HUD presentation.
- Incorrect HP after overlapping equipment and Flag effects are added or removed.
- Duplicate loot, Quest credit, completion, or recovery events.
- Units becoming stuck during acquire, pursue, return, or runtime obstacle changes.
- UI scaling hiding critical information or making causal feedback unreadable.
- Scene objects or UI panels becoming tightly coupled owners of domain state, making later tuning and extensions expensive.
- Scope creep toward logistics, content breadth, or production-grade frameworks that do not serve the Baseline experiment.
- Performance measurements becoming unreliable if reference hardware, build profile, workload, and profiling method are not recorded.
Engine & Framework
Selected Engine
Unity 6.5 Update — 6000.5.6f1
Rationale: The repository already contains an initialized Unity project using this editor version. Unity is appropriate for the Windows-only 3D strategy PoC, providing mature rendering, input, navigation, physics, UI, profiling, and testing support. Keeping the installed Update release avoids an unnecessary downgrade while the project remains an exploratory PoC.
The editor version is pinned by ProjectSettings/ProjectVersion.txt. Version
changes require an explicit architecture decision and a verified project backup.
Project Initialization
Use the existing Unity project in TestGame/. It is already configured as a
Universal Render Pipeline project. Do not import an opinionated RTS starter
framework: its additional economy, combat, selection, and AI assumptions would
conflict with the deliberately bounded Baseline.
No initialization command is required because the Universal 3D project already exists and its editor/package versions are pinned in the repository.
Engine-Provided Architecture
The following choices are PROVIDED BY THE EXISTING UNIVERSAL 3D PROJECT:
| Component | Solution | Notes |
|---|---|---|
| Rendering | Universal Render Pipeline 17.5.0 | Appropriate for readable stylized 3D and the target performance envelope. |
| Physics | Unity 3D Physics | Use colliders, overlap queries, raycasts, and triggers selectively; deterministic domain rules must not depend on unstable collision ordering. |
| Input | Unity Input System 1.20.0 | Provides action maps, pointer/keyboard input, control groups, and UI/world input separation. |
| Navigation | AI Navigation 2.0.14 | Provides NavMesh pathfinding; runtime placement and route validation remain explicit architecture decisions. |
| Audio | Built-in Unity Audio | Optional in Baseline; no gameplay information may depend on sound. |
| Scene Management | Unity scenes and prefabs | Exact bootstrap and gameplay-scene composition remain to be defined. |
| UI Foundation | Unity UI packages | The presentation framework and state-binding pattern remain to be selected. |
| Build System | Unity Windows player build | Local Windows desktop is the only Baseline target. |
| Testing | Unity Test Framework 1.7.0 | Supports Edit Mode and Play Mode validation. |
| Profiling | Unity Profiler and performance test tooling | Reference hardware, build profile, workload, and measurement method must accompany results. |
Development Tooling
MCP for Unity
Use the open-source CoplayDev MCP for Unity as the preferred editor bridge.
Purpose:
- Inspect scenes, assets, GameObjects, components, and editor state.
- Create and modify editor objects through explicit Unity operations.
- Read compiler and Console output.
- Run tests and automate repetitive editor workflows.
- Validate scene wiring that cannot be proven through source inspection alone.
Installation type: Unity Package Manager plus a local Python-based MCP server.
Package URL:
https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#main
MCP access is development-only, remains local, and must not become a runtime or build dependency. Editor-changing operations require deliberate authorization.
Context7
Use Context7 for current, version-aware Unity and package documentation.
Context7 is documentation tooling only. It neither controls Unity nor becomes a game dependency. Prefer official Unity documentation returned through Context7 when resolving API or package-version questions.
Remaining Architectural Decisions
The following decisions must be made explicitly:
- Gameplay state ownership and dependency direction
- Assembly Definition boundaries
- ScriptableObject configuration versus mutable runtime state
- Commands, events, and presentation update flow
- Production and crafting transaction semantics
- Inventory reservation and cancellation semantics
- Stat modifier stacking, rounding, removal, and HP clamping
- Unit, Monster, and Deer state machines
- Runtime placement and navigation-route validation
- Quest counters and exactly-once event processing
- UI framework and state-binding approach
- Simulation clock, timer behavior, and pause semantics
- Scene/bootstrap composition
- Testing boundaries, diagnostics, and performance evidence
Architectural Decisions
Decision Summary
| # | Category | Decision | Version | Rationale |
|---|---|---|---|---|
| 1 | State ownership | Plain C# domain model with Unity adapters | — | Keeps authoritative rules independently testable and separates engine concerns. |
| 2 | Game definitions | Immutable ScriptableObject definitions with plain C# runtime state | Unity 6000.5.6f1 | Provides Inspector tuning without mutable shared-asset state. |
| 3 | Mutation flow | Synchronous command handlers with post-commit domain events | — | Ensures invalid actions are side-effect free and transactions remain atomic. |
| 4 | Time | Central session clock with system-owned accumulators | — | Makes jobs, cooldowns, effects, and recovery testable without lockstep complexity. |
| 5 | Production transactions | Validate all preconditions, then commit atomically | — | Prevents resource/item loss and supports exact-slot restoration. |
| 6 | Stats | Recalculate derived HP from base values and active modifiers | — | Prevents drift and preserves exact stacking, rounding, and clamping rules. |
| 7 | Code organization | Domain, Unity, Presentation, and thin Bootstrap assemblies plus test assemblies | — | Enforces dependency direction with modest project overhead. |
| 8 | Runtime UI | uGUI + TextMesh Pro with passive views and presenters | uGUI 2.5.0 | One mature framework supports both HUD and world-anchored feedback. |
| 9 | Unit and AI behavior | Explicit finite-state machines with NavMesh adapters | AI Navigation 2.0.14 | Makes transitions, fallback behavior, and recovery observable and testable. |
| 10 | Placement/navigation | Authored pads with baked navigation guarantees | AI Navigation 2.0.14 | Eliminates runtime rebuilding and route-blocking ambiguity. |
| 11 | Scene structure | Single gameplay scene with explicit composition root | — | Provides controlled session lifecycle without unnecessary loading infrastructure. |
| 12 | Quests/progress | Authoritative counters with idempotent event processing | — | Supports retroactive acceptance and exactly-once consequences. |
| 13 | Asset loading | Direct references, prefabs, and ScriptableObject catalogs | — | Fits the fixed Baseline content without Addressables complexity. |
| 14 | Testing | Edit Mode domain tests plus focused Play Mode integration tests | Test Framework 1.7.0 | Balances fast rule verification with engine integration confidence. |
Technology versions were verified against the existing project and current documentation on 2026-08-02.
State Management
GameSession is the session-scoped authoritative domain root. It owns resources,
jobs, Inventory, unit state, effects, progress counters, and Quests.
The domain consists of plain C# and does not reference UnityEngine. Unity
components adapt input, physics, navigation, frame updates, and presentation to
domain commands and snapshots. No globally mutable singleton or service locator
owns gameplay rules.
Small explicit finite-state machines govern player units, Monsters, Deer, and encounter recovery. State transitions have guards and defined fallbacks.
Configuration and Runtime Data
ScriptableObjects hold immutable authored definitions:
- Recipes and resource outputs
- Items and eligibility
- Units and first-playable tuning
- Skills and cooldowns
- Quests
- Global balance/timing values
Session startup validates these assets and creates mutable plain C# runtime state. Runtime systems never mutate ScriptableObject assets. Definitions use stable IDs independent of asset names and paths.
Validation detects duplicate IDs, invalid references, negative quantities, unsupported eligibility, and incomplete scene wiring.
Command and Event Flow
Only command handlers mutate authoritative state:
Input/UI → Command → Validate → Atomic commit → Immutable domain events
├─ Progress and Quests
├─ Presentation
└─ Diagnostics
Failures contain a blocker and remedy, change no state, and publish no success event. Events describe facts that have already committed and cannot veto or rewrite the originating transaction.
Event routing is session-scoped with explicit registration. UI renders snapshots of current authoritative state rather than attempting to reconstruct state from event history.
Timing Model
A Unity adapter advances the session using scaled Time.deltaTime. Domain code
never reads Unity’s static time APIs.
Each timed system owns explicit accumulated or remaining time. Large frame deltas are consumed safely, including multiple resource-generation intervals. Jobs, cooldowns, Flags, and recoveries complete once.
Closing panels does not affect simulation. Pausing advances gameplay by zero; presentation may use unscaled time for appropriate visual behavior. Tests advance a fake clock by exact durations.
Deterministic replay, rollback, and lockstep simulation are not required.
Production and Inventory Transactions
Job start validates every precondition before mutation:
- Building availability and Recipe support
- Exact committed inputs
- Item eligibility
- Destination capacity
- Specific Inventory reservation when required
Processing commits resources without reserving Inventory. Final Crafting reserves one exact slot before consuming inputs. Cancellation refunds exact committed inputs and releases that reservation. Completion converts the reserved slot directly to occupied.
T2 Armor consumes one eligible, unequipped T1 Armor and reserves the same slot. Cancellation restores that exact item instance to that exact slot.
Jobs have stable IDs, and start, completion, and cancellation paths are
idempotent. Inventory ownership follows Empty → Reserved → Occupied; focus,
equipment, and eligibility are orthogonal attributes.
Stats and Effects
Base maximum HP is immutable during a session. Equipment and temporary effects contribute modifiers identified by stable source IDs.
Each percentage modifier is independently calculated from base HP using round-half-up, then summed. Sword, Armor, and one Flag modifier may coexist; a second Flag modifier is rejected.
Derived stats are recalculated from base values and active modifiers rather than incrementally patched. Adding an HP item increases current and maximum HP by the derived increase. Removal recalculates maximum HP and clamps current HP to the new maximum, never below one for a living unit.
Defeat, recovery, equipment, and Flag expiration all use this same stat service.
Assembly Boundaries
TestGame.Domain
Plain C# state, rules, commands, events, and definition interfaces
TestGame.Unity
MonoBehaviours, ScriptableObjects, input, physics, navigation,
scene composition, and domain adapters
→ references TestGame.Domain
TestGame.Presentation
HUD, panels, presenters, and world feedback
→ references TestGame.Domain and narrow Unity-facing interfaces
TestGame.Bootstrap
SceneCompositionRoot and top-level application composition only
→ references TestGame.Domain, TestGame.Unity, and TestGame.Presentation
TestGame.Tests.EditMode
→ primarily tests TestGame.Domain
TestGame.Tests.PlayMode
→ tests adapters, scene wiring, navigation, and integrated journeys
TestGame.Domain never references Unity or Presentation. Presentation cannot
mutate state directly; it submits commands through a narrow session interface.
UI Architecture
Use uGUI and TextMesh Pro for Baseline runtime UI.
Views are passive MonoBehaviours. Presenters read immutable snapshots, format player-facing values, populate views, and submit commands from controls. Structured command failures supply exact blockers and remedies.
Persistent HUD regions use layout groups, anchors, and CanvasScaler. World
feedback uses world-space canvases or camera-projected screen elements. Essential
information never exists only on hover or through color.
Transient elements such as damage values are pooled only when profiling demonstrates a meaningful allocation issue. UI Toolkit may be used for future Editor tooling but is not a second Baseline runtime framework.
Unit and AI Architecture
Player units use:
Idle → Moving → Attacking → Defeated → Recovering → Idle
Monsters use:
Idle → Acquiring → Pursuing → Attacking → Returning → Idle
└──────────→ Defeated
Deer use:
Idle → Fleeing → Idle
└→ Defeated
Domain state owns health, eligibility, defeat, recovery, and encounter rules.
Unity adapters own NavMeshAgent operations and report arrival or path failure.
Player commands replace existing movement or attack intent. Invalid targets, pursuit limits, unreachable paths, and recovery always lead to defined states. Nearby-target acquisition uses bounded physics queries at a controlled cadence, not global per-frame searches.
Placement and Navigation
Placement uses authored BuildingPad objects. Each pad defines accepted Building
types, footprint bounds, and a fixed transform.
Buildings snap to pads positioned outside required navigation corridors. A pad transitions once from available to occupied. Validation checks availability, eligibility, footprint fit, and references before confirmation.
The sandbox uses a baked NavMesh. Runtime rebuilding is excluded. Play Mode tests verify representative routes between the starting area, production zone, Deer zone, and both Monster encounters after every Building is placed.
Scene Composition
BaselineSandbox.unity is the only required scene:
BaselineSandbox
├─ SceneCompositionRoot
├─ Environment
├─ Navigation
├─ PlacementPads
├─ UnitsAndEncounters
├─ WorldFeedback
└─ ScreenUI
The composition root validates references, loads definitions, constructs
GameSession, and connects adapters and presenters in an explicit order.
The session is disposed on scene unload, and every load creates fresh state.
Gameplay managers do not use DontDestroyOnLoad. A bootstrap/loading scene is
deferred until a real second scene or asynchronous initialization requirement
exists.
Quest and Progress Architecture
SessionProgress owns qualifying counters such as Monster defeats and completed
T2 Armor. The originating defeat or Craft-completion transaction updates its
counter atomically before publishing. Stable transaction/event IDs prevent
duplicate application.
Quest acceptance initializes from the relevant session counter. Active Quests
derive progress from those counters rather than maintaining competing values.
Quest state only transitions Available → Active → Complete, with completion
emitted once.
Loot eligibility belongs to each Monster’s authoritative state. A designated Monster can produce its configured Scroll once. Popups consume committed events; persistent trackers render Quest snapshots.
Assets and Loading
The scene and composition root use explicit serialized references to prefabs and definition catalogs. Definition assets may directly reference their icons, prefabs, and static dependencies.
Do not use Resources.Load, string-based asset paths, or Addressables in the
Baseline. Missing dependencies fail through Editor validation or session startup.
The small fixed roster and world objects are instantiated normally. Addressables may be reconsidered only for a later milestone requiring multiple large scenes, downloadable content, external catalogs, or demonstrated memory pressure.
Data Persistence and Networking
Baseline sessions begin fresh and persist nothing between launches. No save file, PlayerPrefs gameplay state, cloud synchronization, accounts, networking, or backend service is included.
Runtime API routing, authentication/authorization, databases, background jobs, remote file storage, and security-sensitive external services are not applicable to this local single-player Baseline. This is an explicit scope decision, not an unresolved architectural placeholder.
Settings persistence is deferred with the broader options suite. The architecture must not introduce abstractions for hypothetical multiplayer or saves.
Audio
Use Unity’s built-in audio only if functional cues are added after the visual loop is stable. Gameplay comprehension never depends on audio. FMOD, Wwise, voice, and adaptive music systems are outside the Baseline.
Testing Strategy
Edit Mode tests cover:
- Resource accounting and Recipe validation
- Job timing, completion, and cancellation
- Inventory reservation and exact T1 restoration
- Modifier stacking, rounding, removal, and HP clamping
- Quest counters and duplicate-event handling
- State-machine transitions and recovery rules
Play Mode tests cover:
- Composition-root and scene-reference wiring
- Input adapters and placement feedback
- Representative NavMesh routes
- World/UI synchronization
- Morale radius integration
- Defeat, encounter reset, and recovery
- One complete production-to-Quest journey
Tests use builders, fake clocks, and deterministic IDs. Every corrected transaction or transition defect receives a regression test.
Performance validation runs in a Windows development build using the documented ten-minute workload. Manual evaluation covers comprehension, causality, aggression-versus-preparation perception, resize behavior, and creator motivation.
Architecture Decision Records
ADR-001 — Keep authoritative gameplay outside MonoBehaviours
Unity objects adapt engine facilities; plain C# owns rules and session state.
ADR-002 — Treat every cross-system mutation as a transaction
Commands validate all conditions before committing, and events publish only
after success.
ADR-003 — Prefer explicit Baseline mechanisms over speculative frameworks
No ECS, general RPG framework, behavior-tree package, Addressables, persistence,
network layer, or third-party RTS starter is introduced without demonstrated need.
ADR-004 — Make observability part of correctness
Every authoritative state needed to understand production, combat, and Quests is
available in snapshots suitable for UI, diagnostics, and tests.
ADR-005 — Preserve the Baseline boundary
Extensions may add capabilities through the domain and adapter seams, but must
not silently expand the contracted experiment.
Cross-cutting Concerns
These patterns apply to every system and are mandatory for all implementations.
Error Handling
Strategy: Typed results for expected gameplay failures, startup validation for authored-data failures, and exceptions for broken invariants.
Error classes
| Class | Handling |
|---|---|
| Expected refusal | Return CommandResult; show blocker and remedy; do not mutate or log as an error. |
| Invalid authored data | Reject session startup with asset/reference context. |
| Recoverable integration problem | Log a warning and enter an explicitly defined fallback state. |
| Broken invariant | Throw, log as an error at the Unity boundary, and stop the affected session safely. |
Development and test builds fail loudly. Player builds show a concise critical error panel when safe continuation is impossible.
Empty catch blocks are forbidden. Do not catch Exception merely to continue
with potentially corrupt state.
CommandResult result = session.StartCraft(command);
if (!result.Succeeded)
{
craftView.ShowBlocker(
result.Code,
result.Blocker,
result.Remedy);
return;
}
Logging
Format: Structured plain text
Destination: Unity Console through IGameLogger
External service: None
Domain code never calls UnityEngine.Debug directly.
| Level | Usage |
|---|---|
| Error | Unexpected failure or broken invariant |
| Warning | Handled unexpected condition or degraded fallback |
| Info | Major lifecycle and gameplay milestones |
| Debug | Command validation and state transitions |
| Trace | Detailed timing diagnostics; disabled by default |
Every record includes level, category, stable event name, session ID, and relevant entity, command, event, or job IDs.
Do not log every frame, timer tick, HP refresh, or UI refresh. Development builds enable Debug; release builds default to Info and above. Logs must not include usernames, secrets, or environment tokens.
_logger.Info(
category: "Production",
eventName: "CraftCompleted",
context: new LogContext(sessionId, jobId, recipeId));
INFO Production CraftCompleted session=baseline-01
job=job-0042 recipe=t2-metal-armor
Configuration
Approach: Typed configuration separated by responsibility.
- Genuine invariants use named C# constants.
- Tunable values use immutable, validated ScriptableObject assets.
- Runtime systems receive typed configuration through constructors.
- Runtime state never mutates configuration assets.
- Unity build profiles and Project Settings own platform/build configuration.
- Player-settings persistence and remote configuration are deferred.
Suggested domain configurations:
EconomyConfig
CombatConfig
AiConfig
CameraConfig
FeedbackConfig
Recipes, items, units, Skills, and Quests remain individual definition assets.
Names or metadata state units explicitly, such as CraftDurationSeconds and
AcquireRadiusMeters.
Startup validation rejects invalid ranges, negative durations, duplicate IDs, missing references, and contradictory eligibility.
public sealed class ProductionSystem
{
public ProductionSystem(
EconomyConfigData config,
IGameClock clock,
IGameLogger logger)
{
_config = config;
_clock = clock;
_logger = logger;
}
}
Every core-journey tuning change records its old value, new value, reason, and required retest.
Event System
Pattern: Synchronous, session-scoped, strongly typed dispatcher.
Events are immutable past-tense records:
JobStarted
CraftCompleted
ItemEquipped
UnitDefeated
QuestCompleted
Dispatch occurs only after authoritative state commits. Events cannot veto or rewrite the completed transaction. Subscriber order must not affect gameplay outcomes.
Atomic gameplay consequences, including qualifying session-counter increments, belong inside the originating command handler—not in subscribers. Events serve presentation, notifications, and diagnostics after those counters commit.
Subscriptions are disposable and released with their owning session or view. Events contain stable IDs when idempotency matters. There is no persistent event history or replay system.
public sealed record CraftCompleted(
EventId EventId,
JobId JobId,
RecipeId RecipeId,
ItemInstanceId ItemId,
InventorySlotId SlotId);
_events.Publish(new CraftCompleted(
eventId,
job.Id,
job.RecipeId,
item.Id,
job.ReservedSlotId));
A subscriber failure is logged with event and subscriber context. Independent presentation and diagnostic subscribers may continue; domain correctness cannot depend on their ordering or success.
Debug and Development Tools
Provide a read-only diagnostic overlay showing:
- Session ID, clock state, and frame rate
- Resources and recent committed deltas
- Active jobs, reservations, IDs, and remaining durations
- Unit state, target, path status, HP derivation, and modifiers
- Monster and Deer state-machine information
- Quest counters, processed-event count, and Quest states
- Last command result and recent domain events
- Placement-pad occupancy and validation
- Optional navigation paths and AI-radius gizmos
- Performance markers for session, AI, UI, and command work
F3 toggles the overlay in the Editor and Development Builds.
State-changing test controls live in a separate guarded panel. They use ordinary
commands where possible and log DebugActionExecuted.
#if UNITY_EDITOR || DEVELOPMENT_BUILD
_debugOverlay.Initialize(sessionDiagnostics);
#else
Destroy(_debugOverlay.gameObject);
#endif
No debug UI is included in non-development player builds. Debug tools cannot be required to complete or understand the Baseline. Evaluation records whether they were disabled. Inspector or MCP access never counts as player-facing validation.
Project Structure
Organization Pattern
Pattern: Hybrid—technical boundaries at the top level, game systems within each boundary.
Rationale: Top-level folders and Assembly Definitions enforce dependency direction. Feature folders keep related production, Inventory, combat, and Quest code discoverable without mixing domain rules with Unity or presentation code.
Directory Structure
TestGame/
├─ Assets/
│ ├─ TestGame/
│ │ ├─ Domain/
│ │ │ ├─ TestGame.Domain.asmdef
│ │ │ ├─ Common/
│ │ │ │ ├─ Commands/
│ │ │ │ ├─ Events/
│ │ │ │ ├─ Identifiers/
│ │ │ │ ├─ Logging/
│ │ │ │ ├─ Results/
│ │ │ │ └─ Time/
│ │ │ ├─ Session/
│ │ │ ├─ Economy/
│ │ │ │ ├─ Resources/
│ │ │ │ ├─ Production/
│ │ │ │ └─ Recipes/
│ │ │ ├─ Inventory/
│ │ │ ├─ Equipment/
│ │ │ ├─ Effects/
│ │ │ ├─ Units/
│ │ │ ├─ Combat/
│ │ │ ├─ Skills/
│ │ │ ├─ Quests/
│ │ │ └─ Placement/
│ │ ├─ Unity/
│ │ │ ├─ TestGame.Unity.asmdef
│ │ │ ├─ Definitions/
│ │ │ ├─ Input/
│ │ │ ├─ Camera/
│ │ │ ├─ Selection/
│ │ │ ├─ Navigation/
│ │ │ ├─ Placement/
│ │ │ ├─ Units/
│ │ │ ├─ Combat/
│ │ │ ├─ Effects/
│ │ │ ├─ World/
│ │ │ ├─ Diagnostics/
│ │ │ └─ Logging/
│ │ ├─ Bootstrap/
│ │ │ ├─ TestGame.Bootstrap.asmdef
│ │ │ └─ Composition/
│ │ ├─ Presentation/
│ │ │ ├─ TestGame.Presentation.asmdef
│ │ │ ├─ Common/
│ │ │ ├─ Hud/
│ │ │ ├─ Selection/
│ │ │ ├─ Buildings/
│ │ │ ├─ Production/
│ │ │ ├─ Inventory/
│ │ │ ├─ Equipment/
│ │ │ ├─ Combat/
│ │ │ ├─ Skills/
│ │ │ ├─ Quests/
│ │ │ ├─ Placement/
│ │ │ ├─ Notifications/
│ │ │ └─ Diagnostics/
│ │ ├─ Content/
│ │ │ ├─ Definitions/
│ │ │ │ ├─ Config/
│ │ │ │ ├─ Resources/
│ │ │ │ ├─ Recipes/
│ │ │ │ ├─ Items/
│ │ │ │ ├─ Buildings/
│ │ │ │ ├─ Units/
│ │ │ │ ├─ Skills/
│ │ │ │ └─ Quests/
│ │ │ ├─ Prefabs/
│ │ │ │ ├─ Buildings/
│ │ │ │ ├─ Units/
│ │ │ │ ├─ Items/
│ │ │ │ ├─ Effects/
│ │ │ │ ├─ World/
│ │ │ │ └─ UI/
│ │ │ ├─ Art/
│ │ │ │ ├─ Models/
│ │ │ │ ├─ Materials/
│ │ │ │ ├─ Textures/
│ │ │ │ ├─ Animations/
│ │ │ │ ├─ VFX/
│ │ │ │ └─ Icons/
│ │ │ ├─ Audio/
│ │ │ │ ├─ SFX/
│ │ │ │ └─ Mixers/
│ │ │ ├─ UI/
│ │ │ │ ├─ Fonts/
│ │ │ │ ├─ Sprites/
│ │ │ │ └─ Themes/
│ │ │ └─ Input/
│ │ │ └─ TestGameInputActions.inputactions
│ │ ├─ Scenes/
│ │ │ └─ BaselineSandbox.unity
│ │ ├─ Settings/
│ │ │ ├─ Rendering/
│ │ │ └─ Physics/
│ │ └─ Tests/
│ │ ├─ EditMode/
│ │ │ ├─ TestGame.Tests.EditMode.asmdef
│ │ │ ├─ Common/
│ │ │ ├─ Economy/
│ │ │ ├─ Inventory/
│ │ │ ├─ Equipment/
│ │ │ ├─ Effects/
│ │ │ ├─ Units/
│ │ │ ├─ Combat/
│ │ │ ├─ Skills/
│ │ │ ├─ Quests/
│ │ │ └─ Placement/
│ │ └─ PlayMode/
│ │ ├─ TestGame.Tests.PlayMode.asmdef
│ │ ├─ Composition/
│ │ ├─ Input/
│ │ ├─ Navigation/
│ │ ├─ Placement/
│ │ ├─ Presentation/
│ │ └─ Journeys/
│ ├─ Settings/
│ └─ ThirdParty/
├─ Packages/
│ ├─ manifest.json
│ └─ packages-lock.json
└─ ProjectSettings/
All first-party files belong under Assets/TestGame. Package Manager dependencies
remain under Packages. Imported source assets remain isolated under
Assets/ThirdParty/<VendorOrPackage>.
No Resources, StreamingAssets, Addressables groups, save-data folders, network
folders, or deferred content categories are created for the Baseline.
System Location Mapping
| System | Location | Responsibility |
|---|---|---|
| Session lifecycle | Domain/Session |
Authoritative session root and snapshots |
| Composition | Bootstrap/Composition |
Validate and connect definitions, session, adapters, and presenters |
| Commands, results, events | Domain/Common |
Shared typed interaction contracts |
| Clock and timers | Domain/Common/Time |
Elapsed-time abstraction and timed-state rules |
| Storage/resources | Domain/Economy/Resources |
Quantities and exact accounting |
| Recipes/jobs | Domain/Economy/Recipes, Production |
Validation, input commitment, timing, cancellation, completion |
| Inventory | Domain/Inventory |
Slot ownership, reservations, and item instances |
| Equipment | Domain/Equipment |
Eligibility and equip/remove transactions |
| Stat effects | Domain/Effects |
Modifier sources, stacking, rounding, and clamping |
| Unit state | Domain/Units |
Identities, health, roles, and state machines |
| Combat | Domain/Combat |
Attacks, damage, defeat, recovery, encounter reset |
| Skills | Domain/Skills |
Learn, equip, target, execute, and cooldown rules |
| Quests | Domain/Quests |
Counters, acceptance, progress, and completion |
| Placement rules | Domain/Placement |
Pad eligibility and occupancy |
| Input/control groups | Unity/Input, Unity/Selection |
Convert Input System activity into commands |
| Camera | Unity/Camera |
Pan, rotate, zoom, and framing bounds |
| Navigation | Unity/Navigation |
Agent paths and arrival/failure reports |
| Placement world adapter | Unity/Placement |
Preview, pad detection, and visualization |
| Unit world adapters | Unity/Units, Unity/Combat |
Bind GameObjects and engine queries to domain state |
| Morale radius | Unity/Effects |
Spatial membership and visible area |
| HUD/panels | Presentation/Hud and feature folders |
Render snapshots and submit commands |
| World feedback | Presentation/Combat, Notifications, Placement |
Damage, effects, blockers, and confirmations |
| Definition types/assets | Unity/Definitions, Content/Definitions |
ScriptableObject schemas, instances, and conversion |
| Diagnostics | Unity/Diagnostics, Unity/Logging, Presentation/Diagnostics |
Logging, overlays, profiling, and guarded controls |
| Domain tests | Tests/EditMode/<System> |
Rules, transactions, calculations, and transitions |
| Integration tests | Tests/PlayMode/<Integration> |
Scenes, adapters, navigation, UI, and journeys |
Naming Conventions
Code elements
| Element | Convention | Example |
|---|---|---|
| Namespace | TestGame.<Boundary>.<Feature> |
TestGame.Domain.Production |
| Class, record, enum | PascalCase noun | ProductionJob |
| Interface | I + PascalCase |
IGameClock |
| Method | PascalCase verb phrase | StartCraft |
| Property | PascalCase noun | RemainingSeconds |
| Private field | _camelCase |
_activeJobs |
| Parameter/local | camelCase | recipeId |
| Constant | PascalCase | InventoryCapacity |
| Boolean | Positive Is/Has/Can form |
CanEquip |
| Command | Imperative + noun + Command |
StartCraftCommand |
| Event | Past-tense fact | CraftCompleted |
| Error code | Stable PascalCase identifier | InventoryFull |
| Test | Method_WhenCondition_ExpectedResult |
CancelCraft_WhenActive_RestoresInputs |
One public top-level type is allowed per C# file, and the filename matches it.
Game assets
- Scenes and prefabs: PascalCase.
- Definitions:
<Type>_<Id>, such asRecipe_IronSword. - Materials:
M_<Name>. - Textures:
T_<Name>. - Sprites:
S_<Name>. - Animator controllers:
AC_<Subject>. - Animation clips:
<Subject>_<Action>. - Audio clips:
SFX_<Action>. - UI prefabs:
UI_<Role>. - Stable content IDs: lowercase kebab-case, such as
t2-metal-armor.
Use canonical GDD vocabulary. Avoid vague names such as Manager, Helper,
Util, and Data when a specific role can be named.
Architectural Boundaries
- Domain contains no
UnityEnginereferences. - Unity adapters may reference Domain but not Presentation implementation.
- Presentation references Domain snapshots and narrow Unity-facing interfaces.
- Bootstrap alone references Domain, Unity, and Presentation to construct the application.
- Presentation submits commands and never mutates runtime state directly.
- ScriptableObject instances remain separate from mutable session objects.
- Editor-only code lives in
Editorsubfolders outside runtime assemblies. Commonis used only for proven shared concepts, never miscellaneous helpers.- Tests mirror their production ownership.
- Third-party types do not cross into Domain contracts; adapters isolate them.
- A feature may span boundaries, but each file has one unambiguous responsibility.
Implementation Patterns
These patterns are mandatory wherever applicable. They remove implementation choices that different agents might otherwise resolve incompatibly.
Novel Patterns
Reserved Outcome Transaction
Purpose: Coordinate committed inputs, Building occupancy, exact Inventory reservation, timed completion, and full cancellation restoration without partial state or inferred refunds.
Components:
TransactionPlan— immutable accepted inputs, destination, and restoration planProductionJob— plan, lifecycle, and remaining durationCommittedResource— exact resource ID and quantityCommittedItem— exact item instance and original slotInventoryReservation— exact slot and expected outputProductionSystem— sole validator and transaction ownerCommandResult— side-effect-free rejection- Post-commit events — presentation, progress, and diagnostics facts
Lifecycle:
Requested
→ validate every precondition
→ atomically reserve destination, commit inputs, and create job
→ Running
├─ cancel → restore every committed input exactly → Cancelled
└─ finish → replace exact reservation with output → Completed
→ publish committed event
Once accepted, the job contains everything needed to complete or fully restore itself. Cancellation never recalculates a refund from the current Recipe definition. Partial refunds are unsupported.
T2 Armor records the exact T1 item instance and its original slot, then reserves that slot for the output.
public CommandResult CancelJob(JobId jobId)
{
ProductionJob job = _jobs.GetActive(jobId);
foreach (CommittedResource resource in job.Plan.Resources)
_storage.Add(resource.ResourceId, resource.Quantity);
foreach (CommittedItem item in job.Plan.Items)
_inventory.Restore(item.OriginalSlotId, item.Item);
_inventory.ReleaseOrRestore(job.Plan.Destination);
_buildings.MarkIdle(job.BuildingId);
_jobs.MarkCancelled(jobId);
_events.Publish(new JobCancelled(
_eventIds.Next(),
job.Id,
job.RecipeId));
return CommandResult.Success();
}
Use this pattern whenever an operation commits inputs before delayed delivery.
Causal Feedback Contract
Purpose: Make player-visible consequences and refusals precisely explainable without coupling domain logic to UI strings.
Components:
CommandResultwith stable refusal code and blocker/remedy arguments- Typed player-visible change records
- Committed domain events
- Authoritative snapshots
- Presenters and notification policy
Flow:
Player action
→ validate
├─ reject → stable code + blocker/remedy arguments
└─ commit → typed change + event
├─ presenter reads fresh snapshot
├─ persistent UI displays current truth
└─ transient UI highlights the transition
Typed changes are required for player-visible resource, job, Inventory, equipment, Skill, HP, damage, cooldown, loot, Quest, placement, defeat, recovery, and encounter-reset consequences. Internal bookkeeping changes remain diagnostic.
Payloads contain exact values and cause IDs, never formatted UI strings. Persistent UI remains authoritative after transient feedback disappears.
public sealed record HpChanged(
UnitId UnitId,
int PreviousCurrentHp,
int CurrentHp,
int PreviousMaxHp,
int CurrentMaxHp,
EffectSourceId CauseId);
public void Present(HpChanged change)
{
UnitSnapshot current = _session.GetUnit(change.UnitId);
_unitView.SetHealth(current.CurrentHp, current.MaximumHp);
_notifications.ShowHpChange(
change.PreviousMaxHp,
change.CurrentMaxHp,
change.CauseId);
}
Use this pattern for every player-triggered command and player-visible outcome.
Definition-to-Session Boundary
Purpose: Preserve convenient Unity authoring without leaking ScriptableObject identity, mutability, or Unity serialization into domain logic.
Components:
- ScriptableObject definition and catalog assets
DefinitionValidator- Asset-to-domain converters
- Immutable
DefinitionRegistry - Separate
UnityContentRegistry - Composition root
Flow:
Authored assets
→ validate values, IDs, uniqueness, and references
→ convert asset references to stable IDs
→ create immutable domain records
→ validate domain relationships
→ construct session and Unity content registry
Domain definitions contain no UnityEngine.Object, prefab, sprite, asset-path, or
ScriptableObject reference. Assets and paths never serve as gameplay identity.
Conversion runs once during composition; any failure blocks session startup and
identifies the source asset and field.
DefinitionBuildResult result = _definitionBuilder.Build(_catalog);
if (!result.Succeeded)
throw new DefinitionValidationException(result.Errors);
GameSession session = new(
definitions: result.DomainRegistry,
clock: _clock,
logger: _logger);
_contentRegistry.Initialize(result.UnityContent);
Use this pattern for every authored gameplay definition.
Standard Patterns
Component Communication
Pattern: Explicit dependency injection with boundary-specific references.
- Plain C# services and presenters use constructor injection.
- MonoBehaviours use private serialized references for required Unity objects.
- The composition root supplies runtime dependencies.
- Same-GameObject
GetComponentis allowed only during initialization and cached. - Direct interfaces handle commands; typed events communicate committed facts.
Find*, mutable singletons, service locators, and repeated runtime discovery are forbidden.
public sealed class ProductionPresenter
{
public ProductionPresenter(
IGameSession session,
IProductionView view,
IGameLogger logger)
{
_session = session;
_view = view;
_logger = logger;
}
}
Entity Creation
Pattern: Domain-first creation with focused Unity prefab factories.
- Create or accept authoritative domain identity before a world view.
- Resolve prefabs through
UnityContentRegistry. - Bind every adapter to a stable domain entity ID.
- Gameplay code does not call
Instantiatedirectly. - Scene-authored entities register through composition.
- Pools are profiling-driven and own no authoritative state.
ItemInstance item = _session.CreateCraftedItem(itemDefinitionId);
ItemView view = _itemViewFactory.Create(item.Id, itemDefinitionId);
view.Bind(item.Id);
State Transitions
Pattern: Centralized guarded transition methods.
- Represent state with one enum or sealed state type, not competing booleans.
- Only the owning machine changes state.
- Validate transitions through an explicit transition table.
- Run exit behavior, replace state, run entry behavior, then publish.
- Animation follows gameplay state and never owns it.
- Invalid transitions fail loudly in development and tests.
public void TransitionTo(UnitState next, TransitionCause cause)
{
if (!_transitionTable.IsAllowed(State, next))
throw new InvalidStateTransitionException(State, next, cause);
UnitState previous = State;
Exit(previous);
State = next;
Enter(next, cause);
_events.Publish(new UnitStateChanged(
_eventIds.Next(),
Id,
previous,
next,
cause));
}
Data Access
Pattern: Injected typed registries and narrow snapshots.
- Domain definitions come from typed registries keyed by stable IDs.
- Runtime state remains owned by
GameSession. - Consumers receive immutable snapshots, not mutable collections.
- Unity content uses a separate typed registry.
- Required missing data fails during composition.
- Optional content uses explicit
TryGet. - Commands contain domain IDs, never GameObject references.
- No Asset Database,
Resources.Load, string paths, or generic data manager.
RecipeDefinition recipe = _recipes.Get(command.RecipeId);
ProductionSnapshot state =
_session.GetProductionSnapshot(command.BuildingId);
Sprite icon = _content.GetItemIcon(recipe.OutputItemId);
Consistency Rules
| Concern | Convention | Enforcement |
|---|---|---|
| Identity | Stable typed ID value objects | Constructors and definition validation |
| Mutation | Commands are the only public mutation entry point | Domain API and tests |
| Failure | Typed result for expected refusal | Command-handler contract |
| Transaction | Validate all, then commit once | Transaction tests |
| Events | Immutable, typed, past-tense, post-commit | Dispatcher API |
| Time | Session clock and explicit elapsed seconds | No domain Unity-time reference |
| State | One explicit state with guarded transitions | Transition tables and tests |
| Definitions | Strict ScriptableObject-to-domain conversion | Assembly boundary and validator |
| Presentation | Snapshots plus selective typed changes | Presenter interfaces and UI tests |
| Unity references | Serialized or composition-injected | Scene validation |
| Creation | Focused factories bind views to domain IDs | Factory APIs |
| Data lookup | Typed registries; no strings or global manager | Registry interfaces |
| Logging | Structured categories and stable event names | IGameLogger |
| Tests | Every corrected rule defect gets a regression test | Review checklist |
| Scope | No generalized feature until a Baseline need exists | ADR review |
Architecture Validation
Validation Summary
| Check | Result | Notes |
|---|---|---|
| Decision Compatibility | Pass | Bootstrap assembly removes the composition dependency conflict; domain counters commit atomically. |
| GDD Coverage | Pass | Every identified system, platform constraint, performance requirement, and exclusion has architectural support. |
| Pattern Completeness | Pass | Creation, communication, transitions, errors, data access, events, timing, transactions, and presentation causality are defined. |
| Epic Mapping | Pass | All seven development epics map to explicit folders, boundaries, and patterns. |
| Document Completeness | Pass | Every mandatory section exists and no placeholder text remains. |
Coverage Report
Systems Covered: 15/15
Architectural Decisions: 14
Novel Patterns: 3
Standard Implementation Patterns: 4
Development Epics Mapped: 7/7
Epic Mapping
| Epic | Primary Architecture | Status |
|---|---|---|
| 1. Play Space and Interaction | Unity Input, Selection, Camera, Placement, and Presentation | Pass |
| 2. Storage and Production | Domain Economy and Reserved Outcome Transaction | Pass |
| 3. Crafting, Inventory, and Equipment | Domain Production, Inventory, Equipment, and Effects | Pass |
| 4. Units, Combat, and Morale | Domain Units/Combat/Effects and Unity navigation adapters | Pass |
| 5. Loot, Skills, and Quests | Domain Skills/Quests, entity factories, and atomic counters | Pass |
| 6. UX, Accessibility, and Feedback | Presentation boundary and Causal Feedback Contract | Pass |
| 7. Evaluation and Stabilization | Edit Mode, Play Mode, diagnostics, and build profiling | Pass |
Issues Resolved
- Added
TestGame.Bootstrapas the only assembly that composes Domain, Unity, and Presentation. - Moved qualifying progress-counter updates into their originating authoritative transactions.
- Added the executive summary, existing-project initialization status, starter-provided labels, and explicit non-applicable infrastructure decisions.
Validation Date
2026-08-02
Overall Status: PASS
Development Environment
Prerequisites
- Unity Hub
- Unity Editor
6000.5.6f1with Windows Build Support - A C# IDE supported by the project, such as Visual Studio or Rider
- Git
- Node.js 18 or newer for optional MCP tooling
- Python/
uvas required by MCP for Unity’s local server - The existing Unity project at
TestGame/
The repository already contains an initialized Universal 3D project. Do not create a replacement project or run a starter initialization command.
AI Tooling
| Tool | Purpose | Install type |
|---|---|---|
| CoplayDev MCP for Unity | Unity Editor inspection, scene/asset operations, Console access, and Test Runner automation | Unity Package Manager plus local MCP server |
| Context7 | Current, version-specific Unity and package documentation | Remote MCP or local npx client |
MCP for Unity setup
- Open
TestGame/in Unity6000.5.6f1. - Open Window → Package Manager.
- Select Add package from git URL.
- Add
https://github.com/CoplayDev/unity-mcp.git?path=/MCPForUnity#main. - Open Window → MCP for Unity.
- Choose Configure All Detected Clients and verify the local connection.
- Keep MCP editor access local and require deliberate authorization for editor-changing operations.
MCP tooling is development-only and must not become a runtime or player-build dependency.
Context7 setup
Configure the MCP client to use either Context7’s remote endpoint or its local package:
https://mcp.context7.com/mcp
npx -y @upstash/context7-mcp
Use an API key only when required for the selected access tier, and store it in the MCP client’s secret/environment configuration rather than the repository.
Project Setup and Verification
- Open the existing
TestGame/folder through Unity Hub. - Confirm the Editor reports version
6000.5.6f1. - Allow Unity Package Manager to restore the pinned dependencies.
- Confirm URP, Input System, AI Navigation, uGUI, and Test Framework packages resolve without errors.
- Configure MCP for Unity and Context7 if AI-assisted Editor access is desired.
- Run Edit Mode and Play Mode tests through Unity Test Runner.
- Open
BaselineSandbox.unityonce created and validate the composition root before implementing feature systems.
No project initialization command is required.
First Implementation Steps
- Create the four Assembly Definitions and dependency direction.
- Establish typed IDs,
CommandResult, the clock, logger, and event dispatcher. - Implement definition validation and strict ScriptableObject conversion.
- Build the smallest production transaction with Edit Mode tests.
- Add Unity adapters and presentation only after the domain behavior passes.
