Files
ruflo/scripts/bench-managed-workflows.mjs
T
rUvandr db141ba70e feat: managed workflows, unified console runs and verified advisory learning (#4471)
* feat(agent): add bounded managed dynamic workflows and verified lifecycle observation

* feat(plugins): integrate managed workflows with mod policy and capability discovery

* feat(managed): unify console runs and verified advisory learning

* fix(managed): hydrate native recall receipts validated on ruOS

* docs(managed): record authenticated console host blocker

* docs(managed): qualify installed console and compatible native host

* feat(managed): integrate verified cycles and isolate native memory

* fix(managed): enforce offline native models and qualify feedback E2E

* fix(managed): independent security and correctness review findings

- gate managed_agent_interrupt through the managed_agent_control policy; terminate obeys the allowlist and deletes only its own environment when RUFLO_MANAGED_CONTROL=1
- track only events from the acknowledged message so a later poll cannot report the previous turn as finished
- require the canonical base64 verifier signature so one receipt cannot fill the ledger
- register RUFLO_MANAGED_AGENTS_BASE_URL as a trust-boundary env var (env-precedence audit) and key the credential scope marker with a per-process HMAC (CodeQL)
- best-effort local snapshot writes; report messageId when a prompt fails after it was sent
- console: rescheduling is running, budget/action-blocked sessions are active, not finished
- document the remaining known limits in ADR 0004

Co-Authored-By: RuFlo <ruv@ruv.net>

* docs: user guide section for managed cloud agents; correct ruflo-agent README counts

Co-Authored-By: RuFlo <ruv@ruv.net>

* fix(managed): scope marker is a generation number, never a hash of the API key (CodeQL)

Co-Authored-By: RuFlo <ruv@ruv.net>

---------

Co-authored-by: r <r@x>
2026-10-24 22:42:09 -04:00

57 lines
3.4 KiB
JavaScript

/** Deterministic event-processing benchmark, not a model-quality benchmark.
* Node >=22.18: node scripts/bench-managed-workflows.mjs
*/
import { performance } from 'node:perf_hooks';
import { createHash } from 'node:crypto';
import { readFileSync } from 'node:fs';
import { execFileSync } from 'node:child_process';
import { ManagedProgress } from '../v3/@claude-flow/cli/src/services/managed-workflows.ts';
const baselineCommit = '973d0ab497c9138d7bee1dc3c7a4d3985e37bf9e';
const baselinePath = 'v3/@claude-flow/cli/src/mcp-tools/managed-agent-tools.ts';
const baselineSource = execFileSync('git', ['show', `${baselineCommit}:${baselinePath}`], { encoding: 'utf8' });
// Exercise the exact original summarizer from the frozen source, compiled by
// Node's strip-types, rather than substituting a slower baseline algorithm.
const { stripTypeScriptTypes } = await import('node:module');
const original = baselineSource.slice(baselineSource.indexOf('function summarizeEvents('), baselineSource.indexOf('async function fetchSessionEvents('));
const summarize = new Function(`${stripTypeScriptTypes(original)}; return summarizeEvents;`)();
const hash = value => createHash('sha256').update(value).digest('hex');
const candidateSource = readFileSync('v3/@claude-flow/cli/src/services/managed-workflows.ts');
const events = Array.from({ length: 5000 }, (_, i) => ({
id: `e${i}`, type: 'agent.message', processed_at: new Date(1792843200000 + i).toISOString(),
content: [{ type: 'text', text: 'bounded result\n' }],
}));
events.push({ id: 'idle', type: 'session.status_idle', processed_at: '2026-10-24T13:00:00Z', stop_reason: { type: 'end_turn' } });
function run(mode) {
const p = new ManagedProgress(); let result;
const start = performance.now();
for (let n = 100; n < events.length + 100; n += 100) {
result = mode === 'baseline' ? summarize(events.slice(0, n)) : (p.accept(events.slice(n - 100, n)), p.snapshot());
}
return { ms: performance.now() - start, result };
}
for (let i = 0; i < 3; i++) { run('baseline'); run('incremental'); }
const baseline = [], candidate = [];
for (let i = 0; i < 9; i++) {
// Alternate order to reduce systematic warmup bias.
const order = i % 2 ? ['baseline', 'incremental'] : ['incremental', 'baseline'];
for (const mode of order) {
const r = run(mode); (mode === 'baseline' ? baseline : candidate).push(r.ms);
if (r.result.assistantText !== events.slice(0, -1).map(e => e.content[0].text).join('')) throw new Error('Output mismatch');
}
}
const median = xs => [...xs].sort((a, b) => a - b)[Math.floor(xs.length / 2)];
const polls = Math.ceil(events.length / 100);
const baselineVisits = Array.from({ length: polls }, (_, i) => Math.min(events.length, (i + 1) * 100)).reduce((a, b) => a + b, 0);
console.log(JSON.stringify({
kind: 'synthetic event processing only', node: process.version, baselineCommit,
baselineSha256: hash(baselineSource), candidateSha256: hash(candidateSource),
events: events.length, polls, repeats: 9,
baselineMedianMs: median(baseline), candidateMedianMs: median(candidate),
latencyRatio: median(baseline) / median(candidate),
baselineEventVisits: baselineVisits, incrementalEventVisits: events.length,
eventVisitReduction: 1 - events.length / baselineVisits,
limitations: 'No provider latency, token savings, dollar savings or bug-finding improvement measured. Snapshot output assembly is included in both timings.',
}, null, 2));