Skip to content

Administration Commands

These commands read what already happened and maintain what Rocky keeps. They cover run history, quality metrics, and cost; storage optimization, compaction, profiling, and archival; the embedded state store; and the governance rollups.


Show past pipeline runs from the embedded state store. Each run reports its duration, its status, and the detail for every model it touched.

Terminal window
rocky history [flags]
Flag Type Default Description
--model <NAME> string Filter history to a specific model.
--since <DATE> string Only show runs since this date (ISO 8601 or YYYY-MM-DD).
--audit bool false Include the governance audit trail for each run in JSON output, and print a second governance table after the default summary in text output. See Audit trail below.

Show all recent run history:

Terminal window
rocky history
{
"version": "1.6.0",
"command": "history",
"count": 2,
"runs": [
{
"run_id": "run_20260401_143022",
"started_at": "2026-04-01T14:30:22Z",
"duration_ms": 45200,
"status": "Success",
"trigger": "Manual",
"models_executed": 14,
"models": [
{ "model_name": "stg_orders", "duration_ms": 1200, "rows_affected": 150000, "status": "success" },
{ "model_name": "fct_revenue", "duration_ms": 2300, "rows_affected": 8900, "status": "success" }
]
},
{
"run_id": "run_20260401_080015",
"started_at": "2026-04-01T08:00:15Z",
"duration_ms": 52100,
"status": "PartialFailure",
"trigger": "Manual",
"models_executed": 13,
"models": [ /* per-model records */ ]
}
]
}

Show history for a specific model since a date. The --model variant returns a flat list of that model’s executions:

Terminal window
rocky history --model fct_revenue --since 2026-03-01
{
"version": "1.6.0",
"command": "history",
"model": "fct_revenue",
"count": 2,
"executions": [
{
"started_at": "2026-04-01T14:30:22Z",
"duration_ms": 2300,
"rows_affected": 8900,
"status": "success",
"sql_hash": "a3f2b1c4..."
},
{
"started_at": "2026-03-31T14:30:05Z",
"duration_ms": 8900,
"rows_affected": 150000,
"status": "success",
"sql_hash": "b4e3c2d5..."
}
]
}

sql_hash is stable across runs where the compiled SQL is identical; useful for detecting model body changes across runs.

Show history with table output:

Terminal window
rocky -o table history --since 2026-04-01
RUN ID STARTED STATUS MODELS TRIGGER
------------------------------------------------------------------
run_2026040 2026-04-01 14:30:22 Success 14 Manual
run_2026040 2026-04-01 08:00:15 Failure 13 Manual

The RUN ID column is truncated to 11 characters and the timestamp is rendered without the T/Z separators.

--audit (added in v1.16.0) expands each run record with an eight-field governance trail captured by every rocky apply (and the rocky run alias) against redb schema v6. Default output omits these fields for byte-stability with pre-v1.16 consumers.

Field Description
triggering_identity Principal that initiated the run (OS user, CI service account, etc.).
session_source One of cli, dagster, lsp, http_api, auto-detected at run start.
git_commit Commit SHA at the project root (None when not a git repo).
git_branch Branch name at the project root (None when not a git repo).
idempotency_key Echoed value of --idempotency-key (None when the flag wasn’t used).
target_catalog Resolved target catalog for the executed pipeline.
hostname Hostname where the run executed. Always populated (defaults to "unknown" on pre-v6 rows).
rocky_version CARGO_PKG_VERSION at run time. Always populated ("<pre-audit>" on pre-v6 rows).
Terminal window
rocky history --audit
{
"version": "1.16.0",
"command": "history",
"count": 1,
"runs": [
{
"run_id": "run_20260423_143022",
"started_at": "2026-04-23T14:30:22Z",
"duration_ms": 45200,
"status": "Success",
"trigger": "Manual",
"models_executed": 14,
"models": [ /* per-model records */ ],
"triggering_identity": "alice@acme.io",
"session_source": "dagster",
"git_commit": "a3f2b1c4...",
"git_branch": "main",
"idempotency_key": "nightly-2026-04-23",
"target_catalog": "acme_warehouse",
"hostname": "runner-12",
"rocky_version": "1.16.0"
}
]
}

Text output appends a Governance audit trail (--audit): section after the default run summary with short-form columns (git_commit truncated to 8 chars, hostname to 11) plus a per-run detail line carrying rocky_version and idempotency_key.

  • rocky apply – execute a planned pipeline (rocky run continues to work as an alias)
  • rocky state – view current watermarks
  • rocky metrics – view quality metrics for a model

Show quality metrics for a model, including row counts, null rates, freshness, and trend data across recent runs.

Terminal window
rocky metrics <model> [flags]
Argument Type Default Description
model string (required) Model name to show metrics for.
Flag Type Default Description
--trend bool false Show metric trends over recent runs.
--column <NAME> string Filter to a specific column.
--alerts bool false Show quality alerts (anomalies, threshold breaches).

Show metrics for a model. Output carries one snapshot per recent run with row count, freshness lag, and per-column null rates:

