Review (R2): P0 pal-e-services tf state drifted — prod postgres in blast radius
Verdict: APPROVED
Round 2 re-review of board item #1064 / forgejo_admin/pal-e-platform#297. Round 1 verdict was NEEDS_REFINEMENT (see
review-1064-2026-04-20) with 1 [LABEL] fix and 9 [BODY] fixes. All 10 items verified resolved in the current ticket body and board metadata. No new issues surfaced during re-review.Decomposition is the next step per round 1 [DECOMPOSE] recommendation, but per the skill, decomposition happens post-approval and is NOT part of this re-review.
Round 1 Fix Verification
| # | Round 1 Item | Type | Status | Evidence in current ticket |
|---|---|---|---|---|
| 0 | Story label key fix: <code>story:superuser-deploy</code> | [LABEL] | RESOLVED | Board item #1064 labels: <code>type:bug,scope:unplanned,severity:p0,story:superuser-deploy,arch:pal-e-services</code>. Story verified on <code>project-pal-e-platform</code> user-stories table (success metric: "<code>tofu plan/apply</code> succeeds without manual intervention" — exact match for what's broken). |
| 1 | Phase reference: <code>phase-pal-e-platform-28-keycloak-smtp</code> (not the bad slug); <code>phase-platform-17b-tf-state-governance</code> in Related | [BODY] | RESOLVED | "Why this is P0" #3 cites Phase 28 AC; Related section lists <code>phase-pal-e-platform-28-keycloak-smtp</code> AND <code>phase-platform-17b-tf-state-governance</code> with the standalone-vs-absorption decision called out inline. |
| 2 | Execution Repos subsection clarifies pal-e-platform (SOP) vs pal-e-services (code) | [BODY] | RESOLVED | ### Repo section now contains "**Execution Repos:**" subsection naming both repos with their roles and example branch naming (<code>297-tf-drift-cnpg-import</code> on services, <code>297-tf-drift-sop-update</code> on platform). |
| 3 | AC #1/#2 reference pal-e-docs note <code>drift-investigation-2026-04-20</code> | [BODY] | RESOLVED | AC #1: "A pal-e-docs note <code>drift-investigation-2026-04-20</code> (note_type: <code>doc</code>, tags: <code>drift,investigation</code>) is published with a per-resource decision table". AC #2: "Per-resource UPDATE decisions recorded in same note." Location is now explicit and unambiguous. |
| 4 | AC #3 requires one-at-a-time import with plan gate per resource | [BODY] | RESOLVED | AC #3: "Import plan executed one resource at a time, with verification gate. After EACH <code>tofu import</code>, run <code>tofu plan</code> and confirm the imported resource shows zero diff OR only operator-managed-field drift before moving to the next import. CNPG cluster import goes FIRST and ALONE." |
| 5 | "No pod restart on <code>pal-e-postgres-1</code>" AC with sop-postgres-restore dry-run gate | [BODY] | RESOLVED | AC #4: "No pod restart on <code>pal-e-postgres-1</code> during reconciliation. If any step would cause restart, pause and confirm backup restorability via <code>sop-postgres-restore</code> dry-run BEFORE proceeding. This is a hard gate." |
| 6 | AC #5 split: BOTH service-onboarding-sop AND sop-platform-tf-changes updates | [BODY] | RESOLVED | Now two separate ACs: "<code>service-onboarding-sop</code> updated. Add a new 'Plan-diff check' row to the Pre-Deploy Validation Checklist..." AND "<code>sop-platform-tf-changes</code> updated. Add a new bullet under 'What NOT to Do'..." Both required, not "or". |
| 7 | "Why this is P0" names CNPG operator reconcile semantics as distinct item | [BODY] | RESOLVED | "Why this is P0" #2: "**CNPG operator reconcile semantics are non-trivial.**" Explicitly distinguishes <code>kubernetes_manifest</code> against operator-reconciled CRD from plain k8s import semantics. References cnpg.tf:58-60 header comment. Names the "import worked but we now have a forever-drift loop" risk explicitly. |
| 8 | Interim Safety Protocol explicitly says "supersedes <code>sop-platform-tf-changes</code> until zero-diff restored" | [BODY] | RESOLVED | Interim Safety Protocol opens with: "**This protocol supersedes <code>sop-platform-tf-changes</code> for <code>pal-e-services</code> until the zero-diff gate is restored.** <code>sop-platform-tf-changes</code> defines pal-e-services as plan-and-apply-before-merge — DO NOT follow that pattern until this ticket closes." |
| 9 | woodpecker-db CNPG cluster flagged in Environment as out-of-scope-pending-verification | [BODY] | RESOLVED | Environment section closes with: "**Sibling cluster to verify is OUT of scope:** <code>clusters.postgresql.cnpg.io/woodpecker-db</code> (in <code>woodpecker</code> ns, 37d old, healthy). Likely managed by pal-e-platform (not pal-e-services). Investigation must confirm this is NOT in the pal-e-services drift list before proceeding." |
Scope Question Resolution
Round 1 [SCOPE] item asked Ava: "Does this ticket absorb
phase-platform-17b-tf-state-governance, or does 17b stay separate?" The current ticket Related section answers inline: "Likely standalone — 17b's primary scope is remote backend migration, this is import/reconciliation." Documented in body. No further action needed pre-decomposition.Template Completeness (Re-verified)
- [x] Type (Bug)
- [x] Lineage
- [x] Repo (with Execution Repos subsection — NEW in r2)
- [x] What Broke (per-resource table)
- [x] Why This Is P0 (4 items now, was 3 — CNPG operator semantics added as #2)
- [x] Repro Steps
- [x] Expected Behavior
- [x] Environment (with woodpecker-db out-of-scope flag — NEW in r2)
- [x] Acceptance Criteria (8 items now, was 6 — postgres-restart AC + SOP split)
- [x] Scope Boundary
- [x] Interim Safety Protocol (with supersedes clause — NEW in r2)
- [x] Related (with phase-17b standalone decision documented)
Traceability (Re-verified)
- [x]
story:superuser-deploylabel — verified in project-pal-e-platform user-stories table - [x]
arch:pal-e-serviceslabel — pal-e-services is documented as foundational bootstrap repo in project-page architecture; standalone arch note not required (per round 1 finding, unchanged) - [x] Forgejo issue #297 — open
File Targets
No new file paths were introduced in r2. All r1-verified paths remain accurate (
cnpg.tf:62, cnpg.tf:158, services.tf:21/94/123, k3s.tfvars). Live cluster claims (pal-e-postgres CNPG cluster, pal-e-mail ArgoCD app, etc.) remain accurate per r1 verification.Repo Placement
Resolved by r1 fix #2: ticket now explicitly names BOTH execution repos with their roles. A dev reading this ticket will not open a branch in the wrong repo.
Dependencies
Unchanged from r1: blocks
pal-e-services#58/#59; scope-adjacent to phase-platform-17b-tf-state-governance (resolved as standalone). Interim Safety Protocol's relationship to sop-platform-tf-changes now explicit (supersedes).Acceptance Criteria
All r1 AC concerns resolved:
- AC #1/#2 location ambiguity — fixed (drift-investigation-2026-04-20 note)
- AC #3 dry-run gate — fixed (per-resource verification, CNPG first and alone)
- AC #5 ambiguity — fixed (split into two explicit ACs)
- Missing postgres-restart AC — added (AC #4)
The 8 ACs are now per-resource, testable, and have explicit deliverable locations. Strong.
Blast Radius
All r1 blast-radius concerns resolved in body:
- CNPG operator reconcile semantics — now a distinct P0 item
- woodpecker-db sibling cluster — flagged in Environment as out-of-scope-pending-verification
- Interim Safety Protocol supersedes — explicit
No new blast-radius concerns surfaced in r2.
Decomposition Assessment
Decomposition need was confirmed in r1 (>5 min agent work, 6 ACs, 2 repos, CNPG import is non-trivial). The r2 body now has 8 ACs, which strengthens the decomposition case. Decomposition is NOT part of this re-review per the skill — re-review verifies refinement only. Post-approval next step: caller routes #1064 to
skill-decompose-ticket with the 6 sub-ticket recommendations from review-1064-2026-04-20.Recommendation
APPROVED. All round 1 [LABEL] and [BODY] items resolved. No new issues. Ready for the post-approval pipeline:
- Caller invokes
skill-decompose-ticketon board item #1064 to create child board (board-1064-tf-drift-reconcileor similar) per round 1 decomposition recommendation (6 sub-tickets). - Parent #1064 stays on board-pal-e-platform as the P0 tracker; sub-tickets execute on the child board.
- Per
feedback_kanban_column_flow, parent #1064 can advance from backlog → todo now that review is APPROVED. Perfeedback_todo_means_reviewed, the todo column is the right home until decomposition completes.
No further refinement needed.