Template Scope

Overview

Templates can be constrained to specific parts of your story using the scope field. This prevents templates from being used in inappropriate contexts.

Scope Levels

Global (No Scope)

Templates without scope constraints are available everywhere.

templates:
  generic_guard:
    kind: Actor
    # No scope = global

Scene-Scoped (parent_label)

Templates declared in a scene are automatically scoped to that scene’s blocks.

scenes:
  village:
    templates:
      village_elder:
        kind: Actor
        # Inferred: scope.parent_label = "village"
    
    blocks:
      square:
        roles:
          elder: {actor_template_ref: "village_elder"}  # ✓ In scope
  
  city:
    blocks:
      plaza:
        roles:
          elder: {actor_template_ref: "village_elder"}  # ✗ Out of scope!

Block-Scoped (source_label)

Templates declared in a block are only available in that specific block.

scenes:
  lab:
    blocks:
      containment:
        templates:
          specialist:
            kind: Actor
            # Inferred: scope.source_label = "lab.containment"
        
        roles:
          expert: {actor_template_ref: "specialist"}  # ✓ In scope
      
      research:
        roles:
          expert: {actor_template_ref: "specialist"}  # ✗ Out of scope!

Scope Override

You can explicitly override the inferred scope:

scenes:
  village:
    templates:
      wandering_merchant:
        kind: Actor
        scope: null  # Override to global (available everywhere)
      
      secret_contact:
        kind: Actor
        scope:
          ancestor_tags: ["conspiracy"]  # Only in blocks with conspiracy tag

Scope Selectors

source_label

Template only valid in exact source node:

scope:
  source_label: "village.smithy"
  # Only works in the "smithy" block of "village" scene

parent_label

Template valid in children of parent:

scope:
  parent_label: "village"
  # Works in any block under "village" scene

ancestor_tags

Template valid if ancestor has matching tags:

scope:
  ancestor_tags: ["conspiracy", "hidden"]
  # Works in any block/scene with conspiracy AND hidden in ancestor chain

ancestor_labels

Template valid if ancestor has matching label:

scope:
  ancestor_labels: ["village", "city"]
  # Works in blocks under scenes labeled "village" OR "city"

Provisioning Behavior

When a role/setting references a template:

  1. Template lookup: Find template in world.template_registry by label

  2. Scope check: Validate template is in scope for source node

  3. Instantiation: If valid, create concrete node from template

  4. Rejection: If out of scope, log warning and return no offers

Best Practices

  1. Use global templates for truly generic concepts

    templates:
      generic_guard: {...}  # Works everywhere
    
  2. Use scene templates for location-specific variants

    scenes:
      castle:
        templates:
          royal_guard: {...}  # Only in castle
    
  3. Use block templates for ultra-specific needs

    blocks:
      throne_room:
        templates:
          king_guard: {...}  # Only here
    
  4. Override scope when sharing across scenes

    scenes:
      village:
        templates:
          merchant:
            scope: null  # Share with other scenes
    

Troubleshooting

“Template ‘X’ not found”

  • Template doesn’t exist in registry

  • Check spelling and declaration location

“Template ‘X’ out of scope”

  • Template has scope constraint not satisfied

  • Check scope.parent_label, source_label, etc

  • Consider overriding scope to null for global access

“No offers for requirement”

  • Template exists but is out of scope

  • Check debug logs for scope rejection reason