Agent skill

Claude Session Router Debugger

by weave-os in weave-os/router

Correlates a Claude Code session's local transcript with a model router's production cloud logs to explain why a specific response rendered the way it did.

Apache-2.0Auto-check passedDevelopment

Install Claude Session Router Debugger

skills CLI
$ npx skills add weave-os/router --skill debug-claude-session -a claude-code

Project install by default; add -g for ~/.claude/skills/.

GitHub CLI
$ gh skill install weave-os/router debug-claude-session --agent claude-code

Project scope by default; add --scope user for a personal install. Needs GitHub CLI 2.90.0 or later (public preview).

Manual copy
$ git clone --depth 1 https://github.com/weave-os/router.git skills-src && mkdir -p .claude/skills && cp -r skills-src/.agents/skills/debug-claude-session .claude/skills/debug-claude-session && rm -rf skills-src

Use ~/.claude/skills/ instead of .claude/skills for a personal install. The folder must contain SKILL.md.

Claude Code skills documentation · loads skills from .claude/skills/

Facts

Skill name
debug-claude-session
GitHub stars
5.6k
Token cost
~2.9k tokens
SKILL.md length
933 words
Files
2
Skills in repo
19
Repo updated
First seen
Licence
Apache-2.0

At a glance

Correlates a Claude Code session's local transcript with a model router's production cloud logs to explain why a specific response rendered the way it did.

  • Works in 7 steps: Locate the local transcript → Extract the assistant blocks showing the… → Decode block internals → …
  • Investigating why a specific Claude Code response rendered incorrectly or empty
  • SKILL.md covers Setup: Cloud deployment config, Critical gotchas (read first), Workflow and Example: Empty thinking block, plus 1 more section
  • Calls python3, git and gcloud

What it does

Given a session ID, the skill treats the local transcript file as ground truth for what the client actually rendered, the cloud logs as confirmation of what the upstream model served, and the router's internal translation code as the explanation for why the wire shape looks that way. Before starting, it has you create a gitignored deployment config file naming the cloud provider, service, project and region, so log queries can be constructed; if that file is missing, the agent prompts for the details and walks through creating it.

Several gotchas guide the investigation: a streamed turn is split across multiple assistant lines sharing one message id and model, so the full turn has to be reconstructed by collecting every line with that id; block fields like a signature or input often encode provider-specific state such as encrypted reasoning that must be decoded before dismissing a block as empty; and cloud logs are structured, so queries filter on specific JSON fields rather than free text. Because request IDs are often absent, the skill correlates cloud log entries to the transcript by a tight UTC time window plus the served model name instead.

When your agent uses it

  • Investigating why a specific Claude Code response rendered incorrectly or empty
  • Correlating a session's local transcript with the router's cloud logs
  • Debugging a wire-format or streaming issue for a session routed through the router

Example prompts

  • “This session rendered an empty response, correlate it with the cloud logs and explain why.”
  • “Why did this session's assistant turn look truncated on the client?”
  • “Set up the cloud deployment config so I can debug sessions against our logs.”

Requirements

  • Access to the router's production cloud logs
  • A local Claude Code transcript for the session

Workflow steps

7 steps, taken from the step headings in SKILL.md.

  1. Locate the local transcript
  2. Extract the assistant blocks showing the symptom
  3. Decode block internals
  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

What it can do on your machine

Read from SKILL.md and the folder at commit 21c83ab. It shows what the files ask for, not the result of running them.

  • Tool permissions

    Pre-approves nothing: there is no allowed-tools line, so your agent's usual permission prompts apply.

    From allowed-tools in the SKILL.md frontmatter.

  • Runs code

    Shell commands in SKILL.md call:

    • python3
    • git
    • gcloud

    From the folder's file list and the shell code blocks in SKILL.md.

  • Network

    No URLs in SKILL.md. Its commands use git and gcloud, which can reach the network depending on how they are called.

    From URLs in SKILL.md, links to its own repository left out.

  • Credentials

    Names no API keys, tokens, secrets or passwords.

    From names ending in _API_KEY, _TOKEN, _SECRET, _KEY or _PASSWORD in SKILL.md.

