SoPhi Harness — From Inert Files to Self-Writing Skills: Evolving an AI Agent’s Skill System Sophi's sophi-skills module shipped as an inert Markdown/YAML skill store with a SkillLoader and SkillRegistry that had no consumer for weeks, until a SkillTool was added to let the model pull a skill's instructions into context on demand. The module's pom.xml depends only on kotlin-stdlib, a YAML parser, and sophi-versioning — no sophi-core, Tool interface, or coroutines — so SkillTool could not live inside sophi-skills itself. SkillRegistry layers a global directory under a project directory, with project entries winning on ID collision and malformed cards skipped via runCatching. A general contractor doesn’t wait for a family to pick out a stove before running the gas line to the kitchen. Rough-in work happens before there’s a tenant: the pipe stubs out of the floor where a sink will eventually sit, built to spec and completely inert, because the fixtures that will actually use any of it haven’t been chosen yet. You build for a future occupant you can describe in general terms but haven’t met. sophi-skills Shipped at the very beginning during the skeleton creation as exactly this kind of rough-in work: a place to keep instructions nobody was reading yet. The module did nothing useful the day it landed. It was finished months later by something else entirely — and the gap between "stored" and "read" is where this story actually lives. A skill in Sophi is a single Markdown file with YAML frontmatter — a recipe card, one file per dish: --- title: "Deploy runbook" description: "How to deploy this service safely" tags: ops, deploy --- Steps to deploy... data class Skill val metadata: SkillMetadata, val body: String, val source: Path SkillLoader reads a whole directory of these into memory eagerly — every file, every byte, parsed at startup, sorted by title. SkillRegistry layers a global directory underneath a project directory, project entries winning on ID collision, tolerant of a malformed card runCatching { loader.loadFile file }.getOrNull — one bad recipe doesn't spoil the box . None of that is "lazy" in the way the module's own description implies. What's actually lazy is what happens after loading: a skill's body sits parked in a Map