From claude-commands
Reproduces CI smoke test failures locally, especially MCP smoke and streaming contract failures. Provides methods to simulate CI environment using Docker, act, or clearing fastembed cache.
How this skill is triggered — by the user, by Claude, or both
Slash command
/claude-commands:smoke-test-localThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
**Purpose**: Reproduce CI smoke test failures locally, especially for `mcp_smoke_test.sh` / SCENARIO 7 streaming contracts.
Purpose: Reproduce CI smoke test failures locally, especially for mcp_smoke_test.sh / SCENARIO 7 streaming contracts.
GitHub Actions runners are ephemeral — fresh VM, empty cache on every job. The main traps:
| Difference | Local | CI (GitHub Actions) |
|---|---|---|
| fastembed ONNX model | Cached in /tmp/fastembed_cache/ from prior runs | Not cached → download fails → classifier falls back to MODE_CHARACTER |
| OS | macOS (/var/folders/…) | Ubuntu Linux (/tmp/…) |
| Services | Real Gemini / Firebase OR manual mock | MOCK_SERVICES_MODE=true + local Flask server |
| Secrets | From shell env or ~/.zshrc | GitHub Secrets injected by workflow |
The $PROJECT_ROOT/Dockerfile pre-downloads the fastembed model at build time (line 12) so Cloud Run containers always have it. The CI smoke runner starts plain gunicorn — no Docker, no pre-downloaded model.
rm -rf ~/.cache/fastembed
FASTEMBED_CACHE_PATH=~/.cache/fastembed TEST_MODE=mock ./scripts/mcp_smoke_test.sh
Simulates a cold CI runner. Confirms the download + pre-warm path works before trusting the cache.
Runs everything inside a clean Ubuntu container — same OS, same filesystem state, no local artifacts:
docker run --rm -it \
-e TEST_MODE=mock \
-e MOCK_SERVICES_MODE=true \
-e TESTING_AUTH_BYPASS=true \
-e ALLOW_TEST_AUTH_BYPASS=true \
-e FASTEMBED_CACHE_PATH=/root/.cache/fastembed \
-v $(pwd):/app -w /app \
python:3.11-slim bash -c "
apt-get update -q && apt-get install -y -q curl git &&
pip install -r $PROJECT_ROOT/requirements.txt -q &&
python3 -c \"from fastembed import TextEmbedding; TextEmbedding(model_name='BAAI/bge-small-en-v1.5', cache_dir='/root/.cache/fastembed'); print('fastembed ready')\" &&
./scripts/mcp_smoke_test.sh
"
When to use: Any time the CI fails but you can't repro locally. OS path differences, missing dependencies, env var issues all show up here.
act (run the actual workflow YAML locally)brew install act
act issue_comment -e .github/test-events/smoke-comment.json --secret-file .env.secrets
When to use: Debugging the workflow YAML itself — if: conditions, step ordering, output passing between steps. NOT for debugging test logic (too much setup friction).
Limitations:
.env.secrets — not stored locallymcp-smoke-tests.yml| Symptom | Root cause | Fix |
|---|---|---|
god_streaming_contract: FAIL - god mode stream done payload missing god_mode_response | Streaming endpoint bypasses _load_campaign_and_continue_story mock check → calls real Gemini API → no god_mode_response in response | Add MOCK_SERVICES_MODE short-circuit in continue_story_streaming (fixed in 5b2cccb79) |
CLASSIFIER: Initialization failed after 3 attempts: NO_SUCHFILE model_optimized.onnx | fastembed model not cached; downloads to ephemeral /tmp | Use FASTEMBED_CACHE_PATH=~/.cache/fastembed + pre-warm step (fixed in ac2965ef3) |
All streaming contracts: expected at least 2 chunk events, saw 0 | Entity tracking list-vs-dict bug causes client_error SSE event before any chunks | _coerce_npc_entry_to_dict guard in _tier_entities |
| Auth 401 on remote preview | TESTING_AUTH_BYPASS not set when MCP_SERVER_URL is set and TEST_MODE=real | Force TESTING_AUTH_BYPASS=true when MCP_SERVER_URL is set (fixed in 2b5040483) |
scripts/mcp_smoke_test.sh — main orchestrator; starts local Flask server if no MCP_SERVER_URL.github/workflows/mcp-smoke-tests.yml — CI workflow; mock + fallback jobstesting_mcp/test_smoke.py — SCENARIO 7: streaming chunk timing contracts$PROJECT_ROOT/mocks/mock_llm_service.py — mock LLM responses for MOCK_SERVICES_MODE=true$PROJECT_ROOT/intent_classifier.py — FastEmbed classifier; _FASTEMBED_CACHE_DIR constant$PROJECT_ROOT/Dockerfile — pre-downloads fastembed model at build time (line 12)$PROJECT_ROOT/llm_service.py:_build_mock_streaming_text — mock response for streaming path in mock mode$PROJECT_ROOT/llm_service.py:continue_story_streaming — streaming path; has MOCK_SERVICES_MODE check before provider selectionThe non-streaming endpoint (/api/campaigns/<id>/interaction) calls world_logic._load_campaign_and_continue_story which checks MOCK_SERVICES_MODE and redirects to continue_story (non-streaming) when true.
The streaming endpoint (/api/campaigns/<id>/interaction/stream) calls streaming_orchestrator.stream_story_with_game_state → llm_service.continue_story_streaming directly, bypassing the _load_campaign_and_continue_story mock check. So MOCK_SERVICES_MODE must also be checked inside continue_story_streaming.
npx claudepluginhub jleechanorg/claude-commands --plugin claude-commandsProvides centralized testing utilities, debug protocols, and CI/local parity guidelines. Includes shared lib modules for evidence capture, MCP client, campaign utilities, and browser testing guidance for Playwright vs chrome-superpower.
Tests GitHub Actions workflows locally using nektos/act to validate CI/CD pipelines before pushing, catch errors early, and debug failures.
Runs fast smoke tests validating critical paths like health checks, UI, auth, and APIs post-deployment using curl, Playwright, or Bash scripts.