Each of the 22 consumer agents now self-loads its configured agent_skills in its mandatory init step, so .planning/config.json agent_skills.<type> reaches the agent on every runtime — including Cursor and /gsd-autonomous, where Skill()-delegated workflow bash init did not reliably execute. - gsd-core/references/agent-skills-bootstrap.md: shared contract (query + Read + dedup guard that skips when <agent_skills> is already in the prompt, so Claude's orchestrator-side injection never doubles) - 22 agents/gsd-*.md: one self-load line naming the agent's own type - gsd-core/workflows/autonomous.md: note that delegated agents self-load - tests/agent-skills-bootstrap.test.cjs: regression + parity (CONSUMER_AGENTS bijection + fast-check property) — Generative-Fix-Divergence guard - docs: ADR-1866, CONFIGURATION dual-injection How It Works, INVENTORY row, Changed changeset Closes #1866
3.2 KiB
Agent Skills Self-Load (Bootstrap)
Shared contract. Every
agent_skillsconsumer agent self-loads its configured skills in its mandatory init step, so a project's.planning/config.jsonagent_skills.<agent-type>mapping reaches the agent that actually does the work — even when the orchestrator did not run bash init (e.g. a runtime whoseSkill()delegation does not reliably execute the delegated workflow's bash, such as Cursor; see open-gsd/gsd-core#1600 / #1601). This is the durable counterpart to the orchestrator-side injection documented under Agent Skills Injection.
When to run
In your mandatory init step — right after mandatory-initial-read.md / the
Project skills discovery, before any other work.
Steps
-
Dedup guard (MANDATORY). Look at your own prompt. If it already contains an
<agent_skills>block, the orchestrator already injected one — skip self-load entirely. Loading a second copy wastes context on runtimes where orchestrator-side injection also runs (e.g. Claude Code). The guard is what keeps the two seams from doubling the block. -
Query your configured skills. Use your own agent type — the
name:value in your frontmatter (e.g. an agent whose frontmatter saysname: gsd-executorqueriesgsd-executor). The query is read-only and idempotent — it exits 0 with an empty block when nothing is configured for your type:_AGENT_SKILLS=$(gsd_run query agent-skills <YOUR-FRONTMATTER-NAME> 2>/dev/null || true)The runtime
gsd_runresolver is the standard one from_runtime-launcher.snippet.sh; your own init already defines it. -
Read every listed skill. The block emits entries as
@<path>/SKILL.mdincludes —Readeach one before starting work. If the block is empty, there is nothing to do (zero overhead).
What self-load does and does not cover
| Skill form | Self-loads? | Notes |
|---|---|---|
Project-relative path (skills/my-skill) |
✅ everywhere | Read the @-include |
Global personal (global:<name>) |
✅ everywhere | resolves to the runtime global skills dir, then Read |
Plugin-provided (global:<plugin>:<skill>) |
Claude only | emitted as a Skill-tool directive on Claude; skipped with a warning on all other runtimes — the plugin/Skill-tool model has no equivalent elsewhere (#1601, #1258). Not closeable on Cursor. |
Notes
- Idempotent and read-only.
query agent-skillsnever mutates state; calling it twice (once by the orchestrator, once by the agent) is harmless because the dedup guard suppresses the second load. - No new config keys. This reuses the existing
agent_skillsmap and the existingbuildAgentSkillsBlock/cmdAgentSkillsmachinery (src/init.cts). The 22 consumer agent types are mirrored intests/agent-skills.test.cjs(CONSUMER_AGENTS) and guarded against drift bytests/agent-skills-bootstrap.test.cjs. - Checkers / read-only agents. Bash is universal across consumer agents, so self-load works for plan-checkers, verifiers, and auditors too; the bootstrap assumes no tool an agent lacks.