How skills work

A skill is a rule the agent reads at the moment it acts — your review checklist, your 'never force-push main' discipline — not prompt text it skims and forgets. The operational rundown: where skills live, how they load, and how the gateway hands them over on demand.

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.

Anatomy

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.
Field Required What it does
verb yes One of the 10 cognitive verbs (reference). Closed enum.
lifecycle yes experimental | stable | deprecated. Governs the deprecation conventions you ship under.
body yes The Markdown guidance the model reads. Aim for 100–400 words; one mental model per skill.
source no Where the skill came from (config, ingest, repo provenance). Auto-set by ingest paths; manual entries default to config.
hash no If 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.

Where skills live

Two places, and they compose.

Inline in the gateway config

# 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.

From a repo’s skills/ directory

A resource repo with a praxec.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/praxec.repo.yaml
schema: praxec.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.

How a workflow surfaces them

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:

Scope Surfaces to When you’d use it
Workflow-level Every state in the workflow “This whole workflow is review work; every state benefits from the rubric.”
State-level Just that state “The critiquing state needs the adversarial rubric; the next state doesn’t.”
Transition-level Just 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 praxec.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

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.

How the model finds them

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

praxec.query with query searches them

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 (praxec.query({ query: "review", kind: "skill" })). Closed-enum verbs make the filter deterministic — no fuzzy matching across “review” vs “evaluate” vs “critique.”

praxec.query with subject returns the body

{
  "name": "praxec.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

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:

Hash drift detection

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:

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

The guidance_acknowledged guard pattern

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 praxec.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 praxec — inside Claude Code or Cursor it gates everything routed through it; run the loop in praxec’s own TUI and the guard is absolute. How to run it lays out the modes.

Authoring discipline

Some practical conventions that emerge from real use:

What’s next