# Ask Claude — Print Mode Consulting via Claude Opus 4.8
Includes usage & context tracking: context % from JSON envelope (free), session/weekly % from OAuth usage API (best-effort, not per-turn). Alerts at 80% context, 80% session, 90% weekly.
Consult Claude Opus on 10.0.0.28 using headless print mode (`-p`) with session resumption. Each turn is a request/response — Claude's process exit IS the turn boundary. No tmux, no polling, no send-keys races.
| 5. Done | Process exits. Session auto-expires in ~5h. | Cleanup temp files if desired. |
**Legacy method (mktemp + heredoc) — ONLY for simple questions with zero shell metacharacters.** Breaks on `"`, `$`, `(`, `)`, backticks. The scp method above is preferred for all real questions.
**Default rule: One Hermes session = one Claude session.** The first `ask-claude` call in a Hermes session starts a fresh Claude session. Every subsequent call within the same Hermes session resumes it. Claude accumulates context across turns, just like our conversation does.
```
Is there an active Claude session_id from a previous turn in THIS Hermes session?
│
├─ YES → Use --resume <session_id>
│ Always. Claude remembers everything from earlier in our conversation.
│ This is the default — same Hermes session, same Claude session.
│
└─ NO → Fresh session (no --resume flag)
Only when:
- This is the first ask-claude call in this Hermes session
- The user explicitly said "new topic" or "fresh session"
- The previous session expired (~5 hours since last turn)
```
**Session lifecycle:** Each `claude -p` invocation exits immediately after responding. The session data lives on disk. You don't "close" sessions — they auto-expire after ~5 hours of inactivity. No cleanup needed.
## When to Use
- Deep analysis, planning, or validation
- Second opinion on architecture, security, or design
- Research that benefits from SearXNG + Opus reasoning
- Multi-turn discussion where Claude may ask clarifying questions
- **Adversarial consulting** — ask for advice, then ask Claude to push back on its own advice with web searches. See `references/adversarial-consulting.md` for the pattern.
## Critical Evaluation — You Are the Final Authority
**Claude's advice is input, not a decision.** You are the engineer — you own the code, the system, and the final call. Claude is a consultant who cannot see your files, run your commands, or verify its own claims. Treat every recommendation as a hypothesis to be tested, not a conclusion to be implemented.
**Mandatory before acting on Claude's advice:**
1.**Do your own reasoning first.** Before relaying Claude's answer, ask yourself: does this make sense? Is there a simpler way? Would this actually work in our environment?
2.**Push back when Claude is wrong.** If Claude suggests something that doesn't fit — wrong tool, overengineered solution, unverified claim — tell it so explicitly in the next turn. "That won't work because X. Do a web search for alternatives." Claude corrects itself well when challenged; it doubles down when unchallenged.
3.**Do your own web searches to validate.** Claude's SearXNG searches are real but its interpretation may be wrong. For any claim about API behavior, version compatibility, or tool capabilities, run your own `mcp_searxng_searxng_web_search` and compare. Claude has been wrong about Qdrant endpoints, Python behavior, and shell patterns in this exact session.
4.**Involve the user when there's genuine ambiguity.** If Claude and you disagree after a round of pushback, or if the right approach depends on constraints only the user knows, surface the disagreement with both positions stated clearly. Don't silently pick Claude's answer over your own judgment.
5.**Watch for overcomplication.** Claude's default mode is thoroughness, which can become overengineering. If Claude proposes a multi-step protocol with atomic renames, CAS semantics, and lease expiration — ask: "Is there a simpler way? Can we solve this with a 50-line Python script instead?" The best fix is usually the simplest one that actually works.
## Claude's Limitations
**Claude cannot read your local files.** Claude runs on 10.0.0.28 and has no access to `/home/n8n/workspace` or any other path on your machine. When Claude says "I can't read that file" or "that path doesn't exist here" — **believe it.** Do not ask Claude to validate a file by path. Instead:
**Claude may fabricate plausible-sounding analysis when it can't verify.** In this session, Claude confidently claimed the deployed prompt was a "v5 classify-only rewrite" based on a session title it couldn't re-open. That claim was wrong. When Claude prefaces a claim with "from my notes" or "I recall" without being able to re-read the source, treat it as unverified.
**Claude's environment is not your environment.** Claude has its own tools, its own filesystem, and its own constraints. A command that works in Claude's environment may fail in yours. Always test Claude's suggestions locally before deploying.
- Model: `claude-opus-4-8` — pass explicitly via `--model`, do NOT rely on profile default
- Use full path `/home/n8n/.local/bin/claude` on non-interactive SSH (bare `claude` only works in interactive shells that source `.bashrc`)
- Wrap with `timeout 280` to prevent runaway hangs
- Use `|| true` after claude invocation — `set -e` would kill the script on non-zero exit, but errors still produce parseable JSON
## Workflow
### Step 1: Confirm with User
Tell the user what question you'll send. Skip confirmation for clear, self-contained queries. Confirm when the question needs interpretation.
### Step 2: Send First Question (Fresh Session)
**CRITICAL: Never inline the question in the SSH command.** Shell metacharacters (`"`, `$`, `(`, `)`, backticks) will break. Always write to a temp file first. Use `mktemp` to avoid collisions between concurrent sessions.
```bash
# Step 2a: Write question to temp file on 10.0.0.28 (mktemp avoids collisions)
Account-wide counters. **Do NOT call every turn** — the endpoint aggressively rate-limits (open issues anthropics/claude-code #31637, #31021). Call at most every ~5 minutes, cache the last good value, and never gate a turn's success on this call. Swallow all errors (429s, timeouts, auth failures).
- Requires `anthropic-beta: oauth-2025-04-20` header or it 401s
- **Note:** `seven_day` is all-models weekly, not Opus-specific. `seven_day_opus` exists in the response but is null on this plan. The all-models counter is the one that matters for rate limiting.
**Alert thresholds:**
- Session ≥ 80% — warn before starting a fresh session. "Claude session at 87%, resets at 2:49 PM CT — want to wait or use Sonnet instead?"
- Weekly ≥ 90% — critical. "Claude weekly at 94%, resets Tuesday 2:00 PM CT. Consider Sonnet or waiting."
- When Claude is unavailable (error_budget or rate-limited) — report the exact reset time so the user knows when to try again.
### 3. Alert Rules (apply on every ask-claude turn)
| Check | When | Threshold | Action |
|-------|------|-----------|--------|
| Context % | After every response | ≥ 80% | Warn: wrap up or start fresh |
| Session % | Best-effort (~5min cache) | ≥ 80% | Warn + show reset time, offer Sonnet |
Show Claude's response. If Claude asked a clarifying question (visible in the result text), get the user's answer.
### Step 4: Send Follow-ups (WITH --resume)
Same mktemp pattern, but add `--resume <session_id>` to continue the conversation. Always capture session_id from every response (including errors) — a turn can error yet still advance the session.
echo "ERROR: max turns reached. Retry with higher --max-turns or simplify."
fi
```
**Common errors:**
-`error_max_turns` — question too complex, increase `--max-turns` or simplify. Note: `is_error` is `false` but `result` is empty — detect via `subtype`.
-`error_budget` — hit spending cap, check Pro limits
- Non-zero exit code — SSH failure, Claude not installed, or process killed. `|| true` keeps the script alive so you can still parse the JSON.
- Empty response — timeout (increase from 280s), OOM, or crash
- Non-JSON response — SSH dropped mid-call, Claude crashed before writing output
## Sonnet Fast-Path
For routine questions where Opus-level reasoning is overkill, offer the user a faster option:
- **ask.sh can fail silently while direct `claude -p` works.** The wrapper may return `{"is_error": true, "error": "no Claude output (timeout or CLI failure)"}` even when Claude is healthy. When this happens, fall back to the direct invocation: `ssh n8n@10.0.0.28 'cd ~/claude/hermes_support && timeout 280 /home/n8n/.local/bin/claude -p "$(cat /tmp/ask-claude-q.txt)" --output-format json --model claude-opus-4-8 --max-turns 30 || true'`. The wrapper is preferred (it handles usage API + merge.py), but the direct path is the reliable fallback. If the direct path also fails, Claude is genuinely down.
- **CRITICAL: Never inline questions in SSH commands.** Shell metacharacters (`"`, `$`, `(`, backticks) will break. Always write to a temp file via `mktemp` first, then `claude -p "$(cat $QFILE)"`.
- **Always use `--output-format json`** — without it, you get plain text and can't extract session_id for follow-ups.
- **Always set `--max-turns`** — prevents runaway loops. 30 is a good default for analysis tasks (20 was too tight with CLAUDE.md forcing web-search-first + .md writes).
- **Always pass `--model claude-opus-4-8`** — pin the model explicitly. Profile defaults drift silently. The old "do NOT pass --model" rule was wrong.
- **Always use `|| true` after the claude command** — `set -e` kills the script on non-zero exit, but errors still produce parseable JSON you need.
- **Always wrap with `timeout 280`** — prevents runaway hangs that exceed SSH's 300s timeout.
- **Always add `-o ServerAliveInterval=30`** to SSH commands — Opus turns run 45-90s and SSH can silently disconnect.
- **Use full path `/home/n8n/.local/bin/claude`** — bare `claude` only works in interactive shells that source `.bashrc`.
- **Session IDs expire** — sessions last ~5 hours. After that, `--resume` fails. Start fresh.
- **JSON parsing** — use Python's `json.load(sys.stdin)` not jq, to handle control characters in Claude's output. Wrap in try/except for non-JSON responses.
- **SSH timeouts** — Opus can take 60-120 seconds. Set generous timeouts (300s for SSH, 280s for claude process).
- **CRITICAL: Ask before probing.** Do NOT SSH into 10.0.0.28 without asking the user first.
- **CLAUDE.md is the source of truth** — if behavior seems off, check `~/claude/hermes_support/CLAUDE.md` on 10.0.0.28.
- **Tool sanitization: `$VARNAME` → `***`** — Hermes tooling (write_file, skill_manage patch, execute_code) sanitizes `$VARIABLE` patterns to `***` at write time. When editing the combined script or any code block containing shell variables, use Python's `chr(36)` to construct `$` and write via terminal heredoc or Python file I/O. Example: `dollar = chr(36); script = f'TOKEN={dollar}(jq ...)'`. Same issue affects `|` (pipe) — use `chr(124)` if it gets mangled to `|`.
- **Capture `session_id` from EVERY response** — including errors. A turn can error yet still advance the session. If you only store it on success, resume breaks.
- **Detect `error_max_turns` via `subtype`** — it has `is_error:false` but no `result` field. Your parser will emit empty answers if you don't check subtype explicitly.
- **Iterate `modelUsage` keys** — don't hardcode `modelUsage['claude-opus-4-8']`. The key changes with the model id. Use `for model_name, model_data in mu.items(): cw = model_data.get('contextWindow', 200000); break`.
- **Usage API is best-effort only** — it aggressively rate-limits. Call at most every ~5 min, cache last good value, swallow all errors (429s, timeouts). Never gate a turn on this call.
- **Use `mktemp` for temp files** — `/tmp/ask-claude-q.txt` is a fixed path that concurrent Hermes sessions clobber. Use `mktemp /tmp/ask-claude-q-XXXXXX.txt` and `trap "rm -f $QFILE" EXIT`.
- **Shell metacharacters in questions break heredoc** — when the question contains quotes, parentheses, dollar signs, or other shell-special characters, the `mktemp` + heredoc pattern on the remote host will fail with syntax errors (bash tries to interpret the content). **Workaround:** write the question to a local temp file, `scp` it to the remote host, then use `ask.sh --qfile` with the remote path. Example:
This avoids all shell escaping issues. The `ask.sh` wrapper handles mktemp snapshot + claude invocation + merge.py internally.
## Claude's CLAUDE.md (for reference)
The consulting workspace at `~/claude/hermes_support/CLAUDE.md` documents print mode as the operating procedure and includes behavior rules: zero fluff, SearXNG-first, save findings as .md, stay on topic, ask when unclear, wrap up when done.