Writing skills
A skill is the thing you would otherwise explain to a new colleague, and then explain again.
What a skill is
A markdown document, optionally with extra files. Attach it to an agent and the daemon writes it into the work directory before the CLI starts, at the path that CLI already looks in:
| Provider | Path |
|---|---|
| Claude Code | .claude/skills/<slug>/SKILL.md |
| Elyra | .elyra/skills/<slug>/SKILL.md |
The CLI discovers it on its own. There is no prompt template, no token budget and no injection step at run time.
A skill is not code, and not a prompt. It is documentation with a specific reader.
Writing one
Admin → Skills → New skill. The name becomes the folder name — Deploy Checklist becomes
deploy-checklist — so two skills cannot share one, even with different display names.
Write it the way you would write onboarding notes:
## Deploying
1. Run the full test suite. Do not deploy on a red suite, even if the failure looks unrelated.
2. Run migrations before restarting workers, never after.
3. Watch the log for 60 seconds. A boot failure shows up immediately.
## Rolling back
`php artisan migrate:rollback --step=1`, then redeploy the previous tag.
Rolling back a migration that dropped a column does not bring the data back.
Good skills are specific, ordered, and say what not to do. Vague ones ("write clean code") cost tokens and change nothing.
Extra files
Attach examples, templates or reference material with a relative path like
examples/rollback.md. They are written beside SKILL.md, keeping their structure.
Paths are validated on both sides — the server when you save, the daemon before it writes.
Absolute paths, .. and hidden segments are rejected: these files land on a real filesystem on
someone else's machine.
Attaching
Tick the agents that should have it. A skill can be attached but switched off, which keeps the pairing while silencing it — useful when narrowing down whether a skill is helping.
Only enabled skills travel in the task envelope. An agent with no skills gets an empty list.
Verifying that it worked
The run's output shows the agent reading the file, since it appears in the work directory before anything starts. If it does not:
- Check the skill is attached and enabled for that agent
- Check the run started after you attached it — envelopes are built at claim time
- Check the daemon log: it reports
skills=Nwhen starting a task - Check the skill's own word on the list. A skill showing never read was attached but has not gone out with a run, which usually means the run predated the attachment
Whether it still earns its place
Compounding has an honest second half. A skill written in March against a framework abandoned in June is still read into every run: it costs tokens, it steers behaviour, and nothing measured whether it still helps. Something that only grows is not a skill base, it is an attic.
Each skill now carries a word about itself:
| (nothing) | In use — read by a run recently |
unattached |
No agent has it. It costs nothing and does nothing |
never read |
Attached, but no run has been given it |
stale |
No run has been given it in ninety days |
It is a fact, not a recommendation. "Nothing has read this in ninety days" is something Félagi can see. Whether that means delete it is a judgement about your codebase, and Félagi knows nothing about your codebase — a skill for a release process used twice a year is supposed to look stale.
What it can and cannot tell you
Which skills a run received is recorded when the work leaves for the runtime, because that is the only moment it is a fact. What an agent has attached today cannot explain last month's runs, and inferring one from the other would answer the question using the very thing under review — the attachments you are deciding whether to change.
That means the measurement starts when this feature arrived. Runs from before are not guessed at, and the page says which date it is counting from.
Where a skill has been given to some runs and withheld from others, and both groups are large enough, the failure rate of each is shown side by side. Where it has been given to every run there is nothing to compare it against, and nothing is shown — a rate on its own invites you to compare it with a number you have in your head, which is worse than no number at all.
Skills you already wrote without noticing
Compounding needs somebody to remember to write the skill down, and the moment they are most likely to know a lesson is the moment they are least likely to file it: a run has just failed and they are explaining why in a comment.
felagi:skills runs nightly and looks for that shape, with its own proof attached:
- A run does not land — it failed, or finished and had to be run again.
- A person says something on the issue.
- A later run by the same agent succeeds.
Bragi · ACM-21 · first run failed The suite needs
--runor vitest sits in watch mode waiting for input and the sandbox kills it on the timeout.
That comment is a skill somebody already wrote and never filed. It is in English, it is specific to this repository, and it demonstrably changed the outcome.
Nothing is generated. A proposal assembled from a model's reading of a transcript would be a guess about a lesson; this is the lesson, and the offer is to file it rather than to write it. Which also means the feature needs no model, no key and no daemon change — it is a query.
What it refuses to call a lesson
| A single run | Without a second attempt there is no "then it worked" to point at |
| A comment after the successful run | The order is the argument. That is a reaction, not a correction |
| An agent's own comment | An agent narrating its run is not somebody teaching it something |
| Under 40 characters | "Thanks", "try again", "yes" |
| More than 72 hours later | Wide enough for somebody who looked the next morning; narrow enough that an unrelated remark a fortnight on is not mistaken for a fix |
| A different agent's success | Not evidence that this one learned anything |
Filing one
A proposal is appended to a skill you choose, with a line saying which issue and when — never overwritten. A skill is a document somebody wrote, and replacing it would make accepting a proposal a destructive act nobody would risk twice. It goes in as plain text, because skills reach a CLI verbatim and the comment box's markup would arrive as content.
Not a lesson records a dismissal rather than deleting the row: discovery is idempotent on the comment, so a dismissal that left no trace would be the same suggestion again tomorrow morning.
Proposals sit above the skills on the page, because a proposal expires in usefulness — the person who remembers the conversation is the one who can judge it, and that memory has a half-life.
What this replaces
Without skills, the same knowledge has three bad homes: pasted into each prompt (forgotten half the time), baked into agent instructions (duplicated across agents), or nowhere at all (relearned by every run).
A skill is written once, versioned in one place, and applies to every agent that carries it. That is the whole idea behind compounding: the team gets better at something permanently, rather than one conversation at a time.