feat(#1866): agent-side self-load of configured agent_skills
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
This commit is contained in:
@@ -528,7 +528,13 @@ The `Skill` tool is granted to consumer agents deliberately and is instruction-b
|
||||
|
||||
### How It Works
|
||||
|
||||
At spawn time, workflows call `gsd-tools query agent-skills <type>` (or legacy `node gsd-tools.cjs agent-skills <type>`) to load configured skills. If skills exist for the agent type, they are injected as an `<agent_skills>` block in the Task() prompt.
|
||||
Skills reach a consumer agent through **two cooperating seams** (dual injection — see [ADR 1866](adr/1866-agent-skills-dual-injection-contract.md)):
|
||||
|
||||
1. **Orchestrator-side (primary on Claude Code).** At spawn time, workflows call `gsd-tools query agent-skills <type>` (or legacy `node gsd-tools.cjs agent-skills <type>`) in their bash init and interpolate the resulting `<agent_skills>` block into the `Task()`/`Agent()` prompt. This is established across ~25 workflows.
|
||||
|
||||
2. **Agent-side self-load (durable fallback).** Each of the 22 consumer agents also self-loads in its own mandatory init step — it runs `gsd_run query agent-skills <its-type>` and `Read`s every listed `SKILL.md` per [`gsd-core/references/agent-skills-bootstrap.md`](../gsd-core/references/agent-skills-bootstrap.md). This is the path that works on every runtime, including **Cursor**, where `Skill()`-delegated workflow bash init does not reliably execute and `/gsd-autonomous` delegates via flat `Skill()` calls.
|
||||
|
||||
**Dedup guard.** If an agent's prompt already contains an `<agent_skills>` block (orchestrator already injected one), the agent skips self-load — so on runtimes where both seams run (Claude Code), the prompt never carries two copies. `query agent-skills` is read-only and idempotent: it exits 0 with an empty block when nothing is configured for the type, so self-load is zero-overhead for unconfigured agents.
|
||||
|
||||
For project-relative and global personal skills, entries appear as `@`-includes:
|
||||
|
||||
|
||||
@@ -200,6 +200,7 @@
|
||||
],
|
||||
"references": [
|
||||
"agent-contracts.md",
|
||||
"agent-skills-bootstrap.md",
|
||||
"ai-evals.md",
|
||||
"ai-frameworks.md",
|
||||
"artifact-types.md",
|
||||
@@ -325,9 +326,9 @@
|
||||
"docs.cjs",
|
||||
"drift.cjs",
|
||||
"edge-probe.cjs",
|
||||
"embedding-adapter.cjs",
|
||||
"eval-command-router.cjs",
|
||||
"eval.cjs",
|
||||
"embedding-adapter.cjs",
|
||||
"external-descriptor-trust.cjs",
|
||||
"fallow-runner.cjs",
|
||||
"federated-config.cjs",
|
||||
|
||||
@@ -292,6 +292,7 @@ Full roster at `gsd-core/references/*.md`. References are shared knowledge docum
|
||||
| `common-bug-patterns.md` | Common bug patterns for code review and verification. |
|
||||
| `debugger-philosophy.md` | Evergreen debugging disciplines loaded by `gsd-debugger`. |
|
||||
| `mandatory-initial-read.md` | Shared required-reading boilerplate injected into agent prompts. |
|
||||
| `agent-skills-bootstrap.md` | Shared agent_skills self-load contract (query + Read + dedup guard) injected into all 22 consumer agents. |
|
||||
| `project-skills-discovery.md` | Shared project-skills-discovery boilerplate injected into agent prompts. |
|
||||
| `research-documentation-lookup.md` | Shared documentation-lookup protocol (Context7 MCP + guarded CLI fallback) injected into all researcher agents. |
|
||||
| `research-philosophy.md` | Shared research philosophy (training-as-hypothesis, honest reporting, investigation-not-confirmation) injected into researcher agents. |
|
||||
|
||||
60
docs/adr/1866-agent-skills-dual-injection-contract.md
Normal file
60
docs/adr/1866-agent-skills-dual-injection-contract.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# ADR 1866: agent_skills dual injection — orchestrator-side + agent-side self-load
|
||||
|
||||
- **Status:** Accepted
|
||||
- **Date:** 2026-07-01
|
||||
- **Issue:** [#1866](https://github.com/open-gsd/gsd-core/issues/1866)
|
||||
|
||||
## Context
|
||||
|
||||
The documented `agent_skills` injection contract ([Agent Skills Injection](../CONFIGURATION.md#agent-skills-injection)) is **orchestrator-side**: at spawn time, the *workflow* calls `gsd-tools query agent-skills <type>` in its bash init and interpolates the resulting `<agent_skills>` block into the `Task()`/`Agent()` prompt. This is established across ~25 workflows and was extended to the review family by #991 / PR #1005.
|
||||
|
||||
Three gaps make orchestrator-side injection insufficient on its own:
|
||||
|
||||
1. **`/gsd-autonomous`** delegates plan/execute/review via flat `Skill()` invocations and resolves no `agent_skills` itself — it relies on the delegated workflow's bash init running. On runtimes where `Skill()`-delegated workflow bash does not reliably execute (Cursor; assessed in #1600 / #1601 — "Phase C+D per-platform plugin skill model assessment, all N/A"), the configured skills never reach the consumer agent.
|
||||
2. **GSD agent definitions** (`agents/gsd-*.md`) discover project skills via `project-skills-discovery.md` but never call `query agent-skills` for the `agent_skills` config map.
|
||||
3. The gap is **invisible**: output still looks successful, so users who configure `agent_skills` in `.planning/config.json` see no error — the skills are silently ignored (same failure class as #991).
|
||||
|
||||
`cmdAgentSkills` (`src/init.cts`) is read-only and idempotent — it exits 0 with an empty block when nothing is configured for a type. So an agent re-invoking it at its own init is safe and zero-overhead for unconfigured types.
|
||||
|
||||
## Decision
|
||||
|
||||
Adopt a **dual injection** contract. The orchestrator-side path stays as-is (no removal from the ~25 workflows); each of the 22 consumer agents *additionally* self-loads in its mandatory init step.
|
||||
|
||||
- A shared reference, [`gsd-core/references/agent-skills-bootstrap.md`](../../gsd-core/references/agent-skills-bootstrap.md), owns the contract: the query, the `Read` step, the per-runtime coverage matrix, and the **dedup guard**.
|
||||
- Each consumer agent file carries one self-load line naming its own type (e.g. `query agent-skills gsd-executor`) and `@`-including the shared reference.
|
||||
- **Dedup guard (load-bearing):** if the agent's prompt already contains an `<agent_skills>` block, self-load is skipped. On runtimes where orchestrator-side injection also runs (Claude Code), this prevents the prompt from carrying two copies — a real context-cost regression that the guard closes.
|
||||
|
||||
### Why not orchestrator-side only (Lens A)
|
||||
|
||||
Extending `autonomous.md` to resolve skills itself (the #991 pattern) fixes the *autonomous-orchestrator* gap but not the deeper one: on Cursor, the agent *definition* is what the runtime loads, and the workflow bash init is the unreliable seam. Agent-side self-load moves the resolution to the agent definition, which is the surface Cursor actually consumes. We retain the autonomous.md documentation note (the workflow no longer needs per-delegation injection) but do not duplicate injection code there.
|
||||
|
||||
### Why not remove orchestrator-side injection
|
||||
|
||||
The ~25 workflows' injection is the proven, tested path on Claude Code and remains the primary channel there. Removing it would regress the runtime that works today. The dual contract keeps both; the dedup guard makes coexistence cost-free.
|
||||
|
||||
## Coverage
|
||||
|
||||
| Skill form | Orchestrator-side | Agent self-load |
|
||||
|---|---|---|
|
||||
| Project-relative path | all runtimes | all runtimes |
|
||||
| `global:<name>` | all runtimes | all runtimes |
|
||||
| `global:<plugin>:<skill>` | Claude only (Skill-tool directive) | Claude only — **not closeable on Cursor** (#1601); no plugin/Skill-tool model exists there |
|
||||
|
||||
The `global:plugin:skill` gap on Cursor is **out of scope** for this ADR — it is a runtime limitation, not an injection-contract one.
|
||||
|
||||
## Drift guard
|
||||
|
||||
Three surfaces share the consumer-agent set: the `CONSUMER_AGENTS` list in `tests/agent-skills.test.cjs`, the agents carrying the bootstrap, and the `query agent-skills <type>` calls in workflows. `tests/agent-skills-bootstrap.test.cjs` asserts the bijection between `CONSUMER_AGENTS` and agents-with-bootstrap (Generative-Fix-Divergence, `CLAUDE.md`), with a `fast-check` property test over the agent file set. Any new consumer agent added without the bootstrap (or vice versa) fails CI.
|
||||
|
||||
## Consequences
|
||||
|
||||
- **Positive:** `agent_skills` config reaches consumer agents on every runtime regardless of orchestrator bash discipline; the `/gsd-autonomous` + Cursor path works without project-level workarounds.
|
||||
- **Negative:** Two injection seams to maintain. Mitigated by the dedup guard (no double-load) and the bijection drift-guard test (no silent desync).
|
||||
- **Neutral:** The documented contract in `CONFIGURATION.md` is updated to describe both paths.
|
||||
|
||||
## References
|
||||
|
||||
- #991 / PR #1005 — orchestrator-side injection precedent (review family)
|
||||
- #1258 / #1243 / PR #1261 — cross-runtime skill mapping epic & Claude plugin-skill consumption
|
||||
- #1366 / #1374 / #1400 / #1410 / #1415 / #1424 / #1425 — `agent_skills` resolver hardening (cwd-drift, diagnostics, provenance, flush)
|
||||
- #1600 / #1601 — per-platform plugin skill model assessment (Cursor N/A)
|
||||
Reference in New Issue
Block a user