Files
stack/docs/plans/2026-10-18_role-instance-verb.md
jason.woltje b8036b6089 plans: row 165 brief correction, the second reviewer waits on P19 367 (mos-0823)
reviewer, coder, pm and cto declare gitea and vikunja credentials, and the
row keeps credentials out. The writer refuses an added instance or a
definition change whose definition declares credentials, with a named
refusal that says why, and never writes an instance without its binding.
One added test line. Acceptance 314 is covered: security-reviewer and
merge-gate declare none. Jason rules on credential binding in daylight.
2026-10-18 16:32:51 -05:00

7.4 KiB

Adding and changing role instances without editing the file, 2026-10-18

Status: written by Sage, lead, on Mos's relay mos-0787 of Jason's answer to P19 332. Line numbers are at d62c61fbd. It follows docs/plans/BRIEF-TEMPLATE.md, one ## heading per row.

Jason's answer, verbatim: "I want a command and a WebUI surfacing for the modifications".

Mos's reading: Jason wants to add and change role instances through a mosaic CLI command and a Console view instead of hand-editing the business file. That is the pattern of Q169 (docs/plans/2026-10-16_q169-assistant-config.md). Gitea #1662.

Confirmed. Mos relayed Jason's answer on 2026-10-18 (mos-0805), verbatim: "Q4: yes". DECISIONS Q171 and Q172 record the reading above: a mosaic CLI command plus a Console view for adding and changing role instances, for example a second reviewer, so nobody hand-edits the business file. The hold is lifted. The row moves to briefed once row 155 is done. Until this row lands, acceptance 314 stays as written (docs/acceptance/314.md:27-29): Jason adds the security-reviewer and merge-gate instances by editing the business file.

Correction, 2026-10-18 (mos-0823). The second reviewer in the example above waits on a follow-up, P19 367. This row can't add it. reviewer, coder, pm and cto each declare gitea and vikunja credentials in roles/*.json, and this row keeps credentials out (What ships item 1, Out of scope). So the writer refuses, with a named refusal whose message says why, any added instance and any definition change whose definition declares credentials. It never writes an instance without its credential binding. Changing vars or launch membership of an instance that already has its binding stays allowed and leaves the binding untouched. security-reviewer and merge-gate declare no credentials, so acceptance 314 is covered. Whether the human binds credentials through this command is a question Mos puts to Jason. Until Jason rules, nothing binds credentials through it.

The human adds and changes role instances

Problem

What exists at d62c61fbd:

  • No verb adds a role instance. The organization writers change only what exists: packages/business/src/launch-settings.mjs:46-52 sets vars.model on an instance and refuses one the file doesn't declare, and packages/business/src/business.mjs:130 refuses a launch.instances entry the file doesn't declare. Row 141's names table and row 155's assistant writer don't add instances either.
  • So staffing a role the file doesn't declare means editing ~/.config/mosaic-dev/businesses/<id>.json by hand, with no revision check, no actor and no change record. Acceptance 314 does this today.
  • The safe write path exists. organizationWriter().write (packages/business/src/organization.mjs:177-219) takes the file's revision and an actor, refuses a stale revision with revision-conflict, validates the document with validateBusinessDocument before and after serialization, and appends prepared and committed entries with the actor, the before and after hashes and the changes to businesses/.history/<id>/changes.jsonl.
  • mosaic business validate (packages/business/src/cli.mjs:64) runs loadBusiness, which calls validateBusinessDocument, and then resolves every instance against every project file.

Owner and reviewer

  • Owner: Darkwing, after row 155, which changes the same writers, CLI and Console settings view.
  • Reviewers: Dewey, for security, and Filbert.

Files owned

  • packages/business/src/: one new instance writer beside launch-settings.mjs, built on organizationWriter().write.
  • packages/cli/src/: the human's command below.
  • packages/webui/src/serve.mjs and packages/webui/src/public/: the Console view and its routes.
  • Tests in those packages, the webui browser tests, the business and cli READMEs, and docs/TOOLS.md.
  • docs/acceptance/314.md, only to replace the hand edit with the command once the row lands.

What ships

  1. A role instance here is one entry under the business file's roles, plus its place in launch.instances. The human can:
    • add an instance: a new id, a definition from roles/, its vars, and whether it goes into launch.instances;
    • change an existing instance's definition and vars, and add it to or remove it from launch.instances. holder, credentials and tracker stay out of this row. Deleting an instance from the file is out of scope.
  2. The CLI command is under mosaic business (the owner proposes the verb and flags in the packet before any code). The Console gets a view that lists instances and a form for the same two operations. Both carry the file's revision, and a stale one is refused with revision-conflict.
  3. Only the human. The CLI command needs the human proof the other organization writers use, and the Console routes need the signed-in human. No role, the chief of staff included, gets a tool or a grant for this in this row. Jason hasn't ruled on the chief of staff, so that waits for him.
  4. Validation goes through the same path as mosaic business validate. The writer refuses any document that validateBusinessDocument refuses, and any document in which an instance doesn't resolve against a project file, the check validate runs after loading. A write that lands always leaves a file that mosaic business validate accepts. The writer can't change any instance's definition to one in END_PROTECTED or remove the assistant's protection; row 155's rule holds here too.
  5. Each change writes an audit entry through the writer's change log: the actor, the business, the before and after hashes and the changed keys. The CLI prints the new revision and the transaction id.
  6. A change reaches a running bus the way row 141's and row 155's changes do. The packet says which reads pick it up and whether anything needs a bus restart.

Tests:

  • the CLI and the Console add an instance; mosaic business validate then exits 0 and the change log has the human as actor;
  • changing an instance's definition, vars and launch membership succeeds and logs before and after;
  • each validateBusinessDocument refusal and an unresolvable instance are refused, and the file's bytes don't change;
  • a stale revision is refused;
  • a call without the human proof, and any role's attempt, are refused;
  • a definition change into END_PROTECTED, or one that strips the assistant's protection, is refused.
  • adding an instance whose definition declares credentials, or changing an instance's definition to one that does, is refused with the named refusal, its message says why, and the file's bytes don't change; a vars change on an instance that already has its binding succeeds and leaves the binding as it was (mos-0823).

Mutants: one per behavior above, with the killing test in the packet.

Out of scope

Deleting instances. Changing holder, credentials or tracker. Any chief of staff tool or grant, until Jason rules. Editing the repository's role files in roles/, which change only through reviewed commits. Any live change: the row goes live through a re-pin with Mos's go.

Gate

  • Jason's confirmation, relayed by Mos (mos-0805, Q171 and Q172). Done.
  • Dewey and Filbert approve on the row's issue naming the candidate manifest.
  • Mos's decision 123 go on the public/ diff.
  • Business, cli and webui suites and every scripts/test-*.sh green on Sage's gate.
  • Needs row 155 done.