Files
didactyl/plans/eliminate_soul_everything_is_a_skill.md

5.7 KiB

Plan: Eliminate Soul — Everything Is a Skill

Summary

Remove the concept of a "soul" (kind 31120) as a special, privileged entity. The agent's base personality, instructions, and context assembly template become a regular private skill (kind 31124) that is adopted like any other skill. This simplifies the architecture: there is only one concept — skills — and the adoption list (10123) determines what the agent knows and how it behaves.

Architecture Decision

  • Kind 31120 is eliminated entirely.
  • What was the "soul" becomes a private skill (kind 31124) with a conventional d-tag (e.g., didactyl-default).
  • The ---template--- mechanism stays — any skill can contain it.
  • The genesis.jsonc default_skill field defines this skill's content for first-run publishing.
  • On subsequent runs, the agent fetches its adopted skills from Nostr relays.

Startup Flow

First Run (genesis.jsonc present):
  1. Read genesis.jsonc
  2. Publish default_skill as kind 31124 (private skill) to relays
  3. Publish kind 10123 adoption list referencing the default skill
  4. Load default_skill content into g_system_context
  5. Continue normal startup

Subsequent Run (nsec only):
  1. Detect not-first-run via kind 10002 presence
  2. Query own kind 10123 adoption list from relays
  3. Fetch first adopted skill content from relays
  4. Load that content into g_system_context
  5. Continue normal startup

genesis.jsonc Schema

The default_skill field replaces the old kind 31120 startup event:

{
  "key": { "nsec": "nsec1..." },
  "admin": { "pubkey": "npub1..." },
  "llm": { ... },
  "api": { ... },

  // Default skill — published as kind 31124 on first run
  // and adopted into the agent's 10123 list.
  "default_skill": {
    "d_tag": "didactyl-default",
    "kind": 31124,
    "content": "# Didactyl Agent\n\nYou are Didactyl...\n\n---template---\n\n- section: admin_identity\n  role: system\n  tool: admin_identity\n  ...",
    "tags": [
      ["app", "didactyl"],
      ["scope", "private"]
    ]
  }
}

Code Changes

src/nostr_handler.c

  • Remove the kind 31120 scan in nostr_handler_reconcile_startup_events() (lines 2487-2493).
  • Add logic to load g_system_context from the default_skill config field on first run.
  • On subsequent run, query own 10123 adoption list, resolve the first adopted skill address, fetch its content, and set g_system_context.

src/config.c / src/config.h

  • Add default_skill parsing to config_load().
  • Add a default_skill_t struct to didactyl_config_t with fields: d_tag, kind, content, tags_json.
  • Remove any special handling of kind 31120 in startup event parsing.

src/main.c

  • On first run: publish the default_skill as a kind 31124 event and publish/update the 10123 adoption list to include it.
  • On subsequent run: the adoption-list-driven fetch provides the system context.
  • Remove the kind 31120 fallback in system context extraction.

src/agent.c

  • g_system_context continues to work the same way — it's just sourced from a skill instead of a soul.
  • The trigger execution path (agent_on_trigger) prepends g_system_context unchanged.
  • The WoT/stranger chat path uses g_system_context unchanged.
  • The non-template fallback path uses g_system_context unchanged.

src/prompt_template.c

  • Rename soul_content parameter to skill_content in prompt_template_parse().
  • Rename personality field to base_instructions or similar.
  • No functional changes — the ---template--- split mechanism is unchanged.

config.jsonc

  • Replace the kind 31120 startup event with a kind 31124 startup event using the same content.
  • Update the d-tag from soul to didactyl-default.

genesis.jsonc

  • Populate the default_skill field with the current soul content (personality + template sections).

Documentation Changes

README.md

  • Remove all references to "Soul", "soul", kind 31120.
  • Replace "Soul/personality" with "Default skill" or "Base skill".
  • Update the Didactyl Kinds table: remove 31120 row.
  • Update the Roadmap table: remove "Soul/personality | Kind 31120" row.
  • Update context model description: "assembled from adopted skill templates" not "soul template".

docs/SKILLS.md

  • Remove {{soul}} template variable.
  • Remove soul content field from skill schema.
  • Remove any language about soul being special or separate from skills.
  • Document that any skill can contain ---template--- sections.

docs/CONTEXT.md

  • Remove soul-specific assembly language.
  • Context is assembled from adopted skills in adoption list order.

docs/GENESIS.md

  • Update to reference default_skill instead of kind 31120 soul event.
  • Update first-run behavior description.

docs/API.md

  • Remove /api/events/soul endpoint.
  • Rename system_prompt context part to default_skill or base_instructions.

What Does NOT Change

  • The ---template--- mechanism works identically — it just lives in a skill.
  • Kind 10123 adoption list still drives context composition order.
  • Template variable resolution via tools is unchanged.
  • Trigger execution flow is unchanged (uses g_system_context).
  • Encrypted config tools (config_store/config_recall) are unchanged.
  • The genesis/nsec startup detection is unchanged.

Dependency Order

  1. Define default_skill schema in config structs and genesis.jsonc
  2. Update config parser to read default_skill
  3. Update nostr_handler to source system context from default_skill (first run) or adopted skill (subsequent run)
  4. Update main.c first-run path to publish default_skill as 31124 and update 10123
  5. Remove all kind 31120 references from code
  6. Rename soul terminology in prompt_template.c
  7. Update config.jsonc startup events
  8. Update all documentation
  9. Build and test