73d3c218

Add sydtest-sqitch-postgres and sydtest-sqitch-postgres-persistent

## Summary

Two new sydtest companion packages:

- **`sydtest-sqitch-postgres`** — sanity checks for a sqitch project against a temporary PostgreSQL database:
  - **Per-change round-trip**: deploy through a change, revert one step, redeploy. Schema must match.
  - **Per-change idempotence**: re-execute the deploy script's raw SQL on a database where the change has already been applied. Schema must be unchanged.
  - **Whole-plan deploy/revert/redeploy cycle**: deploy, snapshot, revert all, redeploy, snapshot, compare.
- **`sydtest-sqitch-postgres-persistent`** — on top of `sydtest-sqitch-postgres`, asserts that the schema after deploying every sqitch migration matches what `persistent`'s automatic migration produces against an empty database (columns + indices).

These replace the bespoke migration sanity-check code currently sitting in `NorfairKing/nix-ci#295`.

## API shape

```haskell
sqitchPostgresqlSpec :: SqitchSettings -> TestDef outers a -> TestDef outers a
sqitchPersistentPostgresqlSpec :: SqitchPersistentSettings -> TestDef outers a -> TestDef outers a
```

The combinators allocate fresh empty postgres databases internally (one per check), so nothing about the postgres machinery surfaces in the caller's outer-type stack. IO-level entry points are also exposed for negative tests under `expectFailing`.

## Design highlights

- **Grandfather tag**: a single `Maybe Text` referring to a tag in `sqitch.plan`. Changes at or before the tag are exempt from the per-change round-trip + idempotence checks (legacy migrations that shipped before the checks existed). Changes after must satisfy both.
- **Rework heads** (the post-tag head of a reworked change, with deploy target `name@HEAD`) skip the round-trip check, since sqitch's revert convention undoes the whole change rather than only the rework. The whole-plan cycle test still exercises these.
- **`SchemaSnapshot`** record bundles columns + indices, compared via `compareSchemaSnapshots` with the undefined-trick to prompt for an update when new fields are added.
- **Phase isolation**: `sqitchPersistentPostgresqlSpec`'s schema-equality check runs the persistent baseline (phase 1) on a separate database from the sqitch deploy (phase 2), so they can't contaminate each other.
- **`Path Rel Dir`/`Path Abs File`** for sqitch project dir + executable; `path`/`path-io` used throughout.

## Companion change to `sydtest-persistent-postgresql` (0.3.0.0 → 0.4.0.0)

The internal `adminDBSetupFunc`, `tempUserSetupFunc`, `tempNewDatabaseSetupFunc`, and `dbConnectionOptionsPoolSetupFunc` are renamed to `postgresqlServerSetupFunc`, `postgresqlUserSetupFunc`, `postgresqlDatabaseSetupFunc`, `postgresqlPoolSetupFunc` and promoted to the public API. New convenience `emptyPostgresOptionsSetupFunc` / `emptyPostgresPoolSetupFunc` give a fresh empty database without going through a migrated template. `sydtest-sqitch-postgres` consumes these.

## Test plan

- [x] `stack test sydtest-sqitch-postgres sydtest-sqitch-postgres-persistent --pedantic` — 21/21 + 6/6 passing
- [x] `stack test sydtest-persistent-postgresql --pedantic` — 5/5 still passing
- [x] `nix flake check` — green (pre-commit, hlint, ormolu, cabal2nix, hpack, cross-nixpkgs release builds)
- [x] Toy fixtures cover each failure mode in isolation:
  - `toy-sqitch-ok` (happy path)
  - `toy-sqitch-non-idempotent`
  - `toy-sqitch-broken-revert`
  - `toy-sqitch-grandfathered` (with and without the tag — round-trip and idempotence both skipped pre-tag)
  - `toy-sqitch-drift` (column mismatch between sqitch and persistent)
  - `toy-sqitch-index-drift` (columns match, indices don't)
- [x] `readSqitchPlan` has 12 unit tests covering empty plan, single change, rework, double rework, grandfather tag at start/middle/end, missing tag, blank lines, on-disk fixture
- [x] Replaces bespoke code in `NorfairKing/nix-ci#295` — local branch `use-sydtest-sqitch` confirms 6/6 Migration tests pass under the new API