pgevolve v1.0 charter
This document defines what pgevolve v1.0 means: the gate that triggers the 1.0 cut, the stability commitments that hold across v1.x, the quality bar that every release meets, the Postgres-version support window, the cadence, and the parking lot of items deferred past 1.0.
The charter is living-until-1.0. At the 1.0 cut its sections
either retire (the gate-tracking ones) or get partially merged into
CONSTITUTION.md (the stability commitments +
cadence rules).
Design rationale and the brainstorm transcript live at
superpowers/specs/2026-05-28-v1-charter-design.md.
§1. What v1.0 means
A release of pgevolve where the project commits, in writing, to:
- a defined surface that won't break in the v1.x line,
- a defined quality bar that every release meets,
- a defined Postgres-version support window that rolls with the upstream EOL schedule.
The 1.0 cut happens when both of the following are true:
- Feature checklist complete (§4) — every
📋 Plannedrow currently inspec/roadmap.mdplus the explicit v1.0-blockers listed in §4 below are shipped. - Quality gates green (§3) — the existing per-push CI gate plus a new nightly proptest soak have been running clean for at least 30 consecutive days.
The maintainer cuts the 1.0 release manually. No formal RC cycle (per the "ship-when-ready" cadence in §5); the 30-day clean-CI window functions as the de-facto RC.
§2. Stability commitments
Stable in v1.x — no breaking changes without a major bump
pgevolveCLI: command names, flags, exit codes, argument shapes.pgevolve.tomlconfig schema.- On-disk plan format:
plan.sqldirective headers,intent.tomlschema,manifest.tomlschema, the BLAKE3 plan-id derivation. - The set of supported PG majors must include the Postgres community-supported versions at the time of each minor release (see §6).
Explicitly unstable in v1.x — can break without a major bump
- The
pgevolve-corelibrary API. Library consumers depend on path / git deps or pin to exact patch versions. The crate's docs.rs page leads with this notice. - Lint rule IDs and severities — new rules may land, old rules may be removed, severities may change up or down.
StepKindnames inplan.sql— backward-compatible additions only; renames bump major.- Catalog snapshot JSON schema.
- Internal modules (anything
pub(crate)).
Deprecation policy for the stable surface
Any breaking change starts with a one-minor-cycle deprecation: the old form keeps working with a stderr warning in version N, becomes a hard error in N+1. CHANGELOG notes the deprecation in N and the removal in N+1.
§3. Quality gates (gate #2 for the 1.0 cut)
All five must hold on main for 30 consecutive days before the 1.0
tag:
| Gate | What it checks | Where it runs |
|---|---|---|
| Per-push CI | cargo fmt --check, clippy -D warnings, cargo doc -D warnings, cargo deny check, lib tests, full conformance suite on PG 14/15/16/17/18 | .github/workflows/ci.yml on every push |
| Property-test soak (nightly) | The full Tier-5 property-test set at PROPTEST_CASES=5000 per PG major | .github/workflows/soak.yml cron (already exists; will bump cases to 5000 and add a 1.0-readiness check that flips green/red based on the most recent 30 days of soak runs) |
| Cargo deny advisories | Zero open advisories for ≥ 7 days before the tag | per-push + nightly |
| GH Actions disk-space | The PG-matrix conformance job survives without no space left on device (the v0.3.8 flake) | per-push; runner-cleanup step OR larger runner |
| Cargo doc clean | No broken intra-doc links across the workspace | per-push (enforced since v0.3.8) |
Out of scope for the 1.0 gate: mutation testing (cargo-mutants), fuzzer (cargo-fuzz), code-coverage badges, perf benchmarks. Each gets its own follow-up brainstorm if/when desired.
Operational definition of "30 consecutive days clean"
- The most recent 30 days of pushes to
mainhad all required CI jobs end insuccesson first attempt (reruns count against the streak — the v0.3.8 disk-space flake would have reset it). - Soak cron ran every night during the window with
success. - The streak is tracked manually pre-1.0 (a one-liner check the maintainer runs before tagging). Post-1.0 we may automate.
§4. Feature completeness (gate #1 for the 1.0 cut)
Every 📋 Planned row in spec/roadmap.md must
be ✅ Implemented and shipped. The v1.0 blockers:
| Source | Items |
|---|---|
| v0.4.0 | EVENT TRIGGER, per-partition TABLESPACE, TABLE … USING access method |
| v0.4.1 | AGGREGATE, PG 18 virtual generated columns |
| v0.4.2 | TABLESPACE (cluster object), PL-language wiring → non-SQL FUNCTION bodies |
| v0.4.3 | TEXT SEARCH family |
| v0.5.0 | FDW family (FDW, SERVER, USER MAPPING, FOREIGN TABLE, IMPORT FOREIGN SCHEMA) |
| v0.5.1 | OPERATOR / OPERATOR CLASS / OPERATOR FAMILY |
| v0.5.2 | CAST |
| v0.5.3 | Recursive views (WITH RECURSIVE) — promoted from "🔮 Future" to a v1.0 blocker. Requires cycle-aware dep-graph handling. |
That's 12 sub-spec slots across 8 minor releases. Every row is independent (modulo declared dep edges in the roadmap). The order is fixed; the timing slips.
The roadmap document owns the slotting + dep-edge analysis. The charter just lists the items.
§5. Cadence + versioning
Post-1.0, the rhythm stays the same as today: ship-when-ready, no
time-boxed schedule. Each release is a minor (new feature) or patch
(bugfix). pgevolve commits to main only — no release branches — so
every release tag points at a commit on main that passed the
per-push CI gate.
Versioning rules
- Major bump (v1 → v2): only when a breaking change to the stable surface (§2) is unavoidable. Likely never needed in 2026; pgevolve will plan to ship v1.x for the indefinite future.
- Minor bump (v1.N → v1.N+1): new object kind, new lint rule, new CLI subcommand, new config key, new PG major support.
- Patch bump (v1.N.M → v1.N.M+1): bug fixes only. The v0.3.8 → v0.3.9 emergency-fix flow is the patch template.
Release ceremony
Codified in ../CLAUDE.md directive 11:
push main, sign + push tag, wait for full 5-PG-major CI green,
then cargo publish and cargo yank the prior version if it had a
shipped bug. This rule is binding for every release, v0.x and v1.x.
§6. Postgres-version support commitment
v1.0 supports every Postgres major version supported by upstream at the time of the v1.0 release. The current set is PG 14–18. The version window rolls automatically:
- When a PG major reaches upstream EOL, the next minor release of
pgevolve drops support and removes its conformance fixtures. Each
removal lands in its own commit tagged
chore(eol): drop PG X. The CHANGELOG explicitly calls out the drop. - When a PG major is released (e.g., PG 19 in late 2026), it's added in the next minor release of pgevolve after the corresponding catalog-query work lands. The v0.3.6 release ("PG 18 catalog support") is the precedent.
- Per-version code paths are marked
// PG X+ onlyat the call site so the EOL-drop is a mechanical grep + delete.
No LTS branch. Bug fixes target current main only. Anyone needing
a fix on an older minor cuts their own patch.
§7. Explicitly post-1.0 (parking lot)
These are tracked but won't gate v1.0:
| Item | Why post-1.0 |
|---|---|
Library API stability for pgevolve-core | Tied to a clear set of external consumers materializing first. |
pgevolve-core re-export polish / typed errors at the public boundary | Same. |
| Partition pruning at plan time | Optimization, not correctness. |
SECURITY LABEL integration | Used primarily by SE-Linux; low demand. |
| Security-barrier / leakproof per-function flag review | Lands alongside finer-grained policy review. |
RULE, BASE TYPE, INHERITS, DETACH PARTITION CONCURRENTLY | Already ⛔ Not planned — kept here as "stays out". |
| Mutation testing, fuzzer, perf benchmarks | Quality investments deferred per §3. |
Distribution surface beyond cargo install (Homebrew, Docker, GH Releases binaries) | Picked up once user-facing demand justifies. |
Explicit rename support (a -- @pgevolve rename old -> new directive for columns / renamable objects) | Renames are a bounded future feature; until then they are a documented limitation (§8). Deferred per the 2026-06 architecture review. |
| Full expression/body normalization (commutative-operand sorting, deep paren-folding) | Subtle and partly redundant with the pg_query deparser; the common-case qualifier-recursion fix ships for 1.0, the hard cases are a documented limitation (§8). Deferred per the 2026-06 architecture review. |
Full explicit type-dependent recreation (DROP each dependent + recreate views/functions, replacing the type DROP … CASCADE entirely) | The 1.0 work (Phase 4) makes the CASCADE auditable — the plan names every dependent it will destroy — but keeps the gated CASCADE teardown. The larger recreation engine (which still cannot save dependent column data on an incompatible type change) is deferred. |
The charter is amendable: items can move from post-1.0 into the active checklist (§4) with a CHANGELOG note. Items can also move out of the checklist if the maintainer judges them not v1.0-blocking, with a CHANGELOG note.
§8. Known limitations at 1.0
These are accepted behaviors at 1.0 — not bugs, and not gating items. Each is documented so users know the boundary; the corresponding future work (where any) lives in the §7 parking lot. All resolved in the 2026-06 architecture review.
| Limitation | Behavior | Why accepted |
|---|---|---|
| Column / object rename | A rename is diffed as DROP-old + ADD-new and flagged with a data-loss warning; the operator must intervene. | The prime directive (no silent data loss) is preserved by the warning. Explicit rename is a bounded future feature (§7), deliberately not built for 1.0 to keep the core simple. |
| Column reorder | Unsupported; column order changes are not planned. | Reordering requires a full table rewrite — prohibited for data-bearing tables (Constitution §11) — and column order rarely carries logical meaning. |
| Expression-equivalence edge cases | Boolean operands in a different order (a AND b vs b AND a), differing parenthesization, or an unnamed expression column in a view SELECT (SELECT 1 → pg_get_viewdef renders 1 AS "?column?") may produce a spurious (no-op) diff on some views/expressions. The common multi-table / subquery qualifier cases are normalized for 1.0; name view expression columns to avoid the ?column? case. | The failure direction is always safe — a spurious recreate, never data loss. Full normalization is deferred (§7) as subtle and partly redundant with the deparser. |
| Extension replacement CASCADE not enumerated | DROP EXTENSION … CASCADE (emitted when an extension must be dropped+recreated) silently destroys objects depending on what the extension provides; the destructive warning says so generically but cannot name them. | pgevolve manages the extension declaration, not its provided objects / membership (that's pg_depend data outside the managed model), so the dependents aren't enumerable. The operator must review manually. |