Skip to content

How skills work

A skill is how you give the agent a rule it actually reads at the moment it matters — your code-review checklist, your “never force-push main” discipline — instead of burying it in a system prompt it agreed to and then ignored two turns later. The gateway hands the agent the rule when it’s about to act, not at the top of a context window that’s since filled up.

The conceptual story lives in Skills, workflows, and cognitive architectures — what a skill IS, why the three-layer model matters, when you’d reach for one. This page is the operational rundown: where skills live, how they load, how the gateway surfaces them, and how an in-flight workflow keeps seeing a stable copy.

A skill is four fields under a subject key:

skills:
review.code.adversarial:
verb: review
lifecycle: stable
body: |
# Adversarial patch review
Attack the candidate patch: missed edge cases, hidden regressions,
policy violations, security issues. Cite each finding with
file:line and a counterexample. Return verdict: accept | retry | escalate.
FieldRequiredWhat it does
verbyesOne of the 10 cognitive verbs (reference). Closed enum.
lifecycleyesexperimental | stable | deprecated. Governs the deprecation conventions you ship under.
bodyyesThe Markdown guidance the model reads. Aim for 100–400 words; one mental model per skill.
sourcenoWhere the skill came from (config, ingest, repo provenance). Auto-set by ingest paths; manual entries default to config.
hashnoIf present, must equal sha256(normalize_for_hash(body)). Hash drift is HASH_MISMATCH at load.

You name a skill by giving it a verb and a dotted subject like review.code.adversarial — what it does (review) and what it’s about (code.adversarial). That’s the whole mental model; the rest is validation that keeps names consistent across a growing library.

The subject is the key — review.code.adversarial above. At least two segments, lowercase kebab. The first segment must be one of a fixed set of subject roots; under strict namespacing (default), an unrecognized root fails load with INVALID_SUBJECT_ROOT. See Cognitive verbs for the full root list and the reasoning behind the closed set.

Two places, and they compose.

gateway.yaml
version: "1.0.0"
skills:
review.code.adversarial:
verb: review
lifecycle: stable
body: |
# Adversarial patch review ...

This is the simplest path. One file, no repo manifest, you ship and own the skill yourself. Good for skills that are highly specific to your codebase or your team’s vocabulary.

A resource repo with a flowgate.repo.yaml manifest can ship a skills/ directory whose *.yaml files load into the gateway’s skills registry. Each loaded subject is namespace-prefixed <repo-namespace>/<subject>:

repos/cognitive-architectures/flowgate.repo.yaml
schema: flowgate.repo/v1
name: cognitive-architectures
namespace: cognitive
version: 0.6.0
layout:
skills: skills # ← skills/*.yaml load from here
repos/cognitive-architectures/skills/review.code.adversarial.yaml
skills:
review.code.adversarial:
verb: review
lifecycle: stable
body: |
# Adversarial patch review ...

When the operator wires this repo into their gateway config…

gateway.yaml
version: "1.0.0"
repos:
- path: ~/repos/cognitive-architectures

…the skill lands in the registry under cognitive/review.code.adversarial. Two repos can ship skills with the same bare subject without colliding — the namespace prefix disambiguates.

See Multi-repo loading for the full repo loading semantics. Both inline and repo-loaded skills compose into the same registry; you can have one repo provide review.code.adversarial and another provide review.code.style-only and your gateway sees both.

A workflow names the skills it wants surfaced:

workflows:
review-pipeline:
initialState: critiquing
skills: [review.code.adversarial, review.code.final-approval]
states:
critiquing:
skills: [review.code.adversarial] # state-scoped surfacing
transitions: { ... }

skills: can sit at three scopes:

ScopeSurfaces toWhen you’d use it
Workflow-levelEvery state in the workflow”This whole workflow is review work; every state benefits from the rubric.”
State-levelJust that state”The critiquing state needs the adversarial rubric; the next state doesn’t.”
Transition-levelJust when that transition is the current decision”Only when the model is about to fire escalate, surface the escalation rubric.”

The model gets the subjects (the menu), not the bodies (the meal). When it decides a particular skill is relevant, it calls flowgate.query({subject}) to fetch the body — it pulls one rule at a time, only when it’s about to use it. That’s what keeps the agent from carrying a wall of guidance it skims and forgets.

Cross-namespace references from repo-loaded workflows

Section titled “Cross-namespace references from repo-loaded workflows”

A workflow loaded from a repo references skills by bare subject. The runtime resolves them against:

  1. The workflow’s own namespace prefix first — cognitive/review.code.adversarial.
  2. Then the bare subject — review.code.adversarial.

