Skip to content

Project Layout

Where a new file goes, and why. This document exists so that layout is a decision made once, not re-litigated per pull request.


1. Top level

AppleJackRP's&box/
├── Applejack.sbproj         S&box project descriptor. Declares the addon, its type (game),
│                             its ident, and its asset/code roots. Generated by the editor
│                             on project creation; hand-edited only for metadata.
├── Assets/                  GameResource assets: items, jobs, recipes, prefabs. No code.
├── Code/                    All C# source, one folder per module.
├── UnitTests/                Engine-agnostic tests. Auto-detected by the S&box editor,
│                             which generates the MSTest project — see
│                             Standards/03_TESTING_STANDARDS.md §2.
├── .editorconfig             Authoritative formatting. Warnings are errors.
├── README.md                 The project's front door, for someone who has never seen it.
├── CLAUDE.md                 Binding instructions for AI-assisted sessions. A pointer to
│                             Standards/, never a duplicate of it.
├── tasks.md                  In progress / Backlog / Done. The working task list.
├── handoverdaily.md          Dated session log, newest first. Append-only.
└── Documentation/            This tree.

tasks.md and handoverdaily.md are process files, not specification — they record what happened and what is next. Where either disagrees with Documentation/, Documentation/ wins and the process file is stale.

legacy/ and Applejack_Framework_Docs/ are not part of the shipped project — the former is gitignored content plus the two reference codebases described in Legacy/00_OVERVIEW.md §3; the latter no longer exists, having been superseded in full by this tree.

2. Code/

One folder per module, matching ADR-0002. Modules marked planned have no folder yet — they arrive with ROADMAP.md's Milestone 7:

Code/
├── Core/                     Subdivided by concern -- it is the only module with enough
│   ├── README.md             distinct concerns to need it.
│   ├── Logging/
│   ├── Configuration/
│   ├── Events/
│   ├── Services/
│   ├── Persistence/
│   ├── Modules/              GameModule base type, lifecycle runner, dependency resolver
│   └── Plugins/              Plugin/PluginManifest/IPluginCatalog, PluginDiscovery,
│                              PluginRunner -- the framework's OWN plugin infrastructure. See
│                              Documentation/Architecture/23_PLUGIN_SYSTEM.md.
├── Plugins/                  Third-party (in-repo, Stage 1) plugins live here, one folder
│   └── RicherEconomy/        each -- distinct from Core/Plugins/ above, which is the loader
│                              itself, not a plugin. RicherEconomy is the reference plugin;
│                              see its own README.md.
├── Character/
│   ├── README.md
│   ├── Character.cs          Flat: the data model, the service contract, the store, and
│   ├── CharacterStore.cs     the permission rules all sit at the module root.
│   ├── PermissionSet.cs
│   └── CharacterNetworkComponent.cs
├── Items/
├── Inventory/
│   └── Ui/                   Razor panels -- the one sub-folder a gameplay module takes.
├── Interaction/
├── Ownership/
├── Economy/
│   └── Ui/
├── World/
├── Ui/                       The HUD registry itself, plus HudRoot.razor.
├── Bulletin/
│   └── Ui/
├── Jobs/                     planned
├── Needs/                    planned
├── Crime/                    planned
├── Law/                      planned
├── Chat/                     planned
└── Commands/                 planned

Rules, unconditional:

  • A module folder gets a README.md before its first line of code, using the template in Standards/02_DOCUMENTATION_STANDARDS.md §2. A folder without one is not a module yet, it is a stub.
  • The namespace matches the folder path. Code/Inventories/Ui/ is Applejack.Inventories.Ui. See Standards/01_NAMING.md §8.
  • One type per file, filename matching the type. InventoryStore.cs contains exactly InventoryStore.
  • A gameplay module is flat by default. Types sit at the module root; the only sub-folder a gameplay module currently takes is Ui/. Add a sub-folder when a module's root becomes genuinely hard to scan, not pre-emptively — Core/ earned its six, and no gameplay module has yet earned one.
  • Sub-folders, when they appear, are by concern, not by C# construct. There is no Interfaces/ or Models/ folder — an interface lives beside the concern it belongs to, not sorted by language feature.

The engine-agnostic boundary

