Feature Spec: Inventory Management & Equipment System
| Field |
Value |
| Feature |
Inventory Management & Equipment System |
| Issue |
#TBD |
| Status |
Draft |
| Author |
The Doctor |
| Date |
2026-04-10 |
Overview
What it does
Introduces a character inventory system backed by the Item Catalog (spec #6). Characters can carry items, equip gear into defined slots, and transfer items under DM control. The /inventory Discord command shows a player’s current loadout.
Why it's needed
Inventory management is essential for loot progression, combat equipment, and player agency. This spec connects items to characters and enables the AI Action System to award items.
Out of scope
- Crafting or item upgrades
- Encumbrance penalties beyond weight tracking
- Merchant economy or trading (future spec)
Architecture Notes (The Doctor)
Projects / layers touched
New interfaces in DungeonMaster.Core
IInventoryService — add/remove/equip/transfer items
Integration points with existing systems
Domain Model
Entities
CharacterInventory
PlayerCharacterId (Guid FK, unique)
Items (ICollection)
InventoryItem
CharacterInventoryId (Guid FK)
ItemId (Guid FK)
Quantity (int)
EquippedSlot (EquipmentSlot? nullable)
Enums
EquipmentSlot
MainHand
OffHand
Armor
AccessoryOne
AccessoryTwo
Records
public sealed record InventorySnapshot(
Guid PlayerCharacterId,
IReadOnlyList<InventoryItemDto> Items,
int TotalWeight);
Service Interfaces (CQRS)
public interface IInventoryService
{
Task<InventorySnapshot> GetInventoryAsync(Guid playerCharacterId, CancellationToken ct = default);
Task AddItemAsync(Guid playerCharacterId, Guid itemId, int quantity, CancellationToken ct = default);
Task RemoveItemAsync(Guid playerCharacterId, Guid itemId, int quantity, CancellationToken ct = default);
Task EquipItemAsync(Guid playerCharacterId, Guid itemId, EquipmentSlot slot, CancellationToken ct = default);
Task UnequipItemAsync(Guid playerCharacterId, Guid itemId, CancellationToken ct = default);
Task TransferItemAsync(Guid fromCharacterId, Guid toCharacterId, Guid itemId, int quantity, CancellationToken ct = default);
}
API Contract (Rory)
Endpoints
GET /api/characters/{id}/inventory
POST /api/characters/{id}/inventory/items — add item (DM-only)
DELETE /api/characters/{id}/inventory/items/{itemId} — remove item (DM-only)
POST /api/characters/{id}/inventory/equip — equip item (player/DM)
POST /api/characters/{id}/inventory/unequip — unequip item
POST /api/characters/{id}/inventory/transfer — DM transfer between characters
Discord Commands (Rory — Bot Layer)
/inventory — shows character inventory summary
Test Scenarios (Danny) ⚠️ COMPLETE BEFORE IMPLEMENTATION
Happy path
-
Given a character inventory exists
When the DM adds an item
Then the item appears in inventory with correct quantity
-
Given a character equips a weapon
When EquipItemAsync runs
Then the item is marked with slot MainHand
Edge cases
-
Given an item is already equipped
When it is equipped again
Then the request is rejected with a validation error
-
Given a transfer exceeds quantity
When TransferItemAsync runs
Then the request fails with 400 Bad Request
Error / failure cases
-
Given an invalid item ID
When AddItemAsync runs
Then the API returns 404 Not Found
-
Given a non-DM user attempts to add items
When POST /inventory/items is called
Then the API returns 403 Forbidden
Acceptance Criteria
Functional
Non-functional
Dependencies
Agent Work Breakdown
| Agent |
Task |
Depends On |
| The Doctor |
Approve spec |
— |
| Danny |
Write failing tests from Test Scenarios |
Spec approved |
| Rory |
Implement inventory entities + services |
Danny's tests |
| Rory |
Add API endpoints + Bot /inventory |
Core entities |
| Danny |
Confirm all tests pass |
All implementation |
Definition of Done
Feature Spec: Inventory Management & Equipment System
Overview
What it does
Introduces a character inventory system backed by the Item Catalog (spec #6). Characters can carry items, equip gear into defined slots, and transfer items under DM control. The
/inventoryDiscord command shows a player’s current loadout.Why it's needed
Inventory management is essential for loot progression, combat equipment, and player agency. This spec connects items to characters and enables the AI Action System to award items.
Out of scope
Architecture Notes (The Doctor)
Projects / layers touched
DungeonMaster.Core— inventory entities + servicesDungeonMaster.Infrastructure— EF Core mappings + migrationsDungeonMaster.Api— inventory endpointsDungeonMaster.Bot—/inventorycommandDungeonMaster.Shared— inventory DTOsNew interfaces in
DungeonMaster.CoreIInventoryService— add/remove/equip/transfer itemsIntegration points with existing systems
AwardItemActioninvokesIInventoryServiceDomain Model
Entities
CharacterInventoryPlayerCharacterId(Guid FK, unique)Items(ICollection)InventoryItemCharacterInventoryId(Guid FK)ItemId(Guid FK)Quantity(int)EquippedSlot(EquipmentSlot? nullable)Enums
EquipmentSlotMainHandOffHandArmorAccessoryOneAccessoryTwoRecords
Service Interfaces (CQRS)
API Contract (Rory)
Endpoints
GET /api/characters/{id}/inventoryPOST /api/characters/{id}/inventory/items— add item (DM-only)DELETE /api/characters/{id}/inventory/items/{itemId}— remove item (DM-only)POST /api/characters/{id}/inventory/equip— equip item (player/DM)POST /api/characters/{id}/inventory/unequip— unequip itemPOST /api/characters/{id}/inventory/transfer— DM transfer between charactersDiscord Commands (Rory — Bot Layer)
/inventory— shows character inventory summaryTest Scenarios (Danny)⚠️ COMPLETE BEFORE IMPLEMENTATION
Happy path
Given a character inventory exists
When the DM adds an item
Then the item appears in inventory with correct quantity
Given a character equips a weapon
When EquipItemAsync runs
Then the item is marked with slot
MainHandEdge cases
Given an item is already equipped
When it is equipped again
Then the request is rejected with a validation error
Given a transfer exceeds quantity
When TransferItemAsync runs
Then the request fails with
400 Bad RequestError / failure cases
Given an invalid item ID
When AddItemAsync runs
Then the API returns
404 Not FoundGiven a non-DM user attempts to add items
When POST
/inventory/itemsis calledThen the API returns
403 ForbiddenAcceptance Criteria
Functional
/inventorycommand shows current loadoutIInventoryServiceNon-functional
bigintDependencies
Agent Work Breakdown
/inventoryDefinition of Done