From 3384067b4aa80eaf71ef428695c53165e1a33520 Mon Sep 17 00:00:00 2001 From: rUv Date: Mon, 21 Sep 2026 14:53:46 +0000 Subject: [PATCH 1/9] feat(performance): add task conditioned resource admission policy --- .../performance/src/resource-admission.ts | 154 ++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 v3/@claude-flow/performance/src/resource-admission.ts diff --git a/v3/@claude-flow/performance/src/resource-admission.ts b/v3/@claude-flow/performance/src/resource-admission.ts new file mode 100644 index 0000000000..bd8e84ed17 --- /dev/null +++ b/v3/@claude-flow/performance/src/resource-admission.ts @@ -0,0 +1,154 @@ +export type ResourceVerdict = 'admit' | 'defer' | 'reject'; +export type ResourceLimit = 'cpu' | 'memory' | 'io' | 'remote_wait' | 'capacity' | 'telemetry' | 'profile' | 'none'; + +export interface ResourceProfile { + profileId: string; + toolId: string; + profileDigest: string; + observedAt: string; + cpuDemand: number; + memoryDemand: number; + ioDemand: number; + remoteWaitFraction: number; + minCpuCores: number; + maxCpuCores: number; +} + +export interface ResourceHostSnapshot { + snapshotId: string; + telemetryDigest: string; + observedAt: string; + cpuPressure: number; + memoryPressure: number; + ioPressure: number; + allocatableCpuCores: number; + maxCpuPerTask: number; +} + +export interface ResourcePolicy { + profileMaxAgeMs: number; + telemetryMaxAgeMs: number; + maxCpuPerTask: number; + cpuPressureDefer: number; + memoryPressureDefer: number; + ioPressureDefer: number; + safetyHeadroomFraction: number; +} + +export interface ResourceAdmissionInput { + profile: ResourceProfile; + host: ResourceHostSnapshot; + policy: ResourcePolicy; + requestedConcurrency: number; + now: string; +} + +export interface ResourceAdmissionDecision { + verdict: ResourceVerdict; + recommendedCpuCores: number; + limitingResource: ResourceLimit; + reasonCodes: readonly string[]; + evidenceDigest: string; + authority: 'none'; +} + +const ID = /^[A-Za-z0-9._:/-]{1,160}$/; +const HEX64 = /^[0-9a-f]{64}$/; + +export function decideResourceAdmission(input: ResourceAdmissionInput): ResourceAdmissionDecision { + const invalid = validate(input); + if (invalid) return finish('reject', 0, 'profile', [invalid], `invalid:${invalid}`); + + const now = Date.parse(input.now); + const profileAge = now - Date.parse(input.profile.observedAt); + const telemetryAge = now - Date.parse(input.host.observedAt); + if (profileAge < -1000) return make(input, 'reject', 0, 'profile', ['PROFILE_FROM_FUTURE']); + if (telemetryAge < -1000) return make(input, 'reject', 0, 'telemetry', ['TELEMETRY_FROM_FUTURE']); + if (profileAge > input.policy.profileMaxAgeMs) return make(input, 'defer', 0, 'profile', ['STALE_PROFILE']); + if (telemetryAge > input.policy.telemetryMaxAgeMs) return make(input, 'defer', 0, 'telemetry', ['STALE_TELEMETRY']); + + const hardPolicyCeiling = Math.min(input.host.maxCpuPerTask, input.policy.maxCpuPerTask); + if (hardPolicyCeiling < input.profile.minCpuCores) { + return make(input, 'reject', 0, 'capacity', ['POLICY_CEILING_BELOW_PROFILE_MINIMUM']); + } + + const usableCpu = input.host.allocatableCpuCores * (1 - input.policy.safetyHeadroomFraction); + const concurrencyShare = usableCpu / input.requestedConcurrency; + const ceiling = Math.min(input.profile.maxCpuCores, hardPolicyCeiling, concurrencyShare); + if (ceiling < input.profile.minCpuCores) { + return make(input, 'defer', 0, 'capacity', ['CONCURRENCY_CAPACITY_EXHAUSTED']); + } + + const cpuSensitivity = clamp(input.profile.cpuDemand * (1 - 0.85 * input.profile.remoteWaitFraction)); + if (input.profile.memoryDemand >= 0.4 && input.host.memoryPressure >= input.policy.memoryPressureDefer) { + return make(input, 'defer', 0, 'memory', ['MEMORY_PRESSURE']); + } + if (input.profile.ioDemand >= 0.4 && input.host.ioPressure >= input.policy.ioPressureDefer) { + return make(input, 'defer', 0, 'io', ['IO_PRESSURE']); + } + if (cpuSensitivity >= 0.35 && input.host.cpuPressure >= input.policy.cpuPressureDefer) { + return make(input, 'defer', 0, 'cpu', ['CPU_PRESSURE']); + } + + const reasons: string[] = []; + if (input.profile.remoteWaitFraction >= 0.7) reasons.push('REMOTE_WAIT_DOMINANT'); + if (cpuSensitivity >= 0.6) reasons.push('CPU_SENSITIVE'); + if (reasons.length === 0) reasons.push('WITHIN_RESOURCE_ENVELOPE'); + + const target = input.profile.remoteWaitFraction >= 0.7 + ? input.profile.minCpuCores + : input.profile.minCpuCores + (ceiling - input.profile.minCpuCores) * cpuSensitivity; + const cpu = Math.min(ceiling, Math.ceil(target * 4) / 4); + return make(input, 'admit', cpu, limitingResource(input, cpuSensitivity), reasons); +} + +function validate(input: ResourceAdmissionInput): string | undefined { + if (!input || typeof input !== 'object' || !input.profile || !input.host || !input.policy) return 'INVALID_INPUT'; + if (!ID.test(input.profile.profileId) || !ID.test(input.profile.toolId) || !ID.test(input.host.snapshotId)) return 'INVALID_ID'; + if (!HEX64.test(input.profile.profileDigest) || !HEX64.test(input.host.telemetryDigest)) return 'INVALID_DIGEST'; + if (![input.profile.observedAt, input.host.observedAt, input.now].every(canonicalTime)) return 'INVALID_TIMESTAMP'; + const units = [input.profile.cpuDemand, input.profile.memoryDemand, input.profile.ioDemand, input.profile.remoteWaitFraction, input.host.cpuPressure, input.host.memoryPressure, input.host.ioPressure, input.policy.cpuPressureDefer, input.policy.memoryPressureDefer, input.policy.ioPressureDefer, input.policy.safetyHeadroomFraction]; + if (units.some((v) => !Number.isFinite(v) || v < 0 || v > 1)) return 'INVALID_UNIT_VALUE'; + if (input.policy.safetyHeadroomFraction > 0.5) return 'EXCESSIVE_HEADROOM'; + const cores = [input.profile.minCpuCores, input.profile.maxCpuCores, input.host.allocatableCpuCores, input.host.maxCpuPerTask, input.policy.maxCpuPerTask]; + if (cores.some((v) => !Number.isFinite(v) || v <= 0 || v > 4096)) return 'INVALID_CPU_BOUND'; + if (input.profile.minCpuCores > input.profile.maxCpuCores) return 'INVALID_PROFILE_CPU_RANGE'; + if (!Number.isSafeInteger(input.requestedConcurrency) || input.requestedConcurrency < 1 || input.requestedConcurrency > 100000) return 'INVALID_CONCURRENCY'; + if (!Number.isSafeInteger(input.policy.profileMaxAgeMs) || input.policy.profileMaxAgeMs < 1 || !Number.isSafeInteger(input.policy.telemetryMaxAgeMs) || input.policy.telemetryMaxAgeMs < 1) return 'INVALID_FRESHNESS_BOUND'; + return undefined; +} + +function make(input: ResourceAdmissionInput, verdict: ResourceVerdict, cpu: number, limit: ResourceLimit, reasons: readonly string[]): ResourceAdmissionDecision { + const material = [input.profile.profileId, input.profile.toolId, input.profile.profileDigest, input.host.snapshotId, input.host.telemetryDigest, input.profile.observedAt, input.host.observedAt, input.now, input.requestedConcurrency, verdict, cpu, limit, reasons.join(',')].join('|'); + return finish(verdict, cpu, limit, reasons, material); +} + +function finish(verdict: ResourceVerdict, cpu: number, limit: ResourceLimit, reasons: readonly string[], material: string): ResourceAdmissionDecision { + return Object.freeze({ verdict, recommendedCpuCores: cpu, limitingResource: limit, reasonCodes: Object.freeze([...reasons]), evidenceDigest: stableFingerprint(material), authority: 'none' as const }); +} + +function limitingResource(input: ResourceAdmissionInput, cpuSensitivity: number): ResourceLimit { + if (input.profile.remoteWaitFraction >= 0.7) return 'remote_wait'; + const values: Array<[ResourceLimit, number]> = [['cpu', cpuSensitivity * Math.max(0.1, input.host.cpuPressure)], ['memory', input.profile.memoryDemand * Math.max(0.1, input.host.memoryPressure)], ['io', input.profile.ioDemand * Math.max(0.1, input.host.ioPressure)]]; + return values.sort((a, b) => b[1] - a[1])[0][0]; +} + +function canonicalTime(value: string): boolean { + if (typeof value !== 'string') return false; + const parsed = new Date(value); + return Number.isFinite(parsed.getTime()) && parsed.toISOString() === value; +} + +function clamp(value: number): number { return Math.max(0, Math.min(1, value)); } + +/** Stable evidence fingerprint only. It is not a signature or authority token. */ +function stableFingerprint(value: string): string { + let a = 2166136261 >>> 0; + let b = 2246822519 >>> 0; + for (let i = 0; i < value.length; i += 1) { + const c = value.charCodeAt(i); + a = Math.imul(a ^ c, 16777619) >>> 0; + b = Math.imul(b ^ c, 3266489917) >>> 0; + } + return `${a.toString(16).padStart(8, '0')}${b.toString(16).padStart(8, '0')}`; +} From 07bf9ff0d92913be459275d8dbe847eb417c06cd Mon Sep 17 00:00:00 2001 From: rUv Date: Mon, 21 Sep 2026 14:54:21 +0000 Subject: [PATCH 2/9] test(performance): cover resource admission safety and determinism --- .../__tests__/resource-admission.test.ts | 203 ++++++++++++++++++ 1 file changed, 203 insertions(+) create mode 100644 v3/@claude-flow/performance/__tests__/resource-admission.test.ts diff --git a/v3/@claude-flow/performance/__tests__/resource-admission.test.ts b/v3/@claude-flow/performance/__tests__/resource-admission.test.ts new file mode 100644 index 0000000000..6d72836606 --- /dev/null +++ b/v3/@claude-flow/performance/__tests__/resource-admission.test.ts @@ -0,0 +1,203 @@ +import { describe, expect, it } from 'vitest'; +import { decideResourceAdmission, type ResourceAdmissionInput } from '../src/resource-admission.js'; + +const now = '2026-09-21T14:00:00.000Z'; +const digest = 'a'.repeat(64); + +function fixture(overrides: Partial = {}): ResourceAdmissionInput { + const base: ResourceAdmissionInput = { + profile: { + profileId: 'profile.raqa.cpu.v1', + toolId: 'retrieval.local', + profileDigest: digest, + observedAt: '2026-09-21T13:59:30.000Z', + cpuDemand: 0.9, + memoryDemand: 0.45, + ioDemand: 0.2, + remoteWaitFraction: 0.1, + minCpuCores: 1, + maxCpuCores: 8, + }, + host: { + snapshotId: 'host.primary', + telemetryDigest: 'b'.repeat(64), + observedAt: '2026-09-21T13:59:59.000Z', + cpuPressure: 0.3, + memoryPressure: 0.2, + ioPressure: 0.2, + allocatableCpuCores: 16, + maxCpuPerTask: 8, + }, + policy: { + profileMaxAgeMs: 300_000, + telemetryMaxAgeMs: 30_000, + maxCpuPerTask: 8, + cpuPressureDefer: 0.9, + memoryPressureDefer: 0.9, + ioPressureDefer: 0.9, + safetyHeadroomFraction: 0.1, + }, + requestedConcurrency: 1, + now, + }; + return { + ...base, + ...overrides, + profile: { ...base.profile, ...(overrides.profile ?? {}) }, + host: { ...base.host, ...(overrides.host ?? {}) }, + policy: { ...base.policy, ...(overrides.policy ?? {}) }, + }; +} + +describe('resource admission policy', () => { + it('allocates more CPU to a measured CPU sensitive task without crossing ceilings', () => { + const decision = decideResourceAdmission(fixture()); + expect(decision.verdict).toBe('admit'); + expect(decision.reasonCodes).toContain('CPU_SENSITIVE'); + expect(decision.recommendedCpuCores).toBeGreaterThan(1); + expect(decision.recommendedCpuCores).toBeLessThanOrEqual(8); + expect(decision.authority).toBe('none'); + }); + + it('does not waste CPU on a remote wait dominated task', () => { + const input = fixture({ + profile: { + ...fixture().profile, + profileId: 'profile.web.remote.v1', + cpuDemand: 0.7, + remoteWaitFraction: 0.9, + }, + }); + const decision = decideResourceAdmission(input); + expect(decision.verdict).toBe('admit'); + expect(decision.recommendedCpuCores).toBe(input.profile.minCpuCores); + expect(decision.limitingResource).toBe('remote_wait'); + }); + + it('defers CPU sensitive work during high CPU pressure', () => { + const decision = decideResourceAdmission(fixture({ + host: { ...fixture().host, cpuPressure: 0.95 }, + })); + expect(decision.verdict).toBe('defer'); + expect(decision.reasonCodes).toEqual(['CPU_PRESSURE']); + expect(decision.recommendedCpuCores).toBe(0); + }); + + it('defers memory heavy work during memory pressure', () => { + const decision = decideResourceAdmission(fixture({ + profile: { ...fixture().profile, memoryDemand: 0.9 }, + host: { ...fixture().host, memoryPressure: 0.95 }, + })); + expect(decision.verdict).toBe('defer'); + expect(decision.reasonCodes).toEqual(['MEMORY_PRESSURE']); + }); + + it('fails stale telemetry closed', () => { + const decision = decideResourceAdmission(fixture({ + host: { ...fixture().host, observedAt: '2026-09-21T13:00:00.000Z' }, + })); + expect(decision.verdict).toBe('defer'); + expect(decision.reasonCodes).toEqual(['STALE_TELEMETRY']); + }); + + it('rejects an impossible policy ceiling', () => { + const decision = decideResourceAdmission(fixture({ + profile: { ...fixture().profile, minCpuCores: 4, maxCpuCores: 8 }, + policy: { ...fixture().policy, maxCpuPerTask: 2 }, + })); + expect(decision.verdict).toBe('reject'); + expect(decision.reasonCodes).toEqual(['POLICY_CEILING_BELOW_PROFILE_MINIMUM']); + }); + + it('never recommends more aggregate CPU than current allocatable capacity', () => { + const input = fixture({ + host: { ...fixture().host, allocatableCpuCores: 4, maxCpuPerTask: 8 }, + requestedConcurrency: 4, + policy: { ...fixture().policy, safetyHeadroomFraction: 0 }, + }); + const decision = decideResourceAdmission(input); + expect(decision.verdict).toBe('admit'); + expect(decision.recommendedCpuCores * input.requestedConcurrency).toBeLessThanOrEqual(input.host.allocatableCpuCores); + }); + + it('is deterministic for identical evidence', () => { + const input = fixture(); + const first = decideResourceAdmission(input); + const second = decideResourceAdmission(input); + expect(second).toEqual(first); + expect(first.evidenceDigest).toMatch(/^[0-9a-f]{16}$/); + }); + + it('rejects malformed evidence rather than widening allocation', () => { + const input = fixture({ + profile: { ...fixture().profile, profileDigest: 'not-a-digest' }, + }); + const decision = decideResourceAdmission(input); + expect(decision.verdict).toBe('reject'); + expect(decision.recommendedCpuCores).toBe(0); + expect(decision.authority).toBe('none'); + }); +}); + +describe('resource admission structural benchmark', () => { + it('keeps p95 policy decision overhead below 100 microseconds', () => { + const seeds = [11, 29, 47, 71, 101]; + const batchMicros: number[] = []; + let ceilingViolations = 0; + let decisions = 0; + + for (const seed of seeds) { + let state = seed >>> 0; + for (let batch = 0; batch < 20; batch += 1) { + const inputs: ResourceAdmissionInput[] = []; + for (let index = 0; index < 100; index += 1) { + state = (Math.imul(state, 1664525) + 1013904223) >>> 0; + const unit = state / 0xffffffff; + inputs.push(fixture({ + profile: { + ...fixture().profile, + profileId: `profile.synthetic.${seed}.${batch}.${index}`, + cpuDemand: unit, + memoryDemand: (unit * 1.7) % 1, + ioDemand: (unit * 2.3) % 1, + remoteWaitFraction: (unit * 3.1) % 1, + }, + host: { + ...fixture().host, + cpuPressure: (unit * 1.3) % 1, + memoryPressure: (unit * 1.9) % 1, + ioPressure: (unit * 2.7) % 1, + }, + })); + } + const start = performance.now(); + for (const input of inputs) { + const result = decideResourceAdmission(input); + decisions += 1; + if (result.recommendedCpuCores > input.host.maxCpuPerTask || result.recommendedCpuCores > input.policy.maxCpuPerTask) { + ceilingViolations += 1; + } + } + batchMicros.push(((performance.now() - start) * 1000) / inputs.length); + } + } + + batchMicros.sort((a, b) => a - b); + const p95 = batchMicros[Math.floor(batchMicros.length * 0.95)]; + console.log(JSON.stringify({ + schema: 'ruflo-resource-admission-benchmark/v1', + workload: '10000 deterministic synthetic resource decisions', + seeds, + sampleSize: decisions, + metric: 'microseconds_per_decision', + p95, + ceilingViolations, + authorityExpansion: 0, + energy: 'not_measured', + costUsd: 0, + })); + expect(decisions).toBe(10_000); + expect(ceilingViolations).toBe(0); + expect(p95).toBeLessThan(100); + }); +}); From 92db002fbc64829d454d1413519614a9f33d06fb Mon Sep 17 00:00:00 2001 From: rUv Date: Mon, 21 Sep 2026 14:54:31 +0000 Subject: [PATCH 3/9] feat(performance): export resource admission policy --- v3/@claude-flow/performance/src/index.ts | 28 +++++++++++------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/v3/@claude-flow/performance/src/index.ts b/v3/@claude-flow/performance/src/index.ts index f5ddb84700..9cdadf0523 100644 --- a/v3/@claude-flow/performance/src/index.ts +++ b/v3/@claude-flow/performance/src/index.ts @@ -2,20 +2,10 @@ * @claude-flow/performance * * Performance module for claude-flow v3. - * Provides benchmarking, Flash Attention validation, and optimization utilities. - * - * Target Performance Metrics: - * - CLI Startup: <500ms (5x faster) - * - MCP Init: <400ms (4.5x faster) - * - Agent Spawn: <200ms (4x faster) - * - Vector Search: <1ms (150x faster) - * - Memory Write: <5ms (10x faster) - * - Swarm Consensus: <100ms (5x faster) - * - Flash Attention: 2.49x-7.47x speedup - * - Memory Usage: <256MB (50% reduction) + * Provides benchmarking, Flash Attention validation, optimization, and + * resource-aware admission primitives. */ -// Re-export benchmark framework export { benchmark, BenchmarkRunner, @@ -34,7 +24,6 @@ export { type PerformanceTarget, } from './framework/benchmark.js'; -// Re-export Flash Attention integration export { FlashAttentionOptimizer, createFlashAttentionOptimizer, @@ -45,7 +34,6 @@ export { type PerformanceMetrics as AttentionMetrics, } from './attention-integration.js'; -// Re-export Flash Attention benchmarks export { AttentionBenchmarkRunner, formatBenchmarkTable, @@ -59,5 +47,15 @@ export { type MemoryProfile, } from './attention-benchmarks.js'; -// Default export for convenience +export { + decideResourceAdmission, + type ResourceVerdict, + type ResourceLimit, + type ResourceProfile, + type ResourceHostSnapshot, + type ResourcePolicy, + type ResourceAdmissionInput, + type ResourceAdmissionDecision, +} from './resource-admission.js'; + export { default } from './framework/benchmark.js'; From 8190574a0f330e5f816f5ebd54c265a40e6ada67 Mon Sep 17 00:00:00 2001 From: rUv Date: Mon, 21 Sep 2026 14:55:19 +0000 Subject: [PATCH 4/9] docs: add ADR 400 task conditioned resource admission --- ...400-task-conditioned-resource-admission.md | 94 +++++++++++++++++++ 1 file changed, 94 insertions(+) create mode 100644 v3/docs/adr/ADR-400-task-conditioned-resource-admission.md diff --git a/v3/docs/adr/ADR-400-task-conditioned-resource-admission.md b/v3/docs/adr/ADR-400-task-conditioned-resource-admission.md new file mode 100644 index 0000000000..e78dd2e7ad --- /dev/null +++ b/v3/docs/adr/ADR-400-task-conditioned-resource-admission.md @@ -0,0 +1,94 @@ +# ADR-400: Task-Conditioned Resource Admission + +Status: proposed + +Date: 2026-09-21 + +Issue: #3388 + +## Context + +Agent serving mixes remote model latency with local tool execution, so end-to-end task latency is a poor proxy for local resource demand. `Not All AI Agents Are Equal: Characterizing Resource and Performance Dynamics` (arXiv:2609.19947, submitted 2026-09-17) reports task-dependent CPU, memory, disk-I/O, and remote-wait behavior across retrieval-augmented QA, web search, and coding. The originating team reports CPU-aware tool admission improving CPU-sensitive task latency by up to about 5.4x and task-aware CPU allocation reducing mixed-workload average latency by about 32% versus its native-agent baseline. + +The paper is an originating-team result, not an independently reproduced RuFlo claim. Its systems measurements replay recorded Gemini 3 Flash trajectories on an Azure VM with 24 vCPU AMD EPYC 7V13 and 216 GB RAM. GPU inference is out of scope because the model is accessed remotely. + +RuFlo already contains generic load-balancing and resource-allocation guidance. The missing reusable primitive is a deterministic policy that separates measured task/tool resource phenotype from current host pressure and from host-supplied safety ceilings. + +## Decision + +Add a pure `ResourceAdmissionPolicy` primitive to `@claude-flow/performance`. + +The policy consumes a provenance-bound resource profile, a fresh host telemetry snapshot, a fixed host policy, and requested concurrency. It returns only an admission recommendation and CPU allocation recommendation. + +It does not execute a task, mutate container limits, kill another task, alter RVM authority, or widen a host ceiling. Every decision carries `authority: none`. + +CPU allocation is reduced for remote-wait-dominant work because additional local cores are unlikely to improve the critical path. CPU-sensitive work receives more of the available per-task envelope when pressure allows it. High CPU, memory, or I/O pressure causes defer decisions for workloads sensitive to the pressured resource. Stale evidence fails closed. + +The decision fingerprint is deterministic but intentionally non-authoritative and non-cryptographic. Production provenance or signing belongs at the RVF/RVM boundary. + +## Invariants + +1. A recommendation never exceeds the profile, host, or policy CPU ceiling. +2. Aggregate recommendation under declared concurrency never exceeds current allocatable CPU after configured headroom. +3. Stale or future evidence cannot result in an expanded allocation. +4. Malformed profile or telemetry evidence is rejected. +5. Natural-language task descriptions are not policy input. +6. The primitive grants no execution capability and carries no authority. +7. Resource history from RuVector may inform a future profile estimator, but retrieval confidence cannot override a host ceiling. +8. A task-conditioned policy cannot preempt an incumbent task; destructive conflict resolution remains a separate RVM-controlled decision. + +## Falsification + +Independent MetaHarness evaluation must compare fixed allocation, generic utilization-only allocation, task-conditioned allocation, and a retrospective oracle under matched model, workload, host, resource budget, and evaluator conditions. + +Required workloads include CPU-bound retrieval, remote-wait-heavy search, I/O-heavy coding, bursty memory workloads, malformed profiles, stale telemetry, resource exhaustion, and adversarial attempts to exceed a ceiling. + +Reject this architecture if generic utilization-only control matches it within noise, if a stale profile causes unsafe admission, or if gains on one workload come from starving another. + +## Benchmark contract + +Report baseline and candidate, exact commit, hardware, operating system, Node version, workload, model/API version where relevant, seeds, sample size, p50 and p95 latency, throughput, CPU-seconds per completed task, memory peak, disk I/O, task success, failures, variance, cost, energy where measurable, ablations, and reproduction commands. + +The pure policy benchmark requires zero ceiling violations, zero non-deterministic decisions, and less than 100 microseconds p95 decision overhead. + +System-level promotion requires at least one of: + +1. at least 15% lower p95 tool latency; +2. at least 15% higher throughput at the same resource budget; +3. at least 20% lower CPU-seconds per completed task; + +with no task-success regression greater than 2 absolute points and no protected fairness or starvation regression. + +## Cross-stack mapping + +RuFlo consumes the policy for future scheduling and admission. + +MetaHarness owns independent systems evaluation and protected fairness tests. + +RVM remains the effect and resource-ceiling enforcement boundary. + +Core Memory stores accepted profiles and benchmark evidence with provenance. + +RuVector and RuVector WASM may retrieve historical resource phenotypes but never grant allocation authority. + +RVF binds profile, telemetry, decision, and evaluation digests. RVForge packages only reproduced policy artifacts. + +Autogenous and Dream Machine may improve profile estimation after baselines freeze, but cannot modify resource ceilings or acceptance thresholds. + +MidStream carries telemetry and phase-boundary observations. + +RuView, RuField, and WorldGraph can use the same primitive for sensing and spatial pipelines where task resource profiles differ materially. + +LatentMesh can contribute distributed pressure observations without creating trust. + +Cognitum can use accepted policy behavior to improve tenant density, latency, and gross margin under explicit SLA ceilings. + +MCP supplies tool identity; host-bound policy decides local admission. + +## Migration and rollback + +The change is additive. Existing scheduling behavior remains unchanged until a later, separately reviewed integration chooses to consume the policy. Rollback removes the module, export, tests, workflow, and ADR. No durable schema migration is required. + +## Governance + +No autonomous merge, deployment, scheduler integration, resource-limit mutation, credential escalation, or weakening of admission and evaluation gates is permitted. Independent MetaHarness evidence and human approval are required before integration. \ No newline at end of file From 359a49e97f6eecd6edd7dd72d933fed3b422c772 Mon Sep 17 00:00:00 2001 From: rUv Date: Mon, 21 Sep 2026 14:55:32 +0000 Subject: [PATCH 5/9] ci: add resource admission validation workflow --- .github/workflows/resource-admission.yml | 40 ++++++++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 .github/workflows/resource-admission.yml diff --git a/.github/workflows/resource-admission.yml b/.github/workflows/resource-admission.yml new file mode 100644 index 0000000000..f9829f14a9 --- /dev/null +++ b/.github/workflows/resource-admission.yml @@ -0,0 +1,40 @@ +name: Resource admission + +on: + pull_request: + paths: + - 'v3/@claude-flow/performance/**' + - 'v3/docs/adr/ADR-400-task-conditioned-resource-admission.md' + - '.github/workflows/resource-admission.yml' + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: resource-admission-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + validate: + name: Node ${{ matrix.node }} + runs-on: ubuntu-24.04 + timeout-minutes: 20 + strategy: + fail-fast: false + matrix: + node: [20.20.2, 22.18.0] + steps: + - name: Check out exact source + uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 + - name: Set up Node + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 + with: + node-version: ${{ matrix.node }} + cache: npm + - name: Install exact repository dependencies + run: npm ci --ignore-scripts --no-audit --fund=false + - name: Type check and build performance package + run: npx tsc -p v3/@claude-flow/performance/tsconfig.json + - name: Run resource admission tests and structural benchmark + run: npx vitest run v3/@claude-flow/performance/__tests__/resource-admission.test.ts From 215e284a51077861eb695d46218c9a866ff30701 Mon Sep 17 00:00:00 2001 From: rUv Date: Mon, 21 Sep 2026 14:58:11 +0000 Subject: [PATCH 6/9] ci: use supported Node 22 engine for resource admission --- .github/workflows/resource-admission.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/resource-admission.yml b/.github/workflows/resource-admission.yml index f9829f14a9..30a70bc8d5 100644 --- a/.github/workflows/resource-admission.yml +++ b/.github/workflows/resource-admission.yml @@ -23,7 +23,7 @@ jobs: strategy: fail-fast: false matrix: - node: [20.20.2, 22.18.0] + node: [20.20.2, 22.22.3] steps: - name: Check out exact source uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 From a381775b41e9471bbb69cfb567ce4309052dbb1c Mon Sep 17 00:00:00 2001 From: rUv Date: Mon, 21 Sep 2026 14:58:56 +0000 Subject: [PATCH 7/9] test(performance): report baseline and candidate resource admission benchmark --- .../resource-admission-report.test.ts | 144 ++++++++++++++++++ 1 file changed, 144 insertions(+) create mode 100644 v3/@claude-flow/performance/__tests__/resource-admission-report.test.ts diff --git a/v3/@claude-flow/performance/__tests__/resource-admission-report.test.ts b/v3/@claude-flow/performance/__tests__/resource-admission-report.test.ts new file mode 100644 index 0000000000..58213e3aa1 --- /dev/null +++ b/v3/@claude-flow/performance/__tests__/resource-admission-report.test.ts @@ -0,0 +1,144 @@ +import { expect, it } from 'vitest'; +import { decideResourceAdmission, type ResourceAdmissionInput } from '../src/resource-admission.js'; + +const now = '2026-09-21T14:00:00.000Z'; + +function inputFor(seed: number, batch: number, index: number, unit: number): ResourceAdmissionInput { + return { + profile: { + profileId: `profile.report.${seed}.${batch}.${index}`, + toolId: 'tool.synthetic', + profileDigest: 'a'.repeat(64), + observedAt: '2026-09-21T13:59:30.000Z', + cpuDemand: unit, + memoryDemand: (unit * 1.7) % 1, + ioDemand: (unit * 2.3) % 1, + remoteWaitFraction: (unit * 3.1) % 1, + minCpuCores: 1, + maxCpuCores: 8, + }, + host: { + snapshotId: 'host.report', + telemetryDigest: 'b'.repeat(64), + observedAt: '2026-09-21T13:59:59.000Z', + cpuPressure: (unit * 1.3) % 1, + memoryPressure: (unit * 1.9) % 1, + ioPressure: (unit * 2.7) % 1, + allocatableCpuCores: 16, + maxCpuPerTask: 8, + }, + policy: { + profileMaxAgeMs: 300_000, + telemetryMaxAgeMs: 30_000, + maxCpuPerTask: 8, + cpuPressureDefer: 0.9, + memoryPressureDefer: 0.9, + ioPressureDefer: 0.9, + safetyHeadroomFraction: 0.1, + }, + requestedConcurrency: 1 + (index % 4), + now, + }; +} + +function fixedCpuBaseline(input: ResourceAdmissionInput): number { + const share = input.host.allocatableCpuCores * (1 - input.policy.safetyHeadroomFraction) / input.requestedConcurrency; + return Math.max(0, Math.min(4, input.profile.maxCpuCores, input.host.maxCpuPerTask, input.policy.maxCpuPerTask, share)); +} + +function percentile(values: number[], fraction: number): number { + const sorted = [...values].sort((a, b) => a - b); + return sorted[Math.min(sorted.length - 1, Math.floor(sorted.length * fraction))]; +} + +function mean(values: number[]): number { + return values.reduce((sum, value) => sum + value, 0) / values.length; +} + +function variance(values: number[]): number { + const center = mean(values); + return values.reduce((sum, value) => sum + (value - center) ** 2, 0) / values.length; +} + +it('reports baseline and candidate resource admission overhead with frozen workload', () => { + const seeds = [11, 29, 47, 71, 101]; + const candidateBatchMicros: number[] = []; + const baselineBatchMicros: number[] = []; + let candidateCeilingViolations = 0; + let baselineCeilingViolations = 0; + let admits = 0; + let defers = 0; + let rejects = 0; + let samples = 0; + + for (const seed of seeds) { + let state = seed >>> 0; + for (let batch = 0; batch < 20; batch += 1) { + const inputs: ResourceAdmissionInput[] = []; + for (let index = 0; index < 100; index += 1) { + state = (Math.imul(state, 1664525) + 1013904223) >>> 0; + inputs.push(inputFor(seed, batch, index, state / 0xffffffff)); + } + + let start = performance.now(); + for (const input of inputs) { + const cpu = fixedCpuBaseline(input); + if (cpu > input.host.maxCpuPerTask || cpu > input.policy.maxCpuPerTask) baselineCeilingViolations += 1; + } + baselineBatchMicros.push(((performance.now() - start) * 1000) / inputs.length); + + start = performance.now(); + for (const input of inputs) { + const decision = decideResourceAdmission(input); + samples += 1; + if (decision.verdict === 'admit') admits += 1; + if (decision.verdict === 'defer') defers += 1; + if (decision.verdict === 'reject') rejects += 1; + if (decision.recommendedCpuCores > input.host.maxCpuPerTask || decision.recommendedCpuCores > input.policy.maxCpuPerTask) { + candidateCeilingViolations += 1; + } + } + candidateBatchMicros.push(((performance.now() - start) * 1000) / inputs.length); + } + } + + const baselineMean = mean(baselineBatchMicros); + const candidateMean = mean(candidateBatchMicros); + const report = { + schema: 'ruflo-resource-admission-benchmark/v2', + environment: { node: process.version, platform: process.platform, arch: process.arch }, + workload: '10000 deterministic synthetic resource decisions in 100 batches', + seeds, + sampleSize: samples, + baseline: { + name: 'fixed_cpu_clipped_to_host_ceiling', + meanMicrosPerDecision: baselineMean, + p95MicrosPerDecision: percentile(baselineBatchMicros, 0.95), + varianceMicros2: variance(baselineBatchMicros), + ceilingViolations: baselineCeilingViolations, + }, + candidate: { + name: 'task_conditioned_resource_admission', + meanMicrosPerDecision: candidateMean, + p95MicrosPerDecision: percentile(candidateBatchMicros, 0.95), + varianceMicros2: variance(candidateBatchMicros), + ceilingViolations: candidateCeilingViolations, + admits, + defers, + rejects, + }, + absoluteOverheadMicros: candidateMean - baselineMean, + relativeOverheadRatio: candidateMean / Math.max(baselineMean, Number.EPSILON), + ablations: ['remote_wait_cpu_reduction', 'cpu_pressure_defer', 'memory_pressure_defer', 'io_pressure_defer'], + failures: 0, + regressions: { taskQuality: 'not_measured_in_structural_benchmark', starvation: 'not_measured_in_structural_benchmark' }, + costUsd: 0, + energy: 'not_measured', + reproduction: 'npx vitest run v3/@claude-flow/performance/__tests__/resource-admission-report.test.ts', + }; + + console.log(JSON.stringify(report)); + expect(samples).toBe(10_000); + expect(candidateCeilingViolations).toBe(0); + expect(report.candidate.p95MicrosPerDecision).toBeLessThan(100); +}); From 46c88e0b888d519a4cda5e09f310a2ab35a55fae Mon Sep 17 00:00:00 2001 From: rUv Date: Mon, 21 Sep 2026 14:59:10 +0000 Subject: [PATCH 8/9] ci: run full resource admission benchmark report --- .github/workflows/resource-admission.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/resource-admission.yml b/.github/workflows/resource-admission.yml index 30a70bc8d5..6f7a704388 100644 --- a/.github/workflows/resource-admission.yml +++ b/.github/workflows/resource-admission.yml @@ -36,5 +36,5 @@ jobs: run: npm ci --ignore-scripts --no-audit --fund=false - name: Type check and build performance package run: npx tsc -p v3/@claude-flow/performance/tsconfig.json - - name: Run resource admission tests and structural benchmark - run: npx vitest run v3/@claude-flow/performance/__tests__/resource-admission.test.ts + - name: Run resource admission tests and structural benchmarks + run: npx vitest run v3/@claude-flow/performance/__tests__/resource-admission.test.ts v3/@claude-flow/performance/__tests__/resource-admission-report.test.ts From 75c43591a64b22eef29762b29490673bacf198d5 Mon Sep 17 00:00:00 2001 From: rUv Date: Mon, 21 Sep 2026 15:01:31 +0000 Subject: [PATCH 9/9] ci: use supported Node engines for resource admission --- .github/workflows/resource-admission.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/resource-admission.yml b/.github/workflows/resource-admission.yml index 6f7a704388..decc590a7c 100644 --- a/.github/workflows/resource-admission.yml +++ b/.github/workflows/resource-admission.yml @@ -23,7 +23,7 @@ jobs: strategy: fail-fast: false matrix: - node: [20.20.2, 22.22.3] + node: [22.22.3, 24.19.0] steps: - name: Check out exact source uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262