Standards/03_TESTING_STANDARDS.md §1 requires a testable core with no GameObject, Component, Scene, or Connection reference. That boundary is drawn by type, not by folder. Within a module:

Kind of type Convention Engine-bound?
Data model, service contract, store, rules Plain type at the module root No — this is what UnitTests/ compiles against
Replication and RPC surface Named *Component.cs, derives Component Yes
Panels Under Ui/, .razor Yes
Module registration Named *Module.cs, derives GameModule No, but untested — it is wiring

An earlier version of this document specified a Rules/ sub-folder per module for the engine-agnostic core, with Components/ and Persistence/ beside it. Implementation through Milestone 6 did not do that — the split fell naturally along the *Component naming convention instead, and adding three sub-folders to hold two or three files each would have obscured more than it organised. This section documents what was built. The constraint it enforces is unchanged and is the point: CharacterStore must stay free of engine types whether it lives in Rules/ or not.

Ui/ is listed as a module folder because it needs the same structure and README discipline, but it is architecturally distinct — see 11_UI_ARCHITECTURE.md. It owns no gameplay state and reads every other module only through public interfaces and events.

3. Assets/

Assets/
├── Items/                    19 definitions as of Milestone 5, including the
│   ├── README.md             base_food / base_container / base_weapon inheritance roots.
│   ├── Food/
│   │   └── cake.item
│   ├── Misc/
│   ├── Packaging/
│   └── Weapons/
├── Jobs/                     planned -- Milestone 7
│   └── police_officer.job
├── Recipes/                  planned
└── Prefabs/                  planned
    └── WorldItem.prefab

Mirrors the content categories, not the module structure — Assets/Items/Food/ is a content grouping for authors, unrelated to Code/Items/. Asset filenames are snake_case, matching legacy item IDs deliberately, per Standards/01_NAMING.md §10. Prefabs are PascalCase.prefab.

No model, material or sound assets are checked in. Legacy/00_OVERVIEW.md §3 explains why legacy/content/ cannot ship; this project's own art direction and asset sourcing is out of scope for Documentation/ and tracked separately once art production starts.

4. UnitTests/

Not hand-created. Per Standards/03_TESTING_STANDARDS.md §2, adding a UnitTests/ directory at the project root and restarting the editor causes S&box to generate the MSTest project. Structure inside it mirrors Code/:

UnitTests/
├── Character/
│   ├── CharacterStoreTests.cs
│   └── PermissionSetTests.cs
├── Core/
│   ├── Events/
│   ├── Modules/
│   ├── Persistence/
│   │   └── Fixtures/          Test doubles: InMemoryPersistenceBackend, ...
│   └── Services/
├── Interaction/
├── Inventory/
├── Items/
│   └── Fixtures/
└── ...

Test files are <TypeUnderTest>Tests.cs, per Standards/01_NAMING.md §10. A Fixtures/ sub-folder holds test doubles, which are not themselves tests and must not be named as though they were. Only engine-agnostic types are exercised here — anything needing a Scene is integration or multiplayer testing, covered manually per Standards/03_TESTING_STANDARDS.md §5 until automated. In practice this means no *Component type has a unit test, and that is expected rather than a coverage gap to close.

5. Where does X go? — a worked checklist

You are adding… Goes in Because
A new gameplay rule (arithmetic, resolution, validation) Code/<Module>/ root, a plain type Must run under dotnet test with no engine
A Component that holds runtime state Code/<Module>/<Name>Component.cs Thin — calls into the module's plain types, replicates the result
A service interface another module will call Code/<Module>/ (root of the module) Public surface, not a sub-concern
A typed event Code/<Module>/ alongside the type that publishes it Discoverable from the publisher
A new item, job, or recipe Assets/ Data, not code — ADR-0004
A Razor panel Code/Ui/ or Code/<Module>/Ui/ See 11_UI_ARCHITECTURE.md
A cross-cutting concern (a new kind of log sink, a new config source) Code/Core/ Not gameplay
A behaviour composable onto multiple item types Code/Items/, deriving ItemBehaviour See 09_ITEM_FRAMEWORK.md

If nothing on this list fits, that is a signal the change needs a module boundary decision before it needs a file path — raise it per 00_CONSTITUTION.md §7 rather than guessing.