Context cost

Claude Session Router Debugger loads about 2.9k tokens when it runs. Until then it costs about 90 tokens; SKILL.md has 933 words of instructions outside code blocks.

Always · name and description, kept in context so the agent knows when to use it
~90
When it runs · the whole SKILL.md, loaded when a task matches
~2.9k

Estimates: characters ÷ 4, the usual rule of thumb; real counts depend on the model's tokenizer. Scripts and assets cost tokens only if the agent reads them.

Safety

Auto-check passed

The automated check found no risky patterns in SKILL.md.

Automated static check — not a guarantee. Review scripts before installing. It scans the text of SKILL.md for risky patterns (piping downloads into a shell, reading credential files, hidden Unicode, destructive commands); files beside SKILL.md are not scanned.

SKILL.md

The full file from weave-os/router at commit 21c83ab, republished under its Apache-2.0 licence (© weave-os). 933 words, ~2,866 tokens.

Download SKILL.mdSave it as .claude/skills/debug-claude-session/SKILL.md (or your agent's skills folder). This skill also uses 1 other file; get the full folder from GitHub.
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.

Debugging a Claude Code session

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.

Setup: Cloud deployment config

Before starting, create a gitignored config file with your deployment's cloud logging details:

bash
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 gitignored

If .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.

Critical gotchas (read first)

  • The transcript is the source of truth for rendering. What the client showed is exactly the assistant message.content blocks in the .jsonl — not what you assume the model emitted. Always inspect block contents (decode fields, check lengths) before concluding something is "empty" or "corrupt".
  • Streaming splits one logical turn across multiple assistant lines. Each line may hold a single block; they share a message.id and message.model. Reconstruct the full turn by collecting all lines with the same message.id.
  • Block content may carry encoded state. Fields like signature, id, and input often encode provider-specific state (e.g. encrypted reasoning). Decode and inspect before skipping or dismissing a block.
  • Cloud logs are structured, not free text. Filter queries on specific JSON fields (e.g. jsonPayload.decision_model, jsonPayload.message). The exact field names depend on the router's logging schema — ask if unsure.
  • Correlate by time + model, not request id. The local transcript has UTC timestamps (timestamp field); request IDs are often absent. Use a tight UTC window around transcript timestamps plus the served decision_model to find matching cloud log entries.

Workflow

- [ ] 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
1. Locate the local transcript
bash
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.

2. Extract the assistant blocks showing the symptom

Each line is a typed event. Scan for type: "assistant" entries. Each carries:

  • message.id — groups lines from the same logical turn.
  • message.model — the served model (e.g. gpt-5.5, claude-opus-4-8).
  • message.stop_reason — how the turn ended (end_turn, tool_use, max_tokens).
  • message.content[] — list of blocks (text, thinking, tool_use, tool_result).

Adapt this template to search for your symptom (empty blocks, missing content, unexpected stop_reason, etc.):

bash
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)}")
EOF

Print model, stop_reason, block type/length — enough to see the pattern at a glance.

3. Decode block internals

Don't assume a short or empty field is meaningless. Many blocks carry encoded provider state. Inspect the actual bytes:

bash
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 = base64.b64decode(block["signature"])
            print(f"  signature (decoded): {decoded[:160]!r}")
        except Exception as e:
            print(f"  signature (decode failed): {e}")
EOF

This reveals what's actually inside. Look for:

  • Encrypted state (e.g. encrypted_content, enc fields) that must round-trip to the upstream.
  • Embedded IDs (e.g. OpenAI reasoning signatures embedded in tool_use.id).
  • Redundant carriers — the same state might be duplicated across blocks.
4. Identify model + provider from the transcript

From the message.model field (step 2), note the served model. This tells you:

  • Which upstream (e.g. gpt-* → OpenAI, claude-* → Anthropic, gemini-* → Google).
  • Which translation code handles it (e.g. gpt-* Responses → internal/translate/responses_to_anthropic_writer.go).

Also note message.id (ids are unique per response; the prefix marks the path):

  • msg_responses_* → OpenAI Responses API path (streaming).
  • msg_translated_* → OpenAI chat-completions path (router-generated id; upstream-provided ids pass through unchanged).
  • msg_01... (Anthropic-native id) → Anthropic Messages path (passthrough).
