# Scripts

This page explains what each repository script is for, when to run it, and how
it fits into the release and maintenance workflow. The short index lives at
[`scripts/scripts.md`](../../scripts/scripts.md), and the complete generated
[script inventory](./script-inventory) is checked against the repository on
every release run.

## Stability And Ownership

`release/scripts.json` is the machine-readable source for script ownership,
audience, tier, tools, inputs, outputs, destructive behavior, timeouts,
platform support, and release usage. Run this after adding, moving, or removing
an entry point:

```bash
python3 scripts/check_script_inventory.py --write
python3 scripts/check_script_inventory.py
```

The checker runs `bash -n` over every maintained shell script and ShellCheck
error-level checks when ShellCheck is installed. Public/stable commands retain
their arguments, defaults, environment variables, and exit meanings throughout
1.x. Maintainer/stable commands follow the same window. Internal fixtures can
remain workflow-specific when their caller is recorded in the inventory.

Shared logging, prerequisite, safe database-name, timeout, temporary-directory,
and checksum helpers live in `scripts/lib/pggraph-common.sh`. A cleanup trap
must only name resources created by its own process; cluster-destructive scripts
must continue to require an explicit disposable target.

## Named Release Tiers

The declarative registry in `release/gates.json` defines gate dependencies,
timeouts, and exclusive resources for four tiers:

```bash
python3 scripts/run_release.py --tier pr
python3 scripts/run_release.py --tier nightly
python3 scripts/run_release.py --tier rc
python3 scripts/run_release.py --tier full-matrix
```

Each run writes `release/evidence/<tier>.json` atomically with the exact command,
environment overrides, tool and pgGraph versions, dependency-lockfile hashes,
dataset provenance/checksum, duration, result, thresholds, artifact SHA-256s,
and a gate fingerprint. Combined output is retained under
`release/evidence/logs/<tier>/`. Add `--resume` to reuse only a passing record
whose command, registry, versions, fingerprint, and retained artifact hashes
still match. Gates sharing a PostgreSQL cluster, port, Docker daemon, or Cargo
target are declared exclusive and run serially.

The `rc` and `full-matrix` tiers run destructive crash checks inside a cluster
created and removed by `scripts/with_disposable_postgres.sh`. Direct use of the
historical aggregate command still requires an explicit disposable `PGDATA`.

The historical aggregate interface remains supported:

```bash
cd graph
./tests/heavy/run_release_gate.sh
./tests/heavy/run_release_gate.sh --tier rc
```

With no arguments, all documented `RUN_*`, `DB_PREFIX`, and PostgreSQL feature
environment contracts retain their 1.x behavior.

## Root Script Groups

Root scripts live in `scripts/` and are intended to be run from the repository
root unless noted otherwise.

