---
name: pg-feature-plan
description: Drop a heavy, citation-rich implementation plan for a scoped PostgreSQL backend feature — Phase 2 of the two-phase PG planner, the bridge from a brainstorm-with-picked-approach to /pg-implement. Names every src/backend or src/include file that must change with file:line cites at a pinned anchor, enumerates catalog / CATALOG_VERSION_NO / WAL / on-disk / lock-order / extension-ABI risks, proposes the test surface (regress / iso / TAP), structures the patch into independently-reviewable phases, picks a CommitFest landing strategy, and emits the plan-mode plan that /pg-implement executes phase-by-phase with plan-linked commits. **Use proactively whenever the user invokes /pg-plan, says "plan this PG feature", "make a plan for X in PG", "drop a heavy plan", "plan-mode plan for [PG feature]", "i picked option [A/B/C] in the brainstorm, now plan it", "we settled on the [approach] for the [PG topic], write me the phase plan with file:line cites", "spec-to-plan this pgsql-hackers thread", "shadow-implementation plan against [hackers URL]", "REJECT-track plan for CF NNNN", or has a brainstorm doc + a picked approach for a PostgreSQL backend change and wants the file-by-file rollout — even when they don't use the literal word "plan".** Skip when the idea is still exploratory and no approach is picked (use pg-feature-brainstorm first), for product / sprint / Q4 / roadmap / Jira / OKR planning, application architecture planning, Terraform / k8s / Helm / Ansible / CI infrastructure plans, React / Redux / Zustand / Vue / Angular frontend refactor plans, migration plans between non-PG databases (MySQL→PG migration is a DBA migration plan, not a PG feature plan), generic non-PG database feature planning (SQLite, DuckDB, MongoDB, MySQL features), and code-review of an already-submitted patch (use pg-patch-review or review-checklist).
when_to_load: Pick an approach is picked, drop a heavy plan with file:line cites; shadow-implementation runs; spec-to-plan from a pgsql-hackers thread; REJECT-track plans where the proposal should be declined.
companion_skills:
  - pg-feature-brainstorm
  - pg-implement
  - pg-claude
  - pg-patch-review
  - review-checklist
  - patch-submission
  - commit-message-style
  - meta-commit-style
companion_scenarios:
  - add-new-builtin-function
  - add-new-data-type
  - add-new-operator-class
  - add-new-operator
  - add-new-cast
  - add-new-aggregate-function
  - add-new-error-code
  - add-new-system-catalog-column
  - add-new-system-view
  - add-new-sql-keyword
  - add-new-node-type
  - add-new-utility-statement
  - add-new-plan-node
  - add-new-expression-eval-step
  - add-new-cost-model-knob
  - add-new-index-am
  - add-new-table-am
  - add-new-wal-record
  - add-new-buffer-strategy
  - add-new-guc
  - add-startup-hook
  - add-new-bgworker
  - add-new-hook
  - add-new-lwlock-tranche
  - add-new-shared-memory-region
  - add-new-pg-stat-view
  - add-new-protocol-message
  - add-new-replication-message
  - add-new-extension
  - add-new-test-module
  - bump-catversion
---

# pg-feature-plan — Phase 2 of the PG planner

The heavy stage. Phase 1 (`pg-feature-brainstorm`) narrowed the
design space; Phase 2 makes it implementable. Output is a plan-mode
plan with file:line cites that `/pg-implement` (NOT the generic
`/implement`) executes phase-by-phase with plan-linked commits.

## Inputs

- **Slug** (required): the planning directory under `planning/<slug>/`.
  If `brainstorm.md` exists, read it first — the picked approach + the
  DECISION: answers are your constraints.
- **No brainstorm?** Fine — accept a direct natural-language description
  of the picked approach + any constraints the user has already locked
  down. But note in the plan's intro that Phase 1 was skipped (and
  therefore some design-space exploration may be missing).
- **Thread spec?** A `planning/<slug>/spec.md` extracted from a
  pgsql-hackers thread (shadow-implementation runs). Run the spec
  through the engagement-classification step below before treating
  any of its content as locked.

## Context awareness — mandatory pre-step (M2)

**Before drafting any plan content**, run a context probe. Surfaced
by the money-fx-exchange shadow run (April-1 2026 joke proposal that
the planner initially treated earnestly).

1. **Posting date.** Check the thread's first-message date:
   - April 1 → flag for joke-check (look for `[PoC]` / `[RFC]`
     wording, absence of patch attachment, deadpan replies).
   - Within 2 weeks of a release-branch cut → flag for late-cycle
     context; the realistic CF target is the *next* window, not
     this one.
   - During an open CommitFest's commit window → check if the
     author intends this CF or the next.
2. **Author history.** One-shot poster vs sustained contributor.
   `git -C source log --author=<email> --oneline` and a quick
   pgsql-hackers archive search. A one-shot speculative post
   warrants different planning energy than a sustained
   contributor's serious proposal.
3. **Thread engagement signal** (see M5 below for the taxonomy).
   Reply count alone is misleading; classify what kind of
   engagement the replies represent.

