Commit Graph
3 Commits
Author SHA1 Message Date
DottaandPaperclip a63d19816f fix(server): wake decision continuations from committed changes (#17478)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A completed decision can owe a wake to the agent that proposed it.
> - The decision row already stores the pending continuation.
> - The main maintenance tick scans this debt even when no continuation
is ready.
> - Committed changes can activate recovery and leave settled history
quiet.
> - This pull request gives continuation delivery its own worker and
shares foreground delivery ownership.
> - Pending work stays recoverable without an unconditional continuation
scan.

## Linked Issues or Issue Description

Targets master after #17475 merged. This is slice 09 of the recurring
server work migration. Each PR owns one reason for background work. The
remaining inventory is in
`doc/plans/2026-10-24-recurring-server-sleep-stack.md`.

**What existing behavior does this improve?**

Decision continuation delivery and recovery in the server.

**Current behavior**

Foreground paths send a wake and clear a pending flag. The combined
decision sweep scans pending continuations on every maintenance tick. It
can overlap foreground delivery.

**Proposed behavior**

Terminal decision writes notify a dedicated worker through the existing
transaction boundary. Foreground and recovery calls share one in-flight
delivery per decision and DB owner. Recovery re-reads readiness, pages
past failures, and retries only unresolved debt or errors.

**Reason and benefit**

Open decisions, running effects, and delivered history no longer cause
an independent continuation scan. No queue table is added. Decision
expiry, target invalidation, interrupted effects, and retention remain
separate duties.

## What Changed

- Extract continuation readiness, delivery, and paged recovery into the
decision owner. Preserve the wake payload and at-least-once crash
behavior.
- Signal the ten decision update sites before SQL in their existing
transaction scope. Keep effects and wake calls outside those
transactions.
- Explicitly create the continuation obligation on all three
archive-proposal failure settlements, including recovered running rows
without inherited pending metadata.
- Serialize concurrent decision effect execution through final
settlement. Coalesce foreground and worker wakes. Re-read the durable
flag before sending. Fence effect completion to running state so
competing completion cannot restore acknowledged debt.
- Expose an opaque DB work-scope key. Child pools and nested
transactions share that identity; independent roots remain separate.
Existing signal consumers keep their behavior.
- Bound each recovery pass to 100 candidates, retain a fair cursor
across failures, and stop between deliveries on shutdown. Forward the
existing AbortSignal through the shared deadline adapter.
- Register enabled-heartbeat continuation recovery before decision
housekeeping. Remove only its independent scan from the main tick.
Preserve the public combined sweep and disabled-heartbeat behavior.
- Add generated migration `0388_ambitious_micromax.sql`, an idempotent
concurrent partial index for pending continuations, and keep five
generated snapshots.
- Add real producer, transaction, failure, concurrency, paging, quiet,
and shutdown tests. Document ownership and remaining limits in
`doc/idle-decision-continuations.md`.

## Verification

Current candidate: `18f682863cadb91281a86b4da2553a4579ee054a`, normally
integrated with landed archive squash `5785090ff1`. All 31 independent
incoming master files are preserved exactly, including
controller-authority recovery. All 97 decision/startup/deadline cases
plus server types/build pass after the required Runner vendor rebuild.
The preceding full five-suite selection passed 293 cases; the
continuation owner behavior is unchanged by master reconciliation.

- **97 cases pass at this master integration** across decision, startup
and scoped-deadline suites, plus server types/build. The preceding
parent candidate passed the full 293-case
decision/gateway/archive/startup/deadline selection; those results
remain evidence for that revision. All selected cases executed with
separate PostgreSQL fixtures where needed.
- Tenant-driven global scan amplification reproduced six sweeps for six
sequential real cancellations. Existing thirty-second notification
coalescing now permits one recovery sweep per window while retaining
pending work. The first green fixture wrongly expected the intent hold
to clear before coalesced reconciliation; the hold remains and quiet is
checked after settlement. Foreground delivery stays immediate.
- The archive contention test uses a signaled dedicated
single-connection read and two FIFO SQL round trips instead of a fixed
delay. Removing effect coalescing makes it fail with two contender
reads; restoring production code passes. No production test hook was
added.
- **Three new real PostgreSQL regressions failed before repair**:
recovered archive proposals with invalid signatures, mismatched
manifests or archive execution failures and no inherited pending flag.
Each now persists failure, wakes once, acknowledges continuation
delivery and remains quiet on replay. Running-state fencing is
preserved.
- The unmerged continuation index is regenerated as
`0388_ambitious_micromax.sql` after parent migrations 386/387. All
parent journal entries are unchanged, five generated snapshots remain,
numbering/safety and snapshot/schema-drift checks pass. Fresh database
migration apply and replay pass; the concurrent index is valid.
- Existing lifecycle coverage includes foreground/worker coalescing,
child pools, transaction commit/rollback, failed acknowledgement,
restart, drain/resume, shutdown and fair 100-row recovery. Earlier
bounded-pass and concurrent archive-result regressions and their repairs
remain recorded in [the owning behavior
audit](doc/idle-decision-continuations.md).
- Failure accounting: the first combined command used an incorrect
retention fixture variable and skipped 15 cases. The corrected separate
external-PostgreSQL retention run passed all 15. The historical nine
embedded-only work-signal cases were not executed by this selection.
Extra upstream embedded-only suites attempted on the parent could not
initialize locally; they remain CI verification, as recorded in the
parent PR. Earlier reused-fixture, assertion and compile failures remain
in the behavior audit and stack ledger; they are not erased by later
passing checks.

Commands: `pnpm --filter @paperclipai/server typecheck`, `pnpm --filter
@paperclipai/server build`, server Vitest for `decisions-service`,
`decision-retention-service`, `server-startup-feedback-export` and
`scoped-deadline-work`; `pnpm --filter @paperclipai/db
check:migrations`, migration apply/replay, and the generated
snapshot-drift test. Local targeted evidence does not establish a full
local repository run, deployed sleep or fleet savings. Fresh exact-head
CI and both automated reviews remain merge gates.

## Risks

- Notification-driven recovery remains global but is coalesced to thirty
seconds. This bounds request-driven scans; it does not introduce
company-scoped selection. Foreground delivery remains immediate.
- Delivery remains at least once. A crash or acknowledgement failure
after accepting a wake can send it again. Local coalescing is not a
cross-process claim.
- Supported writers share the application DB signal owner. Independent
SQL writers or multiple active server processes need a separate wake
contract. Wake-producing service methods do not gain a new guarantee
when called inside an uncommitted caller transaction.
- The concurrent index needs top-level migration execution; it is
registered with the existing migration owner and can be replayed safely.
- No Cloud sleep blocker or external alarm coverage changes. Worker
quiet does not prove deployed app or database suspension.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning and code execution. The exact
serving model identifier and context window were not exposed.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 17:44:51 -05:00
Devin FoleyandPaperclip 833769ab55 perf(chat): schedule Telegram maintenance from commits and deadlines (#16094)
Replace periodic Telegram discovery and maintenance scans with bounded startup recovery, connection-specific commit notifications, and durable action deadlines. Preserve ownership, inactive subscription parking, retry and cleanup recovery, and atomic credential binding updates.

Verified full typecheck/build, 30 focused scheduler tests, 12 database-backed recovery cases, all current-head CI checks, and Greptile 5/5 with no unresolved threads.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 07:55:27 -07:00
DottaandPaperclip 835a022936 perf(server): wake delivery queues from transaction-aware work signals (#15625)
## Thinking Path

> - Paperclip manages AI-agent work and its delivery to users and
agents.
> - Five delivery queues scan the database even when empty.
> - Each queue already has durable rows; empty polling wastes idle
hosting capacity.
> - Producers can register intent inside their existing database
transaction.
> - Central transaction tracking can wake consumers after the outer
commit without extra caller wrappers.
> - A lost response needs reconciliation against PostgreSQL before an
empty queue is safe to leave idle.
> - This change uses one scheduler for outstanding work and lets
settled, empty queues go quiet.

## Linked Issues or Issue Description

Supersedes the closed #15614. Related idle-safety work: #15522 and
#15599.

**What existing behavior does this improve?**

Delivery scheduling for feedback exports, chat completions, connection
continuations, question answers, and tool-action receipts.

**Current behavior**

Feedback scans every five seconds. Four delivery queues scan on the
heartbeat interval, normally 30 seconds. Startup and some event paths
also scan them.

**Proposed behavior**

Each producer awaits one intent registration before its queue write. The
existing `createDb` transaction boundary tracks nested savepoints and
wakes the consumer after outer settlement. Startup scans restore durable
work. Pending rows, failures, and unresolved transactions retain retry
deadlines. Empty, settled queues have no timer or recurring scan.

**Reason and benefit**

Normal delivery starts after commit. Central tracking removes
caller-specific post-commit plumbing. PostgreSQL transaction status
resolves lost responses without new tables or a permanent uncertainty
latch.

**Breaking changes**

No API or schema changes. The notification scope is one DB owner and its
dedicated child pools. Direct SQL, separate roots, and other processes
require an explicit wake/recovery integration. This path requires
PostgreSQL 14+ transaction-information functions; embedded PostgreSQL
uses 18.

## What Changed

- Add one named-deadline scheduler and app-owned coordinator for five
existing queues.
- Instrument `createDb().transaction()` centrally, including nested
savepoints. Dedicated child connections share their owner's signal
scope.
- Register work before the five queue insert paths. The first
registration obtains the outer XID; unrelated transactions issue no
extra SQL. Tool receipt insertion gains a small transaction around its
existing insert.
- Query `pg_xact_status` only for rejected, tracked transactions. Probe
before scanning the queue. Retain the idle hold while PostgreSQL still
reports an in-progress transaction or the probe fails. Release it after
settlement and reconciliation.
- Remove unconditional delivery recovery scans and caller-specific
completion post-commit actions. Retain the existing task-scoped
completion activity fast path and durable claims.
- Coalesce wakes without postponing an earlier deadline. Retry
outstanding rows and failures; suppress dispatch during idle drain while
allowing transaction and queue reconciliation; suppress both during warm
standby. Cancel deadlines and await active delivery sweeps on shutdown.
- Serialize feedback flushes. Vote routes return after saving. Uploads
have a 30-second deadline and shutdown cancellation; unfinished exports
remain recoverable.
- Document the writer contract and update the dated sleep inventory.

## Verification

- Final head: `a4609956841107ad60c4cb50fe2acb05636b1363`.
- Passed: all final-head hosted checks, with 54 successes and two
intentional skips. The full test matrix, typecheck, build, canary, and
aggregate verification passed in [run
37940948440](https://github.com/paperclipai/paperclip/actions/runs/37940948440).
- Passed: Greptile 5/5 on the same head, with a successful check run and
zero unresolved review threads. The branch is mergeable.
- Passed locally after the final rebase: 50 coordinator, scheduler, and
startup tests; server typecheck; server build.
- Passed locally before the final import-only rebase: repo-wide
typecheck and build; 237 PostgreSQL producer/delivery and database
signal tests; feedback, native-question, and idle-safety regression
tests.
- Real PostgreSQL tests hold transactions open after an injected client
response loss, then commit or abort. They verify that an empty scan
cannot clear an in-progress transaction. Another test terminates a real
backend and verifies recovery without replay.
- Coordinator tests cover an hour with no empty-queue timers,
pending/error retries, worker replacement, concurrent writes,
earliest-deadline preservation, rollback during idle drain, committed
work during drain, standby, and shutdown.
- Local `pnpm test:run` was started and stopped after embedded
PostgreSQL startup failures appeared. It did not finish and is not
reported as a pass. Later local database reruns had skipped suites;
those skips are not claimed as verification. The earlier PostgreSQL
tests did execute and pass, and the final hosted full matrix passed.

## Risks

- One XID query is added per transaction that registers delivery work.
Nested transactions share that ID. Registration must be awaited before
writing; new enqueue paths must follow this contract.
- Work signals are local to a DB owner and its dedicated child pools.
Independent roots, direct SQL, or other processes are not observed.
Startup scans recover already committed rows, but do not fence late
transactions from a previous process. Cross-process ownership and
database failover require separate work.
- A database outage or genuinely unresolved transaction keeps an idle
hold until status can be reconciled. No timer clears uncertainty by
assumption.
- These changes remove five empty polling loops. They do not implement
whole-instance sleep, database teardown, or an external waker. The
earliest deadline covers only the registered queues.
- Waiting tool reviews retain their existing retry cadence until their
receipts can be delivered.
- Feedback vote responses no longer wait for the remote upload. Sharing
consent and the saved vote response are unchanged.
- Real hosting-provider sleep and cost savings have not been measured.

## Model Used

OpenAI Codex, GPT-6. Used reasoning, repository inspection, code
editing, and command execution. The runtime did not expose the exact
model revision or context window.

## Checklist


- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-09 10:01:26 -05:00