| Script | Use when | What it does |
|---|---|---|
| `check_doc_references.py` | Editing docs or moving files | Validates local Markdown/MDX references so docs do not point at missing local files. |
| `check_docs_drift.sh` | Before PRs and releases | Runs the aggregate documentation drift suite: local references, SQL API/GUC drift, and Rust source-map drift. |
| `check_secrets.sh` | Before commits and releases | Runs gitleaks with redacted output. The default `all` mode scans full Git history and pending tracked changes; `history` and `changes` run either side independently. |
| `check_dependency_updates.py` | Before releases and dependency bumps | Checks pinned Cargo, Python, Docker, and Nix flake GitHub inputs against upstream registries. It recommends only releases or commits at least 6 hours old by default and never updates files unless called with `--update ... --yes`. |
| `check_rust_doc_map_drift.py` | Editing contributor internals docs | Checks that Rust source files and contributor documentation stay aligned. Supports `--list` for inventory inspection. |
| `check_release_contract.py` | Changing a 1.x compatibility surface | Compares the committed release contract with implemented SQL functions, GUCs, diagnostics, supported PostgreSQL majors, distributions, and declared GQL profile. Use `--write` only after reviewing compatibility impact. |
| `check_sql_api_drift.py` | Editing SQL APIs, GUCs, or API docs | Compares implemented SQL-facing functions and GUCs against documented API references. Supports `--list-implemented`. |
| `check_topology_security_inventory.py` | Editing SQL entrypoints or topology visibility composition | Requires every public SQL function to be classified, exactly matches topology `#[pg_extern]` entrypoints and overloads, and checks each declared function's body-scoped route to visibility composition. Rust context types and PostgreSQL tests remain the execution-boundary proof. |
| `tests/heavy/measure_runtime_resources.sh` | Resource-governance changes and release candidates | Repeats persisted loads and exercises query, hydration, sync, compaction, analytics, and typed rejection while sampling backend RSS and Linux PSS. The default 10,000-node profile uses an explicit 256 MiB successful-query budget, asserts resource telemetry, enforces positive 1024 MiB RSS/PSS defaults, and fails when required samples are missing. |
| `tests/heavy/open_type_query_resources.sh` | P9 open-type capacity evidence | Builds the bounded 65,536-label fixture and records synchronized idle, loaded, and active-query RSS/PSS for each PostgreSQL backend, aggregate backend totals, baseline-subtracted totals, projection artifact bytes, and query dimensions. |
| `tests/heavy/open_type_query_latency.sh` | P9 SQL latency evidence | Builds identical 254- and 65,536-label PostgreSQL 17 fixtures, retains exact query oracles, and records same-backend pgbench samples for traversal, shortest path, GQL, and Cypher. |
| `tests/heavy/run_open_type_query_criterion.sh` | P9 fresh Criterion capture | Removes only the three generated open-type Criterion groups, runs the exact benchmark, retains its log, and invokes the exact-case extractor. |
| `tests/heavy/run_open_type_query_resource_matrix.sh` | P9 Linux resource matrix | Runs the resource harness serially for 1, 4, and 8 backends and combines raw samples and summaries without changing their run identities. |
| `tests/heavy/run_open_type_query_resources_docker.sh` | P9 portable Linux resource capture | Builds the matrix image, executes the resource matrix in Linux, and copies raw evidence plus Docker metadata to the host. |
| `graph/tests/heavy/rls_large_table_baseline.sh` | Changing caller-scoped visibility strategy or its resource accounting | Builds scalar, composite-key, and relationship-row fixtures; measures no-RLS, broad-allow, sparse-allow, and sparse-deny cases in persistent restricted-role sessions; and retains samples, percentile summaries, source-scan plans, and fixture metadata. Exact SPI call and key/byte scan counts remain `NA` until dedicated instrumentation exists. |
| `clean_generated_artifacts.sh` | Before packaging, archiving, or release prep | Deletes Cargo and fuzz build outputs plus `.DS_Store` files. Has `--dry-run`. |
| `inspect_pggraph_artifact.py` | Debugging persistence or release evidence | Reads a `.pggraph` artifact and prints section metadata as JSON. Used by synthetic release evidence workflows when an artifact is available. |
| `build_docker_pggraph_package.sh` | Building extension artifacts with Docker | Builds `graph-pg<major>/` package directories for one PostgreSQL major or all supported majors. |
| `copy_pggraph_package_to_docker_postgres.sh` | Installing a prebuilt package into a running container | Copies `graph.control`, `graph--<version>.sql`, and `graph.so` into a running PostgreSQL container, then optionally creates the extension. |
| `prepare_release_bundle.py` | Preparing an immutable source candidate | Archives an explicit Git commit and creates deterministic SHA-256 sums, an SPDX 2.3 dependency SBOM, provenance, and a release manifest. |
| `verify_release_bundle.py` | Reviewing or publishing a prepared candidate | Rejects missing, extra, renamed, or changed files and verifies manifest, checksum, SBOM, provenance, version, and source-commit identity without rebuilding. |
| `install_into_docker_postgres.sh` | One-command install into an existing container | Builds a Docker package and immediately copies it into one running PostgreSQL container. This is a wrapper around the two Docker package scripts. |

