The CLI agent verbs validate the instance config through loadSystem before
any read or write, and print a fixed line for errors they do not own. The
root writer refuses a config directory with an ancestor another user can
write without the sticky bit. The project HTTP route checks agent names
with the organization ID_PATTERN and answers 400 naming the key.
The Settings level forms take their own class, so the role-instance
handlers no longer catch them, and keep both drafts in module state with
the revision each began at. The organization writer tests now reach the
file's own uid check and test the assistant protection with an assistant
in the fixture.
Root writer for <configDir>/agents.json (owner only, revision-checked, atomic, A1-validated,
receipts name agents and keys only), the project.agents writer verb on the organization
writer, `mosaic agents root` and `mosaic project agents` verbs, and owner-only Settings >
Agents edit forms on GET/POST hooks gated like /api/agents/levels.
Review finding 1: step 7 compares messaging.receiveFrom with the chief's as
well as sendTo, since the broker checks the recipient's list too.
Review finding 2: checkComposition reads the role and the chief once, as a JSON
copy; a getter, proxy, toJSON, cycle or BigInt that throws refuses with a fixed
message, so the thrown text never reaches the caller.
Roles round 2, slice P1. checkComposition(role, registry, chief, { assertClean })
runs the design's seven steps in order against a registry from buildRegistry
(core vocabulary plus a caller-passed plugin set, empty by default) and returns
the flagged lines: every decision.resolve.* action and everything above the
chief's own resolved role. Refusals name the field and position, quote only
registered names, and never quote instructions or personality text.
COMPOSITION_BOUNDS holds the length bounds the design leaves open. Nothing
calls checkComposition yet; HIRE_ALLOWED and unhireableAction stay until A4.
Two independent reviews (Sol, daybreak-blue) asked for changes.
- An organization's own assistant instance is scoped to that
organization. Only the root assistant, which no union holds, is
cross-organization.
- A root opt-out or project exclude whose name a more specific level
defines is no longer listed as unavailable.
- A malformed root agents.json refuses with "not valid JSON" only; the
parser's text, which can quote the file's bytes, is dropped.
- A refused root or project var names its key, never its value; an id
check's quote of a root value is dropped too. A value can be a secret
typed in the wrong place, and these refusals reach the Console.
- The root assistant's row shows the model the root file sets.
Tests cover each case, with a sentinel value checked absent from the
route body. The design doc records the behavior.
Phase A slice A1, no authority change. Agents are defined at three levels:
the user's root (<configDir>/agents.json), the organization (roles) and the
project (projects.<p>.agents in the organization file). A project's agents
are their union, the most specific level winning.
- organization: agent-levels.mjs loads and checks the root file (no link,
owner uid, no group or other write) and project entries (define, narrow
intersect keys only, exclude), and resolves the union. The organization
file checks project agents for every reader and leaves them out of the
loaded business, so buses see what they saw before. Old-path re-export
in packages/business.
- webui: GET /api/agents/levels, owner only, with no host path, credential
reference or var other than the model in its body. display.js leaves it
unscoped. SERVER_PROJECT_KEYS holds agents.
- Agents grid: the level band, a project picker, and per agent its level,
the file that defines it, memory, status and model, with narrowed keys,
overrides and what the project does not get.
- Design: docs/plans/2026-11-10_agent-levels-design.md. The roles/ change is
a proposal outside this branch.
Adds the system-type kind with five modules (coding, creative-writing,
marketing, montage, business-management), an org-level enabled list
validated fail-closed, project selections limited to the org's list,
read verbs, and bus migration 3t-3u. REQ-SYS-2 toolsets are designed and
deferred: they need a role authority contract.
Conflict in packages/webui/src/serve.mjs imports: kept F2's
THREAD_DEFINITION and onboarding imports on stage B's organization path.
The F2 assistant test edit followed the move to packages/organization;
onboarding-browser now imports the organization service directly.
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.