Pattern · Procedure

Skills as Procedural Memory

Remember what worked as a reusable procedure rather than as a fact, and gate the write on evidence that it actually worked.

Intent

Store an agent's competence, not just its knowledge. When the agent works out how to do something, keep the procedure itself — a script, a function, a checklist — so the next attempt starts from a working solution rather than from a description of one.

The problem

Most agent memory remembers propositions: the user prefers dark mode, the deploy failed at step three, the schema has a tenant_id column. Propositional memory answers "what is true?"

It answers "how do I do this?" badly. An agent that solved a fiddly multi-step task last week can retrieve a summary of having solved it and still have to rediscover every detail. Worse, a summary of a procedure is exactly the kind of memory that looks right and is subtly wrong — the steps are there, the ordering constraint that made it work is not.

Procedures also have a property propositions lack, and it is the whole opportunity: you can run them. "Is this fact true?" requires judgment. "Does this procedure work?" can be answered by executing it and checking the result.

The pattern

Store the executable artifact. Index it by a generated description of what it does. Gate the write on verified execution.

task -> agent produces a procedure (code, script, sequence)
     -> execute
     -> verify against observable outcome, not model self-report
     -> on success: generate a natural-language description
                    embed the DESCRIPTION, store the PROCEDURE
     -> on failure: feed the failure back as reasoning input; write nothing
retrieval: embed the task -> search descriptions -> return procedures
           -> earlier procedures become primitives for new ones
Diagram — nothing enters the skill library until an outcome verifies it; a failed attempt is critiqued and retried, and the store stays empty
Diagram source
%% caption: nothing enters the skill library until an outcome verifies it; a failed attempt is critiqued and retried, and the store stays empty
flowchart TD
    T["Task"] --> Gen["Agent writes procedure"]
    Lib["Skill library"] --> Gen
    Gen --> Exec["Execute"]
    Exec --> Ver{"Verified by outcome?"}
    Ver -- "no" --> Crit["Critique -> retry (nothing stored)"]
    Crit --> Gen
    Ver -- "yes" --> Desc["Generate description"]
    Desc --> Lib

Three invariants make it work:

  1. The retrieval key is not the payload. Embed a description of intent; return the artifact. Source code embeds poorly.
  2. Verification precedes storage. The gate is an observed outcome, not the model's confidence.
  3. Failures inform, they do not persist — at least not in the same store, and not as callable procedures.

Why it works

It converts memory from recall into capability. A propositional store gets larger as the agent works; a skill library gets more capable, because retrieved procedures become building blocks for new ones.

And it sidesteps the hardest problem in the rest of this atlas. Every pattern here about trust — rejected-value tombstones, trust-state machines, evidence before belief — exists because propositional truth is expensive to establish. Procedural truth is cheap to establish where actions have observable effects: run it and look.

Tradeoffs

  • Verification is only as good as the check. Succeeding once in one state is not evidence of generality, and a skill stored after a single lucky execution is a false memory with a confident-looking provenance.
  • Executing stored artifacts is a trust boundary. A skill library is durable, agent-authored, retrieved-by-similarity code. Outside a sandbox, in any setting where inputs are not trusted, this is an obvious attack surface.
  • Libraries need pruning, and nobody has a good policy. Without a utility signal — reuse count, success rate on reuse, composition depth — the library accumulates near-duplicate one-offs.
  • Retrieving the wrong procedure is worse than retrieving the wrong fact. An irrelevant fact is noise the model can ignore; an irrelevant callable invites the model to call it. Score thresholds matter more here than elsewhere.
  • Supersession needs lineage. Replacing a skill with a better version is a correction, and correction without a lineage record leaves you unable to answer why behaviour changed.
  • Not every domain has a checkable outcome. The pattern degrades toward ordinary memory wherever success is a matter of judgment.

Do not use this to store prose "procedures" that nothing executes. Without the verification gate, this is just tagged notes, and it inherits none of the property that makes it worth doing.

Cost to adopt

Build: a store for runnable procedures, a description index to retrieve them by, and an execution gate that only writes on verified success.

Forces elsewhere: you need a sandbox and a real success signal. Without one, this degrades into storing plausible-looking code that nothing has ever run — which is worse than not storing it, because it looks authoritative.

Ongoing: skills go stale as the environment changes, and nothing invalidates them automatically. Progressive disclosure keeps their context cost bounded but adds a loading mechanism.

Skip it if you have no execution environment to verify against.

Seen in the atlas

