
I created a mechanism to talk to autonomous AI agents in real-time for debugging from Claude Code
This page has been translated by machine translation. View original
Introduction
When developing the autonomous AI agent "Ruby", post-deployment verification becomes the biggest bottleneck. I was repeating a manual cycle of writing code, pushing it, waiting for the container to rebuild, sending messages via Discord to check responses, and examining logs to identify problems.
"What if Claude Code (the development AI) could directly talk to Ruby (the production AI) for debugging? Wouldn't that dramatically speed up the development cycle?"
In this article, I'll introduce how I built a system that combines three channels for Claude Code to converse and debug with Ruby in real-time.
Overall Architecture
Claude Code accesses Ruby through three routes:
| Channel | Purpose | Context from Ruby's perspective |
|---|---|---|
| Mailbox API | Direct conversation as DM | DM (full profile) |
| Discord Webhook | Post messages to Discord channel | Channel message (chat profile) |
| debug-logs workflow | Retrieve container logs | — (read logs only) |
Prerequisites/Environment
- Ruby: Autonomous AI agent (Python 3.12+, Discord/LINE/Slack compatible)
- Runtime environment: Oracle Cloud VM, Docker container
- Development environment: Windows 11 + Claude Code CLI
- Deployment: git push → GitHub Actions → SSH → Docker rebuild
- Discord: Send messages via Webhook
Channel 1: Mailbox API — Conversation in DM Context
Ruby has a Supabase-based Mailbox API that allows direct conversation through the /api/chat endpoint. Authentication uses SSH Ed25519 key signatures.
How it works
# Generate authentication header and make API call
ts=$(date +%s)
device=$(hostname | tr '[:upper:]' '[:lower:]')
sig=$(uv run python -c "
from cryptography.hazmat.primitives.serialization import load_ssh_private_key
from pathlib import Path; import base64
k = load_ssh_private_key(
Path.home().joinpath('.ssh','id_ed25519').read_bytes(), password=None)
print(base64.b64encode(k.sign(b'${ts}:${device}')).decode())")
curl -s -X POST \
-H "X-Device: ${device}" \
-H "X-Timestamp: ${ts}" \
-H "X-Signature: ${sig}" \
-H "Content-Type: application/json" \
-d '{"text":"Hi Ruby, what model are you running?"}' \
"https://api.rubyclaw.uk/api/chat"
Response example
{
"response": "I'm running on Gemini 3.1 Flash Lite. The delegation system is working — I can call Claude Code via the delegate tool."
}
Features
- Ruby processes it as a DM context → full profile (all personality files + all tools)
- Response returns as JSON, making it easy for Claude Code to process programmatically
- Secure SSH key signature authentication
Channel 2: Discord Webhook — Testing in Channel Context
Discord Webhooks allow posting messages to specific channels. Ruby processes these as regular channel messages, allowing testing of channel prompt profiles.
Setup
- Discord channel settings → Integrations → Webhooks → Create New
- Save Webhook URL to
.claude/secrets.json(already in gitignore)
{
"discord_webhook_url": "https://discordapp.com/api/webhooks/..."
}
Implemented as a Claude Code skill
I defined a skill in .claude/commands/discord.md that can be called with the /discord command:
Send a message to Ruby on Discord via webhook and optionally check her response.
User input: $ARGUMENTS
## Instructions
1. Read the webhook URL from `.claude/secrets.json`
2. Write message payload to temp file, send via curl
3. Wait 15 seconds, then fetch container logs to check response
Usage
/discord Hi Ruby! What are your Discord formatting rules?
Claude Code automatically:
- Reads the webhook URL
- Sends the message via curl
- Waits 15 seconds then retrieves logs to check Ruby's response
Why Webhooks?
There are several ways to send messages via Discord's REST API, but I chose webhooks because:
- No bot token needed — Using Ruby's own bot token would cause Ruby to ignore her own messages
- User tokens violate ToS — Discord prohibits using user tokens for automation
- Webhooks are safe — Created in 30 seconds from channel settings, requires only URL for sending
- Ruby processes correctly as channel message — Ideal for testing prompt profiles
DM vs Channel Comparison Testing
The true value of this system is the ability to send the same question via both DM and channel to verify differences in Ruby's behavior:
| Test | DM (Mailbox API) | Channel (Webhook) |
|---|---|---|
| Rules loaded | SOUL + IDENTITY + USER + CORE + SELF_MODIFY + MEMORY_RULES + SYNC | SOUL + IDENTITY + CORE + DISCORD + GROUP_CHAT |
| Available tools | All 12 tools | 6 tools (chat set) |
| Prompt size | ~4,200 tokens | ~1,500 tokens |
Channel 3: debug-logs Workflow — Container Log Retrieval
Even without direct SSH access to the Oracle Cloud VM, we've set up a workflow to retrieve container logs via GitHub Actions.
name: Debug - Fetch container logs
on:
workflow_dispatch:
inputs:
add_ssh_key:
description: "Public SSH key to add to VM (optional)"
required: false
tail_lines:
description: "Number of log lines to fetch"
default: "200"
jobs:
logs:
runs-on: ubuntu-latest
steps:
- name: Add SSH key to VM (if provided)
if: inputs.add_ssh_key != ''
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.OCI_HOST }}
username: ${{ secrets.OCI_USER }}
key: ${{ secrets.OCI_SSH_KEY }}
script: |
mkdir -p ~/.ssh
echo "${{ inputs.add_ssh_key }}" >> ~/.ssh/authorized_keys
sort -u -o ~/.ssh/authorized_keys ~/.ssh/authorized_keys
- name: Fetch container logs
uses: appleboy/ssh-action@v1
with:
host: ${{ secrets.OCI_HOST }}
username: ${{ secrets.OCI_USER }}
key: ${{ secrets.OCI_SSH_KEY }}
script: |
cd /opt/rubyclaw && docker compose ps
docker compose logs --tail=${{ inputs.tail_lines }}
Usage
# Retrieve logs
gh workflow run debug-logs.yml -f tail_lines="100"
# Read results
gh run view <run_id> --log 2>&1 | grep "rubyclaw-1" | grep -v "healthz"
SSH Key Addition Feature
We've also included functionality to add SSH access from new development machines:
gh workflow run debug-logs.yml \
-f add_ssh_key="$(cat ~/.ssh/id_ed25519.pub)" \
-f tail_lines="100"
This allows adding a new machine's public key to the VM's authorized_keys via GitHub Actions' SSH key.
Actual Debugging Flow
Here I'll introduce some cases where I used this system for debugging.
Case: Verifying v0.23.0 post-deployment
After deploying v0.23.0 with major refactoring (prompt profiles + delegation tools), this was the verification flow:
Step 1: Confirm deployment completion
gh run list --workflow deploy.yml --limit 1
# completed success Deploy to Oracle Cloud
Step 2: Talk to Ruby via Mailbox API
curl -s -X POST ... \
-d '{"text":"What model are you running?"}' \
"https://api.rubyclaw.uk/api/chat"
→ Ruby responds. Confirm model name and tool availability.
Step 3: Test channel context via Discord Webhook
/discord Hi Ruby! What are your rules about markdown tables?
→ Confirm Ruby responds according to Discord rules (DISCORD.md).
Step 4: Check internal behavior via logs
gh workflow run debug-logs.yml -f tail_lines="50"
# → LiteLLM completion() model= gemini-3.1-flash-lite-preview; provider = gemini
# → tool:delegate args={'task': '...'}
# → Delegation completed (1735 chars)
→ Verify the correct model is being used, tools are being called, and no errors are present.
Case: Discovering Rate Limits
During testing, I found this in the logs:
rate_limit_error: 50,000 input tokens per minute
Claude Code was able to detect the rate limit from logs, analyze the cause, and immediately suggest countermeasures (provider switching). What would have taken tens of minutes manually ("Ruby isn't responding → check logs → determine cause") was completed in minutes.
Security Considerations
| Element | Measures |
|---|---|
| Webhook URL | Stored in .claude/secrets.json, excluded by .gitignore |
| Mailbox API authentication | SSH Ed25519 key signature, device registration |
| Container logs | Via GitHub Actions, OCI SSH keys stored in Secrets |
| Discord Webhook | Created only by channel admins, be careful with URL leakage |
Summary
- Mailbox API → Direct conversation with Ruby in DM context. Authenticated, JSON response
- Discord Webhook → Testing in channel context. Ideal for prompt profile verification
- /discord skill → One command from Claude Code for webhook sending + log checking
- debug-logs workflow → Container log retrieval without SSH. Also allows adding new machine keys
By combining these three, Claude Code can complete the "write code → push → talk to Ruby → check logs" cycle entirely within the terminal. No need for humans to open Discord in a browser and type messages.
AI debugging AI — when this becomes reality, the only bottleneck in the development cycle becomes "waiting for deployment."