Table of Contents generated with DocToc

Lesson 7 — Writing portable skills

Source page: Writing portable skills Estimated time: 35 minutes (20 min reading + 15 min exercises and self-check) Lesson in sequence: 7 of 11


Learning objectives

By the end of this lesson you will be able to:

  1. Identify the three classes of non-portable element in a skill step (project-specific name, vendor/model name, harness-specific command) and name which portability axis each violates.
  2. Apply the four standard placeholders (<PROJECT>, <upstream>, <tracker>, <security-list>) correctly, knowing when to use each and when a value belongs in adopter config instead.
  3. Rewrite a skill step that hardcodes a project name or issue-tracker URL so that it reads the value from <project-config>/project.md instead.
  4. Convert a vendor-named step (“Ask Claude to…”) to a capability-floor statement, and explain why the floor form is correct even when the task is genuinely complex.
  5. Classify a harness-specific command in a skill step, replace it with a harness-neutral equivalent, and state when a harness-specific limit is acceptable to ship.

Prerequisite knowledge

Lesson 5 — Writing safe skills. The injection-resistance and draft-before-post patterns from that lesson appear in the before-and-after example in the source page. You should be comfortable naming boundaries and treating issue-body text as data before applying the portability rewrites here.

Lesson 6 — Debugging a skill. The output-contract and step-splitting techniques from lesson 6 interact with portability: a step that is complex enough to require a named vendor model is often a step that should be split, not specialised.


Before the lesson

Read the source page Writing portable skills from start to finish. Pay particular attention to:

  • The two portability axes — project-agnostic and model-neutral are independent; understand the difference before the exercises.
  • The four placeholders — their names, what each stands for, and the table of example values. The exercises will ask you to choose the right one.
  • Pattern 2 — Read from adopter config — including the model step that names the keys to extract and what to do when a key is missing.
  • Pattern 4 — Capability floor, not a vendor — the table of “instead of this / write this” substitutions.
  • The before-and-after example — trace each of the three changes and match them to the pattern that justifies them.
  • Check your understanding at the end of the source page — answer those four questions from memory before coming back here.

Exercises

Work through these alone or in pairs. Each exercise takes about two to three minutes. No live system is needed; use the source page as a reference.

Exercise 1 — Spot the portability problems

Each skill step below has one or more portability problems. For each step, list:

  • The non-portable element (quote the exact phrase).
  • Which axis it violates (project-agnostic, model-neutral, or both).
  • The pattern number from the source page that fixes it.

Step A.

Fetch the issue body from the apache/kafka repository:
  gh issue view NNN --repo apache/kafka --json body

Step B.

Ask GPT-4o to read the issue body and decide: is this a bug, a feature
request, or a question? Output one of the three labels.

Step C.

In Claude Code, press Ctrl+K and type /magpie-issue-triage to start the
skill. You will see the output in the conversation panel on the right.

Step D.

Capture a screenshot of the rendered dashboard using the harness's
built-in screen-capture tool, and attach it to the issue.

After labelling each, identify which three can be fixed by editing only the skill body, and which one cannot: it depends on a tool that exists in only one harness, so it calls for a documented limit rather than a rewrite.

Exercise 2 — Apply the placeholder convention

The following steps contain real names that should be placeholders. Rewrite each step using the correct placeholder from the table in the source page (Pattern 1). Choose from <PROJECT>, <upstream>, <tracker>, and <security-list>.

Step A.

Post the following comment on the apache/airflow GitHub Issues thread:

Step B.

If the issue concerns a security vulnerability, forward the full body
to security@apache.org before proceeding.

Step C.

Summarise the open items for the Airflow project and list them under
the heading "Airflow open items".

For step C, think about whether <PROJECT> alone is enough, or whether the summarised output should itself avoid a hardcoded name.

Exercise 3 — Replace a vendor dependency with a capability floor

A colleague has written the step below for a skill that classifies issue bodies using a structured JSON output. Rewrite the step to remove the vendor dependency, following Pattern 4. Your rewrite must:

  • Remove the vendor name.
  • Name the capability the step actually needs (if any annotation is needed at all).
  • Keep the instruction clear enough for any capable model to follow.
Step 3 — Classify the issue

Use Claude Sonnet to read the issue body. Because this step requires
multi-step reasoning and structured output, it will not work reliably on a
smaller model. Return a JSON object with fields `label` (one of BUG /
FEATURE-REQUEST / QUESTION) and `reason` (one sentence).

After rewriting, answer: does removing the vendor name mean the step will always work on any model? If not, what is the honest statement to make?

Exercise 4 — Move a hardcoded value into adopter config

The step below hardcodes a label name that differs from project to project. Rewrite it to use Pattern 2 (read from adopter config). Your rewrite should:

  • Add a preparatory step (or amend an existing one) that reads the required key from <project-config>/project.md.
  • Replace the hardcoded label name with the config-read variable.
  • Include the “stop if missing” guard.
Step 4 — Apply the triage label

