safe-migrate 0.4.4

Analyze PostgreSQL migrations for schema and locking risks
Documentation
# Testing

Use the smallest test layer that proves the behavior, then run the broader
suite before merging user-visible changes.

## Standard checks

```bash
cargo fmt -- --check
cargo test --locked
cargo clippy --all-targets --locked -- -D warnings
```

Use focused tests while developing:

```bash
cargo test rule_evaluation
cargo test architectural_gap
cargo test expression_parsing
cargo test --test cli_tests
```

## Repository script gates

Run the installer contract and generated migration corpus from the repository
root:

```bash
sh scripts/test-install-dry-run
scripts/fuzz
```

The installer test proves a pinned dry run does not need network tooling or
write its requested destination. Its offline download mocks also require
missing, malformed, and mismatched checksums to fail closed while a valid
checksum installs successfully. The fuzz script generates at least 400 SQL
migrations, requires every accepted case to produce valid JSON with a matching
exit status, permits only its named parser rejection, and fails on operational
errors, crashes, or timeouts.

Before tagging a release, verify the exact crate users will install:

```bash
cargo package --locked --allow-dirty
cargo install --locked --path target/package/safe-migrate-<version> \
  --root <temporary-install-root>
<temporary-install-root>/bin/safe-migrate --version
```

`--allow-dirty` is appropriate only for testing an uncommitted release-prep
worktree. The tagged release commit itself must be clean.

## Fixture suites

`live_tests/run.sh` checks SQL fixtures through the compiled CLI. Run one rule
directory while iterating, then the full suite before merge:

```bash
cd live_tests
./run.sh -d rule_25_schema-drift
./run.sh
```

From the repository root, the simulator-versus-PostgreSQL differential harness
needs a disposable local database exposed through `DATABASE_URL`:

```bash
DATABASE_URL='postgres://safe_migrate:safe_migrate@localhost:5432/safe_migrate' \
  scripts/live-differential
```

The same disposable database is required by the dedicated automatic-sync and
encrypted-cache contracts:

```bash
DATABASE_URL='postgres://safe_migrate:safe_migrate@localhost:5432/safe_migrate' \
  scripts/live-auto-sync
DATABASE_URL='postgres://safe_migrate:safe_migrate@localhost:5432/safe_migrate' \
  scripts/live-cache-encryption
```

`scripts/live-auto-sync` exercises successful refresh, cache creation, and an
`Exact` available baseline for both `lint` and `lint-chain`.
`scripts/live-cache-encryption` exercises encrypted sync, inspect, `lint`,
configured automatic sync, and `lint-chain`, plus rejection when encryption is
disabled or the key is missing or incorrect.

Use the script's selectors when diagnosing one case:

```bash
scripts/live-differential --rule rule_25_schema-drift
scripts/live-differential --fixture rule_25_schema-drift/safe_002_create_table.sql
```

The harness resets only its `sm_*` schemas and named fixture objects, but it
still mutates the selected database. Never point it at a shared or production
database.

GitHub Actions runs the enabled manifest against PostgreSQL 14 through 18.
It uploads a verbose log for each version, including failed runs. Treat that
matrix as the supported live-differential scope; excluded fixtures remain
documented in `live_tests/differential_manifest.json` with their reasons.

## What to assert

- AST work: exact facts and source distinctions.
- State work: apply, skip, conflict, rollback, rename, drop, and recreate
  effects as applicable.
- Rule work: rule ID, tier, object, reason, and recipe.
- CLI work: exit status plus exact stdout/stderr separation and JSON fields.
- Cache/sync work: preservation on failure, provenance/freshness, and
  encryption rejection paths.

Formatting and compilation are necessary checks; they are not proof that a new
behavior works. Add a regression that exercises the observed behavior.