Skip to content

Skill Anatomy and Contracts

Why does a skill need a contract?

A skill becomes truly reusable when we know more than “it has a good prompt”. We should also know:

  • what it is for,
  • what input it expects,
  • what output it returns,
  • which tools or data it depends on,
  • what constraints apply,
  • what counts as success or failure.

This is very similar to designing a normal software component or API.

caller
  ↓
Skill Contract
  ↓
implementation
  ↓
model / tools / retrieval / code

The caller can be an agent, workflow, or direct application service.

Main parts of a skill

A useful mental model:

Skill
├── identity / metadata
├── purpose / responsibility
├── input contract
├── instructions
├── knowledge / context policy
├── tool dependencies
├── output contract
├── constraints / permissions
├── success / failure semantics
└── evaluation criteria

Not every platform represents a skill exactly this way, but these are useful separate design questions.

1. Identity and metadata

At minimum, make these clear:

name
version
description

For example:

name: review_pull_request
version: 1.2
description: Identify material correctness, architecture and regression risks in a pull request.

The description may be more than documentation. During skill discovery/routing, a runtime can use it to decide which skill is relevant.

Weak description:

Helps with GitHub things.

Better:

Reviews a pull request for correctness, architecture risks, regressions and missing tests. Read-only.

2. Purpose and responsibility

A skill should have one clear job.

For example:

Given a pull request and repository context, identify material risks and return structured review findings.

This defines the boundary.

The skill does not:

  • merge the PR,
  • automatically fix the code,
  • deploy,
  • write release notes.

Those can be separate skills or workflow steps.

A clear responsibility matters because an LLM may otherwise continue “helpfully” beyond the requested task.

3. Input contract

The caller should know what data must be supplied.

Example logical schema:

{
  "repository": "org/payment-service",
  "pull_request_number": 184,
  "review_focus": ["correctness", "architecture", "tests"]
}

The input contract can define:

  • required fields,
  • optional fields,
  • types,
  • enums,
  • limitations,
  • defaults.

For example:

pull_request_number: positive integer
review_focus: subset of allowed review dimensions
repository: validated repository identifier

The input contract helps not only the LLM. The runtime can reject invalid requests before the model call.

invalid input
    ↓
application validation
    ↓
reject

instead of:

invalid input
    ↓
LLM tries to guess what user meant

4. Instructions

Instructions define the semantic behavior of the skill.

For example:

Review only material issues.
Prioritize correctness over style.
For every finding, cite concrete evidence from the diff or retrieved file.
Do not invent repository state that was not supplied or retrieved.

This is different from the input.

instructions = how to work
input        = what to work on

Stable instructions can be a versioned part of the skill.

A task-specific request is runtime input:

Focus especially on backwards compatibility in the REST API.

5. Knowledge and context policy

The skill contract does not necessarily contain all knowledge itself. Instead, it can define what knowledge is needed and where it should come from.

For example:

Static knowledge:
- internal review principles
- severity definitions

Runtime context:
- current PR diff
- relevant source files
- tests
- repository-specific AGENTS.md

An important distinction:

The skill contract may define what context is required; the runtime can be responsible for acquiring it.

Example:

Skill requires: current PR diff
        ↓
Runtime resolves dependency
        ↓
GitHub connector / API

6. Tool dependencies

A skill may need to declare which capabilities it depends on.

For example:

required capabilities:
- read_pull_request_diff
- read_repository_file

optional capabilities:
- read_test_results

It is usually better to depend on task-level capabilities than on concrete providers.

More coupled:

requires GitHubToolV3.fetch_file()

Better abstraction:

requires repository_file_reader

The runtime decides whether that capability is implemented by a GitHub connector, local checkout, or another backend.

From a software-architecture perspective, this is essentially ports and adapters thinking.

7. Output contract

If an application consumes the skill result, avoid relying only on free text.

For example:

{
  "findings": [
    {
      "severity": "HIGH",
      "location": "src/payment/RetryService.java:87",
      "summary": "Retry can duplicate a non-idempotent payment request",
      "evidence": "The retry path calls charge() without an idempotency key."
    }
  ],
  "overall_risk": "HIGH",
  "summary": "The PR introduces a duplicate-payment risk."
}

The application can then work with:

result.findings
result.overall_risk

As in the Foundations material:

A schema can guarantee structure, not semantic truth.

The conclusion can still be wrong even if the JSON is valid.

8. Constraints and permissions

The skill contract should describe allowed behavior.

Example:

mode: read-only
allowed repositories: current repository only
allowed tools:
  - diff reader
  - file reader
