Project instructions: pg_pure

Read this first, then DESIGN.md. ROADMAP.md is the only plan file, so do not start a new plan or notes file. Domain steering (PostgreSQL, C, AWS) is in .agent-steering-domains.md, which is local and not tracked.

What this is

pg_pure makes Pure a procedural language in PostgreSQL 17, 18 and 19. It embeds libpure from github.com/gburd/pure-lang (branch libpure-embeddable, upstream PR #48). Two SQL languages come from one pg_pure.so: pg_pureu (untrusted, superuser-only) works now; pg_pure (trusted) is an installable stub until libpure gains a sandbox. We ultimately ship pg_pure.

Where work runs

  • All builds, tests and measurements run on EC2 in the kiwi AWS account (us-east-2), never on floki. Greg asked for this on 2026-10-27.
  • The main dev box is Debian 13; FreeBSD boxes are launched per-test and torn down. Coordinates and the teardown script live under /tmp/pgpure_* on floki, never in the repo, and no AWS ID goes in the repo (pg_fts rule 28).
  • Every resource gets the tags Owner=gburd, Purpose=pg_pure-dev and an Expires date. The dev box stops itself 12 hours after boot; push the stop time back if the work runs longer, and start it again after it stops.
  • Resolve AMIs at launch time and record the AMI next to any number kept.
  • Leave the kiwi account’s other instances alone: they belong to other projects.
  • libpure is built from the fork and installed under a prefix; pg_pure is built with PURE_PREFIX pointing there. On a JIT-enabled server (its llvmjit links a different LLVM major than libpure) either remove the server’s JIT package or use the -Bsymbolic libpure (DESIGN.md 5/5a).
  • libpure changes go to the fork, never into this repo as vendored code or patches.

Rules that exist because they were broken

  1. A result needs evidence that the thing you meant to run actually ran. In the first crash matrix, every case reported “0 crashes” because the Pure function did not exist in that database, so Pure never ran. A case counts only after its own output has been checked. pg_weave learned the same lesson nine times.
  2. Look for crashes in the server log, not in client exit codes. The LLVM crash happens when the backend exits, after every query has succeeded. It shows up as “terminated by signal 11” in the log and as a different session’s “terminating connection because of crash of another server process”. Every test that runs Pure also checks the log.
  3. Every suite runs PostgreSQL JIT and Pure in the same backend, in both orders. PGDG builds llvmjit against a different LLVM major version from libpure’s. DESIGN.md section 5.
  4. A projected number is not a measurement. Report what was measured, with the box, AMI, PostgreSQL version and number of runs. When in doubt, write “unmeasured”.
  5. The Pure/PostgreSQL boundary rules in DESIGN.md section 4 are correctness invariants, not style. No ERROR may longjmp through Pure frames, and no Pure exception may escape into PostgreSQL.
  6. Type conversions never lose information silently. A value that does not fit is an ERROR. The prototype returned wrong numbers in three ways (DESIGN.md section 8).
  7. Test what you put in the docs. Every Pure snippet in DESIGN.md and README was run on the box. Keep it that way: strjoin looked plausible and does not reduce.

Code and repository

  • PostgreSQL C style and pgindent. ASCII only in code, SQL, docs and commit messages.
  • Commit messages follow PostgreSQL house style (see the steering file). No agent attribution.
  • Stage files by explicit path. Do not commit agent artifacts (.claude/, .kiro/, .direnv/, .mcp.json, .agent-steering-domains.md, _/).
  • Every new TAP test goes into ci/gate.sh in the same commit.
  • When a published claim turns out wrong, the CHANGELOG says so under “Retracted”.