pack.state gave a pack hire only the chief of staff's resolved route, so an
org whose chief has no model refused every hire pack-hire-route-missing. The
route is now the chief's (launch.by), else the org assistant's (the instance
whose definition is assistant; the one named assistant when there are several,
else the first in key order), else null. Both resolve through resolveInstance,
need harness and model, and carry thinking when set. pack.state adds routeFrom,
and each hired or awaiting-owner receipt line carries it. Refusal next step,
CLI hint, WebUI hire reason and the bus, CLI and TOOLS docs say the refusal now
means neither has a route.
Ruling 2026-11-14: a person's pack apply records one hire decision per
proposed hire and applies it in the same act, through the hire-decisions
path, with the evidence an inbox approval leaves.
- bus: pack.apply.console runs the pack hire loop; pack.hire.console hires
an applied record's open proposals. The owner's apply approves flagged and
credential lines; a member with org.hire gets the unflagged hires and the
rest open as awaiting-owner decisions; an agent apply still refuses
human-required. Project packs hire into projects.<p>.agents. Nothing is
hired twice. Credential lines are approved, not bound. New refusal codes
pack-not-applied and pack-hire-route-missing. The instances read names the
pack each hire came from; the packs read lists hired agents and open
proposals with reasons.
- organization: pack.hire.console maps to org.hire; instances carry
hireDecision.
- cli: mosaic org pack hire ID PACK [--project P]; apply prints the hires.
- webui: pack card shows N agents hired, open proposals with reasons and
"Hire the N proposed agents"; Agents and organization pages redraw on
mosaic-org-changed; agent cards say "from pack X"; Fire (not on the chief
or assistant) removes the card at once with a receipt.
- docs: design doc section 3 dated note, TOOLS.md, package READMEs.
1. Stale bus host lock refusals from bus status, start, stop and restart
name mosaic bus recover ORG.
2. org definitions: unknown org, unknown or malformed name and bad id give
one refusal or usage line, no stack trace, no host path.
3. Pack, board and workflow refusals print their hint as template save does;
tested with the real CliError transport shape.
4. console start and stop clear a console/start.lock whose named supervisor
(pid and start time) is gone; a live or unnamed holder refuses.
5. Template save keeps free text verbatim and writes no placeholders; the
preview lists each field that mentions an org-specific name.
6. A request the shim never sent (no socket, connect refused) exits 5 and
the CLI reports bus-not-running with the start and recover line.
7. autonomy-precondition carries the check that failed, including on the
hire paths.
8. Saving under an existing template name refuses template-exists with a
next step; refusal table entry added, channel-proof pins moved.
Jason's 2026-11-12 ruling: the only deployed system is the dev environment on
dragon-lin, so the system-type plug-in kind goes with no retired kind, fence,
conversion or new refusal code.
- plugins: system-type leaves KINDS; system-types.mjs and the five modules are
deleted. An org file naming the kind refuses unknown-kind.
- organization: the plugins.system-type check, systemTypesFence and the
system-types.console right are removed.
- bus: system-types.console, project.system-types[.set], the system-type half
of the tester check, the runtime reads and system-type-not-enabled are
removed. Step 3v-3w drops project_system_types, its index, its four guards
and its rows; SCHEMA_VERSION is 3w (32 tables, 126 triggers, 5 views).
- cli: mosaic org system-types and mosaic project system-types[-set] are
removed; stack-release COUNTED and the budget supervisor's closed list move
to 3w.
- channel: the project.system-types verbs are removed.
- webui: the System types page, route, dashboard row, chips and project
selection are removed.
- docs: REQ-SYS-1/2 now describe starter packs; README system-type text and
the requirements titles follow.
- template-save: one alternation pass replaces every literal, so an
instance named root, org or agent never rewrites a placeholder already
written in. checkPack's refusal message is passed on as the reason. A
write or fsync failure removes the partial file it created.
- broker #templateFail drops a reason the credential check refuses.
- cli template words: humanTransport throws a CliError named by the bus
code; a known code now gains its hint (the test transport throws the
same CliError).
- template-http: the header notes serve.mjs strips /api/orgs/<id>.
- pins: the two .console verbs in console-decide and request-console;
the packages/business/src re-export for template-save.mjs.
- tests: placeholder collision, a held-value key masked as [key], and a
save after a real pack.apply.console keeping {id, version}.
template-save.mjs captures the org (or one project) as a starter-pack JSON under
<config>/templates/<owner>/<name>.json: copy material and instance credentials
stripped, role credential request lines kept, owner/org/project/agent/root names
replaced by placeholders in free text, at most 8 hires, packs entries {id, version}.
A held credential value in kept text refuses template-secret-suspected naming the
field path. Preview then save with the preview's digest; O_EXCL 0600 write, a
second save refuses.
Bus: template.save/template.list for the owner's CLI cap, .console names for the
owner's Console cap, null in VERB_RIGHTS. CLI: mosaic org template save|list.
Console: GET /api/templates, POST /api/templates/preview, POST /api/templates.
- An --as target that lands on another pack item's name refuses invalid-request
(definitions and workflows); the preview no longer shows two adds for one path.
- A workflow step role naming one of the pack's definitions follows its --as rename,
so an inline definition renamed at preview applies.
- GET /api/board stays the Board read; only POST /api/board is the pack board write.
- Tombstones key on pack id, origin and project: a saved template reusing a shipped
id is another pack.
- An apply that adds nothing and proposes what the last apply proposed writes
nothing, so retries do not fill the 256 packs entries.
- mosaic org pack list refuses inside an agent run like the other words.
- Tests pin coder-1's credential lines, skills.notEnabled and the member's
unsuffixed pack.apply and board.set refusals; the CONSOLE_VERBS pins in two bus
tests name the five pack verbs.
Design section 8. The bus answers packs, pack.preview, pack.apply,
board.set and workflow.set to the organization's human, and their
.console forms to the owner's Console cap. The broker runs the section 3
merge, the plan digest (pack-plan-stale on a mismatch) and the held
credential check. The organization writer keeps packs, board and
workflows out of the generic write and refuses definition writes to
every actor name an agent carries, project agents included.
mosaic org pack list|preview|apply, board set and workflow set|remove
go over the organization's bus as its human and refuse inside an agent
shell before any file is read. The Console serves GET /api/orgs/<id>/packs,
packs/preview, packs/apply, board and workflows/<name> (POST and
DELETE) to the root owner's session only, writes under --send only.
Build review (changes 8) and security review (changes 4):
- P1-1: an owner-approved fire no longer writes the revoke before the
apply (decision-stale). The fire stops the copy's runs, the fire is
written, then the retire revoke starts for the fired instance.
- P2 (both): archive and delete from the CLI and the Console pass the
organization's managed Gitea remote step (copyRemoteFor). A remote
step that was not attempted or failed keeps the record revoking with
failed remote, so the bus finishes it after restore; no done tombstone
over a live token.
- Security P2-1: a bound server that cannot be read is a failed remote
step, not not-managed. updateOrg refuses project-repo-bound when a
project a copy binding grants loses or changes its remote or git.
- P3-1 (security): a delete whose organization file cannot be read says
copiesUnreadable, never an empty copy list.
- P3-2 (security): the remote step requires the record's incarnation to
equal the registry's live one.
- P3-5 (build): the archive's slot discard checks each parent directory
(no symlink, own uid, private mode).
- P3-6 (build): resume keeps credential-revoke-failed as the gate code
while a failed op runs again.
- P3-4 (build): a test pins the refused-begin reopen guard.
Remote mode sets the project checkout's origin and checks it once with
git ls-remote, with no credential, no prompt, a protocol allowlist of
https and ssh and a 15 second limit. The receipt reports reachable,
needs-credential or unreachable with git's error summarized. Nothing is
pushed. URLs are https or ssh only; ext:: and other helpers, file://,
local paths, option-like values and credential-bearing URLs are refused.
`mosaic org project repo set` changes a project's repository later, both
ways between remote and local only, through the same rules and check,
with a revision guard and an org.project.repo.set receipt. The Console's
organization project Settings tab edits it through
/api/orgs/ID/repo/project/PROJECT; Quick Setup accepts ssh and shows the
check.
mosaic org role-templates|hire|change|fire and mosaic org autonomy
[set|review] send the hire form's and the autonomy panel's own requests
to the running Console's org route, so the Console runs the same owner,
send, origin, body and revision checks and the same bus verbs. They
refuse inside an agent run after their usage checks.
mosaic tasks create|edit|archive pass the board's parseTaskWrite and go
to the bus as the person (tasks.create|edit|archive).
Docs: cli README, docs/TOOLS.md, webui README Console actions table.
P1-4: org restore starts the bus as mosaic bus start ID --dev-local-owner
--detach would, and its receipt names the bus. Where that start is refused
(deployed or stamped instance, agent run) restore refuses before changing
anything; --no-bus keeps the old restore. The Console Restore action
restores and starts the bus with one receipt, and offers Restart bus when
the start fails after a good restore.
P1-5: Quick Setup refreshes the sidebar organization list, and the
organization dashboard redraws on the config event Restart bus already
dispatches, so the bus line and System types follow without a reload.
org delete checks --if-revision before --confirm again, so a bad revision
without --confirm says "invalid revision" as it did on BASE.
The archive and delete "bus untouched" check moves from the status test,
whose fixture has no owner file, to the Restart bus test, which has a real
organization and a live bus. An agent run's archive and delete exit 3 and
leave the bus pid and the organization unchanged; the same archive from a
human shell stops the host, and restore makes the organization active again.
Build review finding 2: org archive and org delete stop the organization's
bus, so they now refuse inside an agent run like bus stop, after their usage
checks and before any config is read. Finding 3 (also security P3): the
--pm/--no-launch and Console LAN flag checks now come before the refusal,
so a usage error exits 4 as the docs say.
Roles round 2, slice T1 (roles-and-rails design sections 4, 5 and 13).
- The org file's definitions key: each entry {rev, from?, role, instructions,
personality?} passes checkComposition; a bad entry refuses the whole file,
naming the definition and key, never a value.
- org:<name> instance references resolve to the file's definition; a missing
one refuses definition-missing, naming the instance. Template names are
unchanged.
- Template identity is the name plus a sha256 over the template JSON and its
contract; templateHashes reads the current hash for each template.
- The writer's setDefinition is the only path that changes definitions: it
checks the composition, bumps rev, refuses agent actors, and refuses
definition-in-use for a definition an instance names (A4 adds the change
decision). write() refuses organization-definitions-protected.
- Definition verbs import, edit, invent and remove need org.hire
(definition.*.console in VERB_RIGHTS) and refuse every agent caller.
- The diff read lists the primitives that changed and a line diff of the
instructions. It applies nothing.
- CLI: mosaic org definitions templates|list|show|diff|import|invent|edit|remove.
Sol review (org-redact5, changes 6):
- org-service: Node filesystem errors become "cannot <syscall> <label> (<CODE>)";
code, syscall, deleteStep and deleteReceipt stay. git failures name the verb
and exit code, never git's stderr.
- org-console: consoleStop names "another checkout", not the entry path. Exit 3.
- config-command: the system config is named by where it came from. Exit 3.
- cli org: documents and raw fs errors are named by kind and code. Exit 2.
- instances: --vars file named by flag and kind. Exit 4.
- hosts: a refused command is named by the basename refusedCommand matched.
- role.mjs (T1): the change is back to the readJsonFile label only.
Sentinel tests for each; pins in last-values, rename-b-commands, org-recovery.
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.
GET /api/workspace on the dev-local-owner Console: one snapshot of
projects, sessions, inbox, tasks and per-org metrics across every
organization the owner belongs to. Membership reuses the org list's
admission (organizations.members); each org is read over the same
per-org options the org pages use, with one deadline per org (4000 ms)
and a state (ok, down, starting, timeout, invalid) with a time. Every
row carries its org id; decide commands carry --org. Granted users
and the tester get 403 owner-only. Polling at 10 s, no push.
mosaic console restart and mosaic org reload <id> call the Console's
F3 routes (POST /api/console/restart, POST /api/orgs/<id>/restart/bus)
so gating, proof and trail are the Console's own; both refuse inside
an agent run first.
The cross-org assistant read is designed and stopped: it needs a new
broker read verb or role authority.
mosaic org config validate|resolve runs runConfigCommand
(packages/organization/src/config-command.mjs), the callable service the
old mosaic business validate|resolve executable now calls too. The service
returns the exit code, never exits the process and does nothing on import.
The spelling sets only the stderr prefix and the usage text; status and
stdout are byte-identical, and the v1 output keeps the business key.
mosaic org instances|instance|veto takes the same arguments as
mosaic business instances|instance|veto and runs the same proof-bearing
instances command. It is dispatched before orgCommand, so it never builds
the dev-local-owner context. mosaic org validate FILE is unchanged.
Help, the UI labels for agent actions and instance reasons, and the
current docs now use the org spelling and the packages/organization paths.
Two webui test regexes follow the label text. packages/cli/tests/
rename-b-commands.test.mjs compares the two spellings golden for golden
on status, stdout, stderr and authorization outcomes, and covers a draft
file named like an installed ID.
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.
console start and stop move a console.json whose recorded process is gone
aside instead of refusing (P19 #857). mosaic org lock show|clear reports a
lifecycle lock holder and clears it only when the holder is dead.
mosaic bus recover marks a bus host stopped when its process is gone and its
socket does not answer, and writes a recovery receipt (P19 #862).
mosaic org delete ID --confirm ID --if-revision N, and POST /api/orgs/:id/delete,
mark the organization as deleting, stop its bus, move its files into
<dataRoot>/deleted/<ID>-<stamp>/ with a receipt, then drop it from the registry
and append deleted.log. A failure stops at its step and a rerun resumes. Bus
start refuses an organization that is being deleted. The settings page asks
twice: a panel that lists the effects, then typing the ID before Delete
permanently is enabled. Purge and remote revocation are follow-ups.