--- id: CLI.1 status: ratified ratified: 2026-09-01 (Jason Woltje; PRD rev1 ratification PR) --- # CLI.1 — CLI surface and parity obligation ## The rule The CLI is the **primary execution method** (D8); the WebUI operates the same tooling over the Gateway API and never bypasses it (D12). Register OD-49 fixes `mosaic config` as the stable installation-configuration command family backed by one desired-state engine; register OD-53 makes every interface (CLI, TUI, WebUI, API, automation) a client of that same engine. Parity is therefore **structural, not aspirational**: a capability that exists in the CLI without a WebUI surface is an incomplete projection; a WebUI wish with no backing tool is **"blocked on tooling"** and the tool is built first (D8 consequence). Neither side ever grows private logic. ## Parity matrix obligation The ratified bundle must carry (or cite, per D8's baseline inputs) three artifacts, kept current: 1. **Tool inventory** — what official tooling exists and what is missing. 2. **WebUI→tool mapping** — every page control mapped to the tool it calls ([[UI.1-webui-surfaces]] page inventory is the row source). 3. **Measured `next`-branch state** — what actually works today. All three artifacts were measured 2026-08-31 against `origin/next` commit `9aa4983c` and appear below. Grill: [[GOV.5-open-questions]] Q-C1 (matrix freshness ownership after ratification). ## Artifact 1 — tool inventory (measured, `origin/next` @ `9aa4983c`) Registration root: `packages/mosaic/src/cli.ts` (commander); command modules under `packages/mosaic/src/commands/`; `coord`/`prdy`/`doctor`/runtime launchers dispatch to bash tools under `packages/mosaic/framework/tools/` (subcommand tables at `commands/launch.ts:1151–1255`); sibling packages (`brain`, `forge`, `macp`, `quality-rails`, `log`, `memory`, `queue`, `storage`) register their own families. Control-plane-relevant families, by rev1 domain: | Domain | Families (measured) | |---|---| | SEAT | `fleet` (init/install/systemd/lifecycle/roster/add/remove/verify/ps), `fleet` roster-v2 CRUD (`get/plan/create/update/delete`), `fleet apply`/`reconcile`/`doctor`/`regen`, `fleet provision`, `fleet migrate-v1 preview`, `agent` (configs + enroll + nested fleet-agent commands), `promote`, `comms send` | | ROLE | `fleet persona` (`list/show/customize` — baseline ⊕ `roles.local/` overrides), `fleet profile` (`list/show` topology templates) | | HARN | `config` (framework config + hooks), `compose-contract `, `skill`, `seq`, `init`/`sync`/`bootstrap`, `doctor`, runtime launchers (`claude`/`codex`/`opencode`/`pi`, experimental `claudex`, `yolo`) | | PROV | `gateway config` (raw provider API-key env vars only), `wizard` (setup-time provider config) | | AUTHN | `login`, `auth users {list,create,delete}`, `auth sso {list,test}` (stubbed — see gaps), `auth sessions list` (stubbed), `gateway` token lifecycle (`config rotate-token/recover-token`) | | SESS | `tui`, `sessions {list,resume,destroy}`, `interaction` (durable-session surface: enroll/attach/send/chat/stop/recover), `coord`, `watch`, `mission` | | Governance/other | `prdy {init,update,validate,status}`, `federation {grant,peer}`, `macp tasks gate`, `telemetry`, `upgrade`/`update`/`restore`/`uninstall`, `q`, sibling-package families | Notable structural facts: there is **no top-level `mosaic role` verb** — role management lives at `fleet persona`, three levels deep; and `doctor`/`status` exist twice (top-level framework-scoped vs `fleet`-scoped), shadowing by name. ## Artifact 2 + 3 — WebUI→tool mapping with measured state Rows are the [[UI.1-webui-surfaces]] page domains; measured against `apps/web/src/spa/pages/` and the gateway controllers on the same commit. | Surface function | WebUI today | CLI today | Parity state | |---|---|---|---| | Seat lifecycle & roster (SEAT) | **no Seats page** (routes are only /admin, /settings, /projects, /tasks, /chat) | complete (`fleet`/`agent` families) | CLI-ahead — page is D2's work, tooling exists | | Role/persona config (ROLE) | no page | `fleet persona` | CLI-ahead; naming mismatch: no `mosaic role` verb for the ROLE page to mirror | | User role/ban (AUTHN) | admin UsersTab toggles role/ban via admin endpoints directly | `auth users` lacks `set-role`/`ban`/`unban` | **WebUI-only mutation — violates the D12 rule as implemented** | | SSO admin (AUTHN) | SsoProviderSection reads *public* `/api/sso/providers` discovery | `auth sso list/test` stubbed: "admin endpoint missing" | **blocked on gateway tooling**; CLI and WebUI don't even hit the same surface | | Auth-session admin (AUTHN) | — | `auth sessions list` stubbed (no server endpoint) | blocked on gateway tooling | | Provider list/test (PROV) | settings ProvidersTab: `GET /api/providers`, `POST /api/providers/test` | none — only `gateway config` raw env-var writes | **WebUI-only read/test — no `mosaic provider` family exists** | | Default harness/provider/model selection (HARN/SESS) | `GET/PUT /api/chat/preferences/selection` per user | none persists the stored preference (`tui --model` is per-session only) | WebUI-only mutation | | Authorization hierarchy & grants (UI-audit) | **no page** | **no command** | **the largest D12 gap**: `hierarchy.controller.ts` exposes full CRUD (companies, estates, platform-projects, grants incl. `grants/:id/change`) with audit repository and grant evaluation behind it — reachable only by raw API | | Federation grants/peers | no page | `federation grant/peer` | CLI-ahead (posture pending Q-T1) | **Consequences for the build order** (D8: tool first, then surface): `mosaic provider {list,test}`, `auth users {set-role,ban,unban}`, a stored harness-selection command, the missing gateway admin endpoints for SSO/session listing, and a CLI face for the hierarchy/grant surface all precede their pages. The two **WebUI-only mutations** (role/ban toggle, harness selection) are standing D12 violations to remediate, not precedents to extend. The hierarchy CRUD surface is the natural backing for [[UI.1-webui-surfaces]]'s authorization audit page — but it must get a CLI face and an audit read-path before the page ships. ## Command families in scope for the control plane `mosaic config` (OD-49 desired-state engine), `mosaic coord` (agent coordination boundary — register OD-09), `mosaic prdy` (PRD creation/acceptance — register OD-22), role management (one canonical API, L2-D14), seat lifecycle (launch/relaunch per register OD-59), `mosaic doctor` (drift detection classes, e.g. the #1194 framework-tool drift addendum in [[GOV.4-workstream-contracts]]). ## `mosaic config` v1 subset (pulled 2026-08-31 from the minimal-subset spec) The Q14 ruling (2026-08-29) fixes the current scope: shipped surface `edit/get/set/show/hooks/path` **plus exactly two new read-only verbs** — `mosaic config validate` and `mosaic config plan` (`--file` | `--preset`, mutually exclusive; `--format table|json`). `apply`, `add`, `restructure`, `migrate`, `remove`, `export` are **out of v1 pending a full-engine ruling** ([[GOV.5-open-questions]] Q-D5) — the configuration-lifecycle draft's "stable namespace" table describing the full family is aspirational, not current state. Contract highlights: **valid** vs **conformant** are distinct verdicts with distinct exit codes (nonconformance is a diagnostic, not a parser failure); `plan` emits `create|update|blocked` operations with risk classes (`none|review-required|full-engine-required`), a SHA-256 `planId`, and `applySupported: false` always in v1; destructive/unsupported drift is `blocked`, never silently normalized; absolute no-mutation during validate/plan (no writes, no network, no credential calls); results ride the T78 `CapabilityResultV1` envelope (capability IDs `config.installation.validate`/`.plan`); inputs capped, YAML aliases/anchors/tags rejected, no secret-shaped fields accepted or echoed. ## E2 inputs (triage 2026-08-31; see [[GOV.2-docs-inventory]]) **Canonical ground truth**: `requirements/cli-capability-migration.md` (T78, `source_of_truth: true`), `fleet/reference/cli.md` (local fleet CLI vs gateway-backed catalog), `USER-GUIDE/getting-started/quickstart.md`. **Pending pulls**: DRAFT S2 `tool-gateway-mapping.md`; brain `docs/specs/2026-08-29_mosaic-config-minimal-subset.md` (`mosaic config validate/plan`, cites OD-49–OD-55) and `docs/guides/proposed/workflows/configuration-lifecycle.md` (the OD-49 engine family definition). ## S2 contract feed (extraction 2026-08-31) Full extraction record: lane `S2-EXTRACTION-2026-08-31.md` (per-contract cores, dependency edges, ruling cross-checks). Pulls binding on this section: - **Contract 5 §4.5 is the parity clause this section's matrix enforces**: CLI remains the primary execution method for every Gateway command; no WebUI-only command exists; a Gateway command without CLI exposure is a conformance gap tracked at the family's implementing issue. The hierarchy/grants CRUD gap measured in this section is exactly such a tracked conformance gap once contract 5 ratifies. - **Command envelope**: typed request/result DTOs (no `any`), closed per-family error taxonomy, audit correlation id, fail-closed — aligns with the T78 `CapabilityResultV1` direction already in this section. - **Contract 9 (api-artifacts)**: `ApiAuthClass` closed six-value enum (`none`/`session`/`api-key`/`admin`/`federation`/`bootstrap`); OPENAPI.yaml generated, CI byte-drift-gated, never hand-edited; hard ordering — nothing under contract 9 lands before contract 5 (PR #1438) is on the trunk. - **Roll-up (contract 8)** ships as a query-only tool with no command counterpart (A5 rank 5) — the taxonomy precedent for read-only surfaces in the parity matrix. - **Mandated removals** the CLI inventory must track: `mosaic auth users delete` (with the hard-delete endpoint) is required to be disabled/removed by contract 4. ## Parity freshness ruling (Q-C1, Jason 2026-09-01) The parity matrix becomes a generated artifact with a CI drift-gate witness in the stack repo (the contract-9 pattern): CI regenerates the tool inventory and WebUI→tool mapping from code and fails on divergence from the committed matrix. No human cadence to forget. Building the witness is E6-return follow-up work.