feat(orchestrator): harden zero-impact shadow rollout
Gate and bound Portal shadow observations while preserving Native execution. Add fail-closed service boundaries, terminal retention controls, Canary readiness telemetry, ops visibility, and isolated regression coverage.
This commit is contained in:
@@ -8,9 +8,16 @@ import {
|
||||
|
||||
function createPool() {
|
||||
let row = null;
|
||||
let createTableCalls = 0;
|
||||
return {
|
||||
get createTableCalls() {
|
||||
return createTableCalls;
|
||||
},
|
||||
async query(sql, params = []) {
|
||||
if (sql.includes('CREATE TABLE')) return [[], []];
|
||||
if (sql.includes('CREATE TABLE')) {
|
||||
createTableCalls += 1;
|
||||
return [[], []];
|
||||
}
|
||||
if (sql.includes('SELECT config_json')) return [[...(row ? [row] : [])], []];
|
||||
if (sql.includes('INSERT INTO')) {
|
||||
row = {
|
||||
@@ -33,6 +40,12 @@ test('orchestrator config defaults to a disabled native-safe runtime', async ()
|
||||
assert.equal(state.config.primaryEngine, 'langgraph');
|
||||
assert.equal(state.runtime.effective, false);
|
||||
assert.equal(state.runtime.reason, 'mode_off');
|
||||
assert.deepEqual(state.runtime.shadowObservation, {
|
||||
requested: false,
|
||||
enabled: false,
|
||||
reason: 'shadow_observation_not_requested',
|
||||
environmentGate: false,
|
||||
});
|
||||
assert.equal(state.engines.find((engine) => engine.id === 'native').configured, true);
|
||||
assert.equal(state.engines.find((engine) => engine.id === 'langgraph').configured, false);
|
||||
assert.deepEqual(
|
||||
@@ -59,6 +72,48 @@ test('orchestrator config normalizes unsafe values and keeps a workflow allowlis
|
||||
assert.equal(normalized.requestTimeoutMs, 500);
|
||||
assert.equal(normalized.rolloutPercent, 100);
|
||||
assert.deepEqual(normalized.workflowAllowlist, ['code-run-v1']);
|
||||
assert.equal(
|
||||
normalizeOrchestratorConfig({
|
||||
serviceUrl: 'https://orchestrator.example/internal?token=secret',
|
||||
}).serviceUrl,
|
||||
'',
|
||||
);
|
||||
});
|
||||
|
||||
test('orchestrator config initializes its schema once per shared pool', async () => {
|
||||
const pool = createPool();
|
||||
const first = createOrchestratorAdminConfigService(pool, { env: {} });
|
||||
const second = createOrchestratorAdminConfigService(pool, { env: {} });
|
||||
await first.ensureSchema();
|
||||
await first.getRuntimeState();
|
||||
await second.getAdminConfig();
|
||||
assert.equal(pool.createTableCalls, 1);
|
||||
});
|
||||
|
||||
test('orchestrator blocks non-loopback service origins unless explicitly allowed', async () => {
|
||||
const pool = createPool();
|
||||
const blockedService = createOrchestratorAdminConfigService(pool, { env: {} });
|
||||
const blocked = await blockedService.updateAdminConfig({
|
||||
mode: 'shadow',
|
||||
serviceUrl: 'https://orchestrator.example',
|
||||
});
|
||||
assert.equal(blocked.runtime.effective, false);
|
||||
assert.equal(blocked.runtime.reason, 'service_url_not_allowed');
|
||||
assert.deepEqual(blocked.runtime.shadowObservation, {
|
||||
requested: true,
|
||||
enabled: false,
|
||||
reason: 'service_url_not_allowed',
|
||||
environmentGate: false,
|
||||
});
|
||||
|
||||
const allowedService = createOrchestratorAdminConfigService(pool, {
|
||||
env: {
|
||||
MEMIND_ORCHESTRATOR_ALLOWED_ORIGINS: 'https://orchestrator.example',
|
||||
},
|
||||
});
|
||||
const allowed = await allowedService.getRuntimeState();
|
||||
assert.equal(allowed.runtime.effective, true);
|
||||
assert.equal(allowed.runtime.reason, null);
|
||||
});
|
||||
|
||||
test('orchestrator config persists versioned admin updates and selects canary users', async () => {
|
||||
@@ -194,6 +249,32 @@ test('Shadow mode evaluates the Canary rollout while Native remains effective',
|
||||
assert.equal(native.candidateEngine, 'native');
|
||||
assert.equal(native.candidateReason, 'canary_not_selected');
|
||||
assert.equal(native.dryRun, true);
|
||||
const runtime = await service.getRuntimeState();
|
||||
assert.equal(runtime.runtime.shadowsLangGraph, false);
|
||||
assert.deepEqual(runtime.runtime.shadowObservation, {
|
||||
requested: true,
|
||||
enabled: false,
|
||||
reason: 'environment_shadow_observation_gate_disabled',
|
||||
environmentGate: false,
|
||||
});
|
||||
});
|
||||
|
||||
test('Shadow runtime reports the independent Portal observation wiring gate', async () => {
|
||||
const service = createOrchestratorAdminConfigService(createPool(), {
|
||||
env: { MEMIND_ORCHESTRATOR_SHADOW_OBSERVATION_ENABLED: '1' },
|
||||
});
|
||||
await service.updateAdminConfig({
|
||||
mode: 'shadow',
|
||||
serviceUrl: 'http://127.0.0.1:8093',
|
||||
});
|
||||
const runtime = await service.getRuntimeState();
|
||||
assert.equal(runtime.runtime.shadowsLangGraph, true);
|
||||
assert.deepEqual(runtime.runtime.shadowObservation, {
|
||||
requested: true,
|
||||
enabled: true,
|
||||
reason: null,
|
||||
environmentGate: true,
|
||||
});
|
||||
});
|
||||
|
||||
test('orchestrator emergency kill switch always forces native selection', async () => {
|
||||
|
||||
Reference in New Issue
Block a user