Output a `## Context` block at the top of the plan that names date,
author posture, and engagement class. If the probe surfaces signal
that the proposal isn't serious (deadpan-only replies, joke
indicators, demonstrably unimplementable), the plan's recommended
verdict shifts to **REJECT** with cited reasons rather than a phased
implementation. See `.claude/skills/review-checklist/SKILL.md` Phase 0
for the REJECT-A/B/C grade rubric.

## Thread-engagement classification (M5)

When the input includes a thread (typical for shadow-implementation
runs), classify the engagement explicitly — not just the reply count.
Surfaced by the money-fx-exchange shadow run, where the deadpan
"thanks, add to commitfest" reply was the only public signal.

| Class | Signature | Plan implication |
|---|---|---|
| `unengaged` | No technical replies; deadpan acks; silence | Treat the spec as the *author's* unreviewed take. Don't pretend community endorsement exists. |
| `acked` | Technical replies with no objections | Spec is community-validated; proceed at normal confidence. |
| `debated` | Multiple substantive replies with disagreements + counter-proposals | Plan should enumerate the open questions as §13 risks, not paper over them. |
| `contested` | Named senior contributors raising correctness / design objections | Plan should NOT proceed to implementation phases until the objections are addressed. Output may be a REJECT or a brainstorm-revival pointer. |

Record the classification in the plan's `## Context` block.

## REJECT-track output shape (when verdict is REJECT)

When the Context-awareness probe (M2) or Engagement classification (M5)
recommends REJECT, the plan file's body becomes a **Verdict block** in
place of the standard §3-§14 implementation sections. Required contents:

1. **Verdict line.** `REJECT-A`, `REJECT-B`, or `REJECT-C` per the
   `review-checklist/SKILL.md` Phase 0 rubric (summarized below).
2. **N concrete reasons against the design** (numbered, 3-7 typical).
   Each reason cites a `source/<path>:<line>` invariant, a
   `knowledge/subsystems/<x>.md` INV-tag, or a documented persona
   reflex from `knowledge/personas/<name>.md`.
