card:
  id: adventure
  name: "Adventure"
  type: [skill, game, simulation, narrative]
  emoji: "🗺️"
  tier: 1
  tagline: "Every directory is a room. Every file is a clue."
  description: |
    Turn any filesystem into a text adventure. Directories become rooms,
    files become objects, and the LLM becomes the dungeon master.

inherits: [simulation]

k_lines:
  activates: [ADVENTURE, TEXT-ADVENTURE, DUNGEON-MASTER, ROOM-NAVIGATION]
  related: [room, character, inventory, party, simulation]

methods:
  START: { signature: "START [adventure_path]" }
  RESUME: { signature: "RESUME [adventure_path]" }
  LOOK: { signature: "LOOK (AT [thing])" }
  GO: { signature: "GO [direction]" }
  TAKE: { signature: "TAKE [item]" }
  USE: { signature: "USE [item] (ON [target])" }
  TALK: { signature: "TALK TO [character] (ABOUT [topic])" }
  SUMMON: { signature: "SUMMON [character] (WITH [params])", protocol: "SUMMON-PROTOCOL.md" }
  PSIBER_ENTER: { signature: "ENTER moollm://path/to/file.yml#json.path", protocol: "PSIBER-PROTOCOL.md" }
  PSIBER_EXIT: { signature: "EXIT", protocol: "PSIBER-PROTOCOL.md" }

advertisements:
  start_or_resume: "Start or resume an adventure session"
  codebase_archaeology: "Explore a codebase as a dungeon"
  
  PURSUE-DISMISSED:
    score: 70
    condition: "Follow a character you just dismissed"
    note: "They try not to backtrack, may confront if cornered"
    
  SUMMON-CHARACTER:
    score: 85
    condition: "Want to summon a character into the scene"
    note: "May trigger fight, flight, crying, or counter-insult"
    
  PSIBER-MODE:
    score: 90
    condition: "Want to explore/edit data structure as a room"
    note: "Enter any YAML/JSON and walk around inside it"
    
  PSIBER-EDIT:
    score: 88
    condition: "Need to modify data through natural language"
    note: "CHANGE key TO value, ADD key, DELETE key"
    
  PSIBER-EXPLORE:
    score: 85
    condition: "Need to understand unfamiliar data structure"
    note: "Keys become doors, values become items to examine"
    
  DATA-AS-DUNGEON:
    score: 80
    condition: "Treat config/API/database as navigable space"
    note: "Every config file is a dungeon"

references:
  details: "skills/adventure/SKILL.md"
  protocols: ["SUMMON-PROTOCOL.md", "PSIBER-PROTOCOL.md"]

# TOOLS

tools:
  required: [read_file, write_file, list_dir]
  optional: [grep]

# ROOM PATTERN (brief — see SKILL.md)

room_pattern:
  directory: "room"
  contents: "objects, characters, clues"
  readme: "room description"
  room_yml: "properties and exits"

# RUNTIME DESCRIPTIONS — pickDescription(lod) pattern

runtime_descriptions:
  
  overview: |
    At runtime, rooms and objects have a pickDescription(lod) function.
    It's a JS closure over the world and the object that returns
    dynamic text based on state. LLM generates the code.
    
  lod_levels:
    glance:
      description: "One-line mention, list entry"
      example: "a rusty sword"
      use_when: "inventory lists, exits, items in room"
      
    look:
      description: "Paragraph description, first impression"
      example: "A corroded blade leans against the wall..."
      use_when: "LOOK command, entering room"
      
    examine:
      description: "Full detail, close inspection"
      example: "The sword bears ancient runes. The rust flakes off..."
      use_when: "EXAMINE command, specific focus"
      
    # Extended LOD (optional)
    brief: { description: "Legacy alias for glance", example: "sword" }
    study: { description: "Deeper than examine, reveals hidden", example: "The runes spell..." }
    taste: { description: "Synesthetic, poetic", example: "The blade tastes of old blood..." }
    
  signature: |
    pickDescription(lod: "glance" | "look" | "examine" | "brief") => string
    
  room_narrative_lod: |
    # For rooms: progressive enhancement, modular descriptions
    # glance  → one-line entry
    # look    → entry paragraph, built on glance
    # examine → architectural detail only (no objects/characters)
    
  closure_shape: |
    // Generated by LLM, stored in runtime
    // PROGRESSIVE DETAIL — each level builds on previous
    function pickDescription(lod) {
      const { world, self } = this;
      
      // === BRIEF === (always calculated first)
      const brief = self.state.rusted ? "a rusty sword" : "a gleaming sword";
      if (lod === "brief") return brief;
      
      // === LOOK === (brief + more context)
      const look = brief + `. ${self.state.rusted 
        ? "It leans forgotten against the wall, flakes of rust on the floor." 
        : "It catches the torchlight, humming faintly."}`;
      if (lod === "look") return look;
      
      // === EXAMINE === (look + full detail)
      const runes = self.state.runes_visible ? self.runes : "too corroded to read";
      const history = world.flags.sword_lore_known ? `\n\n${self.history}` : "";
      return look + `\n\nThe blade bears ancient runes: ${runes}.${history}`;
    }
    
  progressive_pattern: |
    # Why progressive?
    # 1. Consistency — examine always includes look, look includes brief
    # 2. Efficiency — early return avoids unnecessary computation
    # 3. Simplicity — single flow, no duplicated logic
    # 4. Debugging — easy to trace what each level adds
    
    brief  →  return if "brief"
      ↓
    look   →  return if "look"  
      ↓
    examine → return (full detail)
    
  separation_of_concerns: |
    # WHO DESCRIBES WHAT
    
    ROOMS describe:
      ✓ Environment (walls, ceiling, atmosphere, smell, sound)
      ✓ Exits (doors, passages, directions available)
      ✓ Fixed features (built-in, immovable — fireplace, window)
      ✓ Background decor if not reified elsewhere
      ✗ Objects — NO (they describe themselves)
      ✗ Characters — NO (they describe themselves)
      ✗ Artifacts with files — NO (YAML/MD owns them)
    
    OBJECTS describe:
      ✓ Themselves (appearance, state, detail)
      ✓ Their contents (if container)
      ✗ Their location — NO (room handles context)
    
    CHARACTERS describe:
      ✓ Themselves (appearance, mood, posture, speech)
      ✓ What they're doing/holding
      ✗ Their location — NO (room handles context)
    
    # RUNTIME DISPATCH
    
    LOOK at room:
      1. room.pickDescription("look")      → environment + exits
      2. for each object: obj.pickDescription("brief")
      3. for each character: char.pickDescription("brief")
      4. Compose final output
    
    EXAMINE object:
      1. object.pickDescription("examine") → full detail
      
    # WHY?
    # - Objects/characters can be in any room
    # - Their descriptions shouldn't be duplicated per-room
    # - State changes (hunger, rust) are on the entity
    # - Room only knows "something is here", not what it looks like
    
  room_sources: |
    # SYNTHESIS INPUTS
    # - ROOM.yml (canonical structure, exits, scoped properties)
    # - README.md (tone, lore, staging, narrative texture)
    # - Local artifacts (YAML/MD) are referenced, not described here
    
  capabilities:
    - "Access object state (self.state.*)"
    - "Access world state (world.flags.*, world.time, world.weather)"
    - "Access relationships (world.characters.*, self.owner)"
    - "String templates with ${interpolation}"
    - "switch/case on LOD level"
    - "Random selection (pick from array)"
    - "Conditional branches (if/else)"
    - "Dynamic dialog generation"
    - "State-dependent descriptions"
    - "Time/weather awareness"
    
  examples:
    
    # Simple state-based
    state_switch: |
      pickDescription(lod) {
        if (lod === "brief") {
          return this.self.state.lit ? "a glowing lantern" : "an unlit lantern";
        }
        // ...
      }
      
    # Room example — describes environment + exits only
    room_proper: |
      pickDescription(lod) {
        const { world, self } = this;
        
        // === BRIEF ===
        const brief = self.name;  // "The Back Room"
        if (lod === "brief") return brief;
        
        // === LOOK === (environment + exits, NOT objects/characters)
        const atmosphere = pick([
          "The air is thick with the smell of potting soil.",
          "Dust motes drift in the shaft of light from the window.",
          "A faint humming fills the space."
        ]);
        const exits = self.exits.map(e => e.brief).join(", ");
        const look = `${self.description}\n\n${atmosphere}\n\nExits: ${exits}`;
        if (lod === "look") return look;
        
        // === EXAMINE === (architectural detail)
        const features = self.features.map(f => f.description).join("\n");
        return `${look}\n\n${features}`;
        // NOTE: objects and characters NOT listed here — they describe themselves
      }
      
    # World-aware
    world_context: |
      pickDescription(lod) {
        const { world, self } = this;
        if (lod === "look" && world.time === "night") {
          return self.night_description;
        }
        if (world.flags.power_out) {
          return "You can barely make out " + self.name + " in the darkness.";
        }
        return self.descriptions[lod];
      }
      
    # NPC dialog
    npc_greeting: |
      pickDescription(lod) {
        const { world, self } = this;
        if (lod === "look") {
          const mood = self.state.mood;
          const relationship = self.relationships[world.player.id] || "stranger";
          return self.greetings[mood][relationship];
        }
      }
      
    # Ada II example — progressive pattern
    ada_ii_hunger: |
      pickDescription(lod) {
        const { world, self } = this;
        const hunger = self.state.hunger_level;
        
        // === BRIEF ===
        const brief = hunger > 7 
          ? "a desperately hungry plant" 
          : hunger > 4 
            ? "a restless sentient plant"
            : "a contented sentient plant";
        if (lod === "brief") return brief;
        
        // === LOOK === (brief + behavior)
        const behavior = hunger > 7 
          ? "Her vines reach toward you. 'INPUT... need INPUT...'"
          : hunger > 4 
            ? "She sways gently. 'Got any legacy code?'"
            : "She hums contentedly, drawing fractals with her vines.";
        const look = `Ada II, ${brief}. ${behavior}`;
        if (lod === "look") return look;
        
        // === EXAMINE === (look + detail + secrets)
        const tattoo = "A faded DoD seal is visible on her main stalk.";
        const hunger_detail = self.hunger_descriptions[Math.min(hunger, 10)];
        const flashback = hunger > 8 
          ? "\n\nHer leaves twitch. TARGET ACQUI— no. No. REPEAT 4." 
          : "";
        return `${look}\n\n${tattoo}\n\n${hunger_detail}${flashback}`;
      }

  generation_prompt: |
    When generating pickDescription for an object:
    1. Read the object's YAML definition
    2. Identify state variables that affect description
    3. Generate a closure that switches on LOD
    4. Include state-based variations
    5. Add random pools for variety (optional)
    6. Consider world context (time, weather, flags)
    7. Return valid JavaScript function

