jobbsok/scripts/cowork_probe_check.sh
Kjell Tore Guttormsen e1ad1b688f test(m1): add a host-side progress check for the Cowork probe
The probe's four questions are answered by the operator in Cowork, but three of
them leave traces on this Mac, and a trace is a measurement where "what did you
see in the UI" is a recollection. This reads the traces so each step can be
verified before the next one starts.

What it can actually see, discovered by measurement rather than assumed:

- Claude Desktop writes one log per MCP server, named mcp-server-<name>.log,
  and it records the full JSON-RPC handshake (verified against the existing
  mcp-server-filesystem.log: initialize, notifications/initialized, tools/list
  all appear as "Message from client: method=..."). So if Cowork spawns
  probe-tools, the entire exchange is readable from disk -- including the
  server's own stderr line, which carries the interpreter version.
- The absence of that log file is itself informative: it means Cowork never
  attempted to start the server, which is a different finding from starting
  and failing.
- pgrep on probe_tools.py confirms a live process, but its absence is not a
  nei: a server may be spawned on demand. The log is the record; the process
  check is corroboration. The script says so rather than over-claiming.

The Cowork store search is bounded to the session directory by name plus the
small manifests. The first version ran a recursive content grep over the whole
Claude support directory and did not finish inside two minutes -- it walks
caches and VM images. A check that hangs is not a check. Bounded version runs
in 0.17s.

It reports, it does not gate: exit 0 when everything has landed, 1 while
anything is pending. Baseline now: vehicle and archive done, three pending.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-04 18:43:22 +02:00

126 lines
5 KiB
Bash
Executable file

