mirror of
https://github.com/ruvnet/ruflo.git
synced 2026-10-23 03:47:00 +09:00
Published to npm — @claude-flow/cli, claude-flow, ruflo all 3.10.33 (latest, alpha, v3alpha dist-tags). Verified end-to-end against the published ruflo@3.10.33: • `ruflo --version` → 33 ms (was 60 s+ on cold ONNX cache, #2256) • MCP stdio `initialize` → clean JSON-RPC on stdout, ONNX progress on stderr (#2253) ruflo wrapper fast-path (#2256 follow-up) `ruflo/bin/ruflo.js` imports `@claude-flow/cli/dist/src/index.js` directly, bypassing `bin/cli.js` and the --version short-circuit added in the previous commit. End-to-end test showed `npx ruflo --version` still hit the 1.4 s ONNX init. Same guard now inlined in the wrapper: reads version from this wrapper's own package.json and exits before any heavy import. CI regression guards (catches regressions of THIS release at PR-time instead of at scheduled-cron-time where the previous breakage hid for 5 consecutive runs): scripts/smoke-workflows-yaml.mjs (#2267) Parses every `.github/workflows/*.yml`. Exits non-zero on any YAML syntax error. The root cause of #2267 was an unquoted `:` inside a step name string — GitHub Actions accepted the push, scheduled the run, and silently produced zero jobs. This guard catches it at PR- time so the next "Guard 2 — smoke: foo" doesn't slip through. scripts/smoke-router-regex.mjs (#2257) Renders the router from the source-of-truth generator (helpers-generator.ts::generateAgentRouter) and asserts: • 11 known mis-route cases route correctly • Confidence on a match is NOT 0.8 (regression of the heuristic-vs- learned-classifier overclaim) • The generator source still contains \b boundary anchors v3-ci.yml: new `static-regression-guards` job runs both smokes early (no build, no install of the workspace — just js-yaml + tsx). 53 jobs total now parse cleanly. Co-Authored-By: RuFlo <ruv@ruv.net>
This commit is contained in:
1 parent
4c01443712
commit
07b4c86092
7 files changed
+207
-4
No files matched your search
@@ -260,6 +260,23 @@ env:
|
||||
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)
|
||||
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
|
||||
|
||||
test:
|
||||
name: Test V3 Packages
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "claude-flow",
|
||||
"version": "3.10.31",
|
||||
"version": "3.10.33",
|
||||
"description": "Ruflo - Enterprise AI agent orchestration for Claude Code. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
|
||||
"main": "dist/index.js",
|
||||
"type": "module",
|
||||
|
||||
+21
-1
@@ -2,10 +2,30 @@
|
||||
// Ruflo CLI - thin wrapper around @claude-flow/cli with ruflo branding
|
||||
import { fileURLToPath, pathToFileURL } from 'node:url';
|
||||
import { dirname, resolve, join } from 'node:path';
|
||||
import { existsSync } from 'node:fs';
|
||||
import { existsSync, readFileSync } from 'node:fs';
|
||||
|
||||
const __dirname = dirname(fileURLToPath(import.meta.url));
|
||||
|
||||
// #2256 fast path: --version / -V must NOT trigger heavy imports (the
|
||||
// downstream @claude-flow/cli dist eagerly loads ruvector + a 23 MB ONNX
|
||||
// model on cold cache, blocking 60+ s and causing SIGTERM under common
|
||||
// timeout windows: npx default, MCP stdio 30s window). Resolve version
|
||||
// from this wrapper's own package.json and exit before any heavy import.
|
||||
// (bin/cli.js has the same guard for the direct path; needed here too
|
||||
// because the wrapper imports dist/src/index.js, bypassing bin/cli.js.)
|
||||
{
|
||||
const _argv = process.argv.slice(2);
|
||||
if (_argv.length === 1 && (_argv[0] === '--version' || _argv[0] === '-V')) {
|
||||
try {
|
||||
const pkg = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf-8'));
|
||||
process.stdout.write(`ruflo v${pkg.version || '0.0.0'}\n`);
|
||||
} catch {
|
||||
process.stdout.write('ruflo v0.0.0\n');
|
||||
}
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
// Walk up from ruflo/bin/ to find @claude-flow/cli in node_modules
|
||||
function findCliPath() {
|
||||
let dir = resolve(__dirname, '..');
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "ruflo",
|
||||
"version": "3.10.31",
|
||||
"version": "3.10.33",
|
||||
"description": "Ruflo - Enterprise AI agent orchestration platform. Deploy 60+ specialized agents in coordinated swarms with self-learning, fault-tolerant consensus, vector memory, and MCP integration",
|
||||
"main": "bin/ruflo.js",
|
||||
"type": "module",
|
||||
|
||||
@@ -0,0 +1,102 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Smoke test for #2257: the agent router's keyword patterns must be
|
||||
* word-boundary-anchored so short tokens (cd, ci, ui, add, structure) do
|
||||
* NOT match inside unrelated words (decision, infrastructure, address,
|
||||
* addendum). Confidence on a match must be a heuristic prior (< 0.8) since
|
||||
* the matcher is static, not learned.
|
||||
*
|
||||
* This guard locks in the #2257 fix so a future refactor that drops the
|
||||
* \b boundaries or bumps confidence back to 0.8 is caught at PR-time.
|
||||
*
|
||||
* Drives the SOURCE OF TRUTH (helpers-generator.ts::generateAgentRouter)
|
||||
* AND the in-repo snapshot under v3/@claude-flow/cli/.claude/helpers/.
|
||||
*
|
||||
* Exit codes:
|
||||
* 0 — all expectations met
|
||||
* 1 — at least one expectation failed
|
||||
*/
|
||||
|
||||
import { writeFileSync, mkdtempSync, readFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'node:path';
|
||||
import { createRequire } from 'node:module';
|
||||
|
||||
const require = createRequire(import.meta.url);
|
||||
|
||||
// Render the generator output to disk so we test the SOURCE OF TRUTH.
|
||||
async function loadGeneratedRouter() {
|
||||
// tsx is the project's standard TS-on-the-fly loader.
|
||||
const { generateAgentRouter } = await import('../v3/@claude-flow/cli/src/init/helpers-generator.ts')
|
||||
.catch(async () => {
|
||||
// Fall back to a tsx-loaded copy if direct ESM TS import isn't supported.
|
||||
const { execSync } = await import('node:child_process');
|
||||
const tmp = mkdtempSync(join(tmpdir(), 'router-smoke-'));
|
||||
const out = execSync(
|
||||
`node --import tsx -e "import('./v3/@claude-flow/cli/src/init/helpers-generator.ts').then(m => process.stdout.write(m.generateAgentRouter()))"`,
|
||||
{ encoding: 'utf8' }
|
||||
);
|
||||
const path = join(tmp, 'router-gen.cjs');
|
||||
writeFileSync(path, out);
|
||||
return { generateAgentRouter: () => readFileSync(path, 'utf8') };
|
||||
});
|
||||
const tmp = mkdtempSync(join(tmpdir(), 'router-smoke-'));
|
||||
const path = join(tmp, 'router-gen.cjs');
|
||||
writeFileSync(path, generateAgentRouter());
|
||||
return require(path);
|
||||
}
|
||||
|
||||
const CASES = [
|
||||
// [task, expectedAgent ("default" means confidence < 0.5)]
|
||||
['review latest issues', 'reviewer'],
|
||||
['make a decision about auth', 'default'], // 'decision' must NOT match 'ci'
|
||||
['set up ci pipeline', 'devops'], // 'ci' as a real word should match
|
||||
['structure the schema', 'architect'], // 'structure' standalone should match
|
||||
['address the bug', 'default'], // 'address' must NOT match 'add'
|
||||
['add JWT support', 'coder'], // 'add' as a real word should match
|
||||
['fix infrastructure rollout', 'devops'], // 'infrastructure' standalone
|
||||
['write specifications', 'default'], // 'specifications' must NOT match 'spec'
|
||||
['write a unit test', 'tester'], // 'unit test' phrase
|
||||
['build a guidance overview', 'coder'], // 'guidance' must NOT match 'ui'
|
||||
['fix critical and high issues', 'default'], // bare 'fix' / 'critical' shouldn't match anything
|
||||
];
|
||||
|
||||
async function main() {
|
||||
const router = await loadGeneratedRouter();
|
||||
if (typeof router.routeTask !== 'function') {
|
||||
console.error('router.routeTask is not a function — generator export shape changed');
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let pass = 0;
|
||||
let fail = 0;
|
||||
for (const [task, want] of CASES) {
|
||||
const r = router.routeTask(task);
|
||||
const ok = want === 'default'
|
||||
? r.confidence < 0.5
|
||||
: r.agent === want && r.confidence < 0.8; // #2257: confidence must NOT regress to 0.8
|
||||
if (ok) { pass++; console.log(` PASS "${task}" → ${r.agent} (${r.confidence})`); }
|
||||
else { fail++; console.log(` FAIL "${task}" → ${r.agent} (${r.confidence}) want=${want}`); }
|
||||
}
|
||||
console.log(`\n${pass}/${CASES.length} router cases passed`);
|
||||
|
||||
// Hard contract on the file format too — if someone strips \b boundaries
|
||||
// the cases above might still pass on this corpus but break on others.
|
||||
const generated = await router.routeTask ? null : null;
|
||||
const sourcePath = 'v3/@claude-flow/cli/src/init/helpers-generator.ts';
|
||||
const src = readFileSync(sourcePath, 'utf8');
|
||||
if (!src.includes('\\\\b')) {
|
||||
console.error(`\nFAIL ${sourcePath} no longer contains \\b boundary anchors — #2257 regression`);
|
||||
fail++;
|
||||
} else {
|
||||
console.log(`OK ${sourcePath} retains \\b boundary anchors`);
|
||||
}
|
||||
if (/confidence:\s*0\.8/.test(src) && !/heuristic prior/.test(src)) {
|
||||
console.error(`\nFAIL ${sourcePath} confidence:0.8 reintroduced without "heuristic prior" justification — #2257 regression`);
|
||||
fail++;
|
||||
}
|
||||
|
||||
process.exit(fail > 0 ? 1 : 0);
|
||||
}
|
||||
|
||||
main().catch(e => { console.error(e); process.exit(1); });
|
||||
@@ -0,0 +1,64 @@
|
||||
#!/usr/bin/env node
|
||||
/**
|
||||
* Smoke test for #2267: every .github/workflows/*.yml must parse as YAML.
|
||||
*
|
||||
* Root cause that motivated this guard: an unquoted `:` inside a step `name`
|
||||
* (`Guard 2 — smoke: generate ...`) made the YAML parser treat `smoke:` as a
|
||||
* second mapping key, rejecting the whole workflow. GitHub Actions accepted
|
||||
* the push and scheduled the run, but produced zero jobs and a "workflow
|
||||
* file issue" failure — invisible to the kind of green-build dashboard that
|
||||
* usually catches regressions. Five consecutive scheduled runs failed
|
||||
* silently before #2267 surfaced it.
|
||||
*
|
||||
* This guard runs as an early CI step so any future YAML break is caught
|
||||
* at PR-time, not at scheduled-cron-time when nobody is looking.
|
||||
*
|
||||
* Exit codes:
|
||||
* 0 — every workflow parsed
|
||||
* 1 — at least one workflow failed to parse
|
||||
*/
|
||||
|
||||
import { readFileSync, readdirSync } from 'node:fs';
|
||||
import { join } from 'node:path';
|
||||
import yaml from 'js-yaml';
|
||||
|
||||
const WORKFLOWS_DIR = '.github/workflows';
|
||||
|
||||
let files;
|
||||
try {
|
||||
files = readdirSync(WORKFLOWS_DIR).filter(f => f.endsWith('.yml') || f.endsWith('.yaml'));
|
||||
} catch (e) {
|
||||
console.error(`No ${WORKFLOWS_DIR} directory (or unreadable): ${e.message}`);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
let failed = 0;
|
||||
const results = [];
|
||||
for (const file of files.sort()) {
|
||||
const full = join(WORKFLOWS_DIR, file);
|
||||
try {
|
||||
const parsed = yaml.load(readFileSync(full, 'utf8'));
|
||||
const jobCount = parsed && parsed.jobs ? Object.keys(parsed.jobs).length : 0;
|
||||
results.push({ file, ok: true, jobs: jobCount });
|
||||
} catch (e) {
|
||||
failed++;
|
||||
results.push({
|
||||
file,
|
||||
ok: false,
|
||||
error: e.message,
|
||||
line: e.mark ? e.mark.line + 1 : null,
|
||||
column: e.mark ? e.mark.column + 1 : null,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
const okCount = results.filter(r => r.ok).length;
|
||||
console.log(`Workflows: ${okCount}/${results.length} parsed`);
|
||||
for (const r of results) {
|
||||
if (r.ok) {
|
||||
console.log(` ok ${r.file} (${r.jobs} jobs)`);
|
||||
} else {
|
||||
console.log(` FAIL ${r.file} ${r.line}:${r.column} ${r.error}`);
|
||||
}
|
||||
}
|
||||
process.exit(failed > 0 ? 1 : 0);
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@claude-flow/cli",
|
||||
"version": "3.10.31",
|
||||
"version": "3.10.33",
|
||||
"type": "module",
|
||||
"description": "Ruflo CLI - Enterprise AI agent orchestration with 60+ specialized agents, swarm coordination, MCP server, self-learning hooks, and vector memory for Claude Code",
|
||||
"main": "dist/src/index.js",
|
||||
|
||||
Reference in new issue
Block a user