So a workflow shipped by cognitive-architectures writes skills: [review.code.adversarial] and lands the namespaced version automatically. To reference a skill from a different namespace, fully qualify it: skills: [internal/review.compliance.strict].

The lookup is the same fall-through pattern Multi-repo loading describes for kind: workflow references.

The two MCP tools are how the model discovers and reads skills:

The search index includes every loaded skill alongside capabilities and workflows. Each skill contributes its subject, verb, source, and body to the index. A query like "adversarial review" ranks review.code.adversarial highly because the term hits the subject + verb + body together.

The model can filter by kind (flowgate.query({ query: "review", kind: "skill" })). Closed-enum verbs make the filter deterministic — no fuzzy matching across “review” vs “evaluate” vs “critique.”

flowgate.query with subject returns the body

Section titled “flowgate.query with subject returns the body”
{
"name": "flowgate.query",
"arguments": { "subject": "cognitive/review.code.adversarial" }
}

returns the verb, body, hash, and source. The model reads the body, applies the guidance, and acts. The describe call is what the workflow’s skills: ref anticipates the model making — you list the subjects the workflow expects the model to need; the model fetches each body once when it actually decides it needs it.

Edit a skill mid-run and the in-flight workflow keeps the old version

Section titled “Edit a skill mid-run and the in-flight workflow keeps the old version”

Edit a skill while a workflow is running, and the in-flight instance keeps the version it started with — no half-applied rule changes, no two states in the same run grading against different rubrics. Fresh instances pick up your edit; the ones already moving finish on the rules they began with.

The mechanism: when a workflow instance is created, the runtime freezes a copy of every skill the workflow references — body, hash, and verb — onto that instance. It carries that copy for its whole lifetime; later get and submit calls read from the frozen copy, never from the live skills: config.

What that buys you:

  • Mid-flight edits don’t leak. Hot-reload the config with an updated review.code.adversarial body, and in-flight review pipelines keep the original until they terminate. No flicker, no half-evaluated workflows.
  • Hot-reload safety. A SIGHUP config reload (config.reloaded audit event) swaps the live registry atomically. Instances mid-transition are unaffected.
  • Reproducibility. Replay a workflow’s audit log a year later and the frozen copy tells you exactly what guidance the model was looking at — even if the skill body in your repo has changed since.

If a skill ships with an explicit hash: field, the runtime computes sha256(normalize_for_hash(body)) at load and refuses to start if they don’t match. Useful when:

  • You vendor a skill from another repo and want to assert it hasn’t drifted (you ship the body + a hash you generated; the load fails loudly if either changes without the other).
  • A skill body is being edited collaboratively and you want CI to catch desync between the body and a manifest that references its hash.

The normalization (whitespace collapse, trailing-newline trim) is stable across releases. Read-side and write-side parity is enforced by cross-impl test.

A capability or transition can require the model to have fetched a skill body before firing:

states:
about_to_review:
transitions:
submit_verdict:
target: done
guards:
- kind: guidance_acknowledged
subject: review.code.adversarial

This passes only if the model called flowgate.query({subject: "review.code.adversarial", workflowId: "..."}) on this workflow AND the recorded body hash matches the current snapshot’s hash. Hash flip → acknowledgment expires → the model has to re-read.

Use this when the guidance is load-bearing — when you want the runtime to enforce “you read the rubric before you submitted the verdict.” Don’t sprinkle it on every transition; it adds friction. Reach for it on the transitions where skipping the rubric would cost something real.

How airtight that enforcement is depends on how you run flowgate — inside Claude Code or Cursor it gates everything routed through it; run the loop in flowgate’s own TUI and the guard is absolute. How to run it lays out the modes.

Some practical conventions that emerge from real use:

  • One mental model per skill. If you find yourself writing # Section 1, # Section 2 headings, split. The model loads skills one at a time; each one should be the smallest coherent thing it could think about.
  • 100–400 words. Long enough to encode actual discipline; short enough that the body cost isn’t a tax on every snapshot-stamped instance.
  • Cite the trap. Skill bodies are at their best when they name a specific failure mode. “The temptation here is to…” or “Anti-pattern:” lines do more for model behavior than abstract principles.
  • Bounded scope. review.code.adversarial is good. review.code.everything-you-need-to-know is too broad — split into the actual sub-disciplines.
  • Lifecycle honestly. Mark experimental skills experimental — operators see this in flowgate.query({subject}) describe responses and treat them accordingly. Promoting to stable is a commitment.