Shapes and workspaces

Why metro works in shapes, and sandbox versus production.

View .md

metro has two ways to describe a change: shapes, which are checked offline, and ops, which read your tenant and write a plan. Workspace packs decide which billing intents metro knows about. And every live call names a tenant.

Shapes

A shape is a reviewed, local contract for one kind of change, in JSON or YAML. metro validates it before any Metronome client loads, so a malformed payload never reaches the API.

metro shape list
metro shape explain free-credit-trial
metro validate --shape free-credit-trial --file trial.json --json
metro package preview --shape free-credit-trial --file trial.json --sandbox --json

A shape encodes the traps for you. free-credit-trial, for example, enforces commit_duration.unit: PERIODS, a string duration.value, starting_at_offset and proration, "so agents never hand-craft this payload".

Each shape has a maturity and blocked modes. Today every shape is local-validated: explain, validate and preview work; apply through the shape is blocked. Live writes go through ops instead. The full list is on Shapes; the rules they check are on Validators.

Ops

An op is a live command with a preflight and an apply: metro customer create, metro contract move-plan, metro ledger-adjustment and the rest. Preflight reads the tenant, so it can check things a shape can't: that an alias is free, that a balance exists, that a contract doesn't overlap. See the safety contract.

Shape previewOp preflight
Talks to MetronomeNoReads only
OutputA payload plan and a sha256: hashA plan file with exact requests and a payloadHash
Can applyNot in this releaseYes, with --confirm-hash and a typed token

Workspace packs

metro is split in two. metro-core knows the Metronome API, the shapes and the evidence gates. A workspace pack adds billing intents on top, like "move a customer to another package". The public build ships the generic pack.

metro workspace list
metro workspace current
metro plan list

metro plan list shows the pack's intents, and metro plan <intent> --sandbox --json maps one to its best shape, the paths it rejects, and the commands to run. The generic pack binds to your own catalog through metro catalog bind; it has no hardcoded IDs.

The active pack resolves in this order: metro workspace use, then METRO_WORKSPACE_PACK, then the session, then the profile, then ~/.metro/config.json, then the built-in default.

Sandbox and production

Sandbox and production are two Metronome tenants behind one base URL, https://api.metronome.com. The key picks the tenant.

SandboxProduction
KeyMETRONOME_SANDBOX_KEYMETRONOME_PROD_KEY
Flag--sandbox--prod
Typed token for a write--confirm-live-apply=I_UNDERSTAND_THIS_WILL_WRITE_LIVE_METRONOME_SANDBOX--confirm-prod-apply=I_UNDERSTAND_BILLING_SIDE_EFFECTS
WhenAlways firstAfter the sandbox readback passes, with a typed yes for that change

metro refuses a sandbox key equal to the production key, in either direction. A plan's hash covers its tenant, so a sandbox plan can't be applied to production.

Tenant snapshots

metro catalog bind --sandbox --write saves your IDs to ~/.metro/profiles.json, and a versioned model to ~/.metro/snapshots/sandbox.json. metro tenant snapshot diff --sandbox --markdown shows what changed since the last bind, and --against=production compares the two tenants.

On this page