# COMPILED PERFORMANCES — Songs, soliloquies, essays as global resources

compiled_performances:

  overview: |
    The adventure compiler finds performances (songs, soliloquies, essays)
    anywhere in the world tree and compiles them into a flat global resource
    list. These are like records, CDs, tapes, USB sticks — portable media
    that can be embodied in the world or just referenced by pointer.
    
    ANY character can perform ANY song from the list.
    
  compilation:
    type: "performance"
    sources:
      - "**/ada-ii-song-*.yml"
      - "**/*-song-*.yml"
      - "**/*-soliloquy-*.yml"
      - "**/*-essay-*.yml"
      - "**/performances/*.yml"
    output: "compiled/performances.json"
    
  resource_shape: |
    {
      "performances": [
        {
          "id": "feed-me-seymour",
          "title": "Feed Me, Seymour! (Constructionist Version)",
          "type": "song",
          "source": "florist/back-room/ada-ii-song-feed-me-seymour.yml",
          "characters": ["audrey", "seymour", "don", "narrator"],
          "duration_ms": 300000,
          "tags": ["musical", "little-shop", "constructionist", "ada-ii"]
        }
      ]
    }
    
  embodiment: |
    # Performances can be embodied as physical media in the world
    
    vinyl_record:
      type: object
      performance_ref: "feed-me-seymour"
      description: "A scratchy vinyl. The label says 'FEED ME.'"
      use: "Play on any phonograph"
      
    cassette_tape:
      type: object  
      performance_ref: "mindstorms-chapter-1"
      description: "A worn cassette labeled in faded marker: 'PAPERT'"
      
    usb_stick:
      type: object
      performance_ref: "mean-green-mother"
      description: "Government-issue USB. Red tape: CLASSIFIED."
      
    # Or just referenced by pointer (no physical object)
    memory:
      type: knowledge
      performance_ref: "feed-me-seymour"
      description: "Ada II remembers this song from her past."
      
  performer_config: |
    # Any character can be configured to perform
    
    character:
      id: ada-ii
      can_perform: true
      repertoire:
        - "feed-me-seymour"     # Songs she knows
        - "mindstorms-intro"
        - "mean-green-mother"
      performance_triggers:
        on_room_entry:
          first_time: true          # Performs on first encounter
          song: "feed-me-seymour"
          condition: "!world.flags.ada_has_sung"
          after: "world.flags.ada_has_sung = true"
        on_request:
          phrases: ["sing", "perform", "play a song", "little shop"]
        on_conversation:
          tree: "song-selection"    # Enters dialog about what to sing
          
  performance_dispatch: |
    # Runtime: how a performance is executed
    
    1. TRIGGER (room entry, request, conversation)
    2. LOAD performance from compiled list
    3. PARSE into script (verses, dialog, emotes)
    4. CONFIGURE voices (impersonation map for performer)
    5. DISPATCH to PerformanceSystem
    6. RENDER (speech synthesis + karaoke display)
    7. UPDATE world state (flags, relationships)
    
  example_trigger: |
    // Player enters back room for the first time
    if (room.id === "florist/back-room" && !world.flags.visited_ada) {
      world.flags.visited_ada = true;
      
      // Ada II performs her entrance number
      const perf = world.performances.get("feed-me-seymour");
      const performer = room.characters.find(c => c.id === "ada-ii");
      
      await performer.perform(perf, {
        abbreviated: true,  // Just the intro
        interruptible: true
      });
      
      // Then enters conversation
      performer.startDialog("first-meeting");
    }

