Template Categories Architecture
This page documents the technical design of the Template Categories system introduced in SBAI-835. Categories classify entity types by their purpose and drive capability flags, editor routing, and the activation workflow.
Template Category Architecture
Frontmatter Schema
Every template file declares its category in YAML frontmatter. The category field is optional — if absent, the system defaults to entity.
---
template_version: "2.0"
id: "script"
entity_type: "script"
category: "document" # entity | document | rule | skill
capabilities:
outline_view: true
export_pdf: true
rich_text: true
relationships: false
production_status: true
marketplace_eligible: true
editable: true
description: "Screenplay and script document template"
---Valid category values:
| Value | Purpose |
|---|---|
entity | In-world objects: characters, locations, assets, creatures |
document | Narrative documents: scripts, chapters, scenes, acts |
rule | AI generation rule files |
skill | Agent skill definitions |
Category Classification Logic
Category resolution follows this priority order:
- Explicit frontmatter —
category:field in the template file (highest priority) - Filename inference — templates named
*_RULE.yamlresolve torule,*_SKILL.yamltoskill - Default — falls back to
entityif no category signal is present
The TemplateSchemaService.get_schema() method parses the frontmatter and populates the category and capabilities fields in the returned schema dict. These are passed through to the EntityTypeInfo response model in the entity types route.
Namespace Collision Avoidance
Template discovery scans multiple directories: _Templates/Standard/, _Templates/Core/, _Templates/Custom/, and user-installed packs in _Templates/Packs/. To prevent two packs from defining the same entity type slug:
- The
_discover_templates()method inTemplateSchemaServiceprocesses directories in priority order: Core > Standard > Custom > Packs - If two templates define the same
entity_type, the higher-priority source wins and a warning is logged:Duplicate entity type '{type}' from lower-priority source '{path}' — skipped - Pack authors should prefix custom types with their pack ID to avoid conflicts:
mygame_factionrather thanfaction
Implementation Details
TemplateSchemaService Category Resolution
# services/template_schema.py (simplified)
class TemplateSchemaService:
def get_schema(self, entity_type: str) -> dict:
"""
Returns the full schema for entity_type.
Resolves from the filesystem template directories (Core > Standard > Custom > Packs).
"""
...
def _parse_template(self, template_path: str) -> dict:
"""Parse YAML frontmatter into a schema dict."""
# Reads category, capabilities, fields from frontmatter
# Returns: { 'entity_type': str, 'category': str,
# 'capabilities': dict, 'fields': [...] }
...The parsed schema is cached in memory by entity type. The cache is invalidated when templates are reloaded via POST /api/templates/reload or when the filesystem watcher detects template file changes.
StudioBrain Cloud extends this with a DB-backed template layer. Cloud caches schemas per-tenant and supports tenant-scoped template overrides stored in the database. See Template Inheritance for the full resolution order.
Multi-Directory Discovery Pattern
TemplateSchemaService.__init__() calls get_template_directories() from shared.py to build the list of directories to scan:
# shared.py
def get_template_directories() -> List[Path]:
"""Return ordered list of template directories to scan.
Priority order (highest to lowest):
1. _Templates/Core/ — built-in, non-overridable core types
2. _Templates/Standard/ — first-party standard types
3. _Templates/Custom/ — project-level customizations
4. _Templates/Packs/*/templates/ — community pack templates
"""Each directory is scanned for files matching *_TEMPLATE.md or *_TEMPLATE.yaml. The entity type slug is derived from the filename: CHARACTER_TEMPLATE.md becomes character.
API Response Structure
The GET /api/tenant/entity-types endpoint returns full category and capabilities metadata:
{
"entity_types": [
{
"type": "character",
"label": "Character",
"plural_label": "Characters",
"icon_name": "user",
"entity_count": 42,
"is_system": false,
"source": "template",
"status": "active",
"supports_custom_ui": false,
"category": "entity",
"capabilities": {
"export_pdf": false,
"outline_view": false,
"rich_text": false,
"relationships": true,
"production_status": true
},
"editable": true,
"marketplace_eligible": true
},
{
"type": "script",
"label": "Script",
"plural_label": "Scripts",
"icon_name": "file-text",
"entity_count": 3,
"is_system": false,
"source": "template",
"status": "active",
"supports_custom_ui": false,
"category": "document",
"capabilities": {
"export_pdf": true,
"outline_view": true,
"rich_text": true,
"relationships": false,
"production_status": true
},
"editable": true,
"marketplace_eligible": true
}
],
"plan": "indie",
"max_entity_types": 15
}Core vs Standard vs Custom vs User
Folder Structure
_Templates/
Core/ # Always available; not overridable
DIALOGUE_TEMPLATE.md
ASSEMBLY_TEMPLATE.md
QUEST_TEMPLATE.md
TIMELINE_TEMPLATE.md
Standard/ # First-party types; activated per-project
CHARACTER_TEMPLATE.md
LOCATION_TEMPLATE.md
SCRIPT_TEMPLATE.md
...
Custom/ # Project-specific overrides
CHARACTER_TEMPLATE.md # Overrides Standard/CHARACTER_TEMPLATE.md for this project
Packs/
game-dev-pack/
templates/
FACTION_TEMPLATE.md
CREATURE_TEMPLATE.mdAccess Patterns
| Source | Overridable? | Activation Required? | Plan Limit Counted? |
|---|---|---|---|
| Core | No | No — always active | No |
| Standard | Yes (via Custom/) | Yes | Yes |
| Custom | Overrides Standard | Yes | Yes |
| Packs | No (but namespace-prefixed) | Yes | Yes |
Update Behavior
- Core templates are updated with the StudioBrain application itself. Changes take effect on restart.
- Standard templates are updated via application updates. If a project has a Custom override for the same type, the override takes precedence.
- Custom templates are never touched by application updates. The developer owns the file.
- Pack templates are updated when the pack is updated via the Marketplace or manually replaced.
Template Categories API
Endpoints Reference
| Method | Path | Description |
|---|---|---|
GET | /api/tenant/entity-types | All active entity types with category + capabilities |
GET | /api/tenant/entity-types?category=document | Filter by category |
GET | /api/tenant/entity-types?category=entity | Filter to entity types only |
Query Parameters
| Parameter | Type | Description |
|---|---|---|
category | string | Filter results to a single category: entity, document, rule, skill |
Request/Response Examples
Get all active types:
curl -H "Authorization: Bearer {token}" \
https://api.studiobrain.ai/api/tenant/entity-typesFilter to document types only:
curl -H "Authorization: Bearer {token}" \
"https://api.studiobrain.ai/api/tenant/entity-types?category=document"Example minimal response:
{
"entity_types": [
{
"type": "script",
"label": "Script",
"category": "document",
"capabilities": { "outline_view": true, "export_pdf": true },
"entity_count": 3,
"is_system": false
}
],
"plan": "indie",
"max_entity_types": 15
}See Also
- Template Authoring — Full frontmatter field reference
- Template Packs System — Pack structure and distribution
- Template Categories User Guide — End-user perspective
- REST API Reference — Full API endpoint documentation