output-validator
Pre-Flight Scheck für jeden Output — validiert JSON, Markdown, Python-Syntax und Pflicht-Sektionen BEVOR der Output an den nächsten Schritt weitergegeben wird. Verhindert, dass malformierter Output in
SKILL.md.claude/skills/output-validator/SKILL.md
GitHubOutput-Validator
Schema-Check vor Handoff — keine Überraschungen im nächsten Schritt. Inspiriert von Claude Agent SDK Structured Outputs.
Prinzip
Output vom Worker → Validator → [PASS] → Critic/Handoff
→ [FAIL] → Sofort-Fix oder Retry
Der Validator prüft SURFACE, nicht SEMANTIK:
- Syntax (ist das JSON valide?)
- Struktur (sind alle Pflicht-Felder da?)
- Typ (ist es Code, Markdown, JSON?)
Semantische Prüfung ("Macht der Code Sinn?") ist Aufgabe des Critic-Gates.
Validierungs-Typen
JSON-Validator
validator.check_json(output, schema={
"required_keys": ["name", "version", "data"],
"types": {"version": "string", "data": "list"}
})
# → {"valid": false, "error": "Key 'version' fehlt"}
Markdown-Validator
validator.check_markdown(output, required_sections=[
"# Überschrift",
"## Setup",
"## Usage"
])
# → {"valid": true, "missing_sections": []}
Python-Code-Validator
validator.check_python(output, requirements=[
"def main", # Funktion muss existieren
"if __name__", # Guard-Klausel
"try:" # Error-Handling
])
# → {"valid": false, "missing": ["try:"], "syntax_error": None}
Generic-Validator
validator.check(output, format="text", min_length=100)
# → {"valid": true, "length": 450}
Return-Wert (immer strukturiert)
{
"valid": false,
"stage": "validator", // validator | critic | handoff
"format": "json", // json | markdown | python | text
"errors": [
{"type": "missing_key", "field": "version", "severity": "critical"},
{"type": "syntax_error", "line": 15, "severity": "fatal"}
],
"fixes": [
"Füge key 'version' hinzu",
"Fixe Syntax-Fehler in Zeile 15"
],
"retry_allowed": true, // false bei fatalen Fehlern
"summary": "2 Fehler gefunden, 2 fixbar"
}
Severity-Levels
| Level | Bedeutung | Aktion |
|---|---|---|
| fatal | Kaputte Syntax, kompiliert nicht | Sofort-Retry, Worker bekommt konkrete Fehler |
| critical | Pflicht-Element fehlt | Retry, Worker bekommt Fehlerliste |
| warning | Stilistisch suboptimal | Weiter an Critic, aber mit Note |
Integration in den Workflow
Position im Pipeline
Research/Implementation →
Worker-Output →
[OUTPUT-VALIDATOR] →
(PASS) → [CRITIC-GATE] → (PASS) → Handoff/Final
(FAIL) → Delta-Feedback → Worker (Retry)
Beispiel: multi-agent-work Phase 4
# Worker liefert Code
code = worker.generate_code(task)
# Validator prüft Syntax + Struktur
v_result = validator.check_python(code, required=["def ", "try:"])
if not v_result["valid"]:
worker.retry(task, feedback=v_result["fixes"])
return # Nicht weitermachen!
# Erst DANN an Critic
c_result = critic.evaluate(code, assertions=[...])
Beispiel: RAG-Pipeline (nach JSON-Extraktion)
# Worker extrahiert aus Dokument
json_output = worker.extract_structured_data(doc)
# Validator prüft JSON
check = validator.check_json(json_output, required_keys=["title", "content"])
if not check["valid"]:
# Sofort re-extrahieren mit anderem Prompt
json_output = worker.extract_with_schema(doc, schema=...)
Kriterien pro Format
Für Code (Python)
- Syntax valide (ast.parse)
- Alle import-Statements auflösbar
- Keine unbenutzten Variablen (optional)
- Guard-Klausel vorhanden (if name == "main")
- Mindestens ein try/except oder Error-Handling
Für JSON
- Valides JSON (json.loads ohne Fehler)
- Alle required_keys vorhanden
- Typen stimmen (string, list, dict, int, bool)
- Keine null-Werte für required Felder
Für Markdown (Doku)
- H1-Überschrift vorhanden
- Alle required_sections als H2/H3
- Keine broken Links (optional, per regex)
- Mindestens 200 Zeichen
Pitfalls
- Validator wird übersprungen → "Der Critic findet es schon" → NEIN. Validator ist schnell, Critic ist langsam
- Zu strenge Regeln → Warning als fatal behandeln → Worker wird frustriert
- Kein Schema-Update → Wenn das Format ändert, muss der Validator mitgepatched werden
- Validator prüft Semantik → NEIN. Der Validator prüft SYNTAX. Semantik ist Critic-Gate
- Validator läuft einmal und gut → Für autonome Systeme (>5 min runtime, live state mutation) reicht ein einzelner Validator-Lauf NICHT. Dry-Run-Validation-Loop VOR Live-Execute ist Pflicht (siehe
references/dry-run-before-live-execute.md). Beim GreyHack-Mission-Orchestrator-Bau (2026-07-06) fanden 30 Sek Dry-Run 5 echte Bugs (YAML-Parse-Error, Section-Header-Matching mit Emoji, OCR-Priorisierung, State-File-Pfad-Mismatch, Frontmatter-Überschreibung). Ohne Dry-Run hätten diese Bugs 60+ Min Live-Run korrumpiert.
Further Reading
references/dry-run-before-live-execute.md— Pflicht-8-Stufen-Dry-Run-Checkliste für autonome Systeme (Computer-Use, Cron-Deployments, Multi-Agent-Swarms). Inkl. der 5 realen Bugs aus dem GreyHack-Orchestrator-Bau als warnende Beispiele. Laden, sobald du ein System baust, das autonom läuft oder Live-State mutiert.references/preflight-check-mission-readiness.md— Pflicht-Environment-Readiness-Check (Binaries, Credentials, Paths, Window-Detection, Services). 10-Punkte-Standard-Checklist, 3-Tier-Severity-Modell (CRITICAL/OPTIONAL/INFO), 3 Output-Modi (Human/Verbose/JSON), CI/CD-freundliche Exit-Codes (0=GO, 1=CONDITIONAL, 2=NO-GO). Laden, BEVOR du ein Dry-Run startest — Pre-Flight ist die Vorstufe zum Dry-Run und validiert das Environment, nicht die Script-Logik.
Shared Library
Der Output-Validator und das Critic-Gate sind zwei Anwendungen derselben Idee:
Validator → Oberfläche (Syntax, Struktur, Schema)
Critic → Tiefe (Semantik, Korrektheit, Qualität)
Beide nutzen dieselben Assertion-Patterns. Der Validator filtert schnell, der Critic prüft sorgfältig.
Implementierung
# Pseudocode für den Validator
import json, ast
class OutputValidator:
def check(self, output, format, schema=None):
if format == "json":
return self._check_json(output, schema)
elif format == "python":
return self._check_python(output, schema)
elif format == "markdown":
return self._check_markdown(output, schema)
else:
return self._check_text(output, schema)
def _check_python(self, code, schema):
errors = []
try:
ast.parse(code)
except SyntaxError as e:
errors.append({"type": "syntax", "line": e.lineno, "severity": "fatal"})
for req in schema.get("required", []):
if req not in code:
errors.append({"type": "missing", "field": req, "severity": "critical"})
return {
"valid": len(errors) == 0,
"errors": errors,
"fixes": [e["field"] for e in errors if e["type"] == "missing"],
"retry_allowed": all(e["severity"] != "fatal" for e in errors)
}