Emit Skill Runtime Aliases at Build Time

A skill's source of truth is its compound-node folder under nodes/Skills/<Title Case>/, with the lead file named to match the folder (nodes/Skills/Node Create/Node Create.md). Two runtime-discovery layouts are build-emitted from the source: .agents/skills/<runtime-name>/SKILL.md for the Anthropic Agent Skills SDK, and .claude/skills/<runtime-name>/SKILL.md for the Claude Code CLI's slash-command autocomplete. .scripts/emit_skills.py reads each source folder, derives the runtime name from the folder's Title Case name (Node Create -> node-create) or from the lead file's runtime_name frontmatter field, and writes symlinks at each runtime path pointing back at the source (SKILL.md -> ../../../nodes/Skills/Node Create/Node Create.md). Both runtime directories are committed to the repository; hand-editing files under either is forbidden; the emission is idempotent and regenerates both trees from source on every build.

Why

The commitment provides single-source-of-truth across dual-residence layouts. A skill has to be two things at once: a graph node conforming to the filename conventions every other node form carries (Title Case, wikilink-resolvable stem, compound-node lead matching folder name), and an agent-runtime artifact conforming to whatever a given runtime expects (lowercase-hyphenated directory name, SKILL.md lead, predictable paths for scripts/ and references/ subdirectories). The graph convention and the runtime conventions conflict directly -- the graph's wikilink resolver is looking for [[Node Create]] at nodes/Skills/Node Create/Node Create.md, while the Anthropic Agent Skills SDK is looking for SKILL.md under .agents/skills/node-create/, and the Claude Code CLI is looking for it under .claude/skills/node-create/. The symlink emission resolves the conflict by making one layout canonical and the runtime layouts views: the source layout is what authors edit and what other nodes link to; each runtime layout is what the corresponding runtime reads.

The Claude Code path is load-bearing for the CLI-facing experience. Without .claude/skills/<runtime-name>/SKILL.md present in the repository, the Claude Code CLI does not show the skill in slash-command autocomplete; the LLM can still find and invoke the skill by reading nodes/Skills/, but the typing-affordance contributors rely on is absent. Emitting to both paths keeps the graph unchanged while making every skill discoverable by both runtimes that the project actively uses.

Symlinks are the mechanism because they preserve the single-source property without any content duplication. A Web-UI edit to nodes/Skills/Node Create/Node Create.md is immediately reflected at both .agents/skills/node-create/SKILL.md and .claude/skills/node-create/SKILL.md -- no build step is needed to propagate the edit, because the symlinks resolve to the edited file at read time. The same property holds for scripts/ and references/ subdirectories: each symlink points at the source folder, so anything added or edited in the source is visible at both runtime paths without regeneration. A Web-UI contributor editing a scion's skill gets correct runtime behavior and CLI autocomplete after a single commit, with no intermediate tooling step.

The runtime directories are committed rather than gitignored because the scion commitment requires the runtime layouts to be present after a clone or instantiation without any local build. A scion owner whose first contribution is a Web-UI edit never runs build.py; if either runtime path were generated locally and gitignored, an agent running in the scion's context -- or a contributor expecting slash-command autocomplete -- would find an empty runtime directory until someone pushed a build. Committing the symlinks makes the runtime layouts part of the repository's shipped shape, matching the scion-publication property at the capability layer.

Idempotent regeneration is what keeps the emission safe under authoring churn. Each build clears every target directory and recreates it from the current source layout; a skill renamed, removed, or added updates both runtime directories mechanically on the next build. Hand-editing under either runtime path is forbidden because any hand edit is lost on the next regeneration -- a hand edit is a symptom of reaching for the wrong layer. The correct layer is always the source lead file.

Alternatives Considered

SKILL.md as the source lead filename. Author each skill as nodes/Skills/<Folder>/SKILL.md directly, eliminating the need for the symlink. Rejected because it would make skills the only node form whose compound-node lead file does not match its folder name. The compound-node convention across the graph is <Folder>/<Folder>.md -- the lead file carries the concept the folder names; breaking it for skills would force every reader, every tool, and every wikilink resolver to treat the Skills taxonomy specially. Wikilink resolution would have to carry a skill-specific exception: [[Node Create]] would resolve to SKILL.md for skills and to Node Create.md for every other form. The naming exception radiates outward; the symlink does not.

