`mosaic business instances BUSINESS` reads the role instances, and `mosaic business instance add|set` writes one add or change through the broker's new human-only `instances` and `instances.set` verbs. The writer is packages/business/src/instance-settings.mjs. It takes the caller's revision, validates the next document, refuses a definition that declares credentials (organization-credentials-out-of-scope), keeps protected instances as they are, logs the human as the actor, and says when the running bus needs a restart. Neither command restarts the bus. The writer refuses the instance ids human and tester with invalid-request, because the broker refuses a business whose roles use either name when the bus starts. The Console's Settings view shows the instances and, under --send only, posts to POST /api/instances with the exact Origin and same-origin Fetch Metadata. A refusal keeps the draft. An answer without the view's shape is outcome-unknown (503). The page's public/ diff carries the decision 123 go in mos-0832. Neither verb is in ORGANIZATION_KEYS, so no agent tool reaches them. Owner Darkwing, brief in row 165 (#1662). Round 3 rebases round 2 onto4f45cadc0(row 156). The public/ +/- digest is 939ecd28...9444, unchanged at landing. Reviewers on candidate manifest e30b54da...f580: Dewey approve 29052, Filbert approve 29053. Seat run on4f45cadc0: 1436/0 singles, 37/37 mutants killed. Sage gate one8b09735a: GATE_PASSED 25/25, packages 2282/0, queue node 156/0, script selftests 422/0, 23:59:13Z to 00:05:35Z. Rebased onto50a57e454over two SESSIONS-only commits. Nothing live.
766 lines
45 KiB
Markdown
766 lines
45 KiB
Markdown
# TOOLS.md — command and tool reference
|
||
|
||
On-demand reference for agent sessions (conductors, bootstrapping agents,
|
||
reviewers). `AGENTS.md` routes here; this file carries the depth: usage,
|
||
inputs/outputs, exit codes, and safety notes for every entry point.
|
||
|
||
Reading guide: system entry points are `scripts/*.sh` (bash) or invoked via
|
||
`node scripts/mosaic-task.mjs` (node). Host-side helpers under `tools/`
|
||
(tmux messaging, watchers, prose checker) are covered under Tools
|
||
(host-side) below. Every script fails closed — missing
|
||
or invalid configuration/policy refuses the operation with a nonzero exit
|
||
and changes nothing.
|
||
|
||
## Lifecycle
|
||
|
||
| Command | Purpose | Notes |
|
||
|---|---|---|
|
||
| `scripts/bootstrap.sh` | Create `~/.config/mosaic-dev/config.json` if absent | Idempotent; existing config validated, never rewritten |
|
||
| `scripts/build.sh` | Build the release image | Tag derived from `RELEASE` + pinned pi version |
|
||
| `scripts/hello.sh` | One-shot startup request | Prints model response on stdout |
|
||
| `scripts/verify.sh` | Full gated test | Exit 0 only on exact `MOSAIC_HELLO_OK`; `EXPECTED_MARKER` overrides for negative drills |
|
||
|
||
## Tasks (missions, runs, evidence)
|
||
|
||
| Command | Purpose | Notes |
|
||
|---|---|---|
|
||
| `scripts/run-task.sh run <task.json>` | Execute a task | Immutable run record under `<dataRoot>/runs/` |
|
||
| `scripts/run-task.sh validate <task.json>` | Strict validation | Writes nothing |
|
||
| `node scripts/mosaic-task.mjs show <runId>` | Inspect a run | Full record + snapshots + artifacts |
|
||
| `node scripts/mosaic-task.mjs list` | List runs | task/workspace/session columns |
|
||
| `node scripts/mosaic-task.mjs retry <runId>` | Re-execute a run's snapshot | New run dir; `retriedFrom` lineage recorded |
|
||
| `node scripts/mosaic-task.mjs prune [--keep=N] [--yes]` | Retention | Dry-run default; receipt in `runs/.pruned.log` |
|
||
| `node scripts/mosaic-task.mjs resolve-role <roleFile>` | Validate a role file, version 1 or 2 | Prints `MOSAIC_ROLE_TOOLS` / `MOSAIC_ROLE_NETWORK`, and `MOSAIC_ROLE_CONTRACT` for version 2; config-free |
|
||
|
||
Task fields: `prompt` (required), `mission` (path), `expectExact`,
|
||
`timeoutSeconds` (5–600), `workspace` (`:run` or named), `capabilities.tools`
|
||
(allowlist: read write edit bash grep find ls), `session`,
|
||
`sessionForkFrom` (requires `session`). Mission fields: `objective`,
|
||
`directives[]`, optional governing `capabilities.tools`. Policy: a task may
|
||
narrow a mission's tools, never widen; empty intersection = tool-free run.
|
||
|
||
## Agent (interactive TUI)
|
||
|
||
```bash
|
||
scripts/agent.sh <name> [--mission <file>] [--workspace <ws>] [--session <s>] [--tools <list>]
|
||
```
|
||
|
||
Launches an interactive pi TUI inside the container with the four immutable
|
||
contracts + optional mission + agent identity as its system prompt,
|
||
persistent named session, optional workspace. Exit with `/quit`.
|
||
|
||
A seat role (`agent.json` `role`) binds to `roles/<role>.json` (M18): the
|
||
contract's tools are a ceiling the seat definition or `--tools` may narrow,
|
||
never escalate past. Missing/invalid contract refuses the launch; empty
|
||
intersection = loud tool-free seat. An explicit `MOSAIC_AGENTS_DIR` override
|
||
that cannot resolve the named seat also refuses (#46) — unset the override
|
||
for the M13 plain governed TUI. `--auth <account>` injects
|
||
`auth.<account>.json` (beside the active credential file) as the launch's
|
||
`PI_AUTH_FILE`; a missing/invalid account refuses (M19).
|
||
|
||
For native repository development, opt in with a **leading** `--host-dev`:
|
||
|
||
```bash
|
||
scripts/agent.sh --host-dev darkwing [--fresh] [--check] [--soul FILE] [--constitution FILE] [--user FILE] [--provider NAME] [--model ID] [--thinking LEVEL]
|
||
```
|
||
|
||
This mode delegates to `scripts/agent-host-dev.sh`, uses host Pi and repository
|
||
tools/skills plus the development goal extension, and keeps its own sessions
|
||
under `.pi/state/<name>/`. It uses native Pi authentication and does not run
|
||
container release alignment or apply managed seat role ceilings. It is a host
|
||
development session, not a sandboxed worker. Container-only flags such as
|
||
`--auth`, `--mission`, and `--tools` are rejected in this mode. Omitting
|
||
`--host-dev` retains the existing container lifecycle and policy checks;
|
||
container failures never trigger a host fallback. Darkwing's agent-local shim
|
||
selects host development explicitly. See `agents/darkwing/README.md`.
|
||
|
||
## Auth (credentials)
|
||
|
||
Credential checkpoint over pi's auth model (one `auth.json` keyed by
|
||
provider; resolution order `--api-key` → `auth.json` → env → models.json).
|
||
No credential material is ever printed — provider names, credential types,
|
||
and env var NAMES only.
|
||
|
||
Ownership rule (#48): `~/.pi` is read-only to the stack, permanently. The
|
||
only interaction is the existing read-only container mount of the default
|
||
credential (`PI_AUTH_FILE`, default `~/.pi/agent/auth.json`). Mosaic-managed
|
||
accounts live under the data root: `<dataRoot>/auth/<account>.json`, perms
|
||
0600 (mirroring `scripts/gitea-api.sh` hygiene — loose perms are flagged in
|
||
listings and refused by `--auth`).
|
||
|
||
| Command | Purpose | Notes |
|
||
|---|---|---|
|
||
| `scripts/auth.sh status` | Report both credential sources | Default harness credential (read-only) + mosaic-managed accounts; never prints material |
|
||
| `scripts/auth.sh accounts` | List mosaic-managed accounts | Under the data root; marks the active one; flags non-0600 |
|
||
|
||
`agent.sh --auth <account>` injects `<dataRoot>/auth/<account>.json` as the
|
||
launch's `PI_AUTH_FILE`; missing/symlinked/non-0600 accounts refuse.
|
||
Headless task runs keep the default credential.
|
||
|
||
## Release
|
||
|
||
| Command | Purpose | Notes |
|
||
|---|---|---|
|
||
| `scripts/release.sh package` | Build + tag the release image | Tag: `mosaic-poc-agent:<pi>-r<release>` |
|
||
| `scripts/release.sh activate` | Health gate → atomic pointer swap | `--fault-injection` proves the refusal path |
|
||
| `scripts/release.sh rollback` | Health-gated return to previous | Refuses if image missing |
|
||
| `scripts/release.sh status` | Release, tag, active pointer, log | Safe on empty state |
|
||
| `scripts/release.sh ensure` | Self-determination: align active pointer to `RELEASE` | Fast path restores a missing/mismatched pointer without a gate; slow path packages + health-gates first. Invoked automatically at launch |
|
||
|
||
## Conductor (worker patches)
|
||
|
||
```bash
|
||
scripts/conductor-apply.sh <runId> [--dry-run]
|
||
```
|
||
|
||
Auto-applies a worker's patch under `roles/conductor-policy.json`:
|
||
succeeded run → clean target tree → path allowlist → syntax gates →
|
||
apply → policy suites → attribution commit. Any failure reverts.
|
||
Push is never automatic.
|
||
|
||
## Conductor (landing approved work)
|
||
|
||
```bash
|
||
scripts/land-work.sh <decision-id> [--base refactor] [--dry-run] [--subject TEXT]
|
||
[--attribution LINE] [--business ID] [--project ID]
|
||
```
|
||
|
||
Turns a merge decision the human resolved into one gated local commit on
|
||
`refactor` (#1610). It reads `<dataRoot>/bus/bus.sqlite` read-only through
|
||
`scripts/land-work/read.mjs` and checks that the human picked the raise's
|
||
approval option, that the question names one `work/vikunja-<p>-<t>`, and
|
||
that the task's latest `review.verdict` approves a commit C that the work
|
||
ref in the working repository still points at. It then applies
|
||
`git diff $(merge-base refactor C) C` in a detached worktree, checks the
|
||
patch-id, runs every package suite except `queue` and every
|
||
`scripts/test-*.sh`, and commits. The subject is the approval option's
|
||
text unless `--subject` overrides it, and `--attribution` replaces the last
|
||
line, `Applied-under: scripts/land-work.sh (conductor)`. `--business` and
|
||
`--project` are needed only when the config has more than one business file
|
||
or the business more than one project. The tool prints the line for the PM
|
||
and doesn't send it. `--dry-run` runs every check and the gate and writes
|
||
no ref. A refusal names its reason and exits 2; the reasons are listed at
|
||
the top of the script. The worktree, its `node_modules` link and its Docker
|
||
network are removed on every exit. The gate log directory stays and the
|
||
tool names it. Keep `TMPDIR` short: the gate inherits it, the cli suite
|
||
puts unix sockets under it, and a 46-byte `TMPDIR` failed 8 of those tests.
|
||
Push is a separate act.
|
||
|
||
Since #1631 the gate, its steps and the root-file rule live in
|
||
`packages/seat/src/landing.mjs`, and the decision checks in
|
||
`packages/bus/src/landing.mjs`. The bus host's `land` op uses both to land
|
||
an agent's task on its working repository's `main`, never on `refactor`
|
||
(`packages/cli/README.md`, "Git socket"). Since #1651 the host starts the
|
||
holder's landing run itself ("Landing runs"). A decision raised for one target
|
||
can't land the other. land-work still accepts only a decision the human
|
||
resolved through the CLI.
|
||
|
||
## Maintenance
|
||
|
||
| Command | Purpose | Notes |
|
||
|---|---|---|
|
||
| `scripts/reset.sh` | Delete the data root | Triple-safety-checked (path, symlink, ownership marker) |
|
||
| `scripts/test-config.sh` | Config selftests (no Docker) | 24 cases |
|
||
| `scripts/test-task.sh` | Task selftests + live cases | 90 cases |
|
||
| `scripts/test-release.sh` | Release selftests | 14 cases |
|
||
| `scripts/test-conductor.sh` | Auto-apply selftests (sandboxed) | 17 cases |
|
||
| `scripts/test-auth.sh` | Auth checkpoint selftests (no Docker) | 13 cases |
|
||
| `scripts/test-land-work.sh` | Land-work selftests (sandboxed, fake Docker) | 84 cases |
|
||
| `scripts/gitea-api.sh <METHOD> <path> [body]` | Gitea API helper; reads the JSON credential file or a raw per-seat token file | Token never on argv/stdout |
|
||
|
||
## Seat launch with registration (`scripts/mosaic`)
|
||
|
||
```bash
|
||
scripts/mosaic launch <seat|seat-dir> [--task TEXT] [--project NAME] [--workspace PATH] [--harness NAME] [--repo PATH] [--config PATH] [-- args...]
|
||
scripts/mosaic seat task <seat> <text> [--by NAME] [--layout repo|fleet|unknown] [--config PATH]
|
||
```
|
||
|
||
Runs a seat's `launch.sh` unchanged (arguments after `--` pass through) and
|
||
leaves one registration record at `<dataRoot>/seats/<layout>/<seat>/registration.json`
|
||
that the control board reads: seat, project, task, workspace, tmux session,
|
||
harness, start time, pid. Rewritten on every launch. `seat task` changes the
|
||
task field only. The four repository seats (`agents/*/launch.sh`) register
|
||
themselves through this path unless called with `--check`. Run it by
|
||
path from the checkout. On a pinned host, `mosaic` on PATH is a wrapper
|
||
around a pinned copy's `scripts/mosaic`
|
||
([Stack copies](#stack-copies-scriptsstack-copysh)) and carries the human
|
||
commands; on Jason's host the estate CLI it replaced is `mosaic-old`.
|
||
Queue commands stay on `scripts/mosaic queue` in the canonical checkout,
|
||
because a copy refuses them ([One pinned
|
||
copy](../packages/cli/README.md#one-pinned-copy-row-85-1579)). Exit
|
||
codes: the launch script's own once it runs; before that 1 could not
|
||
start, 2 invalid config or seat, 4 usage. Details and the record's
|
||
fields: `packages/seat/README.md`.
|
||
|
||
## Work queue (`scripts/mosaic queue`)
|
||
|
||
```bash
|
||
scripts/mosaic queue list | show ID | next [SEAT]
|
||
scripts/mosaic queue add|move|release|assign|note|set ... --op ID [--by NAME]
|
||
scripts/mosaic queue verify [--current] | render [--check] | sync | unlock [--check-gate]
|
||
scripts/mosaic queue review request|resolve|abandon|record ... --op ID [--by NAME]
|
||
scripts/mosaic queue review verify-commit ID REF
|
||
scripts/queue-commit.sh -m MSG
|
||
```
|
||
|
||
`docs/plans/queue.json` holds the rows and an append-only log; the table in
|
||
`docs/plans/QUEUE.md` between the `mosaic-queue` markers is rendered from it.
|
||
Canonical checkout only. Every change needs an `--op ID` chosen before the
|
||
first attempt and reused on retry; only an op whose `ok <op> rev N` receipt
|
||
printed is done. The lead commits queue changes with `scripts/queue-commit.sh`;
|
||
the pre-commit guard refuses any other commit that stages the two queue files.
|
||
Moving a row with reviewers to in-review posts one review request comment
|
||
on its issue as the acting seat, with the seat's own token file in
|
||
`MOSAIC_GITEA_CREDENTIAL_FILE`; reviewers then run `review record`.
|
||
Exit codes: `0` ok · `1` failed · `2` invalid or refused · `3` uncertain,
|
||
retry the same op (for a review request, the retry sends nothing; resolve or
|
||
abandon it) · `4` usage. Details: `packages/queue/README.md`.
|
||
|
||
## Business files and role instances (`scripts/mosaic business`)
|
||
|
||
```bash
|
||
scripts/mosaic business validate <business>
|
||
scripts/mosaic business resolve <business> <instance> [--project <id>]
|
||
scripts/mosaic business instances <business> [--json]
|
||
scripts/mosaic business instance add <business> <instance> --definition <def> [--vars <file>] [--launch] --revision <rev>
|
||
scripts/mosaic business instance set <business> <instance> [--definition <def>] [--vars <file>] [--launch | --no-launch] --revision <rev>
|
||
```
|
||
|
||
Reads `businesses/<business>.json` next to the system config
|
||
(`$MOSAIC_CONFIG` or `~/.config/mosaic-dev/`), the version 2 role files in
|
||
`roles/` (or `$MOSAIC_ROLES_DIR`) and each project's `.mosaic/project.json`.
|
||
Writes nothing. `validate` checks the whole business, stat-checks every
|
||
credential reference without opening it and prints one digest per role
|
||
instance. `resolve` prints one instance's merged variables, their sources,
|
||
its narrowed tools, network and authority, and its credential references.
|
||
The business file must belong to you and not be group or other writable.
|
||
Exit codes: `0` ok · `2` invalid file or credential reference · `3` system
|
||
config problem · `4` usage or a required file missing. Details:
|
||
`packages/business/README.md`.
|
||
|
||
`instances` and `instance add|set` (row 165) go to `packages/cli` and the
|
||
bus's human-only `instances` and `instances.set` verbs, so they need a live
|
||
bus host and refuse inside an agent session. `instances` prints the revision,
|
||
each instance and the definitions `add` and `set` accept. `add` and `set`
|
||
write the business file under that revision and log the change with you as
|
||
the actor. They bind no credentials: a definition that declares credentials,
|
||
or a definition change on an instance that holds them, is refused with
|
||
`organization-credentials-out-of-scope` and stays a hand edit. After an add
|
||
or a definition change the output says a bus restart is needed; the restart
|
||
needs Mos's go or Jason's hand, and the command doesn't run it. Exit codes:
|
||
`0` ok · `2` refused, nothing written · `3` human proof refused · `4` usage.
|
||
The Console's Settings page has the same two forms. Details:
|
||
`packages/cli/README.md`.
|
||
|
||
## Inbox, decisions and the bus host (`scripts/mosaic`)
|
||
|
||
```bash
|
||
scripts/mosaic # no argument: the full-screen app
|
||
scripts/mosaic inbox | tasks | agents [--business <id>] [--json]
|
||
scripts/mosaic inbox --read <id>... | --read-all [--business <id>]
|
||
scripts/mosaic decide <decision> <option> [--note <text>] [--yes]
|
||
scripts/mosaic trail <task|decision> [--json]
|
||
scripts/mosaic bus start <business> [--pm] | stop | status [--json]
|
||
scripts/mosaic talk <role> <text> [--wait SECONDS] [--business <id>]
|
||
scripts/mosaic project create <name> [--repo <repo>] | list [--json]
|
||
scripts/mosaic threads [--project <project>] [--json]
|
||
scripts/mosaic thread create <project> <name> | open <thread> | read <thread> [--json]
|
||
scripts/mosaic talk --thread <thread> <text> [--wait SECONDS | --follow]
|
||
scripts/mosaic follow --thread <thread>
|
||
scripts/mosaic stop <run>
|
||
scripts/mosaic launches off | on [--business <id>]
|
||
scripts/mosaic launches list [--json]
|
||
scripts/bus-service.sh render [--copy ID [--copies DIR]] [--pm] [--claude PATH]
|
||
scripts/bus-service.sh install [--copy ID [--copies DIR]] [--pm] [--dir DIR] [--no-reload] [--claude PATH]
|
||
scripts/bus-service.sh uninstall | status <business>
|
||
```
|
||
|
||
The human commands read and resolve through the broker's human transport
|
||
(`packages/bus/src/human-cli.mjs`), never the SQLite file. They refuse inside
|
||
an agent run. `bus start` is the host the systemd user unit
|
||
`mosaic-bus@<business>` runs. It boots the broker, starts the Discord
|
||
notifier, and needs `<dataRoot>/notify/<business>/notify.json`. That
|
||
directory must be 0700: it also holds the notifier's journal `sent.jsonl`
|
||
(0600, never a symlink). On open the notifier copies a torn final line to
|
||
`torn-<UTC stamp>.bin` and truncates the journal to its last newline; a
|
||
malformed complete line refuses with exit 3.
|
||
|
||
`bus-service.sh` pins the Claude Code binary managed sessions run as the
|
||
unit's `MOSAIC_CLAUDE_BIN`, from `--claude PATH` or else `command -v claude`
|
||
(`packages/cli/README.md`, "Unit").
|
||
|
||
`bus start` also serves the session launcher (slice 1 S6): a PM session's
|
||
`launch` tool starts role sessions through it, each a runner under its own
|
||
PID namespace with a generated bundle, and `--pm` launches the business's
|
||
`launch.by` instance at start. A host that starts ends the launches a
|
||
crashed host left as `host-lost` and refuses with exit 3 on a
|
||
`sessions.json` it can't read. Every 2 s it also starts each launch-block
|
||
instance that holds an approved, unused merge decision, once per decision,
|
||
with a landing turn that calls `land_work`; a holder run that started
|
||
before the approval is stopped between turns first (`land-relaunch` in the
|
||
launch log; row 156, cli README "Landing runs"). `talk` sends a REQUEST and prints only the
|
||
reply to it, marking only that read (polls every 2 s, `--wait` default 900;
|
||
`0` only sends); other messages stay unread for `mosaic inbox`, and
|
||
`--follow` and `follow` print and read everything. `inbox --read <id>...`
|
||
marks unread messages read by the ids `inbox` prints, and `--read-all` marks
|
||
every one it lists (row 150); the cli README, "Human commands", says how
|
||
refusals print. When no run holds the
|
||
role, a line after `sent` says whether `launch.by` was told, or that the role
|
||
isn't in the launch block and `launch.by` can't launch it (row 152); the cli
|
||
README, "Human commands", lists the lines. `stop` sends SIGTERM to
|
||
a run's runner after checking it in
|
||
`sessions.json`. `launches off|on` revokes and restores `role.launch` for
|
||
the business; running sessions keep running. `launches list` reads
|
||
`sessions.json` and needs no broker. `talk`, `stop` and `launches off|on`
|
||
refuse inside an agent run.
|
||
With no argument on a terminal, `scripts/mosaic` opens the full-screen app:
|
||
Inbox, Tasks, Agents, Trail and Help, deciding through the same flow and
|
||
strings as `decide`. In an agent run it exits 3; without a terminal on
|
||
stdin and stdout it exits 4 with the usage.
|
||
Exit codes: `0` ok · `1` failed or outcome unknown · `2` invalid input ·
|
||
`3` refused or config problem · `4` usage. Details: `packages/cli/README.md`.
|
||
|
||
## Stack copies (`scripts/stack-copy.sh`)
|
||
|
||
```
|
||
scripts/stack-copy.sh copy COMMIT [--copies DIR] [--allow-worktree]
|
||
scripts/stack-copy.sh wrapper ID [--copies DIR] [--bin DIR]
|
||
scripts/stack-copy.sh check [--copies DIR] [--bin DIR] [--business ID]
|
||
```
|
||
|
||
One read-only tree supplies the bus, Console and human CLI (row 85,
|
||
#1579). Copies default to `${XDG_DATA_HOME:-$HOME/.local/share}/mosaic-stack/copies/<full id>`.
|
||
`copy` requires a canonical checkout (.git directory); --allow-worktree
|
||
is a test override. COMMIT must be reachable from both local refactor and
|
||
origin/refactor. It archives the runtime/import closure, adapters, roles,
|
||
RELEASE, package.json and package-lock.json; refuses missing manifests;
|
||
runs npm ci inside the temporary copy; removes write permissions; and
|
||
renames beside the target. A published existing copy stays unchanged.
|
||
Dependencies use the exact lock and carry Pi's installed lock record.
|
||
Failure publishes nothing and removes the temporary tree.
|
||
|
||
`wrapper` is a live switching step, reserved for Jason or Mos. It accepts
|
||
only an existing copy and a regular non-symlink mosaic wrapper with the
|
||
Q81 marker on line 2 and its exact lease line to mosaic-old. It preserves
|
||
both lines, adds comments naming the copy root and Q81's mosaic-old lease
|
||
probe, and atomically replaces the wrapper's final exec with the
|
||
copy's scripts/mosaic. --bin is a fixture override; the default is
|
||
~/.npm-global/bin. Neither the Q81 worktree nor mosaic-old is removed.
|
||
`check` changes nothing: it reads effective systemd WorkingDirectory and
|
||
ExecStart (including drop-ins) for the bus and Console, plus the wrapper
|
||
exec. It prints all three paths and succeeds only on the same existing
|
||
copy; absent/failed reads and a mismatched ExecStart fail. Business defaults
|
||
to mosaic-stack. Queue work continues on the canonical scripts/mosaic.
|
||
|
||
Bus render/install accept --copy ID, --copies DIR, --pm and the unchanged
|
||
--claude pin. Without --copy the rendered unit stays the checkout unit.
|
||
--pm adds the PM startup; a refusal exits 3, and RestartPreventExitStatus
|
||
prevents a retry. Install refuses any busy bus instance, even with
|
||
--no-reload; a failed state/drop-in read also refuses. Any template or
|
||
instance drop-in, or inherited mosaic-.service.d/ or service.d/ drop-in,
|
||
assigning ExecStart or WorkingDirectory refuses,
|
||
naming the file. Status reads effective roots, --pm and DropInPaths,
|
||
reports checkout or copy identity and refactor drift, and writes nothing.
|
||
|
||
### Pinned stack update
|
||
|
||
```
|
||
scripts/stack-release.sh adopt BUSINESS
|
||
scripts/stack-release.sh activate ID BUSINESS [--pm] [--migrate] [--timeout S] [--plan]
|
||
scripts/stack-release.sh rollback BUSINESS [--restore ACTIVATION [--events N]] [--timeout S] [--plan]
|
||
scripts/stack-release.sh status BUSINESS
|
||
common options: --copies DIR, --bin DIR
|
||
```
|
||
|
||
This is an operator plan, not an action agents run. Both reviews and the
|
||
landing come before it. Mos grants each live run first. A bus restart ends
|
||
running sessions, so run it only when no PM turn is in flight. `--plan` and
|
||
`status` change nothing and need no go.
|
||
|
||
The script keeps `<dataRoot>/state/stack-active.json` (the live copy, business,
|
||
PM flag, claude pin, Console flags, schema and activation) and appends every
|
||
run to `<dataRoot>/state/stack-activation-log.jsonl`: a `started` line, then
|
||
one final line with `result` `ok`, `refused`, `gate-failed` or
|
||
`rollback-failed`. A refusal exits 3, a gate failure that rolled back exits 1,
|
||
a failed rollback exits 2, usage exits 4. Only one run at a time holds
|
||
`state/stack-release.lock` (`run-locked`).
|
||
|
||
1. Once per host, `adopt BUSINESS` records the running stack as the pointer:
|
||
the copy `stack-copy.sh check` reports, the bus unit's `--pm` and
|
||
`MOSAIC_CLAUDE_BIN`, the Console's flags without `--checkout`, and the
|
||
copy's schema version. It refuses when the check fails, the bus unit has no
|
||
`MOSAIC_CLAUDE_BIN` or a pointer exists.
|
||
2. Make the pushed target copy with `stack-copy.sh copy COMMIT` and note its
|
||
full id.
|
||
3. Run `activate ID BUSINESS --plan` and take the output to Mos. It names the
|
||
store's schema and the target's, any migration with `--migrate` and the
|
||
decision 96 condition 2 line (Jason present), the commits the run adds,
|
||
and the three install commands. It refuses `copies-unrelated` when neither
|
||
copy is an ancestor of the other.
|
||
4. Mos removes any bus drop-in that overrides ExecStart or WorkingDirectory;
|
||
`activate` refuses `drop-in-override` and names the file.
|
||
5. With Mos's go, `activate ID BUSINESS`, plus `--migrate` when the plan
|
||
says so. Before it stops anything, it refuses a missing, unpushed or
|
||
unreadable copy, a busy `mosaic-bus@` instance, and unit flags that differ
|
||
from the pointer. Then it saves the unit files, drop-ins and wrapper under
|
||
`state/stack-rollback/<activation>/`, stops the Console then the bus, and
|
||
reads the stopped store. A `-wal` or `-shm` beside it refuses
|
||
`store-unclean` and leaves the store alone. A migration takes an
|
||
owner-only backup under `<dataRoot>/backups/stack/<activation>/` first.
|
||
It installs the bus, the Console and the wrapper from the pointer's flags,
|
||
starts the bus then the Console, and runs the gate: both units active, a
|
||
live bus host for this start, Console `/healthz`, `stack-copy.sh check`
|
||
and, with `--pm`, exactly one `launched` journal line for a live run of
|
||
this start. A pass writes the pointer.
|
||
6. A gate failure puts back the saved units and wrapper, restores the backup
|
||
when a migration ran, keeps the store it replaced as
|
||
`bus.post-attempt-<stamp>`, starts the old copy and gates it. If that gate
|
||
fails too, both units stay stopped, the log says `rollback-failed`, and
|
||
Jason or Mos settles it from the rollback set the message names.
|
||
7. `rollback BUSINESS` returns to the copy before the pointer's, taken from
|
||
the log. When the schemas differ it needs `--restore ACTIVATION`; events
|
||
written after that backup need `--events N` with the count it prints, and
|
||
Jason decides first.
|
||
8. Jason's plain `mosaic talk pm "..."` must print sent and a reply. Keep the
|
||
`status` output with the receipts.
|
||
|
||
A `started` line with no final line is an interrupted run. `status` shows
|
||
it, and `adopt`, `activate` and `rollback` refuse until Jason or Mos settles
|
||
it. Nothing prunes copies, rollback sets, backups or post-attempt stores.
|
||
|
||
After a switch, the human commands and `--tester` work only through `mosaic`
|
||
on PATH; the checkout's `scripts/mosaic` exits 3 with `human-required` or
|
||
`tester-required`, and queue writes stay on it ([packages/cli/README.md, One
|
||
pinned copy](../packages/cli/README.md#one-pinned-copy-row-85-1579)).
|
||
|
||
## Console service (`scripts/webui-service.sh`)
|
||
|
||
```bash
|
||
scripts/webui-service.sh copy COMMIT [--copies DIR] # make a pinned copy
|
||
scripts/webui-service.sh render [--copy ID [--copies DIR]] [FLAGS] # print the unit
|
||
scripts/webui-service.sh install --copy ID [--copies DIR] [--dir DIR] [--no-reload] [FLAGS]
|
||
scripts/webui-service.sh uninstall [--dir DIR] [--no-reload]
|
||
scripts/webui-service.sh status [--dir DIR]
|
||
# FLAGS: [--port N] [--business ID] [--board URL]
|
||
# [--public-host NAME --auth-header HEADER --allow-user ID [--send]]
|
||
```
|
||
|
||
The systemd user unit `mosaic-webui.service` runs the Console,
|
||
`node packages/webui/src/cli.mjs serve`, with serve's flags as given:
|
||
`--port` defaults to 7332 (7330 is the operator's own Console), and the
|
||
preview set goes all three or none. `--send` (row 83) goes only with the
|
||
preview set and is refused otherwise (exit 1, nothing written); ExecStart
|
||
then ends with `--send`, and the Console needs the system config at start
|
||
(`--send needs the bus configured`, exit 2). Without `--send` the unit is
|
||
read-only. The template is
|
||
`packages/webui/systemd/mosaic-webui.service.in`.
|
||
|
||
Under `--send` the Console's Docs page (row 148) can also save an existing
|
||
spec/north-star.md or docs/NAME.md of a project: one local commit authored by the
|
||
business's human, refused `stale-base` if the file changed since the editor opened it.
|
||
It is the human bus verb `project.file.save` through the human transport; an agent
|
||
refuses it. Nothing is pushed. Without `--send` the page reads and previews only. See
|
||
`packages/webui/README.md` ("Spec and docs editor"). The page opens in a visual editor
|
||
built from reviewable source (row 154): `third_party/editor/` is unmodified upstream
|
||
source with its licenses and `PROVENANCE.json`, and `npm run build:editor` in
|
||
`packages/webui` rebuilds `src/public/editor.js` and `editor.css` with esbuild 0.28.2;
|
||
the save, the verb and the routes are unchanged. See "Visual editor" there. A save of spec/north-star.md also
|
||
sends the business's pm and cto one notice naming the commit (row 143); a BLOCK from
|
||
either goes to the chief of staff and on to the assistant. See `packages/bus/README.md`
|
||
("Spec review notices").
|
||
|
||
The unit runs a pinned copy, not the working checkout (row 77, #1571).
|
||
`copy COMMIT` resolves COMMIT to its full id, refuses one not reachable
|
||
from both `refs/heads/refactor` and `refs/remotes/origin/refactor`, and
|
||
delegates to stack-copy.sh for the archive and pinned npm install into `DIR/<id>/`, by default
|
||
`${XDG_DATA_HOME:-$HOME/.local/share}/mosaic-stack/copies/<id>/`. It writes a
|
||
temp directory beside the target, makes every file and directory read-only
|
||
(so a writer that replaces by rename can't change it either), then renames
|
||
it, so the copy's path is always whole. A copy that exists is left alone
|
||
(`exists:`). `install --copy ID` and `render --copy ID` point
|
||
`WorkingDirectory` and ExecStart at the copy and pass serve
|
||
`--checkout <this checkout>`, where the queue read still runs; `RELEASE`,
|
||
the roles, the config check and the bus child come from the copy. The bus
|
||
child is the human transport, `packages/bus/src/human-cli.mjs`, and the bus
|
||
checks that path against its own, so the bus must run from the same copy
|
||
(below). `install` refuses without `--copy`, or when the copy is missing.
|
||
`render` without `--copy` prints the unit for the checkout, as before.
|
||
|
||
`install --copy ID` also refuses unless the bus runs from the same copy
|
||
(round 2, #225). The bus accepts a human proof only from its own
|
||
`packages/bus/src/human-cli.mjs`, so a Console on a copy whose bus runs
|
||
from the checkout would fail every bus call with `human-required`. With
|
||
`--business ID`, install reads `[email protected]`; without it, every
|
||
loaded `mosaic-bus@` unit. It reads effective WorkingDirectory and ExecStart from
|
||
`systemctl --user show -p LoadState -p WorkingDirectory -p ExecStart`, so a drop-in counts,
|
||
and every ExecStart path must lie inside the copy. It refuses, naming both
|
||
paths, when one doesn't; when the unit is not installed or none is loaded;
|
||
when ExecStart has no path; and when the systemctl call fails. The check
|
||
runs before the unit state read and changes nothing.
|
||
|
||
`install` writes a temp file, renames it and runs
|
||
`systemctl --user daemon-reload`. `install` and `uninstall` refuse while the
|
||
unit is in any state but inactive or failed (`systemctl --user list-units`),
|
||
and say Jason or Mos stops it first; `--no-reload` skips only the
|
||
daemon-reload, never that check. If the state read itself fails they refuse
|
||
with one line naming the call, and change nothing. `copy` and `install`
|
||
refuse outside the canonical checkout (a `.git` directory, not a worktree's
|
||
`.git` file); `--allow-worktree` overrides that for the tests only. `status`
|
||
prints the unit state, the copy id it runs, whether `refactor` points at
|
||
that commit or another, whether the installed unit sends, and
|
||
`GET /healthz` on the installed port. It changes nothing.
|
||
|
||
Each value is held to a plain character set the unit can carry (no space,
|
||
`%`, `$`, quote, backslash, `&` or `|`), and the preview values must start
|
||
with a letter or digit; anything else is refused. With `--auth-header
|
||
X-authentik-uid`, in any letter case, `--allow-user` must be the Authentik
|
||
uid, `^[0-9a-f]{64}$` (lead decision 101). The unit sets no `MOSAIC_*`,
|
||
`CLAUDE*`, `CODEX*` or `PI_*` variable. Serve's exit 2 (usage, config, or a
|
||
port already in use) is not restarted.
|
||
|
||
The update/rollback sequence is [Pinned stack update](#pinned-stack-update)
|
||
above. The queue view reads the checkout and stays current. Copies remain
|
||
until an operator removes them; nothing here prunes or migrates copies.
|
||
|
||
There is no start, stop, enable or restart verb. The Console reads the bus
|
||
through the human transport, so whoever starts it holds the human's
|
||
capability. Jason starts, stops and restarts it
|
||
(`systemctl --user enable --now mosaic-webui`), and so may Mos under lead
|
||
decision 102, which accepts that sends from a Console Mos started enter the
|
||
trail as Jason (`human-cli`). Any other agent starting it, even through
|
||
systemd, works around the human proof (lead decisions 62 and 94).
|
||
Exit codes: `0` ok · `1` failed or refused · `4` usage.
|
||
|
||
## Discord connector (`scripts/discord.sh`)
|
||
|
||
```bash
|
||
scripts/discord.sh check <binding>
|
||
scripts/discord.sh run <binding> [--supervised]
|
||
scripts/discord.sh stop <binding>
|
||
scripts/discord.sh unlock <binding>
|
||
scripts/discord.sh recover <binding>
|
||
scripts/discord.sh reload <binding>
|
||
scripts/discord-service.sh render | install [--dir DIR] [--no-reload] | uninstall [--dir DIR] [--no-reload] | status <binding>
|
||
```
|
||
|
||
One seat's conversation reachable from listed channels of one Discord
|
||
server, chat only. `<binding>` names `<dataRoot>/discord/<binding>.json`
|
||
(0600, never committed; shape in `packages/discord/fixtures/binding.example.json`).
|
||
`check` validates the binding, token file mode, context files and pi, reads
|
||
the bot, guild and channels over REST, opens and closes one gateway
|
||
connection; it sends nothing. `run` serves turns until `stop`, SIGTERM or
|
||
SIGINT and refuses to start when `STOP` exists or a delivery cannot be
|
||
reconciled. `stop` writes `STOP` and signals the running process. `unlock`
|
||
writes `STOP` and removes a `run.lock` left by a dead process; it refuses
|
||
while the owner is alive with an unverifiable identity or its record cannot be
|
||
parsed. `run` refuses a stale lock rather than reclaiming it, and releases a
|
||
claim that meets `STOP`. `recover` is the supervised pre-start, and
|
||
`run --supervised` does it first in the same process: it refuses (exit 3)
|
||
while `STOP` is present or the binding is held, clears a lock whose owner
|
||
is gone and removes only the `STOP` it wrote for that. `reload` validates
|
||
the binding file and sends SIGHUP to the running connector, which applies
|
||
`guildName`, `channels`, `users` and `limits` in place and refuses anything
|
||
else, keeping the old binding; attempts are journaled in `reloads.jsonl`. A
|
||
user entry may carry `channels`, an allowlist of listed channel ids.
|
||
`scripts/discord-service.sh install` renders and writes the systemd user
|
||
unit `[email protected]` (one instance per binding, restart on
|
||
failure, exit 3 never retried, SIGTERM on `systemctl --user stop`, SIGHUP on
|
||
`systemctl --user reload`). With
|
||
`install --copy ID [--copies DIR]`, a full id selects an existing shared
|
||
copy holding the Discord CLI and pi; ExecStart runs Node on that CLI with
|
||
--repo and WorkingDirectory in the copy. Without --copy the checkout unit
|
||
is unchanged. Status names its effective cwd/copy id, including drop-ins.
|
||
`check --offline` validates local binding/context/tool roots/pi/token mode
|
||
and reports journal/STOP without REST or gateway calls; run refuses this
|
||
flag. For a Mos-approved re-pin after the SetSpark freeze: make the copy,
|
||
run `node <copy>/packages/discord/src/cli.mjs check <binding> --repo <copy> --offline`,
|
||
save the installed unit, install --copy, then restart on Mos's go. Context
|
||
files resolve inside the copy and missing files refuse. Rollback restores
|
||
the saved unit and daemon-reloads/restarts on Mos's go; keep both copies.
|
||
The connector's pin is independent of stack-copy.sh check. An optional `tools` key in the binding declares
|
||
file roots; pi then runs with its own tools off and the package's
|
||
extension providing `list_dir`, `read_file` and `search` confined to those
|
||
roots, plus `write_file` and `edit_file` for roots marked `"write": true`
|
||
(temp file and rename, no folder creation, no dot-prefixed path, no
|
||
credential shapes), and `web_fetch` and `web_search` when `tools.web`
|
||
names a SearXNG instance (https only, public addresses only, three
|
||
redirects, capped body, html to text), with each call in the turn record.
|
||
A writable root that is a git work tree may carry `git` (`branch`,
|
||
`identity`, `tokenFile`, `author`, optional `protocol: "vault"`) and
|
||
gains `git_status`, `git_commit` (explicit paths, seat author,
|
||
`Requested-by:` trailer, push at once), `git_pull` (ff-only) and
|
||
`git_push` (one branch, never force), with `reserve_id` under the vault
|
||
protocol; git runs with no host config and the package's own credential
|
||
helper reading a 0600 token file, never printed. `tools.setspark` names the
|
||
SetSpark record service and adds `record_list`, `record_get`,
|
||
`record_create`, `record_update`, `resolve_id`, `open_approval_request`,
|
||
`get_approval_request` and `create_document`, one fixed path each, writes
|
||
keyed per turn and call; approvals of its proposals are an Approve button
|
||
or an exact `approve` reply, checked by the connector against the
|
||
request's approvers, never by the model, with one Discord message per
|
||
approval as evidence. `tools` is a fixed key: changing
|
||
it needs a stop and start. Records
|
||
under `<dataRoot>/discord/<binding>/`: `inbox.jsonl`, `outbox.jsonl`,
|
||
`drops.jsonl`, `admissions.jsonl`, `notices.jsonl`, `reloads.jsonl`, `approvals.jsonl`, write-once `turns/<id>.json`. Suite:
|
||
`scripts/test-discord.sh`.
|
||
Exit codes: 0 ok, 1 operation failed, 2 invalid data or configuration, 3
|
||
refused by a brake (a supervisor must not retry), 4 usage. Details:
|
||
`packages/discord/README.md`.
|
||
|
||
## Tools (host-side)
|
||
|
||
Host-side helpers under `tools/`, outside the `scripts/` command surface.
|
||
Per-tool READMEs: `tools/tmux/README.md` and `tools/unslop-hook/README.md`.
|
||
|
||
| Command | Purpose | Notes |
|
||
|---|---|---|
|
||
| `tools/tmux/agent-send.sh` | Inter-agent tmux message with addressing preamble | Reliable submit (bracketed paste, Enter flush, draft detection); ships `send-message.sh` over ssh for remote panes (remote needs only bash + tmux + base64) |
|
||
| `tools/agent-watch/agent-watch.sh` | Condition watcher per agent seat | One transient systemd `--user` timer + service per watch; fires `agent-send.sh` when the condition command exits 0 |
|
||
| `node tools/unslop-hook/unslop-check.js <file>` | Mechanical AI-tell prose check | Dependency-free node CLI + module driven by `lists.json`; `extension.ts` is the pi extension wrapper |
|
||
|
||
`agent-send.sh` prepends the preamble
|
||
`[<src_host>:<src_session> -> <dst_host>:<dst_session>]`; `-C`/`--class` adds
|
||
a ` class=<CLASS>` token (`terminal-log`, `actionable`, `human`, `reaction`,
|
||
`digest`; consumers treat an absent class as `actionable`). Flags: `-s` dst
|
||
session (required) · `-H` ssh target for a remote pane · `-L` named tmux
|
||
socket · `-n` dst hostname for the preamble · `-m`/`-f`/stdin message body ·
|
||
`-S` source-label override · `-r N` Enter-flush attempts (default 2) · `-v`
|
||
verbose · `-h` help. Exit codes: `0` delivered/queued · `1` target not found ·
|
||
`2` still draft · `3` usage error · `4` ambiguous socket (the session exists
|
||
on more than one tmux server; disambiguate with `-L` or `MOSAIC_TMUX_SOCKET`).
|
||
|
||
`agent-watch.sh` subcommands: `start --name <id> --session <session> --when
|
||
'<shell command; exit 0 = met>' --message <text>` with `--class`,
|
||
`--interval` (default 30), `--timeout` (default 3600), `--repeat`,
|
||
`--quiet-timeout`, `--socket` · `list` · `status [--json]` · `stop <name>` ·
|
||
`log <name>` · `meta-install [--interval 300] [--unit-name <unit>]` ·
|
||
`meta-remove [--unit-name <unit>]`. Interval floor is 10s (a watcher is a
|
||
fallback cadence, never a tight poll); hidden `_tick`/`_scan` subcommands run
|
||
inside the systemd services. `status` exit codes: `0` clean · `3` any stale
|
||
watch or dead meta-watch · `6` systemd user bus unreachable. Delivery goes
|
||
through `agent-send.sh`; rc `2` means the text reached the pane as an
|
||
unsubmitted draft, which counts as delivered and is not retried (other
|
||
failures retry twice, then the watch gives up). Watches are one-shot by
|
||
default; `--repeat` re-arms. Notices carry a `[watch:<name>]` prefix.
|
||
|
||
`unslop-check.js` checks a file (or stdin) against the word, phrase,
|
||
punctuation-density, and pattern lists in `lists.json`, stripping fenced and
|
||
inline code first so a quoted mention never flags. Invocation:
|
||
`node tools/unslop-hook/unslop-check.js <file>`; `UNSLOP_LISTS=<path>`
|
||
overrides the lists location. Exit codes: `0` clean · `1` violations (findings
|
||
printed as JSON on stdout) · `2` gate broken (invalid lists or unreadable
|
||
input; error on stderr, never a clean verdict).
|
||
|
||
## Exit-code convention
|
||
|
||
`0` success · `1` operation failed · `2` invalid data/configuration ·
|
||
`3` configuration missing for a read operation · `4` usage/file/environment
|
||
problem. Scripts print diagnostics on stderr; model responses (and only
|
||
model responses) on stdout.
|
||
|
||
## Agent limits (`scripts/mosaic limits`)
|
||
|
||
View: `scripts/mosaic limits --business ID --json`. Set: `scripts/mosaic limits
|
||
set --business ID --file PATCH`, with the current revision and replacement
|
||
`limits` and/or `models`, or `instances` mapping role names to model-table keys,
|
||
or `assistants` mapping an assistant instance to any of its `vars`, `tracker`
|
||
(`null` removes it) and `definition` (row 155, #1649, Jason's ruling Q169). The
|
||
definition stays an assistant one; `holder` and `credentials` stay hand edits.
|
||
Both use the human bus proof. Settings offers the same edits in signed-in send
|
||
mode. Unset means unlimited, zero blocks new launches, and running sessions
|
||
remain alive when a limit is lowered. Total, provider and exact model limits
|
||
are checked for each launch; counts have no hard-coded policy ceiling.
|
||
|
||
Token limits (row 125) are `limits.tokens.instances.NAME`, `providers.NAME`,
|
||
`models.PROVIDER.MODEL` and `keys.NAME`, each `{ "limit": N }`. The writer adds
|
||
and keeps `since`. `scripts/mosaic limits reset WORD... [--business ID]` (for
|
||
example `reset tokens providers anthropic`) moves one limit's `since` to now,
|
||
logged with its actor. A token limit is enforced only by a switched-on
|
||
`limit-check` plug-in (`token-limit` ships): it refuses a launch at the limit
|
||
and stops the session at the end of the turn that reaches it. Otherwise the
|
||
read shows it as `not enforced`; a failing check refuses and stops (fail
|
||
closed). Without `--json` the read prints JSON without `tokens` followed by
|
||
`token limits:` text lines, so scripts must pass `--json`.
|
||
|
||
The Console's Assistant section (signed-in send mode) also sets an instance's
|
||
seat name, as `scripts/mosaic name` does, and stops the assistant's runs in the
|
||
human's threads. The chief of staff makes the same changes with its
|
||
`change_organization`, `set_name` and `end_run` tools. Until row 134 gives it a
|
||
revision read, the revision comes from the human.
|
||
|
||
Convert an old organization file with `scripts/mosaic limits migrate --file
|
||
ORGANIZATION --models MAPPING --roles-dir roles > CONVERTED`. It needs no bus,
|
||
never edits the input or system config and has no write flag. Inherited models
|
||
require a valid system config and an exact matching provider/model mapping.
|
||
Mapping JSON contains `models`,
|
||
`families` and optional per-role `instances` selections. Jason checks and installs
|
||
the output in Mos's window with the PM run idle; re-pin in that same window.
|
||
Never migrate the live file from an agent session.
|
||
|
||
|
||
### Local project records (`mosaic project`)
|
||
|
||
`mosaic project create` and `mosaic project list` work on the bus record of a
|
||
business's projects; `mosaic project init` and `mosaic project check` work on
|
||
the repository on disk. Threads (`mosaic threads`, `mosaic thread create|open|read`,
|
||
`mosaic talk --thread`, `mosaic follow --thread`) are described in
|
||
`packages/cli/README.md`; a plain `mosaic talk` to the threaded instance exits 2
|
||
and asks for `--thread`.
|
||
|
||
`mosaic project init <business> <project>` creates the declared project's
|
||
separate local record repository, with layout version 1, package-validated JSON
|
||
templates, Markdown spec/docs and its first commit authored as the business's
|
||
human. Init refuses an agent environment, an undeclared project or a non-empty
|
||
directory. Failed init cleans up its own layout and allows retry, preserving a
|
||
pre-existing empty target and unrelated files. A changed owned path refuses
|
||
`project-init-cleanup` for inspection. Records may not overlap any declared
|
||
project's code root. No bus or remote is involved.
|
||
|
||
`mosaic project check <business> <project>` validates project metadata, every
|
||
record, filename/ID agreement and the fixed layout, naming each problem. It
|
||
refuses `project-no-commit` if the layout has no readable HEAD commit. It
|
||
exits 0 when valid, 2 for project/record problems (including an undeclared
|
||
project), 3 for configuration loading problems and 4 for usage. The optional business project `repo` is a normalized absolute
|
||
record-repository path; the default is
|
||
`<dataRoot>/projects/<business>/<project>/`. `root` remains the code checkout.
|
||
The package README describes schemas and the shared agent record writer; agent
|
||
writes never enter `spec/`, never absorb pending edits and refuse remotes.
|
||
|
||
### Token usage (row 124)
|
||
|
||
`mosaic usage [--business ID] [--json]` reads token accounting from the bus.
|
||
Each total, run, instance, model, provider and account group shows input,
|
||
output, cache write, cache read and unknown turns. A malformed or unavailable
|
||
transcript contributes an unknown turn, never a zero-token reading. Accounts
|
||
are unassigned until account tracking ships. Exact totals larger than the
|
||
safe integer range are decimal strings in JSON. Settings has the same
|
||
read-only Token usage section. Counts are a best reading, not proof: the
|
||
session can edit its own transcript. This command changes no setting and
|
||
performs no token-limit enforcement.
|
||
|
||
### Console Chat (row 129, #1623)
|
||
|
||
The Console's **Chat** section uses `#/chat`, `#/chat/projects/UUID` and
|
||
`#/chat/threads/UUID`. Create and name a project and its threads, then send a
|
||
message in that thread's conversation. Names are creation-only. The project
|
||
catalog is business-wide; thread lists and histories belong to the current
|
||
human. This is separate from the Board's seat reply route.
|
||
|
||
`GET/POST /api/chat/projects` lists/creates projects; `GET/POST /api/chat/threads`
|
||
lists/creates threads (`project=UUID` optionally filters the GET).
|
||
`GET /api/chat/threads/UUID/messages?limit=100&after=UUID` pages history forward;
|
||
`POST` on that messages route sends `{body}` as a REQUEST.
|
||
`POST /api/chat/threads/UUID/open` takes `{}` and explicitly resumes the assistant.
|
||
GET never writes, opens a thread or marks messages delivered/read. No browser
|
||
owner, tenant, proof or arbitrary bus verb is accepted. A same-business thread
|
||
owned by someone else is 403 `thread-not-owned`; foreign/missing ids are 404.
|
||
|
||
The four chat POSTs use the existing signed-in `--send` boundary: named Host,
|
||
one exact proxy user header, exact HTTPS Origin, same-origin Fetch Metadata,
|
||
strict JSON and 16 KiB. Read-only mode disables them. Bodies are inert text;
|
||
drafts are memory-only per thread and cleared on sign-out. History starts
|
||
oldest-first in pages of 100, with a 200-message display window. Sending does
|
||
not claim an assistant answer; an uncertain receipt is never retried. Read the
|
||
current list/history before trying again. Activation still needs Mos's separate
|
||
window; changing these files does not authorize a live Console action.
|