## Documentation Scripts

Use the aggregate check first:

```bash
./scripts/check_docs_drift.sh
```

It runs:

```bash
./scripts/check_doc_references.py
./scripts/check_sql_api_drift.py
./scripts/check_topology_security_inventory.py
./scripts/check_rust_doc_map_drift.py
```

Run the underlying Python scripts directly when you need targeted diagnostics or
inventory output:

```bash
python3 scripts/check_sql_api_drift.py --list-implemented
python3 scripts/check_rust_doc_map_drift.py --list
```

## Secret Scanning

The release gate runs gitleaks by default. You can run the same check directly
from any directory inside the repository:

```bash
scripts/check_secrets.sh all
```

Use `changes` for a quick pre-commit scan or `history` when only committed
history needs validation. Findings are redacted, and each scan has a
300-second timeout by default. Set `GITLEAKS_TIMEOUT_SECONDS` to a positive
integer to change that limit. The script fails clearly when gitleaks is not
installed; it never installs packages itself.

Use these checks whenever you change:

| Change | Script |
|---|---|
| SQL function signatures, GUCs, or API docs | `check_sql_api_drift.py` |
| Contributor docs that map files/modules | `check_rust_doc_map_drift.py` |
| Markdown/MDX links or moved docs | `check_doc_references.py` |
| Release docs or broad documentation edits | `check_docs_drift.sh` |

## Dependency Update Checks

Dependencies are intentionally pinned for release reproducibility:

- direct Rust dependencies use exact Cargo requirements and committed
  `Cargo.lock` files;
- Python sandbox requirements use exact `==` versions;
- Docker base images use reviewed versioned tags, and pinned digests where the
  image is fixed in the Dockerfile. Matrix-selected runtime images must be
  reviewed before moving to a new tag family.
- Nix flake inputs are pinned in `flake.lock`; GitHub input updates are reported
  for manual lockfile review.

Run the dependency checker before a release:

```bash
python3 scripts/check_dependency_updates.py
```

The script queries crates.io, PyPI, Docker Hub, and GitHub for Nix flake inputs.
It reports update candidates only when a newer release or commit is at least 6
hours old. Newer releases and commits are flagged for review but are not
recommended for release adoption. Any package-manager fetch or install needed
after a manifest change must go through `sfw`.

To change a supported manifest, name the dependency explicitly and confirm:

```bash
python3 scripts/check_dependency_updates.py --update cargo:serde --yes
python3 scripts/check_dependency_updates.py --update pypi:streamlit --yes
```

Docker and Nix flake input updates are reported but not rewritten automatically.
Review the upstream image, changelog, digest when present, or locked Git
revision manually before changing `Dockerfile` or `flake.lock`.

## Cleanup Script

`clean_generated_artifacts.sh` removes only generated local artifacts:

```bash
./scripts/clean_generated_artifacts.sh --dry-run
./scripts/clean_generated_artifacts.sh
```

It deletes:

| Path | Reason |
|---|---|
| Cargo target directory (`target/` under `graph/`) | Cargo/pgrx build output |
| Fuzz Cargo target directory | Fuzz build output |
| `.DS_Store` files | macOS Finder metadata |

It intentionally keeps test and release assets such as `graph/fuzz/corpus/`,
`graph/fuzz/fuzz_targets/`, `graph/proptest-regressions/lib.txt`,
`graph/tests/heavy/`, and `graph/src/pg_test.rs`.

## Docker Packaging Scripts

The Docker scripts are split into build and install phases so maintainers can
build once and install the same package into multiple containers.

### Build Package Artifacts

```bash
./scripts/build_docker_pggraph_package.sh 17 target/docker-packages
```