Show full SKILL.md (380 more words)Show less
5. Fetch cloud logs for the matching UTC window

Extract the timestamp range from the transcript:

bash
python3 - <<'EOF'
import json
with open("<path>/<SESSION_ID>.jsonl") as f:
    lines = [json.loads(line) for line in f if json.loads(line).get("timestamp")]
    if lines:
        print(f"UTC window: {lines[0]['timestamp']} to {lines[-1]['timestamp']}")
EOF

Then use your cloud logging tool with the config from .deployment.json:

bash
# Example for gcloud/GCP. Adapt to your cloud provider:
gcloud logging read \
  'resource.type="cloud_run_revision" AND resource.labels.service_name="router" AND timestamp>="2026-06-10T01:02:00Z" AND timestamp<="2026-06-10T01:08:00Z"' \
  --project <project_id> --limit 20 --format=json \
  > /tmp/cloud_logs.json

Filter for the served model to narrow results:

bash
python3 - <<'EOF'
import json
with open("/tmp/cloud_logs.json") as f:
    for entry in json.load(f):
        payload = entry.get("jsonPayload", {})
        # Adapt filter to your log schema:
        if payload.get("decision_model") == "gpt-5.5":
            print(json.dumps({
                "timestamp": entry.get("timestamp"),
                "message": payload.get("message"),
                "decision_model": payload.get("decision_model"),
                "decision_provider": payload.get("decision_provider"),
                "stop_reason": payload.get("resp_stop_reason")
            }))
EOF
6. Correlate transcript + cloud logs

Match entries from steps 2 and 5 by:

  • Timestamp (transcript timestamp ≈ cloud log timestamp within ~1-2 seconds).
  • Model (transcript message.model == cloud log decision_model).

Once matched, the cloud log entry tells you:

  • Which model/provider actually served the turn (proof that the transcript came from that upstream).
  • How the turn was reconciled (e.g. stop_reason demotions, tool_use handling, usage accounting).
7. Trace to the translation code

Now that you've identified the model/provider path (step 4) and confirmed it in cloud logs (step 6), open the relevant translation file in internal/translate/:

  • responses_to_anthropic_writer.go — OpenAI Responses API.
  • stream.go + emit_anthropic.go — OpenAI-compatible chat.
  • gemini_stream.go + emit_gemini.go — Google Generative Language.
  • emit_anthropic.go — Anthropic passthrough (mostly copy).

Find the emitter function that produces the block shape you're seeing:

  • emitContentBlockStartThinking, emitContentBlockDeltaThinking — thinking blocks.
  • emitContentBlockStartTool, emitContentBlockDeltaTool — tool_use blocks.
  • Similar for text, tool_result, etc.

Read backward from the emitter to the upstream event that triggered it. This is where the "why" lives.

Example: Empty thinking block

  1. Local transcript shows: thinking: "" but signature: "<1500 chars>" (step 3).
  2. Cloud log shows: decision_model=gpt-5.5, decision_provider=openai, stop_reason=tool_use (step 5-6).
  3. Translation path: OpenAI Responses → responses_to_anthropic_writer.go (step 4).
  4. Trace upstream event: response.reasoning_summary_text.delta with empty delta but populated item on response.output_item.added (step 7).
  5. Emitter: handleReasoningDelta → emitContentBlockDeltaThinking("", ...) which does nothing (empty delta skipped), but the thinking block was already opened by handleOutputItemAdded (line 279).
  6. Root cause: OpenAI returned a reasoning item with encrypted_content but under reasoning.summary:"auto", no summary text. The block can't be skipped because the signature must round-trip to the next turn.

Notes

  • The transcript records the post-translation Anthropic Messages shape — it never shows raw upstream Responses/Gemini events. Reconstruct upstream behavior by reading the emitter code + cloud logs together.
  • message.id groups lines from the same logical turn; reconstruct the full turn by collecting all lines sharing an id.
  • If the cloud log query returns nothing or the wrong model, expand the UTC window or check the model name spelling.
  • To reproduce a translation artifact locally (without prod), use the sibling test-claude-locally skill to run the router with a mock upstream emitting the exact wire shape you're investigating.

