debug · diff

git:20260507.2754f75 to git:20260606.14dba9b

148 added, 243 removed. Audit B to B.

---
name: debug
- description: Debug container agent issues. Use when things aren't working, container fails, authentication problems, or to understand how the container system works. Covers logs, environment variables, mounts, and common issues.
+ description: Debug container agent issues. Use when things aren't working, container fails, authentication problems, or to understand how the container system works. Covers logs, session DBs, mounts, and common issues.
---
# NanoClaw Container Debugging
This guide covers debugging the containerized agent execution system.
## Architecture Overview
+ The host is a single Node process that orchestrates per-session agent containers. The two session DBs are the **sole** IO surface between host and container — there is no IPC, no file watcher, and no stdin piping.
+
```
- Host (macOS) Container (Linux VM)
- ─────────────────────────────────────────────────────────────
- src/container-runner.ts container/agent-runner/
- │ │
- │ spawns container │ runs Claude Agent SDK
- │ with volume mounts │ with MCP servers
- │ │
- ├── data/env/env ──────────────> /workspace/env-dir/env
- ├── groups/{folder} ───────────> /workspace/group
- ├── data/ipc/{folder} ────────> /workspace/ipc
- ├── data/sessions/{folder}/.claude/ ──> /home/node/.claude/ (isolated per-group)
- └── (main only) project root ──> /workspace/project
+ Host (Node) Container (Bun, Linux VM)
+ ──────────────────────────────────────────────────────────────────────
+ src/container-runner.ts container/agent-runner/src/
+ │ │
+ │ spawns one container per session │ polls inbound.db for work,
+ │ with the session folder mounted │ calls the agent provider,
+ │ at /workspace │ writes replies to outbound.db
+ │ │
+ ├── data/v2-sessions/<group>/<session>/ ──> /workspace
+ │ ├── inbound.db (host writes, container reads RO)
+ │ ├── outbound.db (container writes, host reads)
+ │ └── .heartbeat (container touches → /workspace/.heartbeat)
+ ├── groups/<folder> ─────────────────────> /workspace/agent (cwd)
+ ├── <group>/.claude-shared ──────────────> /home/node/.claude
+ └── agent-runner src + skills ───────────> /app/src, /app/skills
```
- **Important:** The container runs as user `node` with `HOME=/home/node`. Session files must be mounted to `/home/node/.claude/` (not `/root/.claude/`) for session resumption to work.
+ **Message flow:** host writes a row to `inbound.db` (`messages_in`) and wakes the container; the container's poll loop picks it up, runs the agent, and writes the reply to `outbound.db` (`messages_out`); the host's delivery poll reads `messages_out` and sends it through the channel adapter. See [docs/db.md](../../../docs/db.md) and [docs/db-session.md](../../../docs/db-session.md) for the full two-DB model.
+ **Container identity:** the container runs as user `node` with `HOME=/home/node`. Per-group Claude state (settings, session history) lives in `<group>/.claude-shared` on the host, mounted to `/home/node/.claude`.
+
## Log Locations
| Log | Location | Content |
|-----|----------|---------|
- | **Main app logs** | `logs/nanoclaw.log` | Host-side WhatsApp, routing, container spawning |
- | **Main app errors** | `logs/nanoclaw.error.log` | Host-side errors |
- | **Container run logs** | `groups/{folder}/logs/container-*.log` | Per-run: input, mounts, stderr, stdout |
- | **Claude sessions** | `~/.claude/projects/` | Claude Code session history |
+ | **Host errors** | `logs/nanoclaw.error.log` | Delivery failures, crash-loop backoff, warnings — check this first |
+ | **Host app log** | `logs/nanoclaw.log` | Full routing chain: inbound routing, container spawn/exit, delivery |
+ | **Setup logs** | `logs/setup.log`, `logs/setup-steps/*.log` | Per-step install output (bootstrap, container, onecli, mounts, service) |
+ | **Session inbound** | `data/v2-sessions/<group>/<session>/inbound.db` (`messages_in`) | Did the message reach the container? |
+ | **Session outbound** | `data/v2-sessions/<group>/<session>/outbound.db` (`messages_out`) | Did the agent produce a reply? |
+ Containers run with `--rm`, so the container's own filesystem is gone after it exits. The host streams container **stderr** into `logs/nanoclaw.log` at debug level, tagged with `container=<group folder>`; raise the log level (below) to see it. If the agent silently failed inside an exited container, there is no persistent in-container log — reconstruct from the session DBs and the host log.
+
## Enabling Debug Logging
- Set `LOG_LEVEL=debug` for verbose output:
+ Set `LOG_LEVEL=debug` for verbose output, including streamed container stderr:
```bash
# For development
LOG_LEVEL=debug pnpm run dev
# For launchd service (macOS), add to plist EnvironmentVariables:
<key>LOG_LEVEL</key>
<string>debug</string>
# For systemd service (Linux), add to unit [Service] section:
# Environment=LOG_LEVEL=debug
```
- Debug level shows:
- - Full mount configurations
- - Container command arguments
- - Real-time container stderr
+ Debug level shows full mount configurations, the container spawn command, and streamed container stderr lines.
+ ## Inspecting Session DBs
+
+ The two session DBs are where the message flow lives. Use the in-tree query wrapper (it goes through the `better-sqlite3` dep that setup already installs, avoiding a dependency on the `sqlite3` CLI):
+
+ ```bash
+ # List sessions and their agent group / messaging group from the central DB
+ pnpm exec tsx scripts/q.ts data/v2.db "SELECT id, agent_group_id, messaging_group_id, status, container_status, last_active FROM sessions"
+
+ # Or via the admin CLI
+ ncl sessions list
+
+ # Did the message reach the container? (inbound.db, host writes / container reads)
+ pnpm exec tsx scripts/q.ts data/v2-sessions/<group>/<session>/inbound.db \
+ "SELECT seq, kind, status, timestamp FROM messages_in ORDER BY seq DESC LIMIT 10"
+
+ # Did the agent produce a reply? (outbound.db, container writes / host reads)
+ pnpm exec tsx scripts/q.ts data/v2-sessions/<group>/<session>/outbound.db \
+ "SELECT seq, kind, timestamp FROM messages_out ORDER BY seq DESC LIMIT 10"
+
+ # Container-side processing status for each inbound message
+ pnpm exec tsx scripts/q.ts data/v2-sessions/<group>/<session>/outbound.db \
+ "SELECT message_id, status, status_changed FROM processing_ack ORDER BY status_changed DESC LIMIT 10"
+ ```
+
+ Reading the flow:
+ - `messages_in` has the message but no matching `messages_out` → the container never produced a reply (check `processing_ack`, then `logs/nanoclaw.log` for spawn/exit and container stderr).
+ - `messages_out` has a reply but the user never received it → a delivery problem (see issue 1 below).
+ - `messages_in` is empty → routing never reached this session (check the router log lines and the central wiring with `ncl wirings list`).
+
## Common Issues
- ### 1. "No adapter for channel type" / Messages silently lost (null platformMsgId)
+ ### 1. "No adapter for channel type" / Messages silently lost (null platform_message_id)
**Symptom:** The bot stops replying. `logs/nanoclaw.error.log` shows repeated:
```
WARN No adapter for channel type channelType="telegram"
WARN No adapter for channel type channelType="signal"
```
- The main log shows "Message delivered" entries with `platformMsgId=undefined` — meaning the delivery poll ran, found no adapter, and permanently marked the message as delivered without sending it.
+ The main log shows "Message delivered" entries with `platformMsgId=undefined` — meaning the delivery poll ran, found no adapter, and marked the message delivered without sending it.
**Root cause: two NanoClaw service instances running simultaneously.**
- When a second service instance (often `nanoclaw-v2-<id>.service` running alongside `nanoclaw.service`) is active with a stale binary, it has no channel adapters registered. Its delivery poll races against the working instance and wins — permanently marking outbound messages as delivered without ever sending them.
+ When a second service instance is active with a stale binary, it has no channel adapters registered. Its delivery poll races the working instance and wins — marking outbound messages delivered without ever sending them.
**Diagnosis:**
```bash
# Check for duplicate running instances
ps aux | grep 'nanoclaw/dist/index.js' | grep -v grep
- # Check which services are active
+ # Check which services are active (Linux)
systemctl --user list-units 'nanoclaw*' --all
# Confirm channel adapters registered by the current process
grep "Channel adapter started" logs/nanoclaw.log | tail -10
```
**Fix:**
- 1. Identify which service has the correct binary and EnvironmentFile (the one showing `signal`, `telegram`, `cli` all started in the log).
+ 1. Identify which service has the correct binary and EnvironmentFile (the one whose log shows the expected channels — e.g. `signal`, `telegram`, `cli` — all started).
2. Stop and disable the stale duplicate service:
```bash
systemctl --user stop nanoclaw.service # or whichever is the old one
systemctl --user disable nanoclaw.service
```
3. If the remaining service unit is missing `EnvironmentFile`, add it:
```bash
# Edit the service unit — add this line under [Service]:
# EnvironmentFile=/home/[user]/nanoclaw/.env
systemctl --user daemon-reload
systemctl --user restart nanoclaw-v2-<id>.service
```
4. Verify only one instance runs: `ps aux | grep nanoclaw/dist/index.js | grep -v grep`
- **Note:** Messages that were marked delivered with a null `platform_message_id` cannot be automatically retried — they are permanently lost. The user must resend their message.
-
- ### 2. "Claude Code process exited with code 1"
+ Messages marked delivered with a null `platform_message_id` are not automatically retried. Ask the user to resend.
- **Check the container log file** in `groups/{folder}/logs/container-*.log`
+ ### 2. Container exits immediately / agent produces no reply
- Common causes:
+ A spawned container that exits without writing to `outbound.db` shows up in `logs/nanoclaw.log` as a `Container exited` line with a non-zero `code`, often preceded by streamed `container=<folder>` stderr (at debug level).
- #### Missing Authentication
- ```
- Invalid API key · Please run /login
- ```
- **Fix:** Ensure `.env` file exists with either OAuth token or API key:
+ **Authentication errors:** secrets are injected per request by the OneCLI gateway — none are passed in env vars or chat context. A `401` from an API whose credential is in the vault usually means the agent is in `selective` secret mode and that secret was never assigned:
```bash
- cat .env # Should show one of:
- # CLAUDE_CODE_OAUTH_TOKEN=sk-ant-oat01-... (subscription)
- # ANTHROPIC_API_KEY=sk-ant-api03-... (pay-per-use)
- ```
-
- #### Root User Restriction
- ```
- --dangerously-skip-permissions cannot be used with root/sudo privileges
+ onecli agents list # check secretMode
+ onecli agents set-secret-mode --id <agent-id> --mode all # inject all matching secrets
```
- **Fix:** Container must run as non-root user. Check Dockerfile has `USER node`.
-
- ### 2. Environment Variables Not Passing
-
- **Runtime note:** Environment variables passed via `-e` may be lost when using `-i` (interactive/piped stdin).
-
- **Workaround:** The system extracts only authentication variables (`CLAUDE_CODE_OAUTH_TOKEN`, `ANTHROPIC_API_KEY`) from `.env` and mounts them for sourcing inside the container. Other env vars are not exposed.
+ If the gateway itself is unreachable, the container runner refuses to spawn (`OneCLI gateway not applied — refusing to spawn container without credentials` in the host log). Confirm the gateway is up at `http://127.0.0.1:10254`.
- To verify env vars are reaching the container:
- ```bash
- echo '{}' | docker run -i \
- -v $(pwd)/data/env:/workspace/env-dir:ro \
- --entrypoint /bin/bash nanoclaw-agent:latest \
- -c 'export $(cat /workspace/env-dir/env | xargs); echo "OAuth: ${#CLAUDE_CODE_OAUTH_TOKEN} chars, API: ${#ANTHROPIC_API_KEY} chars"'
- ```
+ **MCP server failures:** a misconfigured MCP server can abort the agent run. Look for MCP initialization errors in the streamed container stderr (`LOG_LEVEL=debug`).
### 3. Mount Issues
- **Container mount notes:**
- - Docker supports both `-v` and `--mount` syntax
- - Use `:ro` suffix for readonly mounts:
- ```bash
- # Readonly
- -v /path:/container/path:ro
-
- # Read-write
- -v /path:/container/path
- ```
+ Session and group folders are bind-mounted into the container. To see the resolved mounts for a spawn, run with `LOG_LEVEL=debug` and read the spawn command in `logs/nanoclaw.log`, or grep the mount targets directly:
- To check what's mounted inside a container:
```bash
- docker run --rm --entrypoint /bin/bash nanoclaw-agent:latest -c 'ls -la /workspace/'
+ grep -n "containerPath" src/container-runner.ts
```
- Expected structure:
+ Expected mount targets inside the container:
```
- /workspace/
- ├── env-dir/env # Environment file (CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY)
- ├── group/ # Current group folder (cwd)
- ├── project/ # Project root (main channel only)
- ├── global/ # Global CLAUDE.md (non-main only)
- ├── ipc/ # Inter-process communication
- │ ├── messages/ # Outgoing WhatsApp messages
- │ ├── tasks/ # Scheduled task commands
- │ ├── current_tasks.json # Read-only: scheduled tasks visible to this group
- │ └── available_groups.json # Read-only: WhatsApp groups for activation (main only)
- └── extra/ # Additional custom mounts
+ /workspace ← session folder (inbound.db, outbound.db, .heartbeat, inbox/, outbox/)
+ /workspace/agent ← agent group folder (cwd; CLAUDE.md, skills, working files)
+ /home/node/.claude ← per-group .claude-shared (Claude state, settings, history)
+ /app/src ← agent-runner source (read-only)
+ /app/skills ← container skills (read-only)
```
- ### 4. Permission Issues
-
- The container runs as user `node` (uid 1000). Check ownership:
+ To inspect what a fresh container sees:
```bash
- docker run --rm --entrypoint /bin/bash nanoclaw-agent:latest -c '
- whoami
- ls -la /workspace/
- ls -la /app/
- '
+ docker run --rm --entrypoint /bin/bash nanoclaw-agent:latest -c 'whoami; ls -la /workspace/ /app/'
```
-
- All of `/workspace/` and `/app/` should be owned by `node`.
-
- ### 5. Session Not Resuming / "Claude Code process exited with code 1"
+ All of `/workspace/` and `/app/` should be owned by `node`. Use `:ro` on a `-v` mount for read-only.
- If sessions aren't being resumed (new session ID every time), or Claude Code exits with code 1 when resuming:
+ ### 4. Heartbeat / stale-session detection
- **Root cause:** The SDK looks for sessions at `$HOME/.claude/projects/`. Inside the container, `HOME=/home/node`, so it looks at `/home/node/.claude/projects/`.
+ Liveness is a file `touch` on `/workspace/.heartbeat` (host path: `data/v2-sessions/<group>/<session>/.heartbeat`), not a DB write. The host sweep reads its mtime plus the `processing_ack` claim age to decide whether a container is alive or stale. A session stuck "processing" with a stale `.heartbeat` mtime means the container died mid-run:
- **Check the mount path:**
```bash
- # In container-runner.ts, verify mount is to /home/node/.claude/, NOT /root/.claude/
- grep -A3 "Claude sessions" src/container-runner.ts
+ stat -f '%Sm' data/v2-sessions/<group>/<session>/.heartbeat # macOS
+ stat -c '%y' data/v2-sessions/<group>/<session>/.heartbeat # Linux
```
- **Verify sessions are accessible:**
- ```bash
- docker run --rm --entrypoint /bin/bash \
- -v ~/.claude:/home/node/.claude \
- nanoclaw-agent:latest -c '
- echo "HOME=$HOME"
- ls -la $HOME/.claude/projects/ 2>&1 | head -5
- '
- ```
+ ## Container CLI (`ncl`) inside a session
- **Fix:** Ensure `container-runner.ts` mounts to `/home/node/.claude/`:
- ```typescript
- mounts.push({
- hostPath: claudeDir,
- containerPath: '/home/node/.claude', // NOT /root/.claude
- readonly: false
- });
- ```
+ The agent reaches the central DB from inside the container via `ncl`, which uses the session DB transport (`container/agent-runner/src/cli/ncl.ts`). On the host, `ncl` connects over a Unix socket (`src/cli/socket-server.ts`). If `ncl` calls fail from inside a container, check the agent group's `cli_scope` in its container config:
- ### 6. MCP Server Failures
+ ```bash
+ ncl groups config get --id <group-id> # look at cli_scope: disabled | group | global
+ ```
- If an MCP server fails to start, the agent may exit. Check the container logs for MCP initialization errors.
+ `disabled` rejects every `cli_request`; `group` scopes the agent to its own group's `groups`/`sessions`/`destinations`/`members`; `global` is unrestricted.
- ## Manual Container Testing
+ ## Restarting a session's container
- ### Test the full agent flow:
```bash
- # Set up env file
- mkdir -p data/env groups/test
- cp .env data/env/env
+ # Restart all containers for an agent group
+ ncl groups restart --id <group-id>
- # Run test query
- echo '{"prompt":"What is 2+2?","groupFolder":"test","chatJid":"test@g.us","isMain":false}' | \
- docker run -i \
- -v $(pwd)/data/env:/workspace/env-dir:ro \
- -v $(pwd)/groups/test:/workspace/group \
- -v $(pwd)/data/ipc:/workspace/ipc \
- nanoclaw-agent:latest
- ```
+ # Restart and rebuild the image first (after package/Dockerfile changes)
+ ncl groups restart --id <group-id> --rebuild
- ### Test Claude Code directly:
- ```bash
- docker run --rm --entrypoint /bin/bash \
- -v $(pwd)/data/env:/workspace/env-dir:ro \
- nanoclaw-agent:latest -c '
- export $(cat /workspace/env-dir/env | xargs)
- claude -p "Say hello" --dangerously-skip-permissions --allowedTools ""
- '
+ # Restart and wake immediately with a message
+ ncl groups restart --id <group-id> --message "on_wake test"
```
- ### Interactive shell in container:
+ Without `--message`, the container comes back on the next user message. From inside a container, `--id` is auto-filled and only the calling session restarts.
+
+ ## Manual Container Probes
+
+ The container's entry point is `exec bun run /app/src/index.ts`; it talks only to the mounted session DBs, so there is no JSON to pipe in. To probe the image directly:
+
```bash
+ # Interactive shell in the image
docker run --rm -it --entrypoint /bin/bash nanoclaw-agent:latest
+
+ # Check the image contents
+ docker run --rm --entrypoint /bin/bash nanoclaw-agent:latest -c '
+ node --version
+ bun --version
+ ls /app/src/
+ '
```
- ## SDK Options Reference
+ ## Provider SDK Options
- The agent-runner uses these Claude Agent SDK options:
+ The default provider wraps the Claude Agent SDK in `container/agent-runner/src/providers/claude.ts`. The query is configured roughly as:
```typescript
query({
prompt: input.prompt,
options: {
- cwd: '/workspace/group',
- allowedTools: ['Bash', 'Read', 'Write', ...],
+ cwd: input.cwd, // /workspace/agent
+ allowedTools: [...TOOL_ALLOWLIST, ...mcpAllowPatterns],
+ disallowedTools: SDK_DISALLOWED_TOOLS,
permissionMode: 'bypassPermissions',
- allowDangerouslySkipPermissions: true, // Required with bypassPermissions
- settingSources: ['project'],
- mcpServers: { ... }
- }
+ settingSources: ['project', 'user', 'local'],
+ mcpServers: { ... },
+ },
})
```
- **Important:** `allowDangerouslySkipPermissions: true` is required when using `permissionMode: 'bypassPermissions'`. Without it, Claude Code exits with code 1.
+ Each registered MCP server's allow pattern is derived from the `mcpServers` map, so registering a server already exposes its tools.
## Rebuilding After Changes
```bash
- # Rebuild main app
+ # Rebuild host TypeScript
pnpm run build
- # Rebuild container (use --no-cache for clean rebuild)
+ # Rebuild the agent container image
./container/build.sh
- # Or force full rebuild
+ # Force a truly clean rebuild (the buildkit cache retains stale COPY files)
docker builder prune -af
./container/build.sh
```
- ## Checking Container Image
-
- ```bash
- # List images
- docker images
-
- # Check what's in the image
- docker run --rm --entrypoint /bin/bash nanoclaw-agent:latest -c '
- echo "=== Node version ==="
- node --version
-
- echo "=== Claude Code version ==="
- claude --version
-
- echo "=== Installed packages ==="
- ls /app/node_modules/
- '
- ```
-
- ## Session Persistence
-
- Claude sessions are stored per-group in `data/sessions/{group}/.claude/` for security isolation. Each group has its own session directory, preventing cross-group access to conversation history.
-
- **Critical:** The mount path must match the container user's HOME directory:
- - Container user: `node`
- - Container HOME: `/home/node`
- - Mount target: `/home/node/.claude/` (NOT `/root/.claude/`)
-
- To clear sessions:
-
- ```bash
- # Clear all sessions for all groups
- rm -rf data/sessions/
-
- # Clear sessions for a specific group
- rm -rf data/sessions/{groupFolder}/.claude/
-
- # Also clear the session ID from NanoClaw's tracking (stored in SQLite)
- pnpm exec tsx scripts/q.ts store/messages.db "DELETE FROM sessions WHERE group_folder = '{groupFolder}'"
- ```
-
- To verify session resumption is working, check the logs for the same session ID across messages:
- ```bash
- grep "Session initialized" logs/nanoclaw.log | tail -5
- # Should show the SAME session ID for consecutive messages in the same group
- ```
-
- ## IPC Debugging
+ ## Clearing a Session
- The container communicates back to the host via files in `/workspace/ipc/`:
+ Conversation continuity lives in the container-owned `session_state` table in `outbound.db` (the provider's session/continuation id). The agent's `/clear` clears it. To reset a session from the host, remove the session folder so a fresh one is provisioned on the next message:
```bash
- # Check pending messages
- ls -la data/ipc/messages/
-
- # Check pending task operations
- ls -la data/ipc/tasks/
-
- # Read a specific IPC file
- cat data/ipc/messages/*.json
-
- # Check available groups (main channel only)
- cat data/ipc/main/available_groups.json
+ # Inspect first
+ ncl sessions get <session-id>
- # Check current tasks snapshot
- cat data/ipc/{groupFolder}/current_tasks.json
+ # Remove a single session's folder (host re-provisions both DBs on next message)
+ rm -rf data/v2-sessions/<group>/<session>/
```
- **IPC file types:**
- - `messages/*.json` - Agent writes: outgoing WhatsApp messages
- - `tasks/*.json` - Agent writes: task operations (schedule, pause, resume, cancel, refresh_groups)
- - `current_tasks.json` - Host writes: read-only snapshot of scheduled tasks
- - `available_groups.json` - Host writes: read-only list of WhatsApp groups (main only)
-
## Quick Diagnostic Script
- Run this to check common issues:
-
```bash
- echo "=== Checking NanoClaw Container Setup ==="
-
- echo -e "\n1. Authentication configured?"
- [ -f .env ] && (grep -q "CLAUDE_CODE_OAUTH_TOKEN=sk-" .env || grep -q "ANTHROPIC_API_KEY=sk-" .env) && echo "OK" || echo "MISSING - add CLAUDE_CODE_OAUTH_TOKEN or ANTHROPIC_API_KEY to .env"
-
- echo -e "\n2. Env file copied for container?"
- [ -f data/env/env ] && echo "OK" || echo "MISSING - will be created on first run"
+ echo "=== Checking NanoClaw v2 Setup ==="
- echo -e "\n3. Container runtime running?"
+ echo -e "\n1. Container runtime running?"
docker info &>/dev/null && echo "OK" || echo "NOT RUNNING - start Docker Desktop (macOS) or sudo systemctl start docker (Linux)"
- echo -e "\n4. Container image exists?"
- echo '{}' | docker run -i --entrypoint /bin/echo nanoclaw-agent:latest "OK" 2>/dev/null || echo "MISSING - run ./container/build.sh"
+ echo -e "\n2. Agent image exists?"
+ docker run --rm --entrypoint /bin/echo nanoclaw-agent:latest "OK" 2>/dev/null || echo "MISSING - run ./container/build.sh"
- echo -e "\n5. Session mount path correct?"
- grep -q "/home/node/.claude" src/container-runner.ts 2>/dev/null && echo "OK" || echo "WRONG - should mount to /home/node/.claude/, not /root/.claude/"
+ echo -e "\n3. OneCLI gateway reachable?"
+ curl -fsS http://127.0.0.1:10254/ >/dev/null 2>&1 && echo "OK" || echo "CHECK - gateway not responding on 127.0.0.1:10254"
- echo -e "\n6. Groups directory?"
- ls -la groups/ 2>/dev/null || echo "MISSING - run setup"
+ echo -e "\n4. Central DB present?"
+ [ -f data/v2.db ] && echo "OK" || echo "MISSING - run setup"
- echo -e "\n7. Recent container logs?"
- ls -t groups/*/logs/container-*.log 2>/dev/null | head -3 || echo "No container logs yet"
+ echo -e "\n5. Mount targets in container-runner?"
+ grep -q "containerPath: '/workspace'" src/container-runner.ts && echo "OK" || echo "CHECK - session mount target changed"
- echo -e "\n8. Session continuity working?"
- SESSIONS=$(grep "Session initialized" logs/nanoclaw.log 2>/dev/null | tail -5 | awk '{print $NF}' | sort -u | wc -l)
- [ "$SESSIONS" -le 2 ] && echo "OK (recent sessions reusing IDs)" || echo "CHECK - multiple different session IDs, may indicate resumption issues"
+ echo -e "\n6. Single host instance running?"
+ N=$(ps aux | grep 'nanoclaw/dist/index.js' | grep -vc grep)
+ [ "$N" -le 1 ] && echo "OK ($N)" || echo "DUPLICATE - $N instances; stop the stale one (see issue 1)"
+
+ echo -e "\n7. Recent host errors?"
+ tail -n 5 logs/nanoclaw.error.log 2>/dev/null || echo "No error log yet"
```