This builds the root `Dockerfile` builder stage for PostgreSQL 17 and copies the
package directory to:

```text
target/docker-packages/graph-pg17/
```

To build all supported PostgreSQL majors:

```bash
./scripts/build_docker_pggraph_package.sh all target/docker-packages
```

Control the all-version set with `PG_VERSIONS`:

```bash
PG_VERSIONS="16 17 18" ./scripts/build_docker_pggraph_package.sh all
```

### Copy Package Into A Container

```bash
./scripts/copy_pggraph_package_to_docker_postgres.sh \
  my-postgres target/docker-packages/graph-pg17 17 appdb postgres
```

The script verifies the package shape and the target container state, then
copies:

| Source | Target |
|---|---|
| `usr/share/postgresql/<major>/extension/.` | `/usr/share/postgresql/<major>/extension/` |
| `usr/lib/postgresql/<major>/lib/graph.so` | `/usr/lib/postgresql/<major>/lib/graph.so` |

By default it also runs:

```sql
CREATE EXTENSION IF NOT EXISTS graph;
```

Set `SKIP_CREATE_EXTENSION=1` when you only want to copy files:

```bash
SKIP_CREATE_EXTENSION=1 ./scripts/copy_pggraph_package_to_docker_postgres.sh \
  my-postgres target/docker-packages/graph-pg17
```

### One-Shot Container Install

```bash
./scripts/install_into_docker_postgres.sh my-postgres 17 appdb postgres
```

This wrapper builds a temporary package for the requested PostgreSQL major, then
calls `copy_pggraph_package_to_docker_postgres.sh`. Use it for one-off local
installs. Use the split build/copy flow when installing into multiple
containers.

The PostgreSQL major must match the target container. A package built for
PostgreSQL 17 should not be copied into a PostgreSQL 16 container.

## Artifact Inspection

Use `inspect_pggraph_artifact.py` when debugging persistence or release evidence
output:

```bash
python3 scripts/inspect_pggraph_artifact.py /path/to/main.pggraph
```

The script prints JSON with section sizes and offsets. It does not validate the
full graph semantics; use persistence tests and release smoke scripts for that.

## Heavy Script Groups

Heavy scripts live in `graph/tests/heavy/` and are usually run from `graph/`.
They cover operational behavior that pgrx tests do not model well: client/server
SQLSTATEs, transaction lifecycle, Docker images, package installation,
backup/restore, upgrade, concurrency, memory, synthetic release evidence, and
playground query stability.

