Docs: DR-026 inventory/equipment/progression foundation + session log

DR-026 records the architecture (data-driven catalog + replicated buffers + reuse of the StatModifier/RPC machinery), the Phase 0-4 roadmap, the 7 validated decisions, and the deliberate gameplay choices (personal-harvest pivot, automation asymmetry, session-only inventory). Adds the Build-section inventory pointer to CLAUDE.md net-zero (39923 bytes, >1KB headroom) by condensing the persistence/world/HUD bullets; trimmed wording archived to the gotchas archive.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-06-08 09:44:03 -07:00
parent e23bebc84b
commit 510c12a980
4 changed files with 245 additions and 8 deletions
@@ -0,0 +1,92 @@
---
date: 2026-06-08
type: session
tags:
- session
- inventory
- equipment
- progression
- items
- netcode
- ghostfield
- harvesting
- ultracode
permalink: gamevault/07-sessions/2026/2026-06-08-inventory-equipment-progression-phase0
---
# Session 2026-06-08 — Inventory · Equipment · Progression (Phase 0 backbone)
> Driven by `/dots-dev` in **ultracode** mode. Operator: *"Expand combat and harvesting for resources… first the
> concept of an inventory and equipment (granting spells/affects); harvesting should require axes/pickaxes/tools;
> this may require a progression system to be scalable — think carefully, handle scale + flexibility."*
## Decision & design
Architecture, phased roadmap, and the 7 Phase-0 decisions live in [[DR-026_Inventory_Equipment_Progression_Foundation]].
Intake forks (one `AskUserQuestion`): **fully per-player inventory · gear-tier + crafting progression ·
weapon-granted swappable abilities · plan + build the Phase 0 backbone**.
Before any code, a **5-lens adversarial design-review Workflow** (netcode/relevancy · determinism-Burst ·
architecture-scale · economy-regression · test-strategy → synthesis) audited the design against the real code.
Verdict **GO_WITH_CHANGES**. Key catches that changed the implementation:
- **Blocker (4/5 lenses):** the planned "add `RefRO<GhostOwner>` to the projectile query" would make GhostOwner a
*required* component → the 8 existing harvest tests seed owner-less projectiles → they'd never match and **fail**,
and the ledger-fallback never runs. **Fix:** optional cached `ComponentLookup<GhostOwner>`, query unchanged.
- **Reachable deposit trigger required** — harvest→bag severs the build economy until deposit; the dev `#if` hook is
not enough → added a real `G` key.
- **Hoist** the catalog/owner-map/buffer lookups out of the per-hit sweep (stays `[BurstCompile]`).
- **Mandatory player re-bake** (new `[GhostField]` buffer changes the ghost hash) + a **headless Play gate** (EditMode
can't test replication; `NetCodeTestWorld` is internal).
- **Scalability add-now:** bake `byte Tier` into `ItemDefBlob` (free re-bake later, but content-only Phase 1/2 now).
## What was built (code-complete, clean compile, 228/228 EditMode green)
**Simulation (`_Project/Scripts/Simulation/Items/`):** `ItemCategory` (byte consts), `ItemDefBlob`/`ItemDatabaseBlob`
(ID-keyed), `ItemDatabase` (blob singleton), `InventorySlot` (`[GhostField]` `OwnerSendType.All` buffer, twin of
`StatModifier`), `InventoryMath` (pure stack/cap/withdraw), `InventoryDepositRequest` (`IRpcCommand`). +
`Tuning.InventoryMaxSlots=24` / `DefaultStackMax=999`.
**Authoring:** `ItemDefinition` SO (byte `Category`/`Tier` to dodge the MCP enum-drop + Burst-enum hazards) +
`ItemDatabaseAuthoring` baker (mirrors `AbilityDatabaseAuthoring`). `PlayerAuthoring` now `AddBuffer<InventorySlot>`.
**Server:** `InventoryDepositSystem` (RPC → withdraw from bag, deposit to `ResourceLedger`, owner-map idiom,
`ItemId==0` = deposit-all). `ResourceHarvestSystem` rerouted: owner read via optional `ComponentLookup<GhostOwner>`,
deposit into the harvesting player's `InventorySlot` (stackMax from `ItemDatabase` if present), remainder/unresolvable
→ ledger.
**Client:** `InventoryDepositSendSystem` (`G` = deposit all + `#if UNITY_EDITOR` static hook, mirrors
`StorageOpSendSystem`). `HudSystem` folds in a read-only inventory panel (bottom-right, toggle `I`, names from the
catalog, `G` hint) — null-safe, observe-only.
**Tests (`_Project/Tests/EditMode/`):** `InventoryMathTests`, `InventoryHarvestTests` (owned→bag / full-bag→ledger /
no-matching-player→ledger), `InventoryDepositSystemTests` (specific / deposit-all / unresolvable). The 8
`ResourceHarvestSystemTests` stay green via the genuine no-owner fallback.
## Play validation (done — task #9 closed)
Focused-editor finish completed this session:
1. Authored 4 `ItemDefinition` assets (Aether/Ore/Biomass + Stone Pickaxe tool, id 10) under `Assets/_Project/Items/`
and wired `ItemDatabaseAuthoring` (Items list assigned via `execute_code` to dodge the MCP ref-drop) into the
`Gameplay` subscene; `refresh_unity scope=all force` re-baked.
2. Headless `execute_code` Play gate (host+client), all green:
- `ItemDatabase` baked into BOTH worlds (4 items); player ghost carries `InventorySlot` in both → **re-bake +
handshake clean** (no ghost-hash desync).
- Server write to the player bag (`InventoryMath.Deposit` Ore x7 + Aether x3) **replicated to the `ClientWorld`
owner** — the proof EditMode can't make.
- `InventoryDepositSendSystem.Deposit(0,0)` (the `G` path) **round-tripped**: server ledger ← bag, both
inventories emptied, empty state replicated back.
- HUD panel (`_invOpen` forced) rendered 4 rows with **catalog names** incl. `Stone Pickaxe x1` (non-resource
lookup works).
## Deliberate decisions to remember (so a later session doesn't "fix" them)
- Manual harvest is personal-then-deposit; **automation stays base-direct** (asymmetry is intentional).
- Inventory is **session-only** — undeposited haul is lost on Continue (Phase 3 adds `SaveData` v3).
- Don't unify `ResourceLedger` / `SharedStorageContainer` / `InventorySlot` (separate decision); don't touch
`SharedStorageContainer`.
## Next-session intent
Finish the focused-editor Play gate above, then **Phase 1 — Equipment slots + `EquipmentEffectSystem`** (weapon →
`AbilityRef.Id`, gear → `StatModifier` bundle by slot `SourceId`), then **Phase 2 — tool-gated harvesting**.