#!/bin/bash
# Report, from the host, how far the Cowork probe has actually got.
#
# The probe asks four questions that only the operator can answer in a Cowork
# session. But three of them leave traces on this Mac, and a trace is a
# measurement where "what did you see in the UI" is a recollection. This script
# reads those traces so each step can be verified before the next one starts.
#
# It reports; it does not gate. Exit 0 when every check has landed, 1 while any
# is still pending, 2 on a broken precondition.
#
# bash 3.2-clean, ASCII-only, read-only: it opens nothing and writes nothing.
set -u
SCRIPT_DIR=$(cd "$(dirname "$0")" && pwd)
REPO_ROOT=$(cd "$SCRIPT_DIR/.." && pwd)
PROBE_DOC="$REPO_ROOT/docs/cowork-probe.md"
FIXTURE="$REPO_ROOT/tests/fixtures/cowork-probe"
ARCHIVE="${JOBBSOK_PROBE_ARCHIVE:-/tmp/jobbsok-probe.plugin}"
CLAUDE_LOGS="$HOME/Library/Logs/Claude"
MCP_LOG="$CLAUDE_LOGS/mcp-server-probe-tools.log"
CLAUDE_SUPPORT="$HOME/Library/Application Support/Claude"
pending=0
say() { printf '%s\n' "$1"; }
done_() { printf ' [DONE] %s\n' "$1"; }
pend() { printf ' [PENDING] %s\n' "$1"; pending=$((pending+1)); }
info() { printf ' %s\n' "$1"; }
say ""
say "=== 1. Probe vehicle and archive ============================================"
n=$(find "$FIXTURE" -type f 2>/dev/null | wc -l | tr -d ' ')
if [ "$n" = "4" ]; then
done_ "probe vehicle: 4 files under tests/fixtures/cowork-probe"
else
pend "probe vehicle: expected 4 files, found $n"
fi
if [ -f "$ARCHIVE" ]; then
a=$(unzip -Z1 "$ARCHIVE" 2>/dev/null | wc -l | tr -d ' ')
leak=$(unzip -Z1 "$ARCHIVE" 2>/dev/null | grep -cE '^\.git|STATE\.md|\.venv|^\.claude/')
if [ "$a" = "4" ] && [ "$leak" = "0" ]; then
done_ "archive: $ARCHIVE ($a files, no leaked entries)"
else
pend "archive: $ARCHIVE has $a entries and $leak leaked ones - rebuild it"
fi
else
pend "archive not built yet: $ARCHIVE"
info "cd tests/fixtures/cowork-probe && zip -X $ARCHIVE .claude-plugin/plugin.json .mcp.json probe_tools.py skills/probe-versjon/SKILL.md"
fi
say ""
say "=== 2. Is jobbsok-probe installed in Cowork? ================================="
# Bounded on purpose. A recursive content grep over the whole Claude support
# directory walks caches and VM images and does not finish; the plugin store is
# a known, shallow place, so look there by name and read only the small
# manifests. A check that hangs is not a check.
SESSIONS="$CLAUDE_SUPPORT/local-agent-mode-sessions"
hits=$(find "$SESSIONS" -maxdepth 6 -name '*jobbsok-probe*' 2>/dev/null | head -5)
for m in $(find "$SESSIONS" -maxdepth 4 \( -name 'manifest.json' -o -name 'cowork_settings.json' -o -name 'known_marketplaces.json' \) 2>/dev/null); do
if grep -q 'jobbsok-probe' "$m" 2>/dev/null; then
hits="$hits
$m"
fi
done
hits=$(printf '%s\n' "$hits" | grep -v '^$')
if [ -n "$hits" ]; then
done_ "Cowork store mentions jobbsok-probe:"
printf '%s\n' "$hits" | sed 's|^| |'
else
pend "no trace of jobbsok-probe in Cowork's store yet"
info "Upload the archive, or drag it into the Cowork chat and accept it."
fi
say ""
say "=== 3. Did Cowork spawn the probe-tools server on THIS Mac? =================="
say " (this is the -Host-MCP: question, measured rather than recalled)"
if [ -f "$MCP_LOG" ]; then
done_ "log exists: $MCP_LOG"
info "last written: $(stat -f '%Sm' "$MCP_LOG" 2>/dev/null)"
for m in initialize tools/list tools/call; do
if grep -q "method=\"$m\"" "$MCP_LOG" 2>/dev/null; then
done_ "handshake: $m seen"
else
pend "handshake: $m NOT seen in the log"
fi
done
started=$(grep -h '\[probe-tools\] started on' "$MCP_LOG" 2>/dev/null | tail -1)
if [ -n "$started" ]; then
done_ "server's own stderr: $started"
else
pend "the server never printed its start line - it may have failed to launch"
fi
else
pend "no log at $MCP_LOG"
info "Claude names each server's log mcp-server-<name>.log. No file means"
info "Cowork never even attempted to start probe-tools."
fi
if pgrep -f probe_tools.py >/dev/null 2>&1; then
done_ "process alive right now: $(pgrep -fl probe_tools.py | head -1 | cut -c1-100)"
else
info "no probe_tools.py process running at this instant (it may be started"
info "on demand, so this alone is not a nei - the log above is the record)"
fi
say ""
say "=== 4. Are the four answers written down? ==================================="
RE='^- (Sesjonsmodus|Host-MCP|python3|CLAUDE_PLUGIN_ROOT): +(lokal VM|sky|ja|nei)\b'
c=$(grep -cE "$RE" "$PROBE_DOC" 2>/dev/null)
if [ "$c" = "4" ]; then
done_ "all four answered - plan Step 1 Verify passes"
grep -E '^- (Sesjonsmodus|Host-MCP|python3|CLAUDE_PLUGIN_ROOT):' "$PROBE_DOC" | sed 's|^| |'
else
pend "$c of 4 answered in docs/cowork-probe.md"
grep -E '^- (Sesjonsmodus|Host-MCP|python3|CLAUDE_PLUGIN_ROOT):' "$PROBE_DOC" 2>/dev/null | sed 's|^| |'
fi
say ""
say "============================================================================"
if [ "$pending" -eq 0 ]; then
say "ALL CHECKS LANDED."
exit 0
fi
say "$pending check(s) still pending."
exit 1