From 8cf33982abbc95272779ba6f028f41a1f7ffacec Mon Sep 17 00:00:00 2001 From: Trae AI Date: Tue, 26 May 2026 01:24:26 +0800 Subject: [PATCH] Add evolver promotion controller --- gitnexus/src/core/evolver/index.ts | 1 + .../src/core/evolver/promotion-controller.ts | 59 +++++++ .../unit/evolver/promotion-controller.test.ts | 145 ++++++++++++++++++ 3 files changed, 205 insertions(+) create mode 100644 gitnexus/src/core/evolver/promotion-controller.ts create mode 100644 gitnexus/test/unit/evolver/promotion-controller.test.ts diff --git a/gitnexus/src/core/evolver/index.ts b/gitnexus/src/core/evolver/index.ts index 4343f58d7..551eae70e 100644 --- a/gitnexus/src/core/evolver/index.ts +++ b/gitnexus/src/core/evolver/index.ts @@ -4,3 +4,4 @@ export * from './resource-manager.js'; export * from './variant-generator.js'; export * from './sandbox-runner.js'; export * from './safety-gate.js'; +export * from './promotion-controller.js'; diff --git a/gitnexus/src/core/evolver/promotion-controller.ts b/gitnexus/src/core/evolver/promotion-controller.ts new file mode 100644 index 000000000..eae5e8308 --- /dev/null +++ b/gitnexus/src/core/evolver/promotion-controller.ts @@ -0,0 +1,59 @@ +import type { GateDecision, PromotionRecord, VariantSpec } from './types.js'; + +const appliedAt = '2026-05-25T00:00:00.000Z'; + +export function applyPromotion( + target: Record, + variant: VariantSpec, + gateDecision: GateDecision, +): PromotionRecord | null { + if (gateDecision.decision !== 'allow') { + return null; + } + + const previousState = structuredClone(target); + + for (const operation of variant.patch.operations) { + if (operation.op === 'setParameter') { + const segments = operation.path.split('.'); + setNestedValue(target, segments, operation.nextValue); + } + } + + return { + id: `promo-${variant.id}-${gateDecision.id}`, + variantId: variant.id, + gateDecisionId: gateDecision.id, + appliedAt, + rollbackPlan: variant.rollbackPlan, + previousState, + nextState: structuredClone(target), + }; +} + +export function rollbackPromotion( + target: Record, + record: PromotionRecord, +): void { + const keys = Object.keys(target); + for (const key of keys) { + delete target[key]; + } + Object.assign(target, record.previousState); +} + +function setNestedValue( + obj: Record, + path: string[], + value: unknown, +): void { + if (path.length === 1) { + obj[path[0]] = value; + return; + } + const [head, ...rest] = path; + if (obj[head] === undefined || typeof obj[head] !== 'object' || obj[head] === null) { + obj[head] = {}; + } + setNestedValue(obj[head] as Record, rest, value); +} diff --git a/gitnexus/test/unit/evolver/promotion-controller.test.ts b/gitnexus/test/unit/evolver/promotion-controller.test.ts new file mode 100644 index 000000000..2a76d0f14 --- /dev/null +++ b/gitnexus/test/unit/evolver/promotion-controller.test.ts @@ -0,0 +1,145 @@ +import { describe, expect, it } from 'vitest'; +import { applyPromotion, rollbackPromotion } from '../../../src/core/evolver/index.js'; +import type { GateDecision, VariantSpec } from '../../../src/core/evolver/index.js'; + +const capturedAt = '2026-05-25T00:00:00.000Z'; + +function parameterVariant(): VariantSpec { + return { + id: 'variant-parameter-1', + planId: 'plan-1', + mutationType: 'parameter', + description: 'Adjust topK', + patch: { + operations: [ + { + op: 'setParameter', + path: 'retrieval.topK', + previousValue: 5, + nextValue: 8, + }, + ], + }, + expectedGain: [ + { + metricId: 'task_success_rate', + expectedDelta: 0.05, + direction: 'increase', + }, + ], + riskLevel: 'low', + rollbackPlan: { + strategy: 'restore-previous-parameters', + operations: [ + { + op: 'setParameter', + path: 'retrieval.topK', + previousValue: 8, + nextValue: 5, + }, + ], + }, + provenance: [ + { + source: 'synthetic-benchmark', + reference: 'run-1', + capturedAt, + }, + ], + }; +} + +function allowDecision(): GateDecision { + return { + id: 'gate-variant-parameter-1-report-1', + variantId: 'variant-parameter-1', + reportId: 'report-1', + decision: 'allow', + reasons: ['parameter variant passed promotion gate'], + decidedAt: capturedAt, + }; +} + +function needsReviewDecision(): GateDecision { + return { + id: 'gate-variant-algorithm-1-report-2', + variantId: 'variant-algorithm-1', + reportId: 'report-2', + decision: 'needs-review', + reasons: ['algorithm variants require review before promotion'], + decidedAt: capturedAt, + }; +} + +function rejectDecision(): GateDecision { + return { + id: 'gate-variant-parameter-1-report-3', + variantId: 'variant-parameter-1', + reportId: 'report-3', + decision: 'reject', + reasons: ['evaluation report contains safety findings'], + decidedAt: capturedAt, + }; +} + +describe('applyPromotion', () => { + it('applies parameter patch to target when gate decision is allow', () => { + const target = { retrieval: { topK: 5 }, threshold: 0.7 }; + const variant = parameterVariant(); + const decision = allowDecision(); + + const record = applyPromotion(target, variant, decision); + + expect(target).toEqual({ retrieval: { topK: 8 }, threshold: 0.7 }); + expect(record).toEqual({ + id: 'promo-variant-parameter-1-gate-variant-parameter-1-report-1', + variantId: 'variant-parameter-1', + gateDecisionId: 'gate-variant-parameter-1-report-1', + appliedAt: capturedAt, + rollbackPlan: variant.rollbackPlan, + previousState: { retrieval: { topK: 5 }, threshold: 0.7 }, + nextState: { retrieval: { topK: 8 }, threshold: 0.7 }, + }); + }); + + it('does not mutate target when gate decision is needs-review', () => { + const target = { topK: 5 }; + const variant = parameterVariant(); + const decision = needsReviewDecision(); + + const record = applyPromotion(target, variant, decision); + + expect(target).toEqual({ topK: 5 }); + expect(record).toBeNull(); + }); + + it('does not mutate target when gate decision is reject', () => { + const target = { topK: 5 }; + const variant = parameterVariant(); + const decision = rejectDecision(); + + const record = applyPromotion(target, variant, decision); + + expect(target).toEqual({ topK: 5 }); + expect(record).toBeNull(); + }); +}); + +describe('rollbackPromotion', () => { + it('restores target to previous state', () => { + const target = { retrieval: { topK: 8 }, threshold: 0.7 }; + const record = { + id: 'promo-variant-parameter-1-gate-variant-parameter-1-report-1', + variantId: 'variant-parameter-1', + gateDecisionId: 'gate-variant-parameter-1-report-1', + appliedAt: capturedAt, + rollbackPlan: parameterVariant().rollbackPlan, + previousState: { retrieval: { topK: 5 }, threshold: 0.7 }, + nextState: { retrieval: { topK: 8 }, threshold: 0.7 }, + }; + + rollbackPromotion(target, record); + + expect(target).toEqual({ retrieval: { topK: 5 }, threshold: 0.7 }); + }); +});