forbidden actions:
  - modify code
  - merge PR
  - post comment

But the important architectural rule is:

Do not enforce permissions only through prompts/instructions.

The runtime should actually expose only allowed tools or perform authorization before tool execution.

Skill contract says read-only
          +
Runtime exposes only read tools

Together these are stronger than merely saying:

"Please do not modify anything."

9. Success and failure semantics

A reusable capability should define possible outcomes.

Not only:

success / exception

but perhaps:

SUCCESS
INSUFFICIENT_CONTEXT
TOOL_UNAVAILABLE
NOT_AUTHORIZED
INVALID_INPUT
PARTIAL_RESULT

For a deployment-health skill:

{
  "status": "INSUFFICIENT_CONTEXT",
  "missing": ["production deployment state"]
}

is better than making the model guess the missing data.

Explicit failure semantics can directly reduce hallucination pressure.

10. Evaluation criteria

If a skill is a separate artifact, it should be independently evaluable.

For a PR review skill:

- does it find real critical bugs?
- how many false positives does it produce?
- is every finding grounded in evidence?
- does it use tools correctly?
- does it respect the read-only boundary?
- what are latency and cost?

Evaluation is a later topic, but it is useful to know what “good” means while designing the contract.

Contract vs implementation

This is one of the most important separations.

Skill Contract
├── purpose
├── inputs
├── outputs
├── constraints
└── required capabilities

Implementation A
├── model X
├── prompt v4
└── GitHub tools

Implementation B
├── model Y
├── prompt v9
└── local repository tools

From the caller's perspective, both can implement the same capability.

This creates loose coupling.

Skill contract vs application configuration

Not everything belongs in the skill.

Typically part of the skill contract

  • capability name and purpose,
  • semantic instructions,
  • required input,
  • output schema,
  • required tool capability,
  • permission expectations,
  • success/failure semantics.

Typically runtime/application configuration

  • concrete model provider,
  • API endpoint,
  • secrets,
  • production timeout,
  • tenant-specific authorization,
  • concrete GitHub token,
  • rate limit,
  • feature flag,
  • deployment environment.

Do not put something like this in a skill:

GitHub token = ghp_...

or:

Production DB hostname = ...

Instead, the skill can state:

requires repository_read capability

and the runtime injects the real credentials and adapter.

Example: Incident Triage Skill

A conceptual contract:

name: incident_triage
version: 2
purpose: Classify an incident and identify the most relevant next diagnostic action.

input:
  incident_text: string
  service: string
  environment: enum[dev, staging, production]

required_capabilities:
  - observability_query

output:
  severity: enum[SEV1, SEV2, SEV3, SEV4]
  suspected_area: string
  evidence: list[string]
  next_action: string
  confidence: number

constraints:
  read_only: true
  do_not_restart_services: true

The runtime may provide it with:

Datadog adapter

and later:

Grafana adapter

without changing the meaning of the triage capability.

Contract evolution

If an output contract changes from:

v1:
severity
summary

to:

v2:
severity
summary
evidence[]
recommended_action

it may be a breaking change for callers.

Skill contracts should therefore be treated like other API contracts:

  • versioning,
  • compatibility,
  • migration,
  • regression testing.

Anti-pattern: implicit contract hidden in the prompt

Weak:

"Review this and tell me if anything looks bad."

The caller does not know:

  • what review dimensions are used,
  • what input is required,
  • what output it receives,
  • which tools may be used,
  • what happens if there is insufficient data.

That may be fine for a POC. It is not enough for a reusable system capability.

Anti-pattern: domain model as the direct LLM interface

It is not necessarily a good idea to expose a complex internal domain object directly as the model-output contract.

For example:

LLM JSON
  ↓
SkillResult DTO
  ↓
validation / mapping
  ↓
Domain object

is usually cleaner than:

LLM
 ↓
directly constructs Payment domain aggregate

The LLM-facing contract can be simpler and provider-independent, with deterministic application mapping into the domain model.

Takeaways

  • A reusable skill benefits from an explicit contract.
  • Main contract elements: purpose, input, instructions, context policy, tools, output, constraints, failure semantics, and evaluation.
  • Skill interface ≠ skill implementation.
  • Concrete model, credentials, provider, and runtime timeout are usually application configuration rather than skill knowledge.
  • Prefer expressing tool dependencies as capabilities rather than coupling early to a provider.
  • Structured output gives a formal contract, but semantic correctness still needs evaluation.
  • Failure outcomes should be explicit; “insufficient data” is a legitimate result.
  • A skill contract may need the same versioning and maintenance discipline as an API contract.