Skip to content

docs: focus README and docs on new users#178

Merged
NikolayS merged 7 commits intomainfrom
claude/update-readme-docs-Pjk6m
May 3, 2026
Merged

docs: focus README and docs on new users#178
NikolayS merged 7 commits intomainfrom
claude/update-readme-docs-Pjk6m

Conversation

@NikolayS
Copy link
Copy Markdown
Owner

@NikolayS NikolayS commented May 3, 2026

Summary

Tightens the README and docs/ for someone arriving today, not someone migrating from an older state.

  • No more "pre-#X" framing. Drop the pre-fix(roles): restore PgQ producer/consumer split (#102, #106) #163 upgrade block from the README. Migration notes belong in release notes (CHANGELOG.md / GitHub releases), not in docs new readers see first.
  • No PR/issue numbers in README or docs/. Removed PgQue's own #102, #106, #163, #159 references. Behavior is described directly; change history stays in commits and release notes. (External citations to other projects' issue trackers, used as evidence, are fine.)
  • Installation docs are self-complete but brief. Added a tight role-grants block to docs/tutorial.md Step 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.
  • No temp tables in user-facing examples. Replaced create temp table msgs as ... patterns in the README quick start, the README "Any language" block, and docs/examples.md (exactly-once). README now shows select * from pgque.receive(...) and a manual pgque.ack(<batch_id>); docs/examples.md uses a data-modifying CTE so the exactly-once pattern stays one transaction.
  • Tightened README benchmarks paragraph. Clarified the 86k ev/s figure is batched PL/pgSQL insert. Noted the 30-minute sustained test ran under a blocked xmin horizon (concurrent long-running transaction holding an assigned XID — the worst case for SKIP LOCKED queues). Dropped "Server-class numbers to follow" from both the README and docs/benchmarks.md.
  • CLAUDE.md. Added a ### Documentation block under Style Rules with the two principles (write for new users; no PR/issue numbers in README or docs/) and the install-doc completeness rule, so future agents apply them by default.

Test plan

  • Render the README and docs/tutorial.md and confirm no broken anchors or stray PR refs.
  • Eyeball the README Quick start and "Any language" snippets for clarity without temp tables.
  • Confirm docs/examples.md exactly-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

- 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.
@NikolayS NikolayS marked this pull request as ready for review May 3, 2026 11:48
claude added 6 commits May 3, 2026 11:53
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.
Copy link
Copy Markdown
Owner Author

NikolayS commented May 3, 2026

REV-style review

GitHub PR — glab not used; ran the same five-agent shape as REV (https://gitlab.com/postgres-ai/rev/) in parallel locally. SOC2 findings ignored per repo policy. Three agents fired (this PR is docs-only — Security/Sqitch agents would have nothing to do):

  • Bug Hunter (Opus-equivalent) — SQL correctness on paper.
  • Guidelines Checker (Sonnet-equivalent) — CLAUDE.md rules.
  • Docs Reviewer (Sonnet-equivalent) — prose, examples, cross-doc consistency.

Blockers

# Agent File / commit Finding Status
1 Guidelines Checker commits 62d3bcb (60), 407e7ca (62), ec09daa (62), a2483d4 (67), 0dff13a (71) Five of six commit subjects exceed CLAUDE.md's <50 chars rule. Only f8a3a5d (40 chars) is compliant. Will resolve at merge time via squash-merge — the single commit on main will carry a compliant subject, and we avoid the --amend / force-push paths that CLAUDE.md forbids.

Non-blocking — all addressed in commit ef60b67

# 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 ### Documentation rule and ## PR Lifecycle are coherent and not contradicted in README or docs/.

Final state

  • All blocking findings either fixed or scheduled for the squash-merge step.
  • All non-blocking findings either fixed in ef60b67 or 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

Copy link
Copy Markdown
Owner Author

NikolayS commented May 3, 2026

Testing evidence

The PR was walked through verbatim by two agents simulating a brand-new user. Both walkthroughs ran against PostgreSQL 16.13 on a fresh database; pg_cron was unavailable on the test host, so the pgque.start() / cron-driven paths in both docs are flagged as untested locally but pgque.status() correctly reported the unavailability with a helpful message.

docs/tutorial.md — end-to-end

Fresh DB pgque_tutorial_test, install via \i sql/pgque.sql inside begin; … commit;. pgque.version() returned 1.0.0-dev.

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.md exactly-once block still tells one transactional story (CTE; ef60b67 made 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(), scheduled cron.job rows, log-hygiene recipes) — needs a host with pg_cron.
  • docs/reference.md consume-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

@NikolayS NikolayS merged commit cef31ca into main May 3, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants