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:
john
2026-07-25 07:28:37 +08:00
parent 08a48e4849
commit 6df82818c5
33 changed files with 1569 additions and 108 deletions
+69 -6
View File
@@ -12,6 +12,7 @@ import { listPhase5ExecutorAdapters } from './executor-gateway.mjs';
const CONFIG_TABLE = 'h5_orchestrator_admin_config';
const CONFIG_SCOPE = 'global';
const EXECUTION_HANDOFF_IMPLEMENTED = true;
const CONFIG_SCHEMA_PROMISES = new WeakMap();
function normalizeBoolean(value, fallback = false) {
if (value == null || value === '') return fallback;
@@ -39,15 +40,45 @@ function normalizeServiceUrl(value) {
try {
const url = new URL(raw);
if (!['http:', 'https:'].includes(url.protocol)) return '';
if ((url.pathname && url.pathname !== '/') || url.search) return '';
url.username = '';
url.password = '';
url.hash = '';
return url.toString().replace(/\/$/, '').slice(0, 2048);
return url.origin.slice(0, 2048);
} catch {
return '';
}
}
function isLoopbackServiceUrl(value) {
try {
const hostname = new URL(value).hostname.toLowerCase();
return hostname === 'localhost'
|| hostname === '::1'
|| hostname === '[::1]'
|| /^127(?:\.\d{1,3}){3}$/.test(hostname);
} catch {
return false;
}
}
function allowedServiceOrigins(env = process.env) {
const configured = [
env.MEMIND_ORCHESTRATOR_URL,
...String(env.MEMIND_ORCHESTRATOR_ALLOWED_ORIGINS ?? '').split(','),
]
.map(normalizeServiceUrl)
.filter(Boolean);
return new Set(configured);
}
function isServiceUrlAllowed(value, env = process.env) {
const normalized = normalizeServiceUrl(value);
if (!normalized) return false;
return isLoopbackServiceUrl(normalized)
|| allowedServiceOrigins(env).has(normalized);
}
function parseJsonLike(value, fallback) {
if (value == null || value === '') return fallback;
if (typeof value === 'object') return value;
@@ -112,6 +143,10 @@ export function normalizeOrchestratorConfig(input = {}, fallback = defaultOrches
function runtimeState(config, env = process.env) {
const killSwitch = normalizeBoolean(env.MEMIND_ORCHESTRATOR_KILL_SWITCH, false);
const environmentShadowObservationGate = normalizeBoolean(
env.MEMIND_ORCHESTRATOR_SHADOW_OBSERVATION_ENABLED,
false,
);
const environmentExecutionGate = normalizeBoolean(
env.MEMIND_ORCHESTRATOR_EXECUTION_HANDOFF_ENABLED,
false,
@@ -121,6 +156,10 @@ function runtimeState(config, env = process.env) {
if (killSwitch) reason = 'kill_switch';
else if (config.mode === ORCHESTRATOR_MODE.OFF) reason = 'mode_off';
else if (!configured) reason = 'service_url_missing';
else if (
config.primaryEngine === WORKFLOW_ENGINE.LANGGRAPH
&& !isServiceUrlAllowed(config.serviceUrl, env)
) reason = 'service_url_not_allowed';
const executionModeSelected = [ORCHESTRATOR_MODE.CANARY, ORCHESTRATOR_MODE.ACTIVE]
.includes(config.mode);
const executionHandoffRequested = config.executionEnabled
@@ -131,6 +170,11 @@ function runtimeState(config, env = process.env) {
&& environmentExecutionGate
&& executionHandoffRequested
&& config.primaryEngine === WORKFLOW_ENGINE.LANGGRAPH;
const shadowObservationRequested = config.mode === ORCHESTRATOR_MODE.SHADOW
&& config.primaryEngine === WORKFLOW_ENGINE.LANGGRAPH;
const shadowObservationEnabled = !reason
&& environmentShadowObservationGate
&& shadowObservationRequested;
return {
killSwitch,
configured,
@@ -146,9 +190,19 @@ function runtimeState(config, env = process.env) {
plansLangGraph: !reason
&& executionModeSelected
&& config.primaryEngine === WORKFLOW_ENGINE.LANGGRAPH,
shadowsLangGraph: !reason
&& config.mode === ORCHESTRATOR_MODE.SHADOW
&& config.primaryEngine === WORKFLOW_ENGINE.LANGGRAPH,
shadowsLangGraph: shadowObservationEnabled,
shadowObservation: {
requested: shadowObservationRequested,
enabled: shadowObservationEnabled,
reason: shadowObservationEnabled
? null
: !shadowObservationRequested
? 'shadow_observation_not_requested'
: reason
? reason
: 'environment_shadow_observation_gate_disabled',
environmentGate: environmentShadowObservationGate,
},
executionHandoff: {
implemented: EXECUTION_HANDOFF_IMPLEMENTED,
requested: executionHandoffRequested,
@@ -261,7 +315,9 @@ async function probeServiceHealth(config, {
}
async function ensureConfigTable(pool) {
await pool.query(`
const existing = CONFIG_SCHEMA_PROMISES.get(pool);
if (existing) return existing;
const initializing = pool.query(`
CREATE TABLE IF NOT EXISTS ${CONFIG_TABLE} (
config_scope VARCHAR(32) PRIMARY KEY,
config_json JSON NOT NULL,
@@ -269,7 +325,12 @@ async function ensureConfigTable(pool) {
updated_by CHAR(36) NULL,
updated_at BIGINT NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci
`);
`).catch((error) => {
CONFIG_SCHEMA_PROMISES.delete(pool);
throw error;
});
CONFIG_SCHEMA_PROMISES.set(pool, initializing);
return initializing;
}
async function loadStoredState(pool) {
@@ -450,6 +511,8 @@ export const orchestratorAdminConfigInternals = {
CONFIG_TABLE,
engineCatalog,
EXECUTION_HANDOFF_IMPLEMENTED,
isServiceUrlAllowed,
normalizeServiceUrl,
probeServiceHealth,
runtimeState,
};