Two changes, both about a test seam that alters production behaviour. AMD1213-D defect D5 objected that `launchFleetRuntimeForTest` was an exported production API that also set `recordLaunch:false`, changing a second production branch beyond the two the card authorized. Most of that is already closed in this tree: the exported helper is gone, and the specs now enter through the real `registerFleetLaunchCommand -> apply -> launchFleetRuntime -> launchRuntime` route on a fixture seat, with the ledger pointed at the fixture and asserted (`fleet-launch-command.spec.ts` asserts `events.ndjson` contains the record). The seat-seeded/HOME-empty pass and HOME-seeded/seat-empty fail pair both exist. What remained was the `recordLaunch?: boolean` context field itself. Nothing in the package sets it -- it is a dead switch whose only effect was to let a caller silently disable launch recording on the claude branch while codex, opencode and pi recorded unconditionally. Removed, so all four branches record the same way and the asymmetry cannot be reintroduced by passing a flag. The second change is unrelated to D1-D6 and is called out as such. It is here because the amend's required evidence includes a green full-package Vitest run, and one spec made that non-reproducible. `install-ordering-guard.spec.ts` proves that `guardClaudeSettingsWiring` really delegates to `leaseEnforcementActivatable()` by comparing the guard's outcome against its own call to the same predicate. That predicate is not deterministic: `defaultCapabilityProbe` runs `dist/cli.js` out-of-process with a 2000 ms timeout. In a full-package run with 86 spec files scheduled at once, one observation beats that timeout and the next does not, the two disagree, and the test fails -- reporting machine load as a wiring defect. It passed in isolation every time, which is why it read as a flake. Measured rather than assumed. The failure reproduced in three consecutive full runs and passed 3/3 in isolation. It was NOT caused by the recordLaunch removal above: reverting only that edit and re-running the full suite still failed, which is what ruled my own change out. The guard call is now bracketed by two observations of the predicate, and only a pair that agrees is used as ground truth; a disagreeing pair is retried, up to three attempts, and never holding still is itself a failure rather than a skip. This does not weaken the assertion -- a real delegation failure is stable and survives every attempt while load noise is not. Falsified: inverting the guard's default to `!leaseEnforcementActivatable()` turns the test red (1 failed / 18 passed), so the retry did not blunt what the test detects. The inversion was reverted and the file confirmed clean. Verification: typecheck RC=0. Three consecutive full-package runs, RC=0, 86 files / 1619 tests passed, 0 failed, under the sanitized lease environment (MOSAIC_LEASE_* and MOSAIC_RUNTIME_GENERATION stripped). Commit-only per scrappy's controlling packet (comms 20260813T212447Z dc43de): not pushed, PR #1213 not updated, nothing re-authored.
@mosaicstack/mosaic
CLI package for the Mosaic self-hosted AI agent platform.
Usage
mosaic wizard # First-run setup wizard
mosaic gateway install # Install the gateway daemon
mosaic config show # View current configuration
mosaic config hooks list # Manage Claude hooks
Headless / CI Installation
Set MOSAIC_ASSUME_YES=1 (or ensure stdin is not a TTY) to skip all interactive prompts. The following environment variables control the install:
Gateway configuration (mosaic gateway install)
| Variable | Default | Required |
|---|---|---|
MOSAIC_STORAGE_TIER |
local |
No |
MOSAIC_GATEWAY_PORT |
14242 |
No |
MOSAIC_DATABASE_URL |
(none) | Yes if tier=team |
MOSAIC_VALKEY_URL |
(none) | Yes if tier=team |
MOSAIC_ANTHROPIC_API_KEY |
(none) | No |
MOSAIC_CORS_ORIGIN |
http://localhost:3000 |
No |
Admin user bootstrap
| Variable | Default | Required |
|---|---|---|
MOSAIC_ADMIN_NAME |
(none) | Yes (headless) |
MOSAIC_ADMIN_EMAIL |
(none) | Yes (headless) |
MOSAIC_ADMIN_PASSWORD |
(none) | Yes (headless) |
MOSAIC_ADMIN_PASSWORD must be at least 8 characters. In headless mode a missing or too-short password causes a non-zero exit.
Example: Docker / CI install
export MOSAIC_ASSUME_YES=1
export MOSAIC_ADMIN_NAME="Admin"
export MOSAIC_ADMIN_EMAIL="[email protected]"
export MOSAIC_ADMIN_PASSWORD="securepass123"
mosaic gateway install
Runtime launchers
mosaic claude # Launch Claude Code with Mosaic injection
mosaic yolo claude # …with --dangerously-skip-permissions
mosaic codex | opencode | pi
mosaic claudex (EXPERIMENTAL)
Runs GPT models inside the Claude Code harness by pointing Claude Code at a
local claude-code-proxy that
translates the Anthropic Messages API to a ChatGPT-subscription (Codex OAuth)
backend. This is not Anthropic Claude — model behavior, tool use, and output
quality may differ. Intended for evaluation, not production delivery.
mosaic claudex # launch (prompts through the proxy readiness gate)
mosaic yolo claudex # …with --dangerously-skip-permissions
mosaic claudex --print "hello" # trailing args are forwarded to Claude Code
Prerequisite: the claude-code-proxy binary must be installed and
authenticated (claude-code-proxy codex auth …). mosaic claudex runs a
preflight that verifies the binary, the OAuth state (triggering a device re-auth
if needed), and a trusted local listener before launching; it fails closed
if the proxy cannot be brought up with a verified identity.
Isolation (never touches your real Claude state). claudex always launches
against an isolated CLAUDE_CONFIG_DIR (default ~/.config/mosaic/claudex/home).
The ambient CLAUDE_CONFIG_DIR is deliberately ignored, and a guard proves the
resolved dir can never be — or live under — the real ~/.claude. A claudex
session therefore cannot mutate your normal Claude Code config.
No token leakage. claudex never reads the proxy's credential file. Claude
Code is handed only ANTHROPIC_AUTH_TOKEN=unused pointed at the loopback proxy;
the entire credential-bearing env family (ANTHROPIC_*, AWS_*, GOOGLE_CLOUD_*,
GOOGLE_APPLICATION_CREDENTIALS, *_TOKEN, *_KEY, *_SECRET, …) is stripped
from the composed environment. The Bedrock/Vertex routing switches
(CLAUDE_CODE_USE_BEDROCK, CLAUDE_CODE_USE_VERTEX, and the _SKIP_*_AUTH
pair) are force-removed regardless of value — otherwise their mere presence
would route Claude Code to the real Anthropic API via AWS/GCP and bypass the
proxy. The proxy holds the real OAuth credential.
Model tiers (override via env).
| Tier | Env var | Default |
|---|---|---|
| primary (opus/sonnet) | ANTHROPIC_MODEL |
gpt-5.6-sol |
| small/fast (haiku) | ANTHROPIC_SMALL_FAST_MODEL |
gpt-5.6-luna |
Operator-provided values win over the defaults. Additional overrides:
MOSAIC_CLAUDEX_CONFIG_DIR (isolated config dir), ANTHROPIC_BASE_URL (proxy
endpoint).
Hooks management
After running mosaic wizard, Claude hooks are installed in ~/.claude/hooks-config.json.
mosaic config hooks list # Show all hooks and enabled/disabled status
mosaic config hooks disable PostToolUse # Disable a hook (reversible)
mosaic config hooks enable PostToolUse # Re-enable a disabled hook
Set CLAUDE_HOME to override the default ~/.claude directory.