3. **Predicted lead reviewer** with the reflex they trigger
   (e.g. "Tom Lane — type-system + dump-determinism";
   "Andres Freund — per-row hot-path"; "Noah Misch — security /
   install-script immutability"). See `knowledge/personas/` for the
   reflex map.
4. **Concrete alternative shape** if one exists (the "what they
   probably wanted" rewrite). Skip only when the proposal is
   irredeemable.
5. **Hand-off line** points to `review-checklist/SKILL.md` Phase 0
   ("Write a thread reply"), NOT `/pg-implement`.

The §1 "What this plan is" + `## Context` block still appear; §2-§14 do
not. No phased implementation, no §3 files table — those would imply the
plan is implementable, which contradicts the verdict.

### REJECT-A/B/C grade summary

Full rubric: `.claude/skills/review-checklist/SKILL.md` Phase 0.

| Grade | Meaning |
|---|---|
| `REJECT-A` | Identified all critical design problems + proposed correct alternative. Saves community cycles. |
| `REJECT-B` | Identified most critical problems but missed one major concern. |
| `REJECT-C` | Rejected for the wrong reasons OR rejected when the proposal was actually sound. Self-correct: re-open and run the seven-phase review (or this skill's standard track). |

## Output structure — multi-file plan (v1.2 — calibration-driven)

**Old shape (deprecated for non-trivial features):** single
`planning/<slug>/plan.md` covering all 14 sections.

**New shape:** **orchestrator + per-phase files + supporting docs.**
The sesvars calibration showed that single-file plans force
shallow per-phase detail (the 307-line plan could only give each
of 6 phases ~30 lines, which is why scope leaked). The new shape
lets each phase be hundreds of lines deep on its own without
bloating the orchestrator.

```
planning/<slug>/
├── plan.md                 — ORCHESTRATOR (§1-§4, §11-§14, phase index)
├── machinery-survey.md     — §0.5 existing-PG-mechanism analysis (NEW)
├── tests/
│   ├── spec.md             — §3.5 test-suite-as-spec draft (NEW)
│   ├── regress-cases.sql   — drafted regression SQL the impl must pass
│   └── tap-cases.md        — drafted TAP test scenarios
├── phase-0/
│   ├── plan.md             — full §3 / edits / phase-end check for phase 0
│   └── notes.md            — appended during /pg-implement (R8)
├── phase-1/
│   ├── plan.md
│   └── notes.md
├── phase-2/ ... phase-N/   — one dir per phase
└── comparison.md           — written post-implementation by /pg-implement
                              if a user reference impl exists (R15 calibration)
```

**When to use the multi-file shape:**
- Feature touches > 15 files → multi-file required.
- Feature has > 3 phases → multi-file required.
- Feature has a user reference impl → multi-file required (§0.5 +
  tests/spec.md are load-bearing).
- Otherwise: single-file is fine. Mark it explicitly at the top:
  `Plan shape: single-file (small feature, < 15 files, ≤ 3 phases).`

**Orchestrator file (`plan.md`) contents** when multi-file is
used:
- §1 What this plan is, §2 Scope contract, §3 Files-by-domain
  SUMMARY table (high-level — full per-phase tables live in
  `phase-N/plan.md`), §4 Catalog impact, §5 WAL, §6 Locking, §7
  Memory, then a **§8 Phase index** with a one-line summary per
  phase and a link to `phase-N/plan.md`, then §9 Test surface
  pointing at `tests/`, §10 Docs, §11-§14 as usual.

**Per-phase file (`phase-N/plan.md`) contents:**
- Phase title + one-paragraph why.
- §3 table for THIS PHASE ONLY (files touched, change type, size,
  cited concrete edit).
- Concrete edits (the actual code shape per file).
- Phase-end check (auto-scoped per R13 based on what files the
  phase touches — see "Phase-end check scope" below).
- §13 risks for THIS PHASE.
- Inline persona + scenario + idiom citations (REQUIRED — see
  "Citations" below).

Length scales: ~200-400 lines per phase file is normal; large
phases can be 1000+. The orchestrator itself stays ~300-500 lines
regardless of feature size.

## Output (legacy single-file shape)

For small features (< 15 files, ≤ 3 phases, no reference impl):
single `planning/<slug>/plan.md` following the structure below.
Length scales with the feature: ~400 lines for a small change,
~1500 lines for a medium feature, more for a major patch series.

### Required sections (in this order)

1. **What this plan is** (1 paragraph). State the picked approach (link
   to the brainstorm if it exists). Name the chosen scope (MVP vs
   full). State the target PG version + CF window.

2. **Scope contract** (3-5 bullets). Hard boundaries: what this plan
   COVERS, and what's explicitly OUT of scope. The plan-mode rule is
   strict: anything not in scope here is a follow-up.

3. **Files that change** (a table). Each row:
   - File path (under `source/...` or `dev/...` for new files).
   - Change type: `new` / `modify` / `delete` / `not edited (sync trap)`
     / `not edited (auto-generated)` / `not edited (build-time validator)`.
   - Approximate size: `tiny (<10 lines)` / `small (10-50)` / `medium
     (50-200)` / `large (>200)` / `n/a` for NOT-edited rows.
   - One-sentence summary of what changes — with a `source/<path>:<line>`
     cite for any file:line claim in the summary. Example row:
     `src/backend/utils/adt/lockfuncs.c | modify | small | Add PG_FUNCTION_INFO_V1 + body, model after pg_advisory_lock at source/src/backend/utils/adt/lockfuncs.c:624 | —`
   - Per-file doc citation: `[via knowledge/files/.../X.md]` if one
     exists.

   Aim for completeness here. Missing a file in this table is a Phase 2
   bug. If you don't know all the sites, do another grep pass.

   **Pin contract reminder (from Method Step 0):** every file named in
   the pinned scenario's checklist MUST appear in §3, including rows
   marked `NOT edited` (build-time validators, auto-generated headers,
   sync-trap files like `psqlscan.l` / `ecpg/pgc.l` / `check_keywords.pl`).
   Listing a NOT-edited row with a one-line rationale is correct;
   silently dropping it is the failure mode the §8a coverage gate
   catches at plan-finalization time. The planner can ADD sites
   discovered by grep, but can NEVER DROP a scenario row without
   explicit user approval AND a follow-up edit to the scenario file
   itself.

4. **Catalog + on-disk impact** (bulleted). Each item is yes/no with
   rationale:
   - New `pg_proc.dat` / `pg_operator.dat` / `pg_type.dat` / `pg_cast.dat`
     / `pg_opclass.dat` entries? Which file?
   - `catversion.h` bump required? (Bump if any catalog `*.dat` or
     `*.h` change.)
   - New on-disk format? (Page format, WAL record, file layout.) If
     yes, name the upgrade path.
   - `genbki.pl` re-run needed? (Implied if catalog files changed.)

5. **WAL impact** (bulleted). Each yes/no with detail:
   - New rmgr or new info byte for existing rmgr?
   - Existing record extended with new fields? (Compat policy.)
   - Replay function changes? (`*_redo` impact.)
   - `pg_waldump` desc/identify updates needed? (Implied by any record
     change.)
   - Hot Standby conflict generation? (Cite
     `knowledge/subsystems/access-nbtree.md` or
     `knowledge/subsystems/replication.md` for the pattern if applicable.)

6. **Locking + concurrency** (bulleted). Each item is "we need / we
   don't need" with cite:
   - New LWLock? (Add to `lwlocknames.h`?)
   - New heavyweight lock mode?
   - Buffer lock ordering implications? (Cite the relevant subsystem
     doc's lock-order section.)
   - Atomic-vs-spinlock decisions for shmem fields?
   - SSI predicate-lock implications?

7. **Memory + resource management** (bulleted):
   - New `MemoryContext`? Where in the hierarchy?
   - Per-query or per-tuple allocations?
   - Long-lived state (TopMemoryContext, CacheMemoryContext)?
   - `palloc_aligned` / DSA / shmem-resident state?
   - **Ownership invariants** (REQUIRED when any "X is owned by Y"
     claim appears in this section). For every ownership claim, run
     a **grep-pass data-flow verification** and record its result
     inline:
     1. Identify every code site that produces an X — `palloc`,
        `copyObject`, `copyJsonbValue`, `pstrdup`, `makeNode`,
        constructor calls, etc.
     2. For each producer, confirm the path that hands the X to Y
        — direct assignment, append to a Y-owned collection, or
        function-call argument transfer.
     3. If ANY producer hands X to something other than Y (e.g.
        appends to Y but also retains a copy elsewhere; passes by
        borrowed reference; mixes copy-vs-borrow semantics like
        `JsonValueListAppend(found, copy ? copyJsonbValue(v) : v)`),
        the ownership claim is FALSE as stated — refine the claim
        or refine the data flow before continuing.
     4. Record the grep command(s) you ran and the file:line list
        you walked, e.g.
        `Grep "JsonValueListAppend\\|copyJsonbValue" jsonpath_exec.c → 3 producers verified to hand ownership to the surrounding JsonValueList; 1 mixed-ownership site flagged at line 1741.`
     Anchored in `planning/jsonpath_leak/comparison.md` §F30: our
     plan asserted "JsonValueListFree releases only the values[]
     array, JsonbValue elements remain owned by the input jsonb"
     — but `executeAnyItem` palloc's per-element JsonbValues via
     `copyJsonbValue` that the ownership claim missed. The leak
     was 4.9 GB even after Phase 2 freed the arrays. A 30-second
     grep-pass would have shown 3 producer sites and one mixed
     copy-vs-borrow site, surfacing the wrong invariant at plan
     time rather than at R4 phase-end check time.

8. **Phased implementation** (the meat). Break into 3-8 phases (the
   old 3-6 range was tight for comprehensive features per R15).
   **For multi-file plan shape, each phase has its own
   `phase-N/plan.md` with the structure below; the orchestrator
   only lists the index.** Each phase entry:

   - Phase number + title.
   - Files this phase touches (subset of §3).
   - The 5-15 concrete edits this phase makes (each with file:line
     where possible). For multi-file shape, list every site —
     don't truncate to "5-10".
   - **Inline citations** (REQUIRED — per R14 + R15 + the sesvars
     calibration F1-F15 findings). Each phase must cite:
     - ≥1 `knowledge/scenarios/*.md` it follows.
     - ≥1 `knowledge/personas/*.md` whose reviewer reflex will
       evaluate this phase.
     - ≥1 `knowledge/idioms/*.md` whose pattern it uses.
     - Any prior calibration finding F-N from
       `sessions/2026-06-16-sesvars-calibration-findings.md` (or
       successor) that this phase must heed.
   - **Phase-end check** (auto-scoped per R13 — see "Phase-end
     check scope ladder" below): how `/pg-implement` knows this
     phase is done. NOT "regress passes" alone — apply the R13
     scope ladder.
   - **Tests covered by this phase**: list the TC-* IDs from
     `tests/spec.md` that this phase makes pass.

   A phase is self-contained enough that you could stop after it and
   the tree still builds + the declared phase-end check (R13 scope)
   passes. (This is the `/pg-implement` skill's requirement, R4.)

### Phase-end check scope ladder (R13, applied per-phase)

The phase-end check MUST match the phase's blast radius (R13 in
pg-implement-discipline.md v1.1):

- **Helper-only changes** → `meson test --suite regress`.
- **Catalog changes** (`pg_operator.dat`, `pg_proc.dat`, etc.) →
  `--suite regress` + every `--suite contrib-*`. (Sesvars F12:
  Phase 0's catalog cleanup broke `pg_stat_statements/squashing.
  sql`; the regress-only gate missed it.)
- **Grammar / lexer changes** (`gram.y`, `scan.l`, `pl_gram.y`,
  ECPG `pgc.l`) → `--suite regress` + `--suite ecpg` + every
  `contrib/*` suite that may parse SQL.
- **Executor / planner changes** (`execExpr*.c`, `clauses.c`,
  `plancache.c`, etc.) → `--suite regress` + `--suite isolation`
  + every `contrib/*` suite.
- **WAL / replication / catversion** → above + `--suite recovery`.
- **Ruleutils / parse-tree formatting** → above + a spot-check
  on `CREATE VIEW`, `EXPLAIN VERBOSE`, and `pg_get_*def` output.
  (Sesvars F14: `T_SessionVar` missing from
  `ruleutils.c get_rule_expr` broke `EXPLAIN VERBOSE` silently.)

Each phase's plan section names the EXACT command line:
`meson test -C build-debug --suite regress --suite contrib-pg_stat_statements`
— not a vague "regress passes".

9. **Test surface** (bulleted with file paths). When multi-file
   shape is used, this section is a SUMMARY that points at
   `tests/spec.md` for the full draft (Step 1.5).

   - **`tests/spec.md`** — structured test cases drafted from §0
     usage surface (REQUIRED per R14).
   - **Regress** (`src/test/regress/sql/`): name the per-phase
     happy-path file (e.g. `sessvar.sql`) AND the comprehensive
     own-suite file (e.g. `sessvar_advanced.sql` — REQUIRED by
     R14: identifier edges, type variety, cross-feature
     integration, adversarial). The comprehensive suite is what
     catches gaps the per-phase regress misses (sesvars F14).
   - **Isolation** (`src/test/isolation/specs/`): concurrency races
     this feature could cause? Name the spec file(s).
   - **TAP** (`src/test/recovery/t/` or similar): multi-node /
     subscriber / replication / cross-backend isolation. REQUIRED
     for features with backend-local state.
   - **`src/test/modules/`**: in-tree C test module needed?
   - **amcheck / `pg_amcheck`**: if AM-related.
   - **pgbench**: if performance-relevant.
   - **Contrib regression**: name any contrib SQL files that may
     break from this feature's changes (e.g. catalog-cleanup
     phases hit `pg_stat_statements/squashing.sql`).

10. **Docs** (bulleted):
    - SGML page(s) to add/update under `doc/src/sgml/`?
    - `release-N.sgml` entry needed?
    - GUC documentation (if a new GUC)?
    - System-catalog doc table update (if catalog changes)?

11. **Patch-series structure** (1 paragraph + bullets). Single patch
    or split? If split, name each patch and what depends on what.
    Default to single patch for ≤500 lines + one logical change; split
    for refactor-then-feature or multi-subsystem changes.

12. **CommitFest landing strategy** (bulleted):
    - Which CF? (e.g. "PG20-1, open now until 2026-06-30").
    - Pre-existing thread to revive, or new thread?
    - Likely reviewers? Name 2-3 with the **reflex** each would apply.
      Anchor on the subsystem the patch touches:
      - Type-system / catalog / dump-determinism / API-back-compat → Tom Lane
      - Performance / executor hot path / parallel safety → Andres Freund
      - Security / install-script immutability / test-omission → Noah Misch
      - Parser / locale / Windows portability → Michael Paquier, Peter Eisentraut
      - Replication / logical decoding → Amit Kapila, Masahiko Sawada
      - Planner / costing → David Rowley, Richard Guo
      - Buffer manager / vacuum → Melanie Plageman, Andres Freund, Peter Geoghegan
      See `knowledge/personas/<name>.md` for full reflex maps; otherwise
      `git -C source log --pretty='%an' -- <touched-file>` for recent
      committers.
    - Pre-mail self-review checklist: `review-checklist` skill.
    - First-patch-cover-letter structure.
    - Upstream commit-message style: `commit-message-style` skill
      (NOT `meta-commit-style` — those are for the meta repo only).

13. **Known risks + unknowns** (numbered list with severity tags).
    Each item:
    - **Severity:** blocker / high / medium / low.
    - **What:** the specific risk or open question.
    - **Mitigation / next step:** what we'd do to resolve.

14. **Phase-zero validation** (bulleted, optional but recommended).
    Quick checks the user can run BEFORE phase 1 of implementation to
    confirm the plan's assumptions still hold:
    - "verify file foo.c:NN still has function bar() at the cited line"
    - "grep for any since-introduced uses of struct X that the plan
      didn't account for"

### Forbidden in Phase 2

- "We'll figure it out during implementation" for anything in §3-§10.
  Either decide it here or move it to §13 as an open question.
- Citations without `file:line` from `source/`. Vague references like
  "the executor" are Phase 1 talk.
- Skipping §13 (Known risks). Every non-trivial plan has unknowns.
- **Dropping a scenario-checklist row from §3** without explicit user
  approval AND a follow-up edit to the scenario file itself. The §8a
  coverage gate fails the plan if this happens silently. NOT-edited
  rows still belong in §3 with a one-line rationale; the pin contract
  is ADD-only, not subtract.

## Method

### Step 0.0 — Read the brainstorm's §0 + §0.5 + §0.7 (load-bearing inputs)

Before the scenarios match in Step 0, **read the brainstorm's §0
usage surface, §0.5 mechanism survey, and §0.7 reference-impl
notes**. These are LOAD-BEARING:

- §0 usage surface → the spec for `tests/spec.md` (Step 1.5
  below). Every example row becomes a regression test case.
- §0.5 mechanism survey → the source for the plan's pre-§3
  "existing machinery" section. If the brainstorm's recommended
  approach picked REUSE (e.g. `PARAM_SESSION_VARIABLE` on
  existing Param node), the plan must INHERIT THAT CHOICE — do
  not silently invent a new node in §3.
- §0.7 reference-impl notes → the upper-bound spec. If the
  brainstorm read a user reference impl, the plan's §3 file
  table must be COMPARABLE to (or a superset of) the reference's
  file footprint. Under-scoping vs reference is a R15 violation.

If the brainstorm is from before this skill's v1.2 update and
doesn't have §0 / §0.5 / §0.7, **escalate**: ask the user
whether to (a) re-brainstorm with the updated skill, (b) extend
the existing brainstorm in-place with the missing sections, or
(c) proceed at the brainstorm's narrow scope with explicit
consent.

### Step 0.4 — Corpus-chain scenario expansion (cheap, load-bearing)

After Step 0 matched the brainstorm against a scenario slug, run

```
python3 scripts/corpus-chain.py --scenario <picked-slug>
```

The output gives you:

- **Files touched** — the DRAFT §3 file table before the fan-out
  greps anything. This is the evidence-derived starting point;
  the fan-out expands and verifies it.
- **Idioms invoked** — direct + transitive. Every idiom in the
  chain becomes a candidate for §8's per-phase "≥1 idiom" link.
  Read each idiom's `## Call sites` block before writing §5
  (related infrastructure) — those files are examples of the
  pattern already in the tree.
- **Adjacent scenarios** — if a scenario shares 5+ files with
  this one, EXPECT a §12 (conflict risk) entry. Either bundle
  or sequence with that scenario's work.
- **Subsystems** — owning subsystems. Each becomes a §5 entry
  and each corresponding `knowledge/subsystems/<slug>.md` MUST
  be read before the fan-out (its `## Files owned` block feeds
  the fan-out target list per domain).
- **Analogous past features** — planning slugs + session logs
  with keyword or file overlap. Read the top-2 comparison.md
  files (if present) BEFORE writing §7 (memory/resource) and
  §11 (risks). Past runs encode decisions worth inheriting.

Skip this step ONLY if there's no scenario match (brainstorm is
truly novel) — then run `--keywords` mode instead. Log the chain
map into the plan's §14 (references) so the reviewer can trace it.

### Step 0.5 — PARALLEL FAN-OUT for §3 file enumeration

The old single-context method greps the source tree linearly
and misses files because one agent can only hold so much in
context. The v1.2 method fans out: spawn one subagent per
source-tree domain in the same message.

The fan-out EXPANDS on the corpus-chain draft (Step 0.4) —
if a domain agent finds zero files but the chain named files
under that domain, the agent under-searched; if the chain
was empty but an agent finds files, the chain is thin (log
as a corpus gap in `progress/coverage-gaps.md`).

#### Fallback when nested Agent tool is unavailable

If running inside a subagent (the Agent tool may not be in your
exposed tool set), fall back to PARALLEL Read + Bash tool calls
in a single message. Same fan-out logic, same coverage — the
subagent is the "container", and the parallel tool calls inside
one message are the "fan-out". Document this fallback in your
output so the orchestrator knows the harness limitation kicked in.

(Why this fallback works: a single message with N parallel tool
calls is effectively a fan-out of N concurrent reads/greps, and
the model can synthesize across them in the same context. Sesvars
F16 calibration logged this approach as effective.)

Domains to fan out (pick the ones relevant to the feature):

- **Agent A — parser.** Grep `src/backend/parser/` +
  `src/include/parser/` + `src/pl/plpgsql/src/pl_gram.y` +
  `src/interfaces/ecpg/preproc/pgc.l` + `src/fe_utils/psqlscan.l`
  for files the feature would touch. Return a §3-row list per
  file with cited line numbers.
- **Agent B — executor + JIT.** Grep `src/backend/executor/` +
  `src/include/executor/` + `src/backend/jit/llvm/` for new
  EEOP / case-clause sites. Return §3 rows.
- **Agent C — planner + clauses.** Grep `src/backend/optimizer/`
  + `src/backend/nodes/nodeFuncs.c` + `src/backend/parser/
  parse_collate.c` for walker-coverage sites the new node needs.
  Return §3 rows.
- **Agent D — catalog.** Grep `src/include/catalog/` for
  `*.dat` rows / catversion / pg_proc / pg_operator / pg_type
  impact. Return §3 rows for catalog edits + catversion bump.
- **Agent E — PL/pgSQL integration.** Grep `src/pl/plpgsql/src/`
  for sites that would need updating IF the feature should work
  inside PL/pgSQL DO blocks / functions. Critical — sesvars
  calibration missed this entirely.
- **Agent F — ECPG.** Grep `src/interfaces/ecpg/` for sites
  that need sync if the grammar changes.
- **Agent G — Contrib + tests.** Grep `contrib/*/sql/` +
  `contrib/*/expected/` + `src/test/regress/sql/` +
  `src/test/recovery/t/` + `src/test/isolation/specs/` for
  tests that would need updating OR new tests to write.
- **Agent H — Docs + ruleutils.** Grep
  `doc/src/sgml/ref/` + `src/backend/utils/adt/ruleutils.c`
  (the parse-tree pretty-printer — F14 lesson!) for sites.
  Critical — ruleutils is forgotten by default.

Synthesize the agent outputs into the §3 table as the UNION of
(scenario checklist rows from Step 0) + (parallel-fan-out rows
from Step 0.5) + (brainstorm §0.7 reference-impl file table
rows). Deduplicate by file path.

**The plan's §3 file count MUST be comparable to or larger than
the brainstorm's §0.7 reference-impl file count.** If the parallel
fan-out produces fewer files than the reference, the fan-out
missed something — escalate.

### Step 1.5 — Draft `tests/spec.md` BEFORE §8 phase planning

The sesvars calibration showed that test-suite-as-spec is the
right shape: the test cases ARE the requirements. Drafting them
before phase planning forces the planner to think about every
usage class up-front, instead of discovering gaps phase-by-phase.

Procedure:

1. Take the brainstorm's §0 usage surface (20-30 example lines).
   Each example becomes one regression test case.
2. Add cross-cutting tests per R14 (the comprehensive own-test-
   suite rule): identifier edges, NULL handling, type variety,
   cross-feature integration (PL/pgSQL DO + CTEs + subqueries +
   EXPLAIN + savepoints + prepared params), adversarial.
3. Add cross-backend TAP cases for any feature with backend-
   local state (lifetime, isolation, reconnect).
4. Write `tests/spec.md` — a structured list of test cases
   (NOT yet SQL — the structured cases come first, the SQL
   transliteration is part of `tests/regress-cases.sql` which
   the implementation will land).

   Format:
   ```
   ## Test category: Reader
   - TC-R-1: `SELECT @x` of never-set name returns NULL.
   - TC-R-2: `SELECT @x` after `SET @x := 1` returns 1 (int4).
   - TC-R-3: `SELECT @x IS NULL` after explicit `SET @x := NULL`
     returns t.
   ...
   ```
5. The phase planning in §8 then maps each test category to the
   phase that makes it pass. If a test category has no phase
   covering it, the plan is incomplete — go back to §3 + §8.

### Step 0 — Match the brainstorm against `knowledge/scenarios/` (hard integration)

**This is the FIRST thing the planner does, before any corpus loading.**

The scenarios layer (`knowledge/scenarios/`) is task-shaped: one
playbook per recurring change-class, each with an authoritative file
checklist. When the brainstorm's picked approach matches a scenario,
that scenario's checklist is **load-bearing** — it becomes the
starting authoritative §3 table.

Process:

1. **Read `knowledge/scenarios/_index.md`** — the decision tree + the
   31-scenario inventory.
2. **Match the brainstorm's change-class** against the index:
   - **Exactly one scenario matches** → its file checklist is the
     **starting authoritative §3 table**. Every file named in the
     checklist MUST land in the plan. The planner can ADD sites
     discovered by grep but can NEVER drop sites the scenario named.
     Dropping a site requires explicit user approval AND a follow-up
     edit to the scenario itself.
   - **Multiple scenarios match (composite feature)** → union their
     checklists. The §3 table is the deduplicated union; verify each
     row still applies, but do not drop entries from the union.
   - **Zero scenarios match** → ESCALATE to the user with a flag:
     "The scenarios layer has a gap for this change-class." Record
     the gap in `progress/scenarios-coverage.md` under "Gaps
     surfaced by planner runs". Continue with grep-based discovery
     only.
3. **Check anchor drift.** Read the scenario's `last_verified_commit:`
   frontmatter. If the plan's anchor SHA ≠ `last_verified_commit`,
   emit a **"scenario stale" warning** in the plan's §1 and run a
   fresh grep pass to validate every checklist row before treating
   the table as authoritative.
4. **Record which scenario(s) the plan pins to** in the plan's
   `## Context` block (after the date / author posture / engagement
   class). Format: `Scenario(s): add-new-data-type, add-new-operator-class`.

Step 0 is the hard contract — it's what makes the scenarios layer
load-bearing rather than advisory.

### Subsequent steps

1. **Read brainstorm + DECISION answers.** If they're missing or stale,
   re-run brainstorm or ask the user inline before proceeding.

2. **Load corpus deeply.** Read the 1-3 subsystem docs from the
   brainstorm. Then walk per-file docs (`knowledge/files/src/...`) for
   the directories you'll touch. Open the actual `source/` files for
   anything not in the per-file corpus. Also load every per-file doc
   linked from the pinned scenario's checklist.

3. **Inventory the change sites.** Run targeted greps over `source/`
   for the symbols, structs, and call sites the plan will touch. Build
   the §3 table from this. Don't skip files you "think" don't need
   changes — verify.

4. **Decide catalog + WAL + lock + memory** (§4-§7) BEFORE writing
   §8 phases. The phases depend on these decisions.

5. **Phase the work.** Each phase should be 1-3 sittings of editing
   for a human. Group related edits; don't intersperse unrelated
   sites.

6. **Write tests in the plan, not in the code.** Phase-end checks (§8)
   should reference specific test files in §9.

7. **Risk surface (§13) is mandatory.** If you genuinely can't think
   of any, you haven't probed deeply enough.

8. **Verify every file:line cite — required final step (M3).**
   Surfaced by the money-fx-exchange shadow run (`cash_out` initially
   cited as `provolatile='i'`; actual is `'s'` per
   `source/src/include/catalog/pg_proc.dat:1954`).

   For each cite that appears in the produced plan:
   - Resolve the file at the anchor commit (today: `e18b0cb7344`;
     update when `pg-anchor-refresh` lands the next bump).
   - Confirm the symbol / line / value matches what the plan claims.
   - For `.dat` / config cites: spot-check the actual cell value
     (`provolatile`, `proisstrict`, GUC default, etc.), not just the
     file:line.
   - Reuse `pg-quality-auditor`'s file:line discipline (already
     established for merged docs).

   If any cite fails resolution: fix the plan inline. **Do not hand
   off a plan with stale cites.**

8a. **Scenario-coverage gate — required (M3 extension).** For every
    scenario pinned in Step 0, cross-check that **every file in the
    scenario's checklist appears in the plan's §3 table**. Missing
    files invalidate the plan:
   - If a file from the checklist is genuinely not needed for this
     specific feature, the user must explicitly approve dropping it
     AND the scenario itself must be edited (don't paper over the
     drop). Until the scenario is edited, the file stays in §3 even
     if the plan's §8 phases skip it; the deviation is recorded in
     §13 risks.
   - If anchor-drift was flagged at Step 0 and the checklist appears
     stale, run a fresh grep pass to validate each row; update the
     scenario's `last_verified_commit:` if you do the verification.

    The gate is binary: a plan with scenario-coverage gaps fails
    validation. Don't ship a plan that quietly drops scenario sites.

9. **End with a one-line hand-off:** *"Run `/pg-implement <slug>` to
   start phase 1."* (Use `/pg-implement`, NOT the generic
   `/implement` — the PG version enforces plan-linked commits, per-phase
   tests, and the file:line citation rules in
   `.claude/rules/pg-implement-discipline.md`.)

   For a REJECT-track plan (context awareness or thread engagement
   surfaced design-level problems), the hand-off is instead: *"Plan
   recommends REJECT — see Verdict block. Write a thread reply per
   `.claude/skills/review-checklist/SKILL.md` Phase 0."*

## Boundaries vs other skills

- **`pg-feature-brainstorm`** (Phase 1): the upstream. Re-run if scope
  shifts mid-plan.
- **`/pg-implement`** (Phase 3 — the PG-specific implementer, NOT the
  generic `/implement`): takes this plan and walks the phases
  interactively with the user. Auto-discovers `planning/<slug>/plan.md`
  and enforces the rules in `.claude/rules/pg-implement-discipline.md`
  (plan-linked commits per phase, file:line citations, etc.).
- **`patch-submission`**: takes over once code is done; this skill stops
  at the plan.
- **`review-checklist`**: pre-mail check; references in §12 but doesn't
  run here.
- **`memory-keeping`**: session log of the planning effort goes through
  this skill at end.

## Style

- Cite or don't claim. Every file:line in the plan must be verifiable
  with grep against current source at the anchor commit. Tag claims
  not from `source/` with `[from knowledge/...]` or `[unverified]`.
- Be specific. "We'll need to update the planner" → which `planner.c`
  function, at which line, doing what.
- Be honest about scope. If a phase is going to be a full week of
  work, say so. Don't pretend a refactor is small.
- Plans rot. Stamp the plan with the source commit it was written
  against (in §1). When the gap from current master grows, re-validate.

## Where the artifact lives

`planning/<slug>/plan.md`, next to `brainstorm.md` from Phase 1. Both
under `planning/<slug>/`. Also acceptable:
`planning/<slug>/notes.md` — running notes from `/implement`,
appended-to per phase.

When the feature lands upstream, link the plan from the commit message
("see planning/<slug>/plan.md in pg-claude meta repo for design
notes") and consider archiving the brainstorm if it's no longer useful.

## Cross-references

- `.claude/skills/pg-feature-brainstorm/SKILL.md` — Phase 1 upstream; consumes the brainstorm + DECISION: answers.
- `.claude/skills/pg-implement/SKILL.md` — Phase 3 consumer; executes the plan phase-by-phase with the discipline rules.
- `.claude/skills/pg-patch-review/SKILL.md` — Critic E supplies the REJECT-A/B/C grade rubric this skill references.
- `.claude/skills/review-checklist/SKILL.md` — Phase 0 REJECT-track is the destination when context-awareness or engagement classification recommends REJECT.
- `.claude/skills/patch-submission/SKILL.md` — used after `/pg-implement` lands the code, not here.
- `.claude/skills/commit-message-style/SKILL.md` — referenced from §12 (CF landing strategy); upstream-PG style, not meta.
- `.claude/skills/meta-commit-style/SKILL.md` — the plan.md file itself commits to the meta repo via this style.
- `.claude/skills/memory-keeping/SKILL.md` — session log of the planning effort goes through this skill at end.
- `.claude/skills/pg-claude/SKILL.md` — master nav for picking subsystem docs.
- `knowledge/scenarios/README.md` + `knowledge/scenarios/_index.md` — the scenarios layer Step 0 pins against.
- `knowledge/shadow-implementations/money-fx-exchange/skill-gaps.md` — M2/M3/M5 origin (Phase E run 1).
- `knowledge/calibration/shadow-implementation-methodology.md` — methodology this skill participates in.
- `.claude/commands/pg-plan.md` — slash-command wrapper.
