Meta-Skill Authoring Guide
This guide is for authors and maintainers who write, validate, and review
OpenSquilla MetaSkills. For user-facing guidance, read
../features/meta-skill-user-guide.md.
What a MetaSkill Is
A MetaSkill is a SKILL.md file with:
kind: meta;- one or more natural-language
triggers; - a
composition:block that defines a directed acyclic graph of steps.
At runtime, the model may call:
meta_invoke(name="<meta-skill-name>")
OpenSquilla then executes the declared composition step by step and returns the final result to the user. The model chooses the workflow, but the runtime enforces dependency order, template rendering, risk metadata, recursion guards, tool gates, pauses, and final text selection.
Operators can disable model-visible MetaSkill behavior globally:
[meta_skill]
enabled = false
When disabled, MetaSkills remain installed for inventory and historical run
inspection, but they are not injected into prompts, meta_invoke is not
surfaced to the model, and explicit meta_invoke calls are rejected.
When to Use a MetaSkill
Use a MetaSkill when a task is repeatable and naturally decomposes into a small workflow, for example:
- classify the user request, then route to the right specialist skill;
- run two independent analysis skills, then merge their outputs;
- search or inspect context, then summarize it into a user-facing answer;
- execute a deterministic CLI-backed skill, then review or persist the result;
- pause for structured user input before continuing.
Do not use a MetaSkill for one-off instructions, open-ended planning that should remain conversational, or flows that need arbitrary recursion. A MetaSkill cannot compose another MetaSkill.
Where to Put a MetaSkill
For local managed skills, create:
~/.opensquilla/skills/<skill-name>/SKILL.md
For repository-bundled skills, place the skill under the bundled skills tree:
src/opensquilla/skills/bundled/<skill-name>/SKILL.md
Generated proposals are reviewed before installation. After accepting a proposal, OpenSquilla promotes it into the managed skills directory and refreshes the live skill loader.
Basic Authoring Flow
- Define the task contract: inputs, output, boundaries, false positives, and user-confirmation points.
- Write a conservative
name,description, andtriggers. - Split the workflow into steps such as intake, collect, analyze, draft, audit, and deliver.
- Add
depends_onwhenever a step needs output from earlier steps. - Filter all user input and previous step output in templates.
- Declare risk metadata and capabilities.
- Run deterministic and soft-activation checks.
- Inspect proposal and auto-enable audit output before accepting or enabling.
Users can activate a MetaSkill in two ways:
- Soft activation: ask naturally, and let the model choose the right
meta_invokecall. - Explicit activation: ask for the named MetaSkill when debugging or testing.
Required Frontmatter
Every MetaSkill should declare:
---
name: short-stable-name
kind: meta
description: One sentence that tells the model when this workflow applies.
triggers:
- short phrase users naturally type
meta_priority: 50
always: false
final_text_mode: auto
metadata:
opensquilla:
risk: low
capabilities: []
composition:
steps: []
---
The fields have these meanings:
name: stable identifier used bymeta_invoke.kind: must bemeta.description: model-facing description for when to use the workflow.triggers: phrases used by deterministic and model-assisted activation.meta_priority: sort key when multiple MetaSkills may match.always: normallyfalse; MetaSkills should not be injected unconditionally.final_text_mode: how the final answer is derived.metadata.opensquilla.risk: highest unattended auto-enable risk.metadata.opensquilla.capabilities: explicit side-effect capabilities.composition.steps: ordered DAG definition.
Risk Metadata
Use metadata.opensquilla.risk to declare the highest risk level required by
the workflow:
low: read-only reasoning, classification, summarization, or safe local inspection.medium: local file or artifact writes, deterministic document generation, or network reads.high: shell/process control, credential use, network writes, external side effects, or direct tool calls that can alter state.
Use metadata.opensquilla.capabilities to make side effects explicit. Common
capabilities include:
filesystem-write;artifact-write;document-export;network;network-read;network-write;external-side-effect;credential-use;process-control;shell.
If a referenced sub-skill lacks risk metadata, unattended auto-enable treats the dependency conservatively. New skills should declare risk and capabilities instead of relying on legacy compatibility fallbacks.
Step Types
MetaSkill steps support these execution kinds.
agent
Use agent for a normal skill-backed sub-agent turn. This is the best default
for user-facing reasoning and synthesis.
- id: summarize
kind: agent
skill: summarize
with:
text: "{{ outputs.search | truncate(2000) }}"
llm_chat
Use llm_chat for one bounded LLM generation step with no tool loop. This is
useful for intake normalization, compact drafting, final audit, and lightweight
synthesis.
- id: normalize
kind: llm_chat
with:
system: "Extract the request fields. Do not ask a question."
task: "{{ inputs.user_message | xml_escape | truncate(1000) }}"
llm_classify
Use llm_classify when the step should return exactly one value from a closed
set. This is useful for routing, triage, and compact decisions.
- id: classify
kind: llm_classify
output_choices: [BUG, FEATURE, QUESTION]
with:
text: "{{ inputs.user_message | xml_escape | truncate(512) }}"
user_input
Use user_input when the workflow should pause and collect structured data from
the user. The step requires a clarify: schema.
- id: collect_project
kind: user_input
when: "'NEEDS_CLARIFICATION: yes' in outputs.intake"
clarify:
mode: form
intro: "A few fields are needed before this workflow can continue."
nl_extract: true
fields:
- name: topic
type: string
required: true
prompt: "Topic"
max_chars: 200
The supported field types are string, enum, int, and bool. Use
skip_if or when to avoid pausing when intake has enough information.
tool_call
Use tool_call only for deterministic direct tool execution. Declare a
tool_allowlist, keep arguments narrow, and mark the MetaSkill as high risk when
the tool can change state.
- id: persist
kind: tool_call
tool: memory_save
tool_allowlist: [memory_save]
tool_args:
text: "{{ outputs.summary | truncate(2000) }}"
skill_exec
Use skill_exec for a skill with an entrypoint: manifest that should run as a
subprocess. This is appropriate for deterministic CLI-backed skills such as
document conversion or report generation.
- id: render
kind: skill_exec
skill: html-to-pdf
with:
html: "{{ outputs.report | truncate(12000) }}"
Dependencies and Parallelism
Steps without dependencies may run in parallel. A step with depends_on waits
for all named steps to finish.
composition:
steps:
- id: inspect_code
kind: agent
skill: code-reviewer
with:
request: "{{ inputs.user_message | xml_escape | truncate(512) }}"
- id: inspect_tests
kind: agent
skill: test-engineer
with:
request: "{{ inputs.user_message | xml_escape | truncate(512) }}"
- id: merge
kind: agent
skill: summarize
depends_on: [inspect_code, inspect_tests]
with:
text: |
Code review:
{{ outputs.inspect_code | truncate(2000) }}
Test review:
{{ outputs.inspect_tests | truncate(2000) }}
The graph must be acyclic. A step may only depend on step ids declared in the same composition.
Routing and Failure Handling
Use route when an agent or skill_exec step should choose a skill based on
previous outputs:
- id: classify
kind: llm_classify
output_choices: [DOCS, BUG, SECURITY]
with:
text: "{{ inputs.user_message | xml_escape | truncate(512) }}"
- id: handle
kind: agent
skill: summarize
depends_on: [classify]
route:
- when: "outputs.classify == 'DOCS'"
to: writer
- when: "outputs.classify == 'BUG'"
to: debugger
- when: "outputs.classify == 'SECURITY'"
to: security-reviewer
with:
request: "{{ inputs.user_message | xml_escape | truncate(512) }}"
Use on_failure for a single substitute step. The substitute must exist in the
same plan, must not have its own dependencies, and must not have its own
on_failure.
Final Text Modes
Use final_text_mode to control the final user-facing result:
auto: default. The orchestrator summarizes step outputs into a concise final answer.raw: return the last non-substitute step output verbatim.step:<step_id>: return one specific step output verbatim.
Examples:
final_text_mode: auto
final_text_mode: raw
final_text_mode: "step:summarize"
Use step:<step_id> when one step is the intended deliverable. Use raw when
the final step already formats a complete report. Use auto when the workflow
produces several intermediate outputs that need a compact user-facing summary.
Template Safety
Templates are Jinja expressions. Treat user input and previous step output as untrusted:
- For user text, start with
xml_escapeorslugify, then bound it withtruncate. - For
outputs.<step_id>, always bound or encode withtruncate,xml_escape,slugify, ortojson. - Do not pass raw
{{ inputs.user_message }}into a downstream step. - Do not pass raw
{{ outputs.some_step }}into another step. - Keep prompt-shaped strings explicit, short, and task-specific.
Safe examples:
query: "{{ inputs.user_message | xml_escape | truncate(512) }}"
text: "{{ outputs.search | truncate(2000) }}"
slug: "{{ inputs.user_message | slugify | truncate(80) }}"
payload: "{{ outputs.plan | tojson }}"
Unsafe examples:
query: "{{ inputs.user_message }}"
text: "{{ outputs.search }}"
Activation Guidance
Write triggers as short phrases users naturally type:
- Prefer:
summarize recent history - Prefer:
review current diff - Avoid:
run the internal OpenSquilla DAG composition meta skill
Use two to five triggers unless a production workflow has a tested reason to use more. Avoid triggers that collide with explanation questions such as “how does this meta-skill work?” A user asking about a MetaSkill should not accidentally run it.
Set description to explain when the model should choose the workflow. Do not
hide critical constraints in the body only; the model primarily sees the
frontmatter and injected skill summary.
Validation Checklist
Before sharing or enabling a MetaSkill:
- Confirm the frontmatter parses as YAML.
- Confirm
kind: metaandcomposition.stepsare present. - Confirm all
depends_on,route.to, andon_failurereferences point to valid steps or skills. - Confirm the graph has no cycles.
- Confirm all user input and step outputs are filtered.
- Confirm
metadata.opensquilla.riskandmetadata.opensquilla.capabilitiesreflect the workflow’s true side effects. - Run deterministic trigger checks with
scripts/meta_trigger_accuracy.py. - Run model-decision soft activation checks with
scripts/live_meta_soft_activation_e2e.py --env-file /path/to/.env. - For generated skills, inspect the Web UI proposal detail and its auto-enable audit before accepting or enabling.
Troubleshooting
If the MetaSkill does not appear to run:
- Check that the
SKILL.mdis under a loaded skill directory. - Refresh or restart the gateway if the skill was added outside the proposal accept flow.
- Confirm
disable-model-invocationis not set for a MetaSkill you expect the model to invoke. - Confirm the skill has
kind: metaand a non-emptycomposition.stepslist. - Confirm the user wording matches the triggers or description.
If parsing fails:
- Check duplicate step ids.
- Check unknown
kindvalues. - Check missing
skillforagentorskill_execsteps. - Check missing
output_choicesforllm_classify. - Check missing
clarify.fieldsforuser_input. - Check missing
tool, invalidtool_args, or mismatchedtool_allowlistfortool_call. - Check cycles and undefined
depends_onreferences.
If auto-enable is skipped:
- Inspect the proposal’s auto-enable audit in the Web UI.
- Add missing risk metadata to referenced sub-skills.
- Lower the workflow’s side effects, or require manual review for medium/high risk workflows.
Test Prompts
At minimum include:
- English positive trigger;
- explicit invocation;
- pasted-history negative case;
- neighboring-domain negative case;
- output-quality judge rubric.
Use realistic user phrasing with a clear subject and goal. Avoid operator-style phrases that users would not naturally type.
Docs index · Product guide · Improve this page · Report a docs issue