Files
jason.woltje a8d9eecf65 organization: move packages/business to packages/organization (rename r2 stage B1)
git mv packages/business -> packages/organization (@mosaic/organization). The
old package stays as one-line re-export shims of each moved module, with
src/business.mjs re-exporting src/organization-config.mjs and src/cli.mjs
forwarding to the moved executable. BusinessError is OrganizationConfigError
(one constructor, name kept BusinessError); OrganizationError stays distinct.
The bus adapter gains organization-context.mjs (busOrganization), with
business.mjs (busBusiness) as the same function.

Import consumers, manifests, the lockfile, scripts/mosaic, stack-copy.sh
COPY_PATHS (both roots), seat sandbox RUNNER_FILES and the copy closure
walker (organization-imports.mjs, both roots required) move to the new path.
v1 serialization, v1 writers and the stage A fixtures are unchanged; the stage
A contract tests stay where they were and pass as before.
2026-11-09 11:47:48 -06:00

49 lines
2.3 KiB
JavaScript

import { test } from "node:test";
import assert from "node:assert/strict";
import { join } from "node:path";
import { BusinessError, loadProject, projectFilePath, validateProjectDocument } from "../src/index.mjs";
import { tmp, writeJson } from "./helpers.mjs";
function refuses(fn, pattern, exitCode = 2) {
assert.throws(fn, (error) => {
assert.ok(error instanceof BusinessError, `expected BusinessError, got ${error}`);
assert.equal(error.exitCode, exitCode);
assert.match(error.message, pattern);
return true;
});
}
const doc = (extra = {}) => ({
projectVersion: 1, id: "stack",
vars: { "tracker.project": 3, "git.workingBranch": "refactor", "git.protectedBranches": ["main", "next"] },
roles: { coder: { vars: { "limits.tools": ["read", "bash"] } } },
...extra,
});
test("path and load", () => {
assert.equal(projectFilePath("/w/stack"), "/w/stack/.mosaic/project.json");
const root = tmp();
writeJson(projectFilePath(root), doc());
const project = loadProject(root);
assert.equal(project.id, "stack");
assert.equal(project.file, projectFilePath(root));
assert.deepEqual(project.roles.coder.vars["limits.tools"], ["read", "bash"]);
assert.ok(Object.isFrozen(project.vars));
});
test("refusals", () => {
refuses(() => loadProject("relative"), /normalized absolute path/);
refuses(() => loadProject("/a/../b"), /normalized absolute path/);
refuses(() => loadProject(tmp()), /project file not found/, 4);
const v = (d) => validateProjectDocument(d, "/p/.mosaic/project.json");
refuses(() => v(doc({ projectVersion: 2 })), /projectVersion/);
refuses(() => v(doc({ id: "Stack" })), /project id/);
refuses(() => v(doc({ owner: "x" })), /unsupported project file key: "owner"/);
refuses(() => v(doc({ vars: { harness: "pi" } })), /harness can't be set at the project layer/);
refuses(() => v(doc({ vars: { "tracker.baseUrl": "http://x" } })), /project layer/);
refuses(() => v(doc({ roles: { coder: { vars: { model: "x" } } } })), /roles\.coder\.vars: variable model can't be set at the project layer/);
refuses(() => v(doc({ roles: { coder: { holder: "x" } } })), /unsupported .*roles\.coder key: "holder"/);
refuses(() => v(doc({ roles: { "Coder!": {} } })), /role instance/);
assert.deepEqual(v(doc({ vars: undefined, roles: undefined })).roles, {});
});