Shared memory and context tools for agentic work.
Code Rooms
#!/usr/bin/env bash
# =============================================================================
# m1nd End-to-End Integration Stress + Calibration Test
# Agent D0 — final quality gate
set -euo pipefail
SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
BINARY="$SCRIPT_DIR/target/release/m1nd-mcp"
WORKDIR="/tmp/m1nd_e2e_test_$$"
INGEST_PATH="$SCRIPT_DIR"
GRAPH_SNAP="$WORKDIR/graph_snapshot.json"
PLASTICITY="$WORKDIR/plasticity_state.json"
PASS=0
FAIL=0
TOTAL=0
ISSUES=""
mkdir -p "$WORKDIR"
# --- Helpers ----------------------------------------------------------------
rpc() {
# Build a JSON-RPC tools/call message
local name="$1"
local args="$2"
local id="$3"
name="${name#m1nd.}"
name="${name//./_}"
printf '{"jsonrpc":"2.0","method":"tools/call","id":%d,"params":{"name":"%s","arguments":%s}}\n' "$id" "$name" "$args"
}
init_msg() {
echo '{"jsonrpc":"2.0","method":"initialize","id":1,"params":{"protocolVersion":"2024-11-05","capabilities":{},"clientInfo":{"name":"e2e-test","version":"1.0"}}}'
assert_ok() {
local test_name="$1"
local response="$2"
local condition="$3" # jq expression that should return "true"
TOTAL=$((TOTAL + 1))
local result
result=$(echo "$response" | jq -r "$condition" 2>/dev/null || echo "PARSE_ERROR")
if [ "$result" = "true" ]; then
PASS=$((PASS + 1))
echo " [PASS] $test_name"
else
FAIL=$((FAIL + 1))
echo " [FAIL] $test_name (got: $result)"
ISSUES="$ISSUES\n - $test_name"
# Print response snippet for debugging
echo " Response: $(echo "$response" | head -c 500)"
fi
extract_content_text() {
# MCP wraps tool results in content[].text — extract and parse the inner JSON
echo "$1" | jq -r '.result.content[0].text // empty' 2>/dev/null
# --- Phase 1: Build check ---------------------------------------------------
echo ""
echo "================================================================"
echo " PHASE 1: Build and binary check"
if [ ! -x "$BINARY" ]; then
echo "[FATAL] Binary not found at $BINARY. Run: cargo build --release"
exit 1
echo " Binary: $BINARY (OK)"
# --- Phase 2: Self-ingest test -----------------------------------------------
echo " PHASE 2: Self-ingest test (m1nd ingests itself)"
# Build the full sequence of JSON-RPC messages for session 1.
# We send them all as stdin lines; the server processes them sequentially.
# Collect ALL responses (one per line).
SESSION1_INPUT=$(cat <<'JSONRPC_EOF'
INIT_PLACEHOLDER
INGEST_PLACEHOLDER
HEALTH1_PLACEHOLDER
ACTIVATE_PLACEHOLDER
IMPACT_PLACEHOLDER
MISSING_PLACEHOLDER
WHY_PLACEHOLDER
WARMUP_PLACEHOLDER
COUNTERFACTUAL_PLACEHOLDER
PREDICT_PLACEHOLDER
FINGERPRINT_PLACEHOLDER
DRIFT_PLACEHOLDER
LEARN_PLACEHOLDER
RESONATE_PLACEHOLDER
HEALTH2_PLACEHOLDER
JSONRPC_EOF
)
# Build actual messages
MSG_INIT=$(init_msg)
MSG_INGEST=$(rpc "m1nd.ingest" "{\"path\":\"$INGEST_PATH\",\"agent_id\":\"e2e-test\",\"mode\":\"replace\"}" 2)
MSG_HEALTH1=$(rpc "m1nd.health" '{"agent_id":"e2e-test"}' 3)
MSG_ACTIVATE=$(rpc "m1nd.activate" '{"query":"spreading activation","agent_id":"e2e-test","top_k":20}' 4)
# External IDs in the graph use "file::" prefix (set by ingestor)
MSG_IMPACT=$(rpc "m1nd.impact" '{"node_id":"file::m1nd-core/src/graph.rs","agent_id":"e2e-test","direction":"forward"}' 5)
MSG_MISSING=$(rpc "m1nd.missing" '{"query":"graph query","agent_id":"e2e-test"}' 6)
MSG_WHY=$(rpc "m1nd.why" '{"source":"file::m1nd-core/src/graph.rs","target":"file::m1nd-core/src/activation.rs","agent_id":"e2e-test"}' 7)
MSG_WARMUP=$(rpc "m1nd.warmup" '{"task_description":"implement temporal scoring","agent_id":"e2e-test"}' 8)
MSG_COUNTERFACTUAL=$(rpc "m1nd.counterfactual" '{"node_ids":["file::m1nd-core/src/graph.rs","file::m1nd-core/src/types.rs"],"agent_id":"e2e-test"}' 9)
MSG_PREDICT=$(rpc "m1nd.predict" '{"changed_node":"file::m1nd-core/src/graph.rs","agent_id":"e2e-test"}' 10)
MSG_FINGERPRINT=$(rpc "m1nd.fingerprint" '{"target_node":"file::m1nd-core/src/graph.rs","agent_id":"e2e-test"}' 11)
MSG_DRIFT=$(rpc "m1nd.drift" '{"agent_id":"e2e-test"}' 12)
MSG_LEARN=$(rpc "m1nd.learn" '{"query":"test","agent_id":"e2e-test","feedback":"correct","node_ids":["file::m1nd-core/src/graph.rs","file::m1nd-core/src/types.rs"]}' 13)
MSG_RESONATE=$(rpc "m1nd.resonate" '{"query":"graph activation","agent_id":"e2e-test"}' 14)
MSG_HEALTH2=$(rpc "m1nd.health" '{"agent_id":"e2e-test"}' 15)
# Combine all messages
ALL_MSGS=$(printf '%s\n' \
"$MSG_INIT" \
"$MSG_INGEST" \
"$MSG_HEALTH1" \
"$MSG_ACTIVATE" \
"$MSG_IMPACT" \
"$MSG_MISSING" \
"$MSG_WHY" \
"$MSG_WARMUP" \
"$MSG_COUNTERFACTUAL" \
"$MSG_PREDICT" \
"$MSG_FINGERPRINT" \
"$MSG_DRIFT" \
"$MSG_LEARN" \
"$MSG_RESONATE" \
"$MSG_HEALTH2"
echo " Sending 15 JSON-RPC messages to server..."
# Run the server with env vars pointing to our workdir
RESPONSES=$(echo "$ALL_MSGS" | M1ND_GRAPH_SOURCE="$GRAPH_SNAP" M1ND_PLASTICITY_STATE="$PLASTICITY" timeout 120 "$BINARY" 2>"$WORKDIR/stderr.log")
# Parse responses (one per line, corresponding to ids 1-15)
echo " Server exited. Parsing responses..."
# Extract each response by id
get_resp() {
local id=$1
echo "$RESPONSES" | jq -c --argjson id "$id" 'select(.id == $id)' | head -n 1
R_INIT=$(get_resp 1)
R_INGEST=$(get_resp 2)
R_HEALTH1=$(get_resp 3)
R_ACTIVATE=$(get_resp 4)
R_IMPACT=$(get_resp 5)
R_MISSING=$(get_resp 6)
R_WHY=$(get_resp 7)
R_WARMUP=$(get_resp 8)
R_COUNTERFACTUAL=$(get_resp 9)
R_PREDICT=$(get_resp 10)
R_FINGERPRINT=$(get_resp 11)
R_DRIFT=$(get_resp 12)
R_LEARN=$(get_resp 13)
R_RESONATE=$(get_resp 14)
R_HEALTH2=$(get_resp 15)
echo "--- Step 1: Initialize ---"
assert_ok "initialize returns protocolVersion" "$R_INIT" '.result.protocolVersion == "2024-11-05"'
assert_ok "initialize returns serverInfo" "$R_INIT" '.result.serverInfo.name == "m1nd-mcp"'
echo "--- Step 2: m1nd.ingest (self-ingest) ---"
INGEST_TEXT=$(extract_content_text "$R_INGEST")
assert_ok "ingest: no error" "$R_INGEST" '(.result.isError // false) == false'
assert_ok "ingest: nodes_created > 0" "$INGEST_TEXT" '(.nodes_created // 0) > 0'
assert_ok "ingest: edges_created > 0" "$INGEST_TEXT" '(.edges_created // 0) > 0'
INGEST_NODES=$(echo "$INGEST_TEXT" | jq '.nodes_created // 0')
INGEST_EDGES=$(echo "$INGEST_TEXT" | jq '.edges_created // 0')
echo " Nodes: $INGEST_NODES, Edges: $INGEST_EDGES"
echo "--- Step 3: m1nd.health (post-ingest) ---"
HEALTH1_TEXT=$(extract_content_text "$R_HEALTH1")
assert_ok "health1: no error" "$R_HEALTH1" '(.result.isError // false) == false'
assert_ok "health1: node_count > 100" "$HEALTH1_TEXT" '(.node_count // 0) > 100'
assert_ok "health1: edge_count > 200" "$HEALTH1_TEXT" '(.edge_count // 0) > 200'
H1_NODES=$(echo "$HEALTH1_TEXT" | jq '.node_count // 0')
H1_EDGES=$(echo "$HEALTH1_TEXT" | jq '.edge_count // 0')
echo " Health: $H1_NODES nodes, $H1_EDGES edges"
echo "--- Step 4: m1nd.activate ('spreading activation') ---"
ACTIVATE_TEXT=$(extract_content_text "$R_ACTIVATE")
assert_ok "activate: no error" "$R_ACTIVATE" '(.result.isError // false) == false'
assert_ok "activate: results non-empty" "$ACTIVATE_TEXT" '((.activated // []) | length) > 0'
assert_ok "activate: top result activation > 0" "$ACTIVATE_TEXT" '(.activated[0].activation // 0) > 0'
# Sanity: all activations in valid range (not NaN, not negative)
ACTIVATE_SANE=$(echo "$ACTIVATE_TEXT" | jq '[.activated[]?.activation // empty | . >= 0 and . <= 1000] | all')
assert_ok "activate: all activations >= 0 and finite" "$ACTIVATE_SANE" '. == true'
echo " Top result: $(echo "$ACTIVATE_TEXT" | jq -r '.activated[0].label // "N/A"') ($(echo "$ACTIVATE_TEXT" | jq '.activated[0].activation // 0'))"
echo "--- Step 5: m1nd.impact ---"
IMPACT_TEXT=$(extract_content_text "$R_IMPACT")
assert_ok "impact: no error" "$R_IMPACT" '(.result.isError // false) == false'
assert_ok "impact: blast_radius non-empty" "$IMPACT_TEXT" '((.blast_radius // []) | length) > 0'
# Sanity: all signal_strengths >= 0
IMPACT_SANE=$(echo "$IMPACT_TEXT" | jq '[.blast_radius[]?.signal_strength // empty | . >= 0] | all')
assert_ok "impact: all signal_strengths >= 0" "$IMPACT_SANE" '. == true'
echo " Blast radius: $(echo "$IMPACT_TEXT" | jq '(.blast_radius // []) | length') nodes"
echo "--- Step 6: m1nd.missing ---"
MISSING_TEXT=$(extract_content_text "$R_MISSING")
assert_ok "missing: no error" "$R_MISSING" '(.result.isError // false) == false'
assert_ok "missing: returns structural_holes field" "$MISSING_TEXT" 'has("structural_holes")'
echo " Structural holes: $(echo "$MISSING_TEXT" | jq '(.structural_holes // []) | length')"
echo "--- Step 7: m1nd.why ---"
WHY_TEXT=$(extract_content_text "$R_WHY")
assert_ok "why: no error" "$R_WHY" '(.result.isError // false) == false'
assert_ok "why: has paths field" "$WHY_TEXT" 'has("paths")'
# Path may or may not exist depending on graph connectivity
WHY_FOUND=$(echo "$WHY_TEXT" | jq '.found // false')
echo " Path found: $WHY_FOUND, Paths: $(echo "$WHY_TEXT" | jq '(.paths // []) | length')"
echo "--- Step 8: m1nd.warmup ---"
WARMUP_TEXT=$(extract_content_text "$R_WARMUP")
assert_ok "warmup: no error" "$R_WARMUP" '(.result.isError // false) == false'
assert_ok "warmup: has seeds field" "$WARMUP_TEXT" 'has("seeds")'
assert_ok "warmup: total_seeds > 0" "$WARMUP_TEXT" '(.total_seeds // 0) > 0'
echo " Seeds: $(echo "$WARMUP_TEXT" | jq '.total_seeds // 0'), Priming: $(echo "$WARMUP_TEXT" | jq '.total_priming // 0')"
echo "--- Step 9: m1nd.counterfactual ---"
CF_TEXT=$(extract_content_text "$R_COUNTERFACTUAL")
assert_ok "counterfactual: no error" "$R_COUNTERFACTUAL" '(.result.isError // false) == false'
assert_ok "counterfactual: has total_impact" "$CF_TEXT" 'has("total_impact")'
assert_ok "counterfactual: total_impact >= 0" "$CF_TEXT" '(.total_impact // -1) >= 0'
# Check pct_activation_lost in [0, 100]
PCT_LOST=$(echo "$CF_TEXT" | jq '.pct_activation_lost // -1')
assert_ok "counterfactual: pct_activation_lost in [0,100]" "$CF_TEXT" '(.pct_activation_lost // -1) >= 0 and (.pct_activation_lost // 101) <= 100'
echo " Impact: $(echo "$CF_TEXT" | jq '.total_impact // 0'), Pct lost: $PCT_LOST%"
echo "--- Step 10: m1nd.predict ---"
PREDICT_TEXT=$(extract_content_text "$R_PREDICT")
assert_ok "predict: no error" "$R_PREDICT" '(.result.isError // false) == false'
assert_ok "predict: has predictions field" "$PREDICT_TEXT" 'has("predictions")'
echo " Predictions: $(echo "$PREDICT_TEXT" | jq '(.predictions // []) | length')"
echo "--- Step 11: m1nd.fingerprint ---"
FP_TEXT=$(extract_content_text "$R_FINGERPRINT")
assert_ok "fingerprint: no error" "$R_FINGERPRINT" '(.result.isError // false) == false'
# Should have either equivalents or equivalent_pairs
assert_ok "fingerprint: has data" "$FP_TEXT" 'has("equivalents") or has("equivalent_pairs") or has("target_node")'
echo " Equivalents: $(echo "$FP_TEXT" | jq '(.equivalents // []) | length')"
echo "--- Step 12: m1nd.drift ---"
DRIFT_TEXT=$(extract_content_text "$R_DRIFT")
assert_ok "drift: no error" "$R_DRIFT" '(.result.isError // false) == false'
assert_ok "drift: has weight_drift" "$DRIFT_TEXT" 'has("weight_drift")'
assert_ok "drift: has top_velocities" "$DRIFT_TEXT" 'has("top_velocities")'
echo " Drifted edges: $(echo "$DRIFT_TEXT" | jq '(.weight_drift // []) | length'), Top velocities: $(echo "$DRIFT_TEXT" | jq '(.top_velocities // []) | length')"
echo "--- Step 13: m1nd.learn ---"
LEARN_TEXT=$(extract_content_text "$R_LEARN")
assert_ok "learn: no error" "$R_LEARN" '(.result.isError // false) == false'
assert_ok "learn: edges_modified >= 0" "$LEARN_TEXT" '(.edges_modified // -1) >= 0'
echo " Edges modified: $(echo "$LEARN_TEXT" | jq '.edges_modified // 0')"
echo "--- Step 14: m1nd.resonate ---"
RESONATE_TEXT=$(extract_content_text "$R_RESONATE")
assert_ok "resonate: no error" "$R_RESONATE" '(.result.isError // false) == false'
assert_ok "resonate: has harmonics" "$RESONATE_TEXT" 'has("harmonics")'
assert_ok "resonate: has sympathetic_pairs" "$RESONATE_TEXT" 'has("sympathetic_pairs")'
assert_ok "resonate: has resonant_frequencies" "$RESONATE_TEXT" 'has("resonant_frequencies")'
assert_ok "resonate: has wave_pattern" "$RESONATE_TEXT" 'has("wave_pattern")'
echo " Harmonics: $(echo "$RESONATE_TEXT" | jq '(.harmonics // []) | length'), Sympathetic pairs: $(echo "$RESONATE_TEXT" | jq '(.sympathetic_pairs // []) | length')"
echo "--- Step 15: m1nd.health (post-all-tools) ---"
HEALTH2_TEXT=$(extract_content_text "$R_HEALTH2")
assert_ok "health2: no error" "$R_HEALTH2" '(.result.isError // false) == false'
assert_ok "health2: queries_processed > 0" "$HEALTH2_TEXT" '(.queries_processed // 0) > 0'
assert_ok "health2: active_sessions non-empty" "$HEALTH2_TEXT" '((.active_sessions // []) | length) > 0'
# Consistency: node/edge count should match health1
H2_NODES=$(echo "$HEALTH2_TEXT" | jq '.node_count // 0')
H2_EDGES=$(echo "$HEALTH2_TEXT" | jq '.edge_count // 0')
assert_ok "health2: node_count consistent ($H1_NODES == $H2_NODES)" "$HEALTH2_TEXT" ".node_count == $H1_NODES"
assert_ok "health2: edge_count consistent ($H1_EDGES == $H2_EDGES)" "$HEALTH2_TEXT" ".edge_count == $H1_EDGES"
echo " Queries processed: $(echo "$HEALTH2_TEXT" | jq '.queries_processed // 0')"
# --- Phase 3: Persistence round-trip -----------------------------------------
echo " PHASE 3: Persistence round-trip"
# Verify snapshot was written
if [ -f "$GRAPH_SNAP" ]; then
echo " Snapshot exists: $GRAPH_SNAP ($(wc -c < "$GRAPH_SNAP") bytes)"
echo " [FAIL] No snapshot file at $GRAPH_SNAP"
# Restart server and verify graph survives
echo " Restarting server..."
MSG_INIT2=$(init_msg)
MSG_HEALTH3=$(rpc "m1nd.health" '{"agent_id":"e2e-test-session2"}' 3)
MSG_ACTIVATE2=$(rpc "m1nd.activate" '{"query":"spreading activation","agent_id":"e2e-test-session2","top_k":20}' 4)
ALL_MSGS2=$(printf '%s\n' "$MSG_INIT2" "$MSG_HEALTH3" "$MSG_ACTIVATE2")
RESPONSES2=$(echo "$ALL_MSGS2" | M1ND_GRAPH_SOURCE="$GRAPH_SNAP" M1ND_PLASTICITY_STATE="$PLASTICITY" timeout 60 "$BINARY" 2>"$WORKDIR/stderr2.log")
R_HEALTH3=$(echo "$RESPONSES2" | while IFS= read -r line; do
rid=$(echo "$line" | jq -r '.id // empty' 2>/dev/null)
if [ "$rid" = "3" ]; then echo "$line"; break; fi
done)
R_ACTIVATE2=$(echo "$RESPONSES2" | while IFS= read -r line; do
if [ "$rid" = "4" ]; then echo "$line"; break; fi
echo "--- Persistence: health after restart ---"
HEALTH3_TEXT=$(extract_content_text "$R_HEALTH3")
H3_NODES=$(echo "$HEALTH3_TEXT" | jq '.node_count // 0')
H3_EDGES=$(echo "$HEALTH3_TEXT" | jq '.edge_count // 0')
assert_ok "persistence: node_count matches pre-restart ($H1_NODES == $H3_NODES)" "$HEALTH3_TEXT" ".node_count == $H1_NODES"
assert_ok "persistence: edge_count matches pre-restart ($H1_EDGES == $H3_EDGES)" "$HEALTH3_TEXT" ".edge_count == $H1_EDGES"
echo " Post-restart: $H3_NODES nodes, $H3_EDGES edges"
echo "--- Persistence: activate after restart ---"
ACTIVATE2_TEXT=$(extract_content_text "$R_ACTIVATE2")
assert_ok "persistence: activate returns results" "$ACTIVATE2_TEXT" '((.activated // []) | length) > 0'
echo " Top result: $(echo "$ACTIVATE2_TEXT" | jq -r '.activated[0].label // "N/A"')"
# --- Phase 4: Sanity assertions -----------------------------------------------
echo " PHASE 4: Sanity assertions"
# Check for any JSON parse errors in responses
PARSE_ERRORS=0
for resp_var in "$R_INIT" "$R_INGEST" "$R_HEALTH1" "$R_ACTIVATE" "$R_IMPACT" "$R_MISSING" "$R_WHY" "$R_WARMUP" "$R_COUNTERFACTUAL" "$R_PREDICT" "$R_FINGERPRINT" "$R_DRIFT" "$R_LEARN" "$R_RESONATE" "$R_HEALTH2"; do
if [ -z "$resp_var" ]; then
PARSE_ERRORS=$((PARSE_ERRORS + 1))
elif ! echo "$resp_var" | jq . >/dev/null 2>&1; then
done
if [ "$PARSE_ERRORS" -eq 0 ]; then
echo " [PASS] No JSON parse errors in any response"
echo " [FAIL] $PARSE_ERRORS responses had JSON parse errors"
# Check for isError in any tool response
ERROR_COUNT=0
for resp_var in "$R_INGEST" "$R_HEALTH1" "$R_ACTIVATE" "$R_IMPACT" "$R_MISSING" "$R_WHY" "$R_WARMUP" "$R_COUNTERFACTUAL" "$R_PREDICT" "$R_FINGERPRINT" "$R_DRIFT" "$R_LEARN" "$R_RESONATE" "$R_HEALTH2"; do
is_err=$(echo "$resp_var" | jq -r '.result.isError // false' 2>/dev/null)
if [ "$is_err" = "true" ]; then
ERROR_COUNT=$((ERROR_COUNT + 1))
tool_err=$(echo "$resp_var" | jq -r '.result.content[0].text // "unknown"' 2>/dev/null)
echo " [WARN] Tool error: $tool_err"
if [ "$ERROR_COUNT" -eq 0 ]; then
echo " [PASS] No tool returned isError for valid inputs"
echo " [FAIL] $ERROR_COUNT tools returned isError"
# Check NaN/negative in activation scores
NAN_CHECK=$(echo "$ACTIVATE_TEXT" | jq '[.activated[]?.activation // empty | isnan or . < 0] | any' 2>/dev/null || echo "true")
if [ "$NAN_CHECK" = "false" ]; then
echo " [PASS] No NaN or negative activation scores"
echo " [FAIL] Found NaN or negative activation scores"
# Check impact percentages in [0, 100]
if [ -n "$CF_TEXT" ]; then
PCT_CHECK=$(echo "$CF_TEXT" | jq '(.pct_activation_lost // 0) >= 0 and (.pct_activation_lost // 0) <= 100' 2>/dev/null || echo "false")
if [ "$PCT_CHECK" = "true" ]; then
echo " [PASS] Impact percentages in [0, 100]"
echo " [FAIL] Impact percentages out of range"
# Check stderr for panics
STDERR_LOG="$WORKDIR/stderr.log"
if grep -qi "panic\|thread.*panicked\|SIGSEGV\|SIGABRT" "$STDERR_LOG" 2>/dev/null; then
echo " [FAIL] Server panicked! See: $STDERR_LOG"
echo " [PASS] No panics in server stderr"
# --- Final report -------------------------------------------------------------
echo " FINAL REPORT"
echo " Results: $PASS passed / $FAIL failed / $TOTAL total"
echo " Ingest stats:"
echo " Nodes created: $INGEST_NODES"
echo " Edges created: $INGEST_EDGES"
echo " Final health:"
echo " Node count: $H1_NODES"
echo " Edge count: $H1_EDGES"
echo " Persistence:"
echo " Pre-restart: $H1_NODES nodes, $H1_EDGES edges"
if [ "$FAIL" -gt 0 ]; then
echo " Issues found:"
echo -e "$ISSUES"
echo " STATUS: FAIL — $FAIL issues need fixing"
# Save detailed responses for debugging
echo "$R_INIT" > "$WORKDIR/resp_init.json"
echo "$R_INGEST" > "$WORKDIR/resp_ingest.json"
echo "$R_HEALTH1" > "$WORKDIR/resp_health1.json"
echo "$R_ACTIVATE" > "$WORKDIR/resp_activate.json"
echo "$R_IMPACT" > "$WORKDIR/resp_impact.json"
echo "$R_MISSING" > "$WORKDIR/resp_missing.json"
echo "$R_WHY" > "$WORKDIR/resp_why.json"
echo "$R_WARMUP" > "$WORKDIR/resp_warmup.json"
echo "$R_COUNTERFACTUAL" > "$WORKDIR/resp_counterfactual.json"
echo "$R_PREDICT" > "$WORKDIR/resp_predict.json"
echo "$R_FINGERPRINT" > "$WORKDIR/resp_fingerprint.json"
echo "$R_DRIFT" > "$WORKDIR/resp_drift.json"
echo "$R_LEARN" > "$WORKDIR/resp_learn.json"
echo "$R_RESONATE" > "$WORKDIR/resp_resonate.json"
echo "$R_HEALTH2" > "$WORKDIR/resp_health2.json"
echo " Debug responses saved to: $WORKDIR/"
echo " STATUS: PASS — all tools produce sane results"
echo " Session 1 of 3 clean sessions: COMPLETE"
echo " Workdir: $WORKDIR"
echo " Server logs: $WORKDIR/stderr.log"
# Cleanup: remove workdir only on success
if [ "$FAIL" -eq 0 ]; then
rm -rf "$WORKDIR"
exit "$FAIL"