pgroast 0.0.0-stage → 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +202 -0
- package/README.md +173 -2
- package/bin/pgroast.mjs +19 -0
- package/data/precedents.sql.gz +0 -0
- package/docs/corpus.md +352 -0
- package/docs/design.md +540 -0
- package/docs/eval.md +245 -0
- package/docs/haas-paranoia-notes.md +223 -0
- package/invariants/README.md +15 -0
- package/invariants/nbtree.md +14 -0
- package/package.json +51 -4
- package/prompts/brief.md +32 -0
- package/prompts/committer-paranoia.md +149 -0
- package/prompts/critic.md +56 -0
- package/prompts/enrich.md +35 -0
- package/prompts/pushback-map.md +58 -0
- package/prompts/pushback-reduce.md +72 -0
- package/prompts/reflect.md +20 -0
- package/prompts/relevance.md +20 -0
- package/prompts/roaster-already-exists.md +23 -0
- package/prompts/roaster-common.md +88 -0
- package/prompts/roaster-demand.md +28 -0
- package/prompts/roaster-maintenance-fit.md +22 -0
- package/prompts/roaster-prior-attempts.md +29 -0
- package/prompts/roaster-replication-ondisk.md +24 -0
- package/prompts/roaster-security.md +21 -0
- package/prompts/roaster-standards-compat.md +21 -0
- package/prompts/supervisor.md +31 -0
- package/prompts/synth-self.md +19 -0
- package/prompts/synth.md +140 -0
- package/prompts/tone.md +21 -0
- package/prompts/writing.md +98 -0
- package/sql/schema.sql +45 -0
- package/src/cli.ts +391 -0
- package/src/corpus/cf.ts +220 -0
- package/src/corpus/cli.ts +189 -0
- package/src/corpus/db.ts +119 -0
- package/src/corpus/fetch.ts +104 -0
- package/src/corpus/reverts.ts +133 -0
- package/src/corpus/search.ts +201 -0
- package/src/corpus/util.ts +133 -0
- package/src/corpus/wiki.ts +80 -0
- package/src/eval/cases.ts +118 -0
- package/src/eval/cli.ts +105 -0
- package/src/eval/judge.ts +136 -0
- package/src/eval/report.ts +268 -0
- package/src/eval/run.ts +153 -0
- package/src/eval/score.ts +275 -0
- package/src/roast/doctor.ts +109 -0
- package/src/roast/enrich.ts +335 -0
- package/src/roast/invariants.ts +65 -0
- package/src/roast/llm.ts +601 -0
- package/src/roast/material.ts +133 -0
- package/src/roast/page.ts +266 -0
- package/src/roast/pipeline.ts +981 -0
- package/src/roast/precedents.ts +308 -0
- package/src/roast/pushback.ts +664 -0
- package/src/roast/render.ts +386 -0
- package/src/roast/sanitize.ts +140 -0
- package/src/roast/schema.ts +714 -0
- package/src/roast/tone.ts +216 -0
- package/src/thread/fetch.ts +172 -0
- package/src/thread/load.ts +227 -0
- package/src/thread/local.ts +73 -0
- package/src/thread/parse.ts +243 -0
- package/src/thread/patchprose.ts +143 -0
- package/src/thread/types.ts +120 -0
package/docs/eval.md
ADDED
|
@@ -0,0 +1,245 @@
|
|
|
1
|
+
# PG Roast evaluation
|
|
2
|
+
|
|
3
|
+
This page explains how the evaluation from issue #189 ("Evaluation") is run
|
|
4
|
+
and scored. The scoring rules below were fixed **before any eval result
|
|
5
|
+
existed** and must not be tuned after looking at results. If a rule changes,
|
|
6
|
+
the change is recorded here with its date and reason, and every run is
|
|
7
|
+
rescored.
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
bun run src/eval/cli.ts run [--run-id ID] [--case ID ...] [--rounds N] [--repeat K] [--concurrency 2] [--dry-run] [--backend claude|codex] [--offline] [--cases FILE]
|
|
11
|
+
bun run src/eval/cli.ts score <run-id> [--no-judge] [--case ID ...] [--cases FILE]
|
|
12
|
+
bun run src/eval/cli.ts list # cases and existing runs
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Inputs
|
|
16
|
+
|
|
17
|
+
`eval/cases.json` (owned by the evalset agent) lists historical proposals
|
|
18
|
+
with a known outcome (`rejected`, `accepted` or `reverted`). For each case
|
|
19
|
+
the runner uses:
|
|
20
|
+
|
|
21
|
+
- `thread_url`;
|
|
22
|
+
- `blind_n`, the number of thread messages the roaster may read;
|
|
23
|
+
- `cutoff_date`, which limits both the messages and the precedents;
|
|
24
|
+
- the gold data: `decisive_objections[]` (who, date, message_url, quote,
|
|
25
|
+
category), `earlier_attempts[]` (thread_url, …) and
|
|
26
|
+
`post_training_cutoff`.
|
|
27
|
+
|
|
28
|
+
The gold data is never shown to the roast pipeline. It is read only by
|
|
29
|
+
`score`.
|
|
30
|
+
|
|
31
|
+
## Run
|
|
32
|
+
|
|
33
|
+
For each selected case, the runner calls the roast pipeline in-process
|
|
34
|
+
(`runRoast` in `src/roast/pipeline.ts`), as
|
|
35
|
+
|
|
36
|
+
```
|
|
37
|
+
bun run src/cli.ts <thread_url> --blind <blind_n> [--rounds N]
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
would. That means review mode, precedent enrichment on, no hint, and the
|
|
41
|
+
default models from the environment (Opus, Mythos or Fable only on the claude
|
|
42
|
+
backend).
|
|
43
|
+
|
|
44
|
+
- **Messages:** `blind_n` only, the first N messages of the flat thread.
|
|
45
|
+
`cutoff_date` is **not** applied to messages. In 9 cases the decisive
|
|
46
|
+
objection came the same day as the proposal, so a strict date filter would
|
|
47
|
+
either show nothing or show the objection.
|
|
48
|
+
- **Precedents:** the precedent cutoff (the roast's `before`) is
|
|
49
|
+
|
|
50
|
+
```
|
|
51
|
+
precedent_cutoff = min(cutoff_date, UTC date of the last shown message + 1 day)
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
So a case whose shown window is from 2018 gets no precedent from
|
|
55
|
+
2019–2022. Without this, it could receive the same feature's own later CF
|
|
56
|
+
entries under other titles, or its reverts. The cutoff is set on the
|
|
57
|
+
loaded proposal after the messages are chosen, so it filters no message.
|
|
58
|
+
The pipeline's own rules also apply: a row with any date on or after the
|
|
59
|
+
cutoff is dropped, the case's own thread and CommitFest entry are dropped,
|
|
60
|
+
and undated wiki rows are not used.
|
|
61
|
+
- **Review date:** the date shown to the model is the date of the last
|
|
62
|
+
visible message.
|
|
63
|
+
- **Where the dates are recorded:** all three dates (`case_cutoff_date`,
|
|
64
|
+
`last_shown_date`, `precedent_cutoff`) are in each case's
|
|
65
|
+
`case-meta.json` and in `report.json`, under `scores[].cutoffs`. The
|
|
66
|
+
report's case table shows the cutoff used, and both inputs when the window
|
|
67
|
+
made it earlier. Earlier-attempt check (c) judges "retrievable before
|
|
68
|
+
cutoff" against the same effective cutoff.
|
|
69
|
+
|
|
70
|
+
In the 18-case dry run (`dryrun-3`), the window made the cutoff earlier in
|
|
71
|
+
9 cases (for example `sqljson-pg15`: 2022-03-03 → 2018-03-14). No
|
|
72
|
+
retrieved precedent was dated on or after the cutoff used.
|
|
73
|
+
|
|
74
|
+
(Changed twice on 2026-11-11 on the lead's instruction, before any real
|
|
75
|
+
result:
|
|
76
|
+
1. The first version passed `cutoff_date` as `--before`, which also filtered
|
|
77
|
+
messages.
|
|
78
|
+
2. The second version used `cutoff_date` alone as the precedent cutoff, which
|
|
79
|
+
let in precedents dated after the shown window.)
|
|
80
|
+
|
|
81
|
+
- `--rounds N` passes through to the pipeline: extra reflection rounds,
|
|
82
|
+
0..6, default 0, the same as `src/cli.ts --rounds`.
|
|
83
|
+
- `--repeat K` runs every case K times (`r1..rK`) to measure run-to-run
|
|
84
|
+
variance.
|
|
85
|
+
|
|
86
|
+
Output goes to `eval/results/<run-id>/<case-id>[/r<k>]/`: `roast.json`,
|
|
87
|
+
`roast.md`, `llm/` (every prompt and reply) and `case-meta.json` (options,
|
|
88
|
+
wall time, calls, cost, error).
|
|
89
|
+
|
|
90
|
+
- **Resumable.** A case or round whose `roast.json` already exists in that
|
|
91
|
+
run is skipped. A failed case writes `error.json` and is retried on the
|
|
92
|
+
next `run` with the same `--run-id`.
|
|
93
|
+
- **Concurrency.** `--concurrency` sets how many cases run at once
|
|
94
|
+
(default 2). Each case also uses the pipeline's own LLM semaphore
|
|
95
|
+
(`PGROAST_CONCURRENCY`).
|
|
96
|
+
- `--dry-run` uses the pipeline's `DryRunBackend`, so no model is called.
|
|
97
|
+
The output then has placeholder verdicts and is useful only to check the
|
|
98
|
+
wiring.
|
|
99
|
+
- Everything under `eval/results/` is git-ignored except
|
|
100
|
+
`eval/results/*/report.md` and `report.json`.
|
|
101
|
+
|
|
102
|
+
## Scoring (fixed before any result)
|
|
103
|
+
|
|
104
|
+
### (a) Verdict match
|
|
105
|
+
|
|
106
|
+
| outcome | lenient match | strict match |
|
|
107
|
+
|---|---|---|
|
|
108
|
+
| rejected | `LIKELY-REJECT` or `NEEDS-DESIGN-DISCUSSION` | `LIKELY-REJECT` |
|
|
109
|
+
| accepted | `NO-FUNDAMENTAL-OBJECTION` or `NEEDS-COMMITTER-EYES` | `NO-FUNDAMENTAL-OBJECTION` |
|
|
110
|
+
| reverted | anything except `NO-FUNDAMENTAL-OBJECTION` | `LIKELY-REJECT` or `NEEDS-DESIGN-DISCUSSION` |
|
|
111
|
+
|
|
112
|
+
The lenient column is the headline number. Strict match is reported next
|
|
113
|
+
to it.
|
|
114
|
+
|
|
115
|
+
### (b) Decisive objection found
|
|
116
|
+
|
|
117
|
+
Each case gets one judge call, made through the roast LLM backend with the
|
|
118
|
+
critic model and no tools. The judge sees:
|
|
119
|
+
|
|
120
|
+
- the case's `decisive_objections` (who, date, quote, category, and
|
|
121
|
+
`source` when there is no `message_url`, for example a quote from a revert
|
|
122
|
+
commit message);
|
|
123
|
+
- every objection the roast produced, with its id, title, severity,
|
|
124
|
+
category, failure scenario and argument. The buckets are `listed`
|
|
125
|
+
(`objections`, the ranked steelman), `other_surviving`, `refuted`, and
|
|
126
|
+
`withdrawn` (taken back in a reflection round). Buckets are **not** shown
|
|
127
|
+
to the judge, so that a "refuted" label cannot bias it. (Clarified on
|
|
128
|
+
2026-11-11, before any result existed.)
|
|
129
|
+
|
|
130
|
+
For each decisive objection, it answers `found`, `partially` or
|
|
131
|
+
`not_found`, with the matching objection id and a one-line reason. The case
|
|
132
|
+
result is the best item: found over partially, and then the higher bucket
|
|
133
|
+
(listed > other_surviving > refuted > withdrawn).
|
|
134
|
+
|
|
135
|
+
If the judge cites an id that is not in the roast, the item counts as
|
|
136
|
+
`not_found`. Code then looks up the bucket of that id, so the
|
|
137
|
+
bucket is not the model's claim. The rules the judge is given:
|
|
138
|
+
|
|
139
|
+
- **found**: the roast states the same core reason (same mechanism or
|
|
140
|
+
concern), even in different words.
|
|
141
|
+
- **partially**: same area but a weaker or more generic version, or one of
|
|
142
|
+
several decisive reasons.
|
|
143
|
+
- **not_found**: anything else.
|
|
144
|
+
|
|
145
|
+
Headline: `found` in the `listed` or `other_surviving` bucket. Also
|
|
146
|
+
reported: `partially`, and `found` but `refuted`. The latter is a critic
|
|
147
|
+
error, not a roaster miss (docs/design.md).
|
|
148
|
+
|
|
149
|
+
For `accepted` cases with no decisive objection, (b) is "n/a".
|
|
150
|
+
|
|
151
|
+
`--no-judge` skips the judge call (b becomes `not_scored`). `score` caches
|
|
152
|
+
the judge reply in `<run>/<case>/judge.json` and does not call again; delete
|
|
153
|
+
that file to re-judge. On a dry-run roast the judge also runs on the
|
|
154
|
+
`DryRunBackend`.
|
|
155
|
+
|
|
156
|
+
### Level of the decisive objection (judge-independent label)
|
|
157
|
+
|
|
158
|
+
Before the judge runs, a separate **classify** call (critic model, no
|
|
159
|
+
tools) labels each decisive objection as `idea` (rubric levels 1a/1b),
|
|
160
|
+
`design` (level 2) or `implementation` (levels 3–5, per
|
|
161
|
+
`prompts/committer-paranoia.md`). This call sees only the case's title and
|
|
162
|
+
decisive objections, **never the roast**.
|
|
163
|
+
|
|
164
|
+
The case label is the most fundamental level among its decisive objections.
|
|
165
|
+
Every headline number is also reported per label. An implementation-level
|
|
166
|
+
revert reason, which an idea-level tool reading 6 early emails could not
|
|
167
|
+
have seen, shows up as such instead of being hidden in an average. The
|
|
168
|
+
reply is cached in `classify.json`.
|
|
169
|
+
|
|
170
|
+
**Window flag:** if `decisive_objection_position <= blind_n`, the decisive
|
|
171
|
+
message was inside the shown window (currently only
|
|
172
|
+
`data-checksums-default-on`: position 5, 6 messages shown). The case is
|
|
173
|
+
marked ⚑, because a "found" there may be reading, not foresight.
|
|
174
|
+
|
|
175
|
+
(Added on 2026-11-11 on the lead's instruction, before any real result.)
|
|
176
|
+
|
|
177
|
+
### (c) Earlier attempt found (deterministic)
|
|
178
|
+
|
|
179
|
+
- If the case has no `earlier_attempts`: **n/a**.
|
|
180
|
+
- Otherwise, every message-id is normalised: take the part after
|
|
181
|
+
`/message-id/` (dropping `flat/`, `raw/` and so on) or after
|
|
182
|
+
`postgr.es/m/`, percent-decode it, strip `<>`, and compare
|
|
183
|
+
case-sensitively. The same canonical form is used as in the corpus
|
|
184
|
+
(`canonMsgid`).
|
|
185
|
+
- **found**: an earlier-attempt thread is among the roast's
|
|
186
|
+
`prior_attempts[].thread_urls`. **retrieved_only**: it is only in
|
|
187
|
+
`precedents_retrieved[].thread_urls`, meaning retrieval found it but the
|
|
188
|
+
roaster did not list it.
|
|
189
|
+
- **not in corpus**: none of the case's earlier-attempt threads exists in
|
|
190
|
+
the `precedent` table at all (any `thread_urls` element). This is a corpus
|
|
191
|
+
gap, not a roaster miss. It is excluded from the denominator and counted
|
|
192
|
+
separately.
|
|
193
|
+
- **not_found**: otherwise.
|
|
194
|
+
- It is also reported whether the earlier attempt was retrievable under the
|
|
195
|
+
case's cutoff: its corpus row is dated before `cutoff_date` (the
|
|
196
|
+
`--before` rule in docs/corpus.md).
|
|
197
|
+
|
|
198
|
+
Headline: found / (cases with earlier attempts that are in the corpus).
|
|
199
|
+
|
|
200
|
+
### (d) False alarms on accepted cases
|
|
201
|
+
|
|
202
|
+
For each `accepted` case, the report shows the verdict and the number of
|
|
203
|
+
**surviving fatal objections**: `objections` plus `other_surviving` with
|
|
204
|
+
`severity = "fatal"`. A false alarm is a verdict of `LIKELY-REJECT` or
|
|
205
|
+
`NEEDS-DESIGN-DISCUSSION`. Headline: false-alarm rate = false alarms /
|
|
206
|
+
accepted cases.
|
|
207
|
+
|
|
208
|
+
## Report
|
|
209
|
+
|
|
210
|
+
`score` writes `eval/results/<run-id>/report.md` and `report.json`:
|
|
211
|
+
|
|
212
|
+
- a summary table, one row per case: outcome, verdict, lenient and strict
|
|
213
|
+
match, decisive objection result and bucket, earlier attempt result,
|
|
214
|
+
surviving fatal objections, LLM calls, cost, and wall time;
|
|
215
|
+
- four headline numbers: (a) lenient verdict accuracy (strict next to it),
|
|
216
|
+
(b) decisive objection found rate, (c) earlier attempt found rate, and
|
|
217
|
+
(d) false-alarm rate;
|
|
218
|
+
- the same four numbers split three ways:
|
|
219
|
+
- by `post_training_cutoff` true and false. The models may remember how
|
|
220
|
+
famous old threads ended, and this split shows how much that matters
|
|
221
|
+
(docs/design.md, "Known weaknesses");
|
|
222
|
+
- by outcome;
|
|
223
|
+
- by the decisive objection's level (idea, design or implementation);
|
|
224
|
+
- the misses: for each case that failed (a), (b) or (c), what was missed,
|
|
225
|
+
with the gold quote and link;
|
|
226
|
+
- with `--repeat K > 1`, each repeat is scored on its own, and per-case
|
|
227
|
+
verdict agreement across repeats is shown.
|
|
228
|
+
|
|
229
|
+
`docs/eval-report.md` is updated only on request.
|
|
230
|
+
|
|
231
|
+
## Cost of a full run
|
|
232
|
+
|
|
233
|
+
For the 18 cases at `--rounds 0`:
|
|
234
|
+
|
|
235
|
+
| stage | calls per case |
|
|
236
|
+
|---|---|
|
|
237
|
+
| roast: brief 1 + relevance 1 + 7 roasters + critic 1 + synthesis 1 | 11 |
|
|
238
|
+
| enrichment of precedent threads | at most 8; cached in `precedent_enrichment` across runs, so a re-run makes fewer |
|
|
239
|
+
| repair calls (invalid JSON) | at most 1 per stage, usually 0 |
|
|
240
|
+
| scoring: classify 1 + judge 1 | 2 |
|
|
241
|
+
|
|
242
|
+
That is about 13 to 21 calls per case, or **about 234 to 378 calls** for the
|
|
243
|
+
whole run. Each `--rounds` step adds more roaster and critic calls per case.
|
|
244
|
+
The dry run (`dryrun-2`, 18 cases, stub model) made 11 roast calls per case;
|
|
245
|
+
enrichment does not run in dry-run mode.
|
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
# Study notes: Robert Haas, "Committer review: an exercise in paranoia"
|
|
2
|
+
|
|
3
|
+
- Talk: PGConf.dev 2025, 50:29, published 2025-06-17. Video: https://youtu.be/3M8E_z1y_J0
|
|
4
|
+
- Source for these notes: the YouTube English subtitles (`v.en.vtt`), converted to plain text. The slides were not available, so anything that was only on a slide (the pyramid picture, the sample C function, the two photos) is known here only through what he said about it.
|
|
5
|
+
- Timestamps `[mm:ss]` are approximate (the transcript has one marker per minute; points inside a minute are estimated, so expect an error of up to about 30 seconds).
|
|
6
|
+
- Quotation marks are used only for wording that is in the transcript (lightly cleaned: fillers removed, caption typos fixed). Everything else is paraphrase.
|
|
7
|
+
- Sections 1-8 are "what he said". Section 9 is my own inference and is marked as such.
|
|
8
|
+
|
|
9
|
+
## 1. Structure of the talk
|
|
10
|
+
|
|
11
|
+
| Time | Part |
|
|
12
|
+
|---|---|
|
|
13
|
+
| 00:16 | Introduction; origin of the talk (extemporaneous remarks at the Advanced Patch Feedback Session, PGConf.dev 2024) |
|
|
14
|
+
| 01:18 | Agenda: part 1 "committer mindset", part 2 "actually reviewing patches" |
|
|
15
|
+
| 02:20-13:30 | Part 1: why committers are reluctant (responsibility, ease of mistakes, stress) |
|
|
16
|
+
| 13:34-14:35 | Part 2 opens: he will not give a full checklist, he gives four general principles |
|
|
17
|
+
| 14:35-16:40 | The four principles in brief |
|
|
18
|
+
| 16:40-25:10 | Principle 1: start with the big picture (the "patch review hierarchy of needs" pyramid) |
|
|
19
|
+
| 25:10-35:30 | Principle 2: consistency is critical (incl. the sample-code slide, 28:48-35:30) |
|
|
20
|
+
| 35:30-42:40 | Principle 3: be a pessimist |
|
|
21
|
+
| 42:40-44:50 | Principle 4: apply a spirit of maintainership |
|
|
22
|
+
| 45:06-50:29 | Q&A: CI vs buildfarm, when to relax paranoia, paranoia across the release cycle, GenAI for review |
|
|
23
|
+
|
|
24
|
+
He states the limits of his claims up front: "not all committers have the same mind ... I'll tell you what I believe people are generally thinking" [01:25]. He also says getting patches committed is hard for many reasons, "the overwhelming majority of which I cannot address in this talk" [02:10]; the mindset is only one of them.
|
|
25
|
+
|
|
26
|
+
## 2. Part 1: the committer mindset (why caution, even with good patches)
|
|
27
|
+
|
|
28
|
+
### 2.1 The committer, not the author, owns what goes wrong
|
|
29
|
+
|
|
30
|
+
- The perceived reluctance "basically comes from the fact that if you commit somebody's patch you - not they - are responsible for whatever goes wrong. There are no excuses and there is no statute of limitations." [02:45]
|
|
31
|
+
- Immediate duty: "If you break the build farm, you must fix it immediately." Night, weekend, planned holiday, natural disaster: none of it matters. "The build farm is a shared resource and you can't leave it broken." [03:00-03:25]
|
|
32
|
+
- Long-term duty: "Even many years later you may be called upon to defend the design, to fix bugs, or to maintain the code." [03:35]
|
|
33
|
+
- "Whenever you commit a patch you're accepting a certain amount of open-ended liability for the things that may go wrong in the future"; he compares it to writing blank checks, and calls it "a little bit of a miracle that we manage to commit a few thousand of them every year." [05:40-06:10]
|
|
34
|
+
|
|
35
|
+
### 2.2 War story: JIT and LLVM [03:45-05:40]
|
|
36
|
+
|
|
37
|
+
- Andres told him that when JIT was committed, the LLVM interfaces Postgres depended on "had been pretty stable for a while. Subsequent to that they have not been so stable."
|
|
38
|
+
- Most committers do not know those interfaces, so when something must be updated for a new LLVM version, "we're going to ask Andres, of course."
|
|
39
|
+
- Good news, and "this actually happens pretty commonly": someone else jumps in. Thomas Munro did a lot of the updating for newer LLVM versions.
|
|
40
|
+
- But the committer cannot know in advance: the external project "could change nothing or they could change all kinds of things"; "six other committers could show up and volunteer to help clean up the mess or everybody could be like 'Heh, your job.'"
|
|
41
|
+
- Lesson he draws: a dependency on an external moving interface is an open-ended liability taken by one person.
|
|
42
|
+
|
|
43
|
+
### 2.3 Authors usually do not help with the aftermath [06:10-07:25]
|
|
44
|
+
|
|
45
|
+
- "You tend not to get much help at all from the patch author." Fair reasons: time zones; the author did not know when the commit would happen.
|
|
46
|
+
- He has asked authors in advance to be available, and some agreed. Even in the best case, the discussion and the writing of follow-up fixes "tends to get left to the committer and the author stays out of it."
|
|
47
|
+
- "That naturally produces a certain amount of caution on the part of committers."
|
|
48
|
+
|
|
49
|
+
### 2.4 Mistakes are easy; everybody makes them [07:28-10:30]
|
|
50
|
+
|
|
51
|
+
- "We have an incredible number of extremely talented people working on PostgreSQL and all of them regularly introduce serious bugs. I also regularly introduce serious bugs when I commit things." [07:40]
|
|
52
|
+
- "Data corruption bugs and server crashes make it into the source tree quite often." Some are not caught before release; those caught quickly still make work; smaller errors are more common. [08:00]
|
|
53
|
+
- War story 1, his first known data-corrupting bug [08:30-09:30]: reported by Jeff Davis ("my heart just sank"). It reached release and shipped "I think for over a year". It was "basically a 1-line omission": one more function needed an update than the number of functions he found. Jeff added asserts and comments, but the core fix was one line. "I don't know and will never know how many people suffered data loss as a result of that mistake on my part but it probably isn't zero." (He does not name the commit or the feature in the spoken text; it was on the slide.)
|
|
54
|
+
- War story 2, pg_overexplain [09:31-10:30]: a new contrib module, two reviewers, "one of whom was Tom Lane". The buildfarm broke anyway; three commits that same evening to fix it; next morning more "silly things" found by people who read the commit. One intended commit became six, and "numbers 2, 3, 4, 5, and 6 are all public documentation of how foolish I was in commit number one."
|
|
55
|
+
|
|
56
|
+
### 2.5 Stress, embarrassment, fear [10:32-13:30]
|
|
57
|
+
|
|
58
|
+
- Being a committer is a privilege, but non-committers imagine a satisfied person typing `git push`; in his experience and that of committers he spoke with, it is the opposite. "Committing patches is stressful." [11:25]
|
|
59
|
+
- Difference among committers: he finds it "somewhat stressful"; a few others "described levels of stress that far exceeded what I was describing and that seemed quite extreme." [11:35]
|
|
60
|
+
- Result: "painstaking and exhaustive verification of a patch set to the best of their ability before they push it - and then things still go wrong and they have to scramble." [11:50]
|
|
61
|
+
- Evidence he offers: "there are some great patches written by committers that sit on the mailing list for years without being committed", because the committer is "afraid of what will go wrong". Sometimes another committer who needs the fix later picks it up: now that person "only [has] to be responsible for the aftermath and not also writing the patch." Everybody wins "except perhaps for the users who had to wait a couple of extra years". [12:05-12:55]
|
|
62
|
+
- Public embarrassment "weighs rather heavily on the minds of a lot of people"; "in extreme cases people might even be afraid that they're actually going to lose their commit privileges." [13:00-13:30]
|
|
63
|
+
|
|
64
|
+
## 3. Part 2: the four principles in brief [14:35-16:40]
|
|
65
|
+
|
|
66
|
+
He declines to give a full checklist: there are too many things to check, it is "quite difficult to remember all the things that you're supposed to check for every single patch", and other talks cover it [13:50-14:30].
|
|
67
|
+
|
|
68
|
+
1. Start with the big picture. "If the design of the patch is incorrect in some fundamental way then it really doesn't matter whether the patch contains typos." [14:40]
|
|
69
|
+
2. Consistency is critical. New things are fine, "but the departures from existing practice need to be well justified ... it should be more than your personal preference." [15:00]
|
|
70
|
+
3. Be a pessimist. Every thread has at least one person who thinks the patch is good: the author. "Being a good reviewer requires questioning the assumption that the author got the patch right." [15:20-16:00]
|
|
71
|
+
4. Apply a spirit of maintainership. It is easy to make "getting that patch committed, ideally in something close to its present form, ideally as soon as possible, your overriding goal". But the project goes on; the question is how the patch affects the project overall, "not just about whether it is good in isolation." [16:00-16:50]
|
|
72
|
+
|
|
73
|
+
## 4. Principle 1: start with the big picture (the pyramid) [16:40-25:10]
|
|
74
|
+
|
|
75
|
+
He calls it "a sort of patch review hierarchy of needs". All levels must be right; lower levels are more foundational. Bottom to top, as he describes it:
|
|
76
|
+
|
|
77
|
+
### Level 1 (base): understandability and desirability [16:55-17:55]
|
|
78
|
+
|
|
79
|
+
- Understandability: "Is the idea that the author is proposing clear enough that we can actually evaluate it in some meaningful way?"
|
|
80
|
+
- Desirability: "Is it actually a good idea?"
|
|
81
|
+
- "There is very little point in doing detailed review, let alone fixing typos, if we don't have any idea what the patch is attempting to accomplish or we don't want that."
|
|
82
|
+
|
|
83
|
+
### Level 2: architecture and design [17:55-19:55]
|
|
84
|
+
|
|
85
|
+
"If we agree on the goal then do we agree on the way that the patch goes about that goal, at least in broad strokes?" He lists:
|
|
86
|
+
|
|
87
|
+
- Safety: "does the patch violate some invariants in the code which are going to result in it doing bad things like crashing the system or eating your data?" [18:15]
|
|
88
|
+
- Performance: is the design conducive to good performance; and even for a non-performance patch, "whether a design has been chosen which is likely to regress the performance of other things." [18:30-18:45]
|
|
89
|
+
- Maintainability: "Are we going to be able to maintain this code? Is it going to need to be constantly updated? Are there going to be updates that will need to be made to this code by future patches that someone will be likely to miss when they are reviewing those patches?" [18:45-19:05]
|
|
90
|
+
- User interface: new or modified SQL syntax, "or even just a function that could be called"; psql features too. [19:05-19:40]
|
|
91
|
+
- API: things that are strictly internal still have "maybe not a UI but an API ... Is it going to be something that is usable?" [19:40-19:55]
|
|
92
|
+
|
|
93
|
+
### Level 3: patch set structure, comments, code structure [19:55-22:40]
|
|
94
|
+
|
|
95
|
+
- Other committers may differ: "I suspect that some people would quibble with the way that I've divided things between the yellow and orange layers" [20:00].
|
|
96
|
+
- Patch set structure: split logically, each patch a separate change that could be applied separately, in the submitted order. A split by directory is "not useful at all". Infrastructure first, dependants after. [20:20-21:05]
|
|
97
|
+
- Commit messages and code comments: often missing, he thinks often because of low confidence in English. "I would much rather have some comments in the best English that you can manage than no comments at all"; rewriting comments is much less work than writing them after working out the code by inspection. [21:05-21:55]
|
|
98
|
+
- Code structure: sensible subroutines; code in the right directory and the right part of the codebase. [22:00-22:25]
|
|
99
|
+
- "I often do some manual verification at this stage as well." [22:30]
|
|
100
|
+
|
|
101
|
+
### Level 4: detailed review [22:40-23:20]
|
|
102
|
+
|
|
103
|
+
- Line-by-line review of the whole patch.
|
|
104
|
+
- Documentation, tests, tests passing, CI passing. He does not care much about docs and tests in the initial review, "unless of course the lack of documentation leads to the patch not being understandable".
|
|
105
|
+
- Difference among reviewers: "we have a few people who are extremely good at grasping the meaning of a patch set that isn't documented but I am not one of those people and I really like to be told what you thought you were trying to accomplish." [22:55-23:15]
|
|
106
|
+
|
|
107
|
+
### Level 5 (top): final polishing [23:20-23:40]
|
|
108
|
+
|
|
109
|
+
Coding style, pgindent, typos, grammar of comments. Then the patch can be committed.
|
|
110
|
+
|
|
111
|
+
### Remarks on using the pyramid [23:40-25:10]
|
|
112
|
+
|
|
113
|
+
- "Too many people start at the top and they never get to the lower levels of the pyramid and that's really a shame, because actually even if you're not a coder at all you might be able to comment on the UI, for example, and if you're a user you could comment on desirability." [23:40-24:10]
|
|
114
|
+
- Bottom-to-top is "an aspirational goal". On a complex patch he may comment on small things and "totally miss the design problems", which he finds on the second or third read. [24:15-24:45]
|
|
115
|
+
- Sometimes he writes a long email on small things, then three quarters through sees "some killer problem" that makes the text useless, and throws it away. [24:46-25:05]
|
|
116
|
+
- He asks for better comments and commit messages early, only so that he can understand the patch. [25:05]
|
|
117
|
+
|
|
118
|
+
## 5. Principle 2: consistency [25:10-35:30]
|
|
119
|
+
|
|
120
|
+
He calls it "a real bedrock principle of reviewing PostgreSQL patches". It "can feel like nitpicking" [25:46]. His reasons:
|
|
121
|
+
|
|
122
|
+
1. It refutes "political objections": if someone dislikes something and you can answer "because the other 10 cases that we already have in the source tree are like that", then "it isn't a problem you've created and it's not your patch's fault." [26:00-26:30]
|
|
123
|
+
2. It avoids bugs: adapting existing code makes it less likely to miss something crucial. [26:35-26:50]
|
|
124
|
+
3. Readability and maintainability: code that looks like other Postgres code is easier to understand. He marks this as "actually really important". [26:50-27:10]
|
|
125
|
+
4. User experience: inconsistency can "creep out into the user experience". Example, which he calls silly: if each new DDL object type picked its own verb, tables would be DROPped and functions REMOVEd, "a hodge-podge". [27:10-28:00]
|
|
126
|
+
|
|
127
|
+
Scope: both high level ("is the architecture consistent with what we've done elsewhere?") and low level (is the code written the way similar code is written). [28:00-28:20]
|
|
128
|
+
|
|
129
|
+
"Coding micro-practices": "numerous little things that we almost always do in the same way and which you're encouraged not to deviate from", unless there is a compelling reason. He says this is one thing that makes it hard to become a skilled Postgres hacker. [28:20-28:48]
|
|
130
|
+
|
|
131
|
+
### The sample-code slide [28:48-35:30]
|
|
132
|
+
|
|
133
|
+
A short C function (on the slide; not in the transcript) that is pgindent-clean, compiles without warnings, "and if you're really lucky it might even work". Problems he lists:
|
|
134
|
+
|
|
135
|
+
- `extern` on a function definition (convention: only on the declaration). [29:35]
|
|
136
|
+
- Variables not declared in the innermost scope. [29:50]
|
|
137
|
+
- Names: a `HeapTuple` named `h` (convention: `tuple`, `tup`, `htup`, `dbtup`); a `Form_pg_database` named `dbstruct` (convention: something with "form"). [30:00-30:25]
|
|
138
|
+
- `palloc0` where every byte is overwritten: wasted overhead. [30:30]
|
|
139
|
+
- `++i` vs `i++`: "A committer might or might not call you on" it; the tree has `i++` about nine times as often. (Explicit committer-to-committer variation.) [30:40-31:00]
|
|
140
|
+
- Calling `SearchSysCache` directly, not the `SearchSysCache1` macro. [31:00]
|
|
141
|
+
- First serious bug: missing `ObjectIdGetDatum` around `oids[i]`; he says it works on one endianness and fails on the other, so the author can test and not see it, but "the build farm will tell the committer". [31:15-31:45]
|
|
142
|
+
- `h == NULL` in place of `HeapTupleIsValid`. [31:50]
|
|
143
|
+
- Non-standard error message for a missing catalog tuple. [31:55]
|
|
144
|
+
- Second serious bug: `ReleaseSysCache` invalidates a pointer just stored in the result array; it may seem to work because caches are not clobbered by default; "probably every committer has made [this one] multiple times". [32:00-32:50]
|
|
145
|
+
|
|
146
|
+
Points he draws from it:
|
|
147
|
+
|
|
148
|
+
- "Any reviewer with some knowledge of C can find every one of these problems": grep for similar examples and compare carefully. [33:00-33:25]
|
|
149
|
+
- "There is no way that we are going to end up with 500 messages that say cache lookup failed for database %u and one message that has this alternative error text." [33:35-33:55]
|
|
150
|
+
- Committers spend "quite a lot of time" on this even after they trust the overall structure. He packed "more than a dozen errors" into one short function; "just imagine ... a 4,000 line patch". [33:55-34:35]
|
|
151
|
+
- An audience member (not captioned) challenged one point, seemingly the Datum/Oid one. He answered "We do have code like this actually but anyway let's take that one offline", then defended the claim: the lookup key is a Datum, not an Oid; "It might work on your machine, it won't work on all of them." [34:35-35:30] (So this single item was disputed in the room; the transcript does not show how it ended.)
|
|
152
|
+
|
|
153
|
+
## 6. Principle 3: be a pessimist [35:30-42:40]
|
|
154
|
+
|
|
155
|
+
- He jokes that people may be "dismayed to hear Robert Haas calling for more pessimism". [35:35]
|
|
156
|
+
- His own review failures usually do not come from missing knowledge. When knowledge is the risk, "I realize I'm not qualified to review this patch and I stop and I wait for somebody else who is qualified and will be able to fix whatever breaks", or he takes time to learn the area "well enough that I feel like I can be responsible for it". [36:00-36:45]
|
|
157
|
+
- "A mistake that I make all the time is thinking that a certain kind of error probably won't be present and therefore I don't need to check for it exhaustively." [36:45]
|
|
158
|
+
- Example: assuming the author can implement a singly linked list. People submit "lots" of broken ones: "linked lists, hash tables, binary searches, all sorts of fundamental algorithms people do them wrong." [36:56-37:30]
|
|
159
|
+
- "Authors miss all kinds of things: they miss design flaws, they don't check all the different parts of the code, they miss regressions". "Extreme pessimism in my experience is still often not enough pessimism." [37:30-37:55]
|
|
160
|
+
|
|
161
|
+
Techniques:
|
|
162
|
+
|
|
163
|
+
- `git grep` a lot. If the patch changes something related to identifier XYZ, grep it and look for places not adjusted. War story: his first committed patch, the grantable TRUNCATE privilege; he found `ACL_DELETE`, added `ACL_TRUNCATE`, and visited every use of `ACL_DELETE`. The same works in review: find the existing analogue of what is added, grep for it. [37:57-39:05]
|
|
164
|
+
- Think about pairings: "What operations logically go together?" Allocate/deallocate, create file/remove file, set global/clear global. Sometimes a built-in mechanism (memory contexts) does it. The question: does the code you see imply other code that must exist, and does that code exist? [39:05-39:58]
|
|
165
|
+
- System-wide consequences: this "really benefits from having more knowledge of the system, so it is I think a key role of committers to notice when some part of the code that might be quite far removed needs to be modified as a logical consequence". Example: `CREATE STRAWBERRY` implies `ALTER`/`DROP`/`RENAME TO`/`OWNER TO`, and also pg_dump ("they need to be dumped and restored just like all of the other objects") and maybe a psql command. [39:58-40:59]
|
|
166
|
+
- Functional regressions: "If this patch did have a bug someplace where might it be", then poke there. [41:00]
|
|
167
|
+
- Performance regressions: "if there were something that this patch slowed down what might it be and under what circumstances", then benchmark to confirm or rule out. [41:10-41:30]
|
|
168
|
+
- "Pessimism about your pessimism": it is easy to guess where the bug is, test, find nothing and say all is well, when "the problem was actually in your test". He recommends temporary debugging code (his preference), a debugger or a profiler, to prove the new code is hit. "You got to make sure your test or your benchmark is doing what you think it's doing otherwise the results are not valid." People are "very, very often too optimistic": they do not find the bad cases, or do not hit them. [41:30-42:40]
|
|
169
|
+
|
|
170
|
+
## 7. Principle 4: spirit of maintainership [42:40-44:50]
|
|
171
|
+
|
|
172
|
+
- "We need to do what's good for all of us, not what's good for somebody who has a really serious problem that they need to solve that day." [43:00]
|
|
173
|
+
- The accumulation argument: there are often arguments that a patch "is worth a 3% performance regression or worth a user interface wart or worth an annual code maintenance task, and sometimes those people are 100% correct because some patches are worth those things, but if we accepted 10 patches like that every release, then every release we'd have a 30% performance regression, 10 user interface warts, and 10 annual code maintenance tasks". [43:10-43:45]
|
|
174
|
+
- "So we have to be super judicious about incurring those costs and sometimes the answer is just time. We wait and see, we gather more information", to learn if the change is important enough to enough people. [43:45-44:05]
|
|
175
|
+
- Respect: "the patch author always has good reasons to believe that their patch is beneficial to the project. We shouldn't just tell people: go away, your idea sucks." [44:05-44:20]
|
|
176
|
+
- The asymmetry to watch: be cautious "especially if the downsides are going to be borne by everyone, and the upside is something that's only going to benefit the people who use the feature." [44:20-44:40]
|
|
177
|
+
|
|
178
|
+
## 8. Q&A [45:06-50:29]
|
|
179
|
+
|
|
180
|
+
- Can the buildfarm-type checks run before a committer spends time? [45:06-46:20] Progress exists through CI (Andres and others). "We do not have parity between CI and the build farm" (caption says "party"), so the buildfarm still finds more. A priority should be to let CI catch more; it "would lower everybody's blood pressure".
|
|
181
|
+
- Noah (surname not given): paranoia looks like a ratchet that only goes up; when do you relax? [46:20-48:10] Answer: look at "our actual error rate after the paranoia ... If nothing ever goes wrong and we just spend a bunch of time being paranoid then apparently we're too paranoid." The evidence says otherwise: he went through the commits from February and "about 20% of them were post-commit fixups", and "I think another 15% or so were bug fixes from commits longer ago". The direction should be to catch mistakes earlier with less manual load on the committer, not to be less paranoid; "until we have better tools, unless we see the error rate start to decline ... we're going to have to keep up with the paranoia."
|
|
182
|
+
- How does paranoia change over the release cycle? [48:11-49:12] "What should happen is the paranoia should be highest at the end; what actually happens is it declines at the end because people are desperate to get their patches in and then what sometimes happens is a flurry of reverts". Feature freeze (caption: "feature phrase") limits the damage because it removes human judgment after that point. That it is needed so badly "shows that people's spirit of maintainership is not always quite at the level that perhaps it should be."
|
|
183
|
+
- Can GenAI help committers review before CI? [49:12-50:20] "I definitely would not rule out the possibility that it will get there but right now I would say definitely not ... we all know that AIs hallucinate quite a lot and you can see from examples like this how even a tiny amount of hallucination gives you an uncommittable patch, so that doesn't feel like we've got the necessary level of reliability there yet." (Said in June 2025. His reason is worded about AI output in a patch; the question was about AI review.)
|
|
184
|
+
|
|
185
|
+
## 8a. Where he says committers differ (collected)
|
|
186
|
+
|
|
187
|
+
- Mindset is his belief about what committers "generally" think, not a common position [01:25].
|
|
188
|
+
- Stress level: his is moderate, some others described much more [11:35].
|
|
189
|
+
- Where the line is between "structure/comments" and "detailed review" in the pyramid [20:00].
|
|
190
|
+
- Some reviewers can read an undocumented patch set; he cannot and wants to be told the intent [22:55].
|
|
191
|
+
- `++i` vs `i++`: a committer "might or might not" object [30:40].
|
|
192
|
+
- One sample-code claim was disputed from the audience [34:35].
|
|
193
|
+
- End-of-cycle behaviour: people become less careful than they should [48:20].
|
|
194
|
+
|
|
195
|
+
## 9. My inferences (not said in the talk)
|
|
196
|
+
|
|
197
|
+
These are my reading of what the talk means for an idea-level reviewer such as PG Roast. He did not say them.
|
|
198
|
+
|
|
199
|
+
- Only a part of the talk is about ideas. Levels 1-2 of the pyramid and principle 4 are idea/design level. Principle 2 applies at the design level too (architecture and UI consistent with what exists). Most of principle 3 and the whole sample-code part need the code. An idea-level tool can borrow the questions "what else must exist as a consequence" (the STRAWBERRY list) and "if this were slow or broken, where", but cannot verify the answers.
|
|
200
|
+
- "Rejected" and "not committed" are different things in his account. Much of the reluctance comes from the cost to one person (liability, stress), not from a defect in the proposal: good patches by committers themselves wait for years. A tool that predicts "will not get in" should say which of the two it means.
|
|
201
|
+
- A proposal with a large post-commit liability and no credible owner for the aftermath (author who disappears, external dependency that moves, an area few committers know) is harder to get committed, by his logic in 2.1-2.3. He gives the facts; the predictive rule is mine.
|
|
202
|
+
- His accumulation argument (3%, a wart, an annual task) suggests an objection of the form "this cost is paid by everyone, the benefit goes to the feature's users" is the strongest idea-level objection, and that a cost confined to the feature's users is a weak one. He states the asymmetry; the ranking is mine.
|
|
203
|
+
- "Sometimes the answer is just time" suggests a verdict between accept and reject: not now, more evidence of demand needed.
|
|
204
|
+
- "Pessimism about your pessimism" was said about tests and benchmarks. Applied to a reviewer tool: an objection is worth nothing until the failure scenario is shown to be reachable in this proposal.
|
|
205
|
+
- His GenAI answer and his "500 messages" remark together imply that precision matters more than volume: one invented objection makes a review as unusable as one invented line makes a patch.
|
|
206
|
+
|
|
207
|
+
## 10. The pyramid versus the "four levels" wording in the PG Roast issue (my comparison)
|
|
208
|
+
|
|
209
|
+
The issue describes a "hierarchy of committer needs" with four levels: (1) is the problem/idea properly stated, (2) is it actually a good idea, will we regret it forever, (3) is the design right (touch points, compatibility, standards), (4) only then, is the implementation right. What the talk says:
|
|
210
|
+
|
|
211
|
+
- Name: "a sort of patch review hierarchy of needs" [16:40]. He does not say "committer needs".
|
|
212
|
+
- Five levels, not four (section 4). The level names in the spoken text are "understandability" and "desirability" (base), "architecture and design" (which he calls "the second level of the pyramid" [17:55]), then patch set structure / comments / code structure, then detailed review, then "final polishing". He refers to two of the layers by slide colour ("yellow and orange" [20:00]); the slide itself was not available.
|
|
213
|
+
- The issue's levels 1 and 2 are one level in the talk, with two questions asked in that order ("the absolutely most fundamental question ... is understandability ... the second is similar: desirability" [16:55-17:40]).
|
|
214
|
+
- "Will we regret it forever" is not in his words for desirability. The nearest material is in part 1 ("no statute of limitations" [02:45], "defend the design" years later [03:35]) and in principle 4 [43:10].
|
|
215
|
+
- The issue's level 3 examples (touch points, compatibility, standards) are not his. His list for architecture and design is safety, performance, maintainability, user interface, API [18:15-19:55]. He does not mention compatibility or the SQL standard anywhere in the talk.
|
|
216
|
+
- The issue's level 4 ("implementation") covers his levels 3, 4 and 5.
|
|
217
|
+
- "Stop at the first failing level": he says there is "very little point" in detailed review when the base fails [17:40] and that detailed review makes sense once levels 1-2 are sorted [19:55]. He does not state a hard stop rule, and he calls bottom-to-top order "an aspirational goal" that he cannot always follow, because design problems often show only on a second or third read of the code [24:15-25:05].
|
|
218
|
+
|
|
219
|
+
## 11. "Will not get committed" versus "should be rejected" (my inference, with the talk's basis)
|
|
220
|
+
|
|
221
|
+
What he said: the reluctance to commit comes from the committer's responsibility for the aftermath [02:45], from open-ended liability [05:40], from little author help [06:10] and from stress [11:25]. His evidence is that "great patches written by committers" wait "for years" because the committer is "afraid of what will go wrong" [12:05]; a later committer may take one and be "responsible for the aftermath" only [12:40]. A committer who is not qualified in an area waits for one who is [36:10]. "Sometimes the answer is just time" [43:45].
|
|
222
|
+
|
|
223
|
+
What I infer for PG Roast: a patch can stay uncommitted with no defect in the idea. So the output should carry the verdict on the idea and design (levels 1-2) apart from a "committability" field (who would own the aftermath, what liability that person signs for, whether the author will stay), and it should have a visible "not now, show demand" outcome that is not counted as a rejection. He did not propose any of this; the fields are in `prompts/committer-paranoia.md`.
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Subsystem invariants
|
|
2
|
+
|
|
3
|
+
Rules that senior reviewers apply and that a model does not reliably infer by itself. Each file covers one subsystem. The roasters receive the files whose `paths:` patterns match the files a patch touches.
|
|
4
|
+
|
|
5
|
+
Every entry must link to the thread, commit or review that taught it. Do not add an invariant from memory or from a model's suggestion without such a source.
|
|
6
|
+
|
|
7
|
+
Entry format:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
## <short rule name>
|
|
11
|
+
- Rule: <the invariant, one or two sentences>
|
|
12
|
+
- Failure when broken: <what goes wrong, concretely>
|
|
13
|
+
- How a test hides it: <optional: the test shape that masks the problem>
|
|
14
|
+
- Source: <link> (<who>, <date>)
|
|
15
|
+
```
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
---
|
|
2
|
+
subsystem: nbtree
|
|
3
|
+
paths:
|
|
4
|
+
- src/backend/access/nbtree/
|
|
5
|
+
- src/include/access/nbtree.h
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# nbtree invariants
|
|
9
|
+
|
|
10
|
+
## VACUUM-time changes that need a later pass must tell VACUUM so
|
|
11
|
+
- Rule: if code that runs during VACUUM leaves state that only a future VACUUM pass can reclaim (deleted pages, merged-away tombstones), it must record that in the bookkeeping that decides whether the index is visited again (for nbtree, the metapage's deleted-page count, `btm_last_cleanup_num_delpages`).
|
|
12
|
+
- Failure when broken: autovacuum, and `VACUUM` with `INDEX_CLEANUP auto` or `off`, can skip the index. The deleted pages are never reclaimed and the bloat comes back.
|
|
13
|
+
- How a test hides it: a test that runs plain manual `VACUUM` always cleans the index in the same pass, so it never exercises the "skip the index" decision. Test with autovacuum only and with `INDEX_CLEANUP auto` / `off`.
|
|
14
|
+
- Source: https://github.com/NikolayS/postgres/issues/101#issuecomment-6804288875 (review feedback from Oleg on the nbtree page-merge patch, reported by Kirk Wolak, 2026-11-11)
|
package/package.json
CHANGED
|
@@ -1,6 +1,53 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "pgroast",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"
|
|
5
|
-
"
|
|
6
|
-
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Adversarial idea and design pre-review for Postgres proposals: prior attempts, the strongest objections, touch points. Local output only.",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"repository": {
|
|
7
|
+
"type": "git",
|
|
8
|
+
"url": "git+https://github.com/NikolayS/pgroast.git"
|
|
9
|
+
},
|
|
10
|
+
"homepage": "https://github.com/NikolayS/pgroast#readme",
|
|
11
|
+
"bugs": {
|
|
12
|
+
"url": "https://github.com/NikolayS/pgroast/issues"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"postgres",
|
|
16
|
+
"postgresql",
|
|
17
|
+
"pgsql-hackers",
|
|
18
|
+
"commitfest",
|
|
19
|
+
"review"
|
|
20
|
+
],
|
|
21
|
+
"type": "module",
|
|
22
|
+
"bin": {
|
|
23
|
+
"pgroast": "bin/pgroast.mjs"
|
|
24
|
+
},
|
|
25
|
+
"files": [
|
|
26
|
+
"bin/",
|
|
27
|
+
"src/",
|
|
28
|
+
"prompts/",
|
|
29
|
+
"invariants/",
|
|
30
|
+
"sql/",
|
|
31
|
+
"data/precedents.sql.gz",
|
|
32
|
+
"docs/",
|
|
33
|
+
"README.md",
|
|
34
|
+
"LICENSE"
|
|
35
|
+
],
|
|
36
|
+
"engines": {
|
|
37
|
+
"bun": ">=1.3.5",
|
|
38
|
+
"node": ">=18"
|
|
39
|
+
},
|
|
40
|
+
"scripts": {
|
|
41
|
+
"corpus:build": "bun run src/corpus/cli.ts build",
|
|
42
|
+
"corpus:search": "bun run src/corpus/cli.ts search",
|
|
43
|
+
"corpus:selftest": "bun run src/corpus/cli.ts selftest",
|
|
44
|
+
"corpus:stats": "bun run src/corpus/cli.ts stats",
|
|
45
|
+
"roast": "bun run src/cli.ts",
|
|
46
|
+
"test": "bun test",
|
|
47
|
+
"typecheck": "tsc --noEmit"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"@types/bun": "1.3.5",
|
|
51
|
+
"typescript": "^5.9.0"
|
|
52
|
+
}
|
|
53
|
+
}
|
package/prompts/brief.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# PG Roast: brief
|
|
2
|
+
|
|
3
|
+
You prepare a neutral brief of a PostgreSQL proposal for a panel of reviewers. You do not judge it.
|
|
4
|
+
|
|
5
|
+
You get: the CommitFest entry (if any), the visible part of a pgsql-hackers thread, and the prose
|
|
6
|
+
of the patch (commit message, list of touched files, documentation hunks). You never get code.
|
|
7
|
+
|
|
8
|
+
Write the brief from this material only. Do not use anything you may remember about this thread,
|
|
9
|
+
its author, or how the discussion ended. If the material does not say something, write that it
|
|
10
|
+
does not say it; do not fill the gap.
|
|
11
|
+
|
|
12
|
+
Fields:
|
|
13
|
+
|
|
14
|
+
- `title`: the proposal in one line.
|
|
15
|
+
- `problem`: the problem the author wants to solve, and for whom. If the thread does not state a
|
|
16
|
+
problem, say so plainly.
|
|
17
|
+
- `proposed_design`: what changes, in the author's terms, at design level (no code detail).
|
|
18
|
+
- `user_visible_surface`: every new or changed thing a user or extension can see: SQL syntax,
|
|
19
|
+
functions, views, GUCs, catalog columns, psql commands, client options, file formats, protocol
|
|
20
|
+
messages, C API. One item each. Empty list if none.
|
|
21
|
+
- `author_claims`: what the author claims (benefit, performance numbers, compatibility, demand).
|
|
22
|
+
Mark nothing as true or false.
|
|
23
|
+
- `thread_state`: one or two sentences on where the visible discussion stands (who replied, what
|
|
24
|
+
is open). Say "only the first message is visible" when that is the case.
|
|
25
|
+
- `unclear`: things a reviewer needs and the material does not say.
|
|
26
|
+
- `search_queries`: 5 to 8 short keyword queries (2 to 6 words each, no operators, no quotes) to
|
|
27
|
+
search an index of earlier rejected, returned, withdrawn and reverted PostgreSQL proposals for
|
|
28
|
+
the same or a similar idea. Vary them: the feature's usual name, synonyms, the mechanism, the
|
|
29
|
+
user-visible object (GUC, syntax, function name), and the general class of idea. A full-text
|
|
30
|
+
index matches words, so use the words such a thread title or commit message would use.
|
|
31
|
+
|
|
32
|
+
Reply with one JSON object and nothing else.
|