Skill Authoring Guide

There are three paths to creating a skill on the goatcs-harness:

Path 1: Hand-write (DEC-001)

Author the graph.json, modules/*.md, and supporting files manually. This is the path used for harness-internal and meta skills (like epiphany-brief) where forge abstains by design.

Native package structure

text
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
my-skill/
  graph.json              # Topology declaration
  graph.schema.json       # Optional: custom schema
  hats.json               # Persona-to-tier mapping
  modules/
    N-FIRST-NODE.md        # Per-node protocol
    N-SECOND-NODE.md
    ...
  scripts/
    validate-graph.sh      # Graph validation
    ...
  tests/
    run-smoke-tests.sh     # Structural smoke tests
    ...
  wiring-contract.yaml     # Expected wiring
  SKILL.md                 # Orchestrator protocol

Node model

Each node in graph.json declares:

json
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "id": "N-MY-NODE",
  "type": "ANALYZER",
  "exec_type": "inline",
  "hat": "analyzer",
  "tier": "model-medium",
  "scale_gates": {"token_budget": 4000, "time_budget": 300, "spawn_budget": 0, "retry_budget": 1},
  "input_dependencies": ["N-PREVIOUS"],
  "required_output_sections": ["analysis", "signals"],
  "raises_signals": ["my_signal"]
}

Edge model

Edges connect nodes with typed relationships:

json
1
2
3
4
5
6
7
{
  "id": "E1",
  "source": "N-FIRST",
  "target": "N-SECOND",
  "edge_type": "required",
  "signal_field": "first_output"
}

gate_condition DSL

Forward-conditional and back-edge types carry a gate_condition — a Boolean expression evaluated by the harness:

text
1
2
3
gate_pass == true
MODE == 'langgraph' AND gate_pass == true
verify_pass == false AND retry_count < 1

The harness evaluates these against the current signal state. Conditions use ==, !=, AND, OR, <, >, in, and parentheses.

Path 2: GraphBuilder API

Use the Python GraphBuilder API to programmatically construct a graph:

python
1
2
3
4
5
6
7
from goatcs_harness import GraphBuilder

gb = GraphBuilder("my-skill")
gb.add_node("N-INTAKE", type="INGEST", exec_type="inline", tier="model-medium")
gb.add_node("N-ANALYZE", type="ANALYZER", exec_type="spawn", tier="model-large")
gb.add_edge("N-INTAKE", "N-ANALYZE", edge_type="required")
gb.emit("./my-skill/")

Path 3: Import

Import a skill from another format (e.g., epiphany-plan markdown or JSON) using the harness's import adapters.