Phase vs. Skill vs. OS¶
reyn splits responsibilities across three layers. The split is what makes the system extensible: new skills are pure data and never require OS code changes.
The split¶
| Layer | Owns | Knows about |
|---|---|---|
| OS | Control flow, validation, events, Control IR dispatch | None of the skill's domain — just the DSL contract |
| Skill | The phase graph, the entry phase, the final output schema | Only its own phases and artifacts |
| Phase | An input artifact type and instructions for the LLM | Nothing outside its own input |
Phase: stateless and reusable¶
A Phase declares its input and instructions. It does not know:
- which phase comes next
- what its output looks like
- which skill it belongs to
This is what makes phases like revise reusable across skills: nothing in revise.md couples it to a specific draft producer.
Skill: structure-only¶
A Skill says "from entry, the graph allows these transitions, and the final output looks like this." It does not run code itself. A skill is data the OS reads.
Postprocessor: a skill-level finish hook¶
A Skill may optionally declare a postprocessor block — a deterministic transformation that runs at skill finish, between the LLM's final output (which conforms to the skill's final_output schema) and the artifact returned to the caller (which conforms to postprocessor.output_schema). Postprocessor mirrors the phase preprocessor structurally — same step types, same op set, same on_error policy — differing only in fire position (phase entry vs. skill finish). Like the skill graph, the postprocessor block is declared in skill.md and is invisible to both the OS's generic runtime logic and to individual phases. See Concepts: postprocessor and Reference: postprocessor.
OS: skill-agnostic¶
The OS reads the skill's graph, builds the LLM context, validates the result, and dispatches Control IR. It contains zero string literals naming a specific phase, artifact, or field. When a new skill appears, OS code is untouched.
Where each kind of change lands¶
| Change | Lands in |
|---|---|
| "Generate a different artifact field" | Phase instructions + the next phase's input schema |
| "Add a revision loop" | Skill's graph (add review → revise → review) |
| "Add a new control operation kind" | OS (new Control IR op) — every skill can use it for free |
| "Decide between two skills based on the user's input" | A router skill (e.g. skill_router); the OS does no skill picking |
The "not in this layer" smell¶
When you find yourself wanting to:
- Reference a sibling phase's name from inside a Phase → wrong layer (P1). Move the connection to the Skill graph.
- Hardcode a specific artifact type in OS code → wrong layer (P7). Read the type from the skill instead.
- Embed control-flow logic in Phase instructions ("if X, go to phase Y") → wrong layer (P8). Encode the choice as
candidate_outputsinstead.
Comparing common workflow systems¶
| System | Equivalent of "Skill" | Equivalent of "Phase" | Where control flow lives |
|---|---|---|---|
| Imperative agents (e.g. plain prompt loop) | (none) | (the whole prompt) | LLM choices the next call freely |
| State machines | The state diagram | A state | The diagram |
| reyn | A skill folder | A phase markdown | The skill's graph + LLM picks among allowed edges |
The crucial reyn-specific point: the LLM picks the edge but the OS validates the choice against the graph. The LLM can't add a new edge mid-run.