docs: focus README and docs on new users#178
Conversation
- Drop the pre-#163 upgrade block from the README; migration notes belong in release notes, not docs aimed at new readers. - Remove PR/issue numbers from the README and docs/ (PgQue's own #102, #106, #163, #159). Behavior is described directly; change history stays in commits and release notes. - Replace `create temp table msgs as ...` patterns in README and docs/examples.md with displayed-row + ack and a data-modifying CTE, respectively — no temp tables in user-facing examples. - Add a brief role-grants block to docs/tutorial.md Step 1 so the zero-to-hero install doc is self-complete. - Tighten the README benchmarks paragraph: clarify the 86k ev/s figure is batched insert, mention the 30-minute sustained test runs under a blocked xmin horizon, drop "Server-class numbers to follow" (also removed from docs/benchmarks.md). - CLAUDE.md: document the two principles (write for new users; no PR/issue numbers in README or docs/) and the install-doc completeness rule.
After spotting issues from a fresh-clone walkthrough of both docs:
- README "Any language" snippet was silently broken without pg_cron.
Add force_tick + ticker so it works on a vanilla install, with a note
that the manual advance can be omitted when a ticker is running.
- README installation block had no "get the source" step before
`\i sql/pgque.sql`. Add a short clone+cd preamble so cwd-relative
resolution actually works for a new reader.
- Manual-ticker bash one-liners (no pg_cron) were missing -d <db>
while the install block uses -d mydb. Add -d mydb for consistency.
- README quick-start expected output showed the input payload key order
({"order_id": 42, "total": 99.95}) but jsonb canonicalizes to length
order ({"total": 99.95, "order_id": 42}). Update the expected output
and add a one-line note about the sort.
- docs/tutorial.md: same jsonb-key-order fix on every receive/DLQ
expected-output block; inputs left in author-typed order so the
Step-3 "validates and canonicalizes" claim has a visible payoff.
- docs/tutorial.md Step 1 role-grants block fails on a fresh box
because app_orders/app_webhook/metrics don't exist. Reframe the
block as illustrative ("placeholder app-role names you would create
yourself") and tell the reader to skip it on a fresh install.
The `inserted` CTE is never referenced from the outer query. Postgres runs data-modifying CTEs to completion regardless of RETURNING, so the clause was pure noise making the example look magical. Remove it; the prose below still explains why the CTE executes.
Three-way separation of send / tick / receive is a hard requirement of PgQ's snapshot-based visibility, not a stylistic recommendation. Any send inside the same xact as the tick is still in the snapshot's xip list and therefore excluded from the next batch's visibility window. Spell that out so a reader does not assume it is just a best practice.
Six sites where send / force_tick / ticker / receive appear close together get a short inline comment reminding the reader that each statement is its own implicit transaction (psql autocommit) and must not be wrapped in begin/commit. Bundling them silently breaks the snapshot-based visibility rule (the tick records a snapshot in which the same-xact send is still in-progress, so the next batch never sees the event), and that's the easiest mistake to make once a reader sees a multi-line SQL block. Touches: README quick-start, README "Any language", tutorial Step 5, tutorial Step 7 (initial + retry), tutorial Step 8, examples fan-out.
CLAUDE.md
- Add a "PR Lifecycle" section: CI green -> REV-style review (ignoring
SOC2) -> real testing with evidence posted to the PR -> approve and
merge or LOOP back. Red/green TDD for any code fixes inside the loop.
- Extend the "no PR/issue numbers" doc rule to cover concrete version
tags ("Breaking change in v0.2", "PgQue v0.2.0 has..."). Refer to
behavior, not release labels. Roadmap/blueprints can still cite
versions when the version IS the topic.
User-facing docs
- docs/reference.md: drop "Breaking change in v0.2:" from
dlq_replay_all; describe the current return shape directly.
- docs/three-latencies.md: "PgQue v0.2.0 has..." -> "PgQue uses..."
for the subscription/tick UPDATE pattern.
- docs/README.md: "v0.1 default install" -> "default install"; same
rewording for the PHASES.md pointer.
- examples.md exactly-once: use scalar subselect for ack so the call runs exactly once regardless of executor row-count assumptions. - README: turn the buried `\gset` mention into a proper 2-line snippet so a psql user can see the scriptable form right next to the manual flow. - tutorial.md Step 1: lead with the "skip on install owner" note so copy-paste readers see it before the role-grants snippet, not after. - reference.md: drop the "for the current release" hedge from the per-queue ACL paragraph; describe the design directly.
REV-style reviewGitHub PR —
Blockers
Non-blocking — all addressed in commit
|
| # | Agent | File:line | Finding | Fix |
|---|---|---|---|---|
| 1 | Docs Reviewer | docs/examples.md:66 |
select pgque.ack(batch_id) from msgs limit 1 may evaluate pgque.ack per row of msgs before LIMIT applies (executor-dependent). |
Switched to select pgque.ack((select batch_id from msgs limit 1)); — scalar subselect runs once, ack runs once. |
| 2 | Docs Reviewer | README.md:253 |
The \gset mention was buried at the end of a long paragraph; new users coming from the hardcoded pgque.ack(1) quick-start would miss the scriptable form. |
Promoted to a proper 2-line snippet directly under the prose, labelled "scriptable psql idiom". |
| 3 | Docs Reviewer | docs/tutorial.md:42-57 |
Role-grants block uses app_orders / app_webhook / metrics that the tutorial never create users; the "skip on a fresh box" disclaimer was after the block, easy to miss. |
Moved the disclaimer to a bold lead-in before the snippet so a copy-paster sees it first. |
| 4 | Guidelines Checker | f8a3a5d body |
Commit body lines wrap at 73–76 cols (rule of thumb: ~72). | Will reflow when squash-merging. |
| — | Guidelines Checker | various | Followed up on the version-tag rule: re-grepped README + docs/ for #\d+, \bv\d+\.\d+\b, "previously / pre- / before X". |
Zero in-scope hits. External citations (riverqueue/river#59, NikolayS/pgq#1) and hyphenated words (pre-built, Sub-ms) remain — allowed. |
Nits — addressed where cheap
| # | Agent | File:line | Finding | Fix |
|---|---|---|---|---|
| 1 | Docs Reviewer | docs/reference.md:502 |
"intentional design decision for the current release" is borderline release-history framing. | Changed to "This is intentional, by design." (commit ef60b67). |
| 2 | Docs Reviewer | docs/reference.md:118,129,138,147,152 |
Consume-side headings still use the old (queue text, consumer text, …) shorthand; only publishing got the queue_name / type_name rename. |
Deferred — out of scope for this PR (the rename was an earlier change; rewriting consume-side signatures opens a separate API question). Tracked in a follow-up. |
| 3 | Docs Reviewer | README.md:130-134 |
Get-the-source preamble says "run psql from the repo root" but the bash block only does git clone + cd pgque (no psql invocation). |
Deferred — already adjacent to a psql --single-transaction -d mydb -f sql/pgque.sql block four lines below; adding a second psql line risks duplicating without adding clarity. |
| 4 | Guidelines Checker | docs/examples.md:56-68 |
Worth noting that an empty pgque.receive returns 0 rows so pgque.ack is silently skipped (which is correct). |
Deferred — the prose already says "every row in msgs shares the same batch_id"; the empty-batch case is implicit. Bug Hunter explicitly confirmed this is correct behavior. |
Cross-doc consistency (Docs Reviewer, clean)
- Quick start, tutorial Steps 5/7/8, examples fan-out — all use the same "send / force_tick / ticker / receive are separate transactions in psql autocommit" comment shape.
- jsonb canonicalization (
{"order_id": 42, "total": 99.95}→{"total": 99.95, "order_id": 42}) applied consistently across README and tutorial.md. - CLAUDE.md
### Documentationrule and## PR Lifecycleare coherent and not contradicted in README ordocs/.
Final state
- All blocking findings either fixed or scheduled for the squash-merge step.
- All non-blocking findings either fixed in
ef60b67or explicitly deferred with reason. - Bug Hunter explicitly confirmed: SQL is correct on paper across README, examples, tutorial, reference. Zero NIT findings.
Testing evidence will be a separate comment.
Generated by Claude Code
Testing evidenceThe PR was walked through verbatim by two agents simulating a brand-new user. Both walkthroughs ran against PostgreSQL 16.13 on a fresh database;
|
| Step | Result |
|---|---|
| 1 — Install | PASS |
| 1 — role-grants snippet | initially failed on a fresh box (role "app_orders" does not exist); fixed in commit 62d3bcb (illustrative wrapping) and re-clarified in ef60b67 (lead-in disclaimer). |
2 — create_queue / subscribe |
PASS, 1 / 1 |
3 — send (jsonb) |
PASS, returns 1 |
4 — empty receive |
PASS, 0 rows |
5 — force_tick + ticker + receive |
PASS; jsonb canonicalization mismatch on output ({"order_id": …, "total": …} → {"total": …, "order_id": …}) — fixed in 62d3bcb. |
6 — \gset + ack |
PASS; subsequent receive returns 0 rows. |
7 — set_queue_config, nack do-block, maint_retry_events, redelivery |
PASS, retry_count = 1. |
8 — second / third nack cycle, DLQ routing, dlq_inspect, dlq_replay, dlq_purge |
PASS, all column shapes match. |
9 — get_queue_info, get_consumer_info, status |
PASS, all three return the documented column shapes. |
## Production cadence: pg_cron subsection |
untested (pg_cron not on test host). |
README.md — new-user walkthrough
Same fresh DB, pgque_readme_test. Pure verbatim copy-paste in the order the README presents the sections.
| Section | Result |
|---|---|
Installation — \i sql/pgque.sql inside begin/commit |
PASS, 76 functions installed. |
Installation — pgque.start() |
N/A (no pg_cron); error is clear. |
Installation — manual psql -d mydb -c "select pgque.ticker()" block |
PASS once -d mydb was added in 62d3bcb. |
| Roles and grants — typical app setup | PASS. |
| Quick start — 5 transactions | PASS. Initial output mismatch on jsonb key order — fixed in 62d3bcb; explainer comment added so the reader is not surprised. |
| Any language snippet | initially silently broken without pg_cron (receive returned 0 rows). Added force_tick + ticker in 62d3bcb so the snippet works on a vanilla install. |
create temp table msgs patterns |
gone — grep -rE '(create temp|create temporary)' README.md docs/ returns no hits. |
pre-# / Server-class numbers to follow |
gone — `grep -nE '(pre-# |
Test-plan checklist (from PR description)
- Render README +
docs/tutorial.md; no broken anchors, no stray PR refs. - README Quick start + "Any language" snippets work without temp tables.
-
docs/examples.mdexactly-once block still tells one transactional story (CTE;ef60b67made the ack scalar so it runs once). -
grep -rE '(create temp|create temporary)' README.md docs/returns nothing. -
grep -nE '(pre-#|Server-class)' README.md docs/returns nothing.
Out-of-scope, deferred
pg_cron-driven cadence (pgque.start(), scheduledcron.jobrows, log-hygiene recipes) — needs a host withpg_cron.docs/reference.mdconsume-side parameter naming consistency vs. publishing — separate API question, deferred to a follow-up.
CI: 9/9 green on ef60b67. Ready to merge.
Generated by Claude Code
Summary
Tightens the README and
docs/for someone arriving today, not someone migrating from an older state.CHANGELOG.md/ GitHub releases), not in docs new readers see first.docs/. Removed PgQue's own#102,#106,#163,#159references. Behavior is described directly; change history stays in commits and release notes. (External citations to other projects' issue trackers, used as evidence, are fine.)docs/tutorial.mdStep 1 (the zero-to-hero walkthrough) so it covers install + roles in one place. README already has adjacent Installation + Roles and grants sections; left intact.create temp table msgs as ...patterns in the README quick start, the README "Any language" block, anddocs/examples.md(exactly-once). README now showsselect * from pgque.receive(...)and a manualpgque.ack(<batch_id>);docs/examples.mduses a data-modifying CTE so the exactly-once pattern stays one transaction.docs/benchmarks.md.### Documentationblock under Style Rules with the two principles (write for new users; no PR/issue numbers in README ordocs/) and the install-doc completeness rule, so future agents apply them by default.Test plan
docs/tutorial.mdand confirm no broken anchors or stray PR refs.docs/examples.mdexactly-once block still tells one transactional story (CTE form).grep -rE '(create temp|create temporary)' README.md docs/returns nothing.grep -nE '(pre-#|Server-class)' README.md docs/returns nothing.https://claude.ai/code/session_011MgPQfBT3Ai8i4BozkBW1W
Generated by Claude Code