Eine Freigabe, die für einen Lauf gilt, ist keine Freigabe. Diese Lektion baut die andere Variante: ein Skript, das deine Änderungen als JSON-Array liest, eine Signatur darüber bildet und nur schreibt, wenn eine zweite Datei genau diese Signatur nennt, dazu Namen und Umfang. Passt die Signatur nicht mehr, verweigert das Skript den Schreibvorgang und hält die Verweigerung im Journal fest.
Auffällig ist eine Lücke in den Quellen selbst. Die fünf Zusicherungen für einen schreibenden Agenten sind veröffentlicht und datiert, die menschliche Freigabe ist eine davon – woran sie hängt, legt keiner der hier zitierten Texte fest.
Claude Code läuft im Terminal, und was es bearbeitet, muss als Datei existieren: eine Vorlage im Repository, ein Server-Logfile, ein Export aus deinem Crawler. Dass Claude Crawl-Daten auswertet und keine Websites crawlt, hielt Lawrence Hitches am 2. April 2026 fest.
Wen eine KI-Suchmaschine zitiert, behandelt Level 4 zu GEO und AIO. Ob es dafür ein eigenes Fach braucht, ist unter Fachleuten strittig; diese Seite nimmt dazu mit Absicht keine Position ein. Davor liegen Was Claude im SEO leisten kann – und was nicht und Claude Skills für SEO: installieren, nutzen, selbst schreiben.
Was du lernst
- Eine Freigabe an die Signatur eines Plans binden statt an einen Lauf.
- Sieben Bedingungen prüfen, die eine Änderung zurückhalten.
- Elf Rückgabewerte so belegen, dass jeder eine Ursache und einen Zuständigen hat – 1 bleibt frei.
- Von vier Aufgaben – Schema, Weiterleitungskarte, Logfiles, interne Verlinkung – sagen, welche einen maschinellen Gegentest hat.
- Den Kostenposten ausrechnen, der mit der Länge des Plans wächst und in keiner veröffentlichten Ersparnis vorkommt.
- Aus der Umfrage von Keyword.com die vier belegten Zahlen herausziehen und die beiden Nachbarzahlen stehen lassen.
Freigegeben wird ein Plan, kein Lauf
Eine Zustimmung, die im Augenblick des Schreibens erteilt wird, deckt ab, was zu diesem Augenblick in der Plandatei steht, und über frühere Fassungen dieser Datei sagt sie nichts. Dieses Schreibtor gibt deshalb keinen Zeitpunkt frei, sondern einen Wert: einen SHA-256-Hash über den sortiert serialisierten Plan, auf sechzehn Zeichen gekürzt. Ein anderer Dateiname, zwei Zeichen im Suchtext, ein Eintrag mehr – jedes Mal ein anderer Hash.
Eine Signatur auf der Befehlszeile tippt ab, wer den Lauf startet – damit hängt sie wieder am Lauf. Hier liegt sie in einer Datei mit signatur, freigegeben_von und gilt_fuer: versionierbar und hinterher noch lesbar.
gilt_fuer nennt die Zahl der Einträge, für die der Name steht, und sichert kryptografisch nichts – es ist trotzdem die wichtigste Angabe der Datei, weil ein Mensch sie ohne Werkzeug gegen den Plan prüfen kann.
Gesucht wird nach dem Werkzeug, nicht nach einer Erklärung. Die Autocomplete-Erhebung dieses Kurses vom 20. September 2026 gab zum Stamm claude code seo acht Vorschläge zurück, fünf davon mit einem Artefakt im Namen: skill, plugin, agent, skill github, github. Das belegt, dass solche Eingaben vorkommen; über ihre Häufigkeit sagt Autocomplete nichts, und eine Volumenangabe mit benanntem Urheber ist zu diesen Begriffen nicht zu finden.
Jede der fünf Zusicherungen liegt sonst bei jemand anderem
Fünf Zusicherungen braucht Claude Code, bevor es Schreibrechte auf deine Website bekommt. Aufgezählt hat sie Lawrence Hitches am 12. Mai 2026 in „How to Use Claude Code for SEO Automation“, veröffentlicht bei StudioHawk: einen Modus DRY_RUN, ein Audit-Protokoll in JSONL, eine Anfrage pro Sekunde, Sicherungen vor dem Schreiben und eine menschliche Freigabe vor jeder Änderung. Sein Text beschreibt die eigene Arbeit und nennt keine Stichprobengröße. Die mittlere Spalte nennt, wo jede Zusicherung sonst liegt – und jede dieser Stellen gehört jemand anderem als dir.
| Zusicherung | Wo sie in der Praxis stattdessen liegt | Wo sie in diesem Skript liegt |
|---|---|---|
| Probelauf als Voreinstellung | in der Gewohnheit, ohne Option zu starten | geschrieben wird nur bei gültiger Freigabe |
| Journal in JSONL | im Scrollback des Terminals | notiere(), auch im Probelauf |
| Eine Anfrage pro Sekunde | in der Geschwindigkeitseinstellung des Werkzeugs | eine Schreiboperation pro Sekunde, time.sleep(PAUSE) |
| Sicherung vor dem Schreiben | im nächtlichen Backup des Servers | shutil.copy2() vor dem ersten Byte |
| Menschliche Freigabe | in einem Zuruf, den niemand archiviert | freigabe_prueft() gegen eine Datei |
Zur dritten Zeile gibt es eine datierte Beobachtung: Rich Voller hielt am 26. Mai 2026 in „Screaming Frog v24 MCP“ fest, dass ein über MCP gestarteter Lauf 500 URLs in 20 Sekunden abrief, obwohl im Werkzeug eine URL pro Sekunde eingestellt war – ein einzelner Fall auf einer Website, zu Werkzeugversionen steht dort nichts.
Wer das Journal erst beim ersten Schreibvorgang anlegt, dokumentiert Schäden statt zu kontrollieren. Das Skript unten schreibt seine JSONL-Zeilen in jedem Modus.
Das Tor, Zeile für Zeile: was es prüft, bevor es ein Zeichen ersetzt
Das Schreibtor für Claude Code ist original für diese Lektion: gut 240 Zeilen Python, nur Standardbibliothek, kein Netzzugriff, lokale Dateien. Für ein CMS mit API tauschst du schreibe() aus. Anwendbar ist ein Eintrag, der sieben Prüfungen in dieser Reihenfolge übersteht:
- Die Datei ist vorhanden. Sonst stammt der Pfad aus einem alten Export.
- Sie ist eine reguläre Datei. Ein Verzeichnis im Plan ist ein Erzeugerfehler.
- Es liegt keine Sicherung vor. Eine
.bak-Datei heißt: in dieser Lauf-Reihe schon angefasst – überschrieben wird keine. - Der Inhalt ist als UTF-8 lesbar. Getrennt vom Lesefehler: andere Behebung.
- Der Suchtext kommt vor. Sonst entstand der Plan gegen einen älteren Stand.
- Er kommt genau einmal vor. Zwei Treffer heißen: der Plan ist zu grob.
- Suchtext und Ersatztext sind verschieden. Eine Leeränderung erzeugt nur einen Zeitstempel.
Beim Einlesen unterscheidet das Skript ein Feld, das fehlt, von einem, das da und leer ist. Deshalb steht nirgends ein get(feld, ""): ein Standardwert macht aus dem ersten Fall den zweiten und lässt einen beschädigten Export sauber durchlaufen. Leer sein darf nur ersetzen, das löscht den Suchtext.
#!/usr/bin/env python3
"""Schreibtor für SEO-Änderungen in großer Zahl. Ohne gültige Freigabedatei
schreibt es nichts, sondern macht einen Probelauf.
python3 schreibtor.py plan.json
python3 schreibtor.py plan.json --anwenden --freigabe freigabe.json
Nur Standardbibliothek, kein Netzzugriff. Der Plan ist ein JSON-Array; jeder
Eintrag trägt id, datei, suchen und ersetzen als Zeichenketten. Die Freigabe
ist ein JSON-Objekt mit signatur, freigegeben_von und gilt_fuer.
Ein Code pro Ursache, in dieser Reihenfolge geprüft:
0 Lauf zu Ende, kein Eintrag zurückgehalten.
2 Befehlszeile unvollständig, unbekannte Option, oder --help.
3 Plandatei fehlt oder ist keine reguläre Datei.
4 Plandatei ist eine reguläre Datei, deren Bytes nicht zu lesen waren.
5 Die Bytes der Plandatei sind nicht UTF-8.
6 UTF-8, aber kein gültiges JSON.
7 Gültiges JSON, aber nicht ein Array von Objekten mit den vier Feldern
id, datei, suchen und ersetzen als Zeichenketten.
8 Die Felder sind da, und id, datei oder suchen ist leer.
9 Array ohne Einträge.
10 Schreiben verlangt, Freigabe nicht gültig: nichts geschrieben.
11 Mindestens ein Eintrag wurde zurückgehalten.
1 bleibt unbelegt.
"""
import argparse
import hashlib
import json
import os
import shutil
import sys
import time
from datetime import datetime, timezone
PAUSE = 1.0 # Sekunden zwischen zwei Schreibvorgängen
FELDER = ("id", "datei", "suchen", "ersetzen")
NICHT_LEER = ("id", "datei", "suchen") # ersetzen darf leer sein: das löscht
FEHLT = object() # Platzhalter: kein get(feld, "") irgendwo
def abbruch(code, text):
sys.stderr.write(text + "\n")
raise SystemExit(code)
def zeitstempel():
return datetime.now(timezone.utc).isoformat(timespec="seconds")
def notiere(pfad, **felder):
"""Eine JSON-Zeile pro Ereignis, angehängt. Läuft auch im Probelauf."""
felder["ts"] = zeitstempel()
with open(pfad, "a", encoding="utf-8") as datei:
datei.write(json.dumps(felder, ensure_ascii=False, sort_keys=True) + "\n")
def signatur(plan):
roh = json.dumps(plan, sort_keys=True, ensure_ascii=False).encode("utf-8")
return hashlib.sha256(roh).hexdigest()[:16]
def lies_plan(pfad):
if not os.path.exists(pfad) or not os.path.isfile(pfad):
abbruch(3, "Plan nicht gefunden oder keine reguläre Datei: %s" % pfad)
try:
bytes_ = open(pfad, "rb").read()
except OSError as fehler:
abbruch(4, "Plan nicht lesbar: %s (%s)" % (pfad, fehler))
try:
text = bytes_.decode("utf-8")
except UnicodeDecodeError as fehler:
# UnicodeDecodeError erbt von ValueError, nicht von OSError, und
# braucht deshalb seinen eigenen Zweig.
abbruch(5, "Plan ist nicht UTF-8: %s (%s)" % (pfad, fehler))
try:
plan = json.loads(text)
except ValueError as fehler:
abbruch(6, "Plan ist kein gültiges JSON: %s (%s)" % (pfad, fehler))
if not isinstance(plan, list):
abbruch(7, "Plan ist kein JSON-Array, sondern %s" % type(plan).__name__)
for nummer, eintrag in enumerate(plan):
if not isinstance(eintrag, dict):
abbruch(7, "Eintrag %d ist kein Objekt" % nummer)
for feld in FELDER:
wert = eintrag.get(feld, FEHLT)
if wert is FEHLT:
abbruch(7, "Eintrag %d hat kein Feld %s" % (nummer, feld))
if not isinstance(wert, str):
abbruch(7, "Eintrag %d: %s ist keine Zeichenkette" % (nummer, feld))
if feld in NICHT_LEER and not wert.strip():
abbruch(8, "Eintrag %d: %s ist leer" % (nummer, feld))
if not plan:
abbruch(9, "Plan ist ein Array ohne Einträge")
return plan
def ganze_zahl(wert):
"""int oder None. bool ist eine Unterklasse von int und wird ausdrücklich
abgewiesen, damit true in der Freigabedatei nicht als 1 durchgeht."""
if isinstance(wert, bool):
return None
return wert if isinstance(wert, int) else None
def freigabe_prueft(pfad, erwartet, umfang):
"""Gibt den Grund der Verweigerung zurück, oder None samt Namen."""
if not pfad:
return "keine Freigabedatei angegeben", None
try:
with open(pfad, "rb") as datei:
freigabe = json.loads(datei.read().decode("utf-8"))
except (OSError, UnicodeDecodeError, ValueError):
return "Freigabedatei nicht lesbar", None
if not isinstance(freigabe, dict):
return "Freigabedatei ist kein JSON-Objekt", None
name = freigabe.get("freigegeben_von", FEHLT)
if name is FEHLT or not isinstance(name, str) or not name.strip():
return "kein Name in freigegeben_von", None
gefunden = freigabe.get("signatur", FEHLT)
if gefunden is FEHLT:
return "keine Signatur in der Freigabe", None
if gefunden != erwartet:
return "Signatur passt nicht zum Plan", None
deckung = ganze_zahl(freigabe.get("gilt_fuer", FEHLT))
if deckung is None:
return "gilt_fuer ist keine ganze Zahl", None
if deckung < umfang:
return "Freigabe deckt %d Einträge, der Plan hat %d" % (deckung, umfang), None
return None, name.strip()
def sicherungsname(verzeichnis, dateipfad):
flach = dateipfad.strip("./").replace(os.sep, "_").replace("/", "_")
return os.path.join(verzeichnis, flach + ".bak")
def pruefe(eintrag, sicherungen):
"""Gibt (anwendbar, Begründung) zurück und schreibt nie."""
pfad = eintrag["datei"]
if not os.path.exists(pfad):
return False, "Datei fehlt"
if not os.path.isfile(pfad):
return False, "keine reguläre Datei"
if os.path.exists(sicherungsname(sicherungen, pfad)):
return False, "Sicherung liegt schon vor"
try:
text = open(pfad, "rb").read().decode("utf-8")
except UnicodeDecodeError:
return False, "Datei ist nicht UTF-8"
except OSError:
return False, "Datei nicht lesbar"
treffer = text.count(eintrag["suchen"])
if treffer == 0:
return False, "Muster nicht gefunden"
if treffer > 1:
return False, "Muster mehrdeutig, %d Treffer" % treffer
if eintrag["suchen"] == eintrag["ersetzen"]:
return False, "Leeränderung"
return True, "anwendbar"
def schreibe(eintrag, sicherungen):
pfad = eintrag["datei"]
os.makedirs(sicherungen, exist_ok=True)
sicherung = sicherungsname(sicherungen, pfad)
shutil.copy2(pfad, sicherung) # Sicherung VOR dem ersten Byte
text = open(pfad, encoding="utf-8").read()
with open(pfad, "w", encoding="utf-8") as datei:
datei.write(text.replace(eintrag["suchen"], eintrag["ersetzen"], 1))
return sicherung
def main():
leser = argparse.ArgumentParser(add_help=True)
leser.add_argument("plan")
leser.add_argument("--anwenden", action="store_true")
leser.add_argument("--freigabe", default="")
leser.add_argument("--journal", default="journal.jsonl")
leser.add_argument("--sicherungen", default="sicherungen")
try:
argumente = leser.parse_args()
except SystemExit:
# argparse beendet mit 0 bei --help und mit 2 bei einem Fehler. Beide
# Fälle haben nichts geprüft, also bekommen beide denselben Code.
raise SystemExit(2)
plan = lies_plan(argumente.plan)
erwartet = signatur(plan)
print("Plan: %d Einträge, Signatur %s" % (len(plan), erwartet))
grund = name = None
if argumente.anwenden:
grund, name = freigabe_prueft(argumente.freigabe, erwartet, len(plan))
schreiben = argumente.anwenden and grund is None
verweigert = argumente.anwenden and grund is not None
modus = ("Schreiben" if schreiben else
"Freigabe verweigert" if verweigert else "Probelauf")
print("Modus: %s" % modus)
notiere(argumente.journal, ereignis="start", signatur=erwartet,
eintraege=len(plan), modus=modus, freigegeben_von=name)
if verweigert:
sys.stdout.flush() # damit die Reihenfolge im Terminal stimmt
sys.stderr.write("Keine Schreibfreigabe: %s\n" % grund)
notiere(argumente.journal, ereignis="freigabe_verweigert", grund=grund,
signatur_erwartet=erwartet, freigabe=argumente.freigabe or None)
anwendbar = zurueckgehalten = geschrieben = 0
for eintrag in plan:
ok, begruendung = pruefe(eintrag, argumente.sicherungen)
if not ok:
zurueckgehalten += 1
notiere(argumente.journal, ereignis="zurueckgehalten",
id=eintrag["id"], datei=eintrag["datei"], grund=begruendung)
print(" [halt] %-8s %s" % (eintrag["id"], begruendung))
continue
anwendbar += 1
if not schreiben:
notiere(argumente.journal, ereignis="probe", id=eintrag["id"],
datei=eintrag["datei"], grund=begruendung)
print(" [probe] %-8s %s" % (eintrag["id"], begruendung))
continue
sicherung = schreibe(eintrag, argumente.sicherungen)
geschrieben += 1
notiere(argumente.journal, ereignis="geschrieben", id=eintrag["id"],
datei=eintrag["datei"], sicherung=sicherung)
print(" [neu] %-8s Sicherung %s" % (eintrag["id"], sicherung))
time.sleep(PAUSE)
notiere(argumente.journal, ereignis="ende", anwendbar=anwendbar,
zurueckgehalten=zurueckgehalten, geschrieben=geschrieben)
print("anwendbar %d, zurückgehalten %d, geschrieben %d"
% (anwendbar, zurueckgehalten, geschrieben))
print("Journal: %s" % argumente.journal)
if verweigert:
return 10
if zurueckgehalten:
return 11
return 0
if __name__ == "__main__":
sys.exit(main())
Dazu gehören vier erfundene Eingabedateien. Die erste trägt die Änderung, die durchgeht:
<!doctype html>
<meta name='description' content='Gartenschere online kaufen'>
<h1>Gartenschere</h1>
Die zweite trägt denselben Beschreibungstext zweimal, im meta-Element und als Open-Graph-Angabe – deshalb zählt das Tor, statt zu ersetzen.
<!doctype html>
<meta name='description' content='Rasendünger für den Frühling'>
<meta property='og:description' content='Rasendünger für den Frühling'>
<h1>Rasendünger</h1>
Der Plan ist ein JSON-Array, ein Objekt pro Änderung; www/laubsauger/index.html existiert absichtlich nicht.
[
{"id": "meta-01",
"datei": "www/gartenschere/index.html",
"suchen": "content='Gartenschere online kaufen'",
"ersetzen": "content='Gartenschere schärfen: Bypass, Amboss und 6 Modelle'"},
{"id": "meta-02",
"datei": "www/laubsauger/index.html",
"suchen": "content='Laubsauger online kaufen'",
"ersetzen": "content='Laubsauger: Häckselgrad, Lautstärke, 8 Modelle'"},
{"id": "meta-03",
"datei": "www/rasenduenger/index.html",
"suchen": "content='Rasendünger für den Frühling'",
"ersetzen": "content='Rasendünger: Stickstoff, Aufwandmenge, 5 Sorten'"},
{"id": "meta-04",
"datei": "www/gartenschere/index.html",
"suchen": "content='Rasenkante im Vergleich'",
"ersetzen": "content='Rasenkanten: 7 Systeme auf demselben Beet'"},
{"id": "h1-01",
"datei": "www/gartenschere/index.html",
"suchen": "<h1>Gartenschere</h1>",
"ersetzen": "<h1>Gartenschere</h1>"}
]
Die Freigabedatei ist die einzige von Menschenhand:
{"signatur": "babe18c58eee8919",
"freigegeben_von": "txema",
"gilt_fuer": 5}
Ein Rückgabewert, eine Ursache – und wer sie behebt
Von außen sieht ein geplanter Lauf des Schreibtors nur eine Zahl, und sie muss die nächste Handlung bestimmen – deshalb die dritte Spalte. Die Codes 3 bis 9 sehen gleich aus und verlangen sieben Korrekturen an drei Stellen. --help endet mit 2: ein Lauf, der nur seine Hilfe druckt, hat nichts geprüft.
| Code | Was genau eingetreten ist | Wer es behebt |
|---|---|---|
| 0 | Lauf zu Ende, nichts zurückgehalten | niemand |
| 2 | Befehlszeile unvollständig, unbekannte Option, --help |
wer aufruft |
| 3 | Plandatei fehlt oder ist keine reguläre Datei | wer aufruft |
| 4 | reguläre Datei, Bytes nicht zu lesen | wer Rechte verwaltet |
| 5 | die Bytes sind nicht UTF-8 | wer den Plan erzeugt |
| 6 | UTF-8, aber kein gültiges JSON | wer den Plan erzeugt |
| 7 | gültiges JSON, Feld fehlt oder ist keine Zeichenkette | wer den Plan erzeugt |
| 8 | id, datei oder suchen ist leer |
wer den Plan erzeugt |
| 9 | Array ohne Einträge | wer den Plan erzeugt |
| 10 | Freigabe nicht gültig, nichts geschrieben | eine Person muss freigeben |
| 11 | mindestens ein Eintrag zurückgehalten | wer den Plan erzeugt |
| 1 | unbelegt | – |
Code 1 ist absichtlich nicht belegt. Stirbt der Python-Prozess an einer nicht gefangenen Ausnahme, endet er selbst mit 1; wer diese Zahl vergibt, kann einen Absturz des Tors nicht mehr von einer Entscheidung des Tors unterscheiden.
Fünf Aufrufe an demselben Plan
Die Sitzung mit dem Schreibtor steht wörtlich unten; das sed spielt das, was einen geprüften Plan nachträglich verändert.
$ python3 schreibtor.py plan.json
Plan: 5 Einträge, Signatur babe18c58eee8919
Modus: Probelauf
[probe] meta-01 anwendbar
[halt] meta-02 Datei fehlt
[halt] meta-03 Muster mehrdeutig, 2 Treffer
[halt] meta-04 Muster nicht gefunden
[halt] h1-01 Leeränderung
anwendbar 1, zurückgehalten 4, geschrieben 0
Journal: journal.jsonl
$ echo $?
11
$ python3 schreibtor.py plan.json --anwenden
Plan: 5 Einträge, Signatur babe18c58eee8919
Modus: Freigabe verweigert
Keine Schreibfreigabe: keine Freigabedatei angegeben
[probe] meta-01 anwendbar
[halt] meta-02 Datei fehlt
[halt] meta-03 Muster mehrdeutig, 2 Treffer
[halt] meta-04 Muster nicht gefunden
[halt] h1-01 Leeränderung
anwendbar 1, zurückgehalten 4, geschrieben 0
Journal: journal.jsonl
$ echo $?
10
$ sed -i 's/6 Modelle/7 Modelle/' plan.json
$ python3 schreibtor.py plan.json --anwenden --freigabe freigabe.json
Plan: 5 Einträge, Signatur 7efa11bd9fbdcf3b
Modus: Freigabe verweigert
Keine Schreibfreigabe: Signatur passt nicht zum Plan
[probe] meta-01 anwendbar
[halt] meta-02 Datei fehlt
[halt] meta-03 Muster mehrdeutig, 2 Treffer
[halt] meta-04 Muster nicht gefunden
[halt] h1-01 Leeränderung
anwendbar 1, zurückgehalten 4, geschrieben 0
Journal: journal.jsonl
$ echo $?
10
$ sed -i 's/7 Modelle/6 Modelle/' plan.json
$ python3 schreibtor.py plan.json --anwenden --freigabe freigabe.json
Plan: 5 Einträge, Signatur babe18c58eee8919
Modus: Schreiben
[neu] meta-01 Sicherung sicherungen/www_gartenschere_index.html.bak
[halt] meta-02 Datei fehlt
[halt] meta-03 Muster mehrdeutig, 2 Treffer
[halt] meta-04 Sicherung liegt schon vor
[halt] h1-01 Sicherung liegt schon vor
anwendbar 1, zurückgehalten 4, geschrieben 1
Journal: journal.jsonl
$ echo $?
11
$ python3 schreibtor.py plan.json --anwenden --freigabe freigabe.json
Plan: 5 Einträge, Signatur babe18c58eee8919
Modus: Schreiben
[halt] meta-01 Sicherung liegt schon vor
[halt] meta-02 Datei fehlt
[halt] meta-03 Muster mehrdeutig, 2 Treffer
[halt] meta-04 Sicherung liegt schon vor
[halt] h1-01 Sicherung liegt schon vor
anwendbar 0, zurückgehalten 5, geschrieben 0
Journal: journal.jsonl
$ echo $?
11
Von fünf Einträgen übersteht einer die Prüfreihe, die vier anderen scheitern jeweils anderswo: meta-02 zeigt auf eine Datei, die es nicht gibt, meta-03 findet seinen Suchtext zweimal, meta-04 findet ihn nicht, h1-01 ersetzt einen Text durch sich selbst. Zwei der sieben Prüfungen greifen in dieser Sitzung nie: ein Pfad auf etwas, das keine reguläre Datei ist, und ein Inhalt ohne gültiges UTF-8. Als bestanden meldet das Tor sie nicht.
Der vierte Aufruf schreibt meta-01 – und meta-04 und h1-01 tragen nun eine andere Begründung, weil für ihre Datei eine Sicherung liegt. Der fünfte zeigt dasselbe am ganzen Plan: nichts anwendbar, nichts geschrieben, Code 11. Ein zweiter Lauf ändert nichts mehr, und er sagt das auch.
Das Journal dieser fünf Aufrufe hat 37 Zeilen: je Aufruf eine start– und eine ende-Zeile, dazwischen eine pro Eintrag, dazu zwei Verweigerungen. Drei davon tragen die Entscheidungen des Tors. Die Zeitstempel sind die dieses Laufs; die Freigabezeile nennt die vorgelegte Datei als Beweisstück.
{"ereignis": "freigabe_verweigert", "freigabe": null, "grund": "keine Freigabedatei angegeben", "signatur_erwartet": "babe18c58eee8919", "ts": "2026-10-05T11:31:45+00:00"}
{"ereignis": "freigabe_verweigert", "freigabe": "freigabe.json", "grund": "Signatur passt nicht zum Plan", "signatur_erwartet": "7efa11bd9fbdcf3b", "ts": "2026-10-05T11:31:45+00:00"}
{"datei": "www/gartenschere/index.html", "ereignis": "geschrieben", "id": "meta-01", "sicherung": "sicherungen/www_gartenschere_index.html.bak", "ts": "2026-10-05T11:31:45+00:00"}
Vier Aufgaben am Tor: Weiterleitungskarte, interne Verlinkung, Logfiles, Schema
Die Aufgaben aus dem technischen SEO, die bei Claude Code landen, unterscheiden sich nicht im Ergebnis, sondern darin, wogegen es sich prüfen lässt. Lawrence Hitches zählt vier davon in „Claude for Technical SEO: Audits, Crawl Fixes & Schema“ vom 2. April 2026 auf: Logfile-Analyse, JSON-LD, hreflang-Prüfung und Weiterleitungskarten samt Regeln für nginx, Apache, .htaccess und Cloudflare. Ein Praxisartikel ohne Stichprobengröße.
| Aufgabe | Eingabe | Wer oder was das Ergebnis abnimmt | Maschinell? |
|---|---|---|---|
| Logfile-Analyse | das Server-Logfile | deine Einschätzung zum Crawl Budget | nein |
| JSON-LD erzeugen | HTML oder Template einer Seite | Googles Prüfwerkzeug für Rich-Suchergebnisse | ja |
hreflang prüfen |
den Crawl-Export | die Rückverweise der ausgezeichneten Paare | ja |
| Interne Verlinkung planen | einen Crawl mit Seiteninhalten | ein menschliches Urteil je Paar | nein |
| Weiterleitungskarte bauen | alte und neue URL-Liste | ob das Ziel das Äquivalent ist | nein |
| Weiterleitungsregeln schreiben | die freigegebene Karte | ein Diff gegen dieselbe Karte | ja |
Über die Abgabe entscheidet, wo der Maßstab des Gegentests liegt: in einem Validator, einer freigegebenen Karte, einer Reziprozitätsregel – oder in deinem Urteil. Nur in diesen drei Fällen kann eine Maschine prüfen. Beim JSON-LD liegt er im Test für Rich-Suchergebnisse. SE Ranking verglich dort am 7. Juli 2025 (aktualisiert 24. Juni 2026) Claudes und ChatGPTs Markup an vier je einmal ausgeführten Aufgaben: 1 gegen 5 nicht kritische Beanstandungen, bei vier Durchläufen und ohne Stichprobengröße, also keine Rangliste.
Beim Logfile bleibt das Urteil bei dir, der Rest ist Mengenarbeit. Hitches nennt am 2. April 2026 fünf Fragen dazu, die ein Logfile beantwortet und ein Crawl-Export nicht: welche URLs am häufigsten gecrawlt werden, wie oft, welche Fehler liefern, welche wichtigen Seiten nie gecrawlt wurden und wo Crawl Budget an URLs ohne Anspruch verloren geht. Bei Millionen Zeilen rät er zum Aggregieren im Terminal; belastbar wird ein Crawl im technischen SEO.
Bei der internen Verlinkung liefert der Agent Quelle, Ziel, Ankertext und Einfügepunkt; StudioHawk beschreibt am 12. Mai 2026 einen dreistufigen Bewertungsgraphen, der das als Probelauf ausgibt. Der Schwellenwert ist domänenabhängig: Niko Alho nennt am 20. Mai 2026 Werte zwischen 0,78 und 0,85 und setzt den Kipppunkt manueller Verlinkung bei 200 bis 500 Seiten an – ohne Angabe, über wie viele Domains das erhoben wurde. Die Kalibrierung behandelt dieses Level gesondert, interne Verlinkung gehört zu Level 1.
Bei der Weiterleitungskarte liegt der veröffentlichte Ausgangswert bei 0,95: so die Dokumentation von Screaming Frog zu Vektor-Einbettungen für das Redirect-Mapping, Stand 20. Oktober 2025. Unterhalb dieses Wertes beginnt die Arbeit, die den Website-Relaunch entscheidet, und dafür gibt es keine veröffentlichte Anleitung; der Prüfschritt steht in diesem Level an anderer Stelle.
Wie verbreitet die Abgabe ist, hält die einzige hier zitierte Umfrage mit Stichprobenangabe fest: bei Keyword.com (State of AI in SEO 2026, 1. Januar 2026, n = 97, selbst gemeldete Teilnahme) liegt der KI-Einsatz bei technischen SEO-Audits bei 38 %; umgekehrt nennen 40 % derjenigen 79 %, die automatisierbare Aufgaben bewusst liegen lassen, genau dieses Feld. Was der Mensch dabei abfängt, steht bei ContextBolt vom 23. Juni 2026: dort wanderten Zahlen aus einer fremden Schätzung als Messwerte weiter, und ein Keyword mit aussichtslosem Schwierigkeitswert blieb im Vorschlag. Getragen wird das von einem Versuch über eine Woche, auf genau einer Website, ohne Vergleichsgruppe. Gegen beides hilft keine Syntaxprüfung, nur ein Mensch mit der Quelle daneben.
Googles Richtlinie nennt keine Seitenzahl
Googles Spam-Richtlinien bestimmen scaled content abuse über den Zweck einer Seitenmenge, nicht über ihre Größe. Im Text mit dem Stand 28. August 2026 heißt es: „when many pages are generated for the primary purpose of manipulating search rankings and not helping users“. Die Art der Herstellung steht nicht darin, eine Obergrenze auch nicht; eine konkrete Zahl ist deshalb immer die Zahl desjenigen, der sie nennt.
Ein Teil der Branche liest aus demselben Zitat ein Mengenrisiko: legt ein Ablauf fünfhundert Seiten in einer Nacht an, kann hinterher niemand mehr sagen, wofür sie entstanden sind – und genau danach fragt die Richtlinie. Der andere Teil liest es als Entlastung der Herstellungsweise und prüft die einzelne Seite: sind ihre Angaben nachprüfbar, und leistet sie dem Leser etwas?
Zwischen den beiden Lesarten entscheidet keine Messung: keine der hier zitierten Quellen stellt eine Untersuchung gegen die andere, und Google nennt zu dieser Richtlinie keinen Zahlenwert. Eine Position bezieht diese Seite deshalb nicht; die Nachbarschaft ist Grey Hat, Black Hat und Parasite SEO.
Das Feld gilt_fuer bleibt davon unberührt: für fünfhundert neue Seiten steht dort fünfhundert, und ein Plan, der nach einer Freigabe über fünfzig auf fünfhundert angewachsen ist, endet in Code 10 statt in einem Schreibvorgang.
Zwei Zeitangaben, vier Posten und der Faktor, der mit dem Plan wächst
Zwei Zeitangaben kursieren zu Claude Code und SEO, beide unverrechnet. StudioHawk nennt sie am 12. Mai 2026: Metadaten über eine CMS-API mit Search-Console-Daten brauchen statt 6 bis 8 Stunden nur noch Minuten, interne Verlinkung statt 3 bis 5 Tagen weniger als eine Stunde. Beide Werte beziffern, wie lange der Agent braucht. Nicht gegengerechnet sind Einrichtung, Abonnement und die Prüfung, die derselbe Autor für verpflichtend erklärt; über wie viele Websites das erhoben wurde, steht nicht dabei.
Für ein Tor mit Freigabe kommt ein Posten hinzu, der in dieser Gattung nirgends auftaucht: die Lesezeit wächst mit dem Plan. Zehn Einträge sparen zehnmal und wollen zehnmal gelesen sein.
netto = (T_manuell - T_agent) * N
- T_einrichtung
- T_freigabe_pro_eintrag * E * N
lohnt ab: N > T_einrichtung / ((T_manuell - T_agent) - T_freigabe_pro_eintrag * E)
keine Loesung, wenn T_freigabe_pro_eintrag * E >= T_manuell - T_agent
N ist die Zahl der Läufe im Zeitraum, E die Einträge pro Plan, T_einrichtung die einmalige Arbeit, bis Zugänge und Rechte stehen. Der Nenner der Lohnt-ab-Bedingung wird negativ, sobald T_freigabe_pro_eintrag * E den Vorsprung des Agenten überschreitet – dann rechnet sich kein Lauf mehr, und verursacht hat das die Länge deiner Pläne, nicht die Geschwindigkeit des Modells.
| Posten | Wie du ihn ermittelst | Was dazu veröffentlicht ist |
|---|---|---|
| Freigabe je Eintrag | zehn Einträge von Hand lesen, Zeit durch zehn | nichts auffindbar; neu in dieser Lektion |
Einrichtung, T_einrichtung |
einmalig mitstoppen | keine veröffentlichte Messung auffindbar |
| Abonnement und Tokens | von der Abrechnung des Vormonats ablesen | in der ausgewerteten Literatur unbeziffert |
| Prüfung als Pflichtschritt | die Abnahme eines Laufs stoppen | von denselben Autoren verpflichtend genannt; 57 % nennen Qualität als Grund gegen Automatisierung (Keyword.com, n = 97) |
| Aufgabenzeit vorher und nachher | die Aufgabe nach der Einrichtung stoppen | 6–8 Stunden auf Minuten; 3–5 Tage auf unter eine Stunde (StudioHawk, 12. Mai 2026) |
Drei der fünf Zeilen stehen in der dritten Spalte ohne Zahl, und das ist der Befund. Beziffert sind nur zwei Posten: die Aufgabenzeit, von StudioHawk am 12. Mai 2026 ohne Stichprobe genannt, und die Prüfung, für die Keyword.com am 1. Januar 2026 bei n = 97 und selbst gemeldeter Teilnahme eine Rangfolge liefert, keine Hochrechnung. Vollautomatisiert arbeitet dort genau eine Person von 97, also 1 %, und mit 57 % am häufigsten genannt wird „Quality not good enough“.
Zwei Zahlen derselben Seite sagen etwas anderes und werden regelmäßig damit verwechselt: eine zweite von 70 % zu Einschränkungen und eine unverbundene von 57 % zur ChatGPT-Nutzung.
Häufige Fehler
- Mehrere Einträge auf dieselbe Datei legen. Das Tor schreibt den ersten und hält den Rest zurück, weil die Sicherung nicht überschrieben wird. Die Korrektur: ein Eintrag pro Datei und Lauf.
- Ein mehrdeutiges Muster trotzdem anwenden. Ein Ersatz über zwei Treffer hinterlässt eine Datei, die niemand gelesen hat. Die Korrektur: vorher zählen und bei mehr als einem Treffer den Suchtext verlängern.
- Freigeben, ohne festzuhalten, was freigegeben wurde. Eine Zustimmung, von der nur die Beteiligten wissen, lässt sich nicht gegen den gelaufenen Plan halten. Die Korrektur: Signatur, Name und Umfang in eine versionierte Datei schreiben.
- Den Rückgabewert nicht auslesen. Wer das Tor aus einem Zeitplan startet und auf eine Fehlermeldung wartet, erfährt von einem zurückgehaltenen Eintrag nie – abgestürzt ist ja nichts. Die Korrektur: den Wert im aufrufenden Skript abfragen und dort auf Zuständige verteilen.
Kurzfassung
- Die Freigabe dieser Lektion gilt für die Signatur eines Plans und steht in einer Datei mit Name und Umfang; ein geändertes Zeichen verweigert den Schreibvorgang. Elf Codes tragen je eine Ursache und einen Zuständigen; 1 bleibt frei, weil dort schon der abgestürzte Prozess landet.
- Fünf Zusicherungen sind benannt und datiert: Probelauf, JSONL-Journal, eine Anfrage pro Sekunde, Sicherung vorab, menschliche Freigabe (StudioHawk, 12. Mai 2026, ohne Stichprobe). Woran die Freigabe hängt, bestimmt keine dieser Veröffentlichungen.
- Vor jedem Plan steht ein Werkzeug, das nicht Claude ist: Claude Code wertet Crawl-Daten aus und crawlt selbst nicht (Lawrence Hitches, 2. April 2026).
- Zweck statt Menge: so bestimmt der Text der Google-Spam-Richtlinien (Stand 28. August 2026) scaled content abuse, und eine Seitenzahl steht nicht darin. Welche Praxisregel daraus folgt, ist unter Fachleuten strittig; hier steht dazu nur das Mechanische.
- Keyword.com (1. Januar 2026, n = 97, selbst gemeldete Teilnahme) trägt vier belastbare Zahlen: eine Person von 97 vollautomatisiert, 38 % KI bei technischen Audits, 40 % innerhalb der 79 % mit bewusst liegen gelassenen Aufgaben, 57 % „Quality not good enough“. Zwei weitere Zahlen derselben Seite gehören nicht dazu: 70 % zu Einschränkungen und 57 % zur ChatGPT-Nutzung.
- Zwei Ausgangswerte sind veröffentlicht, beide domänenabhängig: 0,95 beim Redirect-Mapping mit Einbettungsvektoren (Screaming Frog, Dokumentation Stand 20. Oktober 2025) und 0,78 bis 0,85 beim automatischen Verlinken (Niko Alho, 20. Mai 2026, ohne Stichprobenangabe).
- Unverrechnet bleiben zwei Werte von StudioHawk (12. Mai 2026): die Metadaten-Aufgabe endet bei Minuten, die Verlinkungsaufgabe bei unter einer Stunde – vorher 6 bis 8 Stunden beziehungsweise 3 bis 5 Tage. Der fehlende Posten wächst mit der Länge des Plans: die Lesezeit der Freigabe.
Häufige Fragen
Warum hängt die Freigabe an einer Datei und nicht am Lauf?
Weil eine Zustimmung, die beim Schreiben erteilt wird, nicht festhält, auf welchen Inhalt sie sich bezog. Die Signatur in der Freigabedatei hält es fest: sie ändert sich mit jedem Zeichen des Plans und liegt neben dem Namen, der sie eingetragen hat. Worauf sich eine Freigabe beziehen soll, steht in keinem der Texte, die diese Lektion zitiert – die Festlegung gehört ihr selbst.
Was mache ich, wenn das Schreibtor 10 zurückgibt?
An deinen Dateien ist nichts passiert: Schreiben war verlangt, die Freigabe nicht gültig. Den Grund nennt die Zeile freigabe_verweigert im Journal – keine Freigabedatei, kein Name in freigegeben_von, Signatur passt nicht, oder gilt_fuer deckt weniger Einträge als der Plan hat. Nur der dritte Fall braucht eine zweite Person.
Kann ein Plan mehrere Änderungen in derselben Datei enthalten?
Er kann, aber dieses Schreibtor führt nur die erste aus: es prüft, ob für die Datei schon eine Sicherung vorliegt, und hält jeden weiteren Eintrag darauf zurück. Für den zweiten Durchgang gibst du unter --sicherungen ein frisches Verzeichnis an.
Quellen
Quellen mit (ohne Link) wurden in diesem Lauf nicht geöffnet und stehen mit Publikation, Autor und Datum.
- Lawrence Hitches, „Claude for Technical SEO: Audits, Crawl Fixes & Schema“, 2. April 2026. Aufgabenliste und Logfile-Fragen. Ohne Stichprobengröße.
- Lawrence Hitches, „How to Use Claude Code for SEO Automation“, StudioHawk, 12. Mai 2026. Die fünf Zusicherungen, der Bewertungsgraph, beide Zeitangaben. Ohne Stichprobe, ohne Kostenabzug.
- Keyword.com, State of AI in SEO 2026, 1. Januar 2026, n = 97 auswertbare Antworten, selbst gemeldete Teilnahme: eine Person von 97 vollautomatisiert, 57 % „Quality not good enough“, 38 % KI bei technischen Audits; innerhalb der 79 %, die automatisierbare Aufgaben bewusst liegen lassen, nennen 40 % technisches SEO.
- Rich Voller, „Screaming Frog v24 MCP“, 26. Mai 2026, aktualisiert 12. Juni 2026. 500 URLs in 20 Sekunden bei eingestellter Grenze von 1 URL pro Sekunde; ein Fall, eine Website, keine Versionsangabe.
- SE Ranking, „Claude AI vs. ChatGPT: Which AI Tool is Better for SEO?“, Yulia Deda, 7. Juli 2025, aktualisiert 24. Juni 2026. JSON-LD 1 gegen 5 nicht kritische Beanstandungen, vier Aufgaben, ohne Stichprobengröße.
- ContextBolt, „Claude SEO Experiment: A Week of Running My Real SEO“, 23. Juni 2026. Selbstversuch auf einer einzigen Website über eine Woche, ohne Vergleichsgruppe; der Autor ist nur mit „David“ angegeben.
- Screaming Frog, Dokumentation zu Vektor-Einbettungen für das Redirect-Mapping, Stand 20. Oktober 2025: Standardschwelle 0,95. (ohne Link)
- Niko Alho, „How to Automate Internal Linking with AI Embeddings“, 20. Mai 2026: Band 0,78 bis 0,85, manuelle Verlinkung kippt bei 200 bis 500 Seiten. Ohne Stichprobenangabe. (ohne Link)
- Google Search Central, Spam-Richtlinien zu scaled content abuse, Stand 28. August 2026. Bestimmung über den Zweck, ohne Seitenzahl. (ohne Link)
- Autocomplete-Erhebung dieses Kurses, 20. September 2026: der Stamm
claude code seogab acht Vorschläge zurück, fünf mit einem Artefakt im Namen. Vorhandensein, nie Menge; eine Volumenangabe mit Urheber ließ sich nicht finden. - Skript, Eingabedateien, Plan, Freigabedatei und Sitzung sind original für diese Lektion; die Ausgabe hängt allein von den hier abgedruckten Dateien ab. Die Eingabedateien sind erfunden.
Weiter im Kurs
- Frühere Lektionen des Levels: Was Claude im SEO leisten kann – und was nicht und Claude Skills für SEO: installieren, nutzen, selbst schreiben.
- Level: SEO mit KI: KI als Arbeitswerkzeug. Das Level wächst Lektion für Lektion – welche Seiten schon online sind, steht in der Übersicht dort.
- Kursverzeichnis: Der kostenlose SEO-Kurs von doctor-seo.net