© weave-os, Apache-2.0. Rendered from Markdown: HTML in the file is shown as text, images as links, and headings moved down two levels. Raw file

Files

SKILL.md and 1 other file in .agents/skills/debug-claude-session of weave-os/router.

  • SKILL.md
  • .deployment.json.example

Open the folder on GitHubat commit 21c83ab

Compare with similar skills

Claude Session Router Debugger next to the 5 skills that share the most tags, products or categories with it. Stars are the repository's; “used in” counts other GitHub owners with a copy.

Claude Session Router Debugger compared with similar skills
SkillStarsUsed inTokensAuto-checkLicenceRepo updated
Claude Session Router Debugger this skillweave-os/router5.6k—~2.9kAutomated safety check: PassApache-2.0
Evlog Log Analyzerevloghq/evlog1.9k—~2.4kAutomated safety check: PassMIT
Logging Patternsdecebals/claude-code-java7501 repos~3.3kAutomated safety check: PassMIT
Mecatl Perf MCP Interpretationstacklok/mecatl218—~2.3kAutomated safety check: PassApache-2.0
NanoClaw Container Debuggingnanocoai/nanoclaw31k—~3.8kAutomated safety check: NotesMIT
LoopX Performance Diagnosisloopx-project/loopx6.2k—~880Automated safety check: PassApache-2.0

Similar skills

  • Evlog Log Analyzer

    evloghq/evlog

    Reads the structured wide-event logs that evlog writes to .evlog/logs/ so the agent can debug errors, find slow requests and explain what the app did.

    1.9k GitHub stars~2.4k tokensUpdated today
    DevelopmentAuto-check passed
  • Logging Patterns

    decebals/claude-code-java

    Java logging best practices with SLF4J, structured logging (JSON), and MDC for request tracing.

    750 GitHub starsUsed in 1 repo~3.3k tokens
    DevelopmentAuto-check passed
  • Guides reading mecatl's perf MCP data to find why a running harness is slow, leaking goroutines or growing in memory, using cheap reads before any CPU capture.

    218 GitHub stars~2.3k tokensUpdated today
    DevelopmentAuto-check passed
  • Troubleshooting guide for NanoClaw's containerized agents: where the logs are, how the two session databases show message flow, and how to raise the log level.

    31k GitHub stars~3.8k tokensUpdated yesterday
    DevelopmentAuto-check: notes
  • LoopX Performance Diagnosis

    loopx-project/loopx

    Profiles a slow command or runtime you own with the right profiler for its language, using uninstrumented baseline timings and keeping raw profiling evidence local and private.

    6.2k GitHub stars~880 tokensUpdated today
    DevelopmentAuto-check passed
  • Production Error Hunt

    different-ai/openwork

    Traces an opaque production error in an OpenWork build to its cause using local server logs and Sentry, names the regressing PR and files a report.

    24k GitHub stars~803 tokensUpdated today
    DevelopmentAuto-check passed

More from weave-os/router

All 19 skills in this repo
  • Stands up the Weave model router in Docker Compose and drives it with claude -p against a real or mocked upstream to reproduce and verify routing and streaming behavior.

    5.6k GitHub stars~3.1k tokensUpdated today
    Auto-check: notes
  • Local test harness for the Weave router: a docker compose stack plus codex exec runs that confirm how Codex requests are routed, translated and marked.

    5.6k GitHub stars~4.7k tokensUpdated today
    Auto-check: notes
  • PR Merge Ready

    weave-os/router

    Works through every review comment on a pull request in one pass, fixes what it can, escalates human decisions and keeps CI churn to a single push.

    5.6k GitHub stars~10k tokensUpdated today
    Auto-check passed
  • Installs a language server such as gopls, typescript-language-server, pyright or rust-analyzer and, with explicit confirmation, its underlying toolchain so the lsp tool can use it.

    5.6k GitHub stars~745 tokensUpdated today
    Auto-check: notes
  • Correlates a Codex CLI session's local transcript with a model router's production logs to explain why a reply rendered the way it did.

    5.6k GitHub stars~4.5k tokensUpdated today
    Auto-check: warnings
  • Shows when to answer a code question through a real language server instead of grep, covering definitions, references, hover, outlines, and errors.

    5.6k GitHub stars~794 tokensUpdated today
    Auto-check passed

