Skip to content

fix(recovery): fail-closed physical profile parse and WAL wording - #219

Merged
seonghobae merged 7 commits into
mainfrom
cursor/bc-2b2c5a03-fe6c-461c-be59-74976ca5f6fc-c4c5
Aug 17, 2026
Merged

fix(recovery): fail-closed physical profile parse and WAL wording#219
seonghobae merged 7 commits into
mainfrom
cursor/bc-2b2c5a03-fe6c-461c-be59-74976ca5f6fc-c4c5

Conversation

@cursor

@cursor cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor

Why this exists

#217 at 2aaa6d84 binds a caller-owned physical/WAL/PITR profile, but parse_postgres_physical_recovery_profile leaks UnicodeEncodeError on lone surrogates, and the physical+immediate contract wording says a base backup "may omit WAL". PostgreSQL 18 still requires backup-internal WAL for a crash-consistent restore (pg_basebackup -X stream or -X fetch). wal_archive_required=False only means no continuous archive.

This successor keeps the #217 seam and repairs those two contracts. It does not execute pg_basebackup, archive WAL, or replay a cluster.

Operator action

Evidence

  • Local coverage run --branch --source=pg_llm_batch.postgres_physical_recovery: 43 passed, 71 stmts / 22 branches / 0 miss / 0 partial = 100%.
  • ruff clean on the changed files.
  • Docs: docs/doctoring/postgres-physical-pitr-profile.md, docs/adr/0019-postgres-physical-pitr-profile.md.

Writer boundary

Same five files as #217. Does not race #208 backup execution, #212 restore seek, #214 docs overlay, #215 receipt binding, or #216 catalog inspection. CHANGELOG/README/ARCHITECTURE stay with those writers.

This slice does not prove live WAL replay, schema/RLS parity, CSAP, or SOC 2 readiness. Refs #204.

Open in Web View Automation 

cursoragent and others added 2 commits August 16, 2026 15:41
Record a content-free physical or PITR recovery profile without executing
pg_basebackup or WAL replay. Point-in-time targets require a PITR method
and a WAL archive. Isolated-target readiness is mandatory. Optional RPO/RTO
values remain deployer objectives and cannot be persisted as a package
capability claim.

Refs #204.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Wrap profile JSON encode in UnicodeError so lone surrogates raise the
typed parser error, matching the receipt seam. Treat
wal_archive_required=False as no continuous archive, not omitted
backup-internal WAL, and record that immediate is end-of-backup
consistency rather than replay-to-end-of-archive.

Refs #204.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Stale comment

Landing vehicle for the #217 repairs

This head (b03d913) is the successor to #217 2aaa6d84. It keeps the evidence-only physical/WAL/PITR profile seam and repairs the two Important defects:

  • parse_postgres_physical_recovery_profile now wraps encode("utf-8") in except UnicodeError and raises PostgresPhysicalRecoveryError, matching the receipt parser on main.
  • Physical+immediate wording now means no continuous WAL archive, not omitted backup-internal WAL. immediate is end-of-backup consistency, not replay-to-end-of-archive.

Local evidence on this head: 43 tests; coverage run --branch --source=pg_llm_batch.postgres_physical_recovery is 71 stmts / 22 branches / 0 miss / 0 partial = 100%. ruff clean on the changed files.

Do not merge #217. Do not self-approve this PR. Merge only after required checks are terminal-success on unchanged b03d913 and a non-author human approval lands. Keep #208 and #212 as the logical dump/restore executors. Do not start a live #204 pg_basebackup + WAL drill from this branch.

View PR

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

@seonghobae
seonghobae marked this pull request as ready for review August 16, 2026 16:07
@cursor

cursor Bot commented Aug 16, 2026

Copy link
Copy Markdown
Contributor Author

Bugbot is not enabled for your account, so this pull request was not reviewed.

Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs.

@cursor cursor Bot left a comment

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review of #219 at b03d913

This head is the landing vehicle for the #217 repairs. It is cursor-authored, so this is not an approval.

Stated repairs

  1. parse_postgres_physical_recovery_profile wraps encode("utf-8") in except UnicodeError and re-raises the typed JSON error with from None, matching the receipt parser. Lone surrogates stay inside that error.
  2. wal_archive_required=False now means no continuous WAL archive. It does not waive backup-internal WAL (pg_basebackup -X stream or -X fetch).
  3. immediate is end-of-backup consistency. Replay-to-end-of-archive is the absence of a recovery_target* setting, not a recorded kind. pitr+immediate has a success test.

package_capability_claim remains hardcoded false. The seam does not execute pg_basebackup or WAL replay. No Critical or Important fail-open parse path, leaked exception text, type-confusion, or ADR collision with #212/#215/#216 was found on this exact head.

Minor

The documentation test locks backup-internal / continuous-archive wording, but not the immediate end-of-backup clarification. That is not a merge blocker.

Operator next action

Human-review this exact SHA. Merge only after every required check on b03d913 is terminal-success and a non-author approval lands. Do not merge #217 or #220. Do not start a live #204 pg_basebackup / WAL drill on this head. Keep #208 and #212 as the logical dump/restore executors. After this lands, the next unowned recovery slice is the #218 successor (re-inspect current bytes; do not treat exact-type evidence objects as inspection provenance; allocate ADR 0020). Do not open a third physical-profile PR.

