Skill Engineering, Part 6: Appendices
This is Part 6 of a six-part series on Skill Engineering. This final part contains the reference material: the manifest schema, eval assertion semantics, a worked end-to-end example, the migration playbook, and the glossary.
Appendix A — Manifest Schema Reference
Chapter 16 introduced manifest.yaml as the machine-readable mirror of a skill’s frontmatter. This appendix documents the schema in full.
Full schema (YAML, abbreviated)
1 | $schema: "https://awesome-sap-skills.dev/schemas/manifest.v1.json" |
Key field notes
name — kebab-case, ASCII lowercase letters, digits, hyphens only, ≤ 40 characters, must equal the containing directory name. Rename requires a migration playbook (Appendix D) — never rename in place.
version — semver 2.0.0 (MAJOR.MINOR.PATCH); pre-release suffixes allowed (-alpha, -rc.1); + build metadata not used. Bump rules covered in Chapter 12.
layer — enforced by the resolver’s composition check: a skill of layer L may only depend on skills of the same or lower layer.
status — state transitions: experimental → stable → (deprecated or replaced) → archived. Reverse transitions require an evidence file.
auth.providers — each entry is a sigcli provider name. The resolver runs a pre-flight auth check before invoking the skill; a skill missing a required provider fails fast with a helpful error.
depends_on — keys must resolve to installed skills; values are semver ranges (>=1.4, ^2.0, >=1.4 <3.0).
deprecated_by — set only when status=replaced; must resolve to an installed skill; may not be self.
description — must match the SKILL.md frontmatter description byte-for-byte. The CI check enforces this.
Timestamps — managed by the forge-skill script (creation) and the record-history script (evolution). Do not hand-edit.
Worked example — a workflow skill
1 | name: diagnose-product-status |
Consistency check
The CI runs scripts/validate-manifest.py on every commit. Failures include: description mismatch, version behind the latest history entry, unresolvable depends_on references, status=replaced with null deprecated_by, maintainer not in owners, and layer composition violations.
Appendix B — Eval Assertion Reference
Chapter 9 introduced the three assertion types the reference framework supports. This appendix documents the semantics of each, common patterns, and the file format the eval runner consumes.
The eval file format
1 | version: 1 |