Questions about Claude Session Router Debugger

What does Claude Session Router Debugger do?

Correlates a Claude Code session's local transcript with a model router's production cloud logs to explain why a specific response rendered the way it did. Given a session ID, the skill treats the local transcript file as ground truth for what the client actually rendered, the cloud logs as confirmation of what the upstream model served, and the router's internal translation code as the explanation for why the wire shape looks that way. Before starting, it has you create a gitignored deployment config file naming the cloud provider, service, project and region, so log queries can be constructed; if that file is missing, the agent prompts for the details and walks through creating it.

When should I use Claude Session Router Debugger?

Claude Session Router Debugger fits situations like: investigating why a specific Claude Code response rendered incorrectly or empty; correlating a session's local transcript with the router's cloud logs; debugging a wire-format or streaming issue for a session routed through the router.

How do I install Claude Session Router Debugger in Claude Code?

Run `npx skills add weave-os/router --skill debug-claude-session -a claude-code`. Or copy the skill folder (.agents/skills/debug-claude-session in weave-os/router) into .claude/skills/debug-claude-session in your project. Claude Code loads it when a task matches its description.

How do I install Claude Session Router Debugger in Codex?

Run `npx skills add weave-os/router --skill debug-claude-session -a codex`. Or copy the skill folder (.agents/skills/debug-claude-session in weave-os/router) into .agents/skills/debug-claude-session in your project. Codex loads it when a task matches its description.

Can I use Claude Session Router Debugger in Cursor, Gemini CLI or GitHub Copilot?

Cursor, Gemini CLI, GitHub Copilot and OpenCode also load SKILL.md folders. With the skills CLI, run `npx skills add weave-os/router --skill debug-claude-session -a cursor` (or -a gemini-cli, github-copilot or opencode for the others). To copy it by hand, put the folder in .cursor/skills/debug-claude-session, .gemini/skills/debug-claude-session, .github/skills/debug-claude-session and .opencode/skills/debug-claude-session in your project.

What does Claude Session Router Debugger need to run?

Going by SKILL.md and its folder, Claude Session Router Debugger needs the command-line tools its instructions call (python3, git and gcloud). Our summary lists: Access to the router's production cloud logs; A local Claude Code transcript for the session.

Does Claude Session Router Debugger access the network?

SKILL.md contains no URLs. Its commands use git, which can reach the network depending on how they are called. This is read from the text; nothing was executed.

Is Claude Session Router Debugger safe to install?

Our automated static check of SKILL.md found no risky patterns, such as piping downloads into a shell, reading credential files or hidden Unicode. It is not a guarantee. Review the folder before installing.

What licence does Claude Session Router Debugger use?

Claude Session Router Debugger is published under the Apache-2.0 licence (the repository's licence). It allows redistribution, so the full SKILL.md is shown on this page.

How many tokens does Claude Session Router Debugger use?

About 2.9k tokens (SKILL.md is roughly 11k characters). Agents keep only the skill's name and description in context until a task matches; then they load SKILL.md in full.

What are the alternatives to Claude Session Router Debugger?

Skills that share tags, products or a category with Claude Session Router Debugger: Evlog Log Analyzer (evloghq/evlog, 1.9k stars), Logging Patterns (decebals/claude-code-java, 750 stars), Mecatl Perf MCP Interpretation (stacklok/mecatl, 218 stars) and NanoClaw Container Debugging (nanocoai/nanoclaw, 31k stars). The comparison table on this page puts their stars, adoption, token cost, safety result and licence side by side.

Who maintains Claude Session Router Debugger?

weave-os (a GitHub organization) maintains it in weave-os/router, which has 5,575 GitHub stars. The repository holds 19 skills in this directory. The repository was last updated on October 7, 2026.

Source: weave-os/router on GitHub. Facts on this page come from the repository at the commit we read; the author's words are quoted as theirs.