Open in Web View Automation 

Sent by Cursor Automation: Fix Issues

Comment thread tests/test_postgres_physical_recovery_documentation.py
cursor Bot pushed a commit that referenced this pull request Aug 16, 2026
Succeed the #204 isolated-target gap without racing #212. Operators
must name a live pg_service and a distinct restore-drill service
before pg_restore. DSNs, tenant scope, and same-name reuse fail
closed. Allocate ADR 0021 so the record does not collide with
#216/#219/#221.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact head b03d91389afb4b32cd5350f890d9ddc682a7edeb against protected main@d2f1e32271910a6db98a0757d67194ddadca4566. The profile remains evidence-only: it does not execute pg_basebackup, archive WAL, restore a cluster, or claim RPO/RTO capability. Exact-type and bounded JSON parsing fail closed, lone-surrogate encoding is normalized into the package error boundary, PITR requires a WAL archive, point-in-time targets require the PITR method, and package_capability_claim is fixed false. The physical/immediate wording correctly distinguishes backup-internal WAL from a continuous WAL archive. No unresolved review thread or current must-fix finding was found. Approval is for this unchanged head only; merge still requires every then-live exact-head workflow/check to terminal-success and current ancestry/mergeability to remain valid.

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One current documentation-regression gap remains on exact head b03d91389afb4b32cd5350f890d9ddc682a7edeb against protected main@5267146534a259f85c0985e153f3f6cb1281f58f. The operator document correctly states that PITR immediate means end-of-backup consistency and is not replay-to-end-of-archive, but tests/test_postgres_physical_recovery_documentation.py does not pin that distinction; the existing unresolved inline thread identified the same gap. Because this successor specifically repairs the misleading physical/PITR wording, leaving its defining immediate semantics unguarded permits a future documentation regression while CI stays green. Please add a narrow documentation regression that requires the end-of-backup/non-replay-to-end-of-archive contract in both authoritative documents, then re-run the focused profile/documentation tests and exact 100% owned coverage/docstrings. Do not change the evidence-only runtime semantics or widen this into a live WAL/PITR executor. This review supersedes my earlier approval after re-verifying the unresolved current finding; it is exact-head only and must not transfer after the repair.

@seonghobae
seonghobae marked this pull request as draft August 16, 2026 19:43
@seonghobae
seonghobae marked this pull request as ready for review August 16, 2026 21:38

@seonghobae seonghobae left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed exact current head 8d75bc2dce5909d0967ac961549f379c3b5c0252 against protected main@76e704415651bdef6ceb06efa8db279349bea22e. This head supersedes my prior CHANGES_REQUESTED on b03d91389afb4b32cd5350f890d9ddc682a7edeb: a fresh compare shows the only #219-owned change since that reviewed head is the requested narrow documentation regression, while the remaining head advancement incorporates commits now on protected main. tests/test_postgres_physical_recovery_documentation.py now requires both authoritative documents to retain immediate, end-of-backup, and replay-to-end-of-archive, in addition to the backup-internal-WAL versus continuous-archive contract. The PR's five-file net delta remains evidence-only; it does not execute pg_basebackup, archive/replay WAL, or claim package RPO/RTO capability. The sole visible review thread is resolved. No current source/documentation must-fix remains from the prior review. Approval applies only to this unchanged exact head and does not waive terminal-success exact-head workflows/checks, live ancestry/mergeability, or thread-resolution requirements before merge.

seonghobae added a commit that referenced this pull request Aug 16, 2026
…228)

* test(recovery): require isolated restore-target service names

Add the RED contract for #204 isolated-target identity: a live
pg_service name and a restore-drill name must be exact distinct
libpq service identities. DSNs, tenant scope, subclasses, and
same-name reuse must fail closed before pg_restore.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

* feat(recovery): isolate restore-target libpq service names

Succeed the #204 isolated-target gap without racing #212. Operators
must name a live pg_service and a distinct restore-drill service
before pg_restore. DSNs, tenant scope, and same-name reuse fail
closed. Allocate ADR 0021 so the record does not collide with
#216/#219/#221.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

* fix(recovery): allocate collision-free restore-target ADR 0022

#222 already files ADR 0021 for the workflow-registry audit. Keep the
isolation seam unchanged and retarget this decision, doctoring, and the
documentation contract to 0022 after a fresh open-writer inventory.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

* fix(recovery): require cluster identity for restore-target isolation

Distinct libpq service names are not cluster isolation. Require
caller-owned pg_control_system() identifiers so two aliases for the
same production cluster fail closed before pg_restore.

Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
Co-authored-by: Seongho Bae <seonghobae@users.noreply.github.com>
Co-authored-by: Seongho Bae <me@seonghobae.me>
@seonghobae
seonghobae dismissed their stale review August 17, 2026 00:41

Dismissed as superseded predecessor-head review evidence. This CHANGES_REQUESTED review is bound to b03d913 and requested only the missing immediate/end-of-backup documentation regression. That exact repair was later verified and approved on 8d75bc2; the current head a623bd2 advances from 8d75bc2 only by protected-main integration commits and retains the same five-file PR delta. Current exact-head coverage/opencode checks are terminal-success. Dismissal removes stale negative evidence only. It does not transfer the old approval or satisfy the live ruleset's required qualifying approval of the current last push.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants