docs(plans): starter packs review round
Design review (8 findings) and security review (6 findings) fixed: expected flagged lines per proposed hire, applyPack writer op, loader-set origin, organization.change holders for board and workflow writes, filtered-view board labels, workflow prompt guidance (inert, no runner), system-type retired for one release before removal, installed packs classified as executable PL1 code, saved-template placeholders and free-text preview, pinned proposals, and removal tombstones.
This commit is contained in:
1 parent
bf65ea9802
commit
2912482cd0
1 file changed
+305
-107
@@ -55,8 +55,9 @@ Status means: **exists** (built and usable by a pack as is), **partial**
|
||||
| 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`). |
|
||||
| Org enable | `plugins['system-type']` in the org file; the kind is checked by `checkPlugins` (`packages/plugins/src/kinds.mjs:146-148`, `unknown-kind`, called at `packages/organization/src/organization-config.mjs:358`) and each module at `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`. |
|
||||
| Other system-type surfaces | CLI `mosaic org system-types installed\|enabled` (`packages/cli/src/org.mjs:5,15,49,51`); channel verbs (`packages/plugins/channel/cli/index.mjs:9-10`); Console page `packages/webui/src/public/system-types.js` with hooks in `workspace.js:173,193-194`, `chat.js:623`, `serve.mjs:23,85-86,826`, `webui/src/cli.mjs:9,193`; refusal `system-type-not-enabled` (`packages/bus/src/refusals.mjs:183`) | exists | All label plumbing; none affects an agent. |
|
||||
| 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. |
|
||||
@@ -64,19 +65,19 @@ Status means: **exists** (built and usable by a pack as is), **partial**
|
||||
| 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). |
|
||||
| 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. The definition name `hire-<decision>` is set at `hire-decisions.mjs:260`; `apply` (line 337 on) writes it and the instance. The input needs a `route` (`hire-decisions.mjs:198-199`). |
|
||||
| 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. |
|
||||
| Workflow templates | grep for "workflow" in `packages`, `scripts`, `roles`, `contracts`, `missions` | missing | No workflow document, store or engine. The one built-in flow is the fixed request, PRD and approval path (`packages/bus/src/refusals.mjs:194,251`, "Role workflow", "Role PRD workflow"); it is not pack-settable and this design leaves it alone. |
|
||||
| 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). |
|
||||
| Agent levels | `packages/organization/src/agent-levels.mjs` (`AGENT_LEVELS` line 15, `checkProjectAgents` line 111, `resolveAgentLevels` line 207) | exists | Root and project agent entries refuse the instance keys that bind an agent to one organization: credentials, holder, tracker, identity, publish, hosts (`BOUND_KEYS`, line 21; `noBoundKeys`). This is instance material, not the role's credential request lines. |
|
||||
| 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. |
|
||||
|
||||
@@ -89,8 +90,10 @@ 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.
|
||||
organization or a project. Once applied it is not a running module, it never
|
||||
stays switched on, and it grants nothing. How the document is produced
|
||||
differs by source (next subsection): a shipped or installed pack is code that
|
||||
returns the document.
|
||||
|
||||
### Where packs live
|
||||
|
||||
@@ -102,10 +105,29 @@ nothing.
|
||||
|
||||
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.
|
||||
logged, a refused core pack failing the listing as system types do today.
|
||||
|
||||
An installed pack is executable PL1 code. The loader imports its `index.mjs`
|
||||
(`loadPinnedModule`, `packages/plugins/src/loader.mjs:244-256`), and a loaded
|
||||
module runs with the host's authority (`loader.mjs:1-6`). Importing it can do
|
||||
anything the host can, before `checkPack` ever sees the document it returns.
|
||||
The hash pin proves the bytes are the ones the owner installed, not that they
|
||||
are safe. So installing a pack is the same trust act as installing any PL1
|
||||
plug-in: the instance owner does it at the host (`installPlugin`, R10), under
|
||||
the same host-authority warning. `checkPack` guards what a pack copies into
|
||||
an organization; it does not contain the code that produced it. Someone who
|
||||
wants to share a pack without that trust shares a saved template file, which
|
||||
is data only. A
|
||||
saved template is data only. It is read with `parseJsonFileText`
|
||||
(`packages/organization/src/util.mjs:66`), never imported or run, capped at
|
||||
256 KiB, and passes `checkPack` with origin `saved`.
|
||||
|
||||
The origin is set by the code that read the document, from where it was
|
||||
read: `shipped` for a core module, `installed` for a PL1 module, `saved` for a
|
||||
file under `<configDir>/templates/`. It is a closed set of those three values
|
||||
and never a field the document supplies, so a shipped or installed pack
|
||||
cannot claim the saved rules. Origin `saved` relaxes exactly the two rules
|
||||
named below and runs every other structural, role, scope and content check.
|
||||
|
||||
### The document (packVersion 1)
|
||||
|
||||
@@ -143,10 +165,10 @@ shipped pack.
|
||||
"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" }
|
||||
{ "id": "plan", "title": "Write the task", "roles": ["pm"], "state": "todo" },
|
||||
{ "id": "build", "title": "Implement", "roles": ["coder"], "state": "in-progress" },
|
||||
{ "id": "review", "title": "Review", "roles": ["reviewer"], "state": "in-review" },
|
||||
{ "id": "merge", "title": "Approve the merge", "roles": ["human"], "state": "done" }
|
||||
]
|
||||
}
|
||||
},
|
||||
@@ -154,10 +176,15 @@ shipped pack.
|
||||
}
|
||||
```
|
||||
|
||||
`checkPack` (new, in `packages/plugins/src/starter-pack.mjs`) refuses with
|
||||
`starter-pack-invalid` unless all of these hold:
|
||||
Pack definitions carry no `rev`. Apply sets `rev: 1` before
|
||||
`checkDefinition`, which requires it (`definitions.mjs:30,85`).
|
||||
|
||||
- Exact top-level keys as above. `scope` is `org` or `project`.
|
||||
`checkPack(doc, {origin})` (new, in `packages/plugins/src/starter-pack.mjs`)
|
||||
refuses with `starter-pack-invalid` unless all of these hold:
|
||||
|
||||
- Exact top-level keys as above. Origin `saved` also allows one more key,
|
||||
`packs` (provenance text, section 5). Nothing else differs by origin except
|
||||
the `from` rule below. `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:
|
||||
@@ -169,36 +196,91 @@ shipped pack.
|
||||
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`.
|
||||
target and credential request line is in the template, and the network
|
||||
is no wider. This is "no grant above the templates". Origins `shipped`
|
||||
and `installed` may not carry a role without `from`. Origin `saved` may
|
||||
(an invented role); it still passes `checkComposition` on apply and is
|
||||
hired only through a decision.
|
||||
- Role `credentials` lines are credential requests: a service and scope
|
||||
names, the same key `ROLE_KEYS` allows today (`definitions.mjs:32`) and
|
||||
`entryFromTemplate` copies from a template (`definitions.mjs:198-213`). A
|
||||
pack may carry them as a subset of the `from` template. Credential
|
||||
material is a different thing and never allowed (next subsection).
|
||||
- `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`.
|
||||
an entry in this pack's `definitions`, so every hire is covered by a hash
|
||||
pin and `starter-pack-template-changed`. There is no `level` field: a hire
|
||||
is org-level in an `org`-scope pack and project-level in a `project`-scope
|
||||
pack, never root. A hire carries no `route`; the raiser supplies it, by
|
||||
default from the organization's `launch` settings.
|
||||
- `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.
|
||||
`STORE_STATES`. Each step's `roles` is a list of 1 to 8 names, each a
|
||||
definition in the pack, a V2 template, or `human` (the user does that
|
||||
step). A list means "any of these", for steps like "the relevant officer
|
||||
drafts".
|
||||
- `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`).
|
||||
|
||||
The held-credential scan cannot run at load: a file load has no credential store
|
||||
(`NO_STORE`, `definitions.mjs:51`). The bus runs the credential store's
|
||||
`assertClean` on every string of the pack at preview, at apply and at template
|
||||
save, as `checkDefinition` does for definitions today (`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;
|
||||
- credential material: instance `credentials`, `copyBindings` or a binding
|
||||
`copyId`, 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).
|
||||
every definition at apply time and refuses a hard wait (R4) and
|
||||
`role.hire`, `role.fire` or `role.change` in a hired composition
|
||||
(`NEVER_HIRED`, `composition.mjs:51`, R9).
|
||||
|
||||
### What a board style and a workflow do
|
||||
|
||||
**Board.** Labels, order and a default layout over the five fixed states. The
|
||||
tasks view aggregates every organization (REQ-WS-8, `docs/prd/mosaic-stack.md`
|
||||
around line 1102), builds one set of columns, and groups each column's cards
|
||||
by organization (`tasks-board.js:146-150`). So:
|
||||
|
||||
- an organization's or project's labels and order apply only when the view is
|
||||
filtered to that one organization or project; across several, today's labels
|
||||
and order stay;
|
||||
- `layout` is a default used only when the viewer has no saved choice. The
|
||||
viewer's choice is kept per device in `localStorage` (`tasks-board.js:18-20`)
|
||||
and wins.
|
||||
|
||||
**Workflow.** A workflow is guidance text that agents plan by. It is not an
|
||||
engine and moves no task by itself. In v1 the generated prompt
|
||||
(`prompt`, `packages/harness/src/bundle.mjs:271`) gains a read-only workflows
|
||||
section for the organization or project the session runs in. It goes to the
|
||||
chief, to any role holding `task.create` or `task.assign`, and to any role a
|
||||
step names, which sees its own steps. The section says that workflows are the
|
||||
organization's guidance, that they grant nothing, and that the contract and
|
||||
the role rails win on any conflict. The chief and the pm then write and
|
||||
assign tasks by them through the task verbs they already hold. A step with
|
||||
`human` in its `roles` is one the agents leave to the user.
|
||||
|
||||
Workflows are inert data. No runner exists, and this design adds none. A
|
||||
runner that acts on workflow steps is a separate design with its own
|
||||
sign-off. Any such runner must resolve a live hired instance for each step
|
||||
and authorize every action against that session's own effective role and
|
||||
project sandbox, as every bus verb does today. A step's `roles` names are
|
||||
labels for planning, never authority.
|
||||
|
||||
Workflow text reaches other agents' prompts, so its writers matter. Only a
|
||||
person with `org.hire` or a holder of `organization.change` writes it, and
|
||||
the chief can already message any agent, so this opens no new channel. The
|
||||
text still gets the held-credential scan and a size cap (20 steps, titles up to 120
|
||||
characters, description up to 1,000).
|
||||
|
||||
### Versioning
|
||||
|
||||
@@ -216,28 +298,53 @@ 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
|
||||
flagged and credential lines each proposed hire would carry against the
|
||||
current chief, any proposal whose definition is in `launch.noHire`, 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.
|
||||
the revision or plan changed. It commits once, through a new writer
|
||||
operation `applyPack(id, plan, {revision, actor, assertClean})`.
|
||||
`writer.setDefinition` cannot do this: it commits one definition per call
|
||||
and refuses any other change in the same write
|
||||
(`organization-definitions-only`, `organization.mjs:401-430`). `applyPack`
|
||||
reuses its rules, not the function:
|
||||
- each `add` definition gets `rev: 1`, passes `checkDefinition` with
|
||||
`assertClean` and `checkComposition`, and keeps `from`;
|
||||
- an agent actor that writes any definition refuses `human-required`, as
|
||||
`setDefinition`'s guard does;
|
||||
- the write refuses if any key changes outside `definitions`, `board`,
|
||||
`workflows`, `projects.<p>.board`, `projects.<p>.workflows` and `packs`;
|
||||
- it writes the `board` and `workflows` items marked `add` and appends
|
||||
`{id, version, sha256, origin, scope, project, at, by, added,
|
||||
proposedHires}` to a new `packs` key. `added` names each item this
|
||||
apply wrote. Each `proposedHires` entry records the pin the chief will
|
||||
raise it with: `{template, sha256}` for a template reference,
|
||||
`{definition, rev}` when this apply wrote the definition, or
|
||||
`{inline, sha256}` (the canonical hash of the full entry) when it did
|
||||
not.
|
||||
|
||||
One commit means a crash leaves either the whole pack or none of it, so the
|
||||
re-apply table never meets half-applied items without a `packs` record.
|
||||
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;
|
||||
`{definition, rev}` and a route it chooses; 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.
|
||||
|
||||
Most shipped hires carry flagged or credential lines, so under the
|
||||
existing `authorizeHire` rule (`hire-decisions.mjs:40,46`) the owner
|
||||
approves them on every autonomy setting. That is today's rule for any
|
||||
hire of these roles, not a gate this design adds. Section 7 lists each
|
||||
proposal's lines.
|
||||
|
||||
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`).
|
||||
to a project proposes project-level hires, which land in
|
||||
`projects.<p>.agents`; those entries refuse instance credential material
|
||||
(`BOUND_KEYS`, `agent-levels.mjs:21`).
|
||||
|
||||
### The surface
|
||||
|
||||
@@ -245,25 +352,46 @@ exactly as if a person had made them by hand. The steps:
|
||||
|---|---|
|
||||
| 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` |
|
||||
| Bus ops | `packs` (read), `pack.preview.console`, `pack.apply.console`; and for agents, `pack.preview` and `pack.apply` for a role whose file lists `organization.change` |
|
||||
| `VERB_RIGHTS` | `packs` to `board.read`; `pack.preview.console` and `pack.apply.console` to `org.hire`, plus an owner check when the pack's origin is `saved` |
|
||||
|
||||
`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.
|
||||
`org.hire`. Board and workflows carry no capability. A saved pack applies
|
||||
only to an organization its saver owns (section 5).
|
||||
|
||||
### 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:
|
||||
decision (section 5). The agent `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.
|
||||
Who may call it: any role whose file lists `organization.change`, the same
|
||||
test `organization.set` uses (`broker.mjs:6248-6255,6321`). The shipped chief
|
||||
holds it. The chief could hire another agent holding it as a plain hire,
|
||||
since step 7 does not flag a line the chief already holds; that agent could
|
||||
then change boards and workflows too, which carry no capability. "The chief"
|
||||
in section 4 means any holder of `organization.change`.
|
||||
|
||||
What it does:
|
||||
|
||||
- it writes `board` and `workflows` directly under `organization.change`,
|
||||
the same way `organization.set` writes limits today
|
||||
(`broker.mjs:6247-6340`), through `applyPack`;
|
||||
- it writes no definitions. Each proposed hire is raised by the chief (only
|
||||
the chief raises, `role-not-chief`). For a template-reference definition
|
||||
the role is `{template, sha256}`, which the `launch.noHire` check reads
|
||||
(`hire-decisions.mjs:219`) and which keeps `from`. Only a full-entry
|
||||
definition goes as `{inline}`; that path starts from an empty role
|
||||
(`hire-decisions.mjs:227-228`), drops `from` and skips `noHire`, so preview
|
||||
marks such a proposal when its `from` template is in `launch.noHire`. The
|
||||
broker computes the lines against the chief either way, and the approved
|
||||
hire writes `hire-<decision>` as it does now.
|
||||
- the pin recorded in `proposedHires` is checked again when the chief raises
|
||||
the hire, so a proposal never resolves to text nobody reviewed. A template
|
||||
pin that no longer matches refuses `decision-stale` and a definition whose
|
||||
`rev` moved refuses `definition-rev-conflict`, both existing checks
|
||||
(`hire-decisions.mjs:225-226`). An inline entry whose hash differs from the
|
||||
recorded one refuses the new `starter-pack-proposal-stale`.
|
||||
|
||||
### A second pack, and re-applying
|
||||
|
||||
@@ -271,15 +399,23 @@ Items merge by name and nothing is overwritten:
|
||||
|
||||
| Item in the pack | Organization already has | Result |
|
||||
|---|---|---|
|
||||
| Definition | nothing by that name | `add` |
|
||||
| Definition | nothing by that name, never added by this pack | `add` |
|
||||
| Definition | nothing by that name, but an earlier apply of this pack id lists it in `added` | `removed`, no write |
|
||||
| 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 | nothing by that name, never added by this pack | `add` |
|
||||
| Workflow | nothing by that name, but an earlier apply of this pack id lists it in `added` | `removed`, no write |
|
||||
| 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 |
|
||||
|
||||
`removed` keeps a deliberate removal from coming back on its own: the
|
||||
earlier `packs` entries act as the tombstone. The person may bring the item
|
||||
back on purpose with `--as` and a new name, which the preview shows as an
|
||||
`add`. The same rule covers a board the organization removed after this pack
|
||||
added it.
|
||||
|
||||
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.
|
||||
@@ -299,16 +435,20 @@ 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.
|
||||
|
||||
"The chief" in this table means any role whose file lists
|
||||
`organization.change` (section 3) for board, workflow and pack writes. Hire
|
||||
and change decisions stay chief-only.
|
||||
|
||||
| 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. |
|
||||
| Hire, fire, change an agent | `hire.start.console` (`org.hire`) | `hire.raise` (only the chief: `role-not-chief`, `broker.mjs:6583`; hired agents never hold `role.hire`, 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. |
|
||||
| Create or edit a workflow | `workflow.set.console` (`org.hire`) | `workflow.set` under `organization.change` | Steps name existing definitions, templates or `human`, and the five states. A workflow grants nothing; agents read it as guidance below the contract. |
|
||||
| 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`). |
|
||||
| Project agents, board and workflow | The same verbs with `--project` | The same, under `organization.change` | Project agent entries refuse instance credential material (`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. |
|
||||
@@ -322,9 +462,11 @@ had none.
|
||||
`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.
|
||||
`hire-decisions.mjs:39-42`). Anything flagged goes to the owner. Of the
|
||||
shipped proposals only `cmo` is plain (section 7), so in practice the
|
||||
owner approves the rest, as for any hire of those roles today.
|
||||
5. A hired agent: `role.hire`, `role.fire` and `role.change` refuse in its
|
||||
composition (`NEVER_HIRED`, 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
|
||||
@@ -354,18 +496,21 @@ 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.
|
||||
`hire-<decision>` are renamed `<from>-<n>` (for example `coder-1`), so no
|
||||
decision id or instance name 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.
|
||||
a generated name `<from>-<n>` and the definition. Instance names are not
|
||||
copied; they often name a person or a client. The level follows the template's scope. 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.
|
||||
The result is a pack document read later with origin `saved` (section 2). A
|
||||
role in a saved template may lack `from` (an invented role) and the document
|
||||
may carry `packs` provenance; those are the only differences from a shipped
|
||||
pack. Every role still passes `checkComposition` on apply and is hired only
|
||||
through a decision.
|
||||
|
||||
### What is stripped
|
||||
|
||||
@@ -374,8 +519,8 @@ 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`;
|
||||
- credential material: 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`;
|
||||
@@ -384,13 +529,32 @@ Everything not listed above. In particular:
|
||||
- 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.
|
||||
Role `credentials` lines are kept. They are requests (a service and scope
|
||||
names), part of what the role is, the same lines a shipped template carries.
|
||||
They carry no token or binding. On apply the broker lists them as credential
|
||||
lines on the hire, so the owner approves each one (`authorizeHire`), and the
|
||||
credential itself arrives only 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.
|
||||
Free text is the owner's work product and is kept: instructions,
|
||||
personality, workflow titles and descriptions, and board labels. It can still
|
||||
carry private details, so save handles it in three ways:
|
||||
|
||||
1. Save replaces each literal occurrence of the organization id, its project
|
||||
ids, its instance names, the owner's account name and the project roots
|
||||
with the placeholders `{org}`, `{project}`, `{agent}`, `{owner}` and
|
||||
`{root}`. The preview lists every replacement by field path.
|
||||
2. The preview shows every kept free-text field in full, grouped by field
|
||||
path, so the owner reads what leaves before taking the digest. A field the
|
||||
owner wants gone is edited in the organization first and saved again.
|
||||
3. Save runs `assertClean` on every string, as definitions do. That check is
|
||||
credential-value detection only: it refuses text that contains a value the
|
||||
credential store holds now (`assertNotHeld`, `packages/bus/src/credentials.mjs:721-723`).
|
||||
It does not find names, paths, clients or a secret the store never held.
|
||||
A hit refuses `template-secret-suspected` and names the field path, never
|
||||
the text.
|
||||
|
||||
Nothing else scrubs free text. The owner is the reviewer of what a template
|
||||
says, which is why save is owner only and shows the exact JSON first.
|
||||
|
||||
### Applying one to a new organization
|
||||
|
||||
@@ -418,23 +582,32 @@ reasons:
|
||||
|
||||
### 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.
|
||||
- **Org files, in two steps.** Removing the kind outright would brick every
|
||||
org file that names it: `checkPlugins` refuses an unknown kind, even with an
|
||||
empty list (`unknown-kind`, `kinds.mjs:146-148`, run at
|
||||
`organization-config.mjs:358`). An organization would stop loading until
|
||||
its owner converted the file by hand, which is an outage and a forced
|
||||
human step. So:
|
||||
1. For one release `system-type` stays in `KINDS` as a retired kind. The
|
||||
reader accepts the key and ignores it. A write that adds a name refuses
|
||||
`verb-retired` (a new code). The next writer commit for that organization, by anyone,
|
||||
drops the key, and its trail event records the old list as evidence.
|
||||
Nothing is applied for it, since the old modules carried nothing. The
|
||||
Console offers the matching packs once.
|
||||
2. A later slice removes the kind once no org file on the instance names
|
||||
it. A print-only conversion in the style of `migrateLaunch`
|
||||
(`packages/organization/src/migrate-launch.mjs`, decision 145) covers a
|
||||
file that was never written in between.
|
||||
- **`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/`.
|
||||
types Console route and page, the CLI words (`mosaic org system-types`,
|
||||
`mosaic project system-types`), the channel verbs,
|
||||
`system-type-not-enabled`, and `installedSystemTypes`, each with its tests
|
||||
(section 1 lists every file). The five modules move to
|
||||
`packages/plugins/starter-pack/`.
|
||||
|
||||
### REQ-SYS-1 and REQ-SYS-2
|
||||
|
||||
@@ -469,6 +642,24 @@ Every organization already has a chief and an assistant from
|
||||
states in the order todo, in-progress, in-review, blocked, done. Every role
|
||||
named is a shipped V2 template.
|
||||
|
||||
**What each proposed hire carries against the chief.** The chief holds tools
|
||||
`read, grep, find, ls`, network `api-only`, and gitea `read:repository,
|
||||
read:user` (`roles/chief-of-staff.json`). `checkComposition` flags every line
|
||||
above that (step 7) and every resolve right (step 5). Under `authorizeHire`
|
||||
any flagged or credential line sends the hire to the owner. That is the rule
|
||||
today for any hire of these roles; packs add no gate. The lines below are
|
||||
tools, actions, resolve rights and credentials read from `roles/*.json`.
|
||||
Messaging lines also count; SP2 checks in the exact broker-computed list.
|
||||
|
||||
| Proposal | Flagged lines | Credential lines | Who approves |
|
||||
|---|---|---|---|
|
||||
| `pm` | tools `write, edit`; actions `task.create, task.assign, task.reassign, task.schedule, task.update.assigned, task.close`; crossRole `task.priority.change, task.scope.change`; resolve `decision.resolve.delivery` | gitea `write:issue`; vikunja task scopes | owner |
|
||||
| `coder` | tools `write, edit, bash`; actions `task.update.assigned, git.push.working, review.request`; crossRole `task.scope.change` | gitea `write:issue, write:repository`; vikunja task scopes | owner |
|
||||
| `reviewer` | tool `bash`; actions `review.verdict, task.update.assigned` | gitea `write:issue, write:repository`; vikunja task scopes | owner |
|
||||
| `research` | tool `web`; action `library.write` | gitea `write:repository` | owner |
|
||||
| `ideas`, `proofreader`, `continuity` | action `library.write` | gitea `write:repository` | owner |
|
||||
| `chief-marketing-officer` | none found | none | plain: by the authority autonomy level |
|
||||
|
||||
### Coding
|
||||
|
||||
| Item | Contents |
|
||||
@@ -476,11 +667,12 @@ named is a shipped V2 template.
|
||||
| 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 |
|
||||
| Workflows | `change`: pm writes the task, coder implements, reviewer reviews, the user (or a hired merge-gate) approves the merge (`roles: ["human", "merge-gate"]`) |
|
||||
| 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.
|
||||
`cto`, `security-reviewer` and `merge-gate` are definitions to hire when the
|
||||
team grows, not first proposals. The chief is the default technical arbiter
|
||||
(`org-template.json` `arbiters`), so a new team does not need a `cto` first.
|
||||
|
||||
### Creative writing
|
||||
|
||||
@@ -489,7 +681,7 @@ hire and the owner approves it. It is a definition, not a proposed hire.
|
||||
| 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 |
|
||||
| Workflows | `chapter`: ideas outlines, the user or companion drafts (`roles: ["human", "companion"]`), continuity checks, proofreader edits |
|
||||
| Skills | ms-unslop |
|
||||
|
||||
### Marketing
|
||||
@@ -499,7 +691,7 @@ hire and the owner approves it. It is a definition, not a proposed hire.
|
||||
| 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 |
|
||||
| Workflows | `campaign`: research finds the angle, ideas drafts, fact-checker checks claims, proofreader edits, the user approves publishing (`roles: ["human"]`) |
|
||||
| Skills | ms-unslop |
|
||||
|
||||
Publishing outside the organization is `message.external` or a hard wait; no
|
||||
@@ -526,12 +718,13 @@ The module name is still open (PRD 1.20 open question 1).
|
||||
| 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 |
|
||||
| Workflows | `decision-brief`: research gathers facts, pm writes the task, the relevant officer drafts (`roles` lists the four officers), the user decides (`roles: ["human"]`) |
|
||||
| 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.
|
||||
proposals. The workflow's "relevant officer" step lists all four in its
|
||||
`roles`.
|
||||
|
||||
## 8. Build slices
|
||||
|
||||
@@ -541,30 +734,35 @@ 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 |
|
||||
| SP1 | `checkPack` and `narrowsTemplate` in `packages/plugins/src/starter-pack.mjs`; kind `starter-pack` in `kinds.mjs`; loader listing with origin set by the loader | A valid pack loads; each forbidden key, stale template hash, role wider than its template, credential line wider than its template, root hire, ninth hire, unpinned hire definition and bad state refuses with its code; a shipped or installed pack with a `packs` key or a role without `from` refuses | `packages/plugins/tests/starter-pack.test.mjs`: a pack whose definition carries `copyBindings` loads (red until `checkPack` exists) | Yes: new plug-in kind, a pack schema |
|
||||
| SP2 | The five shipped pack modules, each with a checked-in list of the flagged and credential lines its proposed hires carry | Each loads through SP1; every template pin equals `templateHash` now; for each proposed hire the lines `checkComposition` computes against `roles/chief-of-staff.json` equal the checked-in list (section 7); `chief-marketing-officer` is the only plain proposal | A test that loads `starter-pack/coding` and compares its pins against `templateHashes` and its computed lines against the list | No (data only, built on SP1) |
|
||||
| SP3 | Org file keys `packs`, `board`, `workflows`, `projects.<p>.board`, `projects.<p>.workflows` in `organization-config.mjs`; writer op `applyPack` in `organization.mjs` | Unknown keys inside each refuse; board needs all five states once; workflow step `roles` name known definitions, templates or `human`; the size caps hold; `applyPack` commits in one write, refuses keys outside its list, refuses `definition-in-use` like `setDefinition`, and refuses `human-required` when an agent actor writes a definition | `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 and a writer op |
|
||||
| SP4 | `pack.preview` and `pack.apply` (owner verbs, bus ops, `VERB_RIGHTS`, the saved-origin owner check) on top of `applyPack` | Preview writes nothing and lists flagged lines, credential lines and `noHire` marks; a pack string holding a held credential value refuses at preview and at apply; apply writes add items only and the `packs` entry with `added` and pinned `proposedHires`; stale digest refuses; second apply of the same pack is all `same`; an edited definition is `kept`; a removed item an earlier apply added is `removed` and not re-added; 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 | Agent `pack.apply`, `board.set` and `workflow.set` for a role whose file lists `organization.change`; proposed hires surfaced in `packs` | An agent's apply writes no definition and refuses `human-required` if the plan has one; an agent without `organization.change` refuses `not-authorized`; a hire the chief raises from a proposal carries broker-computed flagged lines and refuses when its recorded pin no longer matches; a non-chief raise still refuses `role-not-chief` | Broker test: a coder session calling `pack.apply` refuses (red until the authority check is wired) | Yes: agent authority surface |
|
||||
| SP6 | Console: pack picker, preview, apply; board labels on `tasks-board.js`; workflow view and editor | Labels and order apply only when the view is filtered to one org or project; the all-orgs view keeps today's labels; the pack layout is a default and the viewer's saved localStorage choice wins | `tasks-board` test: a view filtered to an org with a `board` shows its labels (red today, labels are fixed at line 21) | No |
|
||||
| SP7 | Workflow guidance in the harness: a read-only workflows section in `prompt()` (`packages/harness/src/bundle.mjs:271`) for the chief, roles holding `task.create` or `task.assign`, and roles a step names | The section lists the org's or project's workflows and says they grant nothing; a role no step names and without those actions gets no section; contract text is unchanged | Bundle test: a chief bundle for an org with a `change` workflow contains the section (red until it is added) | Yes: changes what agents are told |
|
||||
| SP8 | `template.save` and saved-template apply | Save writes a 0600 file with none of the stripped keys and keeps role credential request lines; instance names, org and project ids, the owner's account and project roots become placeholders; the preview lists every kept free-text field; a seeded held credential value refuses; list shows only the caller's own; apply of a saved template goes through SP4 with the owner check | Test: save an org whose instance has `credentials` and `copyBindings`; assert neither appears anywhere in the output (red until the stripper exists) | Yes: data leaving an organization |
|
||||
| SP9 | System-type retirement, step 1: `system-type` kept as a retired kind; PRD REQ-SYS-1/2 text; removal of `systemTypesFence` and the Console, CLI and channel system-type surface | An org file with `plugins['system-type']` loads and the key is ignored; a write adding a name refuses `verb-retired`; the next writer commit drops the key and its 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"]`: it loads after `system-types` code is gone (red if the kind is dropped) | Yes: PRD and data migration |
|
||||
| SP10 | Step 2: remove the retired kind and add the print-only conversion | Runs only once no org file on the instance names `system-type`; the conversion prints a file without the key | Test: the conversion of the SP9 fixture prints no `system-type` key | No (follows SP9) |
|
||||
|
||||
Order: SP1, SP2, SP3, SP4, SP5, SP6, SP7, SP8. SP8 can run after SP4 if the
|
||||
Console needs the space earlier.
|
||||
Order: SP1 to SP10 in number order. SP9 can run after SP4 if the Console
|
||||
needs the space earlier. SP10 waits at least one release after SP9.
|
||||
|
||||
## 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
|
||||
`system-type` kind with `starter-pack`. Org files that name the old kind
|
||||
keep loading for one release and lose the key on their next write;
|
||||
`project_system_types` stays 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.
|
||||
would be a new gate. Any role listing `organization.change` gets the same
|
||||
writes, so the rule is the grant, not the chief's name. Role and hire
|
||||
changes stay decisions, and only the chief raises hires.
|
||||
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
|
||||
|
||||
Reference in new issue
Block a user