docs(plans): starter packs design; system-type toolsets design superseded

Design only, per Jason's 2026-11-12 starter-packs ruling. Inventory of
existing building blocks, pack format, apply and re-apply, customizing
from inside, saved templates, system-type retirement, five shipped
packs, build slices and three open questions.
This commit is contained in:
jason.woltje committed 2026-11-12 14:25:04 -06:00
1 parent 2ad4b30ebd
commit bf65ea9802
2 files changed
+573

No files matched your search

@@ -1,5 +1,7 @@
# System-type toolsets at launch
Superseded 2026-11-12 by `docs/plans/2026-11-12_starter-packs-design.md` under Jason's starter-packs ruling; do not build this design.
Status: design only. Nothing here is built and no `roles/` or contract file is
changed. Prepared 2026-11-10 against `refactor` `8630b8dfc` for phase P2 of
#1873 (unified workspace). PRD requirement: REQ-SYS-2, "An agent working in a
@@ -0,0 +1,571 @@
# Starter packs
Status: design only. Nothing here is built. No `roles/`, `contracts/` or
schema file is changed. Prepared 2026-11-12 against `refactor` `2ad4b30eb`.
It replaces `docs/plans/2026-11-10_system-type-toolsets-design.md`, which is
superseded and must not be built.
## The ruling
Jason, 2026-11-12, about 20:05Z:
> I agree. Starter packs instead of over-restrictive configurations. We
> provide a handful of pre-configured options for system types, then the user
> can customize to their needs. Agent roles will be the main limiter or
> permitter of actual capabilities in the system. The default starter packs
> can affect the various Kanban styles, workflow templates, agent roles, and
> other primitives in the system. The user and their agents will be able to
> customize the system operations by allowing the import and implementation of
> the various roles, workflows, and other available items as needed. The user
> will also have the option to create new workflows within their system. The
> agent roles will be customizable within the absolute constraints that are
> offered. We have discussed that no agent can grant permissions beyond those
> they already possess. The org or project configurations will establish the
> "sandbox" for agent operations. The user and chief/assistant can work
> together to customize the org/project as needed to accomplish a goal. The
> user can also save a custom org/project template for use repeatedly to meet
> their needs.
Earlier he said a system type is there "to provide a basic configuration using
templates for agent setup and system function", and that the user customizes
the system while working inside it. He judged the toolset gating design
counterproductive.
What this design takes from that:
- A pack is a starting point that is copied in once. It is not a switch that
stays on and gates what agents can do.
- Roles are the limit. A pack grants nothing; every agent it proposes is hired
through the existing hire decision, bounded by the chief's own role.
- The organization or project is the sandbox. Nothing a pack, a saved
template or an agent does reaches outside it.
- Customizing from inside uses the paths that already exist: the T1 definition
verbs for people, hire and change decisions for the chief, and the chief's
`organization.change` grant for settings that carry no capability. No new
human gate is added.
## 1. Inventory
Status means: **exists** (built and usable by a pack as is), **partial**
(built, but a pack cannot set it or it lacks a piece a pack needs), **missing**
(not found in the code).
| Item | Where | Status | Notes |
|---|---|---|---|
| System-type plug-in kind | `packages/plugins/src/kinds.mjs:84-86` (`switch: "list"`, export `systemType`) | exists | A pure label today. |
| The five system-type modules | `packages/plugins/system-type/{coding,creative-writing,marketing,montage,business-management}/index.mjs`, `systemType()` | partial | Each returns `{id, label, description, toolsets: []}`. Nothing else. |
| Loading system types | `packages/plugins/src/system-types.mjs:5` `installedSystemTypes` | exists | Any refused module, or keys other than `description,id,label,toolsets`, throws `system-type-invalid`. |
| Org enable | `plugins['system-type']` in the org file; checked at `packages/organization/src/organization-config.mjs:360-365`; fenced by `systemTypesFence`, `packages/organization/src/organization.mjs:126-131` (`organization-system-types-human-only`) | exists | Read back by `system-types.current`, `packages/bus/src/runtime.mjs:904`. Console `GET/POST /api/system-types` (`packages/webui/src/system-types-http.mjs:4-9`), right `org.setup` (`packages/organization/src/org-rights.mjs:90`). |
| Project selection | `project.system-types[.set]`, `packages/bus/src/broker.mjs:8476-8495`; table `project_system_types` in `packages/bus/src/migrations/3t-3u.sql` (append-only) | exists | Human only. CLI `mosaic project system-types` and `system-types-set`, `packages/cli/src/project.mjs:17-18`. |
| System type effect on agents | launcher, harness, `packages/organization/src/resolve.mjs` | missing | No launch path reads system types (no hits in those files). Every module's `toolsets` is empty. |
| Shipped role templates | `roles/*.json` + `.md` | exists | V2 roles: assistant, chief-executive-officer, chief-financial-officer, chief-information-officer, chief-information-security-officer, chief-marketing-officer, chief-of-staff, coder, companion, continuity, cto, fact-checker, ideas, merge-gate, pm, proofreader, research, reviewer, security-reviewer. `researcher.json` is V1 and refused by `readTemplate`. |
| Template identity | `packages/organization/src/definitions.mjs:174,180` `templateHash`, `templateHashes` | exists | Name plus sha256. |
| Org definitions: import, invent, edit, remove | `packages/organization/src/definition-verbs.mjs:61` `definitionVerbs`; CLI `mosaic org definitions ...` (`packages/cli/src/org-definitions.mjs:15`) | exists | `authorize` (line 38) refuses any agent (`human-required`) and needs `org.hire`. Writes go through `writer.setDefinition`; a definition in use refuses `definition-in-use`. |
| Template diff | `packages/organization/src/definitions.mjs:252` `diffDefinition` | exists | Compares a copy with its template's current text. |
| Composition check | `packages/organization/src/composition.mjs:443` `checkComposition` | exists | Refuses hard waits (R4). Flags every line above the chief's own role (tools, network, actions, credentials, messaging) and every resolve right. |
| Plug-in role templates | `roleTemplates` in `packages/plugins/src/manifest.mjs:48` `checkManifest`; read by `packages/organization/src/composition.mjs` | partial | A PL1 manifest may declare templates with plugin-prefixed names. I did not find the hire path reading them. |
| Hire as a decision | `packages/organization/src/hire-decisions.mjs:65` `hireDecisions` (`prepare`); `authorizeHire` line 32; broker `#raiseHire`, `packages/bus/src/broker.mjs:6578` | exists | `instance.hire` fails `decision-required` (6236). Only the chief raises (`role-not-chief`, 6583). A human with `org.hire` starts and approves in one step through `hire.start.console`. Flagged and credential lines need the owner; root needs the instance owner. An approved hire writes definition `hire-<decision>` and the instance (hire-decisions.mjs lines 307-309). |
| Autonomy setting | `packages/organization/src/autonomy.mjs:2-4` | exists | Classes cross-role, merge, policy, authority. Authority defaults to in the loop. Owner-only (`autonomy.*` null in `VERB_RIGHTS`). |
| The chief's own settings grant | `organization.change` in `roles/chief-of-staff.json`; broker `organization.set`, `packages/bus/src/broker.mjs:6247-6340` | exists | The chief changes limits directly under its within-role grant. The trail records each change. |
| Task board columns | `STORE_STATES`, `packages/bus/src/broker.mjs:334`; `packages/tasks/src/startup.mjs:6` `BUCKETS`; `packages/webui/src/public/tasks-board.js:21` `STATES` | partial | Five fixed states (todo, in-progress, in-review, blocked, done) with fixed labels. Nothing configures them. |
| Kanban styles | `packages/webui/src/public/tasks-board.js:146` `kanban`, `:156` `list` | missing | Two layouts (REQ-WS-8). No per-org or per-project style, label or order. |
| Workflow templates | grep for "workflow" in `packages`, `scripts`, `roles`, `contracts`, `missions` | missing | Only refusal text in `packages/bus/src/refusals.mjs`. No workflow document, store or engine. |
| Mission and task templates | `missions/hello.json`; `tasks/*.json`; schemas in `scripts/mosaic-task.mjs:94,139` | partial | These drive headless worker runs (`run-task.sh`), not org operations. `missions/` holds one file. A pack does not carry them. |
| Skills | repo `skills/` (16 skills); `scripts/skill.sh` install/activate into `<dataRoot>/skills-enabled`; role `skills` key resolved in `packages/harness/src/bundle.mjs:126-192` | exists | Machine-scoped. A role names skills; launch refuses a missing one. A pack can name skills but cannot install them. |
| Plug-ins (code) | `packages/plugins/src/loader.mjs:123` `loadInstalled`; `packages/plugins/src/install.mjs:71` `installPlugin` (hash pin, read-only files) | exists | A module runs with the host's authority (loader header). Install is an instance-owner act (R10). |
| Workspace and dashboard sections | `packages/webui/src/workspace-read.mjs:13` `WORKSPACE_SECTIONS` | partial | Fixed: projects, sessions, inbox, tasks, metrics. No per-org configuration found. |
| `mosaic org template render` | `packages/organization/src/org-service.mjs:92` `templateRender`; `packages/organization/src/org-template.json` | exists | Renders human, chief, assistant and one `work` project. Presets from harness and model-provider modules (`discoverPresets`, line 61) overlay `plugins`, `launch` and assistant vars. Console `GET /api/org-template`, `/api/org-presets` (`packages/webui/src/org-admin.mjs:413-419`). |
| Project settings | org file `projects.<id>` keys root, repo, remote, git, agents (`packages/organization/src/organization-config.mjs:306`); repo file `.mosaic/project.json` (`packages/organization/src/project.mjs`, read only) | partial | No board or workflow setting per project. |
| Agent levels | `packages/organization/src/agent-levels.mjs` (`AGENT_LEVELS` line 15, `checkProjectAgents` line 111, `resolveAgentLevels` line 207) | exists | Root and project definitions refuse bound keys: credentials, holder, tracker, identity, publish, hosts (line 21). |
| Copy credential bindings | `copyBindings` fence, `packages/organization/src/organization.mjs:32-36`; writer at line 550 | exists | Owner-approved binding decision only. |
| Pack format, apply, saved templates | none | missing | This design. |
Missing building blocks, plainly: workflow templates, Kanban styles (labels,
order, hidden columns), any per-org or per-project board setting, a pack
format, a pack apply path, and saved org or project templates. Montage has no
video roles or tools in the repository; the OpenMontage integration was not
found.
## 2. What a pack is
A pack is a declarative JSON document. It lists items to copy into an
organization or a project. It never runs, never stays switched on, and grants
nothing.
### Where packs live
| Source | Location | Who adds one |
|---|---|---|
| Shipped | Core plug-in modules `packages/plugins/starter-pack/<name>/index.mjs`, exporting `interfaceVersion = 1` and `starterPack()`, which returns the document | Reviewed commit |
| Installed | PL1 hash-pinned plug-ins under `<dataRoot>/plugins`, kind `starter-pack` | Instance owner, at the host (R10) |
| Saved by a user | `<configDir>/templates/<account>/<name>.json`, mode 0600, written with `wx` (never overwritten) | The organization's owner, through `template.save` (section 5) |
Shipped and installed packs follow the loader exactly: the same component and
symlink checks, the same refusal codes, a refused installed pack omitted and
logged, a refused core pack failing the listing as system types do today. A
saved template is data only. It is read with `parseJsonFileText`, never
imported or run, capped at 256 KiB, and passes the same `checkPack` as a
shipped pack.
### The document (packVersion 1)
```json
{
"packVersion": 1,
"id": "coding",
"version": "1.0.0",
"label": "Coding",
"description": "A small software team with review before merge.",
"scope": "org",
"definitions": {
"coder": { "template": "coder", "sha256": "<64 hex>" },
"release-writer": {
"from": { "template": "coder", "sha256": "<64 hex>" },
"role": { "tools": ["read", "edit"], "authority": { "withinRole": ["record.write"] } },
"instructions": "...",
"personality": { "summary": "..." }
}
},
"hires": [
{ "name": "coder-1", "definition": "coder", "level": "org", "reason": "writes the code" }
],
"board": {
"columns": [
{ "state": "todo", "label": "Backlog" },
{ "state": "in-progress", "label": "Building" },
{ "state": "in-review", "label": "Review" },
{ "state": "blocked", "label": "Blocked" },
{ "state": "done", "label": "Merged" }
],
"layout": "kanban"
},
"workflows": {
"change": {
"description": "One change from task to merge.",
"steps": [
{ "id": "plan", "title": "Write the task", "role": "pm", "state": "todo" },
{ "id": "build", "title": "Implement", "role": "coder", "state": "in-progress" },
{ "id": "review", "title": "Review", "role": "reviewer", "state": "in-review" },
{ "id": "merge", "title": "Merge", "role": "merge-gate", "state": "done" }
]
}
},
"skills": ["ms-sdlc", "ms-unslop"]
}
```
`checkPack` (new, in `packages/plugins/src/starter-pack.mjs`) refuses with
`starter-pack-invalid` unless all of these hold:
- Exact top-level keys as above. `scope` is `org` or `project`.
- `id` matches `NAME` (`packages/plugins/src/kinds.mjs:20`). `version` is a
semver string.
- Each `definitions` entry is one of two shapes:
- A template reference `{template, sha256}`. The name must be a V2 role in
`roles/` and the hash must equal `templateHash` now. A stale pin refuses
`starter-pack-template-changed`, so a pack never imports text nobody
reviewed against it.
- A full entry in the T1 shape (`ENTRY_KEYS`, `definitions.mjs:30`). It
must name `from` and pass `checkDefinition`. Its role must narrow the
template named in `from`, line by line, under the new
`narrowsTemplate(role, template)`: every tool, action, skill, messaging
target and credential line is in the template, and the network is no
wider. This is "no grant above the templates". A shipped or installed pack
may not carry a role without `from`.
- `hires`: at most 8. Each `name` passes `requireId`. Each `definition` names
an entry in this pack's `definitions` or an existing V2 template. `level`
is `org` or `project`, never `root`.
- `board.columns` holds the five `STORE_STATES`, each exactly once, in any
order, each with a label of 1 to 32 characters. `layout` is `kanban` or
`list`. The tracker contract does not change (REQ-TASK-1, REQ-WS-8).
- Each `workflows` entry has 1 to 20 steps. Each step's `state` is in
`STORE_STATES` and its `role` names a definition in the pack or a template.
- `skills` entries match `NAME`. They are a list of what the roles need, not
an install.
- Every string passes the credential store's `assertClean`, as definitions do
today (`checkDefinition`, `definitions.mjs:81`).
### What a pack never contains
The schema has no key for any of these, so a pack carrying one refuses:
- credentials, credential bindings, tokens, tracker bots, identity, publish,
holder or hosts;
- members, rights or the owner;
- the autonomy setting or arbiters;
- plug-in selections (`plugins`), including the task tracker and theme;
- `launch` (models, limits, keep, noHire, account);
- a root-level hire;
- any role line outside the `roles/` rails. `checkComposition` still runs on
every definition at apply time and refuses a hard wait (R4) and `role.hire`
or `role.fire` in a hired composition (R9).
### Versioning
`packVersion` is the schema version; an unknown one refuses. `version` is the
pack's own release. An apply records `{id, version, sha256}` where `sha256`
is the hash of the canonical JSON that was applied. A new pack version is a
new document; it never edits an organization that applied an older one.
## 3. Applying a pack
### What apply does
Apply copies items in once. After that the organization or project owns them,
exactly as if a person had made them by hand. The steps:
1. **Preview.** `pack.preview` reads the pack and the organization and returns
a plan: each item marked `add`, `same` or `kept` (section 3, re-apply), the
flagged lines each proposed hire would carry against the current chief, the
skills not enabled on this machine, and a digest of the plan plus the
organization file revision. Nothing is written.
2. **Apply.** `pack.apply` takes the digest. It refuses `pack-plan-stale` if
the revision or plan changed. In one organization-file write it:
- writes each `add` definition through `writer.setDefinition`, with `from`
kept, so `checkComposition` runs and `rev` starts at 1;
- writes the `board` and `workflows` items marked `add`;
- appends `{id, version, sha256, scope, project, at, by, proposedHires}` to
a new `packs` key.
3. **Hires.** Apply never hires. It leaves the pack's hires as proposals in
the `packs` entry. Each becomes a hire through the existing paths:
- the chief raises `hire.raise` (`broker.mjs:6237`) with role
`{definition, rev}`; the broker computes the flagged and credential
lines, and the decision routes by the authority autonomy level;
- or a person with `org.hire` starts it with `hire.start.console`, which
shows the same lines and needs the owner for any flagged one.
A proposal is filled when an instance by that name exists. A pack applied
to a project proposes project-level hires (`level: "project"`), which land
in `projects.<p>.agents` and refuse bound keys
(`agent-levels.mjs:21`).
### The surface
| Layer | Name |
|---|---|
| CLI (owner verb) | `mosaic org pack list`, `mosaic org pack preview ID PACK [--project P]`, `mosaic org pack apply ID PACK --digest D [--project P]` |
| Console route | `GET /api/packs`, `POST /api/orgs/<id>/packs/preview`, `POST /api/orgs/<id>/packs/apply` |
| Bus ops | `packs` (read), `pack.preview.console`, `pack.apply.console`; and for the chief, `pack.preview` and `pack.apply` under `organization.change` |
| `VERB_RIGHTS` | `packs` to `board.read`; `pack.preview.console` and `pack.apply.console` to `org.hire` |
`org.hire` fits because the only parts of a pack that could ever matter for
capability are role definitions, and T1 already gives definition writes to
`org.hire`. Board and workflows carry no capability.
### The chief applying a pack
The roles-and-rails design says the chief proposes a definition only inside a
decision (section 5). The chief's `pack.apply` respects that:
- it writes `board` and `workflows` directly under the chief's
`organization.change` grant, the same way `organization.set` writes limits
today (`broker.mjs:6247-6340`);
- it writes no definitions. Each proposed hire the chief raises carries the
pack's composition as role `{inline}` or `{template, sha256}`, so the broker
computes the lines against the chief and the approved hire writes
`hire-<decision>` as it does now.
### A second pack, and re-applying
Items merge by name and nothing is overwritten:
| Item in the pack | Organization already has | Result |
|---|---|---|
| Definition | nothing by that name | `add` |
| Definition | same composition, instructions and personality | `same`, no write |
| Definition | different (edited, or a newer pack version) | `kept`. The preview shows `diffDefinition`. The person may import the pack's version under another name with `--as`. |
| Board | no `board` key | `add` |
| Board | a different board | `kept` |
| Workflow | nothing by that name | `add` |
| Workflow | a different workflow by that name | `kept`, with `--as` |
| Proposed hire | an instance or open hire decision by that name | dropped from the new proposals |
There is no forced overwrite flag. To take a newer pack version of a definition
a hired agent uses, the person or chief raises a change decision, which is the
existing path.
### Removing a pack
Removing a pack takes nothing away. Items were copied, so the organization
keeps them. The `packs` entry stays as history. Uninstalling an installed pack
plug-in uses `removePlugin` (`install.mjs:99`); a pack is never "enabled", so
`plugin-enabled` never refuses it, and no organization changes.
## 4. Customizing from inside
The ruling asks that the user and their agents can import roles, workflows and
other items, create new workflows, and customize roles inside fixed limits.
The table shows who does what. Every row reuses a path that exists or adds a
verb at the same level as its neighbours. None adds a person to a path that
had none.
| Change | A person does it directly | The chief does it | The limit at that step |
|---|---|---|---|
| Import a role template | `definition.import` (T1, `org.hire`) | Inside a hire or change decision, role `{template, sha256}` | `checkComposition`. Nothing runs until a hire. |
| Customize or invent a role | `definition.edit` or `invent` (T1) | Inside a hire or change decision, role `{inline}` | `checkComposition`; lines above the chief are flagged and only the owner approves them (R8). Editing a definition in use refuses, so a running agent never widens without a change decision. |
| Hire, fire, change an agent | `hire.start.console` (`org.hire`) | `hire.raise` (only the chief, R9) | Broker-computed flagged and credential lines; authority autonomy level; root needs the instance owner. |
| Apply a pack | `pack.apply.console` (`org.hire`) | `pack.apply` under `organization.change`, no definitions | As above for every hire it later proposes. |
| Import a workflow or board from a pack | Part of apply | Part of the chief's apply | No capability. |
| Create or edit a workflow | `workflow.set.console` (`org.hire`) | `workflow.set` under `organization.change` | Steps name existing definitions or templates and the five states. A workflow grants nothing. |
| Change board labels or order | `board.set.console` (`org.hire`) | `board.set` under `organization.change` | The five states stay. |
| Project agents, board and workflow | The same verbs with `--project` | The same, under `organization.change` | Project agents refuse bound keys (`agent-levels.mjs:21`). |
| Enable a skill on the machine | `scripts/skill.sh activate` (instance owner) | Not available | Machine-scoped. |
| Install a pack plug-in | `installPlugin` (instance owner, R10) | Not available | Host authority. |
| Autonomy level | Owner only | Never (R12) | Unchanged. |
**"No agent grants beyond its own permissions", step by step.**
1. A pack: its definitions narrow the shipped templates (`narrowsTemplate`).
2. Apply: definitions pass `checkComposition`; apply writes no instance.
3. The chief's proposal: the broker, not the chief, computes the flagged lines
against the chief's own role (`checkComposition` step 7,
`composition.mjs:491`). The chief cannot omit them.
4. Resolve: an agent resolver can resolve only a plain hire with no flagged
or credential lines, not at root, not about itself (`authorizeHire`,
`hire-decisions.mjs:39-42`). Anything flagged goes to the owner.
5. A hired agent: `role.hire` and `role.fire` refuse in its composition (R9),
so it cannot hire in turn.
6. Saved templates go through steps 2 to 5 again in the new organization.
**The sandbox.** Every agent a pack proposes is an org or project agent. It
runs in one organization, sandboxed as the instance uid with only its role's
tools (R6, R7). A project agent sees only that project's workspace. A pack
cannot name another organization, a host, a credential or a root-level hire.
**The assistant.** The assistant does not hire, direct agents or assign tasks
(REQ-ASSIST-1). It helps the user pick and preview a pack and passes the
request to the chief. It gets no new verb.
## 5. Saving a template
### The verb
| Layer | Name |
|---|---|
| CLI (owner verb) | `mosaic org template save ID NAME [--project P] --digest D`, with a preview `mosaic org template save ID NAME --preview` |
| Console route | `POST /api/orgs/<id>/templates/preview`, `POST /api/orgs/<id>/templates` |
| Bus op | `template.save.console`, `VERB_RIGHTS` null (owner only) |
| Lists | `mosaic org template list`, `GET /api/templates`: only the caller's own |
Owner only, because a saved template copies the organization's role text,
which is the owner's work product, out of the organization. The preview shows
the exact JSON that will be written; save takes its digest.
### What is captured
- `definitions`: role, instructions, personality and `from`. Definitions named
`hire-<decision>` are renamed after the instance that uses them, so no
decision id leaves the organization.
- `hires`: one proposal per org instance (or project agent, with `--project`):
instance name, definition and level. Never the chief or the assistant,
which every organization already has.
- `board` and `workflows`.
- `skills`: the union of the captured roles' `skills`.
- `packs`: the ids and versions applied before, as provenance text.
The result is a pack document with `source: "saved"`. A role in a saved
template may lack `from` (an invented role); that is the one difference from a
shipped pack, and it still passes `checkComposition` on apply.
### What is stripped
Everything not listed above. In particular:
- the owner, members, accounts and rights;
- identities: `human`, instance `identity`, `publish`, `holder`, `hosts`,
tracker bot names and ids;
- credentials: role `credentials` lines, instance credentials, `copyBindings`
and their `copyId`, `orgIncarnation` and `generation`;
- `launch` (models, limits, keep, noHire, account, grant scope), `autonomy`,
`arbiters`, `plugins`, `appearance`, `usage`, `replyLimits`, `retired`;
- instance and organization `vars`;
- project roots, repos, remotes and git state;
- definition `rev` counters and `hireDecision` references;
- everything on the bus: run history, sessions, threads, tasks, decisions,
the trail.
Stripping role `credentials` lines means a template never carries a request
for a credential. A role that needs one gets it after hire through the
owner-approved binding, as today.
Before writing, save runs `assertClean` on every string, as definitions do.
A hit refuses `template-secret-suspected` and names the field path, never the
text.
### Applying one to a new organization
1. `mosaic org template render ID --output FILE`, then `mosaic org create ID
--from FILE`: the base organization with human, chief and assistant, as
today.
2. `mosaic org pack apply ID saved:<name> --digest D`: the same preview and
apply as a shipped pack. A saved pack applies only to an organization the
caller owns.
3. The chief raises the proposed hires.
There is no create-time shortcut that skips the hire decisions.
## 6. System types
**Decision: the kind is replaced.** The `system-type` kind becomes
`starter-pack`. A pack's `label` is what the UI shows as the system type. The
reasons:
- Nothing reads system types today except the enable and select verbs, and
every module's `toolsets` is empty. A list switch that grants nothing has no
job once packs exist.
- Keeping both would leave a second, empty configuration surface next to the
real one.
### Migration
- **Org files.** A print-only conversion in the style of `migrateLaunch`
(`packages/organization/src/migrate-launch.mjs`, decision 145) prints the
file without `plugins['system-type']`. The owner applies it through the
organization writer, so the write gets the usual receipt and the trail
event keeps the old list as evidence. Nothing is applied for it, since the
old modules carried nothing. The Console offers the matching packs once.
After the kind is removed, the existing unknown-module check
(`organization-config.mjs:360-365`) refuses a file that still names
`system-type`, so an unmigrated file fails closed. The refusal text points
at the conversion.
- **`project_system_types`.** The table stays, rows and triggers unchanged
(write-once, R2). `project.system-types.set` refuses with a new code,
`verb-retired` (not found today). The read verb keeps working for history
until a later PRD removes it. No DDL change.
- **Code removed.** `systemTypesFence`, `system-types.console`, the system
types Console route and CLI words, and `installedSystemTypes`, each with its
tests. The five modules move to `packages/plugins/starter-pack/`.
### REQ-SYS-1 and REQ-SYS-2
Current (PRD 1.20, `docs/prd/mosaic-stack.md:1247-1252`):
- REQ-SYS-1: "System types are a plug-in kind. Its modules include coding,
creative writing, marketing, Mosaic Montage (the OpenMontage fork) and
business management. An organization enables a list of them."
- REQ-SYS-2: "A project may use any system type its organization enables, and
none is required. An agent working in a project gets the toolsets of the
system types that project uses."
Proposed:
- **REQ-SYS-1.** Starter packs are a plug-in kind. Mosaic Stack ships five:
coding, creative writing, marketing, Mosaic Montage and business
management. A pack is a starting configuration of role definitions,
proposed hires, a board style and workflow templates. An organization or
project copies a pack in once and then owns and changes what it copied. A
user can save an organization or project as their own template and apply it
the same way.
- **REQ-SYS-2.** Applying a pack grants nothing by itself. Every agent it
proposes is hired through the hire decision (REQ-AGENT-3), bounded by the
chief's own role, with any line above it approved by the owner. A pack never
sets credentials, rights, the autonomy setting or plug-in choices. Removing
a pack removes nothing it copied.
## 7. The five shipped packs
Every organization already has a chief and an assistant from
`org-template.json`, so no pack lists them. Columns map onto the five fixed
states in the order todo, in-progress, in-review, blocked, done. Every role
named is a shipped V2 template.
### Coding
| Item | Contents |
|---|---|
| Definitions | `coder`, `reviewer`, `security-reviewer`, `pm`, `merge-gate`, `cto` (template references) |
| Proposed hires | `pm-1` (pm), `coder-1` (coder), `reviewer-1` (reviewer) |
| Board | Backlog, Building, Review, Blocked, Merged; kanban |
| Workflows | `change`: pm writes the task, coder implements, reviewer reviews, merge-gate merges |
| Skills | ms-sdlc, ms-unslop, ms-file-read, ms-file-write, ms-goal, ms-communications |
`cto` holds `decision.resolve.technical`, a resolve right, so it is flagged on
hire and the owner approves it. It is a definition, not a proposed hire.
### Creative writing
| Item | Contents |
|---|---|
| Definitions | `ideas`, `research`, `proofreader`, `fact-checker`, `continuity`, `companion` |
| Proposed hires | `ideas-1` (ideas), `proofreader-1` (proofreader), `continuity-1` (continuity) |
| Board | Ideas, Drafting, Editing, On hold, Finished; kanban |
| Workflows | `chapter`: ideas outlines, the user or companion drafts, continuity checks, proofreader edits |
| Skills | ms-unslop |
### Marketing
| Item | Contents |
|---|---|
| Definitions | `chief-marketing-officer`, `research`, `ideas`, `proofreader`, `fact-checker` |
| Proposed hires | `cmo` (chief-marketing-officer), `research-1` (research), `proofreader-1` (proofreader) |
| Board | Ideas, In progress, Approval, Blocked, Live; kanban |
| Workflows | `campaign`: research finds the angle, ideas drafts, fact-checker checks claims, proofreader edits, the user approves publishing |
| Skills | ms-unslop |
Publishing outside the organization is `message.external` or a hard wait; no
role here holds it, so the user publishes.
### Mosaic Montage
| Item | Contents |
|---|---|
| Definitions | `ideas`, `research`, `proofreader`, `reviewer` |
| Proposed hires | `ideas-1` (ideas), `research-1` (research) |
| Board | Concept, Script, Review, Blocked, Final; kanban |
| Workflows | `episode`: research gathers sources, ideas writes the script, proofreader edits, reviewer checks |
| Skills | ms-unslop |
This pack is a placeholder for scripting work. No video or montage role or
tool exists in the repository, and the OpenMontage integration was not found.
The module name is still open (PRD 1.20 open question 1).
### Business management
| Item | Contents |
|---|---|
| Definitions | `chief-executive-officer`, `chief-financial-officer`, `chief-information-officer`, `chief-information-security-officer`, `pm`, `research`, `continuity` |
| Proposed hires | `pm-1` (pm), `research-1` (research) |
| Board | To do, Doing, Waiting on review, Blocked, Done; list |
| Workflows | `decision-brief`: research gathers facts, pm writes the task, the relevant officer drafts, the user decides |
| Skills | ms-unslop, ms-goal, ms-communications |
The officer roles hold only `message.send` and `record.write` today
(`roles/chief-*.json`). They are definitions to hire when wanted, not
proposals.
## 8. Build slices
Each slice lands with its own tests. "Sign-off" marks a slice that touches
`roles/`, `contracts/`, a schema or a security boundary; Jason approves it
before build.
| Slice | Scope | Acceptance | Test that fails first | Sign-off |
|---|---|---|---|---|
| SP1 | `checkPack` and `narrowsTemplate` in `packages/plugins/src/starter-pack.mjs`; kind `starter-pack` in `kinds.mjs`; loader listing | A valid pack loads; each forbidden key, stale template hash, role wider than its template, root hire, ninth hire, bad state and secret-shaped string refuses with its code | `packages/plugins/tests/starter-pack.test.mjs`: a pack with `credentials` in a definition loads (red until `checkPack` exists) | Yes: new plug-in kind, a pack schema |
| SP2 | The five shipped pack modules | Each loads through SP1; every template pin equals `templateHash` now; no proposed hire carries a flagged line against `roles/chief-of-staff.json` | A test that loads `starter-pack/coding` and checks its pins against `templateHashes` | No (data only, built on SP1) |
| SP3 | Org file keys `packs`, `board`, `workflows`, `projects.<p>.board`, `projects.<p>.workflows` in `organization-config.mjs`; writer support | Unknown keys inside each refuse; board needs all five states once; workflow steps name known definitions or templates | `organization-config` test: a file with `board` refuses today as an unknown top key; flip to accept the valid shape and refuse the invalid ones | Yes: org file schema |
| SP4 | `pack.preview` and `pack.apply` (owner verbs, bus ops, `VERB_RIGHTS`) | Preview writes nothing; apply writes add items only and the `packs` entry; stale digest refuses; second apply of the same pack is all `same`; an edited definition is `kept`; apply raises no decision | Broker test: `pack.apply.console` by a caller without `org.hire` refuses `right-required` (red until the verb exists), then the re-apply case | Yes: new verbs in `VERB_RIGHTS` |
| SP5 | The chief's `pack.apply`, `board.set` and `workflow.set` under `organization.change`; proposed hires surfaced in `packs` | The chief's apply writes no definition; a hire the chief raises from a proposal carries broker-computed flagged lines; a hired non-chief agent's apply refuses `not-authorized` | Broker test: a coder session calling `pack.apply` refuses (red until the authority check is wired) | Yes: chief authority surface |
| SP6 | Console: pack picker, preview, apply; board labels on `tasks-board.js`; workflow view and editor | Board reads labels and order from the org or project; no label set means today's labels | `tasks-board` test: an org with a `board` shows its labels (red today, labels are fixed at line 21) | No |
| SP7 | `template.save` and saved-template apply | Save writes a file with none of the stripped keys; a seeded secret refuses; list shows only the caller's own; apply of a saved template goes through SP4 | Test: save an org whose instance has `credentials` and `copyBindings`; assert neither key appears anywhere in the output (red until the stripper exists) | Yes: data leaving an organization |
| SP8 | System-type retirement: print-only conversion, retired verbs, PRD REQ-SYS-1/2 text, removal of `systemTypesFence` and the Console and CLI system-type surface | An org file with `plugins['system-type']` refuses until converted; the applied write's trail event holds the old list; `project.system-types.set` refuses `verb-retired`; `project_system_types` rows unchanged | Migration test with a fixture org holding `["coding"]` | Yes: PRD and data migration |
Order: SP1, SP2, SP3, SP4, SP5, SP6, SP7, SP8. SP8 can run after SP4 if the
Console needs the space earlier.
## 9. Open questions for Jason
1. **Retire system types as a separate kind?** This design replaces the
`system-type` kind with `starter-pack`, migrates org files and keeps
`project_system_types` as history. Recommendation: yes. The modules carry
nothing today, and two surfaces for one idea would confuse users.
2. **May the chief change board styles and workflows directly, as it changes
limits today?** This design puts `board.set`, `workflow.set` and the
non-definition half of `pack.apply` under the chief's existing
`organization.change` grant, with no decision. Recommendation: yes. They
carry no capability, the trail records each change, and a decision here
would be a new gate. Role and hire changes stay decisions.
3. **Board styles as labels and order over the five fixed states, or custom
states?** Custom states would change the task tracker contract, every
tracker module and the Vikunja bucket setup. Recommendation: labels, order
and layout only for now; revisit custom states when a pack needs one.