Terminal window
rocky metrics fct_revenue
{
"version": "1.6.0",
"command": "metrics",
"model": "fct_revenue",
"count": 1,
"snapshots": [
{
"run_id": "run_20260401_143022",
"timestamp": "2026-04-01T14:30:22Z",
"row_count": 148203,
"freshness_lag_seconds": 300,
"null_rates": {
"customer_id": 0.0,
"revenue_month": 0.0,
"net_revenue": 0.0
}
}
]
}

--trend keeps the same snapshot shape but returns multiple entries (one per recent run):

Terminal window
rocky metrics fct_revenue --trend
{
"version": "1.6.0",
"command": "metrics",
"model": "fct_revenue",
"count": 3,
"snapshots": [
{ "run_id": "run_20260401_143022", "timestamp": "2026-04-01T14:30:22Z", "row_count": 148203, "null_rates": { /* … */ } },
{ "run_id": "run_20260331_143005", "timestamp": "2026-03-31T14:30:05Z", "row_count": 145890, "null_rates": { /* … */ } },
{ "run_id": "run_20260330_143010", "timestamp": "2026-03-30T14:30:10Z", "row_count": 143200, "null_rates": { /* … */ } }
]
}

--alerts adds a non-empty alerts array (omitted otherwise). --column <name> sets the top-level column field and populates column_trend with per-run null-rate points:

Terminal window
rocky metrics fct_revenue --column net_revenue --alerts
{
"version": "1.6.0",
"command": "metrics",
"model": "fct_revenue",
"column": "net_revenue",
"count": 3,
"snapshots": [ /* … */ ],
"column_trend": [
{ "run_id": "run_20260401_143022", "timestamp": "2026-04-01T14:30:22Z", "null_rate": 0.023 },
{ "run_id": "run_20260331_143005", "timestamp": "2026-03-31T14:30:05Z", "null_rate": 0.0 }
],
"alerts": [
{ "type": "anomaly", "severity": "warning", "run_id": "run_20260401_143022", "column": "net_revenue", "message": "null rate rose from 0.0% to 2.3% vs. 7-run baseline" }
]
}
  • rocky history – view execution history for the model
  • rocky apply – execute a planned pipeline to generate fresh metrics
  • rocky optimize – get strategy recommendations based on metrics

Analyze materialization costs and recommend strategy changes. Reviews execution history, row counts, and query patterns to suggest whether a model should use incremental, full refresh, or table materialization.

Terminal window
rocky optimize [flags]
Flag Type Default Description
--model <NAME> string Filter to a specific model. If omitted, analyzes all models.

Analyze all models. Each recommendation includes the current and suggested strategy, a free-text reasoning, and an estimated monthly compute savings:

Terminal window
rocky optimize
{
"version": "1.6.0",
"command": "optimize",
"total_models_analyzed": 3,
"recommendations": [
{
"model_name": "fct_revenue",
"current_strategy": "incremental",
"recommended_strategy": "incremental",
"estimated_monthly_savings": 0.0,
"reasoning": "Incremental is optimal. Average 2.3s per run, 1.2% of rows processed each run."
},
{
"model_name": "dim_customers",
"current_strategy": "full_refresh",
"recommended_strategy": "incremental",
"estimated_monthly_savings": 8.50,
"reasoning": "Full refresh takes 18.5s and processes 250K rows. Only 0.3% change rate between runs — switching to incremental saves ~17s per run."
},
{
"model_name": "stg_events",
"current_strategy": "incremental",
"recommended_strategy": "full_refresh",
"estimated_monthly_savings": 0.25,
"reasoning": "Drift detected in 4 of last 5 runs, triggering full refresh anyway. Switching to full_refresh avoids drift detection overhead."
}
]
}

Analyze a single model:

Terminal window
rocky optimize --model dim_customers

Same recommendations shape, single entry. When compile-time incrementality analysis offers additional opportunities, Rocky populates an incrementality_note pointing to rocky compile --output json.


Generate OPTIMIZE and VACUUM SQL for storage compaction on Delta tables. Combines small files, removes old versions, and optionally targets a specific file size.

Terminal window
rocky compact <model> [flags] # generate + persist a compaction plan
rocky compact --catalog <name> [flags] # every Rocky-managed table in the catalog
rocky compact --measure-dedup [flags] # experimental, project-wide scope
rocky compact apply <plan-id> [flags] # execute a previously-generated plan

Exactly one scope is required for plan generation: a fully-qualified <model>, --catalog <name>, or --measure-dedup. The three forms are mutually exclusive. rocky compact apply <plan-id> is a separate subcommand that executes a plan persisted by an earlier generate run.

Argument Type Default Description
model string (one scope required) Target table in catalog.schema.table format.
Flag Type Default Description
--catalog <NAME> string Aggregate per-table OPTIMIZE/VACUUM SQL across every Rocky-managed table in the catalog. The managed-table set is resolved from the pipeline config (replication discovery or transformation model files); no warehouse round trip. Mutually exclusive with <model> and --measure-dedup. Errors with the available catalogs listed if no managed tables match.
--target-size <SIZE> string Target file size (e.g., 256MB, 512MB, 1GB).
--dry-run bool false Show SQL without executing.
--measure-dedup bool false Experimental. Measure the cross-table dedup ratio across all Rocky-managed tables in the project (Layer 0 storage experiment). Project-wide; does not take a model argument.
--exclude-columns <COLS> string Rocky-owned metadata cols Comma-separated columns to exclude from the “semantic” dedup hash. Defaults to _loaded_by,_loaded_at,_fivetran_synced,_synced_at. Requires --measure-dedup.
--calibrate-bytes bool false Run byte-level calibration on a sampled subset of tables. Produces a sharper but more expensive second estimate alongside the cheap partition-level one. Requires --measure-dedup.
--all-tables bool false Scan all warehouse tables instead of only Rocky-managed ones. Requires --measure-dedup.

rocky compact generates SQL and persists it as a plan under .rocky/plans/<plan-id>.json; it does not execute against the warehouse. The plan id is printed in text output and returned as plan_id in JSON. Pipe the SQL to your warehouse yourself once you’re happy with it, or execute the persisted plan with rocky compact apply <plan-id>.

Compact a table (dry run):

Terminal window
rocky compact acme_warehouse.staging__us_west__shopify.orders --dry-run
{
"version": "1.6.0",
"command": "compact",
"model": "acme_warehouse.staging__us_west__shopify.orders",
"dry_run": true,
"target_size_mb": 256,
"statements": [
{ "purpose": "compact small files", "sql": "OPTIMIZE acme_warehouse.staging__us_west__shopify.orders WHERE true\n -- target file size: 256MB" },
{ "purpose": "remove stale data files", "sql": "VACUUM acme_warehouse.staging__us_west__shopify.orders RETAIN 168 HOURS" }
]
}

With a target file size:

Terminal window
rocky compact acme_warehouse.staging__us_west__shopify.orders --target-size 256MB

The generated OPTIMIZE carries the target file size as a trailing -- target file size: <N>MB comment; target_size_mb echoes the parsed value (e.g. 256 for 256MB). When --target-size is omitted it defaults to 256.

--catalog <name> resolves every Rocky-managed table in the named catalog and emits a single envelope keyed by FQN. The flat statements list still carries every SQL statement across all tables in iteration order; consumers that just want to execute the plan don’t need to walk tables.

Terminal window
rocky compact --catalog acme_warehouse --target-size 256MB --dry-run
{
"version": "1.20.0",
"command": "compact",
"catalog": "acme_warehouse",
"scope": "catalog",
"dry_run": true,
"target_size_mb": 256,
"statements": [
{ "purpose": "compact small files", "sql": "OPTIMIZE acme_warehouse.staging__us_west__shopify.orders WHERE true\n -- target file size: 256MB" },
{ "purpose": "remove stale data files", "sql": "VACUUM acme_warehouse.staging__us_west__shopify.orders RETAIN 168 HOURS" },
{ "purpose": "compact small files", "sql": "OPTIMIZE acme_warehouse.staging__us_west__shopify.events WHERE true\n -- target file size: 256MB" },
{ "purpose": "remove stale data files", "sql": "VACUUM acme_warehouse.staging__us_west__shopify.events RETAIN 168 HOURS" }
],
"tables": {
"acme_warehouse.staging__us_west__shopify.events": {
"statements": [
{ "purpose": "compact small files", "sql": "OPTIMIZE acme_warehouse.staging__us_west__shopify.events WHERE true\n -- target file size: 256MB" },
{ "purpose": "remove stale data files", "sql": "VACUUM acme_warehouse.staging__us_west__shopify.events RETAIN 168 HOURS" }
]
},
"acme_warehouse.staging__us_west__shopify.orders": {
"statements": [
{ "purpose": "compact small files", "sql": "OPTIMIZE acme_warehouse.staging__us_west__shopify.orders WHERE true\n -- target file size: 256MB" },
{ "purpose": "remove stale data files", "sql": "VACUUM acme_warehouse.staging__us_west__shopify.orders RETAIN 168 HOURS" }
]
}
},
"totals": { "table_count": 2, "statement_count": 4 }
}

The single-model envelope is byte-stable: catalog, scope, tables, and totals are all skip_serializing_if = "Option::is_none". The catalog identifier is normalized to lowercase to match the managed-table resolver.

--measure-dedup runs a project-wide analysis that hashes each row on its semantic columns and reports the fraction of duplicate content across Rocky-managed tables. It is a research tool for Rocky’s Layer 0 storage work; the output is a measurement, not a plan, and no SQL is issued against the target tables.

Terminal window
# Cheap partition-level estimate across all Rocky-managed tables
rocky compact --measure-dedup
# Add a byte-level calibration pass on a sampled subset
rocky compact --measure-dedup --calibrate-bytes
# Include every warehouse table, not just Rocky-managed ones
rocky compact --measure-dedup --all-tables
# Customize which metadata columns are excluded from the dedup hash
rocky compact --measure-dedup --exclude-columns "_loaded_at,_loaded_by,_synced_at"

The command emits a distinct compact-dedup JSON shape with per-table contributions and a project-wide summary. Use --output json in CI to track the ratio over time.


Profile the storage layout of a table and recommend column encodings, partitioning strategies, and file format optimizations.

Terminal window
rocky profile-storage <model>
Argument Type Default Description
model string (required) Target table in catalog.schema.table format.

Profile a table. Rocky emits a profile_sql query you can run against the warehouse to gather column cardinalities, plus a per-column recommendations list derived from the schema alone:

Terminal window
rocky profile-storage acme_warehouse.staging__us_west__shopify.orders
{
"version": "1.6.0",
"command": "profile-storage",
"model": "acme_warehouse.staging__us_west__shopify.orders",
"profile_sql": "SELECT column_name, approx_count_distinct(...) FROM ... GROUP BY column_name",
"recommendations": [
{
"column": "status",
"data_type": "STRING",
"estimated_cardinality": "low (< 100)",
"recommended_encoding": "dictionary",
"reasoning": "Low-cardinality string — dictionary encoding dramatically shrinks storage and speeds up filtering."
},
{
"column": "customer_notes",
"data_type": "STRING",
"estimated_cardinality": "high",
"recommended_encoding": "lz4",
"reasoning": "High-cardinality free-text — LZ4 gives the best compression without hurting scan latency."
}
]
}

rocky profile-storage is advisory; it does not run the profile SQL for you. Pipe profile_sql into rocky shell (or any SQL client) to collect the actual cardinality numbers.


Archive old data partitions by moving them to cold storage or deleting them based on an age threshold. Supports dry-run mode for previewing which partitions would be affected.

Terminal window
rocky archive [flags] # generate + persist an archive plan
rocky archive --catalog <name> [flags] # every Rocky-managed table in the catalog
rocky archive apply <plan-id> [flags] # execute a previously-generated plan
Flag Type Default Description
--older-than <DURATION> string (required) Age threshold. Accepted formats: 90d (days), 6m (months), 1y (years).
--model <NAME> string Filter to a specific model. If omitted, archives across all models. Mutually exclusive with --catalog.
--catalog <NAME> string Aggregate per-table archive SQL across every Rocky-managed table in the named catalog. The managed-table set is resolved from the pipeline config (no warehouse round trip). Mutually exclusive with --model. Errors with the available catalogs listed if no managed tables match.
--dry-run bool false Show SQL without executing.

Like compact, archive generates SQL and persists it as a plan under .rocky/plans/<plan-id>.json; it does not execute against the warehouse. It builds a Delta-flavoured DELETE FROM <target> WHERE _fivetran_synced < DATEADD(DAY, -<N>, CURRENT_TIMESTAMP()) plus a trailing VACUUM. Pipe the SQL to your warehouse yourself, or execute the persisted plan with rocky archive apply <plan-id>. The generator is gated to Delta-on-Databricks targets and errors on other dialects.

Preview archival for data older than 90 days:

Terminal window
rocky archive --older-than 90d --dry-run

Without --model or --catalog, the target resolves to a * wildcard placeholder — pass --model <fqn> (below) or --catalog <name> to emit concrete per-table SQL:

{
"version": "1.6.0",
"command": "archive",
"older_than": "90d",
"older_than_days": 90,
"dry_run": true,
"statements": [
{ "purpose": "delete rows older than 90 days", "sql": "DELETE FROM *\nWHERE _fivetran_synced < DATEADD(DAY, -90, CURRENT_TIMESTAMP())" },
{ "purpose": "reclaim storage after deletion", "sql": "VACUUM * RETAIN 0 HOURS" }
]
}

Archive a specific model’s old data. The persisted plan is executed later with rocky archive apply <plan-id>:

Terminal window
rocky archive --older-than 6m --model acme_warehouse.staging__us_west__shopify.events

Same output shape: model is set when --model filters the run, and <target> in the DELETE/VACUUM becomes the fully-qualified table. older_than_days is the parsed duration (6m180), which lets orchestrators compute retention windows without re-parsing the string.

--catalog <name> mirrors rocky compact --catalog: it resolves every Rocky-managed table in the catalog from the pipeline config and aggregates per-table DELETE + VACUUM SQL into a single envelope keyed by FQN. The flat statements list still carries every statement across every table.

Terminal window
rocky archive --older-than 90d --catalog acme_warehouse --dry-run
{
"version": "1.20.0",
"command": "archive",
"catalog": "acme_warehouse",
"scope": "catalog",
"older_than": "90d",
"older_than_days": 90,
"dry_run": true,
"statements": [
{ "purpose": "delete rows older than 90 days", "sql": "DELETE FROM acme_warehouse.staging__us_west__shopify.orders\nWHERE _fivetran_synced < DATEADD(DAY, -90, CURRENT_TIMESTAMP())" },
{ "purpose": "reclaim storage after deletion", "sql": "VACUUM acme_warehouse.staging__us_west__shopify.orders RETAIN 0 HOURS" },
{ "purpose": "delete rows older than 90 days", "sql": "DELETE FROM acme_warehouse.staging__us_west__shopify.events\nWHERE _fivetran_synced < DATEADD(DAY, -90, CURRENT_TIMESTAMP())" },
{ "purpose": "reclaim storage after deletion", "sql": "VACUUM acme_warehouse.staging__us_west__shopify.events RETAIN 0 HOURS" }
],
"tables": {
"acme_warehouse.staging__us_west__shopify.events": {
"statements": [
{ "purpose": "delete rows older than 90 days", "sql": "DELETE FROM acme_warehouse.staging__us_west__shopify.events\nWHERE _fivetran_synced < DATEADD(DAY, -90, CURRENT_TIMESTAMP())" },
{ "purpose": "reclaim storage after deletion", "sql": "VACUUM acme_warehouse.staging__us_west__shopify.events RETAIN 0 HOURS" }
]
},
"acme_warehouse.staging__us_west__shopify.orders": {
"statements": [
{ "purpose": "delete rows older than 90 days", "sql": "DELETE FROM acme_warehouse.staging__us_west__shopify.orders\nWHERE _fivetran_synced < DATEADD(DAY, -90, CURRENT_TIMESTAMP())" },
{ "purpose": "reclaim storage after deletion", "sql": "VACUUM acme_warehouse.staging__us_west__shopify.orders RETAIN 0 HOURS" }
]
}
},
"totals": { "table_count": 2, "statement_count": 4 }
}

The single-model envelope is byte-stable: catalog, scope, tables, and totals are absent on the existing rocky archive and rocky archive --model paths.


Inspect, audit, or re-execute a recorded run from the state store. The default view surfaces the per-model SQL hashes, row counts, bytes, and timings captured by RunRecord at execution time: the concrete artefact behind the reproducibility claim. --check runs a read-only replayability audit, and --execute re-runs the recorded recipes and re-derives each output hash — locally on DuckDB, or against the live warehouse with --warehouse.

Terminal window
rocky replay <target> [flags]
Argument Type Default Description
target string (required) A specific run_id, or the literal latest for the most recent run.
Flag Type Default Description
--model <NAME> string Filter to a single model within the run. Errors if the model wasn’t executed.
--check bool false Read-only replayability audit instead of the inspection view. Classifies each model as replayable or non_replayable from the ledger alone and flags static non-determinism. Executes nothing.
--execute bool false Re-execute the recorded recipe (reconstructed from provenance, never the working tree) and re-derive the output hash. Runs on an ephemeral in-memory DuckDB engine by default.
--verify bool false With --execute, compare the re-derived blake3 against the recorded hash and emit a per-model verdict (bit_exact / diverged / non_replayable). Requires --execute.
--warehouse bool false With --execute, re-run against the live warehouse configured in rocky.toml instead of a local engine. Content-addressed models only. All replay writes land in an isolated hcv2_replay_<run> schema — never the production location of any recorded target — and it is dropped afterwards unless --keep. Requires --execute.
--keep bool false With --warehouse, keep the isolated replay schema (and the replayed tables) after the run for inspection. Requires --warehouse.

Replay the most recent run:

Terminal window
rocky replay latest
{
"version": "1.11.0",
"command": "replay",
"run_id": "run_20260420_143022",
"status": "success",
"trigger": "manual",
"started_at": "2026-04-20T14:30:22Z",
"finished_at": "2026-04-20T14:31:07Z",
"config_hash": "cfg_a3f2b1c4",
"models": [
{
"model_name": "stg_orders",
"status": "success",
"started_at": "2026-04-20T14:30:22Z",
"finished_at": "2026-04-20T14:30:23Z",
"duration_ms": 1200,
"sql_hash": "hash_a3f2b1c4",
"rows_affected": 150000,
"bytes_scanned": 41943040,
"bytes_written": 20971520
},
{
"model_name": "fct_revenue",
"status": "success",
"started_at": "2026-04-20T14:30:24Z",
"finished_at": "2026-04-20T14:31:07Z",
"duration_ms": 43000,
"sql_hash": "hash_b4e3c2d5",
"rows_affected": 8900,
"bytes_scanned": 20971520,
"bytes_written": 4194304
}
]
}

Filter to a single model within a specific run:

Terminal window
rocky replay run_20260420_143022 --model fct_revenue

sql_hash is stable across runs where the compiled SQL is identical, so diffing two replays is a fast way to detect whether a re-run would execute the same statements.

--execute reconstructs each model’s recipe from its recorded provenance — the canonical ModelIr embedded at run time, never the current working tree — and re-runs it, re-deriving the output artifact’s blake3. With --verify, that digest is compared against the recorded hash:

Terminal window
rocky replay latest --execute --verify
{
"command": "replay --execute --verify",
"run_id": "run_20260420_143022",
"verified": true,
"model_count": 2,
"bit_exact_count": 2,
"models": [
{ "model_name": "stg_orders", "verdict": "bit_exact", "rows": 150000 },
{ "model_name": "fct_revenue", "verdict": "bit_exact", "rows": 8900 }
]
}

Each per-model verdict is a classification, not a tool failure — bit_exact, diverged, executed (without --verify), or non_replayable — so the command exits 0 and you read the verdict. A model that reads a mutable source is classified non_replayable rather than silently re-run against current data; a model whose recipe contains a non-deterministic construct (now(), random()) is flagged and a diverged there is expected.

By default re-execution runs on an ephemeral in-memory DuckDB engine. --warehouse re-runs against the live warehouse in rocky.toml instead, for content-addressed models whose artifacts live in the lakehouse:

Terminal window
rocky replay latest --execute --verify --warehouse

The warehouse path re-derives each output’s blake3 using the target table’s own physical column mapping, read from its Delta log. A bit_exact verdict therefore means the warehouse reproduced exactly the bytes the content-addressed writer recorded.

Re-execution never touches production. Rocky materializes each replayed model into a fresh schema and redirects every in-run upstream reference into it:

production replay schema hcv2_replay_<run>
────────── ─────────────────────────────────
prod.stg_orders stg_orders ◄── re-run from recipe
(never written) │
│ the downstream model reads
│ the replayed upstream here,
▼ not production
prod.fct_revenue fct_revenue ◄── re-run from recipe
(never written)
the whole replay schema is dropped after the run, unless --keep

Three rules make that isolation hold:

  • Rocky materializes every replayed model into a fresh hcv2_replay_<run> schema, never into the production location of any recorded target. That schema is dropped after the run unless you pass --keep.
  • Rocky writes no object-store objects. It hashes the recomputed artifact in memory and never touches an existing content-addressed file.
  • A model whose upstream the run did not itself produce is classified non_replayable, as is a model that reads a mutable source. Rocky reports the classification rather than falling back to production data.

Render a completed run as a Gantt-style timeline. Sibling to rocky replay: it reads the same RunRecord, but lays out models by start offset and assigns them to concurrency lanes so overlapping models show up on separate rows.

Terminal window
rocky trace <target> [flags]
Argument Type Default Description
target string (required) A specific run_id, or the literal latest.
Flag Type Default Description
--model <NAME> string Filter to a single model within the run.

Trace the most recent run:

Terminal window
rocky trace latest
{
"version": "1.11.0",
"command": "trace",
"run_id": "run_20260420_143022",
"status": "success",
"trigger": "manual",
"started_at": "2026-04-20T14:30:22Z",
"finished_at": "2026-04-20T14:31:07Z",
"run_duration_ms": 45000,
"lane_count": 2,
"models": [
{
"model_name": "stg_orders",
"status": "success",
"start_offset_ms": 0,
"duration_ms": 1200,
"sql_hash": "hash_a3f2b1c4",
"lane": 0,
"rows_affected": 150000,
"bytes_scanned": 41943040,
"bytes_written": 20971520
},
{
"model_name": "stg_customers",
"status": "success",
"start_offset_ms": 100,
"duration_ms": 900,
"sql_hash": "hash_x9y8z7w6",
"lane": 1,
"rows_affected": 8000,
"bytes_scanned": 2097152,
"bytes_written": 1048576
},
{
"model_name": "fct_revenue",
"status": "success",
"start_offset_ms": 2000,
"duration_ms": 43000,
"sql_hash": "hash_b4e3c2d5",
"lane": 0,
"rows_affected": 8900,
"bytes_scanned": 20971520,
"bytes_written": 4194304
}
]
}

Lane assignment is greedy first-fit over sorted start offsets, so lane_count is the observed maximum concurrency. The table-mode output prints a bar chart:

$ rocky -o table trace latest
run: run_20260420_143022
status: success trigger: manual duration: 45.00s
parallelism: 2 lanes
model timeline duration status
stg_orders [###.....................................] 1.20s success
stg_customers [##......................................] 0.90s success
fct_revenue [....####################################] 43.00s success

Historical cost rollup for a completed run. Reads the same RunRecord as rocky replay and rocky trace, then recomputes per-model cost via the adapter-appropriate formula (Databricks / Snowflake duration × DBU rate; BigQuery bytes × $/TB; DuckDB zero). The three are siblings: replay shows what ran, trace shows when, cost shows what it cost.

Terminal window
rocky cost <target> [flags]
Argument Type Default Description
target string (required) A specific run_id, or the literal latest for the most recent run.
Flag Type Default Description
--model <NAME> string Filter to a single model within the run.
--output <FORMAT> `json table` json

Roll up cost for the most recent run:

Terminal window
rocky cost latest
{
"version": "1.11.0",
"command": "cost",
"run_id": "run-20260421-142430-017",
"status": "success",
"trigger": "manual",
"started_at": "2026-04-21T14:24:29.881087+00:00",
"finished_at": "2026-04-21T14:24:30.031036+00:00",
"duration_ms": 149,
"adapter_type": "databricks",
"total_cost_usd": 0.101,
"total_duration_ms": 910000,
"total_bytes_scanned": 104857600,
"total_bytes_written": 20971520,
"per_model": [
{
"model_name": "stg_orders",
"status": "success",
"duration_ms": 310000,
"rows_affected": 150000,
"bytes_scanned": 41943040,
"bytes_written": 10485760,
"cost_usd": 0.034
},
{
"model_name": "fct_revenue",
"status": "success",
"duration_ms": 600000,
"rows_affected": 8900,
"bytes_scanned": 62914560,
"bytes_written": 10485760,
"cost_usd": 0.067
}
]
}

Human-readable table form:

Terminal window
rocky cost latest --output table
run: run-20260421-142430-017
status: success adapter: databricks total: $0.101
model duration rows bytes_scanned cost
stg_orders 5m 10s 150,000 40 MB $0.034
fct_revenue 10m 0s 8,900 60 MB $0.067
  • Databricks / Snowflake: cost computed from recorded duration × DBU rate × $/DBU. Configure via [cost] in rocky.toml (see configuration reference).
  • BigQuery: computed from recorded bytes_scanned × $6.25/TB. rocky cost surfaces real dollars here even when the live rocky apply still reports None for BQ bytes on its own RunOutput.cost_summary, because the state-store record is written before that plumbing completes.
  • DuckDB / local: $0.00 by definition (no billed compute).
  • Discovery adapters (Fivetran, Airbyte, etc.): skipped; cost is None.

Missing adapter_type or unconfigured [cost] degrades cleanly: the command still emits duration + bytes totals but leaves cost_usd as null.

  • rocky replay – same RunRecord, shown as a per-model execution dump
  • rocky trace – same RunRecord, shown as a Gantt-style timeline
  • rocky history – list recent runs to find a run_id
  • [budget] – run-level budget that fires budget_breach events during the run itself

Inspect or manage the embedded state store. rocky state is a subcommand group. The bare form still shows watermarks, for backwards compatibility.

Terminal window
rocky state # show watermarks (default)
rocky state show # same as bare `rocky state`
rocky state clear-schema-cache [--dry-run] # flush the DESCRIBE cache
rocky state retention sweep [--dry-run] # trim the history tables
rocky state schedule pause <pipeline> # hold a pipeline's schedule
rocky state schedule resume <pipeline> # release the hold
Subcommand Description
show (default) Display stored watermarks. Same output as bare rocky state; the named form is provided so scripts can be explicit.
clear-schema-cache Flush the DESCRIBE TABLE schema cache. See rocky state clear-schema-cache.
retention sweep Delete history rows that fall outside [state.retention]. See rocky state retention sweep.
schedule pause / schedule resume Hold or release one pipeline’s schedule at runtime. See rocky state schedule.

When --state-path is omitted, Rocky resolves the state file via rocky_core::state::resolve_state_path:

  1. <models>/.rocky-state.redb: canonical location for new projects; matches the LSP convention so inlay hints observe the same file rocky apply writes.
  2. Legacy CWD .rocky-state.redb: still works; emits a one-time deprecation warning on stderr.
  3. Both present: CWD wins (preserves existing watermarks, branches, and partition bookkeeping); a louder warning asks you to reconcile. Merge is lossy, so delete one copy to silence the warning.
  4. Neither present: fresh project lands on <models>/.rocky-state.redb when a models/ directory exists; otherwise it falls back to CWD (keeps replication-only pipelines working without inventing a models/ directory just to hold state).

Explicit --state-path <PATH> always wins; no resolver logic is applied.


Flush the DESCRIBE TABLE schema cache. Complement to the TTL-driven eviction on the read path; use this when the project needs a fresh typecheck now (e.g., after a manual warehouse DDL change, before a strict-CI run, while debugging a suspected stale-cache mismatch).

Terminal window
rocky state clear-schema-cache
rocky state clear-schema-cache --dry-run
Flag Type Default Description
--dry-run bool false Report how many entries would be removed without touching the store. Useful for automation that asserts emptiness before a scheduled flush.
  • Removes every row from the SCHEMA_CACHE redb table. The cache is cheap to rebuild; the next rocky apply (write tap) or rocky discover --with-schemas warms it back up.
  • No prompt. Entries are disposable; explicit opt-in by running the command is sufficient.
  • Missing state store is a no-op. A fresh CI runner with no .rocky-state.redb yet exits zero with entries_deleted: 0. This keeps “flush before build” safe to run unconditionally on ephemeral runners.
  • Uninitialised cache tables return entries_deleted: 0 without touching redb.
  • JSON output is ClearSchemaCacheOutput (entries_deleted, dry_run).
Terminal window
rocky state clear-schema-cache --dry-run --output json
{
"version": "1.16.0",
"command": "state-clear-schema-cache",
"entries_deleted": 12,
"dry_run": true
}

Delete the history rows that fall outside [state.retention] in rocky.toml. The sweep covers three domains: run history, DAG snapshots, and quality snapshots.

Terminal window
rocky state retention sweep
rocky state retention sweep --dry-run
Flag Type Default Description
--dry-run bool false Plan the sweep and delete nothing. Reports the same counts a real sweep would produce, give or take concurrent writers.

The sweep only trims history. Two guardrails bound it:

  • Operational tables are out of scope: the schema cache, the watermarks, and the partition records are never swept.
  • The most recent min_runs_kept rows in each domain survive unconditionally, whatever the age policy says.

Hold or release one pipeline’s schedule at runtime. A hold is durable: it lives in the state store, not in rocky.toml.

Terminal window
rocky state schedule pause <pipeline>
rocky state schedule resume <pipeline>
Argument Type Default Description
pipeline string (required) The pipeline whose schedule to pause or resume.

No command-specific flags. Uses the global flags only.

A hold controls whichever scheduler reads the same state file this command writes. Point both at the same --state-path or they will not see each other.

rocky --state-path X state schedule pause <pipeline>
▼ writes the hold
┌────────────────────────────┐
│ state file X │
└────────────┬───────────────┘
│ read on every tick
┌────────────────────────────┐ suppresses the cron,
│ rocky serve --scheduler │ after, freshness, and
│ --state-path X │ webhook demand sources,
└────────────────────────────┘ and records a `paused`
skip on each tick

A pause reaches a running scheduler immediately. Editing [schedule] enabled in rocky.toml does not: a resident scheduler cannot see that change until it restarts. The command’s output names the state file it acted on, so pausing the wrong instance is never silent.

resume re-enables autonomous runs, so it is deliberately human-only. Agents get the pause_schedule MCP tool and no matching resume.

  • rocky serve – the resident process a hold controls
  • rocky mcp – the agent-facing pause_schedule tool

Governance rollup over classification sidecars plus the project [mask] policy. Static resolver: answers “are all classified columns masked wherever policy says they should be?” without issuing a single warehouse call.

Terminal window
rocky compliance [--env NAME] [--exceptions-only] [--fail-on exception]
Flag Type Default Description
--env <NAME> string Scope the report to a single environment. When unset, the report expands across the defaults plus every [mask.<env>] override block declared in rocky.toml. A named env that has no matching [mask.<env>] block still reports under that label; the resolver falls back to the [mask] defaults.
--exceptions-only bool false Filter per_column to rows that produced at least one exception. The exceptions list is unaffected; allow-listed tags are suppressed from per_column under this flag.
--fail-on <CONDITION> exception Gate condition. Only exception is supported in v1. When set, exits 1 when one or more exceptions are emitted. Useful as a CI gate that blocks merges that leave classified columns unmasked.
--models <PATH> string models Models directory to scan for [classification] sidecars.
  • Loads rocky.toml and every model sidecar with a non-empty [classification] block. Each (model, column, env) triple is evaluated against the resolved masking strategy.
  • MaskStrategy::None (“explicit identity”) counts as masked: the project has deliberately opted out, which is a conscious policy decision, not an enforcement gap.
  • Tags listed on [classifications] allow_unmasked suppress exception emission but still report enforced = false in the per-column breakdown; the allow list doesn’t pretend the column is masked.
  • Exit 1 with --fail-on exception when any exception is emitted; otherwise exit 0 regardless of exception count (the JSON payload still reports them).
  • Static rollup, no warehouse calls. Fast enough to run in every PR.
  • JSON output is ComplianceOutput (summary, per_column, exceptions).
Terminal window
rocky compliance --env prod --fail-on exception
{
"version": "1.16.0",
"command": "compliance",
"summary": {
"total_classified": 5,
"total_masked": 4,
"total_exceptions": 1
},
"per_column": [
{
"model": "users",
"column": "email",
"classification": "pii",
"envs": [
{ "env": "prod", "masking_strategy": "hash", "enforced": true }
]
},
{
"model": "users",
"column": "ssn",
"classification": "confidential",
"envs": [
{ "env": "prod", "masking_strategy": "unresolved", "enforced": false }
]
}
],
"exceptions": [
{
"model": "users",
"column": "ssn",
"env": "prod",
"reason": "no masking strategy resolves for classification tag 'confidential'"
}
]
}

Report each model’s declared data-retention policy. Walks the compiled model set and emits one row per model with its declared retention = "<N>[dy]" value (or null when unset).

Terminal window
rocky retention-status [--model NAME] [--drift]
Flag Type Default Description
--model <NAME> string Scope the report to a single model by name.
--drift bool false Keep only the models that declare a policy, and probe the warehouse for the value it currently applies.
--models <PATH> string models Models directory.
  • Compiles the project so each model’s resolved retention sidecar value surfaces as a typed Option<RetentionPolicy>.
  • configured_days is None when the model’s sidecar carries no retention key.
  • warehouse_days is None unless --drift probed a warehouse that reports a value.
  • in_sync is true iff configured_days == warehouse_days. Without --drift, unconfigured models collapse to in_sync = true (both sides are None).
  • The --drift probe reads Delta delta.logRetentionDuration + delta.deletedFileRetentionDuration on Databricks, and DATA_RETENTION_TIME_IN_DAYS on Snowflake. BigQuery and DuckDB have no probe and report warehouse_days = null.
  • A probe failure prints a warning on stderr naming the model and its adapter. The command continues with warehouse_days = null rather than aborting.
  • JSON output is RetentionStatusOutput (a flat models array of ModelRetentionStatus).

rocky retention-status --model <NAME> exits 1 when no model carries that name. Stderr reads model '<NAME>' not found (no transformation model with that name). Rocky refuses before it writes any output, so stdout stays empty even under --output json.

--drift keeps only the models that declare a policy, so it can legitimately return nothing. That case exits 0 and the payload gains a message field explaining the empty result. The field is absent whenever models is non-empty.

Situation message
--drift --model <NAME>, and that model declares no policy model '<NAME>' declares no retention policy
--drift with no model selected, and none declares a policy no models declare a retention policy
{
"version": "1.70.1",
"command": "retention-status",
"models": [],
"message": "no models declare a retention policy"
}
Terminal window
rocky retention-status --output json
{
"version": "1.16.0",
"command": "retention-status",
"models": [
{ "model": "fct_revenue", "configured_days": 90, "in_sync": true },
{ "model": "dim_customers", "in_sync": true },
{ "model": "events", "configured_days": 30, "in_sync": true }
]
}

Text mode is a fixed-width table:

Terminal window
rocky -o table retention-status
MODEL CONFIGURED WAREHOUSE IN SYNC
----------------------------------------------------------------------------------
fct_revenue 90 days - yes
dim_customers - - yes
events 30 days - yes