Merge branch 'pr-3389' into integ/train-3.45.1

This commit is contained in:
ruv committed 2026-09-26 11:32:48 -04:00
commit 012daa5fed
6 files changed
+648 -15

No files matched your search

+40
View File
@@ -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: [22.22.3, 24.19.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 benchmarks
run: npx vitest run v3/@claude-flow/performance/__tests__/resource-admission.test.ts 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);
});
@@ -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> = {}): 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);
});
});
+13 -15
View File
@@ -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';
@@ -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')}`;
}
@@ -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.