xmatix metadata round-trips the tenant's metadata — entities, fields, views, validations, relations and field mappings — as a single seed-pack JSON file. Pull writes a pretty-printed, deterministic file designed to be reviewed in git diffs; push imports a tenant-owned seed-pack back; diff compares a local file against a fresh live export without writing anything.
xmatix metadata pull # default scope: TenantOwned
xmatix metadata pull --scope TenantOwned --out xmatix/metadata/tenant-owned.json
xmatix metadata push <file.json>
xmatix metadata diff <file.json> # local file vs fresh server export
Scopes
metadata pull (and diff) accept a --scope; the default output file under xmatix/metadata/ is named for the scope (tenant-owned.json, tenant-full.json, base-metadata.json, …), and --out overrides the path.
| Scope | Purpose |
|---|---|
TenantOwned (default, round-trippable) | Rows the tenant has created or customized |
TenantFull | The tenant's view including base metadata visible to it |
BaseMetadata | The platform-shipped baseline |
PlatformModule | A specific platform module (requires --package-id <id>) |
PackageOwned | A specific installed package (requires --package-id <id>) |
Namespace | All metadata in a logical namespace (requires --namespace <ns>) |
Only TenantOwned is safe to round-trip through metadata push — the import endpoint updates only rows the tenant owns and ignores package-owned rows, so the other scopes are effectively read-only exports for inspection and comparison. pull also accepts --mode full|delta (default full) and --version <n> to select the seed-pack version.
Push
xmatix metadata push xmatix/metadata/tenant-owned.json
Push validates the JSON locally before sending, then shows what it is about to import and asks for confirmation — pass --yes to skip the prompt in pipelines. --source-subscription <guid> supports importing a seed-pack that was exported from a different tenant, so its internal references resolve against the source. The server-side import is the authority on what changes: tenant-owned rows are created or updated, everything else is ignored.
Diff
xmatix metadata diff <file.json> exports a fresh seed-pack into memory and compares it against the local file using deterministic JSON ordering. The output is grouped by table — Entities, EntityFields, EntityViews and so on — with added, removed and modified row counts plus samples of the differing values. Its natural job is a drift gate: run it in a pipeline (or a pre-commit hook) and fail when the live tenant no longer matches the last reviewed export, so untracked in-product changes surface as a diff instead of a surprise. For a drift gate across all artifact kinds, use migrate plan.
Common questions
Why is only TenantOwned round-trippable?
Because ownership is the write boundary. Base metadata belongs to the platform and package metadata belongs to its package — importing either from a file would let a tenant overwrite rows it does not own, so the import endpoint only applies rows the tenant owns. Pull the wider scopes when you need to see the full picture; push only what the tenant can legitimately change.
Does metadata push delete rows I removed from the file?
No — the import creates and updates, it does not delete. Removing an entity or field is a deliberate act with data consequences, so it stays a manual operation in the product. The removed section of a migrate plan tells you what exists on the server but not locally, so nothing disappears silently in either direction.
How do I put a metadata change through pull-request review?
Make the change (in the product or by editing the seed-pack), run xmatix metadata pull, and commit — the deterministic formatting keeps the diff scoped to what actually changed. Reviewers read the JSON diff like any code change; on merge, a pipeline runs xmatix metadata push --yes (or a full migrate apply) against the target tenant.