| Script | Use when | What it does |
|---|---|---|
| `run_release_gate.sh` | Final pre-release validation | Aggregates static checks, docs, Rust tests, pgrx tests, cargo-deny, fuzz compile, package validation, install smoke, metadata audit, boundary tests, backup/restore, lock tests, concurrency, synthetic smoke, playground query validation, public GQL write transaction checks, and pgbench. Set `RUN_TX_DELTA_CRASH=1` with disposable `PGDATA` to include the internal mutable-overlay crash proof; set `RUN_BUILD_MEMORY_STRESS=1` to include opt-in build-memory stress profiles. |
| `run_pg_matrix.sh` | Local PostgreSQL-major compatibility | Runs release-profile Rust tests and optionally pgrx SQL tests for officially supported PostgreSQL 14 through 18 using local `pg_config` binaries. |
| `run_pg_matrix_docker.sh` | Matrix validation without local headers | Builds the Docker matrix image and runs supported PostgreSQL-major checks in Docker. |
| `open_type_release_matrix.sh` | Open-type source and package compatibility | Runs focused open-type PostgreSQL tests, then packages, installs, reloads, and queries the extension on PostgreSQL 14 through 18. |
| `open_type_package_smoke.sh` | Installed-package open-type behavior | Builds 255 relationship labels in a disposable database and checks paginated inventory plus exact traversal, path, GQL, and Cypher results after unload/reload. |
| `run_gql_write_matrix.sh` | Supported-major GQL write correctness | Runs the full and persisted isolation profiles plus deterministic MERGE, relationship, and stale-write races on PostgreSQL 14 through 18, using isolated temporary clusters. |
| `docker_smoke.sh` | Top-level Dockerfile check | Builds the root Docker image, starts Postgres, creates the extension, and runs a small graph workflow. |
| `package_validate.sh` | Package artifact check | Runs `cargo pgrx package` and verifies control, SQL, shared library, and release companion files. |
| `fresh_install_smoke.sh` | Installed extension smoke | Installs pgGraph into a local PostgreSQL major and runs a quick extension workflow. |
| `function_metadata_audit.sh` | SQL metadata contract check | Audits SQL function volatility, security, cost, rows, parallel-safety metadata, and generated sync-trigger definer paths. |
| `run_sqlstate_acl_boundary.sh` | Client-visible errors and ACLs | Runs `sqlstate_acl_boundary.sql` through `psql` and asserts SQLSTATE/role behavior at the client boundary. |
| `tx_delta_lifecycle.sh` | Mutable-overlay transaction lifecycle | Installs the internal pg_test surface and checks transaction-local overlay visibility, rollback discard, commit cleanup, backend isolation, and trigger-sync catch-up. |
| `gql_create_tx_lifecycle.sh` | GQL `CREATE` transaction lifecycle | Installs the extension into a disposable database and checks public mapped `CREATE` rollback and commit behavior. |
| `gql_set_tx_lifecycle.sh` | GQL `SET` transaction lifecycle | Installs the extension into a disposable database and checks public mapped property `SET` rollback behavior, including typed filter-delta cleanup. |
| `gql_delete_tx_lifecycle.sh` | GQL `DELETE` transaction lifecycle | Installs the extension into a disposable database and checks public mapped edge-row `DELETE` rollback behavior, including bidirectional tombstone cleanup. |
| `gql_write_recheck_race.sh` | GQL write predicate rechecks | Runs two-session races proving stale mapped predicates fail with SQLSTATE `22000` and a clean transaction-local graph state for `SET`, `REMOVE`, and `DETACH DELETE`. |
| `gql_merge_race.sh` | GQL `MERGE` same-key race | Uses a trigger-level barrier to make concurrent mapped `MERGE` statements contend on one source key and verifies one insert plus one match retry. |
| `gql_relationship_race.sh` | GQL relationship races | Uses PostgreSQL lock handshakes to prove same-identity relationship `CREATE` produces one winner and one `23505`, while concurrent relationship `DELETE` produces one winner and one stale-row `22000`. |
| `gql_isolation_matrix.sh` | GQL transaction isolation | Runs every advertised write family against concurrent READ COMMITTED, REPEATABLE READ, and SERIALIZABLE readers and requires source/projection visibility to follow PostgreSQL snapshots before and after commit. Set `PERSIST_ON_BUILD=on` for the durable new-node, reload, and repeated-build profile. |
| `tx_delta_crash_recovery.sh` | Mutable-overlay crash/reload | Installs the internal pg_test surface, kills a disposable postmaster with an uncommitted overlay open, restarts it, and verifies the overlay did not persist. |
| `run_pgbench_sync.sh` | Trigger sync stress | Uses `pgbench_sync.sql` to drive concurrent DML and validate explicit sync apply behavior. |
| `read_latency_under_sync.sh` | Read-latency baseline | Measures traversal and path p50/p95/p99 latency with no backlog, pending sync backlog, and concurrent sync writers. |
| `backup_restore_validate.sh` | Dump/restore behavior | Validates source tables, graph catalog state, sync state, and persisted graph behavior across backup and restore. |
| `pg_upgrade_validate.sh` | Upgrade compatibility | Runs `pg_upgrade --check` for a database containing pgGraph. |
| `run_pg_upgrade_matrix.sh` | Supported-major upgrade compatibility | Creates sentinel-owned throwaway clusters and checks adjacent PostgreSQL 14→15 through 17→18 upgrades, followed by a derived-artifact rebuild. |
| `run_crash_matrix.sh` | Supported-major crash recovery | Runs persisted-generation and transaction-overlay crash recovery in separate disposable PostgreSQL 14–18 clusters. |
| `crash_recovery.sh` | Disposable-cluster crash behavior | Kills a disposable postmaster, restarts it, checks persisted graph reload, and verifies corrupt artifact rejection. |
| `background_job_lock_regression.sh` | Background job lock safety | Validates build/maintenance lock and failure-state behavior. |
| `build_lock_regression.sh` | Build/vacuum conflict safety | Validates lock conflicts and persisted artifact behavior around build and vacuum. |
| `concurrency_stress.sh` | Mixed operational concurrency | Exercises build, sync, vacuum, maintenance, and traversal under concurrent activity. |
| `synthetic_release_smoke.sh` | Deterministic release fixture | Builds and queries a synthetic relational graph through SQL for release-gate evidence. |
| `run_synthetic_release_evidence.sh` | Synthetic evidence capture | Runs synthetic release smoke, captures metadata, and inspects `.pggraph` artifacts when available. |
| `playground_release_gate.sh` | Playground query stability | Prepares the Docker-backed Panama playground dataset, validates every sidebar SQL example from the shared playground catalog, and fails if expected row counts or stable result hashes change. |
| `measure_build_rss.sh` | Build memory evidence | Measures peak backend RSS during graph build. |
| `build_memory_stress.sh` | Build memory stress | Runs baseline, small-batch, repeated-rebuild, and low-memory rebuild RSS profiles through `measure_build_rss.sh`. |
| `measure_mmap_pss.sh` | mmap sharing evidence | Linux-only proportional set size check for mmap sharing across backends. |
| `run_memory_sanitizers.sh` | Memory/FFI boundary checks | Runs pgrx tests and optional Rust ASan or disposable PostgreSQL-process Valgrind checks. |
| `run_postgres_process_sanitizer.sh` | PostgreSQL-process memory safety | Runs persisted mmap, corruption, callback, guarded-error, and build-job paths under Valgrind and requires zero unsuppressed errors. |
| `run_durable_projection_matrix.sh` | Supported-major durable projection safety | Runs cross-backend lifecycle, relation-identity DDL, publication, writer-horizon, competing-publisher, build, and vacuum lock regressions on PostgreSQL 14 through 18. |
| `relation_identity_concurrent_ddl.sh` | Concurrent relation identity | Proves builds serialize with relation DDL, renames retain registered OIDs, and drop/recreate cannot retarget a registration by name. |

Most heavy scripts accept `PG_VERSION_FEATURE=pg17`. Many also accept `DBNAME`
or other disposable database names. Scripts that kill, upgrade, or inspect
PostgreSQL clusters require disposable `PGDATA` directories; do not point them
at production clusters.

## Common Workflows

Fast pre-PR check:

```bash
./scripts/check_docs_drift.sh
cd graph
cargo fmt --check
cargo test --features pg17
cargo pgrx test pg17
```

Docker install into an existing container:

```bash
./scripts/install_into_docker_postgres.sh my-postgres 17 appdb postgres
```

Build once, install many:

```bash
./scripts/build_docker_pggraph_package.sh 17 target/docker-packages
./scripts/copy_pggraph_package_to_docker_postgres.sh \
  my-postgres target/docker-packages/graph-pg17 17 appdb postgres
```

Release gate:

```bash
cd graph
PG_VERSION_FEATURE=pg17 ./tests/heavy/run_release_gate.sh
```

PostgreSQL-major matrix:

```bash
cd graph
./tests/heavy/run_pg_matrix_docker.sh
```
