Skills¶
A skill is an agent or workflow packaged as a subgraph: it carries its
own state_schema, declares its output channels up front, and composes into
any parent Graph as a SubGraphNode. The base class is the Plugin API §3
surface; three reference skills (rag, autoresearch, wiki) plus the
react tool-loop primitive ship in-tree.
Skill base class¶
from stargraph.skills import Skill, SkillKind, Example
from pydantic import BaseModel
class Skill(BaseModel):
name: str # slug; same validator as ToolSpec
version: str # SemVer
kind: SkillKind # agent | workflow | utility
description: str
tools: list[str] = [] # tool ids `<ns>.<name>@<ver>`
subgraph: IRRef | None = None
system_prompt: str | None = None
state_schema: type[BaseModel] # declared output channels live here
requires: list[str] = [] # capability strings
examples: list[Example] = []
bubble_events: bool = True # FR-24 default-on
SkillKind is a StrEnum with three members:
agent— open-ended tool loop (usesreactinternally).workflow— fixed topology, no LLM-driven control flow.utility— pure transformation; no external side effects.
Manifest validation runs through the existing pluggy register_skills
hookspec at stargraph.plugin.hookspecs. Namespace conflicts loud-fail at
load (NFR-9).
Agent-as-subgraph¶
A skill is a graph. Its subgraph points at an IR document — inline or
by IRRef — that the engine compiles and pins through the same
structural_hash machinery used for top-level graphs. The composition seam
is SubGraphNode:
from stargraph.graph import Graph
from stargraph.nodes import SubgraphNode
from stargraph.skills.refs.rag import rag_skill
graph = Graph(parent_ir).with_node(
SubgraphNode.from_skill(rag_skill, site="step-3")
)
Two replay-driven contracts make composition safe:
- Site-id determinism (AC-3.5). The skill's instantiation site id is derived from its IR position, not LangGraph's call-order assignment. Replay survives topology mutations within unchanged regions.
- Topology pinning. The subgraph's
graph_hashis content-addressable. Any change to the skill's IR forces a top-levelgraph_hashmismatch — the engine's FR-20 gate fires loudly on resume.
Declared output channels¶
LangGraph #4182 — implicit parent-state mutation from a subgraph — is a silent-corruption bug. Stargraph mitigates it by enforcing declared output channels only:
- The skill's
state_schemadefines every field the subgraph can write. - The
SubGraphNodeboundary translator consumes those field names as the write whitelist. - A subgraph attempting to mutate an undeclared parent key raises a loud
validation error at compile time (
test_skill_compile_rejects_undeclared_output).
This diverges from LangGraph defaults intentionally — replay-first stance.
Pair it with bubble_events=True (FR-24) and the parent run sees every
transition the subgraph emits without needing to thread state by hand
(LangGraph #2484 mitigation).
ReAct primitive — stargraph.skills.react¶
The react skill ships as a tool-loop subgraph with three nodes:
| Node | Action | Side effects |
|---|---|---|
think |
LLM call producing {reasoning, tool_call \| done} |
external (LLM); must_stub on replay |
act |
Tool dispatch via engine FR-24 path | per ToolSpec |
observe |
Append result; salience update | read |
Native function-calling, not regex-parsed prompts — paper-faithful
ReAct is obsolete for capable models. Termination is rule-driven
(AC-10.4): max_steps, done flag in tool result, error_budget
exceeded. Model self-termination is never relied on; the rule pack lives
in CLIPS and fires through stargraph.fathom.
The state schema declares the four output channels:
class ReactState(BaseModel):
trajectory: list[ReactStep] = []
tool_calls: list[ToolCallRecord] = []
done: bool = False
error_budget: int = 3
final_answer: str | None = None
Pydantic set fields are forbidden (use frozenset with declared sort —
inherited from engine NFR-2). Replay matches tool stubs by
(node_name, step_id), not (tool_name, args), because args may drift
across runs even when LLM output is semantically equivalent.
Reference skills¶
Three skills land at stargraph.skills.refs.* — each is a real package, not
a doc-only placeholder:
| Skill | Kind | What it does |
|---|---|---|
rag |
workflow |
RetrievalNode (vector + doc) → LLM → answer-validation |
autoresearch |
agent |
ReAct loop: web fetch + vector retrieval, structured output |
wiki |
agent |
Topic → fan-out queries → wiki entry; every claim cites a source |
Composition rule of thumb: always declare your output channels first, then write the subgraph against them.
See design §3.7–3.13 for the full Skill base, ReAct, and reference-skill specs.