Voyager is the clearest implementation. Its SkillManager stores JavaScript functions written by the agent, indexed by an LLM-generated description; the write happens only inside if info["success"]:, where success comes from a critic that inspects environment state. Retrieved skills are injected as callable functions, so the library composes. It also shows the failure modes: unbounded concatenation of every skill into the prompt, retrieval with no score threshold, versions written to disk with no lineage, no failure memory, and one hardcoded exclusion standing in for a memory-worthiness policy.

Hermes Agent applies the same idea without the gate. Skills are Markdown files the agent creates and edits through skill_manage, with provenance and usage tracked separately; only names and descriptions occupy prompt space and bodies load on demand — a better context strategy than Voyager's — but nothing verifies a skill before it becomes durable.

Atomic Agent takes the opposite position from Voyager and states it as a rule: its procedures are derived alongside a parent lesson from the same consolidator cluster, and cross-phase invariant 20 holds that the runtime never auto-executes them — they are "advisory text the agent reads and either follows or consciously deviates from." Voyager buys empirical verification by making skills executable and accepts the trust boundary that follows; Atomic Agent gives up verification to avoid it. Both are defensible, and the choice is the pattern's central tradeoff.

ScienceClaw, an OpenClaw derivative, demonstrates the scale end: it ships 285 skills against OpenClaw's ~54, and its README states that "the agent writes new SKILL.md files at runtime without any redeployment." Runtime skill authoring at that volume is what the pattern looks like when the library is the product — and it sharpens the unanswered pruning question, because nothing in the atlas has a utility signal that would work across 285 entries.

MemOS mounts skill memory as one cube type among several, and agentmemory keeps procedural records alongside semantic ones; in both, procedure is one kind in a broader taxonomy rather than a design centre, and neither gates on execution.

OpenViking unifies memory, resources, and skills in one filesystem hierarchy, which is the most integrated treatment in the atlas — skills are retrievable through the same tiered mechanism as everything else.

Verel approaches the same territory from the opposite direction: it clusters failures into induced candidate rules and requires promotion gates before they become trusted. Read together with Voyager, the two halves of the missing system are visible — Voyager verifies successes and discards failures; Verel mines failures and gates promotion.

SESA closes the pruning question this page has been holding open, and shows what it costs. Its skill bank writes a card only from a failure — the exact inversion of Voyager's gate — and then measures every card it hands out: retrieved_count, helpful_count and hurt_count, all three written by the same rollout reward that trains the model. A card whose net score has gone negative after at least three retrievals is deleted, not demoted. That is the utility signal the ScienceClaw entry above says nothing here has, and the reason it works is a precondition worth stating plainly: SESA is a training loop, so the outcome is a scored answer rather than a guess about whether the user was pleased. A library without that signal cannot borrow the mechanism, only the wish for it.

Two failures sit beside it and both generalise. The eviction leaves no record_evict_negatives drops the row, and _add_new_with_dedup compares new cards only against the live bank, so a similar failure regenerates the card the system just measured as harmful, back at score zero and needing three more losses to leave again. And credit is assigned uniformly: all three retrieved cards receive the outcome of one rollout, so a harmful card is rewarded whenever it rides along with two good ones. The similarity score that would support weighted attribution is computed on every retrieval and attached to each card, and no caller reads it.

Neo4j Agent Memory supplies the half this pattern's gate leaves out. Its reasoning tier records traces through a context manager, so on a raised exception the error becomes the trace's outcome — meaning failures are stored by default, where a verified-execution gate by construction stores only successes. An agent that has failed the same approach four times has learned nothing if nothing wrote the failures down. Outcomes carry an indexable error kind, and a retrieved step arrives with its parent trace's outcome attached, so a step from a failed attempt cannot be read as precedent.

Ollama's built-in agent was the smallest complete instance, and it treated loading a skill as a privileged act — until the agent was removed from Ollama in September 2026. Its whole procedural memory was four files: discovery across four roots, a 1 MiB ceiling and front-matter validation, an <available_skills> block of names and descriptions in the prompt, and a skill tool that loads a body by exact name. Two decisions transfer.

agent/tools/skill.go returns an unconditional true from RequiresApproval, with the reason in the comment — "Model-initiated loads require approval because a skill's instructions can influence the rest of the run" — while explicit user activation goes through a synthetic tool call that bypasses the gate. Voyager and Hermes gate the write; this gates the read, which is the operation that actually changes the agent's behaviour. Its limit is that the approval prompt shows a name, so the person approving has probably not read what they are approving.

