feat(data): add content schema definitions
JSON Schema (draft 2020-12) for content validation: district, location, npc-profile, dialogue-pool, monologue-pool, triangle, routine, and fact-catalog. NPC schema uses if/then conditional for FRIEND pattern validation. Implements ticket #386. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "dialogue-pool.schema.json",
|
||||
"title": "Dialogue Line Pool",
|
||||
"description": "Tagged dialogue lines scoped by location + role (D-028, D-035).",
|
||||
"type": "object",
|
||||
"required": ["location", "role", "lines"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"location": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]*$",
|
||||
"description": "Location slug this dialogue pool belongs to"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]*$",
|
||||
"description": "Template role slug (e.g. dock-worker, bar-owner)"
|
||||
},
|
||||
"lines": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/dialogue_line" },
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"dialogue_line": {
|
||||
"type": "object",
|
||||
"required": ["id", "text", "role", "access", "trust", "situation"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]*_d_[0-9]{3}$",
|
||||
"description": "Stable line ID: {location_slug}_d_{###}"
|
||||
},
|
||||
"text": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]*$",
|
||||
"description": "Template role this line belongs to"
|
||||
},
|
||||
"access": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["public", "insider", "authority", "peer", "hostile"]
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true,
|
||||
"description": "Access tiers this line is available at (multi-tier eligibility)"
|
||||
},
|
||||
"trust": {
|
||||
"type": "string",
|
||||
"enum": ["surface", "real", "secret"],
|
||||
"description": "Minimum trust level required"
|
||||
},
|
||||
"situation": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"arrival", "shift_start", "shift_end", "shift_transition",
|
||||
"bar_evening", "night_shift", "investigation", "confrontation",
|
||||
"social", "alone", "emergency", "routine", "observation"
|
||||
],
|
||||
"description": "Situation context when this line can fire"
|
||||
},
|
||||
"topic": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"colleague", "routine", "cargo", "money", "trust",
|
||||
"danger", "institution", "personal", "investigation"
|
||||
],
|
||||
"description": "Topic tag for selection weighting"
|
||||
},
|
||||
"mood": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"fond", "comfortable", "worried", "suspicious",
|
||||
"analytical", "conflicted", "concerned", "relieved"
|
||||
],
|
||||
"description": "Mood tag for selection weighting"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Freeform tags for additional filtering"
|
||||
},
|
||||
"knowledge_grant": {
|
||||
"type": "object",
|
||||
"description": "Knowledge the player gains from hearing this line",
|
||||
"properties": {
|
||||
"fact_id": { "type": "string" },
|
||||
"confidence": {
|
||||
"type": "string",
|
||||
"enum": ["suspects", "knows_of", "knows_details", "direct"]
|
||||
}
|
||||
},
|
||||
"required": ["fact_id", "confidence"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "district.schema.json",
|
||||
"title": "District Metadata",
|
||||
"description": "District definition file — one per district directory (D-036).",
|
||||
"type": "object",
|
||||
"required": ["canonical_id", "display_name", "system", "station", "district", "description", "locations", "npc_count"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"canonical_id": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z]+\\.[a-z]+\\.[a-z-]+$",
|
||||
"description": "Canonical ID in format {system}.{station}.{district}"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"system": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z]+$"
|
||||
},
|
||||
"station": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z]+$"
|
||||
},
|
||||
"district": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z-]+$"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"locations": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]*$"
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true
|
||||
},
|
||||
"npc_count": {
|
||||
"type": "integer",
|
||||
"minimum": 0
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,73 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "fact-catalog.schema.json",
|
||||
"title": "Fact Catalog",
|
||||
"description": "Fact definitions — one file per fact category in global/knowledge/ (D-035).",
|
||||
"type": "object",
|
||||
"required": ["category", "facts"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"category": {
|
||||
"type": "string",
|
||||
"description": "Fact category matching the filename"
|
||||
},
|
||||
"facts": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/fact" },
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"fact": {
|
||||
"type": "object",
|
||||
"required": ["fact_id", "description", "discoverable_by"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"fact_id": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9_-]*$",
|
||||
"description": "Stable unique fact identifier"
|
||||
},
|
||||
"description": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"discoverable_by": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "string",
|
||||
"enum": ["smuggler", "detective"]
|
||||
},
|
||||
"minItems": 1,
|
||||
"uniqueItems": true,
|
||||
"description": "Which playable characters can discover this fact"
|
||||
},
|
||||
"abstract": {
|
||||
"type": "boolean",
|
||||
"default": false,
|
||||
"description": "If true, fact cannot reach Direct confidence (only inferred)"
|
||||
},
|
||||
"progression": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/confidence_level" },
|
||||
"description": "Confidence level descriptions in ascending order"
|
||||
}
|
||||
}
|
||||
},
|
||||
"confidence_level": {
|
||||
"type": "object",
|
||||
"required": ["confidence", "text"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"confidence": {
|
||||
"type": "string",
|
||||
"enum": ["suspects", "knows_of", "knows_details", "direct"]
|
||||
},
|
||||
"text": {
|
||||
"type": "string",
|
||||
"description": "Player-facing description at this confidence level"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,54 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "location.schema.json",
|
||||
"title": "Location Definition",
|
||||
"description": "Location metadata — one file per location (D-025, D-036).",
|
||||
"type": "object",
|
||||
"required": ["canonical_id", "display_name"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"canonical_id": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z]+\\.[a-z]+\\.[a-z-]+\\.location\\.[a-z][a-z0-9-]*$",
|
||||
"description": "Full canonical ID: {system}.{station}.{district}.location.{slug}"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"tile_bounds": {
|
||||
"type": "object",
|
||||
"description": "Rectangular tile bounds for this location",
|
||||
"properties": {
|
||||
"x_min": { "type": "integer" },
|
||||
"y_min": { "type": "integer" },
|
||||
"x_max": { "type": "integer" },
|
||||
"y_max": { "type": "integer" },
|
||||
"z": { "type": "integer" }
|
||||
},
|
||||
"required": ["x_min", "y_min", "x_max", "y_max", "z"]
|
||||
},
|
||||
"sightlines": {
|
||||
"type": "object",
|
||||
"description": "Sightline properties for LOS computation",
|
||||
"properties": {
|
||||
"open": {
|
||||
"type": "boolean",
|
||||
"description": "True if the location is open-plan (no internal walls)"
|
||||
},
|
||||
"notes": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"ambient_sound": {
|
||||
"type": "string",
|
||||
"description": "Reference to ambient sound asset"
|
||||
},
|
||||
"social_site": {
|
||||
"type": "string",
|
||||
"description": "Social site template this location belongs to"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "monologue-pool.schema.json",
|
||||
"title": "Monologue Line Pool",
|
||||
"description": "Tagged monologue lines, hard-partitioned by character (D-032, D-035).",
|
||||
"type": "object",
|
||||
"required": ["character", "location", "lines"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"character": {
|
||||
"type": "string",
|
||||
"enum": ["smuggler", "detective"],
|
||||
"description": "Playable character this pool belongs to (hard partition per D-032)"
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]*$",
|
||||
"description": "Location slug, or 'general' for location-independent lines"
|
||||
},
|
||||
"lines": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/monologue_line" },
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"monologue_line": {
|
||||
"type": "object",
|
||||
"required": ["id", "text", "trigger"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]*_m_[sd]_[0-9]{3}$",
|
||||
"description": "Stable line ID: {location_slug}_m_{s|d}_{###}"
|
||||
},
|
||||
"text": {
|
||||
"type": "string",
|
||||
"minLength": 1,
|
||||
"maxLength": 160,
|
||||
"description": "Line text — 160 char max per D-059"
|
||||
},
|
||||
"trigger": {
|
||||
"type": "string",
|
||||
"enum": [
|
||||
"enter_location", "observe_npc", "hear_sound",
|
||||
"observe_anomaly", "post_conversation", "discover_evidence",
|
||||
"witness_interaction", "time_idle", "return_visit"
|
||||
],
|
||||
"description": "What causes this line to fire"
|
||||
},
|
||||
"prerequisites": {
|
||||
"type": "object",
|
||||
"description": "AND-only prerequisite conditions",
|
||||
"properties": {
|
||||
"facts": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/fact_prerequisite" }
|
||||
},
|
||||
"entity_attributes": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/attribute_prerequisite" }
|
||||
},
|
||||
"relationship": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"target": { "type": "string" },
|
||||
"state": {
|
||||
"type": "string",
|
||||
"enum": ["unknown", "known", "friendly", "person_of_interest", "hostile"]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"priority": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"maximum": 10,
|
||||
"default": 5,
|
||||
"description": "Selection priority (higher = more likely to fire)"
|
||||
},
|
||||
"cooldown": {
|
||||
"type": "integer",
|
||||
"minimum": 0,
|
||||
"description": "Minimum ticks before this line can fire again"
|
||||
},
|
||||
"tags": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
}
|
||||
}
|
||||
},
|
||||
"fact_prerequisite": {
|
||||
"type": "object",
|
||||
"required": ["fact_id", "min_confidence"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"fact_id": { "type": "string" },
|
||||
"min_confidence": {
|
||||
"type": "string",
|
||||
"enum": ["suspects", "knows_of", "knows_details", "direct"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"attribute_prerequisite": {
|
||||
"type": "object",
|
||||
"required": ["entity", "key", "value"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"entity": { "type": "string" },
|
||||
"key": { "type": "string" },
|
||||
"value": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,209 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "npc-profile.schema.json",
|
||||
"title": "NPC Profile",
|
||||
"description": "10-axis NPC model profile — one file per NPC (D-024, D-034).",
|
||||
"type": "object",
|
||||
"required": ["canonical_id", "display_name", "tier", "pattern", "motivation"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"canonical_id": {
|
||||
"type": "string",
|
||||
"pattern": "^npc:[a-z][a-z0-9-]*$",
|
||||
"description": "Short-form canonical ID: npc:{slug}"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"tier": {
|
||||
"type": "integer",
|
||||
"enum": [1, 2, 3],
|
||||
"description": "NPC tier: 1 = conspiracy (full depth), 2 = template (social context), 3 = filler (atmosphere)"
|
||||
},
|
||||
"pattern": {
|
||||
"type": "string",
|
||||
"enum": ["FRIEND", "MIRROR", "ANCHOR", "GHOST", "CATALYST", "THRESHOLD", "REMNANT", "SYSTEM", "NOBODY"],
|
||||
"description": "Thematic pattern — System A (D-050)"
|
||||
},
|
||||
"motivation": {
|
||||
"type": "string",
|
||||
"enum": ["HANDLER", "WITNESS", "TURNCOAT", "CIVILIAN", "OPERATOR", "SKEPTIC"],
|
||||
"description": "Functional motivation — System B (D-050)"
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"want": {
|
||||
"type": "object",
|
||||
"description": "Primary want/need driving this NPC",
|
||||
"properties": {
|
||||
"primary": { "type": "string" },
|
||||
"intensity": { "type": "integer", "minimum": 0, "maximum": 10 },
|
||||
"description": { "type": "string" }
|
||||
},
|
||||
"required": ["primary"]
|
||||
},
|
||||
"secret": {
|
||||
"type": "string",
|
||||
"description": "The NPC's hidden truth — what they don't want the player to know"
|
||||
},
|
||||
"relationships": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/relationship" },
|
||||
"description": "Named relationships to other NPCs"
|
||||
},
|
||||
"tolerance": {
|
||||
"type": "object",
|
||||
"description": "Stress tolerance threshold",
|
||||
"properties": {
|
||||
"threshold": { "type": "integer" },
|
||||
"description": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"routine": {
|
||||
"type": "object",
|
||||
"description": "Summary of daily routine (full schedule in routines/schedules.yaml)",
|
||||
"properties": {
|
||||
"summary": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"information": {
|
||||
"type": "object",
|
||||
"description": "What this NPC knows",
|
||||
"properties": {
|
||||
"knows": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Fact IDs this NPC knows"
|
||||
},
|
||||
"access_tier": {
|
||||
"type": "string",
|
||||
"enum": ["public", "insider", "authority", "peer", "hostile"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"contentment": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"level": { "type": "integer", "minimum": -10, "maximum": 10 },
|
||||
"description": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"personality": {
|
||||
"type": "object",
|
||||
"description": "Personality traits and behavioral tendencies",
|
||||
"additionalProperties": { "type": "string" }
|
||||
},
|
||||
"tells": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/tell" },
|
||||
"description": "Observable behavioral tells (D-024)"
|
||||
},
|
||||
"skills": {
|
||||
"type": "object",
|
||||
"description": "Skill set and combat capability",
|
||||
"properties": {
|
||||
"combat_trained": { "type": "boolean" },
|
||||
"skills": {
|
||||
"type": "object",
|
||||
"additionalProperties": { "type": "integer", "minimum": 0, "maximum": 10 }
|
||||
}
|
||||
}
|
||||
},
|
||||
"triangle_membership": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" },
|
||||
"description": "Triangle slugs this NPC participates in"
|
||||
},
|
||||
"trust_levels": {
|
||||
"type": "object",
|
||||
"description": "What the NPC reveals at each trust level",
|
||||
"properties": {
|
||||
"surface": { "type": "string" },
|
||||
"real": { "type": "string" },
|
||||
"secret": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"friend_arc": {
|
||||
"type": "object",
|
||||
"description": "FRIEND arc data — only valid on FRIEND-pattern Tier 1 NPCs (D-034)",
|
||||
"properties": {
|
||||
"bonded_character": {
|
||||
"type": "string",
|
||||
"enum": ["smuggler", "detective"]
|
||||
},
|
||||
"phases": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/friend_phase" }
|
||||
}
|
||||
},
|
||||
"required": ["bonded_character", "phases"]
|
||||
},
|
||||
"dual_lens": {
|
||||
"type": "object",
|
||||
"description": "Authoring-only: how smuggler vs detective perceives this NPC",
|
||||
"properties": {
|
||||
"smuggler": { "type": "string" },
|
||||
"detective": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"notes": {
|
||||
"type": "string",
|
||||
"description": "Authoring-only: design notes"
|
||||
}
|
||||
},
|
||||
"if": {
|
||||
"properties": { "pattern": { "const": "FRIEND" } }
|
||||
},
|
||||
"then": {
|
||||
"required": ["friend_arc"]
|
||||
},
|
||||
"$defs": {
|
||||
"relationship": {
|
||||
"type": "object",
|
||||
"required": ["target", "kind"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"target": {
|
||||
"type": "string",
|
||||
"pattern": "^npc:[a-z][a-z0-9-]*$"
|
||||
},
|
||||
"kind": {
|
||||
"type": "string",
|
||||
"enum": ["colleague", "friend", "rival", "romantic", "family", "superior", "subordinate"]
|
||||
},
|
||||
"trust": {
|
||||
"type": "integer",
|
||||
"minimum": -10,
|
||||
"maximum": 10
|
||||
},
|
||||
"notes": { "type": "string" }
|
||||
}
|
||||
},
|
||||
"tell": {
|
||||
"type": "object",
|
||||
"required": ["trigger", "behavior"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"trigger": { "type": "string" },
|
||||
"behavior": { "type": "string" },
|
||||
"visible_to": {
|
||||
"type": "string",
|
||||
"enum": ["forward", "peripheral", "any"]
|
||||
}
|
||||
}
|
||||
},
|
||||
"friend_phase": {
|
||||
"type": "object",
|
||||
"required": ["phase", "description"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"phase": { "type": "integer", "minimum": 1, "maximum": 5 },
|
||||
"description": { "type": "string" },
|
||||
"trigger": { "type": "string" },
|
||||
"routine_deviation": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "routine.schema.json",
|
||||
"title": "NPC Routine Schedules",
|
||||
"description": "Daily routine schedules — all NPCs in one file per district for cross-NPC validation (D-034).",
|
||||
"type": "object",
|
||||
"required": ["district", "schedules"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"district": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]*$",
|
||||
"description": "District slug this routine file belongs to"
|
||||
},
|
||||
"schedules": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/npc_schedule" },
|
||||
"minItems": 1
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"npc_schedule": {
|
||||
"type": "object",
|
||||
"required": ["npc", "entries"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"npc": {
|
||||
"type": "string",
|
||||
"pattern": "^npc:[a-z][a-z0-9-]*$",
|
||||
"description": "NPC short-form canonical ID"
|
||||
},
|
||||
"entries": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/routine_entry" },
|
||||
"minItems": 1
|
||||
},
|
||||
"deviations": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/deviation" },
|
||||
"description": "Conditional schedule overrides (FRIEND arc staging, etc.)"
|
||||
}
|
||||
}
|
||||
},
|
||||
"routine_entry": {
|
||||
"type": "object",
|
||||
"required": ["phase", "location"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"phase": {
|
||||
"type": "string",
|
||||
"enum": ["morning", "afternoon", "evening", "night"],
|
||||
"description": "Day phase for this entry"
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]*$",
|
||||
"description": "Location slug"
|
||||
},
|
||||
"tile": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"x": { "type": "integer" },
|
||||
"y": { "type": "integer" }
|
||||
},
|
||||
"required": ["x", "y"],
|
||||
"description": "Specific tile coordinates within the location"
|
||||
},
|
||||
"activity": {
|
||||
"type": "string",
|
||||
"description": "What the NPC is doing at this location/time"
|
||||
}
|
||||
}
|
||||
},
|
||||
"deviation": {
|
||||
"type": "object",
|
||||
"required": ["trigger", "location"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"trigger": {
|
||||
"type": "string",
|
||||
"description": "Condition that activates this deviation"
|
||||
},
|
||||
"phase": {
|
||||
"type": "string",
|
||||
"enum": ["morning", "afternoon", "evening", "night"]
|
||||
},
|
||||
"location": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]*$"
|
||||
},
|
||||
"tile": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"x": { "type": "integer" },
|
||||
"y": { "type": "integer" }
|
||||
},
|
||||
"required": ["x", "y"]
|
||||
},
|
||||
"activity": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,99 @@
|
||||
{
|
||||
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
||||
"$id": "triangle.schema.json",
|
||||
"title": "Triangle Definition",
|
||||
"description": "3-NPC relationship triangle — self-contained forks, no cross-triangle cascade in v0.1 (D-024, D-047).",
|
||||
"type": "object",
|
||||
"required": ["canonical_id", "display_name", "members"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"canonical_id": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9-]*$",
|
||||
"description": "Triangle slug"
|
||||
},
|
||||
"display_name": {
|
||||
"type": "string",
|
||||
"minLength": 1
|
||||
},
|
||||
"description": {
|
||||
"type": "string"
|
||||
},
|
||||
"members": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/member" },
|
||||
"minItems": 3,
|
||||
"maxItems": 3,
|
||||
"description": "Exactly 3 NPC members"
|
||||
},
|
||||
"forks": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/fork" },
|
||||
"description": "Possible fork points in this triangle"
|
||||
},
|
||||
"resolution_states": {
|
||||
"type": "array",
|
||||
"items": { "$ref": "#/$defs/resolution" },
|
||||
"description": "Terminal states this triangle can reach"
|
||||
}
|
||||
},
|
||||
"$defs": {
|
||||
"member": {
|
||||
"type": "object",
|
||||
"required": ["npc", "role"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"npc": {
|
||||
"type": "string",
|
||||
"pattern": "^npc:[a-z][a-z0-9-]*$",
|
||||
"description": "NPC short-form canonical ID"
|
||||
},
|
||||
"role": {
|
||||
"type": "string",
|
||||
"description": "This NPC's role within the triangle"
|
||||
}
|
||||
}
|
||||
},
|
||||
"fork": {
|
||||
"type": "object",
|
||||
"required": ["id", "condition"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9_-]*$"
|
||||
},
|
||||
"condition": {
|
||||
"type": "string",
|
||||
"description": "What triggers this fork"
|
||||
},
|
||||
"outcomes": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"id": { "type": "string" },
|
||||
"description": { "type": "string" },
|
||||
"effects": {
|
||||
"type": "array",
|
||||
"items": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"resolution": {
|
||||
"type": "object",
|
||||
"required": ["id", "description"],
|
||||
"additionalProperties": false,
|
||||
"properties": {
|
||||
"id": {
|
||||
"type": "string",
|
||||
"pattern": "^[a-z][a-z0-9_-]*$"
|
||||
},
|
||||
"description": { "type": "string" }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user