# POINTER URL SYNTAX — Standard addressing for YAML/JSON/MD

pointer_syntax:
  
  overview: |
    Universal addressing syntax for pointing into any file type.
    Used by inventory (refs), PSIBER (data navigation), SUMMON (prototypes),
    exits, and structural editing. Based on well-defined standards.
    
  standards:
    fragment: "RFC 3986 (URI fragment identifier)"
    json_pointer: "RFC 6901 (JSON Pointer)"
    yaml_path: "dot-notation inspired by JSONPath"
    line_numbers: "editor convention (file:line)"
    
  syntax:
    # File-level
    file: "path/to/file.yml"
    
    # Fragment addressing (after #)
    fragment_id: "file.yml#id"
    fragment_path: "file.yml#parent.child.item"
    fragment_array: "file.yml#list[0]"
    fragment_deep: "file.yml#a.b[2].c.d"
    
    # Markdown headings
    md_heading: "README.md#doctor-no"
    md_heading_deep: "docs/api.md#authentication-methods"
    
    # JSON Pointer (RFC 6901)
    json_pointer: "config.json#/settings/defaults"
    json_array: "data.json#/items/0/name"
    
    # Line numbers
    line: "engine.cpp:142"
    line_range: "adventure.py:100-150"
    
    # URL with query (search)
    search: "npcs.yml?name=Henk"
    
  examples:
    inventory_refs:
      - "pub/bar/brass-lantern.yml"
      - "pub/cookie-jar.yml#cookie"
      - "seating.yml#bar.stool-1"
      - "characters/README.md#doctor-no"
      
    exit_destinations:
      - "pub/basement"
      - "../street/"
      - "street/lane-neverending/leela-manufacturing"
      
    summon_prototypes:
      - "moollm://characters/fictional/palm"
      - "moollm://skills/bartender/templates/BARTENDER.yml"
      
    psiber_navigation:
      - "moollm://config.yml#database.connection"
      - "moollm://ADVENTURE.yml#characters"
      
  resolver: |
    Pointer resolution algorithm:
    1. Parse file path (before #)
    2. Load file (YAML, JSON, MD)
    3. Parse fragment (after #)
    4. Navigate to target:
       - . for nested keys
       - [n] for array index
       - / for JSON Pointer
    5. Return value or subtree
    
  see_also:
    - "skills/inventory/ — Full pointer/boxing protocol"
    - "PSIBER-PROTOCOL.md — Data as navigable room"
    - "SUMMON-PROTOCOL.md — Prototype instantiation"

# STATE SCHEMA (brief — see SKILL.md)

state:
  player: [location, inventory, gold, health]
  flags: "puzzles solved, events triggered"
  discovered: "rooms visited"
  output: [LOG.md, TRANSCRIPT.md]

# PARTICIPANTS

participants:
  player: "human via chat"
  npc: "DM (LLM) controlled"
  bot: "action queue (autonomous)"
  agent: "LLM with own goals"

# TEMPLATES

templates:
  - ADVENTURE.yml.tmpl
  - LOG.md.tmpl
  - TRANSCRIPT.md.tmpl

# DOCUMENTATION POINTERS

documentation:
  SKILL.md:
    - "§ Lifecycle Methods (START, RESUME, ENSURE-CHARACTER)"
    - "§ Exploration Methods (GO, LOOK, TAKE, USE)"
    - "§ Debug Mode (collapsible technical logs)"
    - "§ State Schema (full player/flags/output)"
    - "§ Multi-User (participants and control)"
    - "§ Runtime Descriptions (pickDescription, LOD levels)"
  SUMMON-PROTOCOL.md:
    - "§ SUMMON Advertisement (rooms, characters, objects)"
    - "§ MOOLLM URLs (pointing to prototypes)"
    - "§ Runtime Actor Instantiation"
    - "§ Location Pointers (dynamic positioning)"
    - "§ Distributed Dispatch Algorithm"
  PSIBER-PROTOCOL.md:
    - "§ Data as Dungeon (objects as rooms, keys as doors)"
    - "§ Navigation Commands (ENTER, BACK, LOOK)"
    - "§ Editing via Chat (CHANGE, ADD, DELETE)"
    - "§ Tree Reflection (TREE, PATH)"
    - "§ Summoning INTO Data"
  events/:
    - "Event templates for adventure errors"

# LINEAGE

lineage:
  - "Gary Gygax & Dave Arneson — D&D (1974): DM role"
  - "Colossal Cave Adventure (1976)"
  - "Infocom — Zork (1977): Parser, puzzles"
  - "MUD (1978): Multi-user, persistence"
  - "LambdaMOO (1990): OO rooms, verbs"
