24 KiB
AGENTS.md — Ashen Realms
Purpose
This file defines the default working rules for AI coding agents contributing to Ashen Realms.
Ashen Realms is a modern, browser-based dark-fantasy PvE RPG inspired by the structure and long-term progression of classic browser MMORPGs, while using a modern UI, server-authoritative game logic, and a data-driven architecture.
Agents must treat the existing project documentation and implemented code as the source of truth. Do not redesign core systems, introduce new architecture, or generalize systems beyond current requirements unless explicitly requested.
1. Project Priorities
When making implementation decisions, use this priority order:
- Preserve the core gameplay loop.
- Preserve server authority and data integrity.
- Keep systems simple enough for the current development stage.
- Prefer reusable domain systems over content-specific special cases.
- Maintain UI consistency with the existing visual language.
- Keep implementation testable and understandable.
- Avoid speculative architecture for future MMO-scale requirements.
Core gameplay loop:
Explore
→ Travel
→ Hunt / Search
→ Choose encounter
→ Fight
→ Receive loot / progression
→ Improve character
→ Defeat stronger challenges
→ Discover new locations
The project should first be a good RPG and only later become a larger MMO.
2. Required Reading Before Major Changes
Before implementing or changing a larger gameplay, architecture, persistence, or UI feature, inspect the relevant documentation in docs/.
Important project documents include:
docs/design-manifest.md
docs/vertical-slice-world-content-design.md
docs/balancing-items-loot-design.md
docs/ui-visual-design-specification.md
Additional feature specifications may exist in docs/ and override older assumptions where they explicitly redefine a system.
Do not rely only on this file when a more specific feature specification exists.
3. Source-of-Truth Order
If multiple sources conflict, use the following precedence unless the task explicitly says otherwise:
- Explicit requirements in the current task.
- Newer dedicated feature specification.
- Newer project documentation.
- Existing implemented behavior and tests.
AGENTS.md.- Older design documents.
Do not silently reconcile conflicting requirements.
If a conflict affects behavior or data design, point it out before making a broad architectural reinterpretation.
4. Current Technical Architecture
Ashen Realms is implemented as a modular monolith.
Stack
Frontend: Angular
Backend: NestJS
Language: TypeScript
Database: PostgreSQL
ORM: TypeORM
Monorepo: npm Workspaces
API: REST
Deployment: one Ashen Realms application container
Database: separate persistent PostgreSQL service
Typical repository structure:
apps/
web/
api/
packages/
shared/
game-content/
docs/
Production behavior:
Browser
↓
NestJS
├── /api/* → REST API
└── /* → built Angular application
↓
PostgreSQL
NestJS is the only runtime process in the application container.
Do not introduce a second production web server for Angular.
5. Server Authority Is Mandatory
Critical gameplay logic is server-authoritative.
The client may display state and send intentions, but must not decide authoritative gameplay results.
The server owns at least:
- character state
- current HP
- effective stats
- combat state
- combat results
- damage
- enemy actions
- loot rolls
- inventory
- equipment
- currencies
- reputation / progression
- travel state
- travel completion
- encounter generation
- quest progress
- item ownership
- regeneration state
Client requests should express actions, not results.
Good:
{
"action": "ATTACK"
}
Bad:
{
"action": "ATTACK",
"damage": 42
}
Never trust client-provided values that the server can calculate or validate itself.
6. API Rules
All API routes use the prefix:
/api
Frontend requests must use relative URLs.
Good:
this.http.get('/api/characters/me');
Bad:
this.http.get('http://localhost:3000/api/characters/me');
Use consistent domain errors.
Example:
{
"statusCode": 400,
"code": "INVALID_TRAVEL_TARGET",
"message": "The selected location is not connected to the current location."
}
Prefer stable machine-readable error codes over UI-dependent text matching.
7. Data and Persistence Rules
TypeORM
Use TypeORM migrations for schema changes.
Production must not use:
synchronize: true
Schema workflow:
change entity
→ create/generate migration
→ inspect migration
→ run migration
→ run tests
Never accept unexpected destructive migration output without reviewing it.
Content vs Player State
Keep static or semi-static game content separate from player-specific persistent state.
Examples of content definitions:
LocationDefinition
LocationConnection
MonsterDefinition
ItemDefinition
LootTable
NPC definition
Quest definition
Shop definition
Examples of player state:
User
Character
CharacterItem
CharacterEquipment
Travel
Hunt
HuntEncounter
Combat
CombatEvent
QuestProgress
Reputation
Do not duplicate content definitions into every player record.
8. Stable Content Keys
Content should have stable human-readable keys in addition to database IDs where appropriate.
Examples:
south-gate
burned-road
ash-rat
road-bandit
worn-short-sword
Use stable keys for seeds, cross-content references, configuration, and tests where this improves maintainability.
Do not hard-code random UUIDs across fixtures or content definitions.
Seeds must be idempotent whenever practical.
Running a seed repeatedly must not create duplicate content.
9. Gameplay Systems Should Be Data-Driven
Repeated game content should be modeled as data rather than one-off code.
This applies especially to:
- locations
- travel connections
- monsters
- encounter pools
- items
- loot
- NPCs
- shops
- abilities
- quests
- reputation requirements
- enemy categories
- drop categories
Prefer:
shared mechanic + content configuration
over:
if monster == "special-monster-x" then custom branch
However, do not over-generalize before multiple real use cases exist.
A special case is acceptable when the abstraction would be more complex than the current requirement.
10. Combat Design Rules
Combat is round-based.
The player chooses one main action per turn.
The combat system should remain deterministic where the current rules define deterministic behavior.
Core values currently revolve around:
HP
Attack
Weapon Damage
Armor
The original V1 combat model uses:
Raw Damage = Weapon Damage + Attack
and armor mitigation based on:
Damage = Raw Damage × 60 / (60 + Armor)
Minimum successful damage:
1
Do not add systems such as critical hits, dodge, accuracy, random damage ranges, elemental resistance, mana, or complex action points unless a newer dedicated specification introduces them.
Enemy difficulty should come primarily from:
- meaningful stats
- telegraphed actions
- status effects
- defensive states
- interrupts
- phase behavior
- encounter composition
not hidden randomness.
11. Combat Engine Separation
Pure combat rules should be kept separate from persistence and HTTP concerns.
Preferred structure:
CombatController
↓
CombatService
↓
CombatEngineService
CombatEngineService should ideally:
- receive game state
- receive an action
- return resulting state/events
- avoid direct database access
- be easy to unit test
CombatService should:
- load persistent state
- validate ownership and turn rules
- invoke the engine
- persist events/state
- handle victory/defeat
- trigger loot/progression
- commit atomically where necessary
Do not bury combat formulas inside controllers or Angular components.
12. Realtime and Event Delivery
Realtime communication is an event transport layer, not the authoritative game state itself.
The project is moving toward a central game-event connection suitable for systems such as:
- multiplayer combat
- delayed NPC actions
- pets / companions
- turn notifications
- combat state changes
- selected character-state updates
Prefer one authenticated realtime connection with typed event channels/messages rather than one independent socket connection per feature.
Examples of event families:
combat.*
character.*
travel.*
system.*
The server remains authoritative.
The client must still be able to recover state through normal APIs after reconnecting.
Do not make correctness depend solely on receiving every realtime event.
13. Travel Rules
Travel is server-authoritative.
The server calculates:
startedAt
arrivesAt
origin
target
travel state
possible encounter
The client may render a countdown using the server-provided timestamp.
Never move the character merely because a browser timer reached zero.
Travel completion must be validated or finalized by the server.
Travel duration is part of the game-world feeling, not just an arbitrary cooldown.
14. Hunting and Encounters
The player does not directly request arbitrary monsters to fight.
Preferred flow:
start hunt/search
→ server creates valid encounter choices
→ player selects one encounter
→ combat starts from that persisted encounter
Use encounter IDs or equivalent server-issued references.
Do not allow:
POST /combat
{
"monsterId": "anything-the-client-wants"
}
without server-side validation that the encounter is actually available to that character.
15. Progression Direction
Ashen Realms is evolving away from a classic:
kill monster
→ receive XP + money
→ level up
model.
Current project direction emphasizes:
- reputation / renown
- region reputation
- world-level progression
- monster materials
- exchanging materials through NPCs / merchants
- reputation-gated offers
- meaningful inventory/bag constraints
- loot and equipment as primary combat progression
When touching XP, direct monster currency rewards, level gating, merchants, drops, or progression, inspect the newest progression/reputation specifications before using older V1 assumptions.
Do not reintroduce old XP-based progression just because older design documents still contain it.
16. Item and Loot Philosophy
Items must be understandable and meaningful.
Prefer handcrafted items with fixed identity over random-affix chaos.
A good item should create a visible upgrade or gameplay decision.
Loot should be targeted enough that the player can understand why a specific enemy is worth fighting.
General principle:
Drops create excitement.
Deterministic progression prevents frustration.
Bosses and important content should not regularly produce meaningless rewards.
Avoid huge undifferentiated loot tables.
17. Bags and Material Categories
The project uses or plans constrained bags for certain material categories.
This system is intended to make carrying capacity part of progression without turning the full inventory into weight micromanagement.
When implementing drops and inventory:
- distinguish normal inventory from specialized material storage where specified
- support monster/drop categories as data
- do not hard-code category behavior into individual monsters
- keep quest/story items separate from normal inventory capacity where appropriate
Inspect the dedicated bag-system specification before implementing or modifying this system.
18. NPC Model
NPCs may combine multiple capabilities.
Avoid rigid inheritance such as:
BaseNpc
├── MerchantNpc
└── QuestGiverNpc
when an NPC can logically be both.
Prefer composition/capabilities, for example:
NPC
+ dialogue
+ merchant capability
+ quest capability
+ reputation relationship
+ location presence
Shared NPC data may include:
- stable key
- name
- location
- portrait/artwork
- dialogue
- availability
- reputation relationship
- interaction capabilities
Use the dedicated NPC specification where available.
19. UI Design Direction
Ashen Realms must not look like a generic web dashboard or mobile game.
The intended style is:
classic browser RPG structure
+
modern premium dark-fantasy presentation
Key characteristics:
- desktop-first
- persistent top bar
- left-side navigation
- large central artwork/content area
- contextual right-side panel
- restrained footer/status area
- dark metal / stone / leather materials
- muted colors
- limited functional accents
- strong fantasy artwork
- readable information density
- clear interaction states
Avoid:
- SaaS dashboard visuals
- white cards
- glassmorphism
- neon cyberpunk styling
- generic Angular Material appearance
- excessive rounded mobile cards
- stacked mobile-game popups
- arbitrary component-specific visual languages
20. UI Reuse Rules
Before creating a new screen or component:
- inspect existing shared layout/components
- inspect similar screens
- inspect design tokens/styles
- reuse existing UI primitives where appropriate
Likely reusable components include concepts such as:
AppShell
TopBar
SideNavigation
Footer
Panel
PanelHeader
Button variants
HealthBar
CharacterHeader
DangerBadge
EncounterCard
ItemIcon
ItemTooltip
CombatActionButton
CombatLog
PotionSlot
StatusEffectIcon
Do not duplicate the same visual pattern independently in multiple feature folders.
21. Artwork Is Part of the Product
Large artworks are a primary part of the game experience.
UI layout should preserve visual space for:
- locations
- monsters
- characters
- NPCs
- combat scenes
- items
Do not convert major game screens into dense tables or grids merely because that is easier to implement.
Gameplay information must be clear, but the world should remain visually dominant.
22. Frontend Responsibilities
Angular is responsible for:
- rendering server state
- user input
- local UI state
- routing
- animations
- countdown display
- displaying realtime events
- presenting combat events
- accessibility and interaction states
Angular is not authoritative for:
- damage
- loot
- inventory ownership
- travel completion
- combat results
- stat calculation
- progression rewards
- encounter validity
Prefer typed API contracts.
Do not leak TypeORM entities directly into frontend assumptions.
23. Shared Package Rules
packages/shared should contain only genuine cross-boundary contracts and shared enums/types.
Good examples:
DTO contracts
shared enums
event message contracts
API-facing types
Do not place:
- NestJS services
- Angular components
- TypeORM entities
- repository implementations
- backend-only business logic
inside packages/shared.
packages/game-content may contain shared content schemas/enums where this is genuinely useful.
24. Testing Expectations
Changes to domain logic require tests.
High-value test targets include:
- combat calculations
- character effective stats
- travel validation
- regeneration logic
- encounter generation
- loot rolls
- inventory/equipment rules
- reputation changes
- bag capacity rules
- quest progression
- realtime event handling
Use deterministic random sources in tests for random game systems.
Do not write tests that rely on uncontrolled Math.random() behavior.
For API flows, prefer integration tests around real validation boundaries.
For frontend features, test behavior and state handling rather than fragile implementation details.
25. Bug-Fix Workflow
When fixing a bug:
- understand the actual failure
- identify the authoritative layer
- reproduce with a focused test where practical
- implement the smallest correct fix
- run relevant tests
- run lint/build where appropriate
- verify no adjacent behavior regressed
Do not treat symptoms in Angular if the actual bug is an invalid backend state transition.
Do not disable validation merely to make an API call pass.
26. Feature Workflow
For non-trivial features:
- read the relevant spec
- inspect the current implementation
- identify affected domain boundaries
- define the smallest complete slice
- add/adjust tests
- implement backend/domain behavior
- add persistence/migration if needed
- expose API/realtime contract
- implement frontend behavior
- verify end-to-end behavior
- update documentation if behavior or architecture changed
Prefer vertical slices over large disconnected infrastructure work.
27. Scope Discipline
Do not add systems merely because they may be useful later.
For V1 and early development, avoid introducing without explicit need:
- microservices
- Kafka
- RabbitMQ
- Redis
- event sourcing
- CQRS frameworks
- GraphQL
- Kubernetes-specific architecture
- generic plugin systems
- unnecessary abstraction layers
- premature distributed locking
- generic workflow engines
- complex state machines when simple domain state is enough
The default question is:
What is the smallest correct design for the current feature?
28. Avoid Premature MMO Architecture
Future features may include:
- parties
- multiplayer combat
- companions
- pets
- chat
- guilds
- trading
- PvP
- auctions
- crafting
- admin balancing tools
Current code should avoid blocking those ideas, but should not fully implement infrastructure for them before needed.
Design extensible domain boundaries, not speculative subsystems.
29. Transaction Boundaries
Use database transactions for operations that must succeed atomically.
Examples:
combat victory
→ reward generation
→ inventory grant
→ reputation/material changes
→ combat completion
or:
equip item
→ validate ownership
→ replace current slot
→ persist equipment state
Do not leave the character in partially updated gameplay states.
30. Concurrency and Idempotency
Assume clients may retry requests or send duplicate requests.
Important state-changing actions should reject or safely handle duplicates.
Examples:
- completing the same travel twice
- resolving the same combat turn twice
- claiming the same reward twice
- submitting the same quest completion twice
- buying the same transaction twice because of retry
Where useful, enforce correctness with:
- explicit status transitions
- database constraints
- version checks
- unique keys
- transaction locking
Do not rely solely on the UI disabling a button.
31. Character Stats
Effective character stats must have a clear authoritative calculation path.
Prefer a dedicated service such as:
CharacterStatsService
It should combine:
base character values
+ equipment
+ item bonuses
+ active effects
+ future set bonuses/buffs where applicable
Do not independently calculate effective stats in combat, profile, inventory, and UI code.
Use one domain source of truth.
32. HP and Regeneration
Persistent HP and regeneration are server-owned state.
If regeneration is timestamp-based, calculate authoritative HP using server timestamps and persisted regeneration anchors/state.
Realtime updates may improve the UI, but must not be required for correctness.
A reconnect or normal API refresh must be able to reconstruct the correct current HP.
Do not implement HP regeneration as a browser-only interval.
33. Naming and Language
Code, API contracts, identifiers, database names, and new game-content source text should default to English unless an existing subsystem explicitly uses another convention.
Prefer clear domain names over abbreviations.
Good:
currentLocationId
reputationRequirement
travelDurationSeconds
monsterCategory
Avoid unclear names such as:
loc
repReq
dur
mc
Public-facing gameplay text should move toward English consistently as the project is migrated.
34. TypeScript Rules
Prefer:
- strict types
- explicit domain types
- discriminated unions where useful
- enums/unions for finite domain states
- immutable inputs for pure engines where practical
- dependency injection for external/random/time sources when testing benefits
Avoid:
any- magic strings scattered across services
- duplicated status literals
- deeply nested untyped JSON blobs for core domain state
JSONB is acceptable for flexible snapshots/events when the schema is still clearly typed in TypeScript.
35. Time Handling
Use server timestamps for authoritative gameplay timing.
Examples:
- travel
- cooldowns
- HP regeneration
- timed encounter state
- buffs/debuffs
- scheduled NPC/combat actions
Prefer storing absolute timestamps such as:
startedAt
arrivesAt
expiresAt
lastRegeneratedAt
The frontend may derive display countdowns from those values.
Do not persist countdown seconds that decrement every second unless there is a strong domain reason.
36. Logging
Use structured backend logging for meaningful domain and infrastructure failures.
Useful context may include:
- character ID
- combat ID
- travel ID
- encounter ID
- action
- domain error code
Do not log secrets, tokens, passwords, or full sensitive authentication payloads.
Avoid noisy per-frame or per-second logs.
37. Security Basics
Never trust client ownership claims.
Always verify:
- the authenticated user owns the character
- the character owns the item
- the encounter belongs to the character
- the combat belongs to the character
- the requested transition is currently legal
Secrets belong in environment variables.
Never commit real secrets.
Do not expose internal stack traces or database details as user-facing API errors.
38. Documentation Updates
Update documentation when a change:
- alters architecture
- changes a core gameplay rule
- replaces an older system
- introduces a reusable domain pattern
- creates a new persistent data model
- changes API/realtime conventions
- invalidates an existing implementation spec
Do not update documentation for trivial refactors that do not change behavior.
When replacing an old system, prefer clearly marking the old assumption obsolete rather than leaving contradictory active docs.
39. Do Not Silently Change Game Design
Coding agents may identify possible improvements, but they should not silently:
- rebalance items
- change travel times
- change drop chances
- change combat formulas
- alter progression rules
- replace reputation requirements
- redesign UI flows
- add/remove player capabilities
unless the requested task includes that design change.
If implementation requires choosing an unspecified behavior, choose the smallest reversible option and make the assumption explicit.
40. Definition of Done
A feature is complete when applicable:
- requirements from the relevant spec are implemented
- authoritative logic is on the server
- persistence is correct
- migrations exist and were reviewed
- ownership and transition validation exists
- tests cover important domain behavior
- frontend uses the authoritative API/state
- realtime is recoverable after reconnect where used
- build passes
- lint passes
- relevant tests pass
- no unrelated architecture was introduced
- documentation is updated when behavior changed
Do not claim completion if relevant tests or builds are failing.
41. Final Decision Filter
Before adding code, ask:
- Does this improve the current core loop?
- Is this required by the current specification?
- Is the server still authoritative?
- Can this be modeled as reusable data instead of a one-off?
- Am I solving a current problem rather than a hypothetical future problem?
- Does this fit the existing architecture?
- Does the UI still feel like Ashen Realms rather than a generic web app?
- Can the implementation be tested cleanly?
- Does this preserve player progress and data integrity?
- Is there a smaller correct implementation?
When in doubt:
Prefer the smallest server-authoritative, data-driven solution that fits the current specification.