Skills in Workflows
Declare the skills a phase needs, bundled in your plugin or pinned to a repository, and have them registered and injected automatically.
A skill is a folder containing a SKILL.md file with YAML frontmatter, following
the vercel-labs/skills convention. It is
the unit of capability a workflow declares for the agent running a phase.
Skills are harness-agnostic. The same declaration works whether a phase runs
Claude or Codex, because the pinned skills CLI inside the workspace knows where
each harness looks.
Two layers, and why it matters
Capability in a Syntropic137 workspace comes from two places. Knowing which is which saves confusion later.
| Platform layer | Workflow layer | |
|---|---|---|
| What | Hooks, commands, guardrails | Skills |
| Where it lives | Baked into the workspace image at /opt/agentic/plugins/ | Declared in your workflow YAML |
| Who owns it | Us, as we build the platform | You, the workflow author |
| Changes when | The image is rebuilt | You edit a workflow |
| Harness | Claude-specific (--plugin-dir) | Any harness |
The platform layer carries things skills cannot express. A hook fires on an event; a skill is instructions an agent reads. Observability capture and dangerous-command guardrails are hooks, so they ship with the image and version with the harness they steer. You do not declare them, and you cannot break them from a workflow.
Everything on this page is the workflow layer.
If a capability ever needs to change without an image rebuild, that is a signal it is not platform-level and belongs here as an ordinary workflow skill.
Declaring skills
skills: is accepted at workflow scope and at phase scope. Workflow scope
applies to every phase; phase scope is additive on top of it.
id: review-workflow
name: Review Workflow
type: review
skills:
- ./skills/repo-conventions # bundled in this plugin
- anthropics/skills/frontend-design@v1.2.0 # pinned external ref
phases:
- id: review
name: Review
order: 1
skills:
- acme/skills/tdd-workflow@v2.0.0 # only this phaseWhen the same skill is declared at both scopes, the phase-scope entry wins. There is no silent last-one-wins across different versions: two conflicting versions of the same skill name abort the run rather than picking one.
The three reference forms
Bundled - a path inside your plugin, relative to its root:
skills:
- ./skills/repo-conventionsShorthand - org/repo/skill-name@version. The third segment names the skill
folder inside the repository, which matters because a skills repo usually
publishes several:
skills:
- anthropics/skills/frontend-design@v1.2.0Verbose - for full URLs, several skills from one source, or a version containing a slash:
skills:
- source: github.com/acme/agent-skills
version: feature/new-review-flow
names: [code-review, tdd-workflow]Pinning is required
@latest is rejected. This is not style enforcement.
Registration is content-addressed: a skill is stored under the sha256 of its file tree, and that hash is the cache. A reference that resolves to an already-registered hash does no network work at all, which is what makes repeated installs fast. An unpinned reference cannot be cached honestly, because the same reference may mean different bytes tomorrow.
So pinning is what makes the cache sound, not merely what makes runs reproducible.
How bundled skills get a version
An external reference carries its own version. A bundled path does not, so the CLI pins it by the sha256 of its file tree and rewrites the reference before uploading.
The practical consequence: editing a bundled skill produces a new registration. That is exactly true, and it is deliberate. A fixed label would mean an edited skill silently resolved to the previously stored copy, which is the failure this design exists to avoid.
Installing
syn workflow install registers every declared skill before creating any
workflow:
syn workflow install ./my-pluginResolving 1 skill(s)...
registered skill repo-conventions@sha256-87701f89...
[1/1] Creating Skills Demo... done (id: skills-demo)Run it again and the hash is already stored, so nothing uploads:
skill repo-conventions@sha256-87701f89... already registeredA bad or unreachable reference fails the install, and no workflow is created. That is the point of doing this at install time: a typo surfaces before you have committed to a run, not in the middle of one.
Installing a package whose workflow id already exists currently fails with a concurrency error. See #822. Skills are unaffected: the preflight reports them as already registered first.
Inspecting what is registered
syn skill list # everything registered
syn skill show repo-conventions # every version of one name
syn skill add ./path/to/skill # register without a workflow installlist and show take --json for scripting and agent consumption.
show returns every registration sharing a name, because a name is not unique:
the same skill can be pinned at several versions, and two sources can publish the
same name. Seeing all of them is how you tell which pin a workflow resolves to.
What happens at run time
- The phase's skills are resolved against the lock: workflow scope merged with
phase scope, keyed by
(source_url, version, skill_name). - Each resolved tree is materialized into the workspace at
/workspace/.syn-skills/<skill-name>/. - During setup, the pinned
skillsCLI installs each one for that phase's harness, offline, from the local path.
Step 3 is the one that matters. Files under .syn-skills/ are staged, not
installed. You can confirm the difference inside a running workspace:
docker exec <workspace> skills list --agent codexProject Skills
repo-conventions ./.agents/skills/repo-conventions Agents: CodexA phase that declares skills but cannot install them fails rather than running without them. Silently proceeding would produce a plausible-looking run against the wrong capabilities, which is worse than a clear failure.
Storage
Skill storage is content-addressed and grows monotonically. Eviction is deliberately not implemented, so size is reported rather than assumed small:
curl "$SYN_API_URL/skills/storage"{"object_count": 2, "total_bytes": 302, "skill_count": 1, "truncated": false}skill_count counts distinct trees, not registrations, so two names over
identical content correctly report as one stored tree.
Reference
- ADR-065 - the decision record and its rationale
- #772 - the specification
- Claude Plugins - the older, Claude-only workflow-declared mechanism that skills replace
Syntropic137 Docs v0.33.1 · Last updated March 2026
How a Phase Reports Its Outcome
The TASK_RESULT block every phase ends with, what success, failure_reason and side_effects mean, how they become phase and execution status, and the one-artifact-per-phase rule
Claude Plugins in Workflows
Declare Claude Code plugins per workflow and per phase so the agent inside each workspace sees exactly the skills it needs.