DeveloperTemplate Categories Architecture

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:

ValuePurpose
entityIn-world objects: characters, locations, assets, creatures
documentNarrative documents: scripts, chapters, scenes, acts
ruleAI generation rule files
skillAgent skill definitions

Category Classification Logic

Category resolution follows this priority order:

  1. Explicit frontmattercategory: field in the template file (highest priority)
  2. Filename inference — templates named *_RULE.yaml resolve to rule, *_SKILL.yaml to skill
  3. Default — falls back to entity if 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 in TemplateSchemaService processes 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_faction rather than faction

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.md

Access Patterns

SourceOverridable?Activation Required?Plan Limit Counted?
CoreNoNo — always activeNo
StandardYes (via Custom/)YesYes
CustomOverrides StandardYesYes
PacksNo (but namespace-prefixed)YesYes

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

MethodPathDescription
GET/api/tenant/entity-typesAll active entity types with category + capabilities
GET/api/tenant/entity-types?category=documentFilter by category
GET/api/tenant/entity-types?category=entityFilter to entity types only

Query Parameters

ParameterTypeDescription
categorystringFilter 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-types

Filter 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