Manual probe script covering session_id field path, --resume flow, stream-json event types, and usage block field names. Must be executed and filled in before Phase 2 release. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
12 KiB
CLI Feature Probe — DisClaw
Manuelles Sanity-Check-Script für kritische Claude Code CLI-Annahmen. Muss vor Phase-2-Freigabe ausgeführt und ausgefüllt werden.
Stand: [Datum der letzten Ausführung]
CLI-Version: [Ausgabe von claude --version]
Kontext
Dieses Dokument deckt die vier Annahmen ab, die in docs/cli-feature-answers.md
als "Unsicherheit: Mittel" oder "empirisch noch nicht bestätigt" markiert sind.
Jede Probe muss in einer sauberen Shell-Session ausgeführt werden (keine
gespeicherten Umgebungsvariablen aus vorherigen Probes).
Voraussetzungen:
claudeist auf PATH (oderCLAUDE_PATHzeigt auf die CLI)jqist installiert (jq --versiongibt etwas aus)python3ist installiert (nur für Probe 3, als Fallback-Pretty-Printer)- Aktiver Claude Pro/Max-Account (die CLI benötigt eine Subscription, keinen separaten API-Key)
Probe 1: session_id Top-Level-Position und Stabilität über Resume
Hypothese
Aus cli-feature-answers.md Abschnitt 2:
"Die Session-ID liegt als Top-Level-Feld
session_idim--output-format json-Result. [...] Per Default bleibt die ID beim--resumestabil — die Session wird in der existierenden JSONL fortgeschrieben."
Wir nehmen also an:
- Der erste
claude -pCall gibt ein JSON-Objekt zurück, in dem.session_iddirekt auf Top-Level liegt (nicht unter.result.session_idoder ähnlichem). - Ein zweiter Call mit
--resume <id>gibt dieselbesession_idzurück.
Kommandos
# Schritt 1: Erste Session starten und session_id extrahieren
id1=$(claude -p "antworte mit dem wort ALPHA und sonst nichts" \
--output-format json --bare \
| tee /tmp/probe1-run1.json \
| jq -r '.session_id')
echo "id1=$id1"
# Schritt 2: Session fortsetzen und id aus dem Response lesen
id2=$(claude -p "antworte mit dem wort BETA und sonst nichts" \
--resume "$id1" \
--output-format json --bare \
| tee /tmp/probe1-run2.json \
| jq -r '.session_id')
echo "id2=$id2"
# Schritt 3: Vergleich
if [ "$id1" = "$id2" ]; then
echo "PASS: IDs sind gleich"
else
echo "ABWEICHUNG: id1=$id1 id2=$id2"
fi
# Schritt 4: Komplettes JSON-Top-Level zur Orientierung
echo "--- Top-Level-Felder Run 1 ---"
jq 'keys' /tmp/probe1-run1.json
echo "--- Top-Level-Felder Run 2 ---"
jq 'keys' /tmp/probe1-run2.json
Erwartete Ausgabe
id1=<uuid>
id2=<uuid> ← gleiche UUID wie id1
PASS: IDs sind gleich
--- Top-Level-Felder Run 1 ---
[
"result",
"session_id",
"type",
...
]
--- Top-Level-Felder Run 2 ---
[
"result",
"session_id",
"type",
...
]
session_id erscheint direkt in keys (Top-Level), nicht nur tief im Baum.
Die UUID ist in beiden Runs identisch.
Tatsächliche Ausgabe
[NOCH NICHT AUSGEFÜLLT — Bitte vor Phase-2-Freigabe ausfüllen]
Status
- Verifiziert
- Abweichung gefunden:
Notizen
Falls id2 != id1: Defensiv-Strategie aus cli-feature-answers.md Abschnitt 2
ist relevant — nach jedem Run die session_id aus dem Response-JSON lesen und
in der DB aktualisieren, unabhängig davon ob sie sich geändert hat.
Falls .session_id nicht auf Top-Level liegt: jq 'path(.. | strings | select(test("^[0-9a-f-]{36}$")))' /tmp/probe1-run1.json zeigt den tatsächlichen
Pfad.
Probe 2: --resume Flow — Kontext-Kontinuität
Hypothese
Aus cli-feature-answers.md Abschnitt 1:
"
claude -p "<neue-msg>" --resume <session-id> --output-format jsonlädt die existierende Session und hängt<neue-msg>als neuen User-Turn an."
Wir nehmen an:
- Claude "erinnert" sich nach einem
--resumean den Inhalt der vorherigen Turns dieser Session. - Der
--resume-Call schlägt nicht stillschweigend fehl (z. B. mit einer leeren Antwort oder einer neuen, unverbundenen Session), wenn diesession_idgültig ist. - Das Muster
--resume "$session_id" --output-format json --bareist ohne weitere Flags ausführbar (kein--verboseo. ä. erforderlich).
Kommandos
# Schritt 1: Session mit einem Geheimnis starten
run1=$(claude -p "Merke dir folgendes Codewort für diese Session: XYLOPHON42. Bestätige nur mit OK." \
--output-format json --bare)
sid=$(echo "$run1" | jq -r '.session_id')
echo "session_id=$sid"
echo "Antwort Run 1: $(echo "$run1" | jq -r '.result')"
# Schritt 2: In derselben Session das Geheimnis abfragen
run2=$(claude -p "Was war das Codewort, das du dir gemerkt hast?" \
--resume "$sid" \
--output-format json --bare)
echo "Antwort Run 2: $(echo "$run2" | jq -r '.result')"
# Schritt 3: Prüfen ob das Codewort im Response vorkommt
if echo "$run2" | jq -r '.result' | grep -q "XYLOPHON42"; then
echo "PASS: Codewort in Antwort gefunden"
else
echo "ABWEICHUNG: Codewort fehlt in der Antwort"
fi
Erwartete Ausgabe
session_id=<uuid>
Antwort Run 1: OK
Antwort Run 2: Das Codewort war XYLOPHON42.
PASS: Codewort in Antwort gefunden
Tatsächliche Ausgabe
[NOCH NICHT AUSGEFÜLLT — Bitte vor Phase-2-Freigabe ausfüllen]
Status
- Verifiziert
- Abweichung gefunden:
Notizen
Falls das Codewort fehlt: Prüfe ob --resume die richtige Session lädt.
ls ~/.claude/projects/ zeigt alle gespeicherten Projekt-Slugs. Das Verzeichnis
für den aktuellen cwd hat den Slug <cwd-Pfad-mit-Bindestrichen>.
Falls --resume + --output-format json + --bare zusammen einen Exit-Code
ungleich 0 erzeugen: Flags einzeln testen, um die Inkompatibilität einzugrenzen.
Probe 3: stream-json Event-Typen
Hypothese
Aus cli-feature-answers.md Abschnitt 4:
"Relevante Typen:
system,assistant,user,stream_event,result." "Text-Deltas landen unter.event.delta.type == "text_delta"mit.event.delta.text." "Fürstream-jsonmüssen praktisch immer--verboseund--include-partial-messagesmitgegeben werden, sonst gibt es keine Text-Deltas, nur den Final-result-Block."
Wir nehmen an:
- Mit
--output-format stream-json --verbose --include-partial-messageserscheinen tatsächlich Zeilen mit"type":"stream_event"im Output. - Mindestens ein
stream_eventhat.event.delta.type == "text_delta"mit einem nicht-leeren.event.delta.text. - Die letzte (oder eine der letzten) Zeilen hat
"type":"result". - Ohne
--include-partial-messageserscheint nur"type":"result"(keinestream_event-Zeilen).
Kommandos
# Schritt 1: Stream MIT partial messages — alle Event-Typen anzeigen
echo "=== MIT --include-partial-messages ==="
claude -p "schreibe genau drei Saetze ueber den Mond" \
--output-format stream-json \
--verbose \
--include-partial-messages \
--bare \
| tee /tmp/probe3-stream.ndjson \
| jq -c '{type, subtype: .subtype?, event_type: .event.type?, delta_type: .event.delta.type?}'
# Schritt 2: Welche type-Werte kommen vor?
echo "=== Vorkommende type-Werte ==="
jq -r '.type' /tmp/probe3-stream.ndjson | sort | uniq -c | sort -rn
# Schritt 3: Einen text_delta-Block vollständig anzeigen
echo "=== Erster text_delta-Block ==="
jq -c 'select(.type == "stream_event" and .event.delta.type? == "text_delta") | {type, text: .event.delta.text}' \
/tmp/probe3-stream.ndjson | head -3
# Schritt 4: Stream OHNE partial messages zum Vergleich
echo "=== OHNE --include-partial-messages ==="
claude -p "antworte mit einem einzigen Wort: HALLO" \
--output-format stream-json \
--verbose \
--bare \
| jq -c '.type'
Erwartete Ausgabe
=== MIT --include-partial-messages ===
{"type":"system","subtype":"init","event_type":null,"delta_type":null}
{"type":"stream_event","subtype":null,"event_type":"content_block_delta","delta_type":"text_delta"}
... (mehrere text_delta-Zeilen)
{"type":"result","subtype":null,"event_type":null,"delta_type":null}
=== Vorkommende type-Werte ===
42 stream_event
3 assistant
2 user
1 system
1 result
=== Erster text_delta-Block ===
{"type":"stream_event","text":"Der "}
{"type":"stream_event","text":"Mond "}
{"type":"stream_event","text":"ist "}
=== OHNE --include-partial-messages ===
"system"
"result"
Tatsächliche Ausgabe
[NOCH NICHT AUSGEFÜLLT — Bitte vor Phase-2-Freigabe ausfüllen]
Status
- Verifiziert
- Abweichung gefunden:
Notizen
Falls neue, unbekannte type-Werte auftauchen: In die Tabelle eintragen.
Der DisClaw-Parser in Phase 5 (src/agent/runner.ts) muss unknown types
silently skippen statt zu crashen.
Falls stream_event fehlt auch mit --include-partial-messages: Prüfe die
CLI-Version. Möglicherweise ist der Flag in der installierten Version noch
nicht vorhanden. claude --help | grep partial prüft das.
Falls das Feld-Layout der Text-Deltas abweicht: Den tatsächlichen Pfad mit
jq 'select(.type == "stream_event") | paths' /tmp/probe3-stream.ndjson | head -20
ermitteln und hier dokumentieren.
Probe 4: usage-Block Pfade im JSON-Output
Hypothese
Aus cli-feature-answers.md Abschnitt 7:
"Der
--output-format json-Response enthält einenusage-Block nach Anthropic-API-Format mitcache_read_input_tokens,cache_creation_input_tokens,input_tokens,output_tokens." "Die genauen JSON-Pfade sollten aber empirisch geprüft werden — wahrscheinlichresult.usage.cache_read_input_tokensoder top-levelusage.cache_read_input_tokens."
Wir nehmen an:
- Der
usage-Block ist im JSON-Output vorhanden (irgendwo im Baum). - Er enthält mindestens die Felder
input_tokensundoutput_tokens. - Nach einem
--resume-Call (zweiter Turn) erscheintcache_read_input_tokensmit einem Wert größer 0.
Kommandos
# Schritt 1: Ersten Run durchführen und usage-Struktur suchen
echo "=== usage-Struktur im ersten Run ==="
run1=$(claude -p "erklaere in drei saetzen was eine linked list ist" \
--output-format json --bare)
echo "$run1" | tee /tmp/probe4-run1.json | python3 -m json.tool
# Schritt 2: Alle Pfade finden die "token" enthalten
echo "=== Pfade mit 'token' im Namen ==="
jq '[path(.. | numbers?) | select(. | map(tostring) | join(".") | test("token"; "i"))]' \
/tmp/probe4-run1.json
# Schritt 3: usage direkt ansprechen (alle möglichen Pfade testen)
echo "=== usage top-level ==="
jq '.usage // "NICHT VORHANDEN"' /tmp/probe4-run1.json
echo "=== result.usage ==="
jq '.result.usage // "NICHT VORHANDEN"' /tmp/probe4-run1.json
# Schritt 4: Zweiten Run mit Resume — cache_read prüfen
sid=$(jq -r '.session_id' /tmp/probe4-run1.json)
echo "=== usage im Resume-Run (cache_read erwartet) ==="
claude -p "fasse deine Erklaerung in einem Satz zusammen" \
--resume "$sid" \
--output-format json --bare \
| tee /tmp/probe4-run2.json \
| jq '.usage // .result.usage // "usage nicht gefunden"'
# Schritt 5: cache_read_input_tokens im zweiten Run prüfen
echo "=== cache_read_input_tokens in Run 2 ==="
jq '(.usage // .result.usage).cache_read_input_tokens // "FELD FEHLT"' \
/tmp/probe4-run2.json
Erwartete Ausgabe
=== usage top-level ===
{
"input_tokens": 42,
"output_tokens": 87,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 0
}
=== result.usage ===
"NICHT VORHANDEN" ← oder vice versa, je nach tatsächlichem Pfad
=== usage im Resume-Run (cache_read erwartet) ===
{
"input_tokens": 12,
"output_tokens": 23,
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 412 ← größer 0 beim Resume
}
=== cache_read_input_tokens in Run 2 ===
412
Tatsächliche Ausgabe
[NOCH NICHT AUSGEFÜLLT — Bitte vor Phase-2-Freigabe ausfüllen]
Status
- Verifiziert
- Abweichung gefunden:
Notizen
Falls cache_read_input_tokens auch im zweiten Run 0 ist: Das deutet darauf
hin, dass Prompt-Caching für kurze Prompts nicht greift (Anthropic cached erst
ab ca. 1024 Tokens). Mit einem längeren Prompt testen oder nach mehreren
Turns prüfen.
Falls der usage-Block gar nicht vorhanden ist: Das wäre ein signifikantes
Problem für Phase 4/5 (Token-Stats pro Turn). Als Fallback: null in der
agent_runs-Tabelle tolerieren.
Tatsächlichen Pfad hier eintragen und in src/agent/runner.ts als Konstante
definieren, damit er genau einmal im Code steht und leicht geändert werden kann.
Ausfüll-Checkliste
| Probe | Ausgeführt von | Datum | Status |
|---|---|---|---|
| 1: session_id Top-Level | [ ] | ||
| 2: --resume Flow | [ ] | ||
| 3: stream-json Events | [ ] | ||
| 4: usage-Block Pfade | [ ] |
Alle vier Probes müssen den Status "Verifiziert" tragen, bevor Phase 2
(Session-Resume-Implementation) als freigegeben gilt. Abweichungen müssen
vor dem Phase-2-Start in docs/cli-feature-answers.md nachgezogen werden.