debug-codex-session
Investigate a specific Codex CLI session by session ID — correlate the local rollout transcript (`~/.codex/sessions/YYYY/MM/DD/rollout-*-<SESSION_ID>.jsonl`)…
Investigate a specific Claude Code session by session ID — correlate the local transcript (`~/.claude/projects/...jsonl`) with the router's production logs to understand what the client rendered vs. what the upstream served. Use when given a session ID and asked "why did X
$ npx -y skills add workweave/router --skill debug-claude-session --agent claude-codeHow it fires
How this skill gets triggered: by you, by Claude, or both.
/debug-claude-sessionContext preview
The summary Claude sees to decide when to auto-load this skill.
Investigate a specific Claude Code session by session ID — correlate the local transcript (`~/.claude/projects/...jsonl`) with the router's production logs to understand what the client rendered vs. what the upstream served. Use when given a session ID and asked "why did X
name: debug-claude-session description: Investigate a specific Claude Code session by session ID — correlate the local transcript (`~/.claude/projects/...jsonl`) with the router's production logs to understand what the client rendered vs. what the upstream served. Use when given a session ID and asked "why did X render?" for a Claude Code conversation routed through the router.
Given a Claude Code **session ID**, pull the local transcript (what the client saw) and the corresponding production cloud logs (which model/provider served it), then correlate them to understand the wire-format translation. The local `.jsonl` is ground truth for *what rendered*; the cloud logs confirm *what the upstream sent*; the `internal/translate` code explains *why the wire shape looks that way*.
Before starting, create a gitignored config file with your deployment's cloud logging details:
cat > .claude/skills/debug-claude-session/.deployment.json <<'EOF'
{
"cloud_provider": "gcp",
"project_id": "your-project-id",
"region": "us-central1",
"service_name": "router",
"log_command_template": "gcloud logging read ... --project {project_id} --format=json"
}
EOF
git add .claude/skills/debug-claude-session/.deployment.json.example
# .deployment.json itself should be gitignoredIf `.deployment.json` is missing, the agent will prompt you for these details and walk you through creating it. The file is gitignored and contains no secrets — it's just the service/project/region names needed to construct cloud log queries.
- [ ] 1. Locate the local transcript - [ ] 2. Extract the assistant blocks showing the symptom - [ ] 3. Decode block internals (signatures, ids, sizes) - [ ] 4. Identify model + provider from the transcript - [ ] 5. Fetch cloud logs for the matching UTC window - [ ] 6. Correlate transcript + cloud logs - [ ] 7. Trace to the translation code in internal/translate
find ~/.claude/projects -name '<SESSION_ID>*' -type f
You get `<path>/<SESSION_ID>.jsonl` (the transcript, one JSON object per line) and a sibling `<SESSION_ID>/` directory (tool-output spillover). The `.jsonl` file is the ground truth. `wc -l` it — typical sessions are tens to hundreds of lines.
Each line is a typed event. Scan for `type: "assistant"` entries. Each carries:
Adapt this template to search for your symptom (empty blocks, missing content, unexpected stop_reason, etc.):
python3 - <<'EOF'
import json
with open("<path>/<SESSION_ID>.jsonl") as f:
for i, line in enumerate(f):
try:
o = json.loads(line)
except:
continue
if o.get("type") != "assistant":
continue
msg = o.get("message", {})
# Adapt this filter to your symptom:
for block in msg.get("content", []):
if isinstance(block, dict) and block.get("type") == "thinking":
thinking_text = block.get("thinking", "")
signature = block.get("signature", "")
if thinking_text == "": # Your condition here
print(f"line {i+1}: model={msg.get('model')} "
f"stop_reason={msg.get('stop_reason')} "
f"thinking_len={len(thinking_text)} "
f"signature_len={len(signature)}")
EOFPrint `model`, `stop_reason`, block type/length — enough to see the pattern at a glance.
Don't assume a short or empty field is meaningless. Many blocks carry encoded provider state. Inspect the actual bytes:
python3 - <<'EOF'
import json, base64
line_num = <LINE> # From step 2
with open("<path>/<SESSION_ID>.jsonl") as f:
o = json.loads(f.readlines()[line_num - 1])
for block in o["message"].get("content", []):
block_type = block.get("type")
print(f"=== {block_type} ===")
# Print all fields and their lengths:
for key, val in block.items():
if isinstance(val, str):
print(f" {key}: len={len(val)} head={val[:80]!r}")
else:
print(f" {key}: {type(val).__name__} {val!r}")
# If any field looks base64-encoded, try decoding:
if "signature" in block and block["signature"]:
try:
decoded =Model router for agentic systems. Routes every prompt to the right model in <50ms. Cut costs 40-70% with just an endpoint change.
Repo: workweave/router
Investigate a specific Codex CLI session by session ID — correlate the local rollout transcript (`~/.codex/sessions/YYYY/MM/DD/rollout-*-<SESSION_ID>.jsonl`)…
Fetches feedback from a GitHub PR — review-thread comments, ad-hoc PR comments (including those posted as a review's body without a thread), and bot/advisory…
Run the Weave router locally in docker compose and drive it with `claude -p` to reproduce and verify routing/translation behavior for a specific upstream model…
Run the Weave router locally in docker compose and drive it with `codex exec` to reproduce and verify routing/translation/marker behavior for Codex's Responses…
Install language servers (gopls, typescript-language-server, pyright, rust-analyzer) and their prerequisite toolchains (Go, Node/npm, rustup) so the lsp tool…
Recipes for the lsp tool — resolving where a symbol is defined, every place it is used, type signatures and docs, file outlines, and compiler/type errors…