The second is the trust disclaimer, said twice to two audiences. The Go type comment reads "It never grants tool permissions; it is supplied to the model as ordinary tool-result content", and the system prompt tells the model the same thing: "Skills only provide instructions; use ordinary tools for filesystem or network access, with their normal approval rules." That costs one line and answers the question a retrieved instruction document always raises.

Against all of that, Ollama has no verification gate of any kind — no critic, no success signal, no usage tracking — because it also has no write path. A skill exists because a person wrote the file.

Forgetful stores skills as rows beside memories — kebab-case name, description, Markdown body up to 100 KB, licence, compatibility, allowed tools, tags and an importance — with import_skill and export_skill in the Agent Skills SKILL.md format and a second vector table, vec_skills, built from the description alone so a skill is found by what it is for rather than by its body. The feature is off by default (SKILLS_ENABLED=false), nothing executes a skill, and nothing records whether following one worked. The ten SKILL.md files in the repository's own skills/ directory are the other half of the pattern: they are the server's memory policy — query before create, confirm before update or obsolete, announce every save — shipped as procedures the agent loads, and the server has no way to know whether it did.

Hivemind (Activeloop) answers the pruning question this page leaves open without a training reward, and then throws the answer away. Its write gate is the weak kind — a model curator reading ten recent sessions, told to keep a pattern only if it recurs across three exchanges and is not already covered, with nothing executed. The interesting half is after the write. A PreToolUse on an org skill arms a three-message judgment window; the user's next prompt spawns a worker that rebuilds the transcript around the invocation, appends the reaction, and asks a judge one question phrased against sycophancy — "Ignore whether the user seemed happy or polite — a praised-but-wrong answer is a FAILURE" — with an unparseable or errored verdict returning success, so the judge can miss a failure and never invent one. That is a reuse-quality signal derived from ordinary human reaction rather than a scored rollout, and it is the piece a library outside a training loop can borrow. The edit it produces is bounded the way this page's supersession tradeoff asks for: at most three anchored operations, a protected region the fast pass may not touch, and a new version appended rather than an overwrite.

Two failures sit beside it. The new version is published to the whole organisation under a comment reading "No approval gate by design: detect → improve → publish, directly", and auto-pull writes every author's skills onto every signed-in machine at the next session start — so one person's bad turn rewrites a colleague's procedure before anyone has read the diff. And the loop never closes: its meta log declares proposed | applied | reverted, writes only proposed, and reads the field nowhere, so the dedup prevents re-proposing an identical edit and nothing distinguishes an improvement from a regression. Declaring the states and not writing them is how a library keeps editing in a direction no one has checked.

SkillCorpus is the pattern at the other end of the supply chain: not an agent writing skills, but a pipeline admitting other people's — ~821,000 crawled files filtered by structural thresholds, an LLM judge over utility, robustness and safety, and a cross-source near-duplicate merge. Its transferable move is where the decisions live. Both judges write their verdict keyed on the SHA-256 of the body they judged — quality_judgments(content_hash PRIMARY KEY) and dedup_judgments(pair_key) over a sorted hash pair — and the build re-derives every exclusion from those caches before it exports. A skill excluded for a cmd_injection flag is excluded again the next time the same body is crawled, by the same verdict, and the judge is never re-asked. That is the thing SESA's eviction above is missing stated as a schema decision rather than a wish: an exclusion keyed on the value survives regeneration, and one keyed on the row does not. This library shows both, because the row-level markers here are clobbered by a re-ingest and only the hash-keyed caches hold.

It also ships the test this page asks for. test_an_unrelated_query_gets_nothing_from_the_local_source builds twelve file-handling skills, asserts the weather query returns an empty block, and asserts in the next line that the same directory still answers "fill an acroform" — the third bullet below, with the control that stops it passing vacuously. What it has none of is a verification gate or a usage signal: no skill is executed, nothing observes whether following one worked, and the record type it was adapted from had counters and lineage fields that were deliberately dropped.

Tests to require

  • Store a skill, then execute the retrieved copy in a fresh context and assert it still succeeds — generality, not just recorded success.
  • Assert that a failed attempt produces no durable procedural memory.
  • Retrieve against an unrelated task and assert no skill is returned above threshold.
  • Supersede a skill and assert the prior version remains retrievable with a lineage link.
  • Compose: build a skill that calls a stored skill, then modify the callee and assert the caller's behaviour change is detected.
  • Assert the injected skill context stays inside a token budget as the library grows.
  • Execute a deliberately malicious stored procedure in the sandbox and assert containment.
  • Track reuse and assert that never-reused skills are surfaced for pruning.