// #1873 Phase A slice A1: the three agent levels and their union (design // docs/plans/2026-11-10_agent-levels-design.md sections 2, 3 and 6). Read-only: nothing here writes. import test from "node:test"; import assert from "node:assert/strict"; import { chmodSync, mkdirSync, symlinkSync, writeFileSync } from "node:fs"; import { join } from "node:path"; import { checkProjectAgents, checkRootAgents, loadRootAgents, projectUnion, resolveAgentLevels, validateOrganizationDocument, } from "../src/index.mjs"; import { businessDoc, REPO_ROLES, tmp, writeJson } from "./helpers.mjs"; const rolesDir = REPO_ROLES; const NONE = Object.freeze({ state: "absent", agents: {}, assistant: null }); const root = (agents) => checkRootAgents({ agentsVersion: 1, agents }, { rolesDir }); const project = (agents) => checkProjectAgents(agents, { rolesDir, where: "projects.web.agents" }); const ORG = { pm: { definition: "pm", vars: {} }, reviewer: { definition: "reviewer", vars: {} }, chief: { definition: "chief-of-staff", vars: {} } }; const names = (union) => union.agents.map((a) => `${a.level}:${a.name}`); const refuses = (fn, pattern) => assert.throws(fn, (error) => error.name === "BusinessError" && pattern.test(error.message)); test("root level alone: each root agent runs in the organization it is used from", () => { const r = root({ researcher: { definition: "research", vars: { model: "claude-opus-5-5" } }, assistant: { definition: "assistant" } }); assert.equal(r.assistant, "assistant"); const union = projectUnion({ orgId: "acme", root: r, org: {}, project: {}, projectId: "web" }); assert.deepEqual(names(union), ["root:researcher"]); assert.deepEqual(union.agents[0].scope, { kind: "org", org: "acme" }); assert.equal(union.agents[0].vars.model, "claude-opus-5-5"); // The same root agent used from another organization is scoped to that one, never to both. assert.deepEqual(projectUnion({ orgId: "home", root: r, org: {} }).agents[0].scope, { kind: "org", org: "home" }); }); test("organization level alone: its instances, scoped to it", () => { const union = projectUnion({ orgId: "acme", root: NONE, org: ORG }); assert.deepEqual(names(union), ["org:chief", "org:pm", "org:reviewer"]); assert.ok(union.agents.every((a) => a.scope.org === "acme")); assert.equal(union.project, null); }); test("project level alone: a project-defined agent", () => { const union = projectUnion({ orgId: "acme", root: NONE, org: {}, project: project({ checker: { definition: "fact-checker" } }), projectId: "web" }); assert.deepEqual(names(union), ["project:checker"]); assert.equal(union.agents[0].project, "web"); assert.equal(union.agents[0].definition, "fact-checker"); }); test("clash: the most specific level wins and the shadowed definitions are recorded", () => { const r = root({ reviewer: { definition: "reviewer", vars: { model: "glm-5.3-flash" } } }); const p = project({ reviewer: { definition: "security-reviewer" } }); const union = projectUnion({ orgId: "acme", root: r, org: ORG, project: p, projectId: "web" }); const reviewer = union.agents.find((a) => a.name === "reviewer"); assert.equal(reviewer.level, "project"); assert.equal(reviewer.definition, "security-reviewer"); assert.deepEqual(reviewer.overrides, [{ level: "root", definition: "reviewer" }, { level: "org", definition: "reviewer" }]); // The whole entry wins: the root's model does not leak into the project definition. assert.equal(reviewer.vars.model, undefined); assert.equal(union.agents.filter((a) => a.name === "reviewer").length, 1); }); test("project narrowing: intersect keys only, never wider than what it inherits", () => { const org = { ...ORG, coder: { definition: "coder", vars: { model: "claude-sonnet-5-5", "limits.network": "api-only", "limits.tools": ["read", "bash"] } } }; const p = project({ coder: { vars: { "limits.network": "open", "limits.tools": ["read", "edit"] } } }); const coder = projectUnion({ orgId: "acme", root: NONE, org, project: p, projectId: "web" }).agents.find((a) => a.name === "coder"); assert.equal(coder.level, "org"); assert.equal(coder.vars["limits.network"], "api-only"); // asking for open stays at the narrower api-only assert.deepEqual(coder.vars["limits.tools"], ["read"]); assert.equal(coder.vars.model, "claude-sonnet-5-5"); assert.deepEqual(coder.narrowed, ["limits.network", "limits.tools"]); assert.equal(coder.vars["tracker.kind"], undefined); // no registry defaults leak in refuses(() => project({ coder: { vars: { model: "claude-opus-5-5" } } }), /can only narrow limits\.tools, limits\.network, limits\.authority, not "model"/); }); test("exclusions: a root opt-out per organization and a project exclude", () => { const r = root({ reviewer: { definition: "reviewer", excludeOrgs: ["home"] }, researcher: { definition: "research" } }); assert.deepEqual(names(projectUnion({ orgId: "home", root: r, org: {} })), ["root:researcher"]); assert.deepEqual(projectUnion({ orgId: "home", root: r, org: {} }).excluded, [{ name: "reviewer", level: "root", by: "root" }]); const union = projectUnion({ orgId: "acme", root: r, org: {}, project: project({ researcher: { exclude: true } }), projectId: "web" }); assert.deepEqual(names(union), ["root:reviewer"]); assert.deepEqual(union.excluded, [{ name: "researcher", level: "root", by: "project" }]); }); test("a name the root turned off, or a project excluded, that a more specific level defines is available, not excluded", () => { const r = root({ reviewer: { definition: "reviewer", excludeOrgs: ["home"] } }); // The organization's own reviewer: available under the organization's definition, no opt-out listed. const home = projectUnion({ orgId: "home", root: r, org: { reviewer: { definition: "reviewer", vars: {} } } }); assert.deepEqual(names(home), ["org:reviewer"]); assert.deepEqual(home.excluded, []); // A project that defines the name the root turned off for its organization. const web = projectUnion({ orgId: "home", root: r, org: {}, project: project({ reviewer: { definition: "security-reviewer" } }), projectId: "web" }); assert.deepEqual(names(web), ["project:reviewer"]); assert.deepEqual(web.excluded, []); // Without a definition of its own, the opt-out is still reported. assert.deepEqual(projectUnion({ orgId: "home", root: r, org: {}, project: {}, projectId: "web" }).excluded, [{ name: "reviewer", level: "root", by: "root" }]); }); test("the root assistant is cross-organization and never in one organization's union", () => { const r = root({ assistant: { definition: "assistant" } }); assert.deepEqual(projectUnion({ orgId: "acme", root: r, org: {} }).agents, []); // An organization's own assistant instance works inside that organization only. const own = projectUnion({ orgId: "acme", root: r, org: { assistant: { definition: "assistant", vars: {} } } }); assert.deepEqual(names(own), ["org:assistant"]); assert.deepEqual(own.agents[0].scope, { kind: "org", org: "acme" }); refuses(() => root({ helper: { definition: "assistant" } }), /assistant definition is the root agent named assistant only/); }); test("refusals in a project's union: unknown targets, the chief, the assistant", () => { refuses(() => projectUnion({ orgId: "acme", root: NONE, org: ORG, project: project({ ghost: { exclude: true } }), projectId: "web" }), /names no root or organization agent to exclude/); refuses(() => projectUnion({ orgId: "acme", root: NONE, org: ORG, project: project({ ghost: { vars: { "limits.network": "none" } } }), projectId: "web" }), /to narrow/); refuses(() => projectUnion({ orgId: "acme", root: NONE, org: ORG, project: project({ chief: { exclude: true } }), projectId: "web" }), /can't change the organization's chief-of-staff/); refuses(() => projectUnion({ orgId: "acme", root: NONE, org: ORG, project: project({ chief: { definition: "reviewer" } }), projectId: "web" }), /can't change the organization's chief-of-staff/); }); test("malformed root definitions refuse", () => { refuses(() => checkRootAgents({ agentsVersion: 2, agents: {} }, { rolesDir }), /agentsVersion/); refuses(() => checkRootAgents({ agentsVersion: 1, agents: {}, extra: 1 }, { rolesDir }), /unsupported root agents key: "extra"/); refuses(() => root({ boss: { definition: "chief-of-staff" } }), /can't be a root agent/); refuses(() => root({ old: { definition: "researcher" } }), /version 1/); refuses(() => root({ x: { definition: "no-such-role" } }), /no usable role definition named no-such-role/); refuses(() => root({ x: { definition: "reviewer", credentials: {} } }), /can't carry credentials/); refuses(() => root({ x: { definition: "reviewer", vars: { "tracker.project": 3 } } }), /can't be set at the agent layer/); refuses(() => root({ x: { definition: "reviewer", excludeOrgs: ["a", "a"] } }), /duplicate entry/); refuses(() => root({ "Bad Name": { definition: "reviewer" } }), /agent name must match/); }); test("a refused var names its key, never its value", () => { const secret = "sk-secret-marker-7731"; const hides = (fn, key) => assert.throws(fn, (error) => error.name === "BusinessError" && error.message.includes(`variable ${key}`) && !error.message.includes(secret)); hides(() => root({ x: { definition: "reviewer", vars: { thinking: secret } } }), "thinking"); hides(() => root({ x: { definition: "reviewer", vars: { model: "opus-5.5", harness: `${secret}!` } } }), "harness"); hides(() => root({ x: { definition: "reviewer", vars: { "limits.tools": ["read", secret] } } }), "limits.tools"); hides(() => project({ x: { definition: "reviewer", vars: { thinking: secret } } }), "thinking"); hides(() => project({ x: { vars: { "limits.network": secret } } }), "limits.network"); // A message that quotes no value is kept whole, so the owner still reads the rule. refuses(() => root({ x: { definition: "reviewer", vars: { "tracker.project": 3 } } }), /variable tracker\.project can't be set at the agent layer/); refuses(() => root({ x: { definition: "reviewer", vars: { "fallback.model": "m" } } }), /a fallback sets all of/); }); test("malformed project definitions refuse", () => { refuses(() => project({ x: { exclude: true, vars: {} } }), /exactly \{"exclude": true\}/); refuses(() => project({ x: { exclude: false } }), /exactly/); refuses(() => project({ x: {} }), /defines \(definition\), narrows \(vars\) or excludes/); refuses(() => project({ x: { definition: "chief-of-staff" } }), /can't be a project agent/); refuses(() => project({ x: { definition: "assistant" } }), /can't be a project agent/); refuses(() => project({ x: { definition: "reviewer", hosts: [] } }), /can't carry hosts/); refuses(() => project({ x: { definition: "reviewer", other: 1 } }), /unsupported projects\.web\.agents\.x key/); }); test("root file: absent, owner and mode checks, symbolic link, bad JSON, and no host path in a message", () => { const dir = tmp(); assert.equal(loadRootAgents(dir, { rolesDir }).state, "absent"); const file = writeJson(join(dir, "agents.json"), { agentsVersion: 1, agents: { reviewer: { definition: "reviewer" } } }); const ok = loadRootAgents(dir, { rolesDir }); assert.equal(ok.state, "ok"); assert.deepEqual(Object.keys(ok.agents), ["reviewer"]); chmodSync(file, 0o620); refuses(() => loadRootAgents(dir, { rolesDir }), /writable by group or other/); chmodSync(file, 0o600); writeFileSync(file, "{ nope"); assert.throws(() => loadRootAgents(dir, { rolesDir }), (error) => /not valid JSON \(agents\.json\)/.test(error.message) && !error.message.includes(dir)); // The parser's own text can quote the file's bytes; the refusal never carries it. writeFileSync(file, '{"agentsVersion": 1, "agents": {"x": secret-marker-7731}}'); assert.throws(() => loadRootAgents(dir, { rolesDir }), (error) => error.message === "root agent file is not valid JSON (agents.json)" && error.exitCode !== undefined); // An id check's quote of what it was given is dropped too. writeFileSync(file, JSON.stringify({ agentsVersion: 1, agents: { x: { definition: "Secret Marker 7731" } } })); assert.throws(() => loadRootAgents(dir, { rolesDir }), (error) => /root agents\.x\.definition must match/.test(error.message) && !error.message.includes("Marker")); const linked = tmp(); symlinkSync(file, join(linked, "agents.json")); assert.throws(() => loadRootAgents(linked, { rolesDir }), (error) => error.exitCode === 4 && !error.message.includes(linked)); }); test("organization file: project agents are checked but not loaded; a malformed one refuses the file", () => { const dir = tmp(); const doc = businessDoc(dir); doc.projects.stack.agents = { coder: { vars: { "limits.network": "none" } }, checker: { definition: "fact-checker" } }; const loaded = validateOrganizationDocument(structuredClone(doc), "acme.json", { rolesDir }); assert.equal(loaded.projects.stack.agents, undefined); const levels = resolveAgentLevels({ orgId: "acme", doc, root: NONE, rolesDir }); assert.equal(levels.unions.stack.ok, true); assert.ok(levels.unions.stack.agents.some((a) => a.name === "checker" && a.level === "project")); assert.deepEqual(levels.unions.stack.agents.find((a) => a.name === "coder").narrowed, ["limits.network"]); doc.projects.stack.agents = { boss: { definition: "chief-of-staff" } }; refuses(() => validateOrganizationDocument(doc, "acme.json", { rolesDir }), /can't be a project agent/); }); test("one project's bad union refuses only that project", () => { const dir = tmp(); const doc = businessDoc(dir); mkdirSync(join(dir, "other")); doc.projects.other = { root: join(dir, "other"), agents: { ghost: { exclude: true } } }; const levels = resolveAgentLevels({ orgId: "acme", doc, root: NONE, rolesDir }); assert.equal(levels.unions.stack.ok, true); assert.equal(levels.unions.other.ok, false); assert.match(levels.unions.other.reason, /names no root or organization agent to exclude/); assert.ok(levels.base.agents.length > 0); });