Address review comments from Hoshe, Paula, and Miri: - Update mood vocabulary in 3 docs (line-pool-format.md §6.5, style-guide §11, content-directory-structure.md Appendix B) from pre-Sprint 14 values to current D-035 enum - Fix worked example IDs in line-pool-format.md §3.5 to match actual the-last-shift kael-davan sequence (_015, _024, _026) - Fix Section 5.1 restart note to describe multi-location continuity - Fix style-guide §16 worked example: dock-worker_d_071 → kael-davan_d_076 - Fix stale mood reference in style-guide §16 Step 3 - Fix orphaned the-terminal_d_040 in maintenance-tech.yaml comment - Fix orphaned the-terminal_d_008/018 in smuggler-inventory.yaml - Fix Lera tenure: twelve → eighteen years (bar-owner_d_018) - Fix fact_id: location.surveillance_gaps → investigation.surveillance_gaps in ring-operative.yaml (2 occurrences) - Fix NPC name: Lera Osk → Lera Sessik in bar-owner.yaml comment - Fix mood line format example in style-guide §5 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
606 lines
29 KiB
Markdown
606 lines
29 KiB
Markdown
# Content Directory Structure — The Settled Reach v0.1
|
|
|
|
**Ticket:** #384 (blocks #385: directory skeleton, #386: schema definitions)
|
|
**Status:** Design specification
|
|
**Authority:** This document is the source of truth for the `content/` directory layout. The server team implements the directory skeleton (#385) and schema files (#386) against this spec.
|
|
|
|
**Decisions referenced:** D-024 (10-axis NPC model), D-027 (vertical slice), D-028 (dialogue architecture), D-032 (monologue partition), D-034 (THE FRIEND), D-035 (tag taxonomy), D-036 (Sova Transit setting), D-049 (YAML format), D-057 (content directory structure)
|
|
|
|
---
|
|
|
|
## 1. Overview
|
|
|
|
The `content/` directory holds all game content in a structured, validated, mod-compatible layout. It is the canonical runtime format consumed by the Rust/bevy_ecs simulation server. All content files are YAML, validated against JSON Schema definitions at build time and deserialized via serde at load time.
|
|
|
|
This document defines:
|
|
|
|
- The complete directory tree
|
|
- Canonical ID format and naming conventions
|
|
- Schema file inventory and purpose
|
|
- Validation pipeline (3-tier)
|
|
- Migration path from the current wiki (`wiki/`)
|
|
|
|
The directory structure is designed so that a district is the atomic content pack unit. Districts can be added, removed, or replaced independently. The structure supports future mod overlay without v0.1 implementation.
|
|
|
|
---
|
|
|
|
## 2. Top-Level Layout
|
|
|
|
```
|
|
content/
|
|
content.yaml # Manifest: district list, content version, load order
|
|
_meta/ # Infrastructure metadata (underscore prefix = not game content)
|
|
README.md # Explains _meta and _schema conventions
|
|
_schema/ # JSON Schema validation files
|
|
npc-profile.schema.json
|
|
dialogue-pool.schema.json
|
|
monologue-pool.schema.json
|
|
district.schema.json
|
|
triangle.schema.json
|
|
routine.schema.json
|
|
location.schema.json
|
|
fact-catalog.schema.json
|
|
global/ # Cross-district content (not district-scoped)
|
|
factions/ # Faction profiles
|
|
technology/ # Technology definitions
|
|
contraband/ # Contraband item profiles
|
|
knowledge/ # Shared FactId definitions, entity attribute enums
|
|
enums/ # Shared enum values (situations, moods, topics, access tiers)
|
|
regions/ # Star system / station metadata
|
|
districts/ # Per-district content packs
|
|
sova-transit/ # v0.1 district (D-036)
|
|
```
|
|
|
|
### Conventions
|
|
|
|
- **Underscore prefix** (`_meta/`, `_schema/`): Infrastructure directories. Not game content. The server content loader skips directories starting with `_` when scanning for content files.
|
|
- **`content.yaml`**: The manifest file. Lists enabled districts, content version, and load order. The server reads this first.
|
|
- **`global/`**: Content that applies across all districts. Faction definitions, shared enums, fact catalogs, and region metadata live here.
|
|
- **`districts/`**: One subdirectory per district. Each district is a self-contained content pack.
|
|
|
|
### Manifest Format
|
|
|
|
```yaml
|
|
# content/content.yaml
|
|
version: "0.1.0"
|
|
districts:
|
|
- id: "sova-transit"
|
|
path: "districts/sova-transit"
|
|
enabled: true
|
|
```
|
|
|
|
The `version` field tracks content schema version. The `districts` list defines load order. Disabled districts are skipped entirely at load time.
|
|
|
|
---
|
|
|
|
## 3. District Structure
|
|
|
|
Each district directory is the atomic pack unit. It contains everything the server needs to instantiate that district: NPC profiles, locations, dialogue, monologue, routines, and triangle definitions.
|
|
|
|
```
|
|
districts/sova-transit/
|
|
district.yaml # District metadata
|
|
npcs/ # NPC profile files (one YAML file per NPC)
|
|
kael-davan.yaml
|
|
sera-venn.yaml
|
|
voss.yaml
|
|
lera-sessik.yaml
|
|
torek-lintar.yaml
|
|
devra.yaml
|
|
maret-korr.yaml
|
|
resha.yaml
|
|
naia-tamm.yaml
|
|
renn.yaml
|
|
pell.yaml
|
|
harek.yaml
|
|
drin.yaml
|
|
sess.yaml
|
|
olin.yaml
|
|
sabel.yaml
|
|
tav.yaml
|
|
locations/ # Location definition files (one per location)
|
|
the-terminal.yaml
|
|
the-last-shift.yaml
|
|
maintenance-corridors.yaml
|
|
templates/ # Social site template definitions
|
|
triangles/ # Triangle relationship definitions
|
|
hub-power.yaml
|
|
worried-knowledge.yaml
|
|
bar-tensions.yaml
|
|
worried-partner.yaml
|
|
informant-question.yaml
|
|
dialogue/ # Tagged dialogue line pools
|
|
the-terminal/ # Subdirectory per location
|
|
dock-worker.yaml # One file per template role
|
|
shift-supervisor.yaml
|
|
scheduler.yaml
|
|
new-hire.yaml
|
|
courier.yaml
|
|
the-last-shift/
|
|
bar-owner.yaml
|
|
bartender.yaml
|
|
bar-regular.yaml
|
|
maintenance-corridors/
|
|
ring-operative.yaml
|
|
monologue/ # Tagged monologue line pools (per character)
|
|
smuggler/ # Hard partition per D-032
|
|
the-terminal.yaml
|
|
the-last-shift.yaml
|
|
maintenance-corridors.yaml
|
|
general.yaml # Location-independent lines
|
|
detective/
|
|
the-terminal.yaml
|
|
the-last-shift.yaml
|
|
maintenance-corridors.yaml
|
|
general.yaml
|
|
routines/ # NPC daily routine schedules
|
|
schedules.yaml # All NPC schedules for this district
|
|
```
|
|
|
|
### District Metadata
|
|
|
|
```yaml
|
|
# districts/sova-transit/district.yaml
|
|
canonical_id: "krenn.sova.transit"
|
|
display_name: "Sova Transit District"
|
|
system: "krenn"
|
|
station: "sova"
|
|
district: "transit"
|
|
description: >
|
|
A 40-year-old prefab-modular-retrofitted freight logistics hub on Station Sova.
|
|
Three social sites: The Terminal (logistics hub), The Last Shift (bar),
|
|
and maintenance corridors.
|
|
locations:
|
|
- "the-terminal"
|
|
- "the-last-shift"
|
|
- "maintenance-corridors"
|
|
npc_count: 17
|
|
```
|
|
|
|
### Subdirectory Rationale
|
|
|
|
| Directory | Scoped by | Rationale |
|
|
|-----------|-----------|-----------|
|
|
| `npcs/` | One file per NPC | NPC profiles are the most frequently edited content. One file per NPC enables parallel authoring and clean diffs. |
|
|
| `locations/` | One file per location | Location metadata (name, tiles, sightlines, ambient) is independent of NPC content. |
|
|
| `templates/` | One file per social site template | Template definitions (roles, triangle slots, NPC capacity) are structural and rarely change after initial authoring. |
|
|
| `triangles/` | One file per triangle | Triangle definitions reference NPCs by canonical ID. Separate files enable independent authoring and review. |
|
|
| `dialogue/` | Location > role | Dialogue lines are authored per template role at a specific location. The location subdirectory groups all roles present at that location. This matches the authoring workflow: write all dialogue for The Terminal, then all dialogue for The Last Shift. |
|
|
| `monologue/` | Character > location | Hard partition by playable character (D-032). Within each character, one file per location plus a `general.yaml` for location-independent lines. |
|
|
| `routines/` | Single file per district | All NPC schedules in one file enables cross-NPC scheduling validation (no two NPCs assigned to the same tile at the same time). |
|
|
|
|
---
|
|
|
|
## 4. Canonical ID Format
|
|
|
|
```
|
|
{system}.{station}.{district}.{type}.{slug}
|
|
```
|
|
|
|
### Examples
|
|
|
|
| Canonical ID | Resolves to |
|
|
|---|---|
|
|
| `krenn.sova.transit.npc.kael-davan` | `districts/sova-transit/npcs/kael-davan.yaml` |
|
|
| `krenn.sova.transit.npc.sera-venn` | `districts/sova-transit/npcs/sera-venn.yaml` |
|
|
| `krenn.sova.transit.location.the-terminal` | `districts/sova-transit/locations/the-terminal.yaml` |
|
|
| `krenn.sova.transit.location.the-last-shift` | `districts/sova-transit/locations/the-last-shift.yaml` |
|
|
| `krenn.sova.transit.triangle.hub-power` | `districts/sova-transit/triangles/hub-power.yaml` |
|
|
| `krenn.sova.transit.triangle.worried-knowledge` | `districts/sova-transit/triangles/worried-knowledge.yaml` |
|
|
|
|
### Type Segment Values
|
|
|
|
| Type | Description |
|
|
|------|-------------|
|
|
| `npc` | NPC profile |
|
|
| `location` | Location definition |
|
|
| `template` | Social site template |
|
|
| `triangle` | Triangle relationship definition |
|
|
|
|
### Rules
|
|
|
|
1. **Canonical IDs are stable references.** Renaming a file does not change its canonical ID. The `canonical_id` field inside each YAML file is the source of truth. File names are a convenience for human navigation; the loader resolves by `canonical_id`, not by file path.
|
|
2. **Canonical IDs are globally unique.** No two content files across any district may share a canonical ID. The build-time validator enforces this.
|
|
3. **Slugs use kebab-case.** Lowercase, hyphen-separated: `kael-davan`, `the-terminal`, `hub-power`.
|
|
4. **Short-form IDs.** Within content files, NPC references use the short form `npc:{slug}` (e.g., `npc:kael-davan`). Location references use `loc:{district}:{slug}` (e.g., `loc:sova-transit:the-terminal`). These short forms are unambiguous within a single district. The full canonical ID is used for cross-district references (future feature).
|
|
|
|
---
|
|
|
|
## 5. Schema Specifications
|
|
|
|
All schema files live in `content/_schema/`. They use JSON Schema draft 2020-12. Each schema file validates one content type.
|
|
|
|
### Schema Inventory
|
|
|
|
| Schema File | Validates | Key Constraints | Decision Reference |
|
|
|---|---|---|---|
|
|
| `npc-profile.schema.json` | `districts/*/npcs/*.yaml` | 10-axis model (want, secret, relationships, tolerance, routine, information, contentment + personality, tells, skills). Tier-conditional fields: `friend_arc` only on FRIEND pattern T1 NPCs. Pattern enum (9 values). Motivation enum (6 values). Access tiers. Trust levels. Triangle membership. | D-024, D-034 |
|
|
| `dialogue-pool.schema.json` | `districts/*/dialogue/**/*.yaml` | Role + location scoped. 6 structural tags (id, text, role, access, trust, situation) + 3 selection tags (topic, mood, tags). Knowledge grants per line. Access is a list (multi-tier eligibility). Situation enum: 13 values. Topic enum: 9 values. Mood enum: 8 values. | D-028, D-035 |
|
|
| `monologue-pool.schema.json` | `districts/*/monologue/**/*.yaml` | Hard partition by `character` (smuggler/detective, D-032). Trigger enum: 9 types. Prerequisite object (AND-only logic: facts, entity attributes, relationship state). Priority (0-10). Cooldown (ticks). 160 char max per line (display constraint, D-059). | D-032, D-035 |
|
|
| `district.schema.json` | `districts/*/district.yaml` | District metadata: canonical_id, display_name, system, station, district slug, description, location list, NPC count. | D-036 |
|
|
| `triangle.schema.json` | `districts/*/triangles/*.yaml` | Triangle definition: 3 NPC members (by canonical_id short form), roles within the triangle, fork conditions, resolution states. Self-contained forks (no cross-triangle cascade in v0.1). | D-024, D-047 |
|
|
| `routine.schema.json` | `districts/*/routines/*.yaml` | Schedule entries per NPC: phase (Morning/Afternoon/Evening/Night), location, tile coordinates, activity. Deviation entries: trigger condition, override location/tile/activity. Deviations are load-bearing for FRIEND arc spatial staging. | D-034 |
|
|
| `location.schema.json` | `districts/*/locations/*.yaml` | Location metadata: canonical_id, display_name, tile bounds, sightline properties, ambient sound reference, social site membership. | D-025, D-036 |
|
|
| `fact-catalog.schema.json` | `global/knowledge/*.yaml` | Fact definitions: fact_id, description, discoverable_by (character list), progression (confidence levels with description text), abstract flag (whether fact can reach Direct confidence). | D-035 |
|
|
|
|
### Schema Design Principles
|
|
|
|
1. **Required fields are minimal.** Only fields the server needs to instantiate an entity are required. Authoring-only fields (`dual_lens`, `notes`) are optional and ignored at load time.
|
|
2. **Enum values are defined in schema, mirrored in `global/enums/`.** The JSON Schema files contain the authoritative enum definitions. The `global/enums/*.yaml` files are the human-readable reference and the source for IDE autocomplete. Both must agree; the build-time validator checks this.
|
|
3. **Pattern validation on IDs.** Canonical IDs, line IDs, and reference IDs use regex patterns in the schema to catch malformed references at validation time, before the server ever sees them.
|
|
4. **Conditional fields.** The `friend_arc` object in `npc-profile.schema.json` is only present on FRIEND-pattern NPCs. The schema uses JSON Schema `if`/`then` for tier-conditional validation where feasible; otherwise, cross-reference validation at build time catches mismatches.
|
|
|
|
### Line ID Formats
|
|
|
|
| Content Type | ID Pattern | Example |
|
|
|---|---|---|
|
|
| Dialogue | `{npc-slug}_d_{###}` | `kael-davan_d_001` |
|
|
| Monologue (smuggler) | `{npc-slug}_m_s_{###}` | `pc-smuggler_m_s_001` |
|
|
| Monologue (detective) | `{npc-slug}_m_d_{###}` | `pc-detective_m_d_001` |
|
|
| Examine | `{npc-slug}_{e}_{###}` | `kael-davan_e_001` |
|
|
|
|
Line IDs are stable. They are never reused, even if a line is deleted. Numbering gaps are expected and acceptable.
|
|
|
|
---
|
|
|
|
## 6. Global Content Structure
|
|
|
|
```
|
|
global/
|
|
factions/ # Faction profiles (cross-district)
|
|
lattice-commission.yaml
|
|
syndics.yaml
|
|
the-ring.yaml
|
|
concord-assembly.yaml
|
|
guardians-of-autonomy.yaml
|
|
veil-institute.yaml
|
|
the-unbound.yaml
|
|
technology/ # Technology definitions
|
|
contraband/ # Contraband item profiles
|
|
knowledge/ # Shared FactId definitions
|
|
contraband.yaml # Contraband-related facts
|
|
location.yaml # Location-related facts
|
|
investigation.yaml # Investigation-related facts
|
|
world.yaml # World/setting facts
|
|
relationship.yaml # Relationship-related facts
|
|
progress.yaml # Progression-related facts
|
|
enums/ # Shared enum values
|
|
situations.yaml # 13 situation values (D-035)
|
|
topics.yaml # 9 topic values
|
|
moods.yaml # 8 mood values
|
|
access-tiers.yaml # public, insider, authority, peer, hostile
|
|
trust-tiers.yaml # surface, real, secret
|
|
triggers.yaml # 9 monologue trigger types
|
|
patterns.yaml # 9 thematic patterns (System A)
|
|
motivations.yaml # 6 functional motivations (System B)
|
|
regions/ # Star system / station metadata
|
|
krenn.yaml # Krenn System profile (D-036)
|
|
```
|
|
|
|
### Global Content Rationale
|
|
|
|
| Directory | Contents | Why Global |
|
|
|---|---|---|
|
|
| `factions/` | One file per faction. Faction name, description, political stance, NPC membership references. | Factions span districts. An NPC in Sova Transit may belong to a faction headquartered elsewhere. |
|
|
| `technology/` | Technology definitions relevant to gameplay (lattice types, span gate specs). | Technology is universal across the setting. |
|
|
| `contraband/` | Contraband item profiles (unlicensed lattice components, medical-grade replacements, counter-surveillance tech per D-037). | Contraband types are not district-specific; the same items may appear in multiple districts. |
|
|
| `knowledge/` | FactId catalog. Each fact has an ID, description, discoverability, and confidence progression text. | Facts are referenced by NPC profiles, dialogue lines, and monologue prerequisites across all districts. The fact catalog is the single source of truth for what can be known. |
|
|
| `enums/` | Enum value definitions. One file per enum type. | Enums are shared vocabulary. Dialogue in any district uses the same 13 situations, 9 topics, and 8 moods. |
|
|
| `regions/` | Star system and station metadata. | Region data provides setting context for districts. Multiple districts may exist on a single station. |
|
|
|
|
### Entity Schema (Attribute Keys)
|
|
|
|
The 16 EntityKnowledge keys (D-055) are defined in:
|
|
|
|
```
|
|
global/
|
|
knowledge/
|
|
entity-attributes.yaml # 16 canonical EntityKnowledge keys with value enums
|
|
```
|
|
|
|
This file defines the attribute key names, their categories (Identity, Spatial, Behavioral, Relational, Role-perspective), allowed value types, and the 4 new role-perspective keys (`risk_assessment`, `loyalty_assessment`, `position_integrity`, `moral_weight`).
|
|
|
|
---
|
|
|
|
## 7. File Naming Conventions
|
|
|
|
### General Rules
|
|
|
|
| Rule | Convention | Example |
|
|
|---|---|---|
|
|
| Extension | Always `.yaml` (never `.yml`) | `kael-davan.yaml` |
|
|
| Case | kebab-case for all filenames | `the-terminal.yaml`, `hub-power.yaml` |
|
|
| NPC files | Named by NPC slug | `kael-davan.yaml`, `sera-venn.yaml` |
|
|
| Location files | Named by location slug | `the-terminal.yaml`, `the-last-shift.yaml` |
|
|
| Triangle files | Named by triangle slug | `hub-power.yaml`, `worried-knowledge.yaml` |
|
|
| Dialogue files | Named by template role | `dock-worker.yaml`, `bar-owner.yaml` |
|
|
| Monologue files | Named by location (inside character subdirectory) | `smuggler/the-terminal.yaml` |
|
|
| General monologue | `general.yaml` for location-independent lines | `smuggler/general.yaml` |
|
|
| Enum files | Named by enum type (plural) | `situations.yaml`, `moods.yaml` |
|
|
| Faction files | Named by faction slug | `lattice-commission.yaml`, `the-ring.yaml` |
|
|
| Fact files | Named by fact category | `contraband.yaml`, `investigation.yaml` |
|
|
|
|
### Directory Naming
|
|
|
|
- District directories use the district slug: `sova-transit/`
|
|
- Dialogue subdirectories use the location slug: `the-terminal/`
|
|
- Monologue subdirectories use the character name: `smuggler/`, `detective/`
|
|
|
|
### What NOT to Do
|
|
|
|
- Do not use CamelCase or PascalCase in filenames.
|
|
- Do not use underscores in filenames (underscores are reserved for line ID segments).
|
|
- Do not abbreviate names: `maintenance-corridors.yaml`, not `maint-corr.yaml`.
|
|
- Do not nest deeper than 3 levels within a district directory.
|
|
|
|
---
|
|
|
|
## 8. Mod-Compatible Conventions
|
|
|
|
The directory structure is designed to support future mod overlay, where a mod mirrors the same tree and the loader merges mod content with base content. v0.1 does not implement overlay loading, but the structure is ready for it.
|
|
|
|
### Design Principles
|
|
|
|
1. **District as atomic pack unit.** A mod can add an entirely new district by placing a new directory under `districts/`. No existing files need modification.
|
|
2. **Mirrored tree.** A mod that modifies existing content mirrors the exact same directory structure. For example, a mod adding an NPC to Sova Transit would place a file at `mod-name/districts/sova-transit/npcs/new-npc.yaml`.
|
|
3. **Canonical IDs prevent collisions.** Because canonical IDs include the system/station/district prefix, mods in different districts cannot accidentally collide. Mods in the same district use a mod-specific prefix convention (future spec).
|
|
4. **Global content extension.** A mod can add new factions, facts, or enum values by placing files in `mod-name/global/`. The overlay loader (future) merges these with base global content.
|
|
|
|
### v0.1 Scope
|
|
|
|
- The directory structure is mod-compatible by design.
|
|
- No overlay loader is implemented in v0.1.
|
|
- No mod tooling, mod manifest format, or mod loading order is defined in v0.1.
|
|
- These are future tickets. The only v0.1 requirement is that the base content structure does not preclude mod overlay.
|
|
|
|
---
|
|
|
|
## 9. Validation Pipeline
|
|
|
|
Content validation operates at three tiers. Each tier catches different classes of errors. All three must pass for content to be considered valid.
|
|
|
|
### Tier 1: Authoring-Time (IDE)
|
|
|
|
**Tool:** YAML Language Server + JSON Schema association
|
|
**What it catches:** Syntax errors, missing required fields, wrong field types, invalid enum values.
|
|
|
|
Configuration: each YAML content file includes a `$schema` comment or the IDE is configured to associate `_schema/*.schema.json` files with the corresponding content directories.
|
|
|
|
```yaml
|
|
# Example: NPC profile with schema association
|
|
# yaml-language-server: $schema=../../_schema/npc-profile.schema.json
|
|
canonical_id: "npc:kael-davan"
|
|
display_name: "Kael Davan"
|
|
# ...
|
|
```
|
|
|
|
This tier is optional (not all authors use IDE schema validation) but strongly recommended. It provides instant feedback during authoring.
|
|
|
|
### Tier 2: Build-Time (`make validate-content`)
|
|
|
|
**Tool:** `tooling/content-tools validate content/` (Rust CLI)
|
|
**What it catches:** Everything Tier 1 catches, plus cross-reference errors.
|
|
|
|
Build-time validation runs two passes:
|
|
|
|
**Pass 1 — JSON Schema validation:**
|
|
- Every YAML file in `content/` is validated against its corresponding `_schema/*.schema.json` file.
|
|
- File-to-schema mapping is determined by directory location (all files in `districts/*/npcs/` validate against `npc-profile.schema.json`).
|
|
- All errors are collected and reported at once (no fail-on-first).
|
|
|
|
**Pass 2 — Cross-reference validation:**
|
|
- All `canonical_id` references resolve to existing files.
|
|
- All FactId references in prerequisites resolve to defined facts in `global/knowledge/`.
|
|
- All NPC relationship targets (`npc:{slug}`) resolve to existing NPC profiles.
|
|
- All location references (`loc:{district}:{slug}`) resolve to existing location files.
|
|
- All triangle member references resolve to existing NPC profiles.
|
|
- No duplicate `canonical_id` values across all files.
|
|
- Enum values in content files match definitions in `global/enums/`.
|
|
- Monologue character partitions are correct (no smuggler lines in detective files, no detective lines in smuggler files).
|
|
- Routine schedule locations resolve to existing location files.
|
|
|
|
**Exit code:** 0 if all checks pass, non-zero if any errors. CI gate: content changes must pass `make validate-content`.
|
|
|
|
### Tier 3: Load-Time (Server Startup)
|
|
|
|
**Tool:** Rust `serde_yaml` deserialization + semantic validation in `server/src/content/`
|
|
**What it catches:** Type mismatches between YAML and Rust structs (schema drift), semantic errors that require runtime context.
|
|
|
|
Load-time validation runs in sequence:
|
|
|
|
1. **Deserialization:** `serde_yaml::from_str()` deserializes each YAML file into the corresponding Rust struct. Any type mismatch, missing required field, or unrecognized enum value causes an immediate load failure. This catches schema drift between the JSON Schema definitions and the Rust struct definitions.
|
|
2. **StableId assignment:** Deterministic integer IDs are assigned from sorted canonical IDs. This produces repeatable entity IDs across loads.
|
|
3. **Relationship wiring:** Canonical ID references (`npc:{slug}`) are resolved to StableIds. Unresolvable references cause load failure.
|
|
4. **FriendArc bonding:** FRIEND-pattern NPC profiles are linked to their bonded playable character via StableId.
|
|
5. **Schedule validation:** Routine entries are checked for location/tile validity.
|
|
|
|
**Failure mode:** The server fails fast on any load-time error. No partial loads. All content must be valid or the server does not start. This is intentional: partial content loads produce subtle, hard-to-debug runtime errors.
|
|
|
|
---
|
|
|
|
## 10. Migration Path
|
|
|
|
### Current State
|
|
|
|
Game content currently lives in `wiki/`, organized as human-readable Markdown files:
|
|
|
|
```
|
|
wiki/
|
|
npcs/ # NPC profile pages (Markdown)
|
|
factions/ # Faction descriptions
|
|
knowledge/ # FactId catalog, entity attributes
|
|
locations/ # Location descriptions
|
|
contraband/ # Contraband profiles
|
|
technology/ # Technology descriptions
|
|
world/ # World-building (Krenn System, Sova Station)
|
|
authoring/ # Authoring guides and style references
|
|
index.md # Wiki index
|
|
```
|
|
|
|
### Migration Strategy
|
|
|
|
The wiki (`wiki/`) remains the authoring source during v0.1. Authors write and edit in the wiki. The `content/` directory is the runtime format — what the server loads.
|
|
|
|
The conversion from wiki Markdown to runtime YAML is a **manual process** during v0.1, tracked as ticket #398 (future). The process:
|
|
|
|
1. Author writes/edits NPC profile in `wiki/npcs/kael-davan.md`.
|
|
2. Author (or tooling) converts the profile to `content/districts/sova-transit/npcs/kael-davan.yaml`, conforming to `_schema/npc-profile.schema.json`.
|
|
3. `make validate-content` confirms the YAML is valid.
|
|
4. Server loads from `content/`.
|
|
|
|
### Mapping Table
|
|
|
|
| Wiki Source | Content Target | Notes |
|
|
|---|---|---|
|
|
| `wiki/npcs/*.md` | `content/districts/sova-transit/npcs/*.yaml` | One-to-one mapping. Wiki profile is the human-readable source; YAML is the machine-readable runtime format. |
|
|
| `wiki/factions/*.md` | `content/global/factions/*.yaml` | Faction profiles converted to structured YAML. |
|
|
| `wiki/knowledge/fact-catalog.md` | `content/global/knowledge/*.yaml` | Single Markdown catalog splits into per-category YAML files. |
|
|
| `wiki/knowledge/entity-attributes.md` | `content/global/knowledge/entity-attributes.yaml` | EntityKnowledge key definitions. |
|
|
| `wiki/contraband/*.md` | `content/global/contraband/*.yaml` | Contraband item profiles. |
|
|
| `wiki/locations/*.md` | `content/districts/sova-transit/locations/*.yaml` | Location metadata extracted from descriptions. |
|
|
| `wiki/world/*.md` | `content/global/regions/*.yaml` | System/station metadata. |
|
|
| (new content) | `content/districts/sova-transit/dialogue/**/*.yaml` | Dialogue pools are new content authored directly in YAML. No wiki source. |
|
|
| (new content) | `content/districts/sova-transit/monologue/**/*.yaml` | Monologue pools are new content authored directly in YAML. No wiki source. |
|
|
| (new content) | `content/districts/sova-transit/routines/*.yaml` | Routine schedules are new content authored directly in YAML. No wiki source. |
|
|
| (new content) | `content/districts/sova-transit/triangles/*.yaml` | Triangle definitions are new content authored directly in YAML. No wiki source. |
|
|
|
|
### What the Wiki Is NOT
|
|
|
|
The wiki is not deprecated. It remains the primary authoring and review surface for narrative content. Authors should not be expected to write raw YAML for narrative text. The conversion to YAML is a production step, not an authoring step.
|
|
|
|
Dialogue, monologue, routines, and triangles are exceptions: these content types are authored directly in YAML because their structure is inherently machine-readable (tagged line pools, schedule entries, relationship definitions). The schemas provide IDE autocomplete for these files.
|
|
|
|
---
|
|
|
|
## Appendix A: Complete Directory Tree (v0.1)
|
|
|
|
```
|
|
content/
|
|
content.yaml
|
|
_meta/
|
|
README.md
|
|
_schema/
|
|
npc-profile.schema.json
|
|
dialogue-pool.schema.json
|
|
monologue-pool.schema.json
|
|
district.schema.json
|
|
triangle.schema.json
|
|
routine.schema.json
|
|
location.schema.json
|
|
fact-catalog.schema.json
|
|
global/
|
|
factions/
|
|
lattice-commission.yaml
|
|
syndics.yaml
|
|
the-ring.yaml
|
|
concord-assembly.yaml
|
|
guardians-of-autonomy.yaml
|
|
veil-institute.yaml
|
|
the-unbound.yaml
|
|
technology/
|
|
contraband/
|
|
knowledge/
|
|
contraband.yaml
|
|
location.yaml
|
|
investigation.yaml
|
|
world.yaml
|
|
relationship.yaml
|
|
progress.yaml
|
|
entity-attributes.yaml
|
|
enums/
|
|
situations.yaml
|
|
topics.yaml
|
|
moods.yaml
|
|
access-tiers.yaml
|
|
trust-tiers.yaml
|
|
triggers.yaml
|
|
patterns.yaml
|
|
motivations.yaml
|
|
regions/
|
|
krenn.yaml
|
|
districts/
|
|
sova-transit/
|
|
district.yaml
|
|
npcs/
|
|
kael-davan.yaml
|
|
sera-venn.yaml
|
|
voss.yaml
|
|
lera-sessik.yaml
|
|
torek-lintar.yaml
|
|
devra.yaml
|
|
maret-korr.yaml
|
|
resha.yaml
|
|
naia-tamm.yaml
|
|
renn.yaml
|
|
pell.yaml
|
|
harek.yaml
|
|
drin.yaml
|
|
sess.yaml
|
|
olin.yaml
|
|
sabel.yaml
|
|
tav.yaml
|
|
locations/
|
|
the-terminal.yaml
|
|
the-last-shift.yaml
|
|
maintenance-corridors.yaml
|
|
templates/
|
|
triangles/
|
|
hub-power.yaml
|
|
worried-knowledge.yaml
|
|
bar-tensions.yaml
|
|
worried-partner.yaml
|
|
informant-question.yaml
|
|
dialogue/
|
|
the-terminal/
|
|
dock-worker.yaml
|
|
shift-supervisor.yaml
|
|
scheduler.yaml
|
|
new-hire.yaml
|
|
courier.yaml
|
|
the-last-shift/
|
|
bar-owner.yaml
|
|
bartender.yaml
|
|
bar-regular.yaml
|
|
maintenance-corridors/
|
|
ring-operative.yaml
|
|
monologue/
|
|
smuggler/
|
|
the-terminal.yaml
|
|
the-last-shift.yaml
|
|
maintenance-corridors.yaml
|
|
general.yaml
|
|
detective/
|
|
the-terminal.yaml
|
|
the-last-shift.yaml
|
|
maintenance-corridors.yaml
|
|
general.yaml
|
|
routines/
|
|
schedules.yaml
|
|
```
|
|
|
|
## Appendix B: Enum Value Reference (v0.1)
|
|
|
|
For quick reference during authoring. Authoritative source: `content/global/enums/*.yaml`.
|
|
|
|
**Situations (13):** `arrival`, `shift_start`, `shift_end`, `shift_transition`, `bar_evening`, `night_shift`, `investigation`, `confrontation`, `social`, `alone`, `emergency`, `routine`, `observation`
|
|
|
|
**Topics (9):** `colleague`, `routine`, `cargo`, `money`, `trust`, `danger`, `institution`, `personal`, `investigation`
|
|
|
|
**Moods (8):** `anxious`, `frustrated`, `content`, `suspicious`, `warm`, `hostile`, `relieved`, `focused`
|
|
|
|
**Access Tiers (5):** `public`, `insider`, `authority`, `peer`, `hostile`
|
|
|
|
**Trust Tiers (3):** `surface`, `real`, `secret`
|
|
|
|
**Monologue Triggers (9):** `enter_location`, `observe_npc`, `hear_sound`, `observe_anomaly`, `post_conversation`, `discover_evidence`, `witness_interaction`, `time_idle`, `return_visit`
|
|
|
|
**NPC Patterns (9):** `FRIEND`, `MIRROR`, `ANCHOR`, `GHOST`, `CATALYST`, `THRESHOLD`, `REMNANT`, `SYSTEM`, `NOBODY`
|
|
|
|
**NPC Motivations (6):** `HANDLER`, `WITNESS`, `TURNCOAT`, `CIVILIAN`, `OPERATOR`, `SKEPTIC`
|
|
|
|
**NPC Tiers (3):** `1` (production-level), `2` (full template), `3` (background)
|
|
|
|
---
|
|
|
|
*Ticket #384 — Content directory structure design. Blocks #385 (directory skeleton) and #386 (schema definitions).*
|