# Chat milestone query performance The milestone selector first materializes runs that match an active or waiting conversation in the same company. It then applies the existing publication origin, continuation, status and final-answer checks. This prevents PostgreSQL from evaluating expensive JSON lineage predicates across unrelated run/conversation pairs. The change preserves `boundRunIssueIdSql()` and all publication authorization, idempotency, ordering and pagination logic. It adds no migration or index. The one-second recovery cadence, startup reconciliation, commit signals, shutdown and native progress selector are unchanged. The separate lifecycle scheduling work in [PR #17606](https://github.com/paperclipai/paperclip/pull/17606) is outside this change. ## Reproduce the measurements Run from the repository root with Docker available: ```sh pnpm --filter @paperclipai/server exec tsx ../scripts/benchmark-chat-milestones.ts --baseline-ref=db70c5acd92f50fa37fb4f0dea3da0e7685089a2 --docker-postgres=17 --report=/tmp/chat-milestones-report.json pnpm --filter @paperclipai/server exec tsx ../scripts/benchmark-chat-milestones.ts --baseline-ref=db70c5acd92f50fa37fb4f0dea3da0e7685089a2 --docker-postgres=17 --dense-chat --report=/tmp/chat-milestones-dense-report.json pnpm --filter @paperclipai/server exec tsx ../scripts/benchmark-chat-milestones.ts --baseline-ref=db70c5acd92f50fa37fb4f0dea3da0e7685089a2 --docker-postgres=17 --cursor-window --report=/tmp/chat-milestones-large-window-report.json ``` Without `--docker-postgres=17`, the harness uses disposable embedded PostgreSQL. Neither mode uses an existing application database. The Docker mode starts a container on an ephemeral localhost port, applies the repository migrations and removes the container after the run. The harness imports the baseline service from the pinned commit and captures the complete SQL and parameters generated by both real service implementations. It stops before publication writes. The report includes full positional result equality, all samples, plans, database version/settings, source hashes and the treatment diff. A detached, uncommitted treatment is identified by its file hashes and diff in addition to its HEAD. ## Local results Measured on PostgreSQL 17.11 with 128 MB shared buffers and 4 MB work memory. The synthetic fixture contains 2,615 runs, 55 conversation issues and contexts containing a base64 string made from 144 KiB of pseudorandom bytes, approximately 192 KiB of text. Each query has two warmup passes and five measured passes; baseline and treatment order alternates. These are local SQL measurements, excluding provider delivery and application processing. | Fixture and query | Baseline median, range | Treatment median, range | Complete ordered rows | | --- | --- | --- | --- | | Selective bindings: milestones | 1,496 ms, 1,483–1,716 | 131 ms, 125.59–135.85 | 60 equal | | Selective bindings: native progress control | 17.15 ms, 16.35–18.38 | 17.43 ms, 16.77–19.78 | 41 equal | | Dense bindings: milestones | 737 ms, 629–1,291 | 809 ms, 463–1,288 | 200 equal | | Dense bindings: native progress control | 23.93 ms, 22.72–25.47 | 24.33 ms, 20.33–29.37 | 41 equal | The selective fixture improved by **11.42×**. Its original merge join discarded 60,962 run/conversation pairs through a complex lineage filter. The treatment materialized 101 matching runs first and discarded no pairs through that join filter. Shared-hit blocks fell from 3,276,955 to 64,903. Neither plan spilled to temporary files. The dense control binds about 2,500 formerly unrelated history rows to active conversations through the first-class issue column, preserving existing native progress receipts. It materializes 1,768 runs. Its treatment median was about 10% slower, with heavily overlapping sample ranges. A subsequent standalone EXPLAIN also varied substantially. **Dense performance is inconclusive, with possible materialization overhead; it does not establish a speedup.** Both native progress controls are neutral. The gain depends on binding selectivity and the database plan. These measurements do not establish a universal speedup, improved request latency or deployed database cost. Compare fresh production query-statistics windows after any separately authorized rollout. ## Large-window cursor control The `--cursor-window` control uses the same service implementation with 12,215 runs, including 12,000 recent native runs with first-class task bindings and approximately 8 KiB encoded contexts. It requests a limit of one; the service's minimum candidate page size is 25. The fixture adds a second conversation for the same task so both late nonempty pages must preserve the other conversation on the cursor's own run. Every statement is captured from `enqueueChatRunMilestones`, including its actual second-page cursor. Capture advances the real keyset loop with a non-publishable placeholder page and stops before any publication write. The finished cursor comes from the last actual candidate on the short final page. All statements then run against the real PostgreSQL fixture, with two warmups and five alternating measurements. | Service page | Baseline median, range | Treatment median, range | Complete ordered rows | | --- | --- | --- | --- | | First page | 762.65 ms, 678.43–807.93 | 800.63 ms, 597.38–838.01 | 25 equal | | Cursor at 90% of history | 234.84 ms, 226.42–241.19 | 54.36 ms, 53.39–63.62 | 25 equal | | Cursor at 99.99% of history | 352.10 ms, 321.27–377.10 | 12.88 ms, 12.13–29.92 | 7 equal | | Cursor after the final candidate | 340.00 ms, 324.40–381.30 | 58.58 ms, 56.55–78.92 | 0 equal | The first page is neutral/inconclusive; its sample ranges overlap. Later pages improved by 4.32×, 27.33× and 5.80× respectively. The unchanged native progress control was neutral at 22.79 versus 22.72 ms, with 25 equal rows. The treatment still materializes 12,101 matching runs before its outer cursor filter. The materialization node took 4.6–10.0 ms in these plans, and no measured page spilled temporary data. These observations do not measure peak memory or prove how every JSON value is stored or decompressed. They do show that this bounded, native-heavy fixture did not reproduce the feared large-window cursor regression. No identity-only rewrite or additional cursor predicate was needed for this change. The dense and first-page performance limits remain disclosed. ## Correctness and blast radius The safety condition is that the new prefilter repeats a predicate already required by the existing conversation join. It uses the same company, conversation-state and native/legacy binding expression within the same SQL statement. All authoritative checks still run afterward. The selective benchmark compares every returned column in exact order and checks 15 explicit historical binding cases. These include whitespace trimming, task-ID fallback, numeric/object/array JSON values, uppercase and malformed identifiers, native and first-class issue precedence, cross-company values and null/scalar/array contexts. Dense results compare the complete first 200 ordered rows; they do not claim that all history fits on that page. Migration 0313's insert trigger normally normalizes recognized JSON identifiers into the first-class issue binding. The fixture disables that trigger only while inserting historical row shapes in its disposable database and restores it in `finally`. Runtime triggers are unchanged. Run the dedicated regression and existing coordination suites: ```sh pnpm --filter @paperclipai/server exec vitest run src/__tests__/chat-run-milestone-candidates.test.ts src/services/chat-publication-reconciliation.test.ts src/__tests__/app-private-hostname-gate.test.ts ``` These three suites passed **27 tests** during validation, with a disposable PostgreSQL 17 constructor override for the database test. The service integration checks cover bounded draining past rejected candidates, exact continuation origins, private summaries, explicit final-answer precedence, idempotent lifecycle updates, ownership-attention messages and native progress reauthorization after a question, final answer or revoked reach wins. The relevant cases live in `server/src/__tests__/chat-channels.integration.test.ts`. These checks prove service behavior using synthetic provider runtimes; they do not prove live provider delivery or deployed sleep/wake behavior. The following selection passed **37 tests** with disposable PostgreSQL 17; 1,529 unrelated cases were skipped: ```sh pnpm --filter @paperclipai/server exec vitest run src/__tests__/chat-channels.integration.test.ts \ -t "drains bounded Slack chat-origin milestones|coalesces one Slack run's lifecycle|edits a Slack working reply|publishes a Telegram failure for an exact confirmation continuation|replaces working with one safe ownership-attention|does not let a late|keeps an internal Telegram run summary private|publishes only a resolver-selected Telegram reply|settles an interrupted native run once|preserves an interrupted run's selected final|skips a locked native progress|reauthorizes skipped native progress|coalesces closed native progress|publishes native progress with a typed issue binding|enforces the native progress cadence|does not replay stale native progress|keeps native progress behind question|reuses a pre-run FIFO notice|link-only interaction continuation|successful GitHub link-question continuation|publishes a closed-choice question, settles its Slack card" ```