Compare commits

..

No commits in common. "0a4cd53b711e4b9ca2fba761c8effc65cc874b75" and "a7786aff305ba5c5689fceeed735056ce0cf75a0" have entirely different histories.

View file

@ -1,430 +0,0 @@
# 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:
- `claude` ist auf PATH (oder `CLAUDE_PATH` zeigt auf die CLI)
- `jq` ist installiert (`jq --version` gibt etwas aus)
- `python3` ist 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_id` im
> `--output-format json`-Result. [...] Per Default bleibt die ID beim
> `--resume` stabil — die Session wird in der existierenden JSONL
> fortgeschrieben."
Wir nehmen also an:
1. Der erste `claude -p` Call gibt ein JSON-Objekt zurück, in dem `.session_id`
direkt auf Top-Level liegt (nicht unter `.result.session_id` oder ähnlichem).
2. Ein zweiter Call mit `--resume <id>` gibt dieselbe `session_id` zurück.
### Kommandos
```bash
# 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 json` lädt die
> existierende Session und hängt `<neue-msg>` als neuen User-Turn an."
Wir nehmen an:
1. Claude "erinnert" sich nach einem `--resume` an den Inhalt der vorherigen
Turns dieser Session.
2. Der `--resume`-Call schlägt nicht stillschweigend fehl (z. B. mit einer leeren
Antwort oder einer neuen, unverbundenen Session), wenn die `session_id` gültig
ist.
3. Das Muster `--resume "$session_id" --output-format json --bare` ist ohne
weitere Flags ausführbar (kein `--verbose` o. ä. erforderlich).
### Kommandos
```bash
# 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ür `stream-json` müssen praktisch immer `--verbose` und
> `--include-partial-messages` mitgegeben werden, sonst gibt es keine
> Text-Deltas, nur den Final-`result`-Block."
Wir nehmen an:
1. Mit `--output-format stream-json --verbose --include-partial-messages` erscheinen
tatsächlich Zeilen mit `"type":"stream_event"` im Output.
2. Mindestens ein `stream_event` hat `.event.delta.type == "text_delta"` mit einem
nicht-leeren `.event.delta.text`.
3. Die letzte (oder eine der letzten) Zeilen hat `"type":"result"`.
4. Ohne `--include-partial-messages` erscheint nur `"type":"result"` (keine
`stream_event`-Zeilen).
### Kommandos
```bash
# 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 einen `usage`-Block nach
> Anthropic-API-Format mit `cache_read_input_tokens`, `cache_creation_input_tokens`,
> `input_tokens`, `output_tokens`."
> "Die **genauen JSON-Pfade** sollten aber empirisch geprüft werden —
> wahrscheinlich `result.usage.cache_read_input_tokens` oder top-level
> `usage.cache_read_input_tokens`."
Wir nehmen an:
1. Der `usage`-Block ist im JSON-Output vorhanden (irgendwo im Baum).
2. Er enthält mindestens die Felder `input_tokens` und `output_tokens`.
3. Nach einem `--resume`-Call (zweiter Turn) erscheint `cache_read_input_tokens`
mit einem Wert größer 0.
### Kommandos
```bash
# 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.