Lowercase-hyphenated source filenames. Author skills as nodes/Skills/node-create/node-create.md to match the Anthropic runtime's case convention directly, eliminating the runtime-rename step. Rejected because Title Case is the filename convention across every node form in the graph, enforced by Markdown Node Contract and relied on by wikilink-stem resolution. A [[Node Create]] wikilink from any other node expects to resolve to a Title Case stem; a lowercase-hyphenated filename would fail to resolve or force the resolver to carry a taxonomy-specific casing rule. The authoring convention and the runtime convention have legitimately different optimizations -- the authoring convention optimizes for cross-node wikilinks and human readability, the runtime convention optimizes for shell-friendly invocation names -- and each is correct in its own layer.

Committed copies of the skill body at the runtime paths. Have the emission write the skill body into each runtime path as a regular file rather than as a symlink. Rejected because a copy drifts silently from its source whenever an author edits one layer and not the other, and the drift is multiplied across each target path. The symlink cannot drift -- it dereferences to the source every time it is read. Silent drift between a source and a committed copy is the same failure [[Publish via Actions Artifact Deploy]] cited when removing committed build output from the publication pipeline; the same reasoning applies at the runtime-alias layer.

Gitignored aliases regenerated locally. Gitignore both runtime paths and expect every developer, scion owner, and CI runner to regenerate them locally before running an agent or opening a CLI session. Rejected because it breaks the scion commitment's capability layer. A Web-UI contributor editing a scion's skill never runs the build locally; an agent running in that scion's context would find an empty runtime directory, and a CLI contributor would not see the skill in slash-command autocomplete. Committed aliases make the runtime layouts part of what a clone or scion receives, consistent with the self-contained-repository shape the scion commitment names.

Emit to only one runtime path and rely on user-level symlinks for the other. Keep the build emitting only .agents/skills/ and let contributors who want Claude Code CLI autocomplete create user-level symlinks from ~/.claude/skills/ into the repository, or rely on the LLM's ability to find skills by reading nodes/Skills/ directly. Rejected because it moves the CLI-autocomplete affordance outside the repository's shipped shape, which contradicts the scion commitment: every scion should produce correct CLI behavior on first clone without asking contributors to configure user-level paths per scion. Emitting both trees keeps the CLI experience identical across every clone and every scion.

What Would Change It

Committed symlinks become unreliable on target surfaces. If a scion's CI runner, a clone-over-HTTPS path, or the GitHub Web-UI-edit round trip stopped preserving committed symlinks reliably -- on a platform that lacks symlink support, or through a tool that flattens them -- the committed-symlink approach would break. The revisit would reconsider committed copies with a drift-detection check in the build, or move to a runtime that accepts the source layout directly.

A runtime absorbs compound-node layouts directly. If either the Anthropic Agent Skills SDK or the Claude Code CLI grew the ability to discover skills from nodes/Skills/<Folder>/<Folder>.md directly -- reading the folder's lead file and tolerating Title Case and space-separated names -- the corresponding alias emission would become redundant. The revisit would remove the affected target from SKILLS_TARGET_DIRS and point that runtime at the source layout while keeping the other emission in place.

The source and runtime layout conventions converge. If the graph's filename conventions shifted toward lowercase-hyphenated filenames across node forms (unlikely, and the current state of Use ASCII Dashes in Filenames specifically preserves Title Case), the source and runtime conventions would converge and the rename step would drop out. The revisit would be a cascade from a larger filename-convention change, not a skill-specific reconsideration.

A scion adopts a different agent runtime with a conflicting layout. If a scion adopts an agent runtime whose expected layout conflicts with the Anthropic or Claude Code ones the template emits, the scion extends SKILLS_TARGET_DIRS with the additional path, or maintains its own emission alongside (or instead of) the template's. The commitment survives; the emission set grows.

Relations