From d074d1c046ea2dda8f151a31e19ddfa0f7e96387 Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Tue, 5 May 2026 06:09:11 +0900 Subject: [PATCH] fix(mcp): exit 1 when JSON envelope contains ok:false (#2995) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix(mcp): exit 1 when JSON envelope contains ok:false mcp info, mcp describe, and mcp list-filter all return {"action":"error","ok":false,...} but previously exited 0, requiring automation callers to inspect the envelope field. After this fix: print_mcp detects ok:false in the rendered JSON value and calls process::exit(1) after printing, so the exit code reflects the semantic error in the envelope. Unaffected: mcp list, mcp show, mcp help all have no ok field and continue to exit 0 (they are not error paths). Closes ROADMAP #68 (partial — agents bogus/mcp show nonexistent found:false remain exit:0 as they use different envelope shapes). * feat(scripts): add dogfood-build.sh — build from checkout and verify provenance Builds claw from the current HEAD, then checks that the binary's git_sha matches git rev-parse --short HEAD. Exits non-zero if the binary is stale or provenance is opaque (git_sha: null). Usage: CLAW=$(bash scripts/dogfood-build.sh) # fail-fast if stale $CLAW version --output-format json # provenance confirmed Addresses ROADMAP #69: dogfooders using a stale installed binary cannot attribute behavior to specific commits. This script makes dogfood round zero unambiguous. Also documents the safe workaround for contributors who have a stale system-installed binary. --- rust/crates/rusty-claude-cli/src/main.rs | 15 +++++++--- scripts/dogfood-build.sh | 38 ++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 4 deletions(-) create mode 100755 scripts/dogfood-build.sh diff --git a/rust/crates/rusty-claude-cli/src/main.rs b/rust/crates/rusty-claude-cli/src/main.rs index d3ee789b..a4ff9bc9 100644 --- a/rust/crates/rusty-claude-cli/src/main.rs +++ b/rust/crates/rusty-claude-cli/src/main.rs @@ -5107,10 +5107,17 @@ impl LiveCli { let cwd = env::current_dir()?; match output_format { CliOutputFormat::Text => println!("{}", handle_mcp_slash_command(args, &cwd)?), - CliOutputFormat::Json => println!( - "{}", - serde_json::to_string_pretty(&handle_mcp_slash_command_json(args, &cwd)?)? - ), + CliOutputFormat::Json => { + let value = handle_mcp_slash_command_json(args, &cwd)?; + // Propagate ok:false → non-zero exit so automation callers + // can rely on exit code instead of inspecting the envelope. + // (#68: mcp error envelopes previously always exited 0.) + let is_error = value.get("ok").and_then(|v| v.as_bool()) == Some(false); + println!("{}", serde_json::to_string_pretty(&value)?); + if is_error { + std::process::exit(1); + } + } } Ok(()) } diff --git a/scripts/dogfood-build.sh b/scripts/dogfood-build.sh new file mode 100755 index 00000000..5617a27e --- /dev/null +++ b/scripts/dogfood-build.sh @@ -0,0 +1,38 @@ +#!/usr/bin/env bash +# dogfood-build.sh — Build claw from current checkout and verify provenance. +# Usage: bash scripts/dogfood-build.sh +# On success: prints the verified binary path. Use as: +# CLAW=$(bash scripts/dogfood-build.sh) && $CLAW version --output-format json +set -euo pipefail + +REPO_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)" +RUST_DIR="$REPO_ROOT/rust" +BINARY="$RUST_DIR/target/debug/claw" +EXPECTED_SHA="$(git -C "$REPO_ROOT" rev-parse --short HEAD)" + +echo "▶ Building claw from $REPO_ROOT ($(git -C "$REPO_ROOT" log --oneline -1))..." >&2 +cargo build --manifest-path "$RUST_DIR/Cargo.toml" -p rusty-claude-cli -q + +if [[ ! -x "$BINARY" ]]; then + echo "✗ Build succeeded but binary not found at $BINARY" >&2 + exit 1 +fi + +BINARY_SHA=$("$BINARY" version --output-format json 2>/dev/null \ + | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('git_sha','null'))" 2>/dev/null || echo "null") + +if [[ "$BINARY_SHA" == "null" || -z "$BINARY_SHA" ]]; then + echo "✗ Provenance check failed: binary reports git_sha: null" >&2 + echo " Binary: $BINARY" >&2 + exit 1 +fi + +if [[ "$BINARY_SHA" != "$EXPECTED_SHA" ]]; then + echo "✗ Provenance mismatch: binary=$BINARY_SHA, HEAD=$EXPECTED_SHA" >&2 + echo " Rerun after 'git pull' or check for uncommitted changes." >&2 + exit 1 +fi + +echo "✓ Binary verified: $BINARY_SHA == HEAD ($EXPECTED_SHA)" >&2 +echo " To dogfood: export CLAW=$BINARY" >&2 +echo "$BINARY"