Add the label `needs-triage` to the issue:
  gh issue edit NNN --repo <upstream> --add-label needs-triage

The label name (needs-triage) varies across projects. Write the config-read step that extracts the project’s actual triage-label name, then use it in the gh issue edit command.

Exercise 5 — Make the harness-dependent steps portable

Return to Step C and Step D from Exercise 1.

  • Rewrite Step C so it runs on any harness: drop the harness-specific command and interface reference, and use a harness-neutral invocation and output instead (Pattern 5).
  • Step D relies on a tool that exists in only one harness, so it cannot be made fully neutral. Describe how to ship it responsibly (Pattern 6): where in the skill you record the dependency, and the condition under which shipping with that limit is acceptable.

Then state, in one sentence, the difference between Step C and Step D that decides whether a harness problem is a body rewrite or a documented limit.


Self-check

Answer each question in a sentence or two before moving to lesson 8. If you cannot answer one, re-read the matching section of the source page.

Q1. A skill step reads: “Post this comment to the apache/kafka issue tracker.” Name the portability problem, name the correct placeholder, and write the corrected step.

Answer

The problem is a project-specific nameapache/kafka is hardcoded instead of using a placeholder. This violates the project-agnostic axis (PRINCIPLE 12). The step names the issue tracker, so the correct placeholder is <tracker>, and the corrected step is: “Post this comment on <tracker>#NNN.” (This mirrors the Pattern 1 example on the source page.) The related placeholder <upstream> stands for the repository identifier itself (org/repo): use <upstream> when a step names the repo and <tracker> when it names the issue tracker. Either way, no real project or repository name should appear in the skill body.


Q2. A colleague argues that a step needs Claude specifically because it “requires advanced reasoning”. Is this a valid reason to write “Use Claude” in the skill body? What would you write instead, and why?

Answer

It is not a valid reason to write a vendor name in the skill body. A vendor name is a dependency on something outside your control — models change, are deprecated, or are replaced by better ones. The honest statement is a capability floor: for example, “This step requires tool-calling capability and multi-step reasoning.” If the step consistently fails on a wide range of models, that is a signal to split the step (lesson 6, step-splitting technique) rather than to lock in a vendor. In most cases, no annotation is needed at all: the user’s configured model runs the step.


Q3. A skill calls gh, uv, and python in its tool steps. Is this skill harness-neutral? What property makes these tools portable?

Answer

Yes, a skill built on gh, uv, and python is harness-neutral. These are harness-neutral tools (Pattern 6): they behave the same way regardless of which agent host — Claude Code, OpenCode, Cursor, or any other — is running the skill. Portability comes from the fact that the tools are external CLI programs with stable interfaces, not APIs tied to a specific agent host. A skill that relies only on such tools can be adopted by any project on any harness without modification.


Q4. A skill hard-codes the label name kind:bug in a gh issue edit command. When would this be acceptable, and when must it be moved to adopter config?

Answer

Hardcoding kind:bug is only acceptable if the skill is written for exactly one project and will never be shared. As soon as the skill is intended to work across projects — which is the normal case for any skill in the framework’s skills/ directory — the label name must be moved to adopter config (Pattern 2). Different projects use different label conventions (bug, type: bug, kind:bug, defect, etc.). The skill body should read the label name from <project-config>/project.md and stop with a clear message if the key is missing, rather than silently applying the wrong label.


Q5. You have applied all six patterns and the validator passes. Is the skill now portable? What else would confirm it?

Answer

Passing the validator and applying the six patterns is a strong signal, but not final proof. The validator checks for known patterns (placeholder usage, harness command references, vendor names); it cannot check logic. The remaining confirmation step is running the skill’s eval suite against two different models (lesson 8, eval-driven development). If the same eval cases pass on both models, you have evidence of model-neutrality in practice. Similarly, running the skill in a second harness (even a minimal one) confirms harness-neutrality. The six patterns are authoring discipline; evals are the evidence.


Summary

Portability is an authoring discipline, not a post-hoc fix. Two axes matter: project-agnostic (no real project names, no hardcoded config values) and model-neutral (no vendor names, no harness commands). Six patterns cover almost every non-portable element a skill can contain: substitute placeholders for project names (Pattern 1), read variable values from adopter config (Pattern 2), run the validator before opening a pull request (Pattern 3), name capability floors instead of vendors (Pattern 4), write steps that any harness can execute (Pattern 5), and prefer harness-neutral tools (Pattern 6). A skill that satisfies all six patterns works for any project that adopts the framework and for any model backend, present or future.


Next

Eval-driven development — step 8 of the learning progression (lesson 8 of this module is not yet packaged; follow the source page directly until it lands). Once a skill is portable, eval-driven development is how you prove that it works — including across the models and harnesses you just wrote it to support.


Licence

Apache License 2.0 (PRINCIPLE 17). Pages written with help from AI carry a Generated-by: note in their commit message following ASF Generative Tooling Guidance.

Suggest a change