Files
ruvnet--ruflo/.github/workflows/v3-ci.yml
T
wehub-resource-sync 23f7624596
🔄 Automated Rollback Manager / 🔄 Execute Rollback (push) Blocked by required conditions
🔄 Automated Rollback Manager / ✅ Post-Rollback Verification (push) Blocked by required conditions
🔄 Automated Rollback Manager / 📊 Rollback Monitoring (push) Blocked by required conditions
🔄 Automated Rollback Manager / ⏳ Manual Rollback Approval (push) Blocked by required conditions
V3 CI/CD Pipeline / MCP protocol smoke / macos-latest (push) Waiting to run
V3 CI/CD Pipeline / Memory import smoke / macos-latest (push) Waiting to run
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / macos-latest (push) Waiting to run
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / windows-latest (push) Waiting to run
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / macos-latest (push) Waiting to run
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / windows-latest (push) Waiting to run
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / macos-latest (push) Waiting to run
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / windows-latest (push) Waiting to run
V3 CI/CD Pipeline / Witness verify (signed manifest) / macos-latest (push) Blocked by required conditions
V3 CI/CD Pipeline / Witness verify (signed manifest) / ubuntu-latest (push) Blocked by required conditions
V3 CI/CD Pipeline / Witness verify (signed manifest) / windows-latest (push) Blocked by required conditions
V3 CI/CD Pipeline / Publish to npm (alpha) (push) Blocked by required conditions
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / macos-latest / Node 22 (push) Waiting to run
V3 CI/CD Pipeline / Plugin hooks smoke / macos-latest / Node 22 (push) Waiting to run
ADR-166 MCP Bridge Security Lock / Static-source security lock (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / Compose default binds loopback + Mongo has auth (push) Failing after 2s
CodeQL Advanced / Analyze (rust) (push) Failing after 0s
ADR-166 MCP Bridge Security Lock / plugin-agent-federation bindHost default (push) Failing after 1s
ADR-166 MCP Bridge Security Lock / Runtime behavior — 401 + terminal gate + fail-closed (push) Failing after 4s
business-pods-smoke / smoke (push) Failing after 1s
all-plugins-smoke / smoke-all (push) Failing after 2s
CI/CD Pipeline / Security & Code Quality (push) Failing after 1s
CI/CD Pipeline / Test Suite (ubuntu-latest) (push) Failing after 1s
CI/CD Pipeline / Build & Package (macos-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (ubuntu-latest) (push) Has been skipped
CI/CD Pipeline / Build & Package (windows-latest) (push) Has been skipped
CI/CD Pipeline / Deploy & Release (push) Waiting to run
CI/CD Pipeline / CI Status (push) Waiting to run
CI/CD Pipeline / Documentation & Examples (push) Failing after 1s
Clone Tracker (14-day rolling) / Snapshot clones for ruflo ecosystem (push) Failing after 1s
CodeQL Advanced / Analyze (actions) (push) Failing after 1s
CodeQL Advanced / Analyze (javascript-typescript) (push) Failing after 1s
federation-peer-rust / stable-noop (push) Failing after 1s
metaharness-ci / score (push) Failing after 1s
metaharness-ci / router-compat (push) Failing after 0s
metaharness-ci / similarity-tests (push) Failing after 0s
no-agentbbs-smoke / smoke-without-agentbbs (push) Failing after 1s
V3 CI/CD Pipeline / Build V3 (windows-latest) (push) Has been skipped
codex-integration-audit / Codex integration audit (push) Failing after 1s
helpers-manifest-guard / guard (push) Failing after 1s
🔗 Cross-Agent Integration Tests / 🤝 Agent Coordination Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🧠 Memory Sharing Integration (push) Has been skipped
🔗 Cross-Agent Integration Tests / 🛡️ Fault Tolerance Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / ⚡ Performance Integration Tests (push) Has been skipped
🔗 Cross-Agent Integration Tests / 📊 Integration Test Report (push) Waiting to run
metaharness-ci / mcp-scan (push) Failing after 1s
metaharness-ci / eject-dryrun (push) Failing after 1s
metaharness-ci / metaharness-real-data (push) Failing after 0s
no-cli-optdep-bloat-2561 / guard (push) Failing after 1s
no-metaharness-smoke / smoke-without-metaharness (push) Failing after 1s
no-phantom-agentic-flow-subpath / guard (push) Failing after 1s
🔄 Automated Rollback Manager / 🚨 Failure Detection (push) Failing after 1s
V3 CI/CD Pipeline / Plugin hooks smoke / ubuntu-latest / Node 22 (push) Failing after 1s
V3 CI/CD Pipeline / ruflo-graph-intelligence build + test smoke (#2044, ADR-123) (push) Failing after 1s
CVE Audit Gate / Audit root (critical-blocking) (push) Failing after 2s
cost-tracker-smoke / smoke (push) Failing after 3s
oia-audit-weekly / audit (push) Failing after 2s
ruflo-agent-smoke / ruflo-agent structural smoke (push) Failing after 1s
📊 Status Badges Update / 📊 Update Status Badges (push) Failing after 1s
V3 CI/CD Pipeline / Static regression guards (#2267 YAML + (push) Failing after 1s
V3 CI/CD Pipeline / Test V3 Packages (push) Failing after 0s
V3 CI/CD Pipeline / agent_execute provider routing smoke (#2042) (push) Failing after 0s
CVE Audit Gate / Audit v3 (critical-blocking) (push) Failing after 1s
federation-peer-rust / stable-native (push) Failing after 2s
🔗 Cross-Agent Integration Tests / 🚀 Integration Test Setup (push) Failing after 2s
neural-trader-smoke / runtime-smoke (push) Failing after 1s
🔄 Automated Rollback Manager / 🔍 Pre-Rollback Validation (push) Waiting to run
V3 CI/CD Pipeline / Build V3 (macos-latest) (push) Has been skipped
V3 CI/CD Pipeline / Build V3 (ubuntu-latest) (push) Has been skipped
V3 CI/CD Pipeline / Type Check V3 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 24 (push) Failing after 1s
V3 CI/CD Pipeline / Smoke (no better-sqlite3) / ubuntu-latest / Node 22 (push) Failing after 2s
V3 CI/CD Pipeline / browser rvf create flag smoke (#2015) (push) Failing after 0s
V3 CI/CD Pipeline / Dependency review (#2046) (push) Has been skipped
V3 CI/CD Pipeline / Supply-chain audit (#2046) (push) Failing after 0s
V3 CI/CD Pipeline / witness marker drift smoke (#2021) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader portfolio CG smoke (#2068, ADR-126 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / neural-trader backtest signing smoke (#2068, ADR-126 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / kg-extract type-import classification smoke (#2049) (push) Failing after 0s
V3 CI/CD Pipeline / witness verify precondition smoke (#1880) (push) Failing after 2s
V3 CI/CD Pipeline / neural-trader pipeline risk-gate smoke (#2068, ADR-126 Phase 5) (push) Failing after 0s
V3 CI/CD Pipeline / neural-trader feature attribution smoke (#2068, ADR-126 Phase 6) (push) Failing after 0s
V3 CI/CD Pipeline / plugin-registry signature verification smoke (#1922, CWE-347) (push) Failing after 4s
V3 CI/CD Pipeline / memory stats legacy-DB smoke (#2120) (push) Failing after 4s
V3 CI/CD Pipeline / github deprecated actions smoke (#2089, ADR-127 Phase 3) (push) Failing after 1s
V3 CI/CD Pipeline / graph query + pathfinder smoke (ADR-130 P2+P5) (push) Has been skipped
V3 CI/CD Pipeline / graph trajectory hooks smoke (ADR-130 P3) (push) Has been skipped
V3 CI/CD Pipeline / graph plugin adapter smoke (ADR-130 P4) (push) Has been skipped
V3 CI/CD Pipeline / graph benchmark (ADR-130 P6) (push) Has been skipped
V3 CI/CD Pipeline / statusline generator delegation smoke (#2195) (push) Failing after 1s
V3 CI/CD Pipeline / wizard init regression guard (#2206 (push) Failing after 1s
V3 CI/CD Pipeline / memory no-stray-db smoke (ADR-125 P7) (push) Failing after 1s
V3 CI/CD Pipeline / github-safe injection smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github actions pin smoke (#2089, ADR-127 Phase 1) (push) Failing after 1s
V3 CI/CD Pipeline / github attribution opt-in smoke (#2089, ADR-127 Phase 4) (push) Failing after 1s
V3 CI/CD Pipeline / pre-bash hook safety smoke (#2017) (push) Failing after 1s
V3 CI/CD Pipeline / Memory import smoke / ubuntu-latest (push) Failing after 0s
V3 CI/CD Pipeline / MCP protocol smoke / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / ruvllm WASM auto-init smoke (#2086) (push) Failing after 4s
V3 CI/CD Pipeline / MCP paired-tool round-trip smoke (#1889) (push) Failing after 1s
V3 CI/CD Pipeline / Plugin package install-safety (#1902/#1903/#1904) (push) Failing after 1s
V3 CI/CD Pipeline / Tool description discoverability (ADR-112) (push) Failing after 3s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (22) (push) Failing after 1s
V3 CI/CD Pipeline / CLI npx-install smoke (#1147 / (24) (push) Failing after 1s
V3 CI/CD Pipeline / Windows hook shim smoke (#2132) / ubuntu-latest (push) Failing after 2s
V3 CI/CD Pipeline / Windows hook execution smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Windows init hooks smoke (#2132) / ubuntu-latest (push) Failing after 1s
V3 CI/CD Pipeline / Vector-index dimension audit (#1947) (push) Failing after 0s
V3 CI/CD Pipeline / Hook-command install safety (#1921) (push) Failing after 1s
V3 CI/CD Pipeline / ToolOutputGuardrail smoke (ADR-131, (push) Failing after 1s
V3 CI/CD Pipeline / init-bundle invariants smoke (#2095, ADR-128 Phase 5) (push) Failing after 1s
V3 CI/CD Pipeline / wasm provider bridge smoke (ADR-129 P1) (push) Failing after 2s
V3 CI/CD Pipeline / wasm gallery CRUD smoke (ADR-129 P3) (push) Failing after 1s
V3 CI/CD Pipeline / wasm plugin bridge smoke (ADR-129 P4) (push) Failing after 0s
V3 CI/CD Pipeline / wasm compose smoke (ADR-129 P2) (push) Failing after 4s
V3 CI/CD Pipeline / graph schema smoke (ADR-130 P1) (push) Failing after 0s
Validate Marketplace / validate (push) Failing after 1s
🔍 Verification Pipeline / 🚀 Setup Verification (push) Failing after 1s
🔍 Verification Pipeline / 🛡️ Security Verification (push) Has been skipped
🔍 Verification Pipeline / 📝 Code Quality (push) Has been skipped
🔍 Verification Pipeline / 🧪 Test Verification (${{ matrix.os }}, Node ${{ matrix.node }}) (push) Has been skipped
🔍 Verification Pipeline / 🏗️ Build Verification (push) Has been skipped
🔍 Verification Pipeline / 📚 Documentation Verification (push) Has been skipped
🔍 Verification Pipeline / ⚡ Performance Verification (push) Waiting to run
🔍 Verification Pipeline / 📊 Verification Report (push) Waiting to run
CVE Audit Gate / High-severity report (warn only) (push) Has been cancelled
chore: import upstream snapshot with attribution
2026-07-13 12:02:19 +08:00

2707 lines
114 KiB
YAML
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
name: V3 CI/CD Pipeline
on:
push:
branches: [main, develop, v3]
paths:
- 'v3/**'
- '.github/workflows/v3-ci.yml'
# Witness-verify + plugin-hooks-smoke depend on these scripts;
# path filter keeps CI in sync with their changes.
- 'plugins/ruflo-core/scripts/witness/**'
- 'plugins/ruflo-core/scripts/test-hooks.mjs'
- 'verification/**'
# scripts/*.mjs audits (tool-descriptions, plugin-packages, hook-commands)
- 'scripts/**'
# hook-command audit (#1921) — every plugin hooks.json + the ruflo-hook shims
- '**/hooks/hooks.json'
- '**/scripts/ruflo-hook.sh'
# pre-bash hook safety (#2017) — both handler copies trigger the smoke
- '**/.claude/helpers/hook-handler.cjs'
# ruflo-browser rvf create flag (#2015) — TS source, plugin shell
# scripts, agent-facing recipes all guarded by the same smoke.
- 'v3/@claude-flow/cli/src/mcp-tools/browser-session-tools.ts'
- 'plugins/ruflo-browser/**'
# ruflo-graph-intelligence (#2044, ADR-123) — outside v3 workspace,
# has its own lockfile + tests; needs to be CI-guarded on every change.
- 'plugins/ruflo-graph-intelligence/**'
# Supply-chain hardening (#2046) — every package.json + lockfile +
# allowlist edit triggers the supply-chain audit.
- '**/package.json'
- '**/package-lock.json'
- '**/pnpm-lock.yaml'
- '.github/supply-chain/**'
- 'scripts/audit-supply-chain.mjs'
# Knowledge-graph plugin (#2049) — kg-extract type-import classifier
# + kg-traverse controller wiring drift fast when SKILL.md edits
# silently revert the bug-fix shape.
- 'plugins/ruflo-knowledge-graph/**'
- 'scripts/smoke-kg-extract-type-imports.mjs'
# Neural-trader portfolio CG (#2068, ADR-126 Phase 3) — drift fast
# when the adapter, skill, or runtime mirror diverge from the
# ADR-123 Wedge 8 contract.
- 'plugins/ruflo-neural-trader/src/sublinear-adapter.ts'
- 'plugins/ruflo-neural-trader/src/sublinear-adapter.mjs'
- 'plugins/ruflo-neural-trader/skills/trader-portfolio-cg/**'
- 'scripts/smoke-neural-trader-portfolio-cg.mjs'
# Neural-trader backtest signing (#2068, ADR-126 Phase 4) — Ed25519
# tamper-evidence for paper→live promotion; verifier MUST pin to a
# trusted key (CWE-347 / #1922 pattern).
- 'plugins/ruflo-neural-trader/src/signed-artifact.ts'
- 'plugins/ruflo-neural-trader/src/signed-artifact.mjs'
- 'plugins/ruflo-neural-trader/skills/trader-backtest/**'
- 'plugins/ruflo-neural-trader/skills/trader-cloud-backtest/**'
- 'scripts/smoke-neural-trader-backtest-signing.mjs'
# Neural-trader SendMessage risk-gate pipeline (#2068, ADR-126 Phase 5) —
# structural gate: trading-strategist refuses --broker without an
# explicit risk-analyst RiskDecision approval.
- 'plugins/ruflo-neural-trader/src/pipeline-messages.ts'
- 'plugins/ruflo-neural-trader/agents/market-analyst.md'
- 'plugins/ruflo-neural-trader/agents/trading-strategist.md'
- 'plugins/ruflo-neural-trader/agents/risk-analyst.md'
- 'plugins/ruflo-neural-trader/agents/backtest-engineer.md'
- 'scripts/smoke-neural-trader-pipeline.mjs'
# Neural-trader feature attribution (#2068, ADR-126 Phase 6) —
# regulator-grade interpretability via single-entry PageRank.
# Same signing scheme as Phase 4; ranking is seed-reproducible.
- 'plugins/ruflo-neural-trader/src/signed-attribution.ts'
- 'plugins/ruflo-neural-trader/src/signed-attribution.mjs'
- 'plugins/ruflo-neural-trader/skills/trader-explain/**'
- 'scripts/smoke-neural-trader-feature-attribution.mjs'
# Plugin-registry CWE-347 regression smoke (#1922) — `discovery.ts`
# signature verifier + the smoke fixture must stay in lockstep.
- 'v3/@claude-flow/cli/src/plugins/store/discovery.ts'
- 'v3/@claude-flow/cli/src/transfer/ipfs/client.ts'
- 'v3/@claude-flow/cli/scripts/publish-registry.ts'
- 'scripts/smoke-plugin-registry-signature.mjs'
# ruvllm WASM auto-init regression smoke (#2086) — the
# `loadRuvllmWasm()` helper in `ruvllm-tools.ts` and the
# `ruvllm_status` un-init diagnostic path must stay in lockstep.
- 'v3/@claude-flow/cli/src/mcp-tools/ruvllm-tools.ts'
- 'v3/@claude-flow/cli/src/ruvector/ruvllm-wasm.ts'
- 'scripts/smoke-ruvllm-wasm-auto-init.mjs'
# agent_execute provider routing (#2042) — executeAgentTask must
# not regress to inline Anthropic fetch, and the OpenRouter branch
# in callAnthropicMessages must stay wired.
- 'v3/@claude-flow/cli/src/mcp-tools/agent-execute-core.ts'
- 'scripts/smoke-agent-execute-providers.mjs'
# memory stats legacy-DB regression guard (#2120) — the WHERE
# status='active' filter must accept NULL too, and the schema
# backfill must promote NULL→'active' on existing DBs.
- 'v3/@claude-flow/cli/src/memory/memory-bridge.ts'
- 'v3/@claude-flow/cli/src/memory/memory-initializer.ts'
- 'v3/@claude-flow/cli/src/commands/status.ts'
- 'scripts/smoke-memory-stats-legacy-db.mjs'
# ADR-125 Phase 7 — no stray DB artifacts after `npm test` in
# @claude-flow/memory. vitest.setup.ts must wipe *.db / *.rvf /
# *.redb files written by agentdb / @ruvector/rvf bindings.
- 'v3/@claude-flow/memory/vitest.setup.ts'
- 'v3/@claude-flow/memory/vitest.config.ts'
- 'v3/@claude-flow/memory/vitest.config.mts'
- 'scripts/smoke-memory-no-stray-db.mjs'
# GitHub skills/agents/helpers surface (#2089, ADR-127) — injection
# smoke + actions pin smoke gate every change to the .github surface.
- '.claude/agents/github/**'
- '.claude/skills/github-*/**'
- 'v3/@claude-flow/cli/.claude/commands/github/**'
- '.claude/helpers/github-safe.js'
- 'v3/@claude-flow/cli/.claude/helpers/github-safe.js'
- 'scripts/smoke-github-safe-injection.mjs'
- 'scripts/smoke-github-actions-pins.mjs'
- 'scripts/smoke-deprecated-actions.mjs'
- 'scripts/smoke-attribution-opt-in.mjs'
- '.github/supply-chain/allowed-deps.json'
# Init-bundle invariants smoke (#2095, ADR-128) — orphan dirs, SKILLS_MAP
# completeness, and plugin-init agent dedup are all guarded by Phase 5.
- 'v3/@claude-flow/cli/.claude/**'
- 'v3/@claude-flow/cli/src/init/**'
- 'plugins/*/agents/**'
- 'plugins/*/skills/**'
- 'plugins/*/commands/**'
- 'scripts/smoke-init-bundle-invariants.mjs'
# ADR-129 — rvagent full integration (P1-P4)
- 'v3/@claude-flow/cli/src/ruvector/agent-wasm.ts'
- 'v3/@claude-flow/cli/src/mcp-tools/wasm-agent-tools.ts'
- 'scripts/smoke-wasm-provider-bridge.mjs'
- 'scripts/smoke-wasm-rvf-compose.mjs'
- 'scripts/smoke-wasm-gallery-crud.mjs'
- 'scripts/smoke-wasm-plugin-bridge.mjs'
# ADR-130 — graph intelligence integration (P1-P6)
- 'v3/@claude-flow/cli/src/memory/memory-initializer.ts'
- 'v3/@claude-flow/cli/src/memory/embedding-quantization.ts'
- 'v3/@claude-flow/cli/src/memory/graph-edge-writer.ts'
- 'v3/@claude-flow/cli/src/mcp-tools/agentdb-tools.ts'
- 'v3/@claude-flow/cli/src/mcp-tools/hooks-tools.ts'
- 'plugins/ruflo-graph-intelligence/src/adapters/knowledge-graph-adapter.ts'
- 'scripts/smoke-graph-schema-migration.mjs'
- 'scripts/smoke-graph-query-dispatch.mjs'
- 'scripts/smoke-trajectory-graph-edges.mjs'
- 'scripts/smoke-graph-plugin-adapter.mjs'
- 'scripts/smoke-graph-pathfinder.mjs'
- 'scripts/benchmark-graph.mjs'
# statusline generator delegation regression guard (#2195)
- 'v3/@claude-flow/cli/src/init/statusline-generator.ts'
- '.claude/helpers/statusline.cjs'
- 'scripts/smoke-statusline-generator-delegation.mjs'
# wizard init regression guard (#2206 #2207 #2208)
- 'v3/@claude-flow/cli/src/init/mcp-generator.ts'
- 'v3/@claude-flow/cli/src/init/executor.ts'
- 'scripts/smoke-wizard-init-regression.mjs'
pull_request:
branches: [main, develop]
paths:
- 'v3/**'
- 'plugins/ruflo-core/scripts/witness/**'
- 'plugins/ruflo-core/scripts/test-hooks.mjs'
- 'scripts/**'
- '**/hooks/hooks.json'
- '**/scripts/ruflo-hook.sh'
- '**/.claude/helpers/hook-handler.cjs'
# ruflo-browser rvf create flag (#2015)
- 'v3/@claude-flow/cli/src/mcp-tools/browser-session-tools.ts'
- 'plugins/ruflo-browser/**'
# ruflo-graph-intelligence (#2044, ADR-123)
- 'plugins/ruflo-graph-intelligence/**'
# Supply-chain hardening (#2046) — every dep + lockfile change is
# CVE-audited, allowlist-checked, integrity-checked.
- '**/package.json'
- '**/package-lock.json'
- '**/pnpm-lock.yaml'
- '.github/supply-chain/**'
- 'scripts/audit-supply-chain.mjs'
# Knowledge-graph plugin (#2049)
- 'plugins/ruflo-knowledge-graph/**'
- 'scripts/smoke-kg-extract-type-imports.mjs'
# Neural-trader portfolio CG (#2068, ADR-126 Phase 3)
- 'plugins/ruflo-neural-trader/src/sublinear-adapter.ts'
- 'plugins/ruflo-neural-trader/src/sublinear-adapter.mjs'
- 'plugins/ruflo-neural-trader/skills/trader-portfolio-cg/**'
- 'scripts/smoke-neural-trader-portfolio-cg.mjs'
# Neural-trader backtest signing (#2068, ADR-126 Phase 4)
- 'plugins/ruflo-neural-trader/src/signed-artifact.ts'
- 'plugins/ruflo-neural-trader/src/signed-artifact.mjs'
- 'plugins/ruflo-neural-trader/skills/trader-backtest/**'
- 'plugins/ruflo-neural-trader/skills/trader-cloud-backtest/**'
- 'scripts/smoke-neural-trader-backtest-signing.mjs'
# Neural-trader SendMessage risk-gate pipeline (#2068, ADR-126 Phase 5)
- 'plugins/ruflo-neural-trader/src/pipeline-messages.ts'
- 'plugins/ruflo-neural-trader/agents/market-analyst.md'
- 'plugins/ruflo-neural-trader/agents/trading-strategist.md'
- 'plugins/ruflo-neural-trader/agents/risk-analyst.md'
- 'plugins/ruflo-neural-trader/agents/backtest-engineer.md'
- 'scripts/smoke-neural-trader-pipeline.mjs'
# Neural-trader feature attribution (#2068, ADR-126 Phase 6)
- 'plugins/ruflo-neural-trader/src/signed-attribution.ts'
- 'plugins/ruflo-neural-trader/src/signed-attribution.mjs'
- 'plugins/ruflo-neural-trader/skills/trader-explain/**'
- 'scripts/smoke-neural-trader-feature-attribution.mjs'
# Plugin-registry CWE-347 regression (#1922)
- 'v3/@claude-flow/cli/src/plugins/store/discovery.ts'
- 'v3/@claude-flow/cli/src/transfer/ipfs/client.ts'
- 'v3/@claude-flow/cli/scripts/publish-registry.ts'
- 'scripts/smoke-plugin-registry-signature.mjs'
# ruvllm WASM auto-init regression (#2086)
- 'v3/@claude-flow/cli/src/mcp-tools/ruvllm-tools.ts'
- 'v3/@claude-flow/cli/src/ruvector/ruvllm-wasm.ts'
- 'scripts/smoke-ruvllm-wasm-auto-init.mjs'
# agent_execute provider routing (#2042)
- 'v3/@claude-flow/cli/src/mcp-tools/agent-execute-core.ts'
- 'scripts/smoke-agent-execute-providers.mjs'
# memory stats legacy-DB regression (#2120)
- 'v3/@claude-flow/cli/src/memory/memory-bridge.ts'
- 'v3/@claude-flow/cli/src/memory/memory-initializer.ts'
- 'v3/@claude-flow/cli/src/commands/status.ts'
- 'scripts/smoke-memory-stats-legacy-db.mjs'
# ADR-125 Phase 7 — no stray DB artifacts after npm test
- 'v3/@claude-flow/memory/vitest.setup.ts'
- 'v3/@claude-flow/memory/vitest.config.ts'
- 'v3/@claude-flow/memory/vitest.config.mts'
- 'scripts/smoke-memory-no-stray-db.mjs'
# GitHub skills/agents/helpers surface (#2089, ADR-127)
- '.claude/agents/github/**'
- '.claude/skills/github-*/**'
- 'v3/@claude-flow/cli/.claude/commands/github/**'
- '.claude/helpers/github-safe.js'
- 'v3/@claude-flow/cli/.claude/helpers/github-safe.js'
- 'scripts/smoke-github-safe-injection.mjs'
- 'scripts/smoke-github-actions-pins.mjs'
- 'scripts/smoke-deprecated-actions.mjs'
- 'scripts/smoke-attribution-opt-in.mjs'
- '.github/supply-chain/allowed-deps.json'
# Init-bundle invariants smoke (#2095, ADR-128)
- 'v3/@claude-flow/cli/.claude/**'
- 'v3/@claude-flow/cli/src/init/**'
- 'plugins/*/agents/**'
- 'plugins/*/skills/**'
- 'plugins/*/commands/**'
- 'scripts/smoke-init-bundle-invariants.mjs'
# ADR-129 — rvagent full integration (P1-P4)
- 'v3/@claude-flow/cli/src/ruvector/agent-wasm.ts'
- 'v3/@claude-flow/cli/src/mcp-tools/wasm-agent-tools.ts'
- 'scripts/smoke-wasm-provider-bridge.mjs'
- 'scripts/smoke-wasm-rvf-compose.mjs'
- 'scripts/smoke-wasm-gallery-crud.mjs'
- 'scripts/smoke-wasm-plugin-bridge.mjs'
# witness manifests / fix list — so witness-verify runs on PRs that
# touch them (otherwise a stale per-OS manifest only fails post-merge).
- 'verification/**'
# statusline generator delegation regression guard (#2195)
- 'v3/@claude-flow/cli/src/init/statusline-generator.ts'
- '.claude/helpers/statusline.cjs'
- 'scripts/smoke-statusline-generator-delegation.mjs'
# wizard init regression guard (#2206 #2207 #2208)
- 'v3/@claude-flow/cli/src/init/mcp-generator.ts'
- 'v3/@claude-flow/cli/src/init/executor.ts'
- 'scripts/smoke-wizard-init-regression.mjs'
workflow_dispatch:
env:
NODE_VERSION: '20'
PNPM_VERSION: '8'
jobs:
# #2267 + #2257 regression guards — fast, no install, run first so any
# YAML or router regression is caught at PR-time, not at scheduled cron.
static-regression-guards:
name: Static regression guards (#2267 YAML + #2257 router + #2562 lockfile)
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '22' }
# js-yaml + tsx are needed by the smokes — install only those.
- name: Install smoke deps
run: npm install --no-save --no-package-lock js-yaml tsx
- name: "#2267 — every .github/workflows/*.yml must parse"
run: node scripts/smoke-workflows-yaml.mjs
- name: "#2257 — router patterns must be word-boundary-anchored"
run: node --import tsx scripts/smoke-router-regex.mjs
# #2562 — FAST lockfile-drift guard. Editing any v3 workspace manifest
# (esp. v3/@claude-flow/cli deps) without regenerating v3/pnpm-lock.yaml
# makes --frozen-lockfile fail in EVERY downstream install job (~34 red
# jobs, cascading — the #2540→#2552→#2562 recurrence). `--lockfile-only`
# verifies lock↔manifest consistency in seconds WITHOUT downloading
# anything, failing once here with a clear fix instead of 34× later.
- name: Setup pnpm (lockfile guard)
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: "#2562 — v3/pnpm-lock.yaml must match workspace package.json (frozen-lockfile drift)"
working-directory: v3
run: |
if pnpm install --frozen-lockfile --lockfile-only 2>lock-err.log; then
echo "✓ v3/pnpm-lock.yaml is consistent with all workspace manifests"
else
echo "::error title=Lockfile drift::v3/pnpm-lock.yaml is OUT OF SYNC with a package.json. Editing v3 workspace deps requires regenerating the lock in the SAME commit."
echo "::error title=How to fix::Run cd v3 && pnpm install --lockfile-only && git add pnpm-lock.yaml then commit."
echo "--- pnpm reported ---"
grep -A25 "OUTDATED_LOCKFILE\|not up to date\|don't match" lock-err.log | head -40 || cat lock-err.log | head -40
exit 1
fi
test:
name: Test V3 Packages
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Install dependencies
working-directory: v3
run: pnpm install --frozen-lockfile
- name: Build (so workspace dist/ exists for runtime imports)
# Some test suites do `await import('@claude-flow/X')` and Vite's
# import-analysis pass needs dist/ files at the resolved path.
# --no-bail keeps building past unrelated failures.
working-directory: v3
run: pnpm --recursive --no-bail run build || true
- name: "ADR-176/177 — self-learning proof artifacts must stay valid"
# Guard the committed evidence that makes the flywheel claim honest:
# the proof-of-mechanism + REAL compounding lineage must replay
# independently (rehash + re-run accept/v1+sig, no service logs), the
# lineage must reconstruct to >=2 promotions back to the immutable root,
# and the shipped config champion must verify against the baked pubkey.
working-directory: v3/@claude-flow/cli
run: node scripts/smoke-flywheel-proof.mjs
- name: "ADR-176 — clean-room replay acceptance test (offline, identical hashes)"
# Replay a PROMOTED generation from its receipt alone: recompute every
# hash bit-identically and re-run accept/v1+sig to reproduce promoted=true,
# with network access trapped. Proves promotions are reproducible without
# trusting any service log.
working-directory: v3/@claude-flow/cli
run: node scripts/replay-generation.mjs
- name: Run tests
working-directory: v3
# Tolerate exit code 139 (SIGSEGV) ONLY when it occurs at process
# shutdown after all tests reported. Native bindings sometimes
# segfault during onnxruntime-node / ruvector cleanup; this is a
# known issue tracked elsewhere and shouldn't block CI when the
# actual test outcomes were captured. Any other non-zero exit
# (real test failures, vitest errors, etc.) still fails the job.
run: |
set +e
pnpm test
EXIT=$?
set -e
if [ "$EXIT" -eq 139 ]; then
echo "::warning::pnpm test exited with SIGSEGV (139) after tests completed — tolerated as native-binding cleanup race"
exit 0
fi
exit "$EXIT"
- name: Upload coverage
if: always()
uses: actions/upload-artifact@v4
with:
name: v3-coverage
path: v3/__tests__/coverage/
typecheck:
name: Type Check V3
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Install dependencies
working-directory: v3
run: pnpm install --frozen-lockfile
- name: Type check
working-directory: v3
run: pnpm typecheck
continue-on-error: true
build:
name: Build V3 (${{ matrix.os }})
runs-on: ${{ matrix.os }}
needs: [test]
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Install dependencies
working-directory: v3
run: pnpm install --frozen-lockfile
- name: Build
working-directory: v3
run: pnpm build
- name: Upload build artifacts
uses: actions/upload-artifact@v4
with:
name: v3-build-${{ matrix.os }}
path: v3/@claude-flow/*/dist/
smoke-install-no-bsqlite:
# Regression guard for ruvnet/ruflo#1867 — ensures @claude-flow/memory
# installs and loads when better-sqlite3 fails to build (e.g. Node 26
# without prebuilds). Reproduces the user-visible failure mode by
# installing with --omit=optional.
#
# Independent of the workspace `build` job because we only need to
# build a single package (memory) — no workspace-wide dist required.
#
# Cross-platform: ubuntu + macos (Windows excluded because the smoke
# script uses bash-specific patterns and runs in user environments
# that typically use WSL/git-bash anyway. The runtime fallback path
# the smoke verifies is platform-independent — a passing run on
# Linux+macOS proves the JS code path works everywhere.)
name: Smoke (no better-sqlite3) / ${{ matrix.os }} / Node ${{ matrix.node }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
node: ['22', '24']
# macos-latest's bundled Python 3.12 removed distutils, breaking
# node-gyp on Node 24's hnswlib-node + better-sqlite3 builds. Drop
# that one combo until the macOS image ships a compatible setup.
# (Linux Node 24 + macOS Node 22 still cover the cross-product.)
exclude:
- os: macos-latest
node: '24'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Guard #2590 — onnxruntime-node postinstall must be skipped
# Regression guard for ruvnet/ruflo#2590 — onnxruntime-node's postinstall
# fetches a GPU nupkg from nuget.org that ETIMEDOUTs from GitHub runners,
# taking `pnpm install --frozen-lockfile` down. The fix requires BOTH:
# 1) v3/package.json → pnpm.neverBuiltDependencies contains "onnxruntime-node"
# 2) this workflow's memory-smoke install uses --ignore-scripts
# If either regresses, this step fails BEFORE the flaky network install.
run: |
set -euo pipefail
# (1) neverBuiltDependencies gate
node -e '
const j = require("./v3/package.json");
const list = (j.pnpm && j.pnpm.neverBuiltDependencies) || [];
if (!list.includes("onnxruntime-node")) {
console.error("REGRESSION #2590: v3/package.json pnpm.neverBuiltDependencies missing onnxruntime-node");
console.error(" got:", JSON.stringify(list));
process.exit(1);
}
console.log("ok: onnxruntime-node in pnpm.neverBuiltDependencies");
'
# (2) workflow install step must pass --ignore-scripts (belt-and-braces)
if ! grep -qE "pnpm install --frozen-lockfile --ignore-scripts" .github/workflows/v3-ci.yml; then
echo "REGRESSION #2590: memory-smoke install step lost --ignore-scripts"
exit 1
fi
echo "ok: --ignore-scripts present on memory-smoke pnpm install"
- name: Install workspace + build memory (pnpm — needed for workspace:* protocol)
working-directory: v3
run: |
pnpm install --frozen-lockfile --ignore-scripts
pnpm --filter @claude-flow/memory... run build
- name: Pack memory tarball
id: pack
working-directory: v3/@claude-flow/memory
run: |
# pnpm pack rewrites workspace:* → resolved versions (npm pack doesn't).
# Pack to RUNNER_TEMP so the path works on both ubuntu and macos.
TARBALL=$(pnpm pack --pack-destination "$RUNNER_TEMP" 2>&1 | grep -E "\.tgz$" | head -1)
[ -z "$TARBALL" ] && { echo "pnpm pack produced no tarball"; exit 1; }
echo "tarball=$TARBALL" >> "$GITHUB_OUTPUT"
- name: Install with --omit=optional (simulates Node 26 native build failure)
run: |
mkdir -p "$RUNNER_TEMP/smoke"
cd "$RUNNER_TEMP/smoke"
npm init -y >/dev/null
npm pkg set type=module
npm install "${{ steps.pack.outputs.tarball }}" --omit=optional --no-audit --no-fund
- name: Assert better-sqlite3 was NOT installed
run: |
if [ -d "$RUNNER_TEMP/smoke/node_modules/better-sqlite3" ]; then
echo "FAIL: better-sqlite3 installed despite --omit=optional — smoke setup is wrong"
exit 1
fi
echo "ok: better-sqlite3 absent (regression scenario active)"
- name: Run smoke script
run: |
cp v3/@claude-flow/memory/scripts/smoke-no-bsqlite.mjs "$RUNNER_TEMP/smoke/"
cd "$RUNNER_TEMP/smoke"
node smoke-no-bsqlite.mjs
plugin-hooks-smoke:
# Regression guard for ruvnet/ruflo#1859 + #1862 — drives each PostToolUse
# hook from plugins/ruflo-core/hooks/hooks.json with synthetic Claude-Code
# JSON against the locally built CLI. Catches:
# - hooks calling flags the CLI doesn't accept (#1862's --format true)
# - CLI parser ambiguity recording the wrong value (#1859's "true" filename)
#
# Independent of the workspace `build` job — only the cli package needs
# to be built. We use `pnpm --filter @claude-flow/cli...` to scope.
name: Plugin hooks smoke / ${{ matrix.os }} / Node ${{ matrix.node }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
# Cross-platform: ubuntu + macos. Windows excluded because the
# plugin's hooks.json uses `/bin/bash -c '...'` and the synthetic
# JSON-on-stdin pipeline assumes POSIX shell. On Windows users
# run Claude Code via WSL or git-bash, which the test would also
# need to simulate; out of scope for this regression guard.
os: [ubuntu-latest, macos-latest]
# Node 22 is the project's documented baseline (engines.node: '>=20')
# but Node 20 had environment-specific issues with multi-line stdin
# round-tripping that didn't reproduce on Node 22 or locally — same
# CLI binary, same hooks.json. Tracked as a follow-up; for now the
# regression guard runs on the version users are actually on.
node: ['22']
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js ${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Install + build cli (scoped, tolerates unrelated workspace failures)
working-directory: v3
run: |
pnpm install --frozen-lockfile
# --no-bail keeps building past unrelated package failures
# (e.g. plugin-agent-federation TS error). We then assert the
# specific dist file we need was produced.
pnpm --recursive --no-bail run build || true
test -f @claude-flow/cli/bin/cli.js \
|| (echo "cli build did not produce bin/cli.js"; exit 1)
test -f @claude-flow/cli/dist/src/commands/hooks.js \
|| (echo "cli build did not produce hooks.js"; exit 1)
- name: Run plugin hooks smoke against local CLI build
run: |
node plugins/ruflo-core/scripts/test-hooks.mjs \
"node $GITHUB_WORKSPACE/v3/@claude-flow/cli/bin/cli.js"
browser-rvf-create-flags-smoke:
# Regression guard for ruvnet/ruflo#2015 — the ruflo-browser
# `browser_session_record` MCP tool wraps `ruvector rvf create`.
# ruvector@0.2.25 makes `-d, --dimension <n>` required, so before
# this fix every session-record call failed with
# `error: required option '-d, --dimension <n>' not specified`
# and the wrapper returned `{ success: false, error: "rvf create
# failed" }`.
#
# This smoke scans every place we shell out to `rvf create
# --kind browser-session` across the repo (TS source, compiled
# dist, shell scripts, agent-facing markdown recipes) and fails
# if any call is missing the dimension flag.
name: browser rvf create flag smoke (#2015)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: 8
- name: Build CLI (so the compiled dist is scanned alongside src)
# The CLI uses `workspace:*` deps which only pnpm understands — npm
# install errors with EUNSUPPORTEDPROTOCOL. Install at the v3
# workspace root (mirrors the Build V3 job) and build everything in
# topological order so cli-core/memory/neural dist exist before cli.
# `continue-on-error: true` keeps the smoke usable even when CLI src
# has unrelated TS errors — the static scan will report any missing
# dist file it cares about, which is the contract we actually want.
continue-on-error: true
run: |
cd v3
pnpm install --frozen-lockfile
pnpm -r build
- name: Static scan of every rvf create call site
run: node scripts/smoke-browser-rvf-create-flags.mjs
graph-intelligence-build-smoke:
# Regression guard for #2044 / ADR-123 — the ruflo-graph-intelligence
# plugin lives outside the v3 pnpm workspace (it has its own
# package-lock.json) so it does not pick up the workspace's
# frozen-lockfile / -r build path. This job installs + builds + tests
# the plugin in isolation so the eleven sublinear wedges are verified
# on every push and PR.
name: ruflo-graph-intelligence build + test smoke (#2044, ADR-123)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install plugin dependencies (npm — plugin is outside v3 workspace)
working-directory: plugins/ruflo-graph-intelligence
run: npm ci --no-audit --no-fund --legacy-peer-deps
- name: Type-check
working-directory: plugins/ruflo-graph-intelligence
run: npx tsc --noEmit
- name: Build
working-directory: plugins/ruflo-graph-intelligence
run: npx tsc
- name: Run vitest
working-directory: plugins/ruflo-graph-intelligence
run: npx vitest run
supply-chain-audit:
# Supply-chain hardening (#2046, follow-up to ADR-123).
#
# Five layers of defence run by scripts/audit-supply-chain.mjs:
# 1. CVE — `npm audit --audit-level=high` on each shipped package.
# HIGH/CRITICAL findings in DIRECT deps fail CI.
# 2. Lockfile integrity — every entry must have a SHA-512 `integrity`
# hash. Missing or weak hashes are a tamper vector.
# 3. Top-level allowlist — new direct deps require an explicit
# `.github/supply-chain/allowed-deps.json` addition. Block silent
# additions via maintainer compromise or typo'd PRs.
# 4. Typosquat reject — names matching the policy block-list fail
# regardless of whether the package actually exists upstream.
# 5. Publisher trust snapshot — logs npm maintainer identity for our
# critical upstream deps so unexpected handover is visible in CI
# logs (not a hard fail; informational).
#
# This job runs on every PR that touches a package.json, lockfile,
# allowlist, or the audit script itself. It also runs on pushes to main
# so direct-to-main changes (rare) are caught.
name: Supply-chain audit (#2046)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install ruflo-graph-intelligence deps (for npm audit)
working-directory: plugins/ruflo-graph-intelligence
run: npm ci --no-audit --no-fund --legacy-peer-deps
- name: Install @claude-flow/browser deps (for npm audit)
working-directory: v3/@claude-flow/browser
run: npm install --no-audit --no-fund --legacy-peer-deps --no-workspaces
- name: Run supply-chain audit
run: node scripts/audit-supply-chain.mjs
- name: Test audit script itself
run: node scripts/__tests__/audit-supply-chain.test.mjs
dependency-review:
# GitHub's official dependency-review-action — runs only on PRs and
# flags any new vulnerable dependency added by the PR vs the base
# branch. Configured to fail on `high` severity and to forbid
# known-bad licenses (GPL-style + non-OSI).
#
# **Setup requirement**: this action needs GitHub's Dependency Graph
# feature enabled at the repo level. Toggle at
# https://github.com/ruvnet/ruflo/settings/security_analysis under
# "Dependency graph". Until that toggle is on, the action errors
# with "Dependency review is not supported on this repository" — so
# this job is marked continue-on-error to keep the CVE/audit/codeowner
# layers of supply-chain hardening usable in the meantime.
#
# https://github.com/actions/dependency-review-action
name: Dependency review (#2046)
runs-on: ubuntu-latest
if: github.event_name == 'pull_request'
steps:
- name: Checkout code
uses: actions/checkout@v4
# `continue-on-error` is at the STEP (not job) level so the action's
# "Dependency review is not supported on this repository" error from
# the off-by-default Dependency Graph feature doesn't show as a PR
# check failure. Once the repo owner toggles Dependency Graph on at
# Settings → Security and analysis, remove this line to make the
# action gating again.
- name: Dependency Review
continue-on-error: true
uses: actions/dependency-review-action@v4
with:
fail-on-severity: high
comment-summary-in-pr: on-failure
# Block licenses we cannot ship:
deny-licenses: AGPL-3.0, GPL-3.0, SSPL-1.0
# Block known-compromised package versions via pURL (package-url)
# format — this is the action's required input shape per its docs.
# https://github.com/actions/dependency-review-action#configuration-options
deny-packages: pkg:npm/event-stream@3.3.6, pkg:npm/flatmap-stream, pkg:npm/ua-parser-js@0.7.29, pkg:npm/ua-parser-js@0.8.0, pkg:npm/ua-parser-js@1.0.0, pkg:npm/colors@1.4.1, pkg:npm/faker@6.6.6
witness-marker-drift-smoke:
# Regression guard for ruvnet/ruflo#2021 — the slow witness-verify
# job below catches marker drift but only as a post-build check on
# cross-platform matrix. Direct pushes to main (e.g. the alpha.43
# release chain) bypass PR-time gating and a bad marker can sneak
# through, leaving the scheduled 12h cron to file a HIGH issue.
#
# This smoke runs verify.mjs's marker-presence layer (no signature
# check, no build, no native deps) on every push and pull-request.
# If a cited file exists on disk and the marker string is missing,
# fail immediately and name the specific fix id that drifted.
#
# The full witness-verify job stays — it adds the signature layer
# and the cross-platform dist build. This smoke is the fast gate
# that fails before that.
name: witness marker drift smoke (#2021)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Scan every witness marker for drift
run: node scripts/smoke-witness-marker-drift.mjs
witness-verify-precondition-smoke:
# Regression guard for ruvnet/ruflo#1880 — the 12h scheduled witness
# verification runs in a source-only checkout (no `npm ci`, no
# `npm run build`). Before this fix, that environment exited 1
# (real verification failure) because `@noble/ed25519` wasn't
# installed and 87+ dist files referenced by the manifest didn't
# exist on disk — so the scheduled runner filed a "verification
# broken" issue every 12 hours even though nothing was actually
# wrong with the signed manifest.
#
# verify.mjs now reserves exit 2 for precondition failures
# (missing dep, source-only checkout) and exit 1 strictly for real
# failures (signature invalid, dist file regressed). This smoke
# drives both shapes through and asserts the contract holds:
# - missing @noble/ed25519 → exit 2 + named in the error
# - all-manifest-files missing → exit 2 + precondition tag
# - built tree → exit is NEVER 2
#
# Independent job so a precondition contract break is named clearly
# in the CI summary. Wired into witness-verify's `needs:` below.
name: witness verify precondition smoke (#1880)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install root dependencies (provides @noble/ed25519 for Case 3)
run: npm ci --legacy-peer-deps
- name: Drive precondition + built-tree shapes through verify.mjs
shell: bash
run: node scripts/smoke-witness-verify-precondition.mjs
kg-extract-type-imports-smoke:
# Regression guard for ruvnet/ruflo#2049 — `kg-extract` previously
# treated TypeScript `import type` and value imports as the same edge
# type, producing phantom runtime cycles in the knowledge graph (a
# 51-service codebase reported a non-existent `findings ⇄ finding-actions`
# cycle driven entirely by a one-way type-only import).
#
# This smoke runs two layers:
# 1. Static contract check on plugins/ruflo-knowledge-graph/skills/{kg-extract,kg-traverse}/SKILL.md
# — assert kg-extract declares `type-depends-on` as a separate relation
# with weight ≤ 0.1, and that neither skill references the
# compiled-out `agentdb_semantic-route` controller.
# 2. Behavioural fixture: write a tiny TS scenario with one type-only
# cycle + one value-import edge to /tmp, run the documented regex
# classifier, and assert the cycle is NOT counted as a value edge.
#
# If a future PR softens either layer (re-introduces semantic-route in
# allowed-tools, drops the type-depends-on relation, or weakens the
# regex), this smoke catches it.
name: kg-extract type-import classification smoke (#2049)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run kg-extract type-import smoke
run: node scripts/smoke-kg-extract-type-imports.mjs
neural-trader-portfolio-cg-smoke:
# Regression guard for ruvnet/ruflo#2068 — ADR-126 Phase 3.
#
# Phase 3 wires the Conjugate-Gradient portfolio path that ADR-123
# Wedge 8 promises a 40-60× speedup over the legacy Neumann series.
# The contract is load-bearing on three artifacts that must stay
# in lockstep:
# 1. `src/sublinear-adapter.ts` — the typed adapter shape
# (SublinearAdapter, solveCG, isMcpAvailable, SolveResult)
# that conforms to ADR-123 §262-289.
# 2. `src/sublinear-adapter.mjs` — the runtime mirror that the
# smoke + bench import directly (the plugin has no build step).
# 3. `skills/trader-portfolio-cg/SKILL.md` — frontmatter must
# declare `mcp__ruflo-sublinear__solve` in allowed-tools and
# write to the canonical `trading-risk` namespace.
#
# The smoke runs three layers:
# [1/3] STATIC ADAPTER CONTRACT — class shape, method signatures,
# SolveResult fields, MCP tool name, symmetric-input validation.
# [2/3] STATIC SKILL CONTRACT — frontmatter, allowed-tools, namespace,
# disable-flag documentation, Neumann fallback path.
# [3/3] RUNTIME CORRECTNESS — solves the textbook 2×2 SPD case
# A=[[4,1],[1,3]], b=[1,2] → x=[1/11, 7/11] within 1e-6, and
# exercises the degraded paths (non-square, non-symmetric).
#
# If a future PR drops solveCG, removes the MCP tool from allowed-tools,
# changes the namespace, or breaks the local CG kernel, this smoke
# catches it before merge.
name: neural-trader portfolio CG smoke (#2068, ADR-126 Phase 3)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run portfolio CG smoke
run: node scripts/smoke-neural-trader-portfolio-cg.mjs
neural-trader-backtest-signing-smoke:
# Regression guard for ruvnet/ruflo#2068 — ADR-126 Phase 4.
#
# Phase 4 wires Ed25519-signed backtest artifacts so the paper→live
# promotion gate has cryptographic tamper evidence. The signing scheme
# mirrors the CWE-347 plugin-registry pattern (#1922): canonical body
# = JSON.stringify without signature fields; verifier pins to a
# caller-supplied `trustedPublicKey`, NOT to the served self-asserted
# `witnessPublicKey` field (which an attacker can swap freely).
#
# Three load-bearing artifacts must stay in lockstep:
# 1. `src/signed-artifact.ts` — the typed signer/verifier contract.
# 2. `src/signed-artifact.mjs` — the runtime mirror imported by the
# smoke + (future) skill harnesses (the plugin has no build step).
# 3. `skills/trader-backtest/SKILL.md` + `skills/trader-cloud-backtest/SKILL.md`
# — the call sites that sign before store + verify before promote.
#
# The smoke runs three layers:
# [1/3] STATIC CONTRACT — verifier pins to trustedPublicKey (NOT to
# artifact.witnessPublicKey), canonical body strips BOTH
# signature fields, .ts and .mjs runtime mirrors agree.
# [2/3] CRYPTO ROUND-TRIP with real Ed25519 — happy path, tampered
# body fails, empty sig/pubkey fails, swapped served pubkey
# still verifies (pin is real), wrong trusted pubkey fails.
# [3/3] CALL-SITE BYTE check — trader-cloud-backtest/SKILL.md calls
# verifyBacktestArtifact AND documents the fail-closed branch;
# trader-backtest/SKILL.md references the signer + degraded
# warning + RUFLO_WITNESS_KEY_PATH env var.
#
# If a future PR drops the verify call, reverts to pinning the served
# field, or removes the fail-closed branch, this smoke catches it
# before merge.
name: neural-trader backtest signing smoke (#2068, ADR-126 Phase 4)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install root deps (for @noble/ed25519)
run: npm install --legacy-peer-deps --no-audit --no-fund --ignore-scripts
- name: Run backtest signing smoke
run: node scripts/smoke-neural-trader-backtest-signing.mjs
neural-trader-pipeline-smoke:
# Regression guard for ruvnet/ruflo#2068 — ADR-126 Phase 5.
#
# Phase 5 refactors the four neural-trader agents (market-analyst,
# trading-strategist, risk-analyst, backtest-engineer) into a typed
# SendMessage pipeline with risk-analyst as a structural BLOCKING
# GATE. The live broker call (`npx neural-trader --broker <name>`)
# cannot fire without an explicit RiskDecision approval event from
# risk-analyst for the proposal's signalId.
#
# Six load-bearing artifacts must stay in lockstep:
# 1. `src/pipeline-messages.ts` — the three TypeScript message
# schemas (RegimeVerdict, SignalProposal, RiskDecision) +
# PipelineMessage discriminated union.
# 2-5. `agents/{market-analyst,trading-strategist,risk-analyst,backtest-engineer}.md`
# — frontmatter `name:` fields + Comms protocol sections with
# the correct upstream/downstream wiring.
# 6. `trading-strategist.md` — the structural risk-gate guard at
# step 5 of the Strategy Development Workflow that refuses
# `--broker` calls without a risk-analyst approval.
#
# The smoke runs four layers:
# [1/4] AGENT FRONTMATTER — each .md declares `name: <agent>` so
# the agent is SendMessage-addressable.
# [2/4] COMMS PROTOCOL — each .md has a "Comms protocol" section
# with the correct upstream/downstream references; backtest-
# engineer documents its orthogonal-lane status.
# [3/4] STRUCTURAL RISK-GATE — trading-strategist.md contains
# explicit refusal logic: mentions `--broker`, requires
# RiskDecision approval, declares REFUSAL, emits [ERROR],
# marks the gate as NON-NEGOTIABLE/structural.
# [4/4] BEHAVIORAL MOCK — drives a mock pipeline through a guard
# function that mirrors the trading-strategist guard. Happy
# path approves and executes; missing approval / rejected
# decision / mismatched signalId are ALL refused.
#
# If a future PR drops the guard, removes a `name` field, breaks
# the pipeline-messages schema, or rewrites a comms section without
# the upstream/downstream wiring, this smoke catches it before merge.
name: neural-trader pipeline risk-gate smoke (#2068, ADR-126 Phase 5)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run pipeline risk-gate smoke
run: node scripts/smoke-neural-trader-pipeline.mjs
neural-trader-feature-attribution-smoke:
# Regression guard for ruvnet/ruflo#2068 — ADR-126 Phase 6.
#
# Phase 6 ships regulator-grade feature attribution for LSTM /
# Transformer trading signals. Given a `signalId`, the `trader-explain`
# skill builds a feature-contribution graph, runs single-entry forward-
# push PageRank from the signal output node (via
# `mcp__ruflo-sublinear__page-rank-entry` when registered, local seeded
# power-iteration fallback otherwise), and stores the top-K ranked
# features as a `SignedAttributionArtifact` (the Phase 4 signing
# scheme — same Ed25519 + CWE-347 trusted-pin pattern).
#
# Three load-bearing artifacts must stay in lockstep:
# 1. `src/signed-attribution.ts` — the typed signer/verifier/PR
# contract.
# 2. `src/signed-attribution.mjs` — the runtime mirror imported by
# the smoke + skill harnesses (zero build step).
# 3. `skills/trader-explain/SKILL.md` — the call site that builds
# the graph + signs the result + persists to `trading-analysis`.
#
# The smoke runs three layers:
# [1/3] STATIC CONTRACT — TS/MJS export shape, verifier pins to
# trustedPublicKey (CWE-347), graphMetadata.seed is part of
# the typed shape, skill writes to `trading-analysis`, skill
# documents the local fallback + the --explain fallback.
# [2/3] CRYPTO ROUND-TRIP — Ed25519 signing fixture, tampered
# feature score fails, tampered seed fails (seed is signed-in),
# swapped served pubkey still verifies (pin is real).
# [3/3] REPRODUCIBILITY — same seed → byte-identical PageRank
# scores + identical top-K ordering. Different seed → scores
# differ (proves the seed is load-bearing, not dead weight).
# Mass conservation: scores sum to ~1.
#
# If a future PR breaks any of these properties — drops the verify
# call, removes the seed from graphMetadata, reverts the seeded
# initializer, drops the local fallback, or rewrites the skill
# without the `trading-analysis` namespace — this smoke catches it
# before merge.
name: neural-trader feature attribution smoke (#2068, ADR-126 Phase 6)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install root deps (for @noble/ed25519)
run: npm install --legacy-peer-deps --no-audit --no-fund --ignore-scripts
- name: Run feature attribution smoke
run: node scripts/smoke-neural-trader-feature-attribution.mjs
plugin-registry-signature-smoke:
# Regression guard for ruvnet/ruflo#1922 (CWE-347 — improper verification
# of cryptographic signature). The plugin-registry signature verifier in
# v3/@claude-flow/cli/src/plugins/store/discovery.ts used to be a stub
# that returned `true` whenever the served `registryPublicKey` field
# started with `"ed25519"`. The call site only `console.warn`ed on
# failure and continued. With `requireVerification: true` (the default),
# a network adversary on the path to an IPFS gateway could swap the
# served registry and have a user install attacker-controlled plugin
# tarballs with filesystem+network+hooks permissions.
#
# This smoke runs three layers:
# 1. Static contract check on discovery.ts — must import
# `verifyEd25519Signature`, must strip both signature fields before
# stringifying, must pin to caller-supplied `expectedPublicKey`
# (not the served `registryPublicKey`), call site must `await` +
# fail-closed.
# 2. Crypto round-trip with real Ed25519, matching the signRegistry()
# scheme in scripts/publish-registry.ts — valid signature passes,
# tampered body fails, empty fields fail, swapped served key
# doesn't defeat the trusted pin.
# 3. Call-site byte check — the `requireVerification` block must
# `await` the verifier AND `return` on failure (not just warn).
#
# If a future PR reverts to prefix-matching, drops the await, or
# silently warns, this smoke catches it before merge.
name: plugin-registry signature verification smoke (#1922, CWE-347)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Install root deps (for @noble/ed25519)
run: npm install --legacy-peer-deps --no-audit --no-fund --ignore-scripts
- name: Run plugin-registry signature smoke
run: node scripts/smoke-plugin-registry-signature.mjs
ruvllm-wasm-auto-init-smoke:
# Regression guard for ruvnet/ruflo#2086 — ruvllm WASM bootstrap not
# exposed via MCP. Reporter: @seo-yas. Every `ruvllm_*` MCP tool that
# touches the WASM runtime calls `loadRuvllmWasm()` in
# `v3/@claude-flow/cli/src/mcp-tools/ruvllm-tools.ts`. That helper used
# to just `import(...)` the module and never call `initRuvllmWasm()`,
# leaving `_wasmReady=false`. Result: `ruvllm_status` reported
# `wasm.initialized=false` even after `ruvllm_sona_create` and
# downstream sona/microlora/hnsw operations silently failed or
# returned empty results.
#
# The fix wires `initRuvllmWasm()` into `loadRuvllmWasm()` (it's
# idempotent — `_wasmReady` short-circuits subsequent calls).
# `ruvllm_status` keeps a separate un-init `loadRuvllmWasmModule()`
# path so diagnostics still report uninitialized state without
# eagerly bootstrapping.
#
# This smoke statically asserts:
# 1. `loadRuvllmWasm()` awaits `mod.initRuvllmWasm()`.
# 2. `loadRuvllmWasmModule()` exists and does NOT init.
# 3. `ruvllm_status` handler uses the un-init loader.
# 4. Every WASM-touching ruvllm_* tool either routes through
# `loadRuvllmWasm()` or looks up a pre-initialized instance.
# 5. No new ruvllm_* tools have been added that bypass the gate.
name: ruvllm WASM auto-init smoke (#2086)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run ruvllm WASM auto-init smoke
run: node scripts/smoke-ruvllm-wasm-auto-init.mjs
agent-execute-providers-smoke:
# Regression guard for ruvnet/ruflo#2042 — agent_execute hardcoded
# the Anthropic SDK and ignored the v3 provider system. Reporter:
# @ummcke00. Fix routes executeAgentTask through callAnthropicMessages
# which dispatches Anthropic / OpenRouter / Ollama by env var.
name: agent_execute provider routing smoke (#2042)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run agent_execute providers smoke
run: node scripts/smoke-agent-execute-providers.mjs
memory-stats-legacy-db-smoke:
# Regression guard for ruvnet/ruflo#2120 — `ruflo memory stats` and
# `listEntries` returned 0 entries against a populated
# `.swarm/memory.db` on WSL2 (reporter: @alexandrelealbess on
# alpha.81). Root cause: the `WHERE status = 'active'` filter
# excluded rows where the status column was NULL (legacy DBs
# created before the status column existed, or via the auto-memory
# bridge path that didn't set status).
#
# Fix: (1) accept `status IS NULL` alongside `'active'` in both
# bridgeListEntries and listEntries; (2) backfill `UPDATE
# memory_entries SET status = 'active' WHERE status IS NULL` in
# ensureSchemaColumns; (3) broaden `isInitialized()` in
# `status.ts` to also accept `.swarm/memory.db` existence.
#
# This smoke builds a 251-entry DB with all-NULL status (mirroring
# the reporter's setup), runs listEntries, and asserts total === 251
# AND that the backfill promoted all rows to 'active'.
name: memory stats legacy-DB smoke (#2120)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Setup pnpm (workspace-aware install)
uses: pnpm/action-setup@v6
with:
version: 8
- name: Install root deps (sql.js for the smoke fixture)
# The smoke creates a `.swarm/memory.db` fixture using `sql.js`
# imported from the repo root. Root has no `workspace:*` deps,
# so plain npm works here.
run: npm install --legacy-peer-deps --no-audit --no-fund --ignore-scripts
- name: Install workspace deps + build CLI (pnpm — `workspace:*` protocol)
# `cd v3/@claude-flow/cli && npm install` errors with
# `EUNSUPPORTEDPROTOCOL: Unsupported URL Type "workspace:"` because
# npm doesn't understand workspace:*. The pnpm workspace root is
# `v3/pnpm-workspace.yaml`. The CLI's tsc references project-refs to
# @claude-flow/swarm (and others), so those must be built first —
# `pnpm --filter @claude-flow/cli build` alone fails with TS6305.
# `--recursive --no-bail` builds the whole graph in topological order
# past unrelated package failures; we then assert the specific dist
# file the smoke imports.
working-directory: v3
run: |
pnpm install --frozen-lockfile=false --ignore-scripts
pnpm --recursive --no-bail run build || true
test -f @claude-flow/cli/dist/src/memory/memory-initializer.js \
|| (echo "CLI build did not produce memory-initializer.js"; exit 1)
- name: Run memory stats legacy-DB smoke
run: node scripts/smoke-memory-stats-legacy-db.mjs
memory-no-stray-db-smoke:
# Regression guard for ADR-125 Phase 7 — vitest.setup.ts in
# @claude-flow/memory must wipe any *.db / *.db-journal / *.db-wal /
# *.rvf / *.redb files written by agentdb / @ruvector/rvf native
# bindings during the test run. This smoke:
# 1. records `git status --porcelain` baseline in v3/@claude-flow/memory
# 2. runs `npm test` inside that package
# 3. asserts no net-new DB-like artifacts appeared after the run
# A failure here means vitest.setup.ts stopped cleaning up, or a new
# binding started writing to a path not covered by LEAK_SUFFIXES.
name: memory no-stray-db smoke (ADR-125 P7)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Setup pnpm
uses: pnpm/action-setup@v4
with:
version: 8
- name: Install workspace deps (pnpm — @claude-flow/memory pulled in workspace:* deps)
# @claude-flow/memory now depends on sibling workspace packages via
# workspace:* protocol, so plain npm install fails with
# EUNSUPPORTEDPROTOCOL. We MUST run install scripts (no --ignore-scripts)
# because the smoke runs `npm test` which opens better-sqlite3 via
# native bindings — the .node addon comes from better-sqlite3's
# prebuild-install postinstall step.
working-directory: v3
run: |
pnpm install --frozen-lockfile=false
pnpm --recursive --no-bail run build || true
# Belt-and-suspenders: explicit rebuild in case prebuild-install was skipped
pnpm rebuild better-sqlite3 || true
- name: Run memory no-stray-db smoke
run: node scripts/smoke-memory-no-stray-db.mjs
github-safe-injection-smoke:
# Regression guard for ruvnet/ruflo#2089 — ADR-127 Phase 1.
#
# Drives adversarial PR/issue bodies (backticks, $(), semicolons,
# >256KB, empty) through github-safe.js and asserts the body lands in a
# temp file verbatim rather than being passed inline to `gh`. Inline
# interpolation of untrusted body content into shell arguments is the
# prompt-injection vector closed by the swarm-pr.md / swarm-issue.md
# fix in Phase 2. This smoke ensures github-safe.js itself always writes
# the temp file and passes --body-file rather than --body.
#
# Runs against both copies:
# - .claude/helpers/github-safe.js (dogfood)
# - v3/@claude-flow/cli/.claude/helpers/ (init-template)
name: github-safe injection smoke (#2089, ADR-127 Phase 1)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run github-safe injection smoke
run: node scripts/smoke-github-safe-injection.mjs
github-actions-pins-smoke:
# Static contract guard for ruvnet/ruflo#2089 — ADR-127 Phase 1.
#
# Scans every `uses:` line in:
# .claude/agents/github/*.md
# .claude/skills/github-[name]/SKILL.md
# v3/@claude-flow/cli/.claude/commands/github/[name].md
#
# Asserts each ref is either SHA-pinned (40-hex) or present in
# .github/supply-chain/allowed-deps.json actions.allowed[].
#
# This smoke catches any future commit that copies @v3 snippets from
# blog posts into the skill files — the most common doc-drift pattern.
# Phase 3 upgrades all @v3 refs to @v4; this smoke is the regression
# gate that prevents them from drifting back.
name: github actions pin smoke (#2089, ADR-127 Phase 1)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run github actions pin smoke
run: node scripts/smoke-github-actions-pins.mjs
github-deprecated-actions-smoke:
# Regression guard for ruvnet/ruflo#2089 — ADR-127 Phase 3.
#
# Scans every `uses:` line in the same scope as the pins smoke and
# fails if any deprecated action ref is still present:
# - actions/checkout@v3 (v4 available since Nov 2023)
# - actions/setup-node@v3 (v4 available since Nov 2023)
# - actions/create-release@* (deprecated; replaced by gh CLI)
# - actions/upload-release-asset@* (deprecated; replaced by gh CLI)
# - softprops/action-gh-release@v1 (v1 has known CVEs; use @v2)
#
# Phase 3 bumps all @v3 refs to @v4. This smoke is the regression gate
# that prevents them from drifting back via copy-paste from blog posts
# or AI-generated snippets that still reference the old versions.
name: github deprecated actions smoke (#2089, ADR-127 Phase 3)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run deprecated-actions smoke
run: node scripts/smoke-deprecated-actions.mjs
github-attribution-opt-in-smoke:
# Regression guard for ruvnet/ruflo#2089 — ADR-127 Phase 4.
#
# Phase 4 removes hardcoded "Generated with Claude Code" attribution
# strings from the static github command templates. Attribution is now
# opt-in via --attribution / options.attribution=true (#1670 / #2089).
# Hard-wired footers silently added a third-party Co-Authored-By line
# to every user's commits and were impossible to undo without rewriting
# git history.
#
# This smoke scans the same three trees as the pins smoke and fails if
# the "Generated with" emoji pattern reappears — preventing copy-paste
# regression from blog posts or AI-generated snippets.
name: github attribution opt-in smoke (#2089, ADR-127 Phase 4)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run attribution opt-in smoke
run: node scripts/smoke-attribution-opt-in.mjs
pre-bash-hook-smoke:
# Regression guard for ruvnet/ruflo#2017 — the `pre-bash` PreToolUse hook
# in `.claude/helpers/hook-handler.cjs` is a security gate that refuses to
# run dangerous commands (`rm -rf /`, fork bombs, etc.). In 3.6.30 it
# silently exited 0 on every dangerous payload because the handler read
# the wrong stdin field (`toolInput` object instead of
# `toolInput.command` string), `.toLowerCase()` threw TypeError, and the
# global safety-timer try/catch swallowed it — printing a misleading
# "[OK] Command validated" while letting the dangerous command through.
#
# This smoke drives real Claude-Code-shaped PreToolUse JSON into BOTH
# the published template (v3/@claude-flow/cli/.claude/helpers/) and the
# repo's dogfood copy (.claude/helpers/), and fails on any of:
# - dangerous command + exit 0 (the security bypass)
# - "[OK] Command validated" on a dangerous input (the misleading log)
# - "[WARN] Hook X encountered an error" anywhere (silent-swallow shape)
#
# Independent job (not nested in plugin-hooks-smoke) so the gate is
# individually visible and the CI summary names the failure clearly.
name: pre-bash hook safety smoke (#2017)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Drive Claude-Code-shaped PreToolUse JSON through both handler copies
shell: bash
run: node scripts/smoke-pre-bash-hook.mjs
mcp-protocol-smoke:
# Regression guard for ruvnet/ruflo#1874 — boots the HTTP MCP server,
# sends a real `initialize` request, and validates the response wire
# format against the MCP spec (YYYY-MM-DD protocolVersion string,
# not a {major,minor,patch} object). This was the missing layer that
# let #1874 ship — the existing in-process unit tests asserted the
# wrong shape, so test+prod agreed on a non-spec format that broke
# Claude Code's Zod validator.
name: MCP protocol smoke / ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Build mcp + shared packages (static scan needs dist)
working-directory: v3
shell: bash
run: |
pnpm install --frozen-lockfile
pnpm --filter @claude-flow/mcp... --filter @claude-flow/shared... run build
- name: Run MCP protocol-shape compliance scan
shell: bash
run: node plugins/ruflo-core/scripts/test-mcp-protocol.mjs
memory-import-smoke:
# Regression guard for ruvnet/ruflo#1883 + #1884 — drives the import-side
# invariants that single-component tests missed:
# #1883 — memory_import_claude(allProjects=false) failed on WSL because
# project-hash derivation was POSIX-only. Verified via candidate
# set including the Claude-Code Windows hash form.
# #1884 — import handler skipped validateMemoryInput, producing keys
# memory_delete then rejected. Verified via property test
# against 27 adversarial inputs covering the full
# dangerous-character set.
# Static dist scan + property test combined cover the bug class.
name: Memory import smoke / ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Build packages (static scan needs cli dist)
working-directory: v3
shell: bash
run: |
pnpm install --frozen-lockfile
# Mirror witness-verify pattern — recursive + no-bail so unrelated
# package failures don't block this guard. The smoke script
# explicitly checks for memory-tools.js dist and fails loudly if
# the cli build itself didn't produce it.
pnpm --recursive --no-bail run build || true
- name: Run memory-import regression guards (#1883 + #1884)
shell: bash
run: node plugins/ruflo-core/scripts/test-memory-import.mjs
mcp-roundtrip-smoke:
# Regression guard for ruvnet/ruflo#1889 — paired MCP tool round-trip.
# When two MCP tools form a store/search pair, the contract is that
# data written by one MUST be retrievable by the other. The original
# bug: pattern-store wrote to memory-store-fallback while pattern-search
# only queried ReasoningBank (always empty), so writes were silently
# lost. Static dist-scan asserts both fallback paths are present;
# behavioural probe runs with an internal 30s timeout so CI never hangs
# on slow memory-backend init.
name: MCP paired-tool round-trip smoke (#1889)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Build cli (smoke needs dist)
working-directory: v3
shell: bash
run: |
pnpm install --frozen-lockfile
pnpm --recursive --no-bail run build || true
- name: Run #1889 round-trip smoke
shell: bash
run: node plugins/ruflo-core/scripts/test-mcp-roundtrips.mjs
- name: Run #1863 cli-no-crash smoke
shell: bash
# Drives `task create` → `task status` + a few command formatters
# and asserts none crash with an unhandled exception. Catches the
# class where callMCPTool<{...}>() types a response field as
# non-optional but the server omits it → `.join()` on undefined.
run: node plugins/ruflo-core/scripts/test-cli-no-crash.mjs
- name: Run ADR-095 G2 consensus-transport guard
shell: bash
# Asserts @claude-flow/swarm ships a real pluggable ConsensusTransport
# (not the implicit single-process EventEmitter): required exports
# present, LocalTransport round-trip works, Ed25519 sign/verify is
# real (no `return true` stub regression). ADR-095 G2.
run: node plugins/ruflo-core/scripts/test-consensus-transport.mjs
tool-descriptions-audit:
# ADR-112 — gate: every MCP tool description must include "Use when …"
# guidance so Claude knows when to pick Ruflo's tool over native
# (Bash, Read, Grep, Glob, Task, TodoWrite, WebFetch). Baseline lives
# in verification/mcp-tool-baseline.json and is monotone-decreasing —
# the script fails the build if the no-guidance count goes UP. Run
# `node scripts/audit-tool-descriptions.mjs --update-baseline` after
# landing a description-improvement PR to lock the new floor.
name: Tool description discoverability (ADR-112)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run tool-description audit
shell: bash
run: node scripts/audit-tool-descriptions.mjs
- name: Run CLI ↔ MCP tool coverage audit (#1916)
# Fails if a `ruflo <cmd>` subcommand callMCPTool()s a tool that isn't
# registered in src/mcp-tools/*.ts (it would die with `MCP tool not
# found`). Monotone-decreasing baseline at verification/cli-mcp-tool-
# baseline.json — new dangling references fail; fixing one and running
# `node scripts/audit-cli-mcp-tools.mjs --update-baseline` lowers the floor.
shell: bash
run: node scripts/audit-cli-mcp-tools.mjs
plugin-package-audit:
# Regression guard for #1902/#1903/#1904 — plugin package.json install
# safety. Checks: (A) no unpublished @claude-flow/* hard dep / non-optional
# peer, (B) no bare-stable peer range that can't resolve a 3.x prerelease
# publish, (C) every main/module/exports path is covered by `files`,
# (D) after building each plugin, every main/module/exports path exists on
# disk (#1904). The build step is what makes check D live.
name: Plugin package install-safety (#1902/#1903/#1904)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Install root deps (semver for audit-wrapper-dep-ranges)
# The audit-wrapper-dep-ranges.mjs script imports `semver` from the
# repo root node_modules. Run a root npm install BEFORE the audit
# job's main install/build step.
shell: bash
run: npm install --legacy-peer-deps --no-audit --no-fund --ignore-scripts
- name: Install workspace + build plugins (so check D — exports-exist-after-build — is live)
working-directory: v3
shell: bash
run: |
pnpm install --frozen-lockfile
for d in plugins/*/; do
if [ -f "$d/package.json" ] && node -e "process.exit(require('./'+process.argv[1]+'package.json').scripts?.build?0:1)" "$d"; then
echo "::group::build $d"
( cd "$d" && pnpm install --frozen-lockfile 2>/dev/null || pnpm install 2>/dev/null || true; pnpm run build || true )
echo "::endgroup::"
fi
done
- name: Run plugin package audit
shell: bash
run: node scripts/audit-plugin-packages.mjs
- name: Run package dep-overlap audit (#1147 / #2018)
# Static guard for the "Invalid Version: " npm 11.x crash. Any package
# declaring the same dep name in both optionalDependencies AND
# peerDependencies trips arborist's dedupe pass when a transitive
# carries a "-dev." prerelease tag. Fail CI on the overlap.
shell: bash
run: node scripts/audit-package-dep-overlap.mjs
- name: Run wrapper dep-range audit (#2127)
# Catches stale alpha-tagged dep ranges in the ruflo wrapper and the
# claude-flow umbrella that point at sibling packages we publish.
# #2127 shipped with `ruflo → @claude-flow/cli@^3.7.0-alpha.11` even
# after cli moved to stable 3.10.0 — a pre-release range against a
# stable dep widens arborist's dedupe walk and can surface as the
# "Invalid Version: (empty)" crash on some npm/Node combos.
shell: bash
run: node scripts/audit-wrapper-dep-ranges.mjs
- name: Run umbrella version-lockstep audit (#2151)
# Asserts @claude-flow/cli, claude-flow, and ruflo all share the
# same version. #2151 reported ruflo@3.10.2 + cli@3.10.1 drift —
# `npx ruflo --version` printed the bundled CLI's 3.10.1, not the
# wrapper's 3.10.2. CLAUDE.md publish rules already require lockstep;
# this audit enforces it so drift can't reach a release.
shell: bash
run: node scripts/audit-umbrella-version-lockstep.mjs
- name: Run better-sqlite3 override audit (#2219)
# agentdb pins better-sqlite3 as an OPTIONAL dep at ^11.8.1, which has
# no Node 24/25/26 prebuild → the optional native build fails silently
# on those runtimes and AgentDB drops to a non-persistent backend
# (silent write loss). Asserts both the root umbrella and the ruflo
# wrapper override better-sqlite3 to >=12.8.0 (which ships Node 2026
# prebuilds). Root overrides do NOT reach the published wrapper (#2112),
# so both must carry it.
shell: bash
run: node scripts/audit-better-sqlite3-override.mjs
- name: Run plugin-hooks cross-platform audit (#2132)
# Catches /bin/bash literals, POSIX-only pipelines (jq, xargs, tr),
# and .sh script invocations in plugin hooks.json files — patterns
# that fail on native Windows (exit 126).
# Files with "_platform": "posix" are exempt (Mac/Linux-only, verified
# to have a companion ruflo-hook.cjs Windows shim). New hooks.json
# files without the marker are scanned strictly.
# Strict (no continue-on-error) — fixed in PR fix/2132-windows-hooks.
shell: bash
run: node scripts/audit-plugin-hooks-cross-platform.mjs
- name: Run env-var-precedence audit (ADR-125 / ADR-130)
# Guards the "CLI flag > ENV var > default" precedence rule added in
# ADR-125 (rvagent integration) and ADR-130 (graph intelligence).
# Scans v3/cli/src and plugins/ for any CLAUDE_FLOW_* / RUFLO_* env
# var read that lacks a documented CLI-flag override path or is not
# registered as an intentional escape hatch (CI/credential/process
# signal). Fails closed (exit 1) — prevents future contributors from
# silently ignoring explicit CLI flags when an env var is set.
# Strict (no continue-on-error).
shell: bash
run: node scripts/audit-env-var-precedence.mjs
cli-npx-install-smoke:
# Regression guard for ruvnet/ruflo#1147 and #2018 — both report:
# $ npx @claude-flow/cli@latest …
# npm error Invalid Version:
# Caused by an optionalDependencies/peerDependencies overlap deep in the
# dep tree (see scripts/audit-package-dep-overlap.mjs for the static
# guard). This is the BEHAVIOURAL guard: pack the locally-built CLI,
# install the tarball into a scratch project, fail if `npm install`
# prints "Invalid Version" or exits non-zero. Runs on Node 22 + 24 so
# the newer npm shipped with Node 24 (where the bug surfaces) is
# actually exercised.
name: CLI npx-install smoke (#1147 / #2018) / Node ${{ matrix.node }}
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
node: ['22', '24']
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js ${{ matrix.node }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node }}
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Build cli (smoke packs from its dist/)
working-directory: v3
shell: bash
run: |
pnpm install --frozen-lockfile
pnpm --recursive --no-bail run build || true
test -f @claude-flow/cli/bin/cli.js \
|| (echo "cli build did not produce bin/cli.js"; exit 1)
- name: Pack, install, and run cli (asserts no 'Invalid Version' crash)
shell: bash
run: node scripts/smoke-cli-npx-install.mjs
windows-hook-shim-smoke:
# Smoke test for ruvnet/ruflo#2132 — ruflo-hook.cjs cross-platform shim.
# Proves that plugins/ruflo-core/scripts/ruflo-hook.cjs can be invoked
# via `node ruflo-hook.cjs <subcommand>`, always exits 0, and accepts
# stdin JSON input without crashing.
# Runs on all 3 OS to prove cross-platform behaviour.
name: Windows hook shim smoke (#2132) / ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run ruflo-hook.cjs smoke
shell: bash
run: node scripts/smoke-ruflo-hook-cjs.mjs
windows-init-hooks-smoke:
# Smoke test for ruvnet/ruflo#2132 — init-time platform detection.
# Proves ruflo init generates correct hook commands per platform:
# - windows-latest: node-based (no /bin/bash, no | jq)
# - ubuntu/macos: POSIX-compatible (sh-based, no cmd.exe patterns)
# Also verifies ruflo-hook.cjs is always deployed to .claude/helpers/.
name: Windows init hooks smoke (#2132) / ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Build CLI
working-directory: v3
shell: bash
run: |
pnpm install --frozen-lockfile
pnpm --recursive --no-bail run build || true
# --no-bail lets unrelated workspace failures pass; assert the
# one dist file the smoke needs is actually produced.
test -f @claude-flow/cli/bin/cli.js \
|| (echo "::error::cli build did not produce bin/cli.js"; exit 1)
- name: Run init hooks smoke
shell: bash
run: node scripts/smoke-windows-init-hooks.mjs
windows-hook-execution-smoke:
# End-to-end validation for ruvnet/ruflo#2132 — simulates a Claude Code
# PostToolUse hook firing. Asserts exit code != 126 ("cannot execute
# binary file") and no POSIX-only pipeline patterns in the generated
# settings.json hook commands.
# Primary target: windows-latest (the OS that originally broke).
# Also runs on ubuntu/macos to catch POSIX regression.
name: Windows hook execution smoke (#2132) / ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Build CLI
working-directory: v3
shell: bash
run: |
pnpm install --frozen-lockfile
pnpm --recursive --no-bail run build || true
- name: Run Windows hook execution smoke
shell: bash
run: node scripts/smoke-windows-hook-execution.mjs
hook-command-audit:
# Regression guard for #1921 (and #1147) — plugin hooks.json must not
# invoke a bare `npx <pkg>@alpha hooks …` per fire. The fix is
# scripts/ruflo-hook.sh (prefers a local `ruflo`/`claude-flow` binary,
# falls back to `npx --prefer-offline`, always exits 0).
name: Hook-command install safety (#1921)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run hook-command audit
shell: bash
run: node scripts/audit-hook-commands.mjs
- name: Run hook-handler prompt-resolution audit (#1944)
shell: bash
run: node scripts/audit-hook-handler-prompt.mjs
- name: Run fix-invariants audit (presence-check guard for #1939, #1941, #1943, #1945, #1946, #1951, #1953, #1968)
shell: bash
run: node scripts/audit-fix-invariants.mjs
- name: Run neural-trader install-safety audit (#1974)
shell: bash
run: node scripts/audit-neural-trader-safety.mjs
- name: Lint the ruflo-hook shims
shell: bash
run: |
# Every shim must be valid bash and exit 0 even when no CLI is
# reachable (the #1921 contract). Probe with an empty PATH (invoke
# bash by full path since PATH= can't find it otherwise).
BASH="$(command -v bash)"
for sh in $(git ls-files '**/scripts/ruflo-hook.sh'); do
echo "checking $sh"
bash -n "$sh"
PATH= "$BASH" "$sh" modify-bash </dev/null || { echo "::error::$sh did not exit 0 with no CLI on PATH"; exit 1; }
done
vector-dim-audit:
# Regression guard for #1947 / #1942 / #1952 — `vector_indexes` rows for
# the `default` and `patterns` namespaces must use `dimensions = 384` to
# match the default ONNX embedding model (Xenova/all-MiniLM-L6-v2). A
# mismatched dim (the historical 768) is silent: HNSW rejects every
# `memory_store --vector` insert and `memory_search` always returns 0.
name: Vector-index dimension audit (#1947)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run vector-dim audit
shell: bash
run: node scripts/audit-vector-dim.mjs
tool-output-guardrail-smoke:
# Smoke test for ADR-131 / ruvnet/ruflo#2149 — ToolOutputGuardrail
# detects the four canonical OWASP ASI01 (Agent Goal Hijacking) attack
# shapes (instruction override, ChatML/Llama frame injection, exfiltration)
# and enforces the documented default policy (critical→reject,
# high→redact, medium→flag, low/none→allow).
#
# The exhaustive behavioural surface is the package's vitest suite; this
# smoke runs against the *built* dist/index.js to catch missing exports
# or accidental relaxation of the default policy.
name: ToolOutputGuardrail smoke (ADR-131, #2149)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Build @claude-flow/security
working-directory: v3
shell: bash
run: |
pnpm install --frozen-lockfile
pnpm --filter @claude-flow/security run build
test -f @claude-flow/security/dist/index.js \
|| (echo "::error::@claude-flow/security/dist/index.js missing after build"; exit 1)
- name: Run guardrail smoke
shell: bash
run: node scripts/smoke-tool-output-guardrail.mjs
witness-verify:
# Cryptographic regression guard: runs `ruflo verify --manifest` against
# the local build to confirm every documented fix in verification.md.json
# still has its marker present. Catches silent regression of any of the
# 81+ fixes without requiring per-fix smoke tests.
#
# The smoke jobs above (smoke-install-no-bsqlite, plugin-hooks-smoke)
# exercise *behavior*; this job validates *presence* — both layers
# combined are what gates `publish`.
#
# Builds the cli + memory + security packages specifically (the ones
# whose dist files are referenced by witness markers). Avoids depending
# on the workspace `build` job so unrelated package failures don't
# block this regression guard.
#
# Cross-platform: ubuntu + macos + windows. The standalone verifier
# is pure JS (only @noble/ed25519) — no native deps, no shell calls.
# Catches platform-specific JSON canonicalization or path-resolution
# bugs (e.g. CRLF normalization on Windows breaking the manifest
# hash) before they reach users.
name: Witness verify (signed manifest) / ${{ matrix.os }}
runs-on: ${{ matrix.os }}
strategy:
fail-fast: false
matrix:
os: [ubuntu-latest, macos-latest, windows-latest]
needs: [smoke-install-no-bsqlite, plugin-hooks-smoke, pre-bash-hook-smoke, witness-verify-precondition-smoke, witness-marker-drift-smoke, browser-rvf-create-flags-smoke, mcp-protocol-smoke, memory-import-smoke, tool-descriptions-audit, mcp-roundtrip-smoke, plugin-package-audit, cli-npx-install-smoke, hook-command-audit, vector-dim-audit, windows-hook-shim-smoke, windows-init-hooks-smoke, windows-hook-execution-smoke]
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Install + build packages witness markers reference
working-directory: v3
shell: bash
run: |
pnpm install --frozen-lockfile
# --no-bail tolerates unrelated package failures. The standalone
# verify.mjs below checks specific dist files exist; if a
# marker-cited file is missing, witness-verify reports it.
pnpm --recursive --no-bail run build || true
- name: Verify witness manifest (via standalone plugin script — no CLI dep)
# `shell: bash` makes Windows use git-bash so the same script
# works across all 3 OSes. RUNNER_TEMP is set on every runner.
shell: bash
run: |
# Use the project-agnostic verifier from ruflo-core plugin. This
# is the same script external adopters use — dogfooding ensures
# the plugin pathway works and avoids CI-specific cli build
# issues (e.g. missing @ruvector/sona, sharp native postinstall).
# Capture both streams; analyze regardless of exit code so we
# always see WHY a verification failed.
# OS-specific manifest path (verification/<os>/manifest.md.json).
# Each runner reads its own snapshot — Linux runner reads
# verification/linux/, macOS reads verification/macos/, etc.
case "$RUNNER_OS" in
Linux) OS_DIR=linux ;;
macOS) OS_DIR=macos ;;
Windows) OS_DIR=windows ;;
*) OS_DIR=linux ;;
esac
MANIFEST="verification/$OS_DIR/manifest.md.json"
echo "verifying: $MANIFEST"
set +e
node plugins/ruflo-core/scripts/witness/verify.mjs \
--manifest "$MANIFEST" \
--json > "$RUNNER_TEMP/witness-result.json" 2> "$RUNNER_TEMP/witness-result.err"
VERIFY_EXIT=$?
set -e
echo "--- verify.mjs exit code: $VERIFY_EXIT ---"
echo "--- stderr ---"
cat "$RUNNER_TEMP/witness-result.err" || true
echo "--- summary ---"
node -e "
const fs = require('fs');
const raw = fs.readFileSync(process.env.RUNNER_TEMP + '/witness-result.json', 'utf8');
if (!raw.trim()) { console.error('verify.mjs produced no JSON output'); process.exit(1); }
const r = JSON.parse(raw);
console.log(JSON.stringify({signature: r.signature, summary: r.summary}, null, 2));
const failures = (r.results || []).filter(x => x.status !== 'pass' && x.status !== 'drift');
if (failures.length) {
console.error('non-pass fixes:');
for (const f of failures) console.error(' ' + f.status + ': ' + f.id + ' (' + f.file + ')');
}
if (!r.ok) { console.error('witness verify FAILED'); process.exit(1); }
if (r.summary.regressed > 0) { console.error('regressed fixes:', r.summary.regressed); process.exit(1); }
console.log('witness verify ok:', r.summary.pass, 'pass,', r.summary.drift, 'drift');
"
- name: Performance verification (lightweight capabilities only)
# Soft gate: appends to verification/<os>/performance.jsonl. Skips
# install_no_optional + memory_round_trip in CI to keep the matrix
# fast — those are slow + network/disk-bound and add minutes.
# Local devs run the full suite with `--capabilities all` (omitted).
shell: bash
run: |
case "$RUNNER_OS" in
Linux) OS_DIR=linux ;;
macOS) OS_DIR=macos ;;
Windows) OS_DIR=windows ;;
esac
node plugins/ruflo-core/scripts/witness/perf.mjs \
--output "verification/$OS_DIR/performance.jsonl" \
--capabilities install_pack,memory_load,witness_verify \
--baseline || echo "perf bench errored — non-blocking"
- name: History summary (ADR-103 — surface regression-introduction commits)
# Soft gate: prints transitions since the previous snapshot. The
# `summary` subcommand exits non-zero on any newly-regressed fix.
shell: bash
run: |
case "$RUNNER_OS" in
Linux) OS_DIR=linux ;;
macOS) OS_DIR=macos ;;
Windows) OS_DIR=windows ;;
*) OS_DIR=linux ;;
esac
HISTORY="verification/$OS_DIR/history.jsonl"
if [ -f "$HISTORY" ]; then
node plugins/ruflo-core/scripts/witness/history.mjs \
--history "$HISTORY" \
summary
else
echo "$HISTORY not present — skipping temporal diff"
fi
publish:
name: Publish to npm (alpha)
runs-on: ubuntu-latest
needs: [build, smoke-install-no-bsqlite, plugin-hooks-smoke, witness-verify]
if: github.ref == 'refs/heads/v3' && github.event_name == 'push'
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: ${{ env.NODE_VERSION }}
registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Install dependencies
working-directory: v3
run: pnpm install --frozen-lockfile
- name: Build
working-directory: v3
run: pnpm build
- name: Publish alpha
working-directory: v3
run: pnpm publish:alpha
env:
NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}
init-bundle-invariants-smoke:
# Regression guard for ruvnet/ruflo#2095 — ADR-128 Phase 5.
#
# Statically asserts three invariants of the @claude-flow/cli init bundle:
#
# 1. NO ORPHANED DIRECTORIES — every subdirectory under
# v3/@claude-flow/cli/.claude/{commands,agents}/ is reachable from
# COMMANDS_MAP or AGENTS_MAP in executor.ts.
#
# 2. SKILLS_MAP COMPLETENESS — every skill referenced in SKILLS_MAP
# has a SKILL.md (or README.md) inside the package's .claude/skills/.
#
# 3. NO INIT-PLUGIN AGENT COLLISION — no .md in the init-template
# agents/ shares a basename with any plugin agent. Enforces the
# "plugin is canonical" dedup rule from ADR-128 Phase 2.
#
# Zero runtime deps; pure Node.js readFileSync + readdirSync.
name: init-bundle invariants smoke (#2095, ADR-128 Phase 5)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run init-bundle invariants smoke
run: node scripts/smoke-init-bundle-invariants.mjs
wasm-provider-bridge-smoke:
# ADR-129 Phase 1 — JsModelProvider routes WasmAgent through the v3
# provider system. Pre-P1: set_model_provider() was never called and
# the echo-stub bypass competed with the WASM runtime. P1 fix:
# attachJsModelProvider() is called at agent-creation time.
# Static-only smoke: no build, no API keys required.
name: wasm provider bridge smoke (ADR-129 P1)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run wasm provider bridge smoke
run: node scripts/smoke-wasm-provider-bridge.mjs
wasm-compose-smoke:
# ADR-129 Phase 2 — wasm_agent_compose MCP tool + addMcpTools bridge.
# Fixes the silent drop of template.mcp_tools in buildRvfFromTemplate.
# Static-only smoke: no build, no API keys required.
name: wasm compose smoke (ADR-129 P2)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run wasm compose smoke
run: node scripts/smoke-wasm-rvf-compose.mjs
wasm-gallery-crud-smoke:
# ADR-129 Phase 3 — 16 new MCP tools (10 gallery CRUD + 6 agent
# introspection), with AIDefence gate on wasm_gallery_import.
# Static-only smoke: no build, no API keys required.
name: wasm gallery CRUD smoke (ADR-129 P3)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run wasm gallery CRUD smoke
run: node scripts/smoke-wasm-gallery-crud.mjs
wasm-plugin-bridge-smoke:
# ADR-129 Phase 4 — plugin manifest "rvagent" field + includePlugins
# in wasm_agent_compose. Static + behavioral fixture smoke.
name: wasm plugin bridge smoke (ADR-129 P4)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Run wasm plugin bridge smoke
run: node scripts/smoke-wasm-plugin-bridge.mjs
graph-schema-smoke:
# ADR-130 Phase 1 — graph_edges schema + PQ encoder + edge writer
name: graph schema smoke (ADR-130 P1)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Install root deps (sql.js for smoke scripts)
# The smoke scripts use `await import('sql.js')` (bare ESM specifier),
# which resolves from root node_modules. pnpm install inside v3/ alone
# does not reliably expose sql.js to scripts run from the repo root.
# Same pattern as the #2120 memory-stats smoke job.
run: npm install --legacy-peer-deps --no-audit --no-fund --ignore-scripts
- name: Build better-sqlite3 native binding for graph-edge-writer
# --ignore-scripts above skipped better-sqlite3's prebuild-install,
# leaving node_modules/better-sqlite3 on disk without the .node
# binary. The graph-edge-writer does `await import('better-sqlite3')`
# which Node resolves from the importing module's nearest
# node_modules — that ends up being the *root* tree (closer than
# v3/.pnpm), so the broken root copy wins and _openDb returns null
# → insertGraphEdge returns false → TEST 2/3/5 all fail. Rebuild it.
run: npm rebuild better-sqlite3
- name: Install v3 workspace deps and build
run: |
cd v3
pnpm install --frozen-lockfile
pnpm -r build
- name: Run graph schema smoke
run: node scripts/smoke-graph-schema-migration.mjs
graph-query-smoke:
# ADR-130 Phase 2+5 — agentdb_graph-query + agentdb_graph-pathfinder dispatch
name: graph query + pathfinder smoke (ADR-130 P2+P5)
runs-on: ubuntu-latest
needs: graph-schema-smoke
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Install root deps (sql.js for the smoke fixture)
# Same pattern as the graph-schema-smoke job — sql.js is a root
# transitive dep, not declared in any workspace package.json.
run: npm install --legacy-peer-deps --no-audit --no-fund --ignore-scripts
- name: Build better-sqlite3 native binding
# See graph-schema-smoke comment — same root-resolution hazard.
run: npm rebuild better-sqlite3
- name: Install v3 workspace deps and build
run: |
cd v3
pnpm install --frozen-lockfile
pnpm -r build
- name: Symlink sql.js into v3/node_modules
run: |
mkdir -p v3/node_modules
ln -sfn "$(pwd)/node_modules/sql.js" v3/node_modules/sql.js
- name: Run graph query dispatch smoke
run: node scripts/smoke-graph-query-dispatch.mjs
- name: Run graph pathfinder smoke
run: node scripts/smoke-graph-pathfinder.mjs
graph-trajectory-smoke:
# ADR-130 Phase 3 — SONA trajectory-to-graph hooks
name: graph trajectory hooks smoke (ADR-130 P3)
runs-on: ubuntu-latest
needs: graph-schema-smoke
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Install root deps (sql.js for smoke scripts)
run: npm install --legacy-peer-deps --no-audit --no-fund --ignore-scripts
- name: Build better-sqlite3 native binding
# See graph-schema-smoke comment — same root-resolution hazard.
run: npm rebuild better-sqlite3
- name: Install dependencies and build
run: |
cd v3
pnpm install --frozen-lockfile
pnpm -r build
- name: Run trajectory graph edges smoke
# #2312 root cause (fixed): memory-bridge's rescueAgentdbEmbedder
# patched agentdb.embedder.embed to delegate to generateEmbedding —
# which is bridge-first, so the patched embed re-entered itself via
# bridgeGenerateEmbedding in an unbounded async cycle (heap OOM at
# the V8 limit, SIGABRT 134; SONA/EWC were innocent). The rescue now
# delegates to generateLocalEmbedding (a bridge-free leaf), so this
# smoke gates again at default heap — verified green in a node:22
# Linux container at 512 MB with a cold model cache.
run: node scripts/smoke-trajectory-graph-edges.mjs
graph-plugin-adapter-smoke:
# ADR-130 Phase 4 — plugin adapter contract (GraphEdgesSource)
name: graph plugin adapter smoke (ADR-130 P4)
runs-on: ubuntu-latest
needs: graph-schema-smoke
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Install root deps (sql.js for smoke scripts)
run: npm install --legacy-peer-deps --no-audit --no-fund --ignore-scripts
- name: Build better-sqlite3 native binding
# See graph-schema-smoke comment — same root-resolution hazard.
run: npm rebuild better-sqlite3
- name: Install dependencies and build
run: |
cd v3
pnpm install --frozen-lockfile
pnpm -r build
- name: Run graph plugin adapter smoke
run: node scripts/smoke-graph-plugin-adapter.mjs
graph-benchmark:
# ADR-130 Phase 6 — graph write/query throughput benchmark
name: graph benchmark (ADR-130 P6)
runs-on: ubuntu-latest
needs: [graph-query-smoke, graph-trajectory-smoke, graph-plugin-adapter-smoke]
timeout-minutes: 40
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
cache: 'pnpm'
cache-dependency-path: v3/pnpm-lock.yaml
- name: Install root deps (sql.js for smoke scripts)
run: npm install --legacy-peer-deps --no-audit --no-fund --ignore-scripts
- name: Build better-sqlite3 native binding
# See graph-schema-smoke comment — same root-resolution hazard.
run: npm rebuild better-sqlite3
- name: Install dependencies and build
run: |
cd v3
pnpm install --frozen-lockfile
pnpm -r build
- name: Run graph benchmark
run: node scripts/benchmark-graph.mjs
statusline-generator-delegation-smoke:
# Regression guard for ruvnet/ruflo#2195 — the statusline generator
# previously re-implemented all data readers locally with fragile file
# probes that returned wrong values (DDD 0/5, intelligence 1%, ADR 87/87).
#
# The fix delegates to 'npx @claude-flow/cli@latest hooks statusline --json'
# as the single source of truth and counts ADRs in BOTH directories
# (v3/implementation/adrs/ + v3/docs/adr/).
#
# Two guards:
# [1/2] STATIC — assert generator still delegates to the CLI command
# and does NOT contain the old fragile heuristics.
# [2/2] SMOKE — build the CLI, run the generated .cjs with --json,
# assert all numeric fields are within valid ranges and that
# the known-bad fallback values (domainsCompleted=0 when patterns>0,
# intelligencePct=1 when system is healthy) are not present.
name: statusline generator delegation smoke (#2195)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Guard 1 — static delegation contract
shell: bash
run: |
echo "Checking that statusline-generator.ts delegates to 'hooks statusline --json'..."
grep -q "hooks statusline --json" v3/@claude-flow/cli/src/init/statusline-generator.ts \
|| { echo "::error::statusline-generator.ts no longer delegates to 'hooks statusline --json' — regression of #2195"; exit 1; }
echo "ok: delegation pattern found"
echo "Checking that the old fragile heuristics are NOT present..."
# The buggy fallback computed domains from file-based pattern counts —
# this produced domainsCompleted=0 when AgentDB had 26k+ patterns.
! grep -q "getLearningStats\b" v3/@claude-flow/cli/src/init/statusline-generator.ts \
|| { echo "::error::statusline-generator.ts still has getLearningStats (old local reader) — regression of #2195"; exit 1; }
! grep -q "getV3Progress\b" v3/@claude-flow/cli/src/init/statusline-generator.ts \
|| { echo "::error::statusline-generator.ts still has getV3Progress (old local reader) — regression of #2195"; exit 1; }
echo "ok: old local-reader heuristics removed"
echo "Checking that both ADR directories are counted..."
grep -q "v3/docs/adr" v3/@claude-flow/cli/src/init/statusline-generator.ts \
|| { echo "::error::statusline-generator.ts missing v3/docs/adr ADR directory — regression of #2195 ADR-count bug"; exit 1; }
grep -q "v3/implementation/adrs" v3/@claude-flow/cli/src/init/statusline-generator.ts \
|| { echo "::error::statusline-generator.ts missing v3/implementation/adrs ADR directory"; exit 1; }
echo "ok: both ADR directories present"
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Install and build CLI
working-directory: v3
run: |
pnpm install --frozen-lockfile
pnpm --recursive --no-bail run build || true
test -f @claude-flow/cli/dist/src/init/statusline-generator.js \
|| { echo "::error::statusline-generator.js not built"; exit 1; }
- name: "Guard 2 — smoke: generate .cjs and validate output ranges"
shell: bash
run: node scripts/smoke-statusline-generator-delegation.mjs
wizard-init-regression-guard:
# Regression guard for ruvnet/ruflo#2206 #2207 #2208 — three init-wizard
# bugs that collectively broke every new user install:
#
# #2206: mcp-generator registered the server under key 'ruflo', causing all
# 33 ruflo plugins to fail with "Unknown tool mcp__claude-flow__*".
# The key must be 'claude-flow'; the command args (ruflo@latest) are
# correct and must NOT change.
#
# #2207: detectExistingRufloMCP only checked for 'ruflo' in mcpServers.
# After #2206, the key becomes 'claude-flow', so the detector would
# NEVER recognise a prior install. Both keys must be accepted. Also
# guarded: bare .claude/settings.json must NOT be a false positive.
#
# #2208: init --force silently destroyed existing CLAUDE.md without backup,
# causing data loss. A .pre-ruflo backup must be created before
# any overwrite; timestamped fallback when a backup already exists.
#
# Three guards (static + runtime each):
# [1/3] STATIC #2206 — mcp-generator source must write 'claude-flow' key
# [2/3] RUNTIME #2206 + #2208 — build CLI, run init in tmp dir, assert
# .mcp.json has 'claude-flow' key and CLAUDE.md.pre-ruflo was created
# [3/3] RUNTIME #2207 — bare .claude/settings.json must NOT trigger
# the duplicate-detection guard (false-positive check)
name: wizard init regression guard (#2206 #2207 #2208)
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
- name: Guard 1 — static #2206 server key contract
shell: bash
run: |
echo "=== Guard 1: mcp-generator must write the claude-flow key ==="
echo "Checking mcp-generator uses 'claude-flow' as the server key..."
grep -qE "mcpServers\['claude-flow'\]|mcpServers\[\"claude-flow\"\]" \
v3/@claude-flow/cli/src/init/mcp-generator.ts \
|| { echo "::error::mcp-generator no longer writes 'claude-flow' server key — #2206 regression"; exit 1; }
echo "ok: 'claude-flow' key found"
echo "Checking mcp-generator does NOT use bare 'ruflo' as the server key..."
! grep -qE "mcpServers\['ruflo'\]|mcpServers\[\"ruflo\"\]" \
v3/@claude-flow/cli/src/init/mcp-generator.ts \
|| { echo "::error::mcp-generator writes 'ruflo' server key — breaks mcp__claude-flow__* tool naming (#2206)"; exit 1; }
echo "ok: bare 'ruflo' server key absent"
echo "Checking command args still invoke ruflo@latest (must NOT change)..."
grep -q "ruflo@latest" v3/@claude-flow/cli/src/init/mcp-generator.ts \
|| { echo "::error::mcp-generator no longer invokes ruflo@latest — command args must be preserved (#2206)"; exit 1; }
echo "ok: ruflo@latest invocation present"
echo "Guard 1 passed."
- name: Setup pnpm
uses: pnpm/action-setup@v6
with:
version: ${{ env.PNPM_VERSION }}
- name: Install and build CLI
working-directory: v3
run: |
pnpm install --frozen-lockfile
pnpm --recursive --no-bail run build || true
test -f @claude-flow/cli/dist/src/init/executor.js \
|| { echo "::error::executor.js not built — CLI build failed"; exit 1; }
test -f @claude-flow/cli/dist/src/init/mcp-generator.js \
|| { echo "::error::mcp-generator.js not built"; exit 1; }
- name: Guard 2 — runtime #2206 server key + #2208 CLAUDE.md backup
shell: bash
run: |
echo "=== Guard 2: generated .mcp.json key and CLAUDE.md backup ==="
TMP=$(mktemp -d)
cd "$TMP"
# Seed CLAUDE.md with sentinel content so #2208 backup is observable
printf '# Sentinel Project Content — do not lose\n' > CLAUDE.md
# Run init --force (non-interactive); ignore non-zero exit from
# optional components that need extra deps (skills, helpers, etc.)
HOME="$TMP" node "$GITHUB_WORKSPACE/v3/@claude-flow/cli/bin/cli.js" \
init --force 2>&1 || true
echo "--- .mcp.json contents ---"
cat .mcp.json 2>/dev/null || echo "(not found)"
# #2206: 'claude-flow' key must exist
node -e "
const cfg = JSON.parse(require('fs').readFileSync('.mcp.json','utf-8'));
if (!cfg.mcpServers || !('claude-flow' in cfg.mcpServers)) {
console.error('FAIL: .mcp.json missing claude-flow server key (#2206)');
process.exit(1);
}
console.log('ok: claude-flow key present in .mcp.json');
" || { echo "::error::generated .mcp.json missing 'claude-flow' server key (#2206)"; exit 1; }
# #2206: stray 'ruflo' key must NOT exist
node -e "
const cfg = JSON.parse(require('fs').readFileSync('.mcp.json','utf-8'));
if (cfg.mcpServers && 'ruflo' in cfg.mcpServers) {
console.error('FAIL: .mcp.json has stray ruflo server key (#2206)');
process.exit(1);
}
console.log('ok: stray ruflo key absent');
" || { echo "::error::generated .mcp.json has stray 'ruflo' server key (#2206)"; exit 1; }
# #2208: CLAUDE.md.pre-ruflo backup must exist with sentinel content
BACKUP=$(ls CLAUDE.md.pre-ruflo* 2>/dev/null | head -1)
if [ -z "$BACKUP" ]; then
echo "::error::init --force did not create CLAUDE.md.pre-ruflo backup (#2208)"
exit 1
fi
echo "ok: backup file created: $BACKUP"
grep -q "Sentinel Project Content" "$BACKUP" \
|| { echo "::error::CLAUDE.md backup missing original sentinel content (#2208)"; exit 1; }
echo "ok: backup contains original content"
echo "Guard 2 passed."
- name: Guard 3 — runtime #2207 false-positive check
shell: bash
run: |
echo "=== Guard 3: bare .claude/settings.json must NOT trigger duplicate detection ==="
TMP=$(mktemp -d)
cd "$TMP"
# Simulate Claude Code's own project settings.json with NO ruflo registration
mkdir -p .claude
echo '{"env": {}, "hooks": {}}' > .claude/settings.json
# Run init WITHOUT --force; use a custom HOME with no ~/.claude.json
# so the real user's global config doesn't interfere
FAKE_HOME=$(mktemp -d)
OUT=$(HOME="$FAKE_HOME" node "$GITHUB_WORKSPACE/v3/@claude-flow/cli/bin/cli.js" \
init 2>&1 || true)
echo "--- init output ---"
echo "$OUT"
# The init must NOT claim "already initialized" due to the bare settings.json
if echo "$OUT" | grep -qi "existing 'ruflo' MCP registration\|existing 'claude-flow' MCP registration"; then
echo "::error::init false-positives on bare .claude/settings.json (#2207)"
exit 1
fi
echo "ok: no false-positive on bare .claude/settings.json"
rm -rf "$FAKE_HOME"
echo "Guard 3 passed."