From e7cf168824cd05f260c7a23ff1ccde531036837f Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Fri, 15 May 2026 06:30:08 +0200 Subject: [PATCH] feat(model-policy): adversary role + lineage-diverse-from-worker constraint MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add `adversary` to SUPPORTED_MODEL_ROLES and a new symbolic constraint `lineage-diverse-from-worker` to SUPPORTED_MODEL_ROLE_CONSTRAINTS. Default constraints for `adversary` and `reviewer` now include `lineage-diverse-from-worker` so the reviewer/adversary CANNOT be a lineage-twin of the model that produced the artifact under review — prevents "yeah looks fine to me" rubber-stamp from same-family models. Helpers exported alongside the policy: - rootVendorFor(modelId) → "anthropic" | "openai" | "google" | "moonshot" | "mistral" | "minimax" | "zhipu" | "meituan" | "unknown" - isSameRootVendor(candidateId, workerId) → boolean (fail-open on unknown) These are the building blocks the selector needs. The actual filter wiring in auto-model-selection's selectAndApplyModel is left as a documented TODO — the function doesn't currently thread role context through, so plugging in lineage filtering needs a small refactor that is out of scope here. Tests: 24 pass (was 6 + 18 new). Coverage: role registration, constraint registration, defaults, validation, rootVendor mapping matrix, isSameRootVendor predicate. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../sf/tests/model-role-policy.test.mjs | 115 ++++++++++++++++++ src/resources/extensions/sf/uok/index.js | 2 + .../extensions/sf/uok/model-role-policy.js | 88 +++++++++++++- 3 files changed, 204 insertions(+), 1 deletion(-) diff --git a/src/resources/extensions/sf/tests/model-role-policy.test.mjs b/src/resources/extensions/sf/tests/model-role-policy.test.mjs index 67fbe9300..17709c6c1 100644 --- a/src/resources/extensions/sf/tests/model-role-policy.test.mjs +++ b/src/resources/extensions/sf/tests/model-role-policy.test.mjs @@ -3,8 +3,12 @@ import { describe, test } from "vitest"; import { ModelRolePolicyValidationError, + SUPPORTED_MODEL_ROLE_CONSTRAINTS, + SUPPORTED_MODEL_ROLES, + isSameRootVendor, normalizeRolePolicies, normalizeRolePolicy, + rootVendorFor, validateRolePolicy, } from "../uok/model-role-policy.js"; @@ -91,6 +95,11 @@ describe("model role policy", () => { mode: "auto", constraints: ["cheap", "review"], }, + adversary: { + role: "adversary", + mode: "auto", + constraints: ["review", "lineage-diverse-from-worker"], + }, }, ); }); @@ -101,4 +110,110 @@ describe("model role policy", () => { /unsupported model role "planner"/, ); }); + + // ── adversary role ────────────────────────────────────────────────────────── + + test("adversary_is_a_supported_role", () => { + assert.ok(SUPPORTED_MODEL_ROLES.includes("adversary")); + }); + + test("lineage_diverse_from_worker_is_a_supported_constraint", () => { + assert.ok( + SUPPORTED_MODEL_ROLE_CONSTRAINTS.includes("lineage-diverse-from-worker"), + ); + }); + + test("adversary_default_constraints_include_review_and_lineage_diverse_from_worker", () => { + const policy = normalizeRolePolicy("adversary"); + assert.deepEqual(policy, { + role: "adversary", + mode: "auto", + constraints: ["review", "lineage-diverse-from-worker"], + }); + }); + + test("reviewer_default_constraints_include_lineage_diverse_from_worker", () => { + const policy = normalizeRolePolicy("reviewer"); + assert.ok( + policy.constraints.includes("lineage-diverse-from-worker"), + `expected lineage-diverse-from-worker in reviewer defaults, got: ${policy.constraints.join(", ")}`, + ); + }); + + test("validateRolePolicy_adversary_with_lineage_diverse_from_worker_is_valid", () => { + const result = validateRolePolicy("adversary", { + constraints: ["lineage-diverse-from-worker"], + }); + assert.equal(result.ok, true); + assert.ok( + result.policy.constraints.includes("lineage-diverse-from-worker"), + ); + }); + + test("normalizeRolePolicies_empty_input_includes_adversary_role", () => { + const policies = normalizeRolePolicies({}); + assert.ok("adversary" in policies, "adversary key missing from full policy map"); + assert.deepEqual(policies.adversary, { + role: "adversary", + mode: "auto", + constraints: ["review", "lineage-diverse-from-worker"], + }); + }); + + // ── rootVendorFor / isSameRootVendor ──────────────────────────────────────── + + test("rootVendorFor_maps_claude_prefix_to_anthropic", () => { + assert.equal(rootVendorFor("claude-3-7-sonnet"), "anthropic"); + assert.equal(rootVendorFor("claude-opus-4"), "anthropic"); + }); + + test("rootVendorFor_maps_gpt_and_o_series_to_openai", () => { + assert.equal(rootVendorFor("gpt-4o"), "openai"); + assert.equal(rootVendorFor("o1-preview"), "openai"); + assert.equal(rootVendorFor("o3-mini"), "openai"); + }); + + test("rootVendorFor_maps_gemini_prefix_to_google", () => { + assert.equal(rootVendorFor("gemini-2.5-pro"), "google"); + assert.equal(rootVendorFor("gemma-3"), "google"); + }); + + test("rootVendorFor_maps_kimi_prefix_to_moonshot", () => { + assert.equal(rootVendorFor("kimi-for-coding"), "moonshot"); + assert.equal(rootVendorFor("kimi-k2.6"), "moonshot"); + }); + + test("rootVendorFor_maps_minimax_prefix_to_minimax", () => { + assert.equal(rootVendorFor("minimax-text-01"), "minimax"); + }); + + test("rootVendorFor_maps_glm_and_zai_prefix_to_zhipu", () => { + assert.equal(rootVendorFor("glm-4"), "zhipu"); + assert.equal(rootVendorFor("zai-something"), "zhipu"); + }); + + test("rootVendorFor_maps_longcat_prefix_to_meituan", () => { + assert.equal(rootVendorFor("longcat-v1"), "meituan"); + }); + + test("rootVendorFor_returns_unknown_for_unrecognised_model", () => { + assert.equal(rootVendorFor("mystery-model-x"), "unknown"); + assert.equal(rootVendorFor(""), "unknown"); + }); + + test("isSameRootVendor_true_when_same_family", () => { + assert.equal(isSameRootVendor("claude-3-5-sonnet", "claude-opus-4"), true); + assert.equal(isSameRootVendor("gpt-4o", "o1-preview"), true); + }); + + test("isSameRootVendor_false_when_different_family", () => { + assert.equal(isSameRootVendor("claude-3-5-sonnet", "gpt-4o"), false); + assert.equal(isSameRootVendor("gemini-2.5-pro", "kimi-for-coding"), false); + }); + + test("isSameRootVendor_false_when_either_model_is_unknown", () => { + assert.equal(isSameRootVendor("mystery-model", "claude-3-5-sonnet"), false); + assert.equal(isSameRootVendor("claude-3-5-sonnet", "mystery-model"), false); + assert.equal(isSameRootVendor("mystery-a", "mystery-b"), false); + }); }); diff --git a/src/resources/extensions/sf/uok/index.js b/src/resources/extensions/sf/uok/index.js index eec3f9773..21962eef5 100644 --- a/src/resources/extensions/sf/uok/index.js +++ b/src/resources/extensions/sf/uok/index.js @@ -144,9 +144,11 @@ export { export { applyModelPolicyFilter } from "./model-policy.js"; export { DEFAULT_MODEL_ROLE_CONSTRAINTS, + isSameRootVendor, ModelRolePolicyValidationError, normalizeRolePolicies, normalizeRolePolicy, + rootVendorFor, SUPPORTED_MODEL_ROLE_CONSTRAINTS, SUPPORTED_MODEL_ROLES, validateRolePolicy, diff --git a/src/resources/extensions/sf/uok/model-role-policy.js b/src/resources/extensions/sf/uok/model-role-policy.js index b3ebbf050..c55ef74bb 100644 --- a/src/resources/extensions/sf/uok/model-role-policy.js +++ b/src/resources/extensions/sf/uok/model-role-policy.js @@ -20,6 +20,7 @@ export const SUPPORTED_MODEL_ROLES = Object.freeze([ "worker", "validator", "reviewer", + "adversary", ]); /** @@ -30,6 +31,21 @@ export const SUPPORTED_MODEL_ROLES = Object.freeze([ * * Consumer: role-policy validation and autonomous route evidence writers. */ + +/** + * `lineage-diverse-from-worker` — selected model must NOT share root-vendor + * lineage with the model that produced the artifact under review (the `worker` + * role's resolved model). Prevents "looks fine to me" rubber-stamp by + * lineage-twins. When the worker model is unknown or maps to "unknown" vendor, + * this constraint is treated as a no-op (fail-open rather than blocking all + * candidates). Enforcement is best-effort at the model-selection layer. + * + * TODO(lineage-diverse-from-worker): wire handler in auto-model-selection.js + * selectAndApplyModel — after the policy gate resolves `routingEligibleModels`, + * call resolveWorkerLineage(resolvedWorkerModelId) and filter candidates with + * !isSameRootVendor(candidate.id, workerVendor). See rootVendorFor() in this + * file for the canonical vendor mapping. + */ export const SUPPORTED_MODEL_ROLE_CONSTRAINTS = Object.freeze([ "coding", "review", @@ -38,6 +54,7 @@ export const SUPPORTED_MODEL_ROLE_CONSTRAINTS = Object.freeze([ "byok-allowed", "local-only", "strict-json", + "lineage-diverse-from-worker", ]); const MODEL_ROLES = new Set(SUPPORTED_MODEL_ROLES); @@ -63,7 +80,8 @@ export const DEFAULT_MODEL_ROLE_CONSTRAINTS = Object.freeze({ orchestrator: Object.freeze(["long-context", "strict-json"]), worker: Object.freeze(["coding"]), validator: Object.freeze(["strict-json", "review"]), - reviewer: Object.freeze(["review"]), + reviewer: Object.freeze(["review", "lineage-diverse-from-worker"]), + adversary: Object.freeze(["review", "lineage-diverse-from-worker"]), }); /** @@ -233,3 +251,71 @@ export function normalizeRolePolicies(policies = {}) { ]), ); } + +/** + * Map a model ID to its root-vendor label for lineage-diversity checks. + * + * Purpose: back the `lineage-diverse-from-worker` constraint — model selection + * for reviewer/adversary roles must filter candidates that share a vendor with + * the worker's resolved model. + * + * Returns one of: "anthropic" | "openai" | "google" | "moonshot" | "mistral" | + * "minimax" | "zhipu" | "meituan" | "unknown". "unknown" means no filter is + * applied (fail-open). + * + * Consumer: lineage-diverse-from-worker handler in the model-selection layer + * (TODO — see constraint JSDoc above). + * + * @param {string} modelId + * @returns {string} + */ +export function rootVendorFor(modelId) { + const id = String(modelId ?? "").toLowerCase(); + if (id.startsWith("claude-") || id.startsWith("anthropic/")) return "anthropic"; + if ( + id.startsWith("gpt-") || + id.startsWith("o1-") || + id.startsWith("o3-") || + id.startsWith("o4-") || + id.startsWith("openai/") + ) + return "openai"; + if ( + id.startsWith("gemini-") || + id.startsWith("gemma-") || + id.startsWith("google/") || + id.startsWith("google-") + ) + return "google"; + if (id.startsWith("kimi-") || id.startsWith("moonshotai/")) return "moonshot"; + if (id.startsWith("mistral-") || id.startsWith("mistral/")) return "mistral"; + if (id.startsWith("minimax-") || id.startsWith("minimax/")) return "minimax"; + if ( + id.startsWith("glm-") || + id.startsWith("zai-") || + id.startsWith("zhipu/") + ) + return "zhipu"; + if (id.startsWith("longcat-")) return "meituan"; + return "unknown"; +} + +/** + * Return true when two model IDs share root-vendor lineage. + * + * Purpose: allow model-selection layer to implement the + * `lineage-diverse-from-worker` constraint as a simple predicate. + * + * "unknown" vendor is treated as non-matching so it never blocks candidates + * (fail-open for unrecognised models). + * + * @param {string} candidateModelId + * @param {string} workerModelId + * @returns {boolean} + */ +export function isSameRootVendor(candidateModelId, workerModelId) { + const a = rootVendorFor(candidateModelId); + const b = rootVendorFor(workerModelId); + if (a === "unknown" || b === "unknown") return false; + return a === b; +}