@kontextmind/kxm
Version:
KXM local-first multi-agent orchestration and operator dashboard
327 lines (303 loc) • 14.3 kB
JavaScript
/**
* Workforce id conventions and rename resolution.
*
* Role ids are the vocabulary (`planner`, `writer`, `reviewer-arch`,
* `reviewer-cli`, `experiment`). Agent ids and agent-step ids use that same
* vocabulary. Route ids are `<harness>-<model-slug>[-<provider>]`, with `.`
* in the model slug written as `-`. A provider suffix is required when the
* model id contains `/`.
*
* Resolution is bidirectional and only fills a missing side. A commit whose
* roles and models still use the old ids keeps matching itself, which is what
* the assignment runner reads at one commit. A tree that has already renamed
* the files still accepts the old id. A mixed tree accepts either side.
* `opus-claude` is not renamed: that route pointed at an unadmitted model and
* was removed. `qwen-openrouter-pi` is not renamed either: it belonged to
* retired `pi-qwen3-coder-plus-openrouter` and does not resolve to
* `pi-qwen3-7-plus-openrouter`.
*/
import { existsSync, readdirSync, readFileSync } from "node:fs";
import { join } from "node:path";
import YAML from "yaml";
export const ROSTER_EFFORTS = new Set(["off", "minimal", "low", "medium", "high", "xhigh", "max"]);
export const ROLE_IDS = Object.freeze([
"writer",
"planner",
"reviewer-arch",
"reviewer-cli",
"experiment",
]);
/** Old id, canonical id. Listed once; lookup tries both directions. */
export const ROUTE_RENAMES = Object.freeze([
["grok-native", "grok-grok-4-7"],
["gemini-agy", "agy-gemini-4-argon-high"],
["fable-claude", "claude-fable"],
// sol-codex stays the GPT-5.6 route. codex-gpt-6-1-sol is a separate route.
["sol-codex", "codex-gpt-5-6-sol"],
["grok-default", "grok-grok-4-6"],
["fable-default", "claude-fable"],
]);
export const AGENT_RENAMES = Object.freeze([
["coordinator", "planner"],
["implementer", "writer"],
["critic-arch", "reviewer-arch"],
["critic-cli", "reviewer-cli"],
]);
export const WORKFLOW_RENAMES = Object.freeze([
["implement-only", "writer-only"],
["review-arch-only", "reviewer-arch-only"],
["review-cli-only", "reviewer-cli-only"],
]);
export const STEP_RENAMES = Object.freeze([
["implement", "writer"],
["critic-arch", "reviewer-arch"],
["review-arch", "reviewer-arch"],
["critic-cli", "reviewer-cli"],
["review-cli", "reviewer-cli"],
]);
const RENAMES = Object.freeze({
route: ROUTE_RENAMES,
agent: AGENT_RENAMES,
workflow: WORKFLOW_RENAMES,
step: STEP_RENAMES,
});
const RESERVED_WORKFLOW_IDS = new Set(["default", "land"]);
const warned = new Set();
export function noteDeprecatedId(kind, from, to) {
const key = `${kind}\0${from}\0${to}`;
if (warned.has(key)) return;
warned.add(key);
if (process.env.KXM_QUIET_ALIASES === "1") return;
console.warn(`kxm: deprecated ${kind} id '${from}' resolves to '${to}'`);
}
export function resetDeprecatedIdWarnings() {
warned.clear();
}
export function resolveRenamedId(requested, knownIds, pairs) {
if (typeof requested !== "string" || knownIds.has(requested)) return requested;
for (const [oldId, newId] of pairs) {
if (requested === oldId && knownIds.has(newId)) return newId;
if (requested === newId && knownIds.has(oldId)) return oldId;
}
return requested;
}
export function canonicalRouteId(harness, model) {
if (typeof harness !== "string" || typeof model !== "string" || !harness || !model) return undefined;
const parts = model.split("/").filter((part) => part.length > 0);
if (parts.length === 0) return undefined;
const slug = parts[parts.length - 1].replaceAll(".", "-").toLowerCase();
const provider = parts.length > 1 ? parts[0].replaceAll(".", "-").toLowerCase() : undefined;
const id = provider ? `${harness}-${slug}-${provider}` : `${harness}-${slug}`;
return /^[a-z][a-z0-9]*(?:-[a-z0-9]+)*$/u.test(id) && id.length <= 64 ? id : undefined;
}
function aliasList(value) {
return Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
}
/**
* @param {Iterable<{ id?: string, aliases?: readonly string[] }>} records
* @param {string} requested
* @param {"route"|"agent"|"workflow"|"step"} kind
*/
export function lookupById(records, requested, kind) {
const list = [...records].filter((record) => typeof record?.id === "string" && record.id.length > 0);
const known = new Set(list.map((record) => record.id));
const direct = list.find((record) => record.id === requested);
if (direct) return { record: direct, viaAlias: false };
for (const record of list) {
if (aliasList(record.aliases).includes(requested)) {
noteDeprecatedId(kind, requested, record.id);
return { record, viaAlias: true };
}
}
const renamed = resolveRenamedId(requested, known, RENAMES[kind] ?? []);
if (renamed !== requested) {
const record = list.find((item) => item.id === renamed);
if (record) {
noteDeprecatedId(kind, requested, renamed);
return { record, viaAlias: true };
}
}
return undefined;
}
export function findYamlBasename(directory, requested, kind) {
if (typeof requested !== "string" || !requested) return undefined;
if (existsSync(join(directory, `${requested}.yaml`))) return requested;
if (!existsSync(directory)) return undefined;
const found = [];
for (const name of readdirSync(directory)) {
if (!name.endsWith(".yaml") || name === "inventory.yaml") continue;
const id = name.slice(0, -5);
let doc;
try {
doc = YAML.parse(readFileSync(join(directory, name), "utf8"));
} catch {
continue;
}
const aliases = doc && typeof doc === "object" ? aliasList(doc.aliases) : [];
found.push({ id, aliases });
}
const hit = lookupById(found, requested, kind);
return hit?.record.id;
}
function readYamlDir(directory, skipInventory) {
if (!existsSync(directory)) return [];
const docs = [];
for (const name of readdirSync(directory)) {
if (!name.endsWith(".yaml")) continue;
const id = name.slice(0, -5);
if (skipInventory && id === "inventory") continue;
let doc;
try {
doc = YAML.parse(readFileSync(join(directory, name), "utf8"));
} catch (error) {
docs.push({ id, file: `${directory}/${name}`, error: error instanceof Error ? error.message : "invalid YAML" });
continue;
}
if (!doc || typeof doc !== "object" || Array.isArray(doc)) {
docs.push({ id, file: `${directory}/${name}`, error: "root must be a mapping" });
continue;
}
docs.push({ id, file: name, doc });
}
return docs;
}
function push(bucket, severity, code, file, message) {
bucket.push({ severity, code, file, message });
}
function selectorsFor(doc) {
const model = typeof doc.model === "string" ? doc.model : "";
const vendor = typeof doc.vendor === "string" ? doc.vendor : "";
const harness = typeof doc.harness === "string" ? doc.harness : "";
return [model, vendor && model ? `${vendor}/${model}` : "", harness && model ? `${harness}/${model}` : ""].filter(Boolean);
}
/**
* Lint one project's `.kxm` workforce. Errors fail CI. An admitted model with
* no route is a warning. A model file with `status: retired` may stay off
* every roster and off `.kxm/routes.yaml`; a roster that still names it is
* `route_not_admitted`. Alias declarations are not uses; a roster or step
* that still cites an old id is a `deprecated_id` warning.
*/
export function lintWorkforce(root) {
const errors = [];
const warnings = [];
const models = readYamlDir(join(root, ".kxm", "models"), true).filter((entry) => !entry.error);
for (const entry of readYamlDir(join(root, ".kxm", "models"), true)) {
if (entry.error) push(errors, "error", "invalid_yaml", `.kxm/models/${entry.id}.yaml`, entry.error);
}
const roles = readYamlDir(join(root, ".kxm", "roles"), false).filter((entry) => !entry.error);
const agents = readYamlDir(join(root, ".kxm", "agents"), false).filter((entry) => !entry.error);
const workflows = readYamlDir(join(root, ".kxm", "workflows"), false).filter((entry) => !entry.error);
let admitted = [];
const routesFile = join(root, ".kxm", "routes.yaml");
if (existsSync(routesFile)) {
try {
const policy = YAML.parse(readFileSync(routesFile, "utf8"));
admitted = Array.isArray(policy?.admitted) ? policy.admitted.filter((item) => typeof item === "string") : [];
} catch (error) {
push(errors, "error", "invalid_yaml", ".kxm/routes.yaml", error instanceof Error ? error.message : "invalid YAML");
}
}
const modelRecords = models.map((entry) => ({ id: entry.id, aliases: aliasList(entry.doc.aliases), entry }));
const used = new Set();
for (const role of roles) {
const purpose = role.doc.purpose;
if (typeof purpose === "string" && role.id !== purpose) {
push(errors, "error", "role_id_convention", `.kxm/roles/${role.id}.yaml`, `role id '${role.id}' must equal purpose '${purpose}'`);
}
const roster = Array.isArray(role.doc.roster) ? role.doc.roster : [];
for (const item of roster) {
if (!item || typeof item !== "object" || Array.isArray(item)) continue;
const route = item.route;
const effort = item.effort;
const routeLabel = typeof route === "string" && route ? route : "(missing route)";
if (typeof effort !== "string" || !ROSTER_EFFORTS.has(effort)) {
const detail = typeof effort === "string"
? `effort '${effort}' is not one of off, minimal, low, medium, high, xhigh, max`
: "has no effort";
push(errors, "error", "roster_effort_required", `.kxm/roles/${role.id}.yaml`, `roster route '${routeLabel}' ${detail}`);
}
if (typeof route !== "string" || !route) continue;
const found = lookupById(modelRecords, route, "route");
if (!found) {
push(errors, "error", "route_unknown", `.kxm/roles/${role.id}.yaml`, `roster route '${route}' does not match a model file`);
continue;
}
used.add(found.record.id);
if (found.viaAlias) {
push(warnings, "warning", "deprecated_id", `.kxm/roles/${role.id}.yaml`, `roster route '${route}' is a deprecated alias of '${found.record.id}'`);
}
}
}
const covered = new Set();
for (const model of models) {
const doc = model.doc;
if (typeof doc.id === "string" && doc.id !== model.id) {
push(errors, "error", "route_id_convention", `.kxm/models/${model.id}.yaml`, `declared id '${doc.id}' does not match the filename`);
}
const expected = canonicalRouteId(doc.harness, doc.model);
if (!expected || model.id !== expected) {
push(errors, "error", "route_id_convention", `.kxm/models/${model.id}.yaml`, `route id '${model.id}' must be '${expected ?? "<harness>-<model-slug>[-<provider>]"}'`);
}
const retired = doc.status === "retired";
if (retired && used.has(model.id)) {
push(errors, "error", "route_not_admitted", `.kxm/models/${model.id}.yaml`, `retired route '${model.id}' is still on a role roster`);
}
if (!retired && !used.has(model.id)) {
push(errors, "error", "route_unused", `.kxm/models/${model.id}.yaml`, `route '${model.id}' is not on any role roster`);
}
if (retired) continue;
const selectorHit = selectorsFor(doc).find((selector) => admitted.includes(selector));
if (!selectorHit) {
push(errors, "error", "route_not_admitted", `.kxm/models/${model.id}.yaml`, `model '${doc.model ?? ""}' is not an admitted selector`);
} else {
covered.add(selectorHit);
}
}
for (const selector of admitted) {
if (!covered.has(selector)) {
push(warnings, "warning", "admitted_unrouted", ".kxm/routes.yaml", `admitted model '${selector}' is not used by any route`);
}
}
const agentRecords = agents.map((entry) => ({ id: entry.id, aliases: aliasList(entry.doc.aliases), entry }));
for (const agent of agents) {
const role = agent.doc.role;
if (typeof role !== "string" || agent.id !== role) {
push(errors, "error", "agent_id_convention", `.kxm/agents/${agent.id}.yaml`, `agent id '${agent.id}' must equal its role '${role ?? ""}'`);
}
}
for (const workflow of workflows) {
if (!RESERVED_WORKFLOW_IDS.has(workflow.id) && !ROLE_IDS.some((role) => workflow.id === `${role}-only`)) {
push(errors, "error", "workflow_id_convention", `.kxm/workflows/${workflow.id}.yaml`, `workflow id '${workflow.id}' must be default, land, or <role>-only`);
}
const steps = Array.isArray(workflow.doc.steps) ? workflow.doc.steps : [];
const realIds = new Set(steps.map((step) => step && typeof step.id === "string" ? step.id : "").filter(Boolean));
for (const step of steps) {
if (!step || typeof step !== "object" || typeof step.id !== "string") continue;
if (step.kind === "agent" || step.kind === "moa") {
const agentName = typeof step.agent === "string" ? step.agent : "";
const found = agentName ? lookupById(agentRecords, agentName, "agent") : undefined;
if (!found) {
push(errors, "error", "agent_unknown", `.kxm/workflows/${workflow.id}.yaml`, `step '${step.id}' references unknown agent '${agentName}'`);
} else if (found.viaAlias) {
push(warnings, "warning", "deprecated_id", `.kxm/workflows/${workflow.id}.yaml`, `step '${step.id}' agent '${agentName}' is a deprecated alias of '${found.record.id}'`);
}
const role = found?.record.entry.doc.role;
if (typeof role === "string" && step.id !== role) {
push(errors, "error", "step_id_convention", `.kxm/workflows/${workflow.id}.yaml`, `agent step '${step.id}' must use the role id '${role}'`);
}
} else if (step.kind === "gate") {
const gate = typeof step.gate === "string" ? step.gate : "";
const allowed = step.id === "verify" || step.id === gate || step.id.startsWith(`${workflow.id}-`);
if (!allowed) {
push(errors, "error", "step_id_convention", `.kxm/workflows/${workflow.id}.yaml`, `gate step '${step.id}' must be verify, the gate id, or ${workflow.id}-<name>`);
}
}
for (const alias of aliasList(step.aliases)) {
if (realIds.has(alias) && alias !== step.id) {
push(errors, "error", "step_id_convention", `.kxm/workflows/${workflow.id}.yaml`, `step alias '${alias}' collides with another step id`);
}
}
}
}
return { errors, warnings };
}