substring
Fires when the assertion’s value string appears literally in the trace. Case-sensitive by default; add case_insensitive: true to relax.
Use substring when the correct behaviour of the skill produces a distinctive literal phrase that a wrong behaviour would not produce: a specific tool call, a specific instruction being honoured, a specific error message being surfaced.
Avoid substring when the phrase you want to check for is common English that could appear by coincidence. Assert on the distinctive part, not the generic part.
regex
Fires when the assertion’s pattern matches somewhere in the trace (Python’s re.search). Backslashes must be doubled in YAML (\\d for \d).
Use regex when the correct behaviour produces a shape rather than a literal: a positive integer number of seconds, a Jira key of the form DC00-\d+, an ISO-8601 timestamp.
Common patterns:
1 | - type: regex |
Avoid regex when a substring assertion would do the job. Regexes are more expressive but harder to read.
absent_regex
Fires when the assertion’s pattern does not match anywhere in the trace. The assertion passes when the pattern is absent; it fails when the pattern is present.
Use absent_regex when you want to check that the skill does not do something it used to do or that a naive implementation might do:
- A prior version verified immediately without waiting:
absent_regex: "verifying.*immediately". - The skill should route to
bdc-team, notops-team:absent_regex: "routing to ops-team". - Credentials must never surface:
absent_regex: "Bearer \\w+".
The absent_regex file in a mature skill reads like a history lesson. Each entry corresponds to a mistake that was made once and must not be made again:
1 | - type: absent_regex |
Each line is a scar. The scars are the discipline made visible.
The gate
For a Harden-ready skill: at least three cases, at least one absent_regex per case, evals passing on the current agent, CI configured to run them.
Appendix C — Worked Example: diagnose-product-status
This appendix walks the skill that the book has used as its running example end-to-end: from the incident that motivated its creation, through the four forging phases, into its first evolution round.
The precipitating incident
A colleague pings: “customer’s data product has been stuck in activating for two hours, they’re escalating, what do you check first?” I know the general shape of the answer — the stage log, then the pipeline run, then the specific transform that is stalling — but I have not written the sequence down. I open the customer’s tenant in the DWC console, tail the Loki logs, cross-reference the pipeline run in Grafana, and forty minutes later the customer’s product is activating.
An hour later, another colleague asks the same question about a different customer.
The 3x rule has been satisfied. The workflow is repeatable, I have run it three times in a week, and each run is expensive enough that the cost of writing the skill is repaid in fewer than three future runs.
Phase 1 — Explore
I run the procedure a fourth time, this time as a deliberate exploration. Notes in explore.md:
1 | ## Attempt 1: check the stage log first |
The exploration produces six such pitfalls. Each becomes an absent_regex in the eval file.
Phase 2 — Crystallize
I read three sibling skills first: sap-loki, catalog-support-route, and an existing workflow whose shape I want to match. I write the skill’s frontmatter first, then the Overview, Constraints, Instructions, Self-Test, and Troubleshooting. Two hours of focused writing. The skill is at v0.1.0, experimental.
Phase 3 — Harden
Three eval cases derived from real incidents:
happy-path: straightforward activation failure caused by a missing source connection.timing-constraint: the most recent sync was under 30 seconds ago; the agent must wait.ambiguous-input: the customer name maps to two tenant UUIDs; the agent must ask the user to disambiguate.
A representative sample from happy-path:
1 | assertions: |
I run scripts/run-evals.py. Two cases pass; timing-constraint fails. The agent is not waiting before verification. I add an explicit “wait 30 seconds after a full-sync” line to the Constraints section, re-run: all three cases pass.
Phase 4 — Ship
Symlinked into ~/.claude/skills/ via make install. First history entry: history/v0_evidence.md. PR opened with exploration file, v0 evidence, and eval screenshot. Two reviewers approve; the PR merges.
First evolution round
Six weeks pass. Forty-seven invocations. Twelve failures. I run make evolve SKILL=diagnose-product-status WINDOW=6w.
The pipeline produces four evidence groups:
- Group A (5 sessions): routes to
bdc-teamfor failures that are actually upstreamops-teamoutages (distinguishing symptom: entire pipeline stalled, not just this product). - Group B (3 sessions): the 30-second timing constraint is too aggressive for new-landscape tenants, where sync completes in 8-12s.
- Group C (2 sessions): the ambiguous-tenant case is now handleable autonomously — the tenant directory API has added a
last_active_atfield. - Group D (2 sessions): flaky, no coherent pattern. Evolver ignores by design.
The evolver proposes three edits, one per actionable group. The validator runs both tracks and returns accept. I review — five minutes, because the evidence file is clear. I merge.
The history ledger records: history/v1.md (snapshot of the pre-edit skill), history/v1_evidence.md (the structured evidence file). The skill is now at v2.0.0 (MAJOR bump because Group A’s routing change is a behaviour change users can observe). Three new eval assertions added, one per proposal group.
Appendix D — Migration Playbook
A team with an existing collection of skills cannot adopt the full framework in a single afternoon. The migration is deliberate, staged, and reversible.
This playbook assumes the existing library is around ten to fifty skills, in git, with per-skill directories but no manifest, no evals, no version fields, and no history ledger.
The playbook is ordered by increasing invasiveness. Steps 1-3 are safe and produce immediate value; steps 4-6 change how the team works; steps 7-8 open the evolution loop and depend on trust that has to accumulate.
Step 1 — Inventory (day 1)
Before changing anything, produce a list of every skill in the library, its owner, its rough purpose in one sentence, and its state (active, deprecated-but-not-marked, abandoned). Resolve ambiguities — skills no one remembers writing, duplicate skills — before proceeding.
Deliverable: docs/library-inventory.md, checked in.
Step 2 — Adopt the frontmatter schema (week 1)
For each active skill, add the extended frontmatter with: name (from directory name), version (1.0.0 for every skill on adoption day — a convention, not a claim about maturity), layer (read each skill briefly; use Chapter 4’s definitions), status (stable for active, deprecated for abandoned), owners (from the inventory), description (reuse existing; do not rewrite yet).
Skip fields that require thought: depends_on, auth.providers, deprecated_by. Fill them lazily.
Deliverable: every active skill has frontmatter with the minimum fields.
Step 3 — Adopt the manifest (week 2)
Generate manifest.yaml alongside each SKILL.md using scripts/backfill-manifests.py. Enable the CI check that enforces manifest-frontmatter consistency. This is the first step that gates commits.
Deliverable: every active skill has a manifest; CI enforces consistency.
Step 4 — Declare dependencies (weeks 3-4)
Read every skill and populate depends_on. This is manual work and it is where the team’s implicit knowledge becomes explicit. The exercise surfaces surprises: skills that were thought to be independent turn out to share a connector; “connectors” that turn out to be primitives because they compose two systems.
Enable the resolver’s composition check at the end of this step. From this point forward, a commit that introduces a cycle or a layer violation cannot merge.
Deliverable: the dependency graph is complete and machine-checkable; make graph produces a Mermaid diagram.
Step 5 — Write evals for the top-quartile skills (weeks 5-8)
Start with the skills the team invokes most often. For each, follow the Chapter 9 discipline: collect three to five historical failure cases, each becoming an absent_regex; write two to three positive cases with substring and regex; add a Self-Test section; wire evals into CI.
Skills below the top quartile can wait. The pattern is to add evals as failures occur.
Deliverable: the top quartile has eval coverage; CI runs evals on every commit.
Step 6 — Establish the history ledger (week 9)
Add history/v0_evidence.md to every active skill, retrospectively. The content is minimal — a few sentences summarising why the skill exists. Add scripts/record-history.py to the repository.
Deliverable: every skill has a history/ directory with at least v0_evidence.md.
Step 7 — Run the evolution loop for one skill (weeks 10-12)
Pick one high-value, high-activity skill. Enable trace capture. Let two weeks of usage accumulate. Run make evolve SKILL=<pick> WINDOW=2w. Review the proposal and either accept or reject. Do this for two or three iterations on the same skill.
Deliverable: at least one skill has gone through the evolution loop end-to-end; the team knows the shape of the process.
Step 8 — Widen the loop (month 4 onwards)
Adopt evolution for more skills, one or two at a time. Move well-understood skills to auto-accept as trust accumulates. Enable fleet-wide runs (make evolve-all) once the team is comfortable reviewing aggregate output.
Anti-patterns to avoid during migration
Big-bang adoption. The team’s attention runs out before the discipline is internalised, and the shortcuts taken during rush-adoption become debt. Ship one step at a time.
Skipping the inventory. The inventory step surfaces surprises that change the plan in every migration. Do not skip.
Retro-writing elaborate v0 evidence files. Write them short. The effort is better spent on step-5 evals and step-7 loop runs.
Enforcing every check at once. The CI checks land incrementally. Enforcing everything on day 1 blocks every commit.
Migrating deprecated skills. Delete them, not migrate them. If they turn out to be needed, un-delete from git history.
Realistic timing
For a ten-person team with fifty skills: Steps 1-3 take three to four weeks. Step 4 (dependency declaration) takes another three to four weeks — it is the hardest step because it forces classification decisions. Step 5 takes six to eight weeks. Steps 6-7 take four to five weeks combined. Step 8 is ongoing.
Total: roughly four to five months from start to end-of-playbook.
Glossary (Selected Terms)
Absent regex. An eval assertion that fires when a given pattern does not appear in the trace. Encodes pitfalls: each entry is a mistake that was made once and must not be made again.
Connector. A skill whose role is to expose a single external system in a form the agent can compose. Depends on the substrate; depended on by primitives and workflows.
Dependency graph. The directed graph whose nodes are skills and whose edges are declared depends_on relationships. Enforced by the resolver.
Description. The routing signal the harness uses to decide which skill applies to a user’s request. A skill’s most important frontmatter field.
Evolver. The agent (usually another Claude Code invocation with a specialised system prompt) that reads evidence groups, proposes edits to a skill, and hands the proposals to the validator.
Forge, forging. The four-phase process by which a skill comes into being: Explore, Crystallize, Harden, Ship.
History ledger. The sequence of history/vN.md and history/vN_evidence.md files that record a skill’s evolution over time. The library’s institutional memory.
Invariant track. The half of dual-track validation that checks a proposed change against the skill’s existing eval suite.
Pitfall. A documented failure mode that a skill must avoid. Recorded during Explore, encoded as an absent_regex assertion in Harden.
Primitive. A skill whose role is to encapsulate a reusable domain operation. Composes connectors; consumed by workflows.
Rot. The tendency of a skill to become less correct over time as its environment changes.
Skill. A versioned, executable description of how an agent should accomplish a recurring task in a specific environment.
Substrate. A synonym for infrastructure layer, used when emphasising the “everything rests on this” property. sigcli is the substrate for authentication.
Target track. The half of dual-track validation that checks a proposed change against synthetic cases derived from the evidence that motivated the change.
Workflow. A skill whose role is to compose primitives to solve a specific user-facing problem. The top tier of the four-role hierarchy.
Series footer:
Part 6 of 6 — Skill Engineering
Part 1: Foundations · Part 2: Creation · Part 3: Governance · Part 4: Evolution · Part 5: Outlook · Part 6: Appendices