feat(gsd-uok): flip default to UOK with emergency legacy fallback
This commit is contained in:
parent
5a6a13eb39
commit
f9f712098d
9 changed files with 77 additions and 8 deletions
|
|
@ -191,8 +191,10 @@ Setting `prefer_skills: []` does **not** disable skill discovery — it just mea
|
|||
- `hooks`: boolean — enable routing hooks. Default: `true`.
|
||||
- `capability_routing`: boolean — enable capability-profile scoring for model selection within a tier. Requires `enabled: true`. Default: `false`.
|
||||
|
||||
- `uok`: Unified Orchestration Kernel controls (all flags default to `false` during migration). Keys:
|
||||
- `enabled`: boolean — enable kernel wrappers and contract observers.
|
||||
- `uok`: Unified Orchestration Kernel controls. Keys:
|
||||
- `enabled`: boolean — enable kernel wrappers and contract observers. Default: `true`.
|
||||
- `legacy_fallback.enabled`: boolean — emergency release fallback that forces legacy orchestration behavior even when `uok.enabled` is `true`. Default: `false`.
|
||||
- Runtime override: set `GSD_UOK_FORCE_LEGACY=1` (or `GSD_UOK_LEGACY_FALLBACK=1`) to force legacy behavior for the current process.
|
||||
- `gates.enabled`: boolean — route checks through the unified gate runner and persist `gate_runs`.
|
||||
- `model_policy.enabled`: boolean — enforce policy filtering before model capability scoring.
|
||||
- `execution_graph.enabled`: boolean — enable DAG scheduler facade/adapters for execution.
|
||||
|
|
|
|||
|
|
@ -213,6 +213,9 @@ export type UokTurnActionMode = "commit" | "snapshot" | "status-only";
|
|||
|
||||
export interface UokPreferences {
|
||||
enabled?: boolean;
|
||||
legacy_fallback?: {
|
||||
enabled?: boolean;
|
||||
};
|
||||
gates?: {
|
||||
enabled?: boolean;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -178,7 +178,7 @@ export function validatePreferences(preferences: GSDPreferences): {
|
|||
}
|
||||
|
||||
const parseEnabledBlock = (
|
||||
key: "gates" | "model_policy" | "execution_graph" | "audit_unified" | "plan_v2",
|
||||
key: "legacy_fallback" | "gates" | "model_policy" | "execution_graph" | "audit_unified" | "plan_v2",
|
||||
): void => {
|
||||
const value = raw[key];
|
||||
if (value === undefined) return;
|
||||
|
|
@ -201,6 +201,7 @@ export function validatePreferences(preferences: GSDPreferences): {
|
|||
}
|
||||
};
|
||||
|
||||
parseEnabledBlock("legacy_fallback");
|
||||
parseEnabledBlock("gates");
|
||||
parseEnabledBlock("model_policy");
|
||||
parseEnabledBlock("execution_graph");
|
||||
|
|
@ -243,6 +244,7 @@ export function validatePreferences(preferences: GSDPreferences): {
|
|||
|
||||
const knownUokKeys = new Set([
|
||||
"enabled",
|
||||
"legacy_fallback",
|
||||
"gates",
|
||||
"model_policy",
|
||||
"execution_graph",
|
||||
|
|
|
|||
|
|
@ -383,6 +383,9 @@ function mergePreferences(base: GSDPreferences, override: GSDPreferences): GSDPr
|
|||
uok: (base.uok || override.uok)
|
||||
? {
|
||||
enabled: override.uok?.enabled ?? base.uok?.enabled,
|
||||
legacy_fallback: (base.uok?.legacy_fallback || override.uok?.legacy_fallback)
|
||||
? { ...(base.uok?.legacy_fallback ?? {}), ...(override.uok?.legacy_fallback ?? {}) }
|
||||
: undefined,
|
||||
gates: (base.uok?.gates || override.uok?.gates)
|
||||
? { ...(base.uok?.gates ?? {}), ...(override.uok?.gates ?? {}) }
|
||||
: undefined,
|
||||
|
|
|
|||
|
|
@ -40,7 +40,9 @@ dynamic_routing:
|
|||
cross_provider:
|
||||
hooks:
|
||||
uok:
|
||||
enabled: false
|
||||
enabled: true
|
||||
legacy_fallback:
|
||||
enabled: false
|
||||
gates:
|
||||
enabled: false
|
||||
model_policy:
|
||||
|
|
|
|||
39
src/resources/extensions/gsd/tests/uok-flags.test.ts
Normal file
39
src/resources/extensions/gsd/tests/uok-flags.test.ts
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
import test from "node:test";
|
||||
import assert from "node:assert/strict";
|
||||
|
||||
import { resolveUokFlags } from "../uok/flags.ts";
|
||||
|
||||
test("uok flags default to enabled when preference is unset", () => {
|
||||
const flags = resolveUokFlags(undefined);
|
||||
assert.equal(flags.enabled, true);
|
||||
assert.equal(flags.legacyFallback, false);
|
||||
});
|
||||
|
||||
test("uok legacy fallback preference forces legacy path", () => {
|
||||
const flags = resolveUokFlags({
|
||||
uok: {
|
||||
enabled: true,
|
||||
legacy_fallback: { enabled: true },
|
||||
},
|
||||
});
|
||||
assert.equal(flags.enabled, false);
|
||||
assert.equal(flags.legacyFallback, true);
|
||||
});
|
||||
|
||||
test("uok legacy fallback env var forces legacy path", () => {
|
||||
const previous = process.env.GSD_UOK_FORCE_LEGACY;
|
||||
process.env.GSD_UOK_FORCE_LEGACY = "1";
|
||||
try {
|
||||
const flags = resolveUokFlags({
|
||||
uok: {
|
||||
enabled: true,
|
||||
},
|
||||
});
|
||||
assert.equal(flags.enabled, false);
|
||||
assert.equal(flags.legacyFallback, true);
|
||||
} finally {
|
||||
if (previous === undefined) delete process.env.GSD_UOK_FORCE_LEGACY;
|
||||
else process.env.GSD_UOK_FORCE_LEGACY = previous;
|
||||
}
|
||||
});
|
||||
|
||||
|
|
@ -7,6 +7,7 @@ test("uok preferences validate nested flags and turn_action", () => {
|
|||
const input = {
|
||||
uok: {
|
||||
enabled: true,
|
||||
legacy_fallback: { enabled: false },
|
||||
gates: { enabled: true },
|
||||
model_policy: { enabled: true },
|
||||
execution_graph: { enabled: false },
|
||||
|
|
@ -23,6 +24,7 @@ test("uok preferences validate nested flags and turn_action", () => {
|
|||
const result = validatePreferences(input as never);
|
||||
assert.equal(result.errors.length, 0);
|
||||
assert.equal(result.preferences.uok?.enabled, true);
|
||||
assert.equal(result.preferences.uok?.legacy_fallback?.enabled, false);
|
||||
assert.equal(result.preferences.uok?.gitops?.turn_action, "status-only");
|
||||
assert.equal(result.preferences.uok?.plan_v2?.enabled, true);
|
||||
});
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { loadEffectiveGSDPreferences } from "../preferences.js";
|
|||
|
||||
export interface UokFlags {
|
||||
enabled: boolean;
|
||||
legacyFallback: boolean;
|
||||
gates: boolean;
|
||||
modelPolicy: boolean;
|
||||
executionGraph: boolean;
|
||||
|
|
@ -13,10 +14,20 @@ export interface UokFlags {
|
|||
planV2: boolean;
|
||||
}
|
||||
|
||||
function envForcesLegacyFallback(): boolean {
|
||||
const raw = process.env.GSD_UOK_FORCE_LEGACY ?? process.env.GSD_UOK_LEGACY_FALLBACK;
|
||||
if (!raw) return false;
|
||||
const normalized = raw.trim().toLowerCase();
|
||||
return normalized === "1" || normalized === "true" || normalized === "yes" || normalized === "on";
|
||||
}
|
||||
|
||||
export function resolveUokFlags(prefs: GSDPreferences | undefined): UokFlags {
|
||||
const uok = prefs?.uok;
|
||||
const legacyFallback = uok?.legacy_fallback?.enabled === true || envForcesLegacyFallback();
|
||||
const enabledByPreference = uok?.enabled ?? true;
|
||||
return {
|
||||
enabled: uok?.enabled === true,
|
||||
enabled: enabledByPreference && !legacyFallback,
|
||||
legacyFallback,
|
||||
gates: uok?.gates?.enabled === true,
|
||||
modelPolicy: uok?.model_policy?.enabled === true,
|
||||
executionGraph: uok?.execution_graph?.enabled === true,
|
||||
|
|
|
|||
|
|
@ -36,6 +36,11 @@ function writeParityEvent(basePath: string, event: Record<string, unknown>): voi
|
|||
}
|
||||
}
|
||||
|
||||
function resolveKernelPathLabel(flags: ReturnType<typeof resolveUokFlags>): "uok-wrapper" | "legacy-wrapper" | "legacy-fallback" {
|
||||
if (flags.legacyFallback) return "legacy-fallback";
|
||||
return flags.enabled ? "uok-wrapper" : "legacy-wrapper";
|
||||
}
|
||||
|
||||
export async function runAutoLoopWithUok(args: RunAutoLoopWithUokArgs): Promise<void> {
|
||||
const { ctx, pi, s, deps, runLegacyLoop } = args;
|
||||
const prefs = deps.loadEffectiveGSDPreferences()?.preferences;
|
||||
|
|
@ -44,7 +49,7 @@ export async function runAutoLoopWithUok(args: RunAutoLoopWithUokArgs): Promise<
|
|||
|
||||
writeParityEvent(s.basePath, {
|
||||
ts: new Date().toISOString(),
|
||||
path: flags.enabled ? "uok-wrapper" : "legacy-wrapper",
|
||||
path: resolveKernelPathLabel(flags),
|
||||
flags,
|
||||
phase: "enter",
|
||||
});
|
||||
|
|
@ -81,7 +86,7 @@ export async function runAutoLoopWithUok(args: RunAutoLoopWithUokArgs): Promise<
|
|||
await runLegacyLoop(ctx, pi, s, decoratedDeps);
|
||||
writeParityEvent(s.basePath, {
|
||||
ts: new Date().toISOString(),
|
||||
path: flags.enabled ? "uok-wrapper" : "legacy-wrapper",
|
||||
path: resolveKernelPathLabel(flags),
|
||||
flags,
|
||||
phase: "exit",
|
||||
status: "ok",
|
||||
|
|
@ -89,7 +94,7 @@ export async function runAutoLoopWithUok(args: RunAutoLoopWithUokArgs): Promise<
|
|||
} catch (err) {
|
||||
writeParityEvent(s.basePath, {
|
||||
ts: new Date().toISOString(),
|
||||
path: flags.enabled ? "uok-wrapper" : "legacy-wrapper",
|
||||
path: resolveKernelPathLabel(flags),
|
||||
flags,
|
||||
phase: "exit",
|
||||
status: "error",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue