rivet-cli 0.22.0

Rivet: PostgreSQL/MySQL/SQL Server/MongoDB → Parquet/CSV (local, S3, GCS, Azure). Crate name rivet-cli; binary rivet.
Documentation
# Destination-backend correctness matrix — SCENARIO × object-store BACKEND.
# Captures the destination dimension the export-strategy ledgers do not: the
# shared OpenDAL CloudDestination path (S3 / GCS / Azure) vs the local filesystem
# destination, and the dogfood findings on it. Verified on fake-gcs / MinIO
# emulators AND (for the prefix + round-trip) real GCS via the DuckDB independent
# oracle — the same discipline as source_parity_sweep, extended to the cloud leg.
#
# `engines:` is the guard's generic column axis — here the destination backends.
# S3/GCS/Azure share ONE CloudDestination impl (src/destination/cloud.rs), so a
# scenario proven by a shared-seam UNIT test (e.g. the prefix normalization) is
# the SAME test in all three cloud columns; `local` is a separate impl (fs joins,
# no OpenDAL) and is `na` where the scenario is a cloud-object-key concern.
#
# Cell = { test: <fn> } | { na: "<why n/a for this backend>" } | { gap: "<why,
# ratcheted>" }. Same drift-guard as the sibling ledgers
# (tests/offline/chunking_matrix_guard.rs).

engines: [local, gcs, s3, azure]

scenarios:
  - id: prefix_no_trailing_slash_normalized
    what: "dogfood B (MEDIUM): a cloud prefix WITHOUT a trailing slash must not jam the part name onto it (`exports/mydata` + `orders.parquet` → `exports/mydataorders.parquet`) — every op builds the key as `format!(\"{}{}\", prefix, key)` and list_prefix appends `/`, so an un-normalized prefix produces a FALSE PART_MISSING on 100%-present, DuckDB-readable data. normalize_prefix adds the slash at construction; the three cloud backends share the one CloudDestination path, so one unit test covers all. Live-verified on real GCS."
    local: { na: "the local filesystem destination joins with Path::join, not string concat — no trailing-slash key-jamming; the prefix is a directory." }
    gcs:   { test: normalize_prefix_appends_a_trailing_slash_only_when_needed }
    s3:    { test: normalize_prefix_appends_a_trailing_slash_only_when_needed }
    azure: { test: normalize_prefix_appends_a_trailing_slash_only_when_needed }

  - id: full_export_round_trips_and_reads_back
    what: "an export LANDS at the destination and reads back COMPLETE (content, not just object presence) — the emulator round-trip. Representative-engine cells (the stand runs one engine per backend; the read-back is via the store's own list/get, an independent oracle, not rivet's counter)."
    local: { na: "local round-trip is the implicit default of every batch stand/live test (count_parquet_rows re-reads the written files)." }
    gcs:   { test: stand_dest_gcs_mysql }
    s3:    { test: stand_dest_s3_mysql }
    azure: { gap: "no azurite full-round-trip stand test yet — the CloudDestination path is shared with S3/GCS (proven above), but an azure-specific emulator round-trip is unwritten." }

  - id: row_count_parity_across_backends
    what: "the SAME export to local vs S3 vs GCS yields IDENTICAL row counts — an independent cross-backend oracle (dogfood confirmed the fake-gcs count == real-GCS count == source via a DuckDB re-read, so the emulator does not lie about the real backend)."
    local: { test: destination_parity_row_counts_match_across_local_s3_gcs }
    gcs:   { test: destination_parity_row_counts_match_across_local_s3_gcs }
    s3:    { test: destination_parity_row_counts_match_across_local_s3_gcs }
    azure: { na: "the parity harness compares local/s3/gcs; azure joins the same CloudDestination seam, so its parity is covered by the shared round-trip + prefix cells, not a fourth column here." }

  - id: validate_verdict_is_advisory_not_a_run_gate
    what: "dogfood A (design, ADR-0001 §I7 MFN): `run --validate` populates the report/metrics verdict but is NON-fatal — the run exits 0 even on a verified-wrong verdict; the HARD gate is the standalone `rivet validate` command (exit 3) and `--reconcile` (COUNT mismatch). A dogfood verify-agent mis-flagged this as a bug not knowing the ADR; the roast test pins the documented contract. Destination-agnostic (the verdict path is the same for every backend)."
    local: { test: roast_metric_validated_matches_final_summary_verdict }
    gcs:   { na: "the --validate-is-advisory contract is destination-agnostic; the roast test exercises it on local, which is sufficient (the verdict path does not branch on backend)." }
    s3:    { na: "destination-agnostic — see the local cell." }
    azure: { na: "destination-agnostic — see the local cell." }