Commit Graph
65 Commits
Author SHA1 Message Date
DottaandPaperclip 2a6d4d1694 perf(db): index canonical task activity lookups (#18043)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/177/changes/paperclip/177
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip helps people manage AI agents and their tasks.
> - Task lists rank tasks by their latest canonical activity.
> - The existing lookup reads every event for each task before
pagination.
> - Query-only alternatives failed a narrow visibility control or the
performance goal.
> - This PR adds an index that lets the unchanged lookup stop at the
newest qualifying event.
> - The result is less history work with the same permissions, ordering,
and response.

## Linked Issues or Issue Description

Refs: #13803. That broader proposal stores a new activity field with
triggers and a backfill. This index was authored independently and uses
the existing query.

**Subsystem affected**

Task-list activity ranking and the database schema.

**Current behavior**

The list computes `MAX(activity_log.created_at)` for every eligible
task. The existing entity index reads 50 events per task across 1,199
candidates in the representative fixture.

**Proposed behavior**

Add `(company_id, entity_type, entity_id, created_at)`. Keep the
production task service and permission helpers unchanged. PostgreSQL
reads backward until an event passes the existing exclusions.

**Reason and benefit**

Fresh paired measurements on the current base improve the
full-description scoped SQL from **44.60 to 20.33 ms (2.19×)**. Activity
buffer accesses fall from **63,547 to 4,796**, and history rows per
probe fall from 50 to one. The complete service median falls from 50.63
to 31.31 ms. Complete positional SQL results and service responses
match.

## What Changed

- Add generated migration `0414_closed_edwin_jarvis.sql` and the
matching Drizzle index.
- Use `CREATE INDEX CONCURRENTLY IF NOT EXISTS` through the existing
top-level runner and invalid-index retry cleanup. The client change is
one whitelist entry.
- Preserve all upstream SQL and journal entries; retain snapshots
0410–0414.
- Cover full and index projections, canonical ordering, invalid-index
recovery, concurrent migrators, and incomplete fixture rejection.
- Repair benchmark seeding, apply requested work memory to both
connections, and record both settings. Hosted defaults now include six
Slack conversations, 642 GitHub conversations, and 53 removed
conversations.

## Verification

- **All final-head CI gates passed**, including full workspace
typechecks, tests, build, Runner checks, and all eight E2E shards.
Greptile is **5/5** on `67ca7ae1cf15fc054ef515ffd8205b1fe30a0d55`; no
unresolved review threads remain.
- Local database/server typechecks and migration numbering/safety checks
pass. Three server regression tests and three migration regression
suites pass on real PostgreSQL.
- The original pre-index work guard fails at eight rows per probe; the
indexed guard now checks both the full-description query and index
projection. The service oracle covers cleanup/inbox exclusions, raw text
IDs, company scope, comments, ties, sort directions, visibility before
pagination, empty lists, and one child per parent.
- The documented pre-index seed command reproduced the review failure
before the fix and now succeeds. A paired run with non-default memory
confirms both independent connections use `16MB`.
- Fresh proof pins pre-index base
`929e840595c8933ca485ac7a2e99413e1e11ca64`, with identical cloned data,
unchanged generated SQL and parameters, two warmups, and five
alternating samples per arm. All **18 comparison cells** preserve every
positional row and complete service response. The 15-case default-memory
matrix passes its physical-work, speed, and control bounds.
- The one-visible-task control is 6.64 → 7.25 ms SQL; the 20-task
control is 3.71 → 3.72 ms. Both pass the declared 25% or 2 ms budget.
Full-query memory controls use matching `4MB` or `16MB` settings.
- Committed 1,000-row write batches measured 40.09 → 43.17 ms median
(+7.7%). Ranges overlap (34.67–71.85 and 34.27–48.05 ms); this is an
observed local cost, not a precise throughput guarantee.
- The final-numbered migration builds a valid index through the normal
runner on the fresh 300,000-row fixture in 0.55 s. Earlier large-fixture
proof of the same index SQL used 5,000 tasks and 1.25M activity rows:
2.58 s first apply, concurrent write/read probes 5.16/2.43 ms,
interrupted invalid-index cleanup, successful retry, and one receipt.
Current migration recovery tests verify the renumbered path.
- [The proof
document](https://github.com/paperclipai/paperclip/blob/67ca7ae1cf15fc054ef515ffd8205b1fe30a0d55/docs/performance/task-list-activity-index.md)
retains the historical baseline, rejected rewrites, and the earlier
matrix invalidated by severe host contention. Those historical timings
are not substituted for the fresh measurements above.

## Risks

The migration is an exception after measured query-only alternatives
failed. The index adds storage and work to every activity write: about
30 MiB for 300,000 rows and 123 MiB for 1.25M rows. Concurrent creation
avoids the writer blocking observed with an ordinary index build, but
long transactions can delay it. Local timing does not establish Neon
build duration.

The query still ranks all eligible tasks, and excluded newer events
still need to be skipped. These are local SQL/service measurements;
deployed HTTP latency remains unverified. No hosted DDL or deployment
was performed.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, code execution, and tool
use. The environment does not expose the exact model identifier or
context-window size.

## 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 linked existing issues or described the issue using
the relevant issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal issue
id
- [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-29 09:06:41 -05:00
79ce86630a fix(server): schedule governed review response recovery (#17702)
## Thinking Path

> - Paperclip manages agents, tasks and their external chat responses.
> - A completed chat turn can owe a response while an earlier review
gate remains pending.
> - Periodic recovery revisits that response history even when no work
can advance.
> - This change uses the existing delivery coordinator and saved retry
deadlines.
> - Approval, interaction and authority changes wake the affected issue
or run after commit.
> - The existing presentation owner verifies the durable result and
current authority.
> - Settled and dormant history becomes quiet without losing future
eligible responses.

## Linked Issues or Issue Description

Refs #17696 (merged) and #15625. This is slice 49, now based on master.
It owns governed-review response presentation. Accepted responses and
pending decision effects keep their existing owners.

**What happened?**

Native finalization recovery repeatedly inspects committed review
responses. Removing the scan without covering approval, interaction,
task-policy and authority changes would strand responses that become
eligible later.

**Expected behavior**

Recover an owed response once from existing durable facts. Enforce
authority at publication, wake after relevant outer transactions commit,
retain real retry deadlines and restart/drain recovery, and stop
recurring SQL when no response can advance.

**Steps to reproduce**

Complete an external chat turn while an earlier completion review
remains pending. Block its response behind an approval or interaction,
then resolve that blocker. The response must appear once without a
periodic scan or resolving the original review gate.

## What Changed

- Add a scoped governed-review worker using the existing coordinator,
finalizations, decisions and comment receipts. No queue table or
transaction wrapper is added.
- Keep recovery and publication policy in the native finalizer. Preserve
binding, source/session authority, ownership, deadline and
deleted-comment fences; match coordinator-before-issue lock ordering.
- Notify through actual approval, interaction, OAuth, task-policy and
lifecycle producers before their owned writes. Serialize approval
resolution with task linking so a concurrent link retains its wake.
Nested transactions still settle at outer commit.
- Scope ordinary milestones, finalization and coalesced input to their
issue/run. Use the explicit company authority boundary for genuine
global restoration, including membership changes. Both response workers
share this boundary.
- Register under maintenance and drain gates, disable the two periodic
repair entrances, and retain fresh finalization and explicit recovery.
Add the concurrent 0413 comment-receipt index and migration
registration; retain five snapshots.
- Preserve the producer inventory, source qualifications and earlier
failures in the owner audit. Retain a real Photon regression proving
routine ownership assertions stay quiet and concurrent authority
restoration wakes each worker once.

## Verification

- Scoped repair source `2341714720b5c273f0ce011b48345af0ef39227f` passes
**2053 distinct cases**: 1703 in 15 complete producer/authority/recovery
files, including all 273 governed-response cases and 95 wake-queue
adapter cases, plus 350 selected chat cases. Another 1463 chat cases are
outside that selection. Thirteen fresh PostgreSQL 17 fixtures migrate
through unchanged 0413; the one temporary queue-fixture import restores
exactly. All sixteen captured hashes match.
- Two actual repeated coarse-hint SQL regressions fail before repair and
pass afterward. Actual membership restoration passes before and after.
The same assertions and time limits prove dormant work stays quiet and
real authority restoration still publishes once. Real coalesced input
covers both queues after commit.
- Combined source `586f5674cd9798b33cd22437255e0480b5726d20`, including
independent master #18129, passes **1297 distinct cases**: 1093 in five
complete server files, 185 in five runner files, 14 Node protocol cases
and five UI registry cases. This includes the complete 22-case Photon
file for both response workers. The external Photon transport is mocked;
Paperclip callbacks, transaction fences and observed SQL are real. The
earlier and combined case counts are separate, with no cumulative
double-counting.
- A normal merge retains both complete owned and independent master
binary patches. All twelve new-context hashes and sixteen earlier repair
hashes match. Three dedicated fresh 0413 fixtures plus the live-event
helper's own fresh database use PostgreSQL 17 without source
adaptations. Database build, server types/build, boundaries, full-SHA
migration order and generated TS/Rust profiles pass. The new inherited
Runner vendor inputs rebuild successfully.
- Database bytes remain identical to the previously qualified 0413
source. The recorded populated 0412-to0413 upgrade retains all 411 prior
receipts and 38 companies, appends one receipt and none on replay; the
index is valid and ready. The earlier 2828-case acceptance, CLI journey,
setup failures and their repairs retain their recorded source scope in
`doc/plans/2026-10-26-governed-review-response-sleep-audit.md`.
- Parent #17696 merged with 58 clean checks, Greptile 5/5 and Ponytail
5/5. Its Photon warning was disproved by real callback/SQL evidence and
the security check became successful. This PR still requires its own
fresh exact-head CI and review gates. No deployed sleep, external alarm
or fleet savings claim is made.

## Risks

- Notifications use the existing process-local writer contract.
Independent SQL writers and uncoordinated replicas are outside its
coverage.
- Genuine startup and company authority reconstruction remain
proportional to the owed backlog. Queries page 100 rows, but discovery
accumulates the candidates; this is not a constant-cost global
operation.
- Current publication authorization, durable binding, ownership and lock
ordering remain mandatory. The worker does not resolve the original
review gate.
- The concurrent index uses existing migration replay handling. Other
recovery duties and Cloud sleep eligibility remain separate.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository tools, shell
execution and PostgreSQL tests. The runtime does not expose a more
specific serving snapshot or context-window size.

## 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>
Co-authored-by: Codex <noreply@openai.com>
2026-10-29 02:54:46 -05:00
ec3b558277 fix(server): schedule accepted native response recovery (#17696)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native runs store accepted results and their finalization decisions.
> - A completed run can still owe its public answer after an
interruption.
> - Periodic recovery scans for that answer even when no new work
exists.
> - This pull request wakes recovery from committed changes and saved
retry deadlines.
> - The existing owner still verifies the result, source, and current
publication authority.
> - Settled or dormant response work no longer needs its own periodic
scan.

## Linked Issues or Issue Description

Refs #17690 (merged). This is slice 48 of the sleep migration stack, now
based on master. Related: #17661 handles GitHub terminal publication;
this change handles native accepted-response presentation.
Governed-review presentation remains a separate duty.

**What happened?**

Native finalization recovery repeatedly checks already accepted results
for a missing answer. Removing that check without complete notifications
would lose responses when evidence arrives late, an agent resumes, or
admission settles after execution.

**Expected behavior**

Recover the answer from its saved result when a supported change makes
it eligible. Retain real retry deadlines. Stop querying when the answer
is selected or cannot currently be published. Reconstruct work after
restart.

**Steps to reproduce**

1. Commit a native accepted result with no selected response comment.
2. Run finalization recovery while its source is unavailable or locked.
3. Restore that source or release the lock. The answer must appear once
without repeating provider execution.

## What Changed

- Honor recorded non-create decisions and skip-comment policy in both
discovery and recovery. Apply durable conversation/result/continuation
guards before recovery locks. Preserve genuine unanswered and legacy
recovery.
- Scope routine response wakes to the affected issue/run. Coalesced
input names its active run; pending-effect settlement names its issue.
Sixteen existing global authority/configuration hooks use one explicit
company invalidation boundary. No new transaction wrappers or tables are
added.
- Add an accepted-response worker using the existing delivery
coordinator and scoped deadline helper. Preserve saved retry deadlines
and pending-effect fences under the owner's lock.
- Share the finalization notification boundary. Wake on finalization
settlement, exact accepted-result/final-message evidence, agent
availability, retry admission, and existing chat authority changes.
- Keep lifecycle policy in services. A narrow pre-write status hook lets
lifecycle retain ownership of its transaction and status transitions.
- Register the worker during enabled maintenance startup. Remove its
authority subscription on shutdown. Periodic replay skips this duty on
both committed and fresh-commit paths; ordinary finalization and
explicit recovery retain their behavior.
- Add a partial index and expression statistics for unpresented native
runs. Migration 0412 supports concurrent replay and adds no queue table.
- Add real PostgreSQL and lifecycle coverage. Preserve the
implementation and producer audit in the plan document.

## Verification

- Additional real Photon callback acceptance on the published production
code disproves the security warning: ten active assertions cause zero
response-recovery hints/SELECTs, while three concurrent assertions
during an actual attention-to-active recovery cause exactly one
wake/SELECT. The existing locked status guard already enforces this
boundary. No production change was needed; the resolved finding now has
a successful current-head security check. The regression will be
retained with the next slice.
- Eligibility repair source `892f36e5f66ba1af123034936a7160c64c3ab883`
passes **1489 distinct server cases**: 1139 in six complete
recovery/finalizer/monitor/summary/workspace suites (81.02 seconds),
plus 350 selected chat cases (297.68s). Another 1463 chat cases are
outside the pattern; the complete 1813-case file is not claimed. All
eleven new regressions are included in that count. Five dedicated fresh
PostgreSQL 17 databases migrate through unchanged 0412, with no
fixture-source adaptations. Database build, server types/build,
boundaries and full-SHA migration ordering pass. All seven test source
hashes match.
- The exact published baseline first fails eight behavioral assertions
while the three genuine-answer cases pass. The same corrected test
source passes all eleven after repair: recorded none/reuse remain
suppressed; five ineligible history families take zero recovery row
locks across repeated issue hints; missing/legacy/create-null answers
still publish once; fresh periodic finalization defers presentation to
its worker. Earlier fixture setup and variable/compiler failures remain
explained in the audit, rather than erased by later success.
- Repair source `2fc8b0614aaf9ac7337045de4ced7325f95edc45` passes **1821
server cases**: 1496 in 13 complete producer/lifecycle/authority suites
(110.63 seconds), plus 325 selected chat cases (278.99 seconds).
Coverage includes all native-session cases, queue admission, status
decisions, membership/board access, the complete GitHub workflow,
accepted-response lifecycle and real authority restoration. The other
1477 chat cases are outside this selection; the complete 1802-case file
is not claimed for this repair run.
- Actual PostgreSQL regressions first reproduce a coalesced-input hint
missing its run scope and two routine hint paths producing 12
response-history SELECTs each after three notifications. The same
assertions pass with run-scoped coalescing and zero response-history
SELECTs for unscoped routine hints. Actual membership restoration still
publishes the waiting response once. Assertion time limits are
unchanged; this measures real driver SQL and persisted producer work.
- Eleven dedicated fresh PostgreSQL 17 databases migrate through 0412.
One embedded-only wake-queue fixture import temporarily uses its own
external database and restores byte for byte. All 14 selected source
files match pre-run hashes. This qualifies external-database behavior
rather than reproducing embedded CI locally.
- Master advanced independently with #18126's Pi archive retry fix.
Normal merge `2a535bc61259ef99387369e8b32498f774e5ba2c` preserves both
complete binary patches. All accepted server/database/UI/test/CLI trees
stay identical; the three independent runner installer/manifest files
are retained. All 13 installer tests pass, including stalled header/body
cancellation. Combined server types/build, boundaries and full-SHA
migration order pass. A subsequent normal merge of #18128 at
`e8b78cce5bb557b086bee9ca930eeaab1d09b244` retains its three test-only
file changes with both complete patches preserved. The changed HTTPS
backfill case passes on a fresh PostgreSQL fixture (1 selected case; 178
outside the pattern), and combined types/build, boundaries and migration
order pass again. All 14 repair source hashes remain unchanged. The
independent browser fixture changes are preserved; no local browser run
is claimed.
- The earlier source `9feec170ef8174011fc3c5d3d6d76f0a0f39fea0` passed
the 4171-case full baseline before this scope repair. That scope is
preserved in the audit rather than represented as a current full rerun.
All 0412 database bytes remain unchanged: original concurrent
index/statistics/ANALYZE, prior executable history, journal entries and
retained snapshots. Five snapshots 0408 through 0412 remain. The
populated 0411-to0412 upgrade preserved 410 prior receipts and 38
companies, appended one receipt and none on replay; index/statistic
checks passed.
- The earlier P2 security failure, Greptile's former conditional
dependency on #17702, and the subsequent actual Ponytail eligibility
findings are recorded in
`doc/plans/2026-10-25-native-response-presentation-sleep-audit.md`. This
PR now includes the issue-scoped pending-effect notification fix itself,
so it is independently correct before #17702. Fresh published-head CI,
Greptile 5/5 and a clean security scan remain required. No
deployed-sleep, external-wake or fleet-savings claim is made.


## Risks

- The writer contract is process-local. Independent SQL writers and
other replicas are not covered by these notifications.
- Unpresented backlog reconstruction still has memory and time cost
proportional to candidate count. Pages contain at most 100 rows;
execution uses the shared fair batch. Selected history is excluded.
Health-only signals do not trigger reconstruction.
- Publication policy remains with its current owner. Digest, company/run
binding, source authorization, selected-answer suppression, and
ownership fences remain required.
- The rest of native recovery and governed-review presentation still run
separately. This PR does not relax Cloud sleep eligibility or add
external wake coverage.
- The stack parent has merged and its actual ancestry is reconciled.
This publication still needs fresh current-head CI and review.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository tools, shell
execution, and PostgreSQL tests. The runtime does not expose a more
specific model identifier or context-window size.

## 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>
Co-authored-by: Codex <noreply@openai.com>
2026-10-29 02:31:20 -05:00
a8a038e659 fix(server): schedule native pending effect recovery (#17690)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native status decisions store effect targets and a delivery ledger.
> - Historical pending or failed effects still need recovery.
> - The periodic finalizer queries that ledger even when work is
complete.
> - This pull request gives pending effects their own commit-driven
worker and saved deadlines.
> - Completed history becomes quiet while incomplete effects remain
recoverable.

## Linked Issues or Issue Description

Refs #17687 (merged). This is slice 47 of the sleep migration stack, now
based on master.

**What problem does this solve?**

Periodic native finalization repeatedly checks effect delivery. A tenant
with completed history should not perform recurring SQL for this duty.
Historical unresolved effects must still recover without duplicating
their targets.

**Proposed solution**

Reconstruct current pending effects at startup. Wake from committed
coordinator changes. Use the saved retry and ownership deadlines. Keep
the existing dispatcher as the only recovery mutation owner.

**Alternatives considered**

A slower unconditional scan still creates idle traffic. A new queue
table duplicates existing durable state. Replaying materialization can
duplicate effects. This change uses the existing ledger, transaction
signals, and shared scheduler.

## What Changed

- Add a scoped worker with 100-row discovery pages and fair 25-entry
dispatch batches. Delivered, superseded, and terminal-failure history
has no recurring scan.
- Preserve exact company/issue/run/decision binding and validate
materialized targets before settling effects. Recheck phase, saved
result, retry, and lease under the coordinator lock. Missing targets
retain recovery.
- Notify from decision replay, accepted-result recovery, coordinator
claim/retry/release, attention replay, and execution-control recovery.
Only two DB-only producer writes need a new transaction wrapper. Nested
transactions use the existing outer-commit boundary.
- Exclude pending-effect candidates from periodic finalization so it
cannot skip ahead of their owner. Keep explicit and startup recovery
behavior compatible. Respect maintenance disablement, drain, standby,
and shutdown.
- Add concurrent, idempotent migration 0411 with partial indexes for
pending effects and their current coordinator binding. Retain the five
newest schema snapshots.
- Document producers, query evidence, failure history, and scope in
`doc/plans/2026-10-25-native-pending-effects-sleep-audit.md`.

## Verification

- Source `ba38ee465df02b6026286b5d5e15ae49fe07a367` passes all 2284
distinct cases across 25 complete files, without skips. The main
selection passes 2225 in 203.94 seconds; safe-replacement passes 59 in
26.03 seconds. Coverage includes all 968 native-session cases, the
94-case status-authority/pending-effect corpus, actual decision/lease
producers, Stop, queue, interaction, reconstruction and Slack consumers.
The audit gives per-file counts.
- The complete 94-case corpus passes again on the same database in 16.03
seconds. Retained PSC/OUT rows and an unrelated sentinel remain
unchanged. The repeat is not counted again as distinct coverage.
- Twenty-one separate fresh PostgreSQL 17 databases migrate through 0411
before setup. Four embedded-only fixture imports temporarily use their
own external database on this IPC-exhausted host and are restored byte
for byte. Assertions and timing limits stay unchanged. This qualifies
external-database behavior; it does not claim an unmodified embedded CI
run.
- Database build, server typecheck/build, module boundaries and
exact-head migration order pass. Original runtime/schema edits and
unrelated upstream blobs are preserved. Both original concurrent index
statements, all prior executable history, journal entries and retained
snapshot bytes are unchanged; five snapshots 0407 through 0411 remain.
- Populated 0410-to0411 upgrade preserves all 409 prior migration
receipts and 38 companies, appends one receipt and adds none on replay.
Both indexes are valid and ready on fresh and upgraded databases.
- Adopting the latest prepared prerequisite changes ancestry and parent
audits only. All accepted core trees remain identical. The audit retains
earlier failures and qualifications. Actual landed-parent ancestry is
now reconciled; fresh published-head CI and exact-head Greptile 5/5
remain required. No deployed sleep, Cloud-wake or fleet-cost claim is
made.

- Current parent context: integration
`5f7cd7bc589ad0169dfb5f33df611ead56510ca3` changes only the parent's
GitHub observation bound and independent cleanup assertion. All
production code, packages and 25 accepted test files remain unchanged.
Both focused GitHub idle cases pass in 15.26 seconds and the complete
six-case cleanup suite passes in 8.47 seconds on two fresh 0411
PostgreSQL fixtures. This does not claim the full parent workflow was
rerun here.

## Risks

- A missing producer can delay work until restart. The audit covers
current supported writers. Independent SQL and other processes remain
outside the local notification contract.
- Recovery validates existing targets; it cannot recreate a missing
target or silently mark it delivered. Such work retains an error and
retry.
- The indexes add write/storage cost and use the existing restart-safe
concurrent migration runner.
- Other finalization duties still poll until their separate migrations.
This PR does not prove host/database suspension, extend Cloud alarms, or
claim fleet savings.
- The actual prerequisite squash is adopted through a normal merge. Its
complete tree equals the reviewed parent, and the complete accepted
child tree is preserved. Fresh published-head CI and review remain
required.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning and code-execution tools.
The exact serving model ID and context-window size are not exposed in
this session.

## 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>
Co-authored-by: Codex <noreply@openai.com>
2026-10-29 00:28:00 -05:00
DottaandPaperclip 4e715eb7ea fix(server): schedule historical unsafe export recovery (#17687)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native runs can retain accepted results after an unsafe workspace
export fails.
> - Historical recovery can retire an old notice or resume the accepted
result without another provider turn.
> - The periodic reaper repeatedly scans failures that cannot currently
recover.
> - Existing rows and committed changes can identify the actual recovery
obligation.
> - This pull request wakes recovery from those changes and retries only
owed work.
> - Dormant history becomes quiet while ownership and explicit operator
decisions remain protected.

## Linked Issues or Issue Description

**What happened?**

The native finalization reaper repeatedly visits historical unsafe
workspace-export failures. Cancelled tasks, newer runs, changed
contracts, and explicit operator decisions can make those rows
permanently ineligible. They still produce periodic database reads.

**What did you expect?**

Retire stale unsafe-export notices and recover eligible accepted
results. Stop reading dormant history until a relevant committed change
occurs. Keep a retry while a real obligation is blocked by ownership or
a transient failure.

**Steps to reproduce**

Retain an unsafe-export terminal failure whose task is cancelled or
superseded. Leave the instance idle after startup. The periodic reaper
continues reading that history.

Prerequisite #17680 is merged into master. Related: #14402 established
safe export finalization; #14329 was an earlier historical recovery
proposal. #17679 owns separate policy-review cleanup. This PR changes
only historical unsafe-export scheduling and its producers.

## What Changed

- Register a scoped worker through the existing coordinator and
scheduler. Remove this duty from periodic reaping. Preserve the
manual/startup recovery entry point.
- Distinguish stale notices, eligible accepted results, dormant history,
and concrete ownership contention. Reuse the existing mutation owner and
lock order. Preserve explicit settlement vetoes and exact
result/contract identity.
- Signal relevant task, run, action, and deletion writes before they
commit. Existing transaction boundaries handle nested commits and
uncertain outcomes. Notifications contain only scope IDs.
- Retain a retry only for owed work. Reconstruct work at startup,
preserve drain/resume, and stop through the shared lifecycle.
- Add concurrent, idempotent partial index migration 0410 for unsafe
terminal failures. Keep the five newest generated schema snapshots.
- Document producer coverage, query plans, supported writer boundaries,
and test failures in the unsafe-export audit.

## Verification

- Source `2a13dd08a6cc52f0adab09e3df9ce31e97b746b9` passes all 2188
cases across 24 complete files, without skips. The main selection passes
2129 cases in 192.58 seconds; safe-replacement passes 59 in 26.04
seconds. Coverage includes all 968 native-session cases, real
export/action producers, explicit continuation, workspace
reconstruction, interaction routing, Stop, queue and Slack consumers.
The audit gives per-file counts.
- Twenty separate fresh PostgreSQL 17 databases migrate through 0410
before setup. Four embedded-only fixture imports temporarily use their
own external database on this IPC-exhausted host, then are restored byte
for byte. Assertions and timing limits stay unchanged. This is qualified
external-database acceptance, not an unmodified embedded CI run.
- Database build, server typecheck/build, module boundaries and
exact-head migration order pass. Normal integration preserves the
complete original runtime/schema patch and unrelated upstream blobs.
Original concurrent index SQL, all prior executable history, journal
entries and retained snapshot bytes are unchanged; five snapshots 0406
through 0410 remain. Added comment removal preserves the full
non-comment TypeScript token streams.
- A populated 0409-to0410 upgrade preserves all 408 prior migration
receipts and 38 companies. It appends exactly one receipt; replay adds
none. The index is valid and ready on fresh and upgraded databases.
- The owning audit retains earlier fixture failures, skipped checks and
notice-retirement regression history. Older combined stack checks retain
their original scope in their owning audits. Actual parent
`73d24aa5acfcf17b3f676b4da7cd05eb70b60b99` is reconciled through a
normal merge. Its tree equals the reviewed ancestor and the complete
accepted child tree is unchanged. Fresh published-head CI and Greptile
5/5 remain required. No deployed sleep, fleet-cost or live provider
qualification claim is made.

- Inherited prefix correction: current integration
`8f43c9f81fac23b50997e7f0e1d0d9cf383a8109` changes only two values in a
previously unselected corpus fixture. All production code, the complete
package tree and all 24 accepted test files remain byte-identical to the
2188-case source above. The additional complete 72-case corpus passes in
14.00 seconds and again on the same database in 13.08 seconds. Retained
PSC/OUT and sentinel rows remain unchanged. The repeat is not
double-counted. The parent audit retains the original collision and
rejected cleanup attempt.

- Latest parent context: integration
`4d51231abe8b68c21ce896d0d2ac16865978c8ab` carries only the parent's
one-argument GitHub observation correction and an independent master
cleanup assertion. All production code, packages, 24 accepted suite
files and the additional corpus are unchanged. The two focused
native-presentation cases pass in 15.14 seconds and the complete
cleanup-removal suite passes all six cases in 8.31 seconds on two fresh
0410 PostgreSQL fixtures. This does not claim the full parent workflow
was rerun here.

Current head `b28399babac72ba9db1c4a4ef882c5e87aded38c` has passing CI
and Greptile 5/5 with no actionable findings. The first CI attempt lost
runners: fourteen jobs explicitly received simultaneous shutdown
signals, two reported lost runner communication, and two aggregate gates
failed downstream. One failed-job retry on unchanged source passes. The
underlying host shutdown cause remains unconfirmed; no code fix or extra
retry is claimed. [CI run and
attempts](https://github.com/paperclipai/paperclip/actions/runs/40819408528).
No Ponytail verdict is present.

## Risks

- A missing future producer can delay dormant-history discovery until
restart. The audit names current producers. Independent SQL writers and
other server processes remain outside the process-local signal contract.
- Physical ownership and uncertain operations retain retries. Time alone
does not authorize taking workspace ownership. No provider turn or
unsafe archive retry is added.
- The recovery owner remains authoritative if state changes after
discovery. Terminal UI status does not authorize resource destruction or
accepted-result replay.
- The partial index adds matching-row write/storage cost. It builds
concurrently through the existing restart-safe migration runner.
- This proves the changed worker becomes quiet. It does not prove host
suspension, external wake coverage, or fleet cost savings. Other sleep
migrations remain. This slice still requires fresh current-head CI and
reviews.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning and code-execution tools.
The exact serving model ID and context-window size are not exposed in
this session.

## 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-29 00:13:22 -05:00
DottaandPaperclip 51c3f8924c fix(server): schedule cancelled workspace preservation (#17677)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A cancelled native run may still owe preservation of prepared
workspace files.
> - The existing recovery receipt and source lease record that
obligation.
> - The orphan reaper still searches for these cancellations on every
pass.
> - This PR registers committed cancellations with the shared deadline
worker.
> - The existing retention owner keeps file pinning and ownership
checks.
> - Idle and completed history no longer causes this preservation scan.

## Linked Issues or Issue Description

Refs #17674 and #17145. Stopped instruction collection is merged into
master. It owns file-preservation discovery only. The existing
environment cleanup worker continues to own stop-only teardown.

**What needs to be improved?**

`preserveCancelledNativeWorkspaces` runs from every orphan-reaper pass.
It queries even when no cancelled native workspace needs a file-recovery
hold. Current foreground Stop can also defer retention after a lock or
database failure, so startup-only recovery would be insufficient.

**Why is this improvement important?**

Empty queries keep idle tenants active. Removing them must not lose user
files, weaken Stop ownership, replay cancelled work, or turn a retained
allocation into destructive cleanup.

**Proposed solution**

Register the existing durable obligation from committed run-status,
cancellation-acknowledgement and workspace-reference writes. Reconstruct
it at startup. Keep retries while actual preservation remains
unresolved. Disarm after the existing owner records the hold. Related-PR
search and ROADMAP.md review found no duplicate of this scheduling
migration.

## What Changed

- Add a company/run-scoped preservation worker with 100-row keyset
reconstruction and fair batches of five.
- Separate pending file debt from immediate coordinator/lease readiness.
Retain a 30-second retry for known unfinished preservation and shared
failure backoff for errors.
- Signal the existing status, acknowledgement, workspace-reference,
deletion and settings transactions. Add no table or independent timer.
- Keep exact source checks, no-workspace contracts, task/coordinator/run
lock order, retention receipt and no-replay recovery card with their
owners.
- Recheck cancellation acknowledgement, run status and coordinator phase
under the retention locks. Preserve the existing foreground preterminal
API.
- Keep startup/manual recovery; skip only this duty in periodic reaping.
Preserve drain/standby and shutdown behavior.
- Add concurrent, replay-safe partial index migration 0409 and retain
the five newest snapshots.
- Add the producer and lifecycle audit in
`doc/plans/2026-10-25-cancelled-workspace-preservation-sleep-audit.md`.

## Verification

- Current source `21b46a8e4293ca670235117458b984493a181759`: all 1472
cases in 16 complete files pass without skips. The owner passes 84 cases
in 14.93 seconds. Fifteen consumer files pass 1388 cases in 133.46
seconds, including all 968 native-session cases and Stop, dispatch,
restart, workspace-sync and upgrade-readiness coverage.
- Twelve separate fresh PostgreSQL 17 databases migrated through 0409
before setup. The embedded-only lifecycle fixture temporarily used its
own external database on this IPC-exhausted host and was restored byte
for byte. Assertions and timing limits stayed unchanged. This qualifies
external-database behavior; it does not claim an unmodified embedded CI
run.
- Database build, server typecheck/build, module boundaries and
exact-head migration order pass. The complete original runtime/schema
patch and unrelated current-parent blobs are preserved. Original
concurrent index SQL, all earlier executable history and retained
snapshot bytes are unchanged; five snapshots 0405 through 0409 remain.
- A populated 0408-to0409 upgrade preserves all 407 prior receipts and
38 companies. Exactly one receipt is appended; replay adds none. The
preservation index is valid and ready on fresh and upgraded databases.
- Actual parent `82529741418e138d9d623bf13d2fa22c1ff79d36` is merged
through a normal ancestry reconciliation. Its complete tree equals the
reviewed parent. The entire accepted child tree and all source hashes
are preserved. The audit retains earlier failures, qualifications and
per-file counts. Published head
`b34f0589847f33754ecde9413fd36bf007353d2c` has Greptile 5/5 and all 57
checks passing or skipped. No Ponytail review is present. These checks
do not establish deployed host/database suspension or Cloud alarm
delivery.

## Risks

- Signals cover instrumented single-process writers. Independent SQL
writers and additional replicas need integration.
- Status signals may inspect a run without relevant file debt once. They
do not create recurring work. Discovery and memory still scale with
actual candidates.
- Missing ownership or stop evidence remains pending. Active retries are
intentional while a concrete file obligation is unresolved.
- Retry timing is process-local; startup reconstructs durable debt. The
worker does not overwrite another native-finalizer deadline.
- Admitted database operations may finish after drain begins. Shutdown
joins them. Provider teardown remains with the existing cleanup owner.
- The partial index adds storage and write cost. Its concurrent
migration uses the existing interrupted-index repair path.
- Cloud alarm coverage, sleep eligibility and host suspension are
unchanged. This proves worker behavior, not fleet savings.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, shell tools, and PostgreSQL
tests. The runtime does not expose a more precise model ID or
context-window size for this turn.

## 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-28 22:27:11 -05:00
DottaandPaperclip 8252974141 fix(server): schedule stopped instruction collection (#17674)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Runs can stop while their private instruction edits still need
collection.
> - Existing copy rows and stop evidence describe that unfinished work.
> - The orphan reaper still queries for it on every maintenance pass.
> - This PR activates collection from committed copy and run
transitions.
> - It uses saved retry deadlines when stop proof or collection remains
pending.
> - Active copies and completed history no longer cause collection
polling.

## Linked Issues or Issue Description

Refs #17671 and #17670. Both prerequisites are merged into master.
Captured saves and post-save release remain separate reasons with their
own workers.

**What needs to be improved?**

`recoverStopped` queries instruction copies on every orphan-reaper pass,
even when no terminal run owes collection. A run can become terminal
without changing its copy row, so copy-only notifications would miss
real work.

**Why is this improvement important?**

Repeated empty queries prevent idle tenant instances from becoming
quiet. Removing them must preserve edits, stop proof, current ownership,
and recovery after restart.

**Proposed solution**

Use the existing transaction signals, scoped deadline worker, and
scheduler. Reconstruct eligible copies at startup, signal supported copy
and terminal-run writers, and retain saved retry deadlines for
unresolved work. Preserve the existing collection and directory owners.

Related PR search found the warm agent-file ownership work (#14695),
verified stop boundary (#14311), and task-workspace cleanup (#16464).
This PR does not replace those contracts. ROADMAP.md has no duplicate
migration.

## What Changed

- Add scoped stopped-instruction collection with keyset reconstruction
and fair batches of 20.
- Signal before copy preparation, mutation and adoption writes, and
before terminal run writes in their existing transactions.
- Cover heartbeat, native finalization/restart/session recovery,
execution-control, queue and dispatch producers.
- Signal settings changes and company/agent deletion. Preserve drain,
standby, restart and shutdown ownership.
- Recheck terminal-run eligibility in the due-time claim. Keep terminal
status separate from process-stop proof.
- Retain existing stop checks, attempt limits, saved candidate bytes and
remote-resource fencing.
- Skip only this lane in periodic orphan recovery. Preserve
manual/startup recovery.
- Add concurrent, replay-safe partial index 0408 and retain the five
newest snapshots. Add no table.
- Record the owner and producer audit in
`doc/plans/2026-10-25-stopped-instruction-collection-sleep-audit.md`.

## Verification

- Source `cb2818a8f46ef961f744977a258f0e9042e31a42`: all 1469 cases
across 14 complete files pass without skips in 141.69 seconds. This
includes all 968 native-session cases, filesystem saves, startup,
terminal-run producers, dispatch, queue, execution-control, finalization
and lifecycle.
- Eleven separate fresh PostgreSQL 17 databases migrated through 0408.
The embedded-only lifecycle fixture temporarily used its own external
database on this IPC-exhausted host and was restored byte for byte.
Assertions and timing limits stayed unchanged. This qualifies
external-database behavior; it does not claim an unmodified embedded CI
run.
- Database build, server typecheck/build, module boundaries and
exact-current migration order pass. The original concurrent index SQL is
unchanged. All prior executable migration history, journal entries and
retained snapshot bytes are preserved; the five newest snapshots are
0404 through 0408.
- A populated 0407-to0408 upgrade preserves all 406 prior migration
receipts and 38 companies. It appends exactly one receipt. Replay adds
none. The collection index is valid and ready on fresh and upgraded
databases.
- The normal merge of actual parent
`267842fc3c6e6542deed8a1bcc2c78bec8df7862` preserves both complete
patches, all independent sidebar changes from #18117, and the full
accepted server/package/test trees. The complete sidebar test file
passes all 36 cases on the combined tree. React act warnings remain.
- Prior failures and fixture qualifications remain in the audit.
Published head `d176d731f083c7edd2ede688cb901d1e66d8f1d8` has Greptile
5/5, all 57 checks passing or skipped, and no unresolved review threads.
No Ponytail review is present. These local tests do not establish
deployed host/database suspension or Cloud alarm delivery.

## Risks

- Process-local signals cover instrumented writers. Independent SQL
writers and additional replicas still need integration.
- Terminal-run signals can cause one scoped inspection even if no
instruction copy exists. They do not create a recurring scan.
- Reconstruction and memory scale with actual pending candidates; active
candidates are filtered by the run join.
- Admitted file/provider operations can outlive a later drain request.
Shutdown joins them. Uncertain ownership remains pending rather than
being discarded.
- The partial index adds storage and write cost. Its concurrent
migration uses the existing interrupted-index repair path.
- Cloud alarms, sleep eligibility and provider suspension are unchanged.
Worker quiet is not a claim of deployed host/database sleep or measured
savings.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, shell tools, and
PostgreSQL/filesystem tests. The runtime does not expose a more precise
model ID or context-window size for this turn.

## 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-28 22:13:57 -05:00
DottaandPaperclip 267842fc3c fix(server): schedule owed agent directory cleanup (#17671)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent runs can save their directory before temporary-file cleanup
finishes.
> - The existing receipt records stop proof and unfinished cleanup.
> - The orphan reaper still searches for this debt on every maintenance
pass.
> - This PR activates cleanup from committed changes and saved retry
deadlines.
> - The directory owner keeps its resource checks and file lock.
> - Settled directory history no longer causes recurring cleanup
queries.

## Linked Issues or Issue Description

Refs #17670, the merged captured-instruction commit slice. This PR
targets master. It does not include stopped instruction collection or
change the saved-file contract from #14695.

**What needs to be improved?**

The post-save part of `recoverCaptured` queries directory copies on each
orphan-reaper pass, even after all temporary directories have been
released.

**Why is this improvement important?**

Empty database work prevents idle tenant instances from becoming quiet.
Cleanup must still survive a crash after a save and preserve files when
stop or resource ownership is uncertain.

**Proposed solution**

Use existing copy receipts as the source of truth. Notify the shared
coordinator inside relevant copy-write transactions. Reconstruct pending
work at startup and register each saved retry deadline. Keep physical
cleanup in the directory owner. Add a partial index over unfinished
cleanup rather than a new queue table.

Related-PR search found workspace archive and SSH task-workspace cleanup
changes (#17175 and #16464). They own different resources and do not
replace this stopped agent-directory receipt recovery. ROADMAP.md has no
duplicate migration.

## What Changed

- Add a scoped directory cleanup worker with keyset discovery and
batches of 20.
- Signal relevant copy transitions, company/agent deletion, and worktree
execution policy changes.
- Preserve the due-time claim, current receipt read, directory lock,
stop proof, and exact remote-resource checks.
- Read the receipt after release; a returned promise does not establish
cleanup completion.
- Keep unresolved cleanup on its saved retry or shared failure backoff.
Final receipts disarm the worker.
- Register the worker during startup and skip only this lane in periodic
recovery. Keep manual recovery compatible.
- Add concurrent, replay-safe partial index migration
`0407_greedy_star_brand.sql`. Retain the five newest schema snapshots.
- Add the producer audit and lifecycle evidence in
`doc/plans/2026-10-25-directory-cleanup-sleep-audit.md`.

## Verification

Published head: `210fcb1215a3b5e5ed84e420ddb73ab5952d9365`. Source
acceptance: `70a6800eea874bdb87685bf6eea66de79c48b577`. Parent #17670
landed as `b294de546bf9cd3a8578a7a66d7e8ec0eb939799`; normal
squash-ancestry reconciliation preserves the entire accepted descendant
tree. Final follow-ups change audits only.

- All 284 cases in six complete filesystem, startup and heartbeat owner
files pass, without skips, in 48.88 seconds. Four unused fresh
PostgreSQL 17 databases migrated through 0407 isolate destructive
fixtures.
- Three fixtures use supported external database paths. The
embedded-only lifecycle fixture temporarily uses its own external
database on this IPC-exhausted host, then is restored byte for byte. All
assertions and original timing limits remain unchanged. This qualifies
the local result; CI must still pass with the original embedded fixture.
- Actual database build, server typecheck/build, module boundaries and
exact-current migration order pass. The original concurrent idempotent
index SQL bytes are unchanged. All prior executable migration history,
journal entries and retained snapshots stay intact. The five snapshots
are 0403 through 0407.
- Populated 0406-to0407 upgrade preserves all 405 old receipts and all
38 companies, adds exactly one receipt and adds none on replay. The
cleanup index is valid and ready on fresh and upgraded databases. The
entire current database tree equals the source used for this proof.
- Both complete original and upstream patches are preserved. The actual
parent squash tree equals the already-adopted reviewed parent; resolving
eleven duplicate-ancestry text conflicts leaves the whole accepted tree
unchanged, not just selected files. Complete server, package, UI and
test trees still match the 284-case source acceptance.
- The audit retains earlier ownership, stopped-resource, receipt,
fixture and local-runner failures. The no-projects build filter was not
counted as a build pass; corrected actual build and complete reruns
passed before publication. Old draft review scores do not count for this
new head.
- Current-head Greptile gives 5/5 with no actionable findings, and all
57 checks pass. These checks establish worker behavior, not deployed
host/database suspension or measured savings.

## Risks

- Signals cover the existing single-process writer model. Independent
SQL writers and replicas need separate integration.
- The worker retains identifiers for actual pending work. Its index
excludes completed history but does not make large pending backlogs
free.
- File/provider cleanup can outlive an admission change once started.
Shutdown joins admitted operations. Unknown remote ownership remains
pending and no replacement environment is acquired for cleanup.
- The partial index adds storage and write cost. Its concurrent
migration uses the existing interrupted-index repair path.
- Cloud alarm coverage and host-sleep safety remain unchanged. This
establishes worker quiet, not deployed host/database suspension or
measured savings.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, shell tools, and
PostgreSQL/filesystem tests. The runtime does not expose a more precise
model ID or context-window size for this turn.

## 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-28 21:51:47 -05:00
DottaandPaperclip 27270e9bf8 fix(server): wake interrupted retry cancellation from committed intent (#17667)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A parked scheduled retry has no executor to finish an interrupted
Stop request.
> - Its saved cancellation fence already records the work that remains.
> - The orphan reaper currently scans for that work on every heartbeat,
including idle periods.
> - This PR registers the existing records with the shared delivery
scheduler and wakes recovery after committed writes.
> - Recovery keeps the existing cancellation guards and settlement
behavior, then becomes quiet.

## Linked Issues or Issue Description

Refs #17666, the merged preceding PR in the recurring-server-work
migration stack. This PR targets master. Related cancellation work found
in the dedup search: #12301 and #7340. This PR changes the trigger for
interrupted parked-retry cancellation; it does not replace their
cancellation or retry policies.

**What happened?**

The periodic orphan reaper queries scheduled retries with cancellation
fences even when no cancellation needs recovery. Its first-20-row scan
can also stop behind malformed evidence.

**Expected behavior**

Committed cancellation intent wakes bounded recovery. Failed work
retains a retry. Startup reconstructs durable work. Empty or settled
work performs no recurring SQL for this duty.

**Steps to reproduce**

1. Start a server with heartbeat scheduling enabled and no interrupted
retry cancellations.
2. Observe the orphan reaper repeatedly query for cancellation fences.
3. Cancel a scheduled retry and fail the final status write after its
fence commits. Recovery must complete the existing Stop request without
running a provider or dropping its metadata.

**Paperclip version or commit**

Current parent squash `7afee8f0622e273d10506b0febfb68f143d168b1`. Source
acceptance `66107ae0a7ad85fd0476f91c4f8f98ae74a7a048`.

**Deployment mode**

Self-hosted server with the existing single-process transaction signal
model. Local evidence uses PostgreSQL 17.

## What Changed

- Add one domain-owned recovery worker over existing heartbeat run rows.
No new table. It delegates to run control and preserves original
cancellation, budget, and operator metadata.
- Signal cancellation fences, lifecycle transitions, dispatch
deferral/promotion/suppression, relevant settings changes, and deletion
within their owning transactions. Wrap only the ordinary database fence
in a short transaction; provider calls stay outside it.
- Keep startup/manual recovery compatible and remove this duty from the
periodic orphan sweep. Other reaper duties remain for separate PRs.
- Extend the shared scoped worker to retain pending work with a
memory-only admission retry while its owner is paused. Without pending
work, it schedules nothing. Existing GitHub consumers retain their
behavior.
- Add a concurrent idempotent partial index and lifecycle/producer
tests. Enable guarded disposable PostgreSQL URLs in affected test
harnesses.
- Reconstruct one hundred candidates per scope per scheduler pass.
Resume from the saved scope cursor and retain fair twenty-item execution
batches. Manual recovery starts cancellation before reading the full
backlog.
- Require the recovered browser reply to be saved before testing reload.
Keep all original assertions and time limits.
- Record the full producer audit, lifecycle limits, and failed attempts
in `doc/plans/2026-10-25-scheduled-retry-cancellation-sleep-audit.md`.

## Verification

Published head: `405e533394b26215e22d0452e9b0207d3f784a79`. Source
acceptance: `a35fa43b98e2bd378d4a920d2775ce243654838b`. The final commit
changes the audit only.

- All 363 backend cases in ten complete owner and consumer files pass
without skips. This includes actual Stop/retry producers, transaction
settlement, interrupted-write recovery, pause/resume, shutdown, fair
backlog processing and quiet after settlement. Seven separate fresh
PostgreSQL 17 databases prevent fixture interference.
- The two embedded-only backend fixtures temporarily use separate
external databases on this IPC-exhausted host. Their fixture imports and
native client teardown are restored byte for byte afterward. Assertions
and timing limits are unchanged. CI must still pass with the original
embedded fixtures.
- Two paging regressions fail on the prior worker and pass on the fix.
They cover cancellation before full reconstruction, a malformed first
page, fair batches, a newly committed intent behind the cursor, and no
later idle SQL. Per-candidate admission reads remain deliberate safety
checks.
- The repeated CI reload failure is reproduced with a temporary
twelve-second fixture settlement delay. The trace retains the same
five-second missing-reply assertion, even though a cleanup race masked
it in the top-level error. Requiring exactly one saved reply from the
resumed run passes under the identical delay. The fault is removed. All
six browser journeys pass on the final source in 40.5 seconds, with zero
retries and the original 120-second case and five-second reload limits.
- Browser acceptance uses the canonical CLI/server/static-UI path and a
fresh external database. Only fixture database-URI setup is temporarily
adapted and then restored. This is not an unmodified embedded
CI-environment reproduction. The six cases include the controlled case;
repeated runs are not added to the distinct count.
- Actual database build, server typecheck/build, module boundaries and
exact-current migration order pass. The database source tree is
unchanged from the verified 0405-to0406 upgrade: all 404 prior receipts
and 300 companies stay intact, exactly one receipt is added, replay adds
none, and the index is valid on upgraded and fresh databases. The five
snapshots remain 0402 through 0406.
- All earlier failures remain recorded in the audit. Prior head
`48b3fcb83f305616df02a4337a42ec0867bb4b80` had exact-head Greptile 5/5
but failed the repeated browser assertion. It was not retried blindly.
Its completed Ponytail review's backlog concern and old migration
paragraph are addressed here. Old scores do not satisfy new-head gates.
- Current-head CI passes. Exact-head Greptile and Ponytail both give
5/5, with no remaining actionable findings. These checks do not prove
deployed instance or database suspension.

## Risks

- Only supported in-process writers produce local transaction signals.
Independent SQL writers or other replicas need a separate ingress
contract. Startup reconstructs saved obligations.
- A real failure retains bounded retries. Unknown commit outcomes retain
the shared intent hold. Neither is discarded to report quiet.
- Per-owner drain uses a memory-only admission recheck. The shared
helper change affects existing consumers, which have focused regression
coverage.
- The partial index is concurrent and idempotent; it adds index storage.
Each scoped load reads at most one hundred rows. The shared helper
retains discovered pending IDs between passes, so a large unresolved
backlog still uses memory.
- Cloud alarms, idle-safety exemptions, and other reaper duties remain
separate. Merge requires passing current-head CI and review gates.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, shell tools, and local
PostgreSQL tests. The runtime does not expose a more precise model
identifier or context-window size for this turn.

## 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-28 21:07:55 -05:00
DottaandPaperclip 8bc051245f fix(server): wake stale GitHub review repair from committed changes (#17664)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/215/changes/paperclip/215
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - GitHub checks and task admission can finish in either order.
> - A check can reject an old head before admission creates its review
row.
> - The chat maintenance loop repeatedly scans for that late review.
> - Existing cancellation and delivery receipts already identify the
repair.
> - This pull request wakes repair from committed changes and leaves
settled history quiet.

## Linked Issues or Issue Description

**What happened?**

GitHub chat maintenance repeatedly scans all review projections. Empty
instances and retained history incur database work even when no repair
is owed.

**What did you expect?**

A committed stale-head cancellation or admitted review should wake
repair. Only the exact delivery's unassessed queued/running review
should become superseded. When no repair remains, this duty should stop
making database calls.

**Steps to reproduce**

Configure a GitHub chat endpoint and leave it idle after all reviews
settle. The previous maintenance callback continues to call
repairSupersededReviews. A rapid push can also cancel a check before
admission inserts its review, which explains why recovery must remain.

Refs #17662, the merged review-check publication parent. Related #17661
and #17656 migrate terminal responses and acknowledgements. This slice
owns only late stale-head projection repair.

## What Changed

- Move the repair predicate and execution into the GitHub provider
owner. Match the original admitted delivery and cancellation receipt.
Recheck state and assessment in the update.
- Register a scoped worker with the existing coordinator. Use keyset
pages and fair bounded dispatch. Keep retries only for unresolved repair
or failed reconstruction.
- Signal before cancellation, automatic receipt insertion,
automatic-review binding, and check-driven review-state writes. Preserve
existing transaction ownership and outer commit behavior.
- Remove the periodic provider wrapper and publication-maintenance
callback. Preserve explicit manual recovery and independent
missed-webhook recovery.
- Add two concurrent, idempotent partial indexes. Use a guarded delivery
UUID lookup and a bounded exact-receipt lookup. Add no tables.
- Add ten real-database lifecycle and producer cases. Document the
producer audit, failure history, and remaining limits.

- Keep YAML browser reads scoped to the editor after focus changes, with
the original journey assertions and deadlines.

## Verification

Current source: `0886a0229b1c4f68d2f582da86aa1f248e9124f3`.

- At `dd6bca0485c3444d6e3d0ee10ec4aa651e801392`, **287 distinct cases
pass**: 274 complete GitHub workflow cases and 13 startup cases. The
GitHub command filters out 1,485 unrelated cases. The workflow suite
uses a new, pre-migrated PostgreSQL database.
- Coverage includes both orders of cancellation and admission, outer
commit and rollback, restart, drain, standby, failed-update retry,
concurrent assessment under an observed row lock, shutdown during that
wait, more than one candidate page, manual history, and settled-history
quiet.
- Database build, server typecheck/build, module boundaries, and target
migration order pass.
- Canonical migration `0403_natural_cerise.sql` preserves both original
index definitions and concurrent idempotent creation. All 401 parent
migration receipts remain unchanged during upgrade. One new receipt is
added. Replay adds none. Both indexes are ready and valid on fresh and
upgraded databases. The five newest snapshots remain.
- The normal parent merge preserved the prepared tree. The later browser
fixture repair changes only its central read helper and verification
note. Entire server, package, and UI trees remain identical to the
accepted source.
- [The owning
audit](https://github.com/paperclipai/paperclip/blob/0886a0229b1c4f68d2f582da86aa1f248e9124f3/doc/plans/2026-10-25-github-review-projection-sleep-audit.md)
records the exact evidence and earlier failures. Earlier query-plan
measurements retain their historical revision scope.
- Fresh current-head CI and Greptile review remain required. No deployed
sleep, full repository-suite, or fleet-savings claim is made.

Failure accounting: browser shard 7 failed twice on
`1453a4447c0d4d33f713ae3e00734c7950f95426` because the YAML helper read
whole-page clipboard text while main was focused. Three isolated
unchanged local passes did not resolve that failure. A controlled native
focus-loss probe reproduced the same error before the repair and passed
after it. The helper now clears stale clipboard data, focuses the
editor, and waits within the existing expectation budget for a fresh
YAML copy. All six complete YAML journeys pass with zero retries in 50.9
seconds. Journey assertions and deadlines are unchanged. The temporary
fault is removed. The exact CI focus-change trigger remains unknown.
[Both CI
attempts](https://github.com/paperclipai/paperclip/actions/runs/40777754306)
remain part of the verification record. Fresh new-head checks are
required.

Ponytail follow-up: the 4/5 review of `b3c81ba` was conditional on
fixing a stale startup-test slice marker, so the merge was held.
Boundary assertions reproduced the invalid -1 end index. The test now
uses the existing timer declaration and checks both bounds; all 13
startup/hostname cases pass. The optional no-op coordinator cleanup is
already in #17666. The six YAML journeys and all production code remain
unchanged. Fresh review and CI are required for this corrected head.

## Risks

- Missing a future producer signal could delay discovery until restart.
Supported writers share the current database owner's process-local
notification scope. Independent SQL writers and other server processes
need their own integration.
- The migration builds indexes concurrently. The existing migration
runner handles interruption and invalid index recovery.
- Direct GitHub missed-webhook discovery remains periodic. This change
does not make the whole chat service idle.
- Existing Cloud wake coverage and sleep blockers remain unchanged.
These tests do not prove deployed host or database suspension.
- Each remaining sleep slice needs its own review and CI checks before
merge.

## Model Used

OpenAI Codex (GPT-6), with reasoning and code execution. The exact
serving model ID and context-window size 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
- [ ] All Paperclip CI gates are green
- [ ] 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-28 18:42:54 -05:00
DottaandPaperclip 65fa1cd617 fix(server): schedule GitHub review checks from committed changes (#17662)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/213/changes/paperclip/213
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - GitHub review checks report the state of admitted agent work.
> - The chat maintenance loop searches for check work even when none
remains.
> - Existing action rows already record unfinished publication and retry
deadlines.
> - Committed changes can wake those rows through the shared
coordinator.
> - An unresolved check must still detect a remote head change when a
webhook is missed.
> - This pull request schedules only outstanding checks and leaves
settled history quiet.

## Linked Issues or Issue Description

**What happened?**

The GitHub chat maintenance callback repeatedly queries review-check
actions. Empty queues and completed history still incur database reads.
Local state changes wait for the maintenance callback.

**Expected behavior**

Committed check creation, review binding, run changes, and
publication-lease release wake the worker. Existing retry and
remote-head deadlines apply only to unfinished checks. A settled worker
makes no recurring database or provider calls.

**Steps to reproduce**

Configure a GitHub bot with no outstanding checks, or finish all of its
checks. Leave the instance idle. The old maintenance callback continues
to call the check service.

Refs #17661, the merged terminal-response parent. Related #17656
migrates response acknowledgements. Related #16385 changes branch-policy
admission; this PR preserves that separate policy boundary. Late
stale-head projection repair remains a separate follow-up slice.

## What Changed

- Move the existing check-state reader into the GitHub provider owner.
Preserve current authority, run selection, assessment ownership, and
check replacement behavior.
- Register a scoped worker with the existing coordinator and scheduler.
Existing received and retryable-failed actions remain durable truth. No
new table is added.
- Signal check enqueue, task review receipts, manual review
insertion/binding, run lifecycle changes, and publication-lease release
through existing transaction ownership.
- Preserve the saved failure backoff, eight-attempt policy, and
competing lease expiry. Keep five-second remote-head reconciliation only
while a received check remains unresolved.
- Check admission before each HTTP request. Shutdown leaves durable work
recoverable without spending a provider failure attempt.
- Remove check dispatch from the periodic GitHub callback. Keep the
separate late-projection repair and the manual combined recovery method
compatible.
- Add an idempotent concurrent partial index for unfinished check
selection. Retain the newest five generated snapshots.
- Add eleven real-database worker cases and startup wiring coverage.
Document producer coverage and the external reconciliation limit.

## Verification

Current reviewed source: `2bba08403b195bf646eda48992b46d6f5cebf756`.

- Normal merges preserve the complete review-check patch, the landed
terminal-response parent, and current master changes. No landed SQL
migration or journal entry changes.
- At `6995f84b240a14a7b8660e1cf095b3aa1e1e3168`, **331 distinct tests
pass**: 264 complete GitHub workflow cases, 30 completion cases, eight
issue-tree producer cases, 16 login-cleanup cases, and 13 startup cases.
The GitHub command filters out 1,485 unrelated cases. Login cleanup uses
its real embedded PostgreSQL fixture.
- Combined-master acceptance: **277 distinct tests pass** at
`9c538aed1251fd7f7cecb829a0ecbce6110fb1ed`: 264 complete GitHub workflow
cases and 13 startup cases. The GitHub command filters out 1,485
unrelated cases. A new PostgreSQL database receives all migrations
through 0402..
- Migration `0402_ancient_malice.sql` preserves the original partial
index definition and concurrent idempotent creation. All 400 prior
migration receipts stay unchanged during upgrade. One new receipt is
added. Replay adds no receipt. The index is ready and valid on fresh and
upgraded PostgreSQL databases. The five newest snapshots are retained.
- A rollback-only query probe adds 10,000 processed actions and one
pending action. The actual parameterized candidate predicate selects the
one pending row through the partial index, with two shared buffers and
0.007 ms execution time. This measures candidate discovery, not all
lease or provenance reads.
- Database build, server typecheck and build, module boundaries,
migration safety and migration order pass. Audit: [GitHub review-check
scheduling](https://github.com/paperclipai/paperclip/blob/2bba08403b195bf646eda48992b46d6f5cebf756/doc/plans/2026-10-25-github-review-check-sleep-audit.md).
- Fresh checks and Greptile review are required for this head. Prior
scores do not count. These checks do not prove deployed host suspension
or fleet savings.

## Risks

- Unresolved queued/running checks still make remote-head requests at
the existing five-second deadline. GitHub webhook receipt does not prove
future pushes cannot be missed. This PR does not claim active reviews
become network-idle.
- Run and configuration hints rebuild outstanding checks for the
affected company. Enqueue, review, and lease hints narrow to the
affected endpoint/repository/PR. Startup and scope reconstruction read
existing pending rows; they are not constant-cost operations.
- Delivery admission failures remain discoverable through the unresolved
check's external reconciliation deadline. Not every shared delivery
mutation gains a new immediate signal.
- Notifications use the existing process-local database ownership
contract. Independent SQL writers and other replicas need their own
supported integration.
- Provider markers, publication leases, and saved actions preserve
recovery after uncertainty. This change does not expand Cloud alarm
coverage or prove deployed host/database suspension.
- Each remaining sleep slice needs its own review and CI checks before
merge.

## Model Used

OpenAI Codex (GPT-6), with reasoning and code execution. The exact
serving model ID and context-window size 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
- [ ] All Paperclip CI gates are green
- [ ] 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-28 17:08:27 -05:00
DottaandPaperclip f8226b0515 fix(server): wake GitHub terminal responses from committed changes (#17661)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/210/changes/paperclip/210
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip coordinates agent work and presents results in chat.
> - GitHub acknowledgements already persist the response action and
provider receipt.
> - A terminal run can still owe a final response after acknowledgement.
> - Periodic maintenance searches those rows even when no relevant state
changed.
> - Committed run, issue, presentation, lease and publication changes
identify when recovery can advance.
> - The owner should recover from those events and real deadlines, then
become quiet.

## Linked Issues or Issue Description

Refs #17656, which is merged. This existing terminal-response slice
targets master.

**Problem:** A saved GitHub response can remain nonfinal while its run,
native presentation, publication or source is unresolved. Removing
maintenance without covering those producers loses recovery. Polling
settled history keeps the tenant active.

**Expected behavior:** Committed supported changes wake the terminal
owner. It reconstructs existing actions, preserves provider authority
and pending publication ownership, waits at saved deadlines where
needed, and becomes quiet after settlement.

## What Changed

- Register terminal GitHub response work with the shared scoped deadline
coordinator, using existing action/delivery/publication/run rows.
- Notify response availability from supported run milestones, native
identity, recovery resolution, issue updates and tree/workspace
restoration writes. Preserve outer transaction commit/rollback.
- Keep GitHub response interpretation in its platform owner. Use the
bounded run projection for both direct authorization and scheduling,
including native presentation guards and deliberate passive outcomes.
- Retain the existing publication lease, markers, source/principal
checks and final receipt precedence. A publication owner or uncertain
outcome keeps its existing authority; the worker does not replay it.
- Remove only recurring terminal-response recovery, preserving the
manual path and neighbouring duties. Add no table or scheduler service.
- Regenerate the original sparse terminal-work index as
`0401_yummy_ultimates.sql`, concurrently and idempotently through the
existing top-level migration runner. Preserve all prior SQL/journal
entries and the five newest snapshots.

## Verification

Published head: `88a209e28b969b5229e9a621aeddfd3cbae9b0cd`, integrated
with landed #17656 (`ba5bba86dcdde32b14d4d7dec1b51898e5f6f22d`) and
master `52b86c2032a189b0ea259c5b0cb7158b07ef788c`.

- **74 distinct current cases pass:** 17 terminal-response
lifecycle/producer cases (1,721 other chat cases skipped), all six
queued-comment consumption regressions for the new upstream query
optimization, all 30 heartbeat completion cases, all eight real/unit
issue-tree control cases and all 13 startup cases. Database build,
server typecheck/build, module boundaries and four fresh migrations
through 0401 pass.
- The earlier **304-case** prepared-parent matrix passes at
`aa72f32641b2e2c680342cd49477272349aca9db`: all 253 GitHub workflow
cases, 30 completion cases, eight issue-tree cases and 13 startup cases.
It retains that revision scope. Repeated cases are not added to the
current 74.
- The terminal matrix covers finalized-history quiet, deleted Board
sources, pending presentation/passive outcomes, outer rollback/commit,
paused/new runs, restart/drain/standby, committed native identity, lease
waits, full-page unrelated history, uncertain publication ownership and
tree/workspace/exhausted-recovery producers.
- A populated 0400 database upgrades to 0401 with all 399 prior
migration receipts identical, exactly one added, and none added on
replay. The terminal index is valid/ready on fresh and upgraded
databases. Its SQL matches the original draft after whitespace
normalization; generation keeps snapshots 0397–0401.
- Normal integration `3c8339a541bf68bc9957b9d5a8df80d3a7c3452d`
preserves the complete 24-path owned binary patch and all five new
master paths. The landed parent tree also equals the previously
published parent tree byte for byte. Both normal merge parents and the
exact composition tree are checked. No force push or rebase occurs.
- [The owner
audit](https://github.com/paperclipai/paperclip/blob/88a209e28b969b5229e9a621aeddfd3cbae9b0cd/doc/plans/2026-10-25-github-terminal-response-sleep-audit.md#acceptance-on-the-landed-github-parent-and-current-master)
preserves the original producer, quietness, failure-isolation,
skipped-suite and presentation-wait results with their scopes. Current
passes do not erase earlier failures or turn skips into passes.
- The audit-only follow-up leaves all owned source/test/migration hashes
unchanged. **Fresh exact-head CI and Greptile 5/5 with no actionable
findings remain required.** No full local repository suite, live GitHub
tenant or deployed sleep result is claimed.

## Risks

- Process-local notifications cover supported application writers and
shared pools. Independent SQL writers and other replicas gain no new
wake guarantee.
- Native terminal status alone does not authorize fallback publication.
Pending presentation and existing publication ownership remain separate
obligations.
- Stop can leave an in-flight provider request settling; admission
fences stop a subsequent request. Unknown outcomes retain their existing
recovery rules.
- Index creation can take time on large histories. Concurrent/idempotent
migration and existing replay recovery remain in charge.
- Worker quiet does not establish host/database suspension. Cloud alarm
coverage and sleep admission remain unchanged.

## Model Used

OpenAI Codex, using reasoning, repository inspection, code execution and
real PostgreSQL tests. The exact model identifier and context-window
size are not exposed in this session.

## 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
- [ ] All Paperclip CI gates are green
- [ ] 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-28 16:51:57 -05:00
DottaandPaperclip ba5bba86dc fix(server): wake GitHub acknowledgements from committed changes (#17656)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/206/changes/paperclip/206
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip coordinates agent work and presents its progress in chat.
> - GitHub deliveries save acknowledgement actions and provider
receipts.
> - Periodic maintenance currently searches for unfinished
acknowledgements even when idle.
> - Committed delivery, lease, response and presentation changes
identify when work can advance.
> - The existing owner should wake on those changes and recover only at
saved deadlines.
> - Completed history should become quiet without losing unfinished
provider effects.

## Linked Issues or Issue Description

Refs #17653, which is merged. This existing GitHub acknowledgement slice
targets master.

**Problem:** A saved GitHub acknowledgement can wait for a publication
lease, fast-response result or native presentation decision. Polling
empty or unchanged work keeps the tenant active. Removing maintenance
without covering these producers can strand the acknowledgement.

**Expected behavior:** Committed changes wake the scoped owner. It
reconstructs existing actions on startup, retries at saved deadlines,
parks unchanged presentation waits and becomes quiet when settled.

## What Changed

- Register GitHub acknowledgement work with the shared scoped deadline
coordinator. Reuse existing action, delivery, lease and fast-response
rows.
- Add transaction-aware response-availability notifications at supported
writes. Keep GitHub protocol details in its platform owner and shared
notifications limited to IDs.
- Share the bounded latest-request/run projection between scheduling and
direct response authorization. An absent native presentation decision
parks the action until its real writer commits.
- Retain publication leases, source/principal authority, version
fencing, response markers and final receipt precedence. Check admission
before each new provider request; let in-flight work settle. Stop does
not consume the provider retry budget.
- Remove only recurring acknowledgement recovery and keep its manual
path. Add no work table or scheduler service.
- Regenerate the three original indexes as
`0400_absent_phantom_reporter.sql`: pending acknowledgement IDs,
fast-response delivery lookup and publication comment/destination
lookup. Each uses concurrent/idempotent creation and the existing
top-level migration runner. Preserve all landed SQL/journal entries and
the five newest snapshots.

## Verification

Published head: `09ca00b9f029838434ec042cda691345d67b493e`, on landed
continuation parent `063cfd43813cabf18417605fb9eefb9aacf95c8d`.

- **87 distinct current cases pass:** 18 real PostgreSQL acknowledgement
lifecycle/presentation cases, all 26 fast-response service cases, all 30
heartbeat completion cases and all 13 startup cases. Database build,
server typechecking/build, module boundaries and three fresh migrations
through 0400 pass. The current Runner bundle rebuild also passes after
upstream provider-event changes.
- The lifecycle matrix covers empty/history quiet, lease release,
restart, drain/standby, fast-response failure/expiry/publication/unknown
outcome, streaming quarantine, outer rollback/commit, saved provider
retry, stop between requests and receipt state. The real presentation
writer proves quiet while pending, rollback, committed reactivation and
final quiet.
- The earlier **305-case** matrix (236 full GitHub workflow cases, 26
fast-response service cases, 30 completion cases and 13 startup cases)
passes at `337f272853d4f8a51b5b9300900a46eb4e13f698`. It retains its
prepared-parent scope. Repeated cases are not added to current 87.
- A populated 0399 database upgrades to 0400 with all 398 prior
migration receipts unchanged, one new receipt and no additional receipt
on replay. All three indexes are valid/ready on fresh and upgraded
databases. Their SQL definitions match the original draft after
whitespace normalization; migration history and retained snapshot bytes
are preserved.
- Normal integration `9b3607e159153e00ccdfe538d52b591bd38d6499`
preserves the full 28-path owned binary patch and all 20 unrelated
master paths, including native retry, provider-event and evaluation
workflow changes. Both normal merge parents and the exact tree are
checked. No force push or rebase occurs.
- [The owner
audit](https://github.com/paperclipai/paperclip/blob/09ca00b9f029838434ec042cda691345d67b493e/doc/plans/2026-10-25-github-chat-maintenance-sleep-audit.md#acceptance-on-the-actual-landed-continuation-parent)
retains the original missing-notification, JSON precedence, receipt
lifecycle, external-database fixture and migration-registry failures.
Earlier 69/228/broad counts keep their original scope. Skips and failed
revisions are not relabelled as current passes.
- The final audit-only edit leaves all owned source/test/migration
hashes unchanged. **Fresh exact-head CI and Greptile 5/5 with no
actionable findings remain required.** No full local repository, live
GitHub tenant or deployed sleep result is claimed.

## Risks

- Process-local transaction hints cover supported application writers
and shared pools. Independent SQL writers and other replicas do not gain
a new wake guarantee.
- Provider effects remain guarded by the existing publication lease and
current source/principal authority. Unknown outcomes retain their
existing recovery semantics.
- Presentation waits depend on their supported milestone/transaction
producers. A missing producer can defer recovery until another supported
change or restart.
- Index creation can take time on large histories and runs concurrently
outside a transaction. Existing migration recovery handles replay; prior
SQL is unchanged.
- Worker quiet is distinct from host/database suspension. Cloud wake
coverage and sleep admission remain unchanged; later maintenance duties
are separate slices.

## Model Used

OpenAI Codex, using reasoning, repository inspection, code execution and
real PostgreSQL tests. The exact model identifier and context-window
size are not exposed in this session.

## 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
- [ ] All Paperclip CI gates are green
- [ ] 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-28 16:06:42 -05:00
DottaandPaperclip 063cfd4381 fix(server): wake interaction continuations from committed responses (#17653)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/204/changes/paperclip/204
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip coordinates agent work and human approvals.
> - Human decisions already persist the continuation action that resumes
an agent.
> - A response can commit before its callback or publication finishes.
> - Periodic maintenance repeatedly discovers those rows, including when
idle.
> - Committed writes should wake the existing owner; saved retries and
claims supply real deadlines.
> - Startup reconstructs durable work, and settled history becomes
quiet.

## Linked Issues or Issue Description

Refs #17651, which is merged. This existing continuation slice targets
master and removes its duty from periodic chat maintenance.

**Problem:** A saved `interaction_wakeup` action can survive a committed
human response whose callback crashes. Without producer coverage,
removing maintenance strands the continuation. Repeated empty
maintenance also keeps the database active.

**Expected behavior:** The outer transaction's settlement wakes the
continuation owner. It handles ready conversation publications,
dispatches under existing authority, preserves interrupted/retry work,
and becomes quiet after settlement.

## What Changed

- Register continuation recovery with the shared scoped deadline
coordinator. Reconstruct existing issued/processing actions and retain
the existing retry and stale-claim deadlines.
- Notify from both enqueue producers and owner-local claim/settlement
writes. Preserve outer commit/rollback and avoid moving provider or
heartbeat calls into database transactions.
- Give the existing publication queue an optional
company/endpoint/conversation scope. The continuation owner gives ready
cards in that conversation a bounded delivery opportunity before
claiming the action; FIFO, capacity, ambiguity and provider authority
remain with the queue.
- Carry admission/cancellation through publication preparation and check
before every new send. Join in-flight requests and retain pending
actions/publications through drain, standby, shutdown and resume.
- Remove only recurring continuation recovery; keep the manual API. Add
no table. A concurrent partial index on existing pending continuation
actions and matching company/id keyset paging prevent retained history
from being scanned. Parent #17651's indexed confirmation discovery and
current master are retained.

## Verification

Published head: `a1d16f84b3fae361d5d648670a5964486370c3a3`, on actual
landed terminal-confirmation parent
`ee67d2ece9bda157dfad9bdf25175b1d004ed341`.

- **75 distinct current cases pass:** 32 selected real PostgreSQL chat
cases (14 continuation lifecycle/producer/stop cases, one new
continuation query-cost case, and 17 parent confirmation/query-cost
cases; 1,671 skipped), and all 43 cases in complete
interaction-publication and app/startup suites. Database build, server
typecheck/build, module boundaries and fresh migrations through 0399
pass. The original landed-parent matrix passed 74 cases before the
additional query-cost repair; those are not added again.
- The new real-SQL regression seeds 20,000 completed/unrelated actions
and one pending processing claim. Before repair it examines 20,345 rows
and hits 20,421 buffers. The final startup and company-scoped queries
must examine at most two action rows and fewer than 20 action buffers;
empty scoped recovery must also stay below 20 buffers. Planner settings
are unchanged. Migration `0399_ambitious_onslaught.sql` is
concurrent/idempotent and uses the existing top-level migration runner;
prior SQL/journal entries and the five newest snapshots are preserved.
- The matrix covers callback recovery, concurrent responses, malformed
prefixes, more than one page, saved retries/stale claims, outer
rollback/commit, deferred/future/paused publication states, restart,
drain and stop before each new provider request. Quiet assertions
inspect real selects/transactions across virtual idle time.
- Normal integration `db5a84c0f8b73a0ce863f4d2fbaee0a115cf044f`
preserves the complete 12-path owned binary patch and all 12 unrelated
current-master paths, including managed Cloud sandbox changes. A
verified semantic base resolves squash-ancestry conflicts; both normal
parents and the exact tree are checked. The subsequent index/keyset
repair is a deliberate additional change, tested separately; the final
patch is not claimed identical to the original 12-file implementation.
No force push or rebase occurs.
- The same 74 pre-index cases passed on the earlier prepared and actual
landed parents. The new query-cost regression failed before its repair,
then passes on the repaired source. Earlier 57-case and broad acceptance
retain their original revision scopes; repeated cases are not added to
the current count. The audit-only follow-up leaves all owned server
source/test hashes unchanged.
- [The owner
audit](https://github.com/paperclipai/paperclip/blob/a1d16f84b3fae361d5d648670a5964486370c3a3/doc/plans/2026-10-25-interaction-continuation-sleep-audit.md#acceptance-on-the-actual-landed-terminal-confirmation-parent)
retains the original missing-signal regression, the skipped standalone
publication suite, and the review's stop-before-send failures with their
corrections. Skips and earlier failing revisions are not relabelled as
current passes.
- **Fresh exact-head CI and Greptile 5/5 without unresolved actionable
findings remain required.** The former remote head's reviews are stale.
No full local repository suite, live provider or deployed sleep result
is claimed.

## Risks

- Process-local transaction hints cover supported application writers
and shared pools. Independent SQL writers and other replicas do not gain
a new wake guarantee.
- The publication queue's existing FIFO, capacity, retry/ambiguity and
authorization contracts still govern effects. In-flight provider
requests can settle after Stop; the fence prevents a subsequent request
from starting.
- Missing a supported producer can defer recovery until another change
or startup. Producer and rollback tests cover this slice's supported
paths.
- Worker quiet is separate from host/database suspension. External alarm
coverage and Cloud sleep gates are unchanged.

## Model Used

OpenAI Codex, using repository inspection, code execution and real
PostgreSQL tests. The exact model identifier and context-window size are
not exposed in this session.

## 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
- [ ] All Paperclip CI gates are green
- [ ] 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-28 15:45:18 -05:00
DottaandPaperclip ee67d2ece9 fix(server): wake terminal confirmation recovery from committed changes (#17651)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/202/changes/paperclip/202
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip persists confirmation clicks and interaction decisions
before dispatch completes.
> - A crash after a decision commits can leave a processing confirmation
action.
> - Periodic publication maintenance repeatedly scans those actions even
when idle.
> - Saved decisions and the supported board/admin writers already
identify when settlement can change.
> - This owner reconstructs durable actions, reconciles real changes and
then becomes quiet.

## Linked Issues or Issue Description

This existing terminal-confirmation slice follows merged #17649. It
removes only terminal-confirmation recovery from periodic chat
maintenance while retaining the explicit manual API and existing durable
action/interaction/publication rows.

**Problem:** An accepted response can commit before its dispatch
settles. Removing periodic recovery without covering board decisions,
expiry and ownership changes would strand processing actions. Pending
human decisions must remain recoverable without polling indefinitely.

**Expected behavior:** Committed supported changes wake the scoped
owner. It reconciles terminal responses once, parks unresolved human
decisions, preserves concurrent commits/restart/drain and becomes quiet
when settled.

## What Changed

- Register terminal-confirmation work with the existing delivery
coordinator and scoped lifecycle. Reconstruct processing action IDs,
bound selection pages, preserve concurrent wakes and keep current
authorization in the existing interaction owner.
- Add the existing transaction-aware confirmation notification topic at
real resolution writes and callback settlement. Reuse milestone signals
instead of adding per-caller recovery wrappers.
- Cover ordinary and checkbox confirmations, suggestions and connection
intents through their owning service. Cover board accept/reject,
cancellation/withdrawal, opt-in comment supersession, expiry,
reassignment and already-satisfied connection resolution.
- Preserve task/actor authority, one-use callback tokens and immutable
provider publication semantics. Explicitly fail malformed missing
bindings instead of silently retaining unrecoverable processing actions.
- Remove only this duty from recurring publication maintenance. Add no
table or scheduler infrastructure. A concurrent partial index on
existing action rows prevents recovery discovery from scanning completed
history.

## Verification

Published repair head: `a35e78d076c1b618bc643391b7520fa6952fc996`,
targeting master after normal integration of
`644937c93150c423f12674992c7ea1e8b5dbc98c`. [Detailed acceptance and
preserved
failures](https://github.com/paperclipai/paperclip/blob/a35e78d076c1b618bc643391b7520fa6952fc996/doc/plans/2026-10-25-terminal-confirmation-sleep-audit.md#bound-retained-history-reads--october-28-review-repair).

- **New query-cost regression:** Capture the actual SQL emitted by
startup and company-scoped confirmation discovery. Seed 20,000
completed/unrelated action rows and one pending human decision. Before
the index, the regression fails because recovery examines 20,009 action
rows. After the partial index, startup and scoped discovery must examine
at most two action rows and fewer than 20 table/index buffers; the
no-pending scoped path must also stay below 20 buffers. Planner settings
are unchanged, and returned IDs remain correct.
- Match startup keyset ordering to `(company_id, id)`, the partial index
order. The index alone still let PostgreSQL choose the primary key and
scan 20,206 rows for `ORDER BY id`; the final query fixes this without
forcing a planner setting or adding a second index. Fixed kind/status
predicates stay literal for prepared-plan index eligibility.
- Add the generated `0398_yielding_random.sql` migration, using `CREATE
INDEX CONCURRENTLY IF NOT EXISTS` and the existing top-level migration
runner. Keep the five newest generated snapshots and preserve every
prior SQL/journal entry. No work table is introduced.
- **110 distinct cases pass:** 17 final confirmation
lifecycle/query-cost cases (1,671 skipped), seven real Runner descriptor
variants (295 skipped), and all 86
app/startup/bundled-plugin/plugin-database cases. Server/database
typechecking and builds, migration safety/numbering, module boundaries
and a fresh PostgreSQL migration through 0398 pass. The final test-only
vacuum precondition is followed by another 17-case confirmation pass and
server typecheck. Counts are not added across repeated runs. Normal
integration of master `644937c93150c423f12674992c7ea1e8b5dbc98c`
preserves the complete owned patch and all 18 unrelated master paths.
- Previous landed-parent acceptance: 29 distinct cases (16 confirmation
lifecycle/producer cases with 1,671 skipped, plus all 13 startup cases)
passed on `32e64a7e9ea5b457e54ca7b268be90e1e155b451`; the earlier
80-case matrix remains scoped to
`c1e8542b588a4fa59b62f9829541d3daea4f1ddc`. Counts are not added across
repeated runs.
- Actual producer cases prove parking/quiet, caller-owned outer
transaction rollback, commit-driven automatic settlement, restart/drain
and concurrent commits. Checkbox, suggestion, connection-intent and
Telegram/Slack callback paths retain existing authority and immutable
publication semantics.
- **Fresh exact-head CI and Greptile 5/5 with no unresolved actionable
findings are required.** The preceding head's Runner CI failed its
timeout-attachment durable-suspension case (1 failed/1,955 passed/4
skipped); that failure is retained and not counted as a pass. The new
head must pass this job. No full local repository, live provider or
physical suspension claim is made.

The owner audit retains the earlier missing-board-producer regressions
and their corrections, including the invalid supersession fixture that
assumed a new checkbox expires an older one. Existing behavior was
preserved by using the supported opt-in human-comment path. Prior
scheduler/backoff, route and plugin test failures remain at their
original revisions; no old pass is substituted for current remote gates.

## Risks

- Process-local notifications cover supported application writers and
their shared pools, not independent SQL writers or other replicas.
- Processing actions waiting on a human response are deliberately
parked. Supported interaction resolution/expiry writers must use the
notification boundary; a missing producer delays settlement until
restart.
- Invalid bindings have explicit failure evidence. Source, actor, task
and publication authority remain checked by their existing owners.
- Following continuation/GitHub duties remain separate. Cloud sleep
gates and external alarms are unchanged; local worker quiet does not
establish physical host/database suspension or fleet savings.

## Model Used

OpenAI GPT-6 / Codex, with reasoning, repository inspection, code
execution, and PostgreSQL tests. The exact runtime model identifier and
context-window size are not exposed in this session.

## 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
- [ ] All Paperclip CI gates are green
- [ ] 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-28 15:00:31 -05:00
DottaandPaperclip 8049fbf51d fix(server): schedule voice call reports from saved deadlines (#17586)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Voice calls retain reports and project guest transcripts into intake
tasks.
> - Chat maintenance repeatedly scans recent calls even when no report
is due.
> - Empty scans keep the tenant database active.
> - Existing session and report rows already contain the work and its
deadlines.
> - This pull request moves report refresh to the shared coordinator and
removes the final voice maintenance lane.
> - Idle voice configuration and old call history no longer cause report
scans.

## Linked Issues or Issue Description

Refs #17583, #17580, #17578, #17576. This is slice 25 of the
recurring-server sleep stack. It now follows merged #17583 and targets
master.

**What existing behavior does this improve?**

Voice call-report refresh and guest transcript projection.

**Current behavior**

The chat interval scans terminal calls for reports. It repeats the scan
even when there are no eligible calls or all saved deadlines are in the
future.

**Proposed behavior**

Committed session transitions and signed callbacks wake a scoped worker.
Startup reconstructs existing rows. Later refreshes use saved deadlines.
The worker becomes quiet after all eligible windows expire.

**Reason and benefit**

Stop empty database scans without losing late transcript and billing
updates. Keep the existing 24-hour refresh policy, provider response
ordering, and guest-task trust restrictions.

## What Changed

- Add a voice-owned report worker using the existing scoped deadline
helper. No queue table or field is added; the existing session table
receives a report-window index.
- Save each attempt and its retry before provider I/O. Compare that
ownership on settlement so an older response cannot overwrite a later
callback or claim.
- Preserve available reports when a response is incomplete or stale.
Preserve their finite refresh window.
- Route voice-change and reply call-ended writes through
transaction-aware report signals.
- Preserve guest transcript locks and use the observed document revision
when updating an existing transcript.
- Register the worker in app startup. Remove voice reconciliation from
the chat interval. Keep explicit one-shot diagnostics.
- Document producer scope, lifecycle behavior, tests, and external wake
limits.

## Verification

Published head: `758cfe15c3e6283d38ba512c8416ccce4d200862`. The
follow-up after `c28de398` changes only two documentation files; the
verified production, migration and test source is unchanged. It normally
merges landed retention/master
`980775e0e956eecdc28c73c9b4aca0407ad45e1a`. The 20 owned paths remain
the complete slice, including officially regenerated migration 0397, its
snapshot and required pruning of snapshot 0392. The non-database
reviewed patch was preserved during ancestry integration; the only
subsequent source edit makes one existing report test’s provider
responses call-specific. All production report behavior and unrelated
master changes are preserved. No shared worker, workflow or lockfile
change belongs to reports. All 58 current-head checks are now clean,
including the previously failing approval shard; exact-head Greptile is
5/5 with no new actionable issues. Ponytail’s current turn stopped
without a verdict, an authorized nonblocking state. The prior
conditional documentation requirement was fixed; old CI failures remain
recorded below.

- **Prior-head evidence at `9bf20467df`: 303 distinct targeted cases
passed**, not rerun wholesale for this integration: all 132 voice
integration cases; 170 cases across 11 complete report, retention,
cleanup, recovery, company-removal, shared-worker and startup/hostname
suites; and one complete snapshot/schema drift case. This includes 15
report, 13 retention and 14 cleanup lifecycle cases. Voice integration
and the four other database owners use separate initially empty
databases and migrate themselves. Report cases use their dedicated
database after the setup repair.
- Five real connection saves over 205 sessions with saved future report
deadlines selected 1,025 session rows before the notification repair.
They now select zero and preserve the original deadline. The report
notification helper accepts only exact session scopes. Configuration,
authority and connection hints retain their existing live
recovery/reply/cleanup behavior but do not rebuild report history. Real
session terminal writes, callback hints, report claims and receipts
still reactivate reports.
- Database/server typechecks and builds, migration numbering/safety, and
module-boundary checks pass. Final server types/build also pass after
integrating the retention review repairs. The parent 205-admission,
five-configuration-save test still observes zero retained-history rows
and preserves its deadline.
- The old eager reader selected all 105 saved-report headers before
yielding and failed the 100-row budget. Registered reconstruction now
uses the existing paged contract and yields at 100 rows per scope
refresh. A partial scan can restart on the same owner and reconstruct
every saved deadline. At due time all 105 report IDs are fetched exactly
once. Future deadlines remain quiet between actual retries.
- The company-fairness case places a small company deterministically on
page two and requires it in the next 25-work batch after discovery, with
all 105 reports processed before the failed provider's retry. Guarded
mutations catch a missing cursor reset and bypassed company rotation.
Original source was restored before final acceptance.
- At prior head `9bf20467df`, migration 0396 was generated with `pnpm
db:generate` on the parent 0395 lineage. It creates the partial
`(ended_at, id)` report-window index concurrently and replayably through
the existing migration runner. All 394 historical SQL files and the
journal prefix are unchanged; snapshots 0392 through 0396 remain. Fresh
application migration and two replay invocations succeed with a valid
index. The real plan case retains 20,001 old sessions, 205 recent
sessions, precision-safe keysets, and fewer than 1,000 rows removed by
filtering on each page.
- Existing real hangup/callback/reply-error producers, callback
ownership fencing, monotonic report results, saved retry before provider
contact, guest transcript projection after a document lock, transaction
rollback/nesting, drain and shutdown remain covered. No table, field or
provider protocol is added. Available reports retain the existing finite
24-hour refresh policy.
- [Current-parent acceptance and failure
ledger](https://github.com/paperclipai/paperclip/blob/9bf20467dfd50b81848e1c8dd7ddc905d206418f/doc/plans/2026-10-25-voice-call-report-sleep-audit.md#current-parent-reconstruction-and-migration-acceptance-october-28)
records the failed fresh fixture, eager read, fairness-clock attempts,
guards, commands, counts and limits. Older 174/138-case and
workspace-wide results belong to their stated heads. No new complete
local repository suite, live provider, Cloud wake, host/database
suspension or savings claim is made.
- [Report configuration-scope
repair](https://github.com/paperclipai/paperclip/blob/9bf20467dfd50b81848e1c8dd7ddc905d206418f/doc/plans/2026-10-25-voice-call-report-sleep-audit.md#configuration-scope-review-repair-october-28)
records the current 303 cases and measured rescan repair. The prior
`80207ba6bb` head had Ponytail 5/5; that review is stale after this
change. Fresh exact-head Greptile 5/5 and all CI remain required. Actual
failing Ponytail findings must be fixed; a stopped or paused turn
without a verdict is an authorized nonblocker. Active no-verdict review
waits are capped at 30 minutes from the original request/start
(25-minute monitor cutoff). Older scores and parent checks are not
inferred as current passes.

- **Current-master verification: 185 distinct cases pass**: final
complete voice integration 132, complete report/cleanup/retention
15+24+13, and snapshot/schema drift 1. Each database owner used its own
empty fixture database. The first combined run had 183 passing cases and
one report-test failure; it is preserved in the audit. A deliberately
earlier-ended competing call reproduces the old one-shot mock consuming
the intended response. The corrected test selects responses by provider
session ID, verifies independent costs for both calls, then rejects
stale target data. The isolated repair and full 132-case suite pass;
production behavior and timeouts are unchanged. These 185 cases are not
added to the older 303 count.
- **Current migration lineage:** official `pnpm db:generate` after
actual master creates `0397_known_madrox.sql`; the reviewed
concurrent/idempotent report-index SQL is retained byte-for-byte. All
395 existing SQL files and journal entries are unchanged; snapshots
0393–0397 remain and the new snapshot links parent 0396. The application
migrator boots a fresh database, repairs a deliberately invalid index
during two concurrent replay invocations, records one history row, and
replays again successfully. Database build, server build/typecheck,
migration checks and module boundaries pass. [Full current-master
acceptance and failure
ledger](https://github.com/paperclipai/paperclip/blob/c28de398fef074f650eff532952ab6f5563739a8/doc/plans/2026-10-25-voice-call-report-sleep-audit.md#landed-master-migration-and-acceptance-october-28).

- **Review follow-up and preserved CI timeout:** Ponytail’s prior
`c28de398` review was 4/5 advisory, with no must-fix production defect
and a request to fix the stale design-doc migration reference before
shipping. That document now names migration 0397 and links current
185-case acceptance; older 302/303-case evidence is explicitly
historical. The optional suggestions about one-shot diagnostic inventory
cost and inherited test-clock helpers remain disclosed. CI run
`40703490340` failed the unchanged approval-queue backoff test on its
existing 15-second timeout and then an activity-log FK in teardown
(2,097 pass/one fail/one skip in that shard). The complete unchanged
28-case approval suite passes on a new isolated database; the exact CI
timing cause remains unproven. No timeout, workflow or production logic
was changed, and no old-head rerun or failure waiver is claimed. This
documentation head requires fresh CI and exact-head Greptile. [Review
and timeout
ledger](https://github.com/paperclipai/paperclip/blob/758cfe15c3e6283d38ba512c8416ccce4d200862/doc/plans/2026-10-25-voice-call-report-sleep-audit.md#review-documentation-and-preserved-remote-timeout-october-28).

## Risks

- Process-local commit signals do not observe independent SQL writers or
another server process.
- Available reports still refresh for 24 hours. The provider's complete
flag does not promise immutable final data.
- Transcript projection retries remain within the existing report
window. This change does not add indefinite projection repair.
- One-shot diagnostic `reconcile(limit)` inventories the eligible window
before slicing; it remains a manual diagnostic cost, not recurring idle
work. Some inherited test helpers advance fake time during assertion
retries, an optional reliability follow-up noted by review.
- A local deadline cannot wake a stopped host. Cloud alarms and sleep
eligibility remain unchanged. No deployed sleep or cost saving is
claimed.
- This PR depends on the preceding stack. Final master reconciliation
and remote review gates remain required.

## Model Used

OpenAI GPT-6 through Codex, with repository inspection, code editing,
and local test execution. The exact serving model ID and context window
are not exposed in this session.

## 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-28 09:41:15 -05:00
DottaandPaperclip 71b448b9cb fix(server): schedule inbound voice cleanup from durable obligations (#17580)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Incoming voice calls need approval, expiry, and safe provider
cleanup.
> - The remaining voice maintenance tick scans admissions every second,
including idle endpoints.
> - Admission status does not prove that the provider call has ended.
> - Existing admission rows can retain the cleanup owner, confirmation,
and retry deadline.
> - This pull request replaces the cleanup scan with committed changes
and actual deadlines.
> - Idle cleanup stops querying while unfinished calls keep a
recoverable path to termination.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Inbound voice admission cleanup. Its reply-delivery parent #17578 has
landed; this PR targets master. Live-session recovery is in #17576. It
preserves separate ownership for those services.

**Current behavior**

The voice maintenance tick scans expired admissions even when no cleanup
is due. A session can commit before the separate approved-admission
write. Expiry can then terminate that authorized call. A rejected guest
call can be labeled ended before provider termination succeeds.
Seven-day retention can delete that unfinished cleanup.

**Proposed behavior**

Wake cleanup from committed admission and session changes. Schedule the
original expiry or saved retry. Claim current state under the admission
lock before provider I/O. Commit approval with session reservation. Keep
unconfirmed termination durable and transfer bound calls to live-session
recovery.

**Reason and benefit**

Configured idle endpoints and confirmed history cause no recurring
admission cleanup SQL or provider requests. Approval, retries, crash
recovery, and cleanup remain supported. History retention and call
reports stay separate migration items.

## What Changed

- Recover terminal and rejected legacy admissions whose session binding
was lost. Copy known termination or bind a proven matching live session
for cleanup without granting approval.
- Register inbound cleanup with the shared coordinator and scoped
deadline worker. Yield after each 100-row scope page and reset cursors
on registration. Remove only cleanup from periodic voice maintenance.
Keep complete manual reconciliation.
- Add commit-aware notifications at admission, approval, rejection,
guest binding, pending-tool expiry, lifecycle confirmation, and session
termination writes.
- Commit authorized session binding and admission approval together.
Recheck cleanup ownership under the same admission lock. Recover older
interrupted bindings only from matching session evidence.
- Add nullable cleanup confirmation, next-attempt, and provider-account
fields to the existing admission table. No new queue table is added.
Generated migration 0396 backfills only provable historical termination
in committed 1,000-row keyset batches and adds a concurrent partial
index.
- Save the retry before provider contact. An unconfirmed end receipt
retains cleanup. Bound calls remain owned by live-session recovery.
- Verify the signed provider account before using a rotated key for
unbound cleanup. A different account cannot inspect or end the saved
call. Legacy admissions require their original credential fingerprint.
- Prevent history pruning and company deletion from erasing unfinished
cleanup. Company deletion locks endpoints before the voice owner's
check, matching ingress serialization.
- Document producer coverage, ownership, deadlines, verification, and
remaining sleep limits.

## Verification

Published head: `a4e7ccf976fc5793a8914e4e4861aa080d43a4da`. The reply
parent #17578 has landed. A normal merge integrates actual master
`e7ea2e2961863171956e70fb05d3d1f3790f7909`, preserves its unrelated
changes, and regenerates this slice’s migration after landed migration
0395. Fresh exact-head reviews and CI are required.

- The previous Ponytail 3/5 finding is repaired: legacy terminal
admissions no longer enter approval-only repair, and rejected admissions
with valid matching live sessions retain their rejection and persist the
binding needed for session-scoped cleanup confirmation. Five regressions
failed with ownership conflicts before the repair. Five
conflicting-session regressions prevent an unrelated leg from being
claimed. No cleanup operation grants approval or starts a provider call.
- **24 complete cleanup lifecycle cases and 122 complete voice-store
integration cases pass** on the repair. After actual-master integration
and migration regeneration, the complete 24-case cleanup suite passes
again on a new empty PostgreSQL 17 database, and the database migration
integration test passes separately. The cleanup owner, voice-store test
and admission schema are byte-identical to that tested repair; the
cleanup fixture only changes the migration filename. Migration
numbering/safety, database build, production server typecheck and build
pass.
- Migration `0396_tan_paladin.sql` was generated with `pnpm
db:generate`. Every landed SQL migration and the complete parent journal
prefix are unchanged. Generated snapshots retain exactly 0392–0396,
pruning 0391. The reviewed cleanup SQL, concurrent idempotent index and
committed 1,000-row historical backfill are retained byte for byte under
the new generated filename, registered for top-level execution. The real
application migrator boots a fresh database, repairs an interrupted
concurrent index, serializes replays and verifies eligible-history
backfill across actual transactions.
- The preceding published head
`48d8356e73308579470c182e759eead6e83173d2` received Greptile 5/5 but
failed CI server shard 9/12: one existing connected MCP rate-limit test
expected two real-clock calls to share a fixed 60-second bucket. A
controlled window rollover reproduced its exact failure. That existing
fixture now freezes only `Date` around the same-window 429 assertion,
verifies acceptance after advancing one window, and restores the real
clock in `finally`. All 92 gateway acceptance cases pass. Production
rate limiting, timeouts and assertions are unchanged. This adds one
existing CI fixture to the slice; the original failed run remains
recorded.
- The first regression invocation skipped the suite because it used the
wrong database variable; that is not a pass. The corrected invocation
reproduced five ownership conflicts. The first fixed cleanup run passed
22 and failed two new low-level deletion test calls on a
retained-session foreign key; the regressions now check the cleanup
deletion guard at its owning boundary, with production deletion ordering
unchanged. The final full suite passes all 24.
- The first complete voice-store invocation passed 121 and timed out on
one transcript case at the unchanged 15-second limit. That case passed
in isolation on a fresh database. One subsequent complete invocation,
with builds finished, passed all 122 without source, assertion or
timeout changes. The underlying timeout cause is unproven; the original
failure remains recorded. A new PostgreSQL startup also failed from
exhausted host semaphore capacity; fresh databases on the already-owned
server were used without removing shared IPC resources.
- Prior source `b8b12199b9` passed 266 distinct cases and carried three
guarded mutation proofs; those older results are not added to this
repair’s case count. Its CI run 40641168596 failed E2E shard 3 when
legacy cancellation hit a one-second PostgreSQL lock timeout. GitHub
initially rejected a retry while the run was active, then accepted one
targeted retry after completion; attempt 2 passed on that same old head.
The lock holder and underlying cause remain unproven. The unchanged Node
policy checker’s older failure on ignored generated Copilot manifests
and passing tracked-input mirror are also retained in the audit; no
workflow, checker, vendor manifest or ignore rule was modified.
- [Current repair and failure
record](https://github.com/paperclipai/paperclip/blob/a4e7ccf976fc5793a8914e4e4861aa080d43a4da/doc/plans/2026-10-25-voice-inbound-cleanup-sleep-audit.md#legacy-terminal-admissions-missing-their-session-binding--october-28).
Current-head CI and Greptile 5/5 gate merge. A completed failing
Ponytail verdict must be fixed; missing or interrupted reviews do not
block, and active pending review waits are capped at 30 minutes under
the maintainer’s explicit instruction. No new full local repository
suite, real provider, Cloud alarm, host/database suspension or savings
claim is made.

## Risks

- The migration distinguishes admission status from physical
termination. Legacy ended guest rejections without proof remain cleanup
obligations. Missing original credentials can keep those old obligations
unresolved.
- Future writers must use the existing notification boundary.
Independent SQL writers and multiple processes need their own wake
contract.
- Pending inbound cleanup now blocks company deletion instead of
allowing cascade deletion to lose the provider obligation. Existing
voice-history deletion restrictions remain. This deliberately includes
an active anonymous inbound ring and an old call whose termination
cannot be verified. An operator must stop ingress and settle the call
before deletion; this slice adds no abandonment override and never
treats missing credentials as proof of termination. This is the retained
cleanup ownership policy, not a claim that every deletion attempt can
complete immediately.
- History retention and report processing still run in the remaining
maintenance lane. This PR does not extend Cloud wake coverage or claim
measured host/database suspension.

## Model Used

OpenAI Codex (GPT-6), with reasoning and code execution. The exact
serving model ID 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
- [ ] All Paperclip CI gates are green
- [ ] 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-28 08:30:15 -05:00
DottaandPaperclip 7d8bab773d perf: avoid full JIT and activity scans in Mine unread badges (#17973)
## Thinking Path

> - Paperclip helps people manage AI agents and their work.
> - Mine includes tasks the person created, was assigned, or
participated in.
> - Its unread badge ranks the visible personal top 100 before checking
unread state.
> - Correlated participation and privacy predicates can trigger
expensive PostgreSQL JIT work.
> - This change joins native participant IDs before ranking a narrow
candidate projection.
> - A partial actor index avoids scanning company activity history for
each badge.
> - Local representative scoped reads improve while dense unrestricted
overhead remains explicit.

## Linked Issues or Issue Description

**What existing behavior does this improve?** The Mine unread badge in
the company sidebar.

**Current behavior:** A fresh five-minute window recorded 402 ranking
calls and 31.7 seconds of SQL time. Correlated predicates can spend more
time compiling than executing.

**Proposed behavior:** Build the same company-scoped participant set,
use native issue keys, apply fresh authority before the top-100 limit,
then retain the original canonical rank and second unread query.

**Reason and benefit:** Reduce full JIT compilation and avoid an
unindexed company-history activity scan. Realistic activity-only
participation is included in the measurement.

**Breaking changes:** No API or policy change. This PR adds one
concurrent partial index migration. Related Mine rules in #16356 and
#17756 are preserved.

## What Changed

- Join distinct native participant IDs; guard exact legacy text before
UUID conversion.
- Skip redundant activity participation already covered by
creation/assignment. Sort a narrow eligible candidate projection before
canonical activity ranking.
- Share existing pending-request and user-activity predicates with
ordinary Mine reads.
- Add partial `activity_log(company_id, actor_id, entity_id)` for
user-authored issue activity. Register its idempotent concurrent
migration with the existing top-level/invalid-index-repair runner.
- Add real PostgreSQL coverage for visible top 100,
unread/archive/source semantics, exact IDs, company scope and fresh
grant/private-project access.
- Add a pinned service benchmark with separate pre-index/indexed
databases, identical data, full positional equality, ordinary-call
timings, plans and real migration/cancellation proof.

## Verification

- Fourteen focused PostgreSQL 17 tests pass (16.37 s), including
assembled badge routes and grant revocation. Full workspace typecheck
and build pass on the final production sources.
- The earlier full local suite is incomplete: native PostgreSQL startup
exhausted system semaphores, independently reproduced with empty initdb.
All sixteen failed cases passed with task-owned Docker PostgreSQL 17.
This is not counted as a full local-suite pass. Full Linux CI passes on
final head `f44e46ce580e8345209d7165f57bd8bf696c517c`. Greptile and
Paperclip Review are both 5/5 with zero unresolved comments.
- Baseline `70c864633744cd96c0dc7394d631fc7119cd17d2`; source and DDL
hashes retained. Two warmups and five alternating samples per arm/cell.
Capture and EXPLAIN occur outside timed ordinary service calls.
- All twelve 300k-history cells match complete positional rows in both
SQL phases and final returned IDs. Half the personal tasks qualify
through activity alone; the actor index contains matching and
other-human rows.

| Local service case | Before median | After median | Before / after |
| --- | ---: | ---: | ---: |
| dense / unrestricted / JIT on | 18.33 ms | 25.06 ms | 0.73x |
| dense / scoped / JIT on | 909.30 ms | 221.10 ms | 4.11x |
| sparse / unrestricted / JIT on | 13.64 ms | 5.84 ms | 2.34x |
| sparse / scoped / JIT on | 719.07 ms | 10.79 ms | 66.64x |

Small paths add at most 0.6 ms at the median. Dense unrestricted paths
regress by 6.7–7.7 ms; dense scoped JIT-off is approximately neutral
(218.34→223.24 ms). Sparse JIT-off paths improve too. This is not a
uniform speedup.

The actual baseline fails the compiler-work assertion. The final query
avoids full JIT optimization/inlining and exceeds 1.5x where the old
plan triggers those passes. Small/sparse controls meet the 25% or 2 ms
allowance. Dense no-JIT cases are explicit diagnostics. The optional 60
ms absolute goal still fails at 221.10 ms on dense scoped reads:
substantial privacy-predicate execution remains after compilation is
removed. The failure is retained rather than counted as green.

The actual normal migration runner upgraded 300k activity rows in 668.79
ms including inspection, index build/validation and journal work. Rerun
took 21.83 ms; a deliberately cancelled build left an invalid index, and
retry repaired it in 187.71 ms. Concurrent reads/writes completed; the
final index was 188416 bytes. No migration was run on the hosted
database.

The million-row mixed-participation sparse control also passes all four
visibility/JIT pairs with complete equality: unrestricted JIT-on
13.44→5.76 ms; scoped JIT-on 807.76→11.59 ms. The actor branch visits 36
indexed rows rather than scanning company history. Actual
migration+runner took 255.06 ms; rerun 20.32 ms; cancellation
repair/retry 241.73 ms. Concurrent reads and writes completed without
failures.

Reproduce:

```sh
pnpm --filter @paperclipai/server exec tsx ../scripts/benchmark-mine-unread-badge.ts --baseline-ref=70c864633744cd96c0dc7394d631fc7119cd17d2 --history-decoys=240000 --verify-migration --assert-performance --evidence-dir=<directory>
```

Add `--fixture=sparse --history-decoys=940000` for the million-row
control. The default harness starts disposable embedded PostgreSQL and
ignores DATABASE_URL. Recorded evidence used PostgreSQL 17.11 through a
scratch constructor provider because the host native runtime has
semaphore limits. All normal database allocation/migration/client paths
are retained.

Source-only EXISTS/lateral alternatives were measured and rejected after
company scans or sparse regressions. A time bound would lose historical
participation, and existing indexes cannot select a user's activity.
This justifies the exceptional partial index migration. The exact sample
ranges, plans, failures, source hashes, migration footprint and
feedback-loop timing are in
`doc/plans/2026-10-27-mine-unread-badge-query.md`.

## Risks

- Dense unrestricted reads are slightly slower, and dense scoped reads
retain substantial authority work. The PR removes a compilation/lookup
bottleneck rather than all badge latency.
- The concurrent build scans activity history and can wait for
long-lived transactions. Local build/cancellation timings do not
guarantee the duration on larger or slower hosted databases.
User-authored issue activity gains one narrow index to maintain.
- Participation, visibility, ranking and legacy ID semantics must remain
exact; the real database regressions exercise those boundaries.
- Hosted improvement is unverified. The affected ranking SQL averaged
78.8 ms while HTTP badge requests averaged 752 ms without correlated
traces. No merge or deployment is included.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, tool use and local code
execution. The runtime does not expose the exact model ID or context
window to this agent.

## 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-28 07:20:08 -05:00
DottaandPaperclip 09ad9adce2 feat(server): adopt running remote agents through durable controller checkpoints (#17439)
## Thinking Path

> - Paperclip must keep admitted remote work alive while its main server
is replaced.
> - Runner owns recovery mechanics and exposes opaque checkpoints
through #17097.
> - The server owns company scope, encryption, Stop, tool authority and
execution ownership.
> - A successor must prove current ownership before it adopts the
original execution.
> - Checkpoint bursts must also leave capacity for normal requests and
lease renewal.
> - This PR persists and adopts that authority through the existing
runtime boundaries.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Remote agents retain the original execution during a bounded main-server
outage.

**Current behavior**

A server restart can lose the controller's local state and connection.
An unsafe replacement could repeat a turn or an uncertain mutation.
Checkpoint writes in the request pool can also delay ordinary requests
during an aligned burst.

**Proposed behavior**

Restore encrypted control records from Postgres and adopt the original
runner under a fenced lease. Recover known tool receipts through current
tool authority. Give control checkpoints a shared pool capped at two
connections per database owner.

Depends on #17097. Successors are #17455 and #17456. Qualification and
presentation remain separate review slices.

## What Changed

- Add company/run-bound encrypted checkpoint records and migration 0394.
Keep the SQL idempotent after renumbering against current master.
- Recover startup and expired ownership through native runtime and
heartbeat. Preserve the sandbox lease, admitted runner artifact, harness
configuration, native session and active turn.
- Keep Stop, finalization, preparation ownership and stale-writer
fencing transactional. Preserve graceful detach and natural crash
takeover.
- Recover committed tool receipts through the existing authority.
Reauthorize reads and hold uncertain mutations.
- Bound startup retention with the existing coordinator and preserve
zero idle scans.
- Share at most two checkpoint connections per database client. Keep
application and liveness capacity separate. Close the checkpoint pool
during shutdown; late writes cannot reopen it.
- Count SQL across both application and checkpoint clients. Prove
request/liveness progress, command recovery and failure after pool
shutdown with real Postgres.

## Verification

Project creation recovery now uses the existing project activity receipt
owner. It retains authenticated input normalization, fingerprint checks
and current run authority after the receipt lock. An absent receipt
returns no result before mutation. The new lost-HTTP-response and
no-effect regression uses real API routes and Postgres. The seven
transport cases pass locally. This Mac skips all 20 database cases. On
current source `5de803c2cd`, Linux [job
122593415890](https://github.com/paperclipai/paperclip/actions/runs/40620576702/job/122593415890)
executes all 20 project-tool cases successfully (9.480 seconds),
including the committed-project lost-response regression. Its shard
passes 1,627 cases with three unrelated skips. Server typecheck passes.
The transport regression initially has two failures; a subsequent
incorrect expectation about schema defaults was corrected before the
final pass. Ordinary execution adds no checkpoint writes or recurring
work.

Current candidate `5de803c2cd` includes the project-creation receipt
recovery fix and master `2cd747d1b6`. Migration 0394 retains the prior
idempotent checkpoint DDL. Every master SQL file and journal entry is
preserved. Snapshots 0390–0394, migration checks and server typecheck
pass.

The ordinary warm-turn regression fails on its second turn before
`a219ab51ad`. After correction, three completed warm turns select the
correct prior checkpoint and preserve the original session. All 288
provider-wiring cases pass. The control plane, external persistence
callback, identity rotation and filesystem are real; the backend and
database-load seam are scripted. The 13 warm admission/configuration
variants also pass after final master integration. This does not qualify
an underlying harness.

At current source `5de803c2cd`, Linux [job
122593415907](https://github.com/paperclipai/paperclip/actions/runs/40620576702/job/122593415907)
passes all 940 native-session-executor cases (61.431 seconds), including
the warm external-checkpoint behavior; the shard passes 2,358 cases with
one skip. Current [Runner job
122593415475](https://github.com/paperclipai/paperclip/actions/runs/40620576702/job/122593415475)
passes the native recovery lane: 115 passes and five container-only
skips, including the encrypted restore guard, fresh preparation (63.189
seconds), blocked claim lock (1.226 seconds) and recovery beyond 101
blocked owners (3.897 seconds). The first lane passes 2,215 cases with
one skip. The SQL cost test reports zero idle and streaming SQL for both
output volumes; this is component evidence, not a production capacity
claim. At prior candidate `1ff7bb44f4`, Linux job 122549307215 passes
all 938 native-executor cases, including the three-turn
external-checkpoint regression. Job 122549306469 passes the native
recovery lane with 115 passes and five container-only skips, including
the encrypted-restore session guard, fresh preparation (63.664 seconds),
claim-lock contention (3.961 seconds), and 101-owner recovery (3.824
seconds). This Mac skips the real-Postgres suite. The earlier full local
executor attempt has 921 passes and 16 failures during embedded-Postgres
initialization before assertions. The original fixture collisions and
their corrections remain recorded.

At earlier candidate `8fbe62b41d`, server shard job 122535673841 has
3,876 passes, one failure and one skip in an unchanged expiry-worker
retry/drain timing test. The same server/database tree on qualification
source `b64cac6951` passes all 20 expiry cases and all 3,877 shard
cases. The single diagnostic retry, job 122541891915, passes all 20
expiry cases and all 3,877 shard cases (one skip). No assertion was
weakened. The original failure remains recorded and its exact timing
race is not proven. All Actions checks and legacy status passed on
`8fbe62b41d`, with completed Greptile 5/5 and no open feedback. The next
candidate `1ff7bb44f4` also passed all Actions checks and Greptile 5/5.
Its Paperclip Review remained queued before the new master conflict
required integration. The new candidate needs fresh exact-head CI and
reviews; old checks do not qualify it. Migration generation is
checkpoint-only, all master SQL and journal entries remain unchanged,
and migration checks, server typecheck and all 13 warm-owner variants
pass after integration. The first migration check was blocked by TSX
sandbox IPC; its permitted repeat passes. The first warm-test command
used an incorrect path and ran no tests; the corrected command supplies
the 13-case receipt.

All 16 database pool-lifetime/options/teardown cases pass after granting
loopback permission; the first batch had two EPERM failures and 14
passes. Earlier semantic-tool/HTTP recovery, adoption, encryption-batch,
zero-streaming-write and idle-SQL receipts remain source-attributed in
the [qualification
record](https://github.com/paperclipai/paperclip/blob/2ff66f57da0da35bac1c19a2366ea4b84d87a986/doc/plans/2026-10-22-hot-upgrade-qualification.md).
That record retains earlier assertion, setup and cleanup failures and
their corrections. No failed gate is waived.

The historical 300-session component campaign passes four after phases:
steady read p99 is 107–108 ms at 500 reads/s and 100 writes/s;
aligned-burst read p99 is 103–105 ms after the bounded pool, versus
584–689 ms before. This comparison isolates the persistence corrections.
It does not measure the final stack's hosted active-agent capacity. The
hosted zero-agent API pair leaves SQL and CPU essentially unchanged, but
both arms fail absolute latency gates and candidate peak cgroup memory
fails its gate. The active-agent/viewer matrix, different-artifact
upgrade and rollback remain open.

Run `PAPERCLIP_TEST_DATABASE_URL=<owned-disposable-postgres> pnpm
--filter @paperclipai/server exec vitest run
src/services/native-runtime/native-runner-restart-recovery.integration.test.ts`.

Previous head `f2db8a6568` had one failed workspace setup gate: [job
122555672441](https://github.com/paperclipai/paperclip/actions/runs/40608984238/job/122555672441)
passes all 4,169 UI tests and 285 CLI cases (one skip), but the company
import/export suite reaches its 30-second health deadline during doctor
startup. Its output does not reach the server-start step. The CLI,
server, database, shared, adapter and plugin trees are identical on
qualification source `a866619d7b`, whose [job
122555886663](https://github.com/paperclipai/paperclip/actions/runs/40609057937/job/122555886663)
passes that fixture and all 286 CLI cases. The one diagnostic retry,
[job
122561057225](https://github.com/paperclipai/paperclip/actions/runs/40608984238/job/122561057225),
passes the formerly failing fixture and all 286 CLI cases. All Actions
checks and legacy status on `f2db8a6568` pass. Its exact-head Greptile
completed at 5/5 with no open feedback. The current integrated head
requires fresh checks and a completed Paperclip Review before merge. The
original failure remains recorded; its exact timing cause is unproven. A
repeated failure requires investigation and repair rather than another
retry.

All current-head CI, legacy status, exact-head completed Greptile 5/5,
resolved feedback and ordinary GitHub merge eligibility remain required
before merge.

The latest integration preserves master’s published Copilot v70 profiles
and every published migration. Checkpoint migration
`0394_conscious_whizzer.sql` was generated with `pnpm db:generate` and
retains the prior idempotent DDL. The Runner vendor rebuild and replay
parity checks pass. Server typecheck and all 13 warm-owner variants pass
after that rebuild. The imported report-lock patch is byte-identical to
master; its intentional patch-context whitespace is retained, and the
feature delta passes whitespace checks. No hosted experiment was run.

## Risks

The pool adds up to two active connections per instance, shared across
agents. Checkpoint delivery can wait behind that bound; it still
requires its commit. Steady component-test Postgres CPU rises by about
1.7 percentage points of a two-CPU allocation. These corrections do not
remove the stack's control and renewal writes. The migration and key
must survive replacement. Fresh-database tests and local load do not
establish different-artifact hosted upgrade or rollback continuity.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository tools and code
execution. The runtime does not expose a more specific model ID or
context-window size.

## 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
- [ ] 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
- [ ] All Paperclip CI gates are green
- [ ] 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 21:39:23 -05:00
DottaandPaperclip 8406047b4e fix(server): schedule join request retention from durable deadlines (#17508)
## Thinking Path

> - Paperclip manages agent companies and their access requests.
> - Pending join requests appear in the attention feed and can age into
automatic archival.
> - The maintenance tick reads those requests even when no archive is
due.
> - Existing request rows already store the activity timestamp needed
for retention.
> - Committed changes can wake a worker that registers the next actual
deadline.
> - This PR gives join-request retention its own bounded worker and
removes its recurring feed scan.
> - Idle and resolved history stop producing retention traffic without
changing access approval.

## Linked Issues or Issue Description

**What happened?**

Every retention maintenance pass reads pending join requests through the
attention feed. Empty, resolved, kept, and future-due states still
participate in recurring discovery.

**Expected behavior**

Committed request and retention changes wake the owner. It waits until
the next actual archive deadline and stops querying when no eligible
request remains. Existing approval and visibility behavior is preserved.

**Steps to reproduce**

Start an idle company with no pending join requests, or with requests
younger than the 90-day archive threshold. Observe the maintenance feed
repeatedly select join_requests. No state change is needed to cause
those reads.

This PR targets master after #17505 merged. Approval retention is
#17497. Other attention source kinds remain separate migration duties.
No duplicate open join-retention PR was found.

## What Changed

- Add a join-request retention owner that preserves pending status,
active-company visibility, matching invite ownership, and keep/archive
controls. Invite expiry or revocation retains the existing feed policy.
- Reuse one `createDecisionRetentionSweep` for approval and join paging,
atomic retention and finite scan cycles that revisit failures despite
continuing arrivals. Eligibility and source-row locks stay with each
owner.
- Register real saved deadlines, rounded upward in SQL to millisecond
precision, with the existing coordinator. Preserve 100-row pages and
thirty-second notification coalescing without idle polling.
- Materialize unseen join state inside the existing authorized
keep/archive/revive transaction, preserving archive choices and source
locking.
- Notify before request inserts and updates in the existing acceptance
and Slack transactions. Use a small SQL-only helper for replay and
approval/rejection updates. Company status, budget pause/resume,
deletion, and retention controls notify this owner too.
- Preserve the approved-only API-key consumption path without a new
wrapper; it cannot produce pending retention work.
- Exclude join requests from maintenance feed collection while retaining
normal UI reads.
- Add an idempotent concurrent partial pending index and prune old
schema snapshots through the existing generator.
- Document producer coverage, boundaries, failure evidence, and
remaining work.

## Verification

Test revision: `df1815fbba`. Normal integration of landed workspace
precision `d23dd19750` has an identical complete source tree. This
remains only the join-retention duty and its shared approval-retention
mechanics, targeting master.

- **55 worker cases pass**, including two new PostgreSQL regressions
that failed before repair: continuing overdue arrivals no longer
postpone a recovered failure indefinitely, while an entire failed page
still allows later healthy rows to progress.
- The nine complete suites passed 341 cases but failed an attention
cleanup hook after its 16,400-run case. After vacuum/analyze of only
that disposable fixture, the full 31-case attention suite passes
unchanged. Together with the original eight passing suites (311 cases),
all 342 distinct cases pass across complete-suite executions. Recovery
is established; the original cleanup slowdown is not claimed explained.
Server typecheck and production build pass.
- Earlier CI at `df1d4d0` failed the unchanged decision-expiry restart
deadline assertion (a thirty-second retry appeared before the saved
expiry). All twenty unchanged expiry cases pass locally, but the CI
timing cause remains unproven. No retry, timeout relaxation or assertion
change is used to dismiss it; fresh final-head CI remains required.

- **Five regressions failed before repair:** three unseen join controls
returned 404, and both owners advertised a microsecond deadline one
millisecond early. All pass. Two old real-producer expectations failed
after the correct rounding change; they now derive exact expected
milliseconds independently from database integer microseconds instead of
truncating a JavaScript Date.
- The initial timing-only concurrency test still passed a missing-lock
mutation. It now observes the actual PostgreSQL wait before releasing
the writer. Production passes; removing the lock fails with zero waiters
instead of one. Source is restored before commit. The archive-first
committed-order case confirms that later activity stays archived until
explicit revival; no broader race guarantee is claimed.
- Generated concurrent/idempotent `0393_open_grandmaster.sql` adds only
the pending join index. All 391 parent journal entries and historical
SQL files remain unchanged. Five generated snapshots remain (0389–0393).
Migration safety/numbering, schema drift and actual apply/replay on six
isolated PostgreSQL fixtures pass.
- Existing lifecycle cases retain real HTTP invite
acceptance/approval/rejection, Slack identity linking, rollback/outer
commit, source visibility, failure rotation, drain/resume and shutdown
coverage. Earlier stack setup and CI failures remain in the owning audit
and stack ledger; they are historical evidence, not this head's
acceptance.

Commands: server Vitest for `join-request-retention-work`,
`approval-retention-work`, `approvals-service`, `attention-service`,
`decision-retention-service`, `budgets-service`, `tool-gateway-service`,
`server-startup-feedback-export`, `scoped-deadline-work`; server
typecheck/build; DB migration checks, snapshot drift and actual
apply/replay. Fresh exact-head CI, security and Greptile/Ponytail 5/5
remain required. No full local repository-suite, deployed suspension or
cost claim is made.

## Risks

- A future supported producer must signal its transaction. Independent
SQL writers and another active server process are outside the
process-local notification contract.
- A writer that wins the source lock first is rechecked before archival.
If archival commits first, later activity preserves the archived state
until explicit revival. Existing invite replay expires before the
retention cutoff and Slack replay deduplicates. Future producers needing
renewed visibility must request revival. Cross-table visibility retains
the existing contract.
- The worker registers only under the existing maintenance enablement
switch. The internal deadline does not add Cloud alarm coverage or relax
conservative sleep blockers.
- New eligibility can wait within the thirty-second aggregate
notification window; known deadlines preempt it.
- Archival preserves pending approval status. Other attention sources
and aggregate maintenance remain separate work.

## 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
- [ ] All Paperclip CI gates are green
- [ ] 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 20:03:16 -05:00
DottaandPaperclip 553a004926 fix(server): schedule approval retention from durable deadlines (#17497)
## Thinking Path

> - Paperclip manages agent work and should become idle when it owes no
background work.
> - The attention feed archives old pending approvals under the existing
90-day policy.
> - The server rebuilds the feed on each maintenance tick, even when
approval state has not changed.
> - Some approvals have never been read, so retention rows alone cannot
define the work.
> - Approval records and committed eligibility changes define the real
deadline and wake triggers.
> - This PR gives approval retention its own deadline worker and reuses
the existing retention and archive-delivery contracts.
> - The approval lane stops idle scans while other attention sources
remain separate migration work.

## Linked Issues or Issue Description

Stacked on #17490. This PR owns approval retention only. Archive
notification delivery is already separated in #17475. Other
attention-source retention and remaining recurring server duties remain
in the stack inventory. The related-PR search found no overlapping
approval-retention worker migration.

**What existing behavior does this improve?**
Automatic archival of old pending approvals from attention.

**Current behavior**
The shared maintenance tick builds the attention feed for each active
company. That query discovers pending approvals and materializes
retention records before checking their age. An unchanged or empty
approval source is read repeatedly.

**Proposed behavior**
Reconstruct eligible approvals at startup. Schedule the earliest
approval activity timestamp plus 90 days. Wake after committed changes
to approvals, links, issue visibility, company activity, or retention
controls. Stop scheduling when no eligible approval remains. Archive
records through the existing version checks, audit writes, and
notification outbox.

**Reason and benefit**
Idle approval retention should generate no recurring SQL. Unseen
approvals still need eventual archival. A stale candidate must not
archive an approval that was resubmitted while the worker waited for its
source lock.

**Breaking changes**
None intended. The archive age, eligibility, keep controls, audit, and
notification policy remain. Normal attention reads still include
approvals. No Cloud sleep blocker is relaxed.

## What Changed

- Restrict tool-review signals to their existing terminal transition
owners (approve/reject/cancel). Creation/link/revision/resubmit signal
retention only.
- Coalesce retention notifications under one thirty-second aggregate
read budget. Known deadlines still preempt it; no notification or
durable deadline means no scan.
- Add a bounded approval-retention worker. It uses the shared deadline
coordinator and reads pending approvals directly.
- Share first-linked-issue ordering and execution visibility through
`approval-visibility.ts`, used by both the feed and worker. Preserve
actor-specific read filtering and locked archival rechecks.
- Materialize unseen approvals on demand inside manual
keep/archive/revive transactions. Use the saved activity timestamp and
existing versioned state; retain company scoping and rollback on
failure.
- Add awaited pre-write signals to approval, budget, tool, link,
visibility, company, and retention mutation owners. Preserve outer
transaction ownership; keep provider calls outside SQL wrappers.
- Narrow retention inputs to the source fields the existing service
uses. Signal materialization only for inserted or changed state so
unchanged reads converge.
- Exclude approval collection from the maintenance feed before its
query. Other source kinds remain in that sweep.
- Add the pending-only approval deadline index with concurrent,
idempotent creation. Keep the five newest migration snapshots.
- Add real-Postgres lifecycle tests and startup wiring tests. Enable
validated external test databases for existing attention and budget
suites.
- Document producer coverage, quiet conditions, recovery and remaining
scope in `doc/idle-approval-retention.md` and the stack ledger.

## Verification

Current test revision: `dd844f4e8cbbf1db681a774dc31cd552301bef48`,
followed by docs-only `3efda33812`, which received both 5/5 reviews and
passing CI. This candidate normally integrates landed Grok `2adcde162e`.
All runtime and acceptance-test sources are byte-identical to that
tested/reviewed tree; only four independent Runner E2E accounting files
and the ledger differ. Migration 0392 and the parent journal prefix are
unchanged. Local checks are reused for their unchanged source; fresh
final-head CI and reviews are required against master. This remains the
approval-retention duty.

- **315 cases pass** across eight complete suites: approval
retention/service, attention, decision retention, budgets, tool gateway,
server startup and scoped deadlines. All 30 real-PostgreSQL lifecycle
cases pass. Server production typecheck and build pass.
- **Four regressions failed before repair**: unseen young
keep/archive/revive returned 404; the manual keep rollback case could
not reach its update. They now pass. A failed keep update leaves no
retention row or audit; retry succeeds with one audit.
- Both linked-issue orders are checked against the feed: hidden
first/visible later is excluded; visible first/hidden later remains
eligible. The feed and worker use the same owner query, with actor read
filtering preserved.
- Generated concurrent/idempotent migration
`0392_small_frightful_four.sql` adds only the pending approval index.
Every historical parent SQL migration and all 390 parent journal entries
are unchanged. Five generated snapshots remain (0388–0392). Schema
drift, migration safety/numbering and actual fresh apply/replay on five
isolated PostgreSQL databases pass.
- Existing precision, transaction, drain/resume, failure fairness and
shutdown tests remain. Earlier transaction-stub, budget-import,
diagnostic-client and CI failures are recorded in the owning audit and
stack ledger; later passes do not erase them.

- Three additional real regressions failed before the wake repair:
ordinary mutations emitted tool-review signals in both resolution cases,
and sequential tenant writes caused repeated global reads. Tests now
prove terminal approve/reject/cancel signals and one aggregate
reconciliation per thirty-second window with a different company's
pending approval retained. The aggregate budget is stricter than a
separate per-company budget and matches the old default cadence; known
deadlines preempt it.
- The initial coalescing fixture expected the concrete pending-work hold
to clear before reconciliation; it now preserves that hold and measures
SQL. The first broad repair passed 313 cases but failed the existing
parked-review cancel case. The original cancel producer is restored; all
315 now pass without weakening assertions. At old head `5991f257bb`,
security was action-required and Greptile returned service error
`8fb1835c-e564-476b-b802-d5be164ea915`. Fresh repaired-head results are
required, not assumed.

Commands: server Vitest for `approval-retention-work`,
`approvals-service`, `attention-service`, `decision-retention-service`,
`budgets-service`, `tool-gateway-service`,
`server-startup-feedback-export`, `scoped-deadline-work`; server
typecheck/build; DB snapshot drift, migration checks and fresh
apply/replay. Fresh exact-head Greptile/Ponytail and CI remain merge
gates. No full local repository suite or deployed host suspension/cost
claim is made.

Earlier stack verification below is historical evidence at the
explicitly named revisions, not this head's acceptance:

Approved delegated Test-tab calls now evaluate caller and target
policies in the existing claim transaction. Real HTTP regressions first
showed a missing caller charge and shared-limit self-rejection. All 78
connection-management cases and 92 gateway cases now pass on the owning
branch, along with server production typechecking. All 78
connection-management cases also pass at combined tip
`d25f830db89c1623bff3547bfc68015c1c66e0cf`. The three new cases cover
shared charges, separate charges, rollback on denial, duplicate
approval, and rejection of a later authorized call. Intermediate
counter/identity fixture failures and a reused-database Slack collision
are recorded in [the owning
audit](https://github.com/paperclipai/paperclip/blob/20557d2c3dd0949c734eaa8095fd78b9b07d7fd5/doc/idle-approved-tool-recovery.md#delegated-test-tab-admission-after-policy-changes).
The earlier combined checks below remain evidence for their stated
revisions. Current-head CI and review remain required.

Earlier published head: `20557d2c3dd0949c734eaa8095fd78b9b07d7fd5`. This
remains a single-duty PR stacked on #17490.

- Integrated pinned master `8548173fc9efdc0fa07644fb92dcc0b6851a95e5`
with normal merges. Every branch passed generated-schema checks and its
target-branch migration-order gate. Existing SQL is unchanged; the 25
stack migrations follow master’s new migration.
- Duty-specific producer coverage, lifecycle tests, and earlier failure
records:
[2026-10-24-recurring-server-sleep-stack.md](https://github.com/paperclipai/paperclip/blob/76a49810c7ff403ae7720bef0d453747bd844731/doc/plans/2026-10-24-recurring-server-sleep-stack.md).
- Combined tip `51e6cbe67979b55b340ea9f0f434c5b5c682ccd6`: **355
startup/cancellation/recovery/dispatch/preservation tests**, **75
connection-management tests**, and production server typechecking pass.
These combined checks do not mean every intermediate PR independently
ran the full repository suite.
- Real master-to-stack database upgrade passes: **379 unchanged prior
migration receipts plus 25 new migrations**, with the gateway
responsibility fixture preserved.
- Owning-branch checks: 84 runner installation/settlement cases plus
runner typechecking and generated TypeScript/Rust profile agreement; 253
GitHub review workflow cases. Stop-attribution and held-worker readiness
regressions failed before their fixes.
- Failure accounting: the initial combined run passed 349 cases and
failed six idle assertions in a reused preservation database containing
old cancelled runs. Repeating the combined command with a fresh
preservation database passed. Earlier invalid project selection, merge
import/type errors, and the interrupted full local suite are not counted
as passing checks.
- **Latest-head CI and review remain required.** Prior CI findings
include GitHub idle observation, route teardown, attention cleanup,
touch-picker E2E, workspace health, and voice-cleanup timing; local
passes do not erase those failures. No full local repository-suite,
deployed sleep, fleet cost, or live Copilot qualification claim is made.

## Risks

- Signals cover the application database owner and its child pools.
Independent SQL writers and other active processes need an explicit wake
integration. Startup reconstructs committed durable state.
- The source row lock prevents stale approval-activity archival. It does
not add cross-table serializability for concurrent link, visibility, or
company-policy changes.
- Newly changed eligibility can wait up to the thirty-second
notification window, matching the former default maintenance cadence.
Saved deadlines still preempt; no new idle polling is added.
- Failed records retain recovery debt. Bounded cursor progress lets
later rows proceed without dropping failures.
- The shared retention transaction now groups materialization and
activity-version updates. Existing attention and retention consumers are
tested against PostgreSQL.
- Kept, archived, resolved or inactive-company approvals can become
quiet, but their supported mutations must retain their wake
notifications.
- The local scheduler cannot wake stopped compute. This PR neither adds
approval deadlines to Cloud's external alarm contract nor claims
measured host 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
- [ ] All Paperclip CI gates are green
- [ ] 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 19:23:24 -05:00
DottaandPaperclip 2adcde162e fix(server): wake Grok dispatch from committed reservations (#17490)
## Thinking Path

> - Paperclip manages agent work and should become idle when no work is
owed.
> - Grok routine dispatch already persists each outbound intent before
delivery.
> - Its dispatcher starts a one-second timer whenever public MCP is
configured.
> - That timer queries settings even when Grok is disabled, and scans
dispatches when enabled but idle.
> - The reservation transaction and feature-setting transaction already
define when work can become available.
> - This PR connects those writes to the shared coordinator and retains
the Grok service's claim and delivery policy.
> - The worker becomes quiet without adding a queue table or replaying
uncertain sends.

## Linked Issues or Issue Description

Stacked on #17486. This PR owns only Grok routine dispatch. It does not
include decision retention or other remaining maintenance owners.
Related open Grok PR #17278 concerns Runner credential-lease fixtures,
not this dispatcher. The duplicate search found no overlapping
dispatcher migration.

**What existing behavior does this improve?**
Grok routine dispatch recovery in the server.

**Current behavior**
With public MCP configured, the dispatcher reads instance settings every
second, including when Grok is disabled. With Grok enabled, it scans for
prepared dispatches every second. The baseline regression observed three
queries in 3.2 seconds with Grok disabled.

**Proposed behavior**
Reconcile once at startup. Wake from committed dispatch reservations and
experimental-setting changes. Schedule another pass only while prepared
dispatches remain or reconciliation failed. Disabled and settled workers
perform no recurring SQL or provider calls.

**Reason and benefit**
Remove unnecessary tenant database activity while preserving dispatch
recovery, current authorization, provider outcome semantics, and
shutdown ownership. This follows the existing sleep-aware maintenance
direction; ROADMAP review found no competing dispatcher redesign.

**Breaking changes**
No public API or provider payload change. The internal dispatcher start
method receives the shared coordinator; that coordinator now owns
shutdown. Sending, accepted, unknown, and terminal dispatches remain
non-replayable. This does not extend Cloud wake coverage.

## What Changed

- Register the Grok worker with the existing coordinator; remove its
independent timer.
- Signal reservation and experimental-setting writes before commit.
Nested writes follow their outer transaction.
- Preserve ten-row batches, durable send claims, current authority
checks, and existing cancellation/uncertainty policy.
- Join admitted work during shutdown and leave later rows for restart.
- Add a concurrent, replay-safe partial index for prepared dispatch
lookup. Keep only the five newest generated snapshots.
- Reuse the existing real OAuth/agent/routine fixture for lifecycle
tests and add a validated external PostgreSQL test option.
- Document producers, recovery, quiet conditions, and remaining
host-sleep limitations.

## Verification

Test revision: `6d81746fe260ee879442e7e0c879cfe88c250c8e`, followed by
docs-only head `679f523d78`, which received both 5/5 reviews and passing
CI. This candidate normally integrates landed target `8dfed893d8`. Its
entire runtime and acceptance-test sources match that reviewed/tested
tree; only four independently merged Runner E2E accounting files and the
ledger differ, preserved exactly. Migration 0391 and the parent journal
prefix are unchanged. Local checks were not repeated on unchanged
runtime; fresh final-head CI and reviews are required.

- **273 cases pass** in nine complete suites: real-PostgreSQL dispatch
lifecycle, Grok qualification, server startup, scoped deadlines and all
five instance-settings suites. Server typecheck and production build
pass with the updated Runner vendor.
- The baseline disabled-feature regression failed with three SQL reads.
Existing lifecycle cases cover first reservation, restart,
non-replayable history, re-enable, outer commit/rollback, drain,
failure/retry, concurrent enqueue, paging, foreground send race, expiry,
settings commit/rollback, and shutdown.
- Generated migration `0391_third_roulette.sql` adds only the
concurrent/idempotent prepared-dispatch index. All 389 parent journal
entries are unchanged; five generated snapshots remain (0387–0391).
Migration safety/numbering, schema drift and actual fresh apply/replay
on two isolated PostgreSQL databases pass.
- The earlier 259-case selection and 40,000-row generic-plan probe
remain historical evidence: both unchanged queries used the partial
index, six shared buffers including the routine join, 0.032ms / 0.025ms
local execution. No new deployed suspension or cost claim is made.
- Earlier browser CI failed during agent-chat fixture restoration (lock
timeout/HTTP 500), followed by a test seeing the experiment left
enabled. Those failures remain recorded in the owning audit; they were
not dispatcher assertions. Later pre-reconciliation `a74305e2b4` checks
were terminal and green. Fresh CI and both 5/5 bot reviews are required
for this head.

Commands: server Vitest for `grok-dispatch-work`, `grok-bot-routine`,
`server-startup-feedback-export`, `scoped-deadline-work` and the five
`instance-settings-*` suites; server typecheck/build; DB migration
safety/numbering, snapshot drift and fresh migration apply/replay.

## Risks

- Signals are local to the shared database owner and its child pools.
Independent SQL writers and other replicas need explicit wake
integration. Startup recovers committed prepared rows.
- Disabled Grok retains prepared rows and resumes after a committed
settings change. This does not relax Cloud's binding/assignment/hold
guards.
- Existing send-error cancellation policy is preserved. Sending or
unknown outcomes are not reset or automatically replayed; this is not an
exactly-once delivery change.
- Shutdown awaits the current send within existing transport timeouts
and stops before the next row.
- The index adds write/storage cost but bounds queries independently of
completed history. Existing migration execution runs concurrent index
creation outside a transaction.

## 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
- [ ] All Paperclip CI gates are green
- [ ] 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 19:07:53 -05:00
DottaandPaperclip e9e84bb8a1 fix(server): recover decision effects from saved claim deadlines (#17482)
## Thinking Path

> - Paperclip manages agent work and the decisions that authorize it.
> - A committed decision can leave effects unfinished after an
interrupted execution.
> - Recovery must preserve that debt without scanning settled history
forever.
> - Claims already store the state and time needed to schedule recovery.
> - A failed claim must get another attempt even while newer claims
arrive.
> - Malformed claims need a durable failure outcome instead of an
endless retry.
> - This PR uses committed signals, saved deadlines, and bounded
recovery rotations.

## Linked Issues or Issue Description

**What happened?**

Periodic decision maintenance queries for unfinished effects even when
none remain. A cursor that only wraps after a short page can postpone an
old failure indefinitely under continuous new work. Missing saved
deciders or chosen options can also leave non-executable claims scanning
forever.

**Expected behavior**

Open decisions and completed history produce no recurring
effect-recovery SQL. Committed claims wake recovery, and restart
reconstructs their deadlines. Genuine failures retain retries. Known
malformed outcomes settle with an audit and preserve completed effects.
Recovery must not invent authority, repeat committed effects, or lose
continuation delivery.

**Steps to reproduce**

Create an old failing claim with a one-row recovery page, then keep
adding newer due claims. Before the rotation fix, the old failure is
never revisited. Separately, recover a claim with a missing decider or
chosen option: the old implementation skips or throws on every attempt
without settling it. Creating an ordinary open decision also caused an
unnecessary effect scan.

Stacked on #17479. Other decision expiry, continuation, target and
retention duties have separate PRs in the stack.

## What Changed

- Coalesce sequential committed notifications for up to one second
without delaying already saved deadlines. This bounds instance-wide
recovery reads and adds no idle polling.

- Register effect recovery with the shared scheduler and
transaction-aware notifications. Derive deadlines from the existing
claim timestamp and recovery grace.
- Remove effect recovery from recurring aggregate maintenance when the
worker owns it. Preserve the explicit combined recovery entry point.
- Page within a frozen final timestamp/ID and retain PostgreSQL
precision at both boundaries. Failed claims and healthy later claims
both make progress.
- Load the current claim and derive recovery authority inside the shared
execution flight. Preserve foreground joining, current authorization and
per-effect locks.
- Recheck known malformed claims under a row lock. Save a failed
outcome, or partial when an executed effect receipt exists, with
`effectRecoveryError` and an atomic `decision.recovery_failed` audit.
Publish activity after commit and use normal continuation delivery.
- Avoid effect-recovery notifications for open-decision creation. Claim
and settlement writes retain their existing notification boundary.
- Add generated migration `0390_salty_killraven.sql`, the concurrent,
idempotent partial index for running-claim lookup. No queue table is
added.
- Cover quietness, transaction and lifecycle boundaries, retry fairness,
malformed outcomes, and concurrent repair. Document the contract in
`doc/idle-decision-effect-recovery.md`.

## Verification

Current candidate normally integrates landed expiry `3421366903`,
including expiry-only creation signals and all 21 independent master
changes. The test revision is
`dba8a79d1e1a5b97b8b5f6dac0f7c7837536531a`; the publication adds only
this evidence documentation.

- **143 current cases pass** across all 26 recovery, 54 decision, 20
expiry, 42 startup and one scoped-deadline cases. Server types/build
pass with the rebuilt Runner vendor (1,524 inputs and 1,563 outputs).
These results follow the landed dependency reconciliation; prior 69- and
141-case results remain historical evidence for their stated revisions.
- Generated migration `0390_salty_killraven.sql` adds the existing
running-claim partial index through the concurrent executor. All 388
parent journal entries are unchanged; five generated snapshots remain.
Schema drift, numbering/safety and actual migration apply/replay pass.
- Existing regressions exercise failed-claim rotation under sustained
arrivals, malformed decider/option outcomes, atomic audit failure,
row-lock repair races, current authority, foreground effect joining,
coalesced tenant notifications, transaction commit/rollback,
drain/resume, paging with PostgreSQL timestamp precision, and
shutdown/restart. Completed effects are not repeated.
- The first integrated run passed 140 cases and failed an overlap test
that assumed the effect deadline was the scheduler's earliest deadline.
Continuation coalescing now creates an earlier registered wake. The test
services that earlier wake before asserting the original effect grace
deadline; it still requires one shared effect flight, two lock attempts
including the test gate, one committed comment and one continuation. No
production timing was relaxed.
- Earlier red/green fairness, malformed-claim, notification
amplification and fixture failures remain recorded in [the owning
audit](doc/idle-decision-effect-recovery.md). Extra upstream
embedded-only tests attempted during parent integration could not
initialize locally; CI must exercise that environment.

- Previous candidate `f54a707d74` received both 5/5 reviews but its CI
server shard 6 was interrupted (run 40597921712). The step was
cancelled, with no failing assertion or suite summary, and the verify
gate also failed. No retry or passing claim was made for that run. The
necessary parent repair is included here; fresh CI and both reviews are
required.

Commands: server typecheck/build and Vitest for
`decision-effect-recovery-work`, `decisions-service`,
`decision-expiry-work`, `server-startup-feedback-export`, and
`scoped-deadline-work`; DB migration checks, schema snapshot drift and
real migration apply/replay. No full local repository-suite or deployed
sleep/cost claim is made. Fresh current-head CI and Greptile/Ponytail
are required before merge.

## Risks

Missing deciders and missing chosen options now produce durable failed
or partial outcomes instead of remaining running forever. Their audit
and receipts remain available; no effect executes under invented
authority. Only these known malformed states are settled this way.
Database, audit, and other execution failures retain retries.
Continuations retain their existing at-least-once delivery contract.

The supported writer model remains process-local notifications plus
restart reconstruction. Independent SQL writers and multiple replicas
gain no new wake protocol. Cloud admission and durable external alarms
are unchanged. These tests establish worker behavior, not deployed host
suspension or fleet savings.

## 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
- [ ] All Paperclip CI gates are green
- [ ] 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 18:24:55 -05:00
DottaandPaperclip 3421366903 fix(server): schedule decision TTL expiry from saved deadlines (#17479)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Open decisions have a saved expiry time.
> - The main maintenance tick scans those times even when none is due.
> - Committed creation and resolution can maintain the actual next
deadline.
> - The existing scheduler can run expiry only when that deadline
arrives.
> - This pull request gives decision TTL expiry its own worker.
> - Terminal history becomes quiet while expiry and continuation
delivery remain recoverable.

## Linked Issues or Issue Description

Targets master after #17478 merged. This is slice 10 of the recurring
server work migration. It owns decision TTL expiry. Target invalidation,
interrupted effects, and retention remain separate migrations. The full
ledger is in `doc/plans/2026-10-24-recurring-server-sleep-stack.md`.

**What existing behavior does this improve?**

Time-based decision expiry in the server.

**Current behavior**

The combined decision maintenance sweep queries expired open decisions
on each tick. It mixes that query with target invalidation, effect
recovery, and continuation delivery.

**Proposed behavior**

Creation, bundle creation, and existing decision updates notify the
shared scheduler. A dedicated worker runs at the first saved open
expiry. Resolving an open decision removes its deadline. The combined
tick opts out of TTL selection while its other duties remain intact.

**Reason and benefit**

A future decision no longer requires repeated TTL queries before its
deadline. Terminal history and an empty decision table leave no
recurring TTL work. No queue table is added.

## What Changed

- Add an expiry owner for bounded due selection, next-deadline lookup,
and expiry settlement. Reuse it for expiry-on-use and the existing
target expiration transition.
- Use the central expiry-only creation notification before insert,
including bundles and caller-owned transactions. Update and terminal
transitions continue to notify expiry and continuation.
- Register the enabled-heartbeat TTL worker before decision
housekeeping. Preserve disabled-heartbeat behavior and the public
combined sweep's default batch limit.
- Honor `PAPERCLIP_DECISIONS_SWEEP_BATCH_SIZE` (default 100). Keep a
fair expiry/ID cursor across failures. Stop between candidates on
shutdown and reconstruct saved work after restart.
- Recheck open status and due TTL during settlement. Merge metadata and
persist activity atomically with expiry. Publish activity and deliver
continuation after commit. Handle exact timestamp equality.
- Continue successful overdue batches after one second through the
existing deadline adapter; preserve thirty-second failure retry and all
default callers.
- Add generated migration `0389_brave_hulk.sql`, an idempotent
concurrent partial index on open expiry and ID. Preserve five generated
snapshots.
- Add 20 real-PostgreSQL lifecycle cases and startup ownership
assertions. Document contracts and limitations in
`doc/idle-decision-expiry.md`.

## Verification

Current candidate: `aa03d2c4c25af4e6c2acf49d3160bbaf6ada030b`. This PR
targets master after #17478 merged. Creation now signals only its real
expiry obligation; terminal updates preserve continuation delivery.

- **117 current cases pass** across all 20 expiry, 54
decision/continuation, 42 startup and one scoped-deadline cases. Server
typecheck/build pass. Separate migrated PostgreSQL fixtures exercise
real transaction and notification behavior.
- Single and bundle creation each failed a regression before the
expiry-only signal repair: open rows unnecessarily notified continuation
recovery. Both now arm their saved expiry without a continuation signal.
Cancelling each row afterward still signals and delivers continuation
normally. The distinction is in the existing central notification owner;
there are no per-route flags or counters.
- **Three new real PostgreSQL regressions failed before repair:** a
configured batch of two was ignored, a successful 101-row backlog waited
for the thirty-second error retry, and an activity insert failure left
the row expired without its audit. All three pass. The audit failure
rolls back expiry; retry records one activity, preserves unrelated
metadata and wakes once.
- The shared deadline adapter has optional `continuationMs`. Only this
owner opts into one-second successful overdue continuation. Other
callers keep the previous default, and errors still retry after thirty
seconds. The archive consumer passes with the option absent.
- Migration `0389_brave_hulk.sql` is regenerated from the parent's
metadata. Parent SQL/journal entries are preserved, five generated
snapshots remain, schema drift and migration numbering/safety pass.
Fresh migration apply and replay pass.
- Lifecycle coverage includes first producer activation, future-deadline
quiet, terminal history, exact equality, commit/rollback, restart,
drain/resume, failed expiry and wake delivery, fair paging through
failed rows at PostgreSQL timestamp precision, shutdown and recovery.
- Failure accounting: the first full repair selection passed 113 cases
and failed a startup assertion that observed a pending follow-up
notification before reconciliation. The assertion now waits for the
exact saved future deadline, then still proves no early expiration.
Earlier fault-fixture and timestamp-cursor failures remain recorded in
[the behavior audit](doc/idle-decision-expiry.md). Extra upstream
embedded-only tests attempted on the parent could not initialize
locally; CI must cover that environment.

Commands: server typecheck/build and Vitest for `decision-expiry-work`,
`decisions-service`, `decision-retention-service`,
`server-startup-feedback-export` and `scoped-deadline-work`; DB
migration checks, snapshot drift and actual migration apply/replay. No
full local repository-suite or deployed sleep/cost claim is made. Fresh
current-head CI and Greptile/Ponytail remain required before merge.

## Risks

- Expiry uses `expiresAt <= now`; the old combined loop skipped exact
equality. The deadline boundary is now covered explicitly.
- TTL expiry records the TTL reason. Target-driven expiry preserves its
own reason. When both causes apply, the first terminal transition wins.
- Supported producers share the application DB signal owner. Independent
SQL writers or multiple active processes need a separate notification
contract. New deadline-edit APIs must use the existing helper.
- The index requires top-level concurrent migration execution. It is
registered with the migration owner and replayed in verification.
- Future in-process deadlines are not external Cloud alarms. Existing
sleep guards remain conservative. This PR does not claim host/database
suspension or measured savings.

## 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
- [ ] All Paperclip CI gates are green
- [ ] 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 18:10:36 -05:00
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
DottaandPaperclip 5785090ff1 fix(server): wake archive notification delivery from committed changes (#17475)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Auto-archive writes an existing notification outbox in its archive
transaction.
> - The maintenance sweep polls that outbox even when it is empty.
> - The committing producer can wake delivery. Saved rows and claim
timestamps can restore unfinished work.
> - This PR separates delivery from retention scanning and uses the
existing coordinator for retries and deadlines.
> - Settled delivery becomes quiet while archive and notification
behavior remain intact.

## Linked Issues or Issue Description

**What happened?**

The retention maintenance sweep calls archive notification delivery on
every tick. An empty outbox and retained delivered history still cause
database work.

**What did you expect?**

Committed auto-archive should wake delivery. Pending work and existing
claims should retain retry or expiry deadlines. Settled history should
cause no recurring delivery SQL.

**Steps to reproduce**

Start a configured instance with an empty or fully delivered archive
outbox. Leave it idle. The old maintenance tick still checks the outbox.

This is slice 08 of the sleep migration. It remains stacked on
projection repair #17468. Retention eligibility and scanning, decision
TTL expiry and decision continuation recovery have separate owners and
PRs.

## What Changed

- Notify archive delivery before the existing outbox insert in the
auto-archive transaction. Preserve outer transaction commit, rollback
and uncertainty handling.
- Register a delivery worker with the existing coordinator. Remove
notification delivery from retention sweeps.
- Keep claim recovery, selection, company/agent grouping and
timestamp-fenced settlement in the delivery service. Keep the existing
service API and payload.
- Count a notified group only when it settles rows. Use one timestamp
for failure reset.
- Add generated concurrent migration
`0387_boring_professor_monster.sql`. Its partial index matches pending
order: `last_attempt_at NULLS FIRST, created_at, id`. Add no table.
- Test real startup registration and prove a retention tick does not
send notifications. Preserve the disabled-scheduler no-notification
contract.

## Verification

Current candidate: `457c70fc6acbd681fcccd90d08b6391efb202bce`, normally
merged with pinned master `8161f2a813` after projection #17468 landed.
All 39 independent files from that master integration are preserved
exactly. Archive owner behavior and migration 0387 are unchanged.

- **58 archive/startup/scoped-deadline cases pass** after master
reconciliation. Server typecheck and build pass. The preceding
projection-parent integration passed all **239**
gateway/projection/archive/startup/deadline cases.
- All 385 parent journal entries remain an exact prefix, with own
migration 0387 appended; the five newest generated snapshots remain.
Generated index apply, replay, schema drift and real pending-order query
measurements passed in the prior owner verification.
- Failure accounting: the broader local command also attempted six
upstream heartbeat-session-preparation and 55 native control-plane
cases. The embedded PostgreSQL runtime was unavailable: the six
heartbeat cases skipped, and native setup failed before its 55 cases
could execute. This is an incomplete additional local check, not a
passing full run. The owning 58 cases passed with the existing external
fixture where applicable. CI must exercise its supported embedded
environment.
- The first independent-master comparison used the shared mutable remote
ref, which another worktree advanced during verification. Pinning the
actual merged master SHA confirms all 39 independent files match
exactly. No incoming source change was dropped.

The behavior audit in `doc/idle-decision-archive-delivery.md` records
earlier fixture failures, the stale-claim regression and the index
probe. Local tests do not prove deployed suspension or fleet savings.
Fresh current-head CI and both reviews remain required.

## Risks

- Delivery remains at least once. A wake accepted before an
acknowledgement is lost can be retried.
- Each pass keeps the existing 500-row limit. Failed or remaining
pending work retains the existing thirty-second retry. Saved delivery
claims retain their five-minute deadline.
- Auto-archive is the existing outbox producer. Manual archive/revive
and reviewed-manifest actions do not gain notifications.
- Signals use the existing single-active-writer database owner.
Independent SQL or another active server needs a wake contract.
- Concurrent index creation uses the existing top-level executor and
invalid-index retry path. Existing SQL and migration receipts are
unchanged.
- Cloud sleep guards and external-wake coverage do not change. Local
worker quiet is not proof of database or host 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


The final projection efficiency parent is integrated normally. All 239
affected gateway/projection, archive, startup and scoped-deadline cases
pass, with server typecheck/build. The archive owner behavior and index
remain unchanged. Fresh reviews and CI are required on this parent
integration.

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 17:26:54 -05:00
DottaandPaperclip 8161f2a813 fix(server): wake tool-review card repair from committed changes (#17468)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Tool requests and invocations store the execution outcome. Review
cards show that outcome.
> - The maintenance tick repeatedly joins these tables even when every
card matches.
> - Committed changes can wake repair. Existing rows can restore work
after restart.
> - A large company backlog also needs a bounded pass so other companies
and shutdown can proceed.
> - This PR uses the shared coordinator for scoped repair, continuation
and failure deadlines. Settled history becomes quiet.

## Linked Issues or Issue Description

**What happened?**

The aggregate maintenance tick scans tool-review projections on every
tick. Matching terminal history still causes database work. A company
with many stale cards can also hold one worker in an unlimited drain.

**What did you expect?**

Relevant writes and startup should wake repair. Failed or unresolved
cards should retain recovery. Matching history should cause no recurring
SQL. Each normal worker pass should honor drain and shutdown.

**Steps to reproduce**

Start an instance with completed tool requests and matching cards. Leave
it idle. The old maintenance tick keeps querying for mismatches. A large
mismatch backlog also drains without a row limit or cancellation check.

This is slice 07 of the sleep migration. It now targets master after
#17458 landed. Related owners are pending expiry #17453 and approved
recovery #17454. Their execution duties remain separate from card
repair. The prior open-PR search found no duplicate projection
migration.

## What Changed

- Register company-scoped projection repair with the existing delivery
coordinator. Remove its recurring aggregate-tick scan.
- Signal through the existing review-write transaction owner and linked
interaction mutations. Ordinary unlinked confirmations do not wake
repair.
- Keep interpretation, locks, current-state checks, activity and
continuation callbacks in the gateway. A repair never repeats a provider
tool call.
- Limit each normal worker pass to 500 rows. Check abort and admission
between rows. Save company progress through truncated and failed passes
so blocked early pages cannot hide later cards.
- Schedule truncated work after one second. Back off unresolved cards
from thirty seconds to five minutes; fresh committed hints reset the
delay. Clear obsolete cursors after empty reconstruction.
- Skip repeated company discovery for scoped hints and the extra
candidate query on an empty sweep with no saved cursor. Attempted or
resumed passes still verify remaining mismatches because reflection
returns no settlement result.
- Add generated migration `0386_steady_epoch.sql` for the existing
`(company_id, interaction_id)` lookup. Use the existing concurrent-index
executor. Add no table.

## Verification

Current candidate: `d4706fbf2a04a00ade30181eb1bd9322e8455818`. All 222
cases in the full gateway/projection/interruption, startup and
scoped-deadline suites pass without skips after the query/retry
efficiency repair; server types and build pass. A single committed
company hint with matching retained history and a separate empty direct
sweep each perform one candidate query. The unresolved-card regression
failed before repair and now verifies backoff through the five-minute
cap, prompt producer activation and quiet after settlement. An initial
assertion incorrectly assumed an entire approval emitted one wake; its
separate real writes require isolating the single-hint measurement.

The earlier master integration `fe6e10629b` additionally established the
following evidence before this efficiency follow-up:

- **320 cases pass** across full gateway/projection/interruption,
startup, scoped-deadline, narrowed identity-read and authenticated
gateway HTTP suites. The HTTP selection includes all 81 cases.
- **Six real PostgreSQL regressions failed before their fixes:** pass
bounds, abort, drain, progress past unprojectable pages, progress past
failed pages, and first repair after empty reconstruction. The complete
owning selection passes all 220 cases.
- Server production typechecking and build pass. Generated index
migration, snapshot drift and numbering/safety checks pass. The five
newest snapshots remain; master journal entries are unchanged.
- A real missing-link query on 30,000 history rows changed from a
sequential scan that filtered all 30,000 rows and touched 429 buffers to
the new index with two buffers. Both treatments returned no rows.
- Normal master integration preserves 35 independent performance files
exactly. The gateway also retains master's narrowed identity read.

Commands include `pnpm --filter @paperclipai/server typecheck`, `pnpm
--filter @paperclipai/server build`, and server Vitest selections for
`tool-gateway-service`, `server-startup-feedback-export`,
`scoped-deadline-work`, `run-identity-projections`, and
`connection-management.integration`, with separate PostgreSQL test
fixtures.

[The behavior and failure audit](doc/idle-tool-review-projection.md)
records the producer contract, earlier fixture failures, index
measurement, red/green regressions and integration evidence. Fresh
current-head CI and Greptile/Ponytail remain required before merge.

## Risks

- Signals use the existing single-active-writer database owner. Another
replica or independent SQL writer needs a wake contract.
- An already issued database query or reflection finishes before
cancellation takes effect. Checks between rows bound further dispatch.
- Legacy manual/global and disabled-scheduler one-shot repairs keep
their complete-sweep behavior. The normal coordinator worker always
supplies its row budget and admission controls.
- Unprojectable cards and thrown failures retain recovery with a
five-minute backoff cap. The company mismatch query still grows with
terminal history. Empty history does not retain a recurring scan.
- The index migration uses concurrent creation and the existing
invalid-index recovery path. Prior SQL and journal entries remain
unchanged.
- Cloud sleep eligibility and external-wake coverage do not change.
Local worker quiet is not proof of host 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
- [ ] All Paperclip CI gates are green
- [ ] 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:09:31 -05:00
DottaandPaperclip f6bc653f7f fix(server): schedule interrupted tool claims by their deadlines (#17458)
## Thinking Path

> - Paperclip governs tool execution and keeps durable approval records.
> - An interrupted execution may have produced an external effect
without a saved final request state.
> - The maintenance tick currently scans executing requests even when
none exist.
> - Every executing claim already has a ten-minute stale deadline.
> - This PR schedules that deadline from committed claim changes and
clears it after settlement.
> - Recovery preserves saved provider outcomes and quarantines unknown
outcomes without another provider call.

## Linked Issues or Issue Description

Refs #17454. This is the sixth recurring-server sleep slice, stacked on
`codex/sleep-05-approved-tool-recovery`.

**Problem**
The aggregate maintenance tick repeatedly searches for stale executing
tool requests. Its settlement can overwrite a saved successful
invocation with an unknown-outcome failure after a crash between
invocation and request writes.

**Proposed Solution**
Use the existing review notification boundary and shared scheduler. Read
the earliest executing claim deadline through a partial index. At that
deadline, lock invocation then request, recheck claim identity, and
atomically settle from the saved outcome or quarantine the unknown
outcome.

**Alternatives Considered**
A longer polling interval still queries empty work. A new queue would
duplicate existing records. Replaying an interrupted call can duplicate
an external effect. Treating every stale request as unknown discards
already saved outcomes.

## What Changed

- Coalesce sequential review notifications for up to 30 seconds while
preserving already loaded recovery deadlines. Do not schedule scans when
no notification or execution deadline remains.

- Extract interrupted-execution deadline lookup and settlement into the
owning tool-review service.
- Register its worker at startup. Use committed claim/result changes to
update the existing ten-minute deadline; retry failed settlement after
30 seconds.
- Preserve terminal invocation results, reasons, and completion times
when repairing request state.
- Settle uncertain request/invocation state atomically with the existing
unknown-outcome reason, without dispatching a provider call.
- Notify all gateway claim and settlement paths through the existing
review transaction helper. Keep framework wiring and the shared
scheduler free of provider-specific behavior.
- Add an idempotent concurrent partial index for executing deadlines.
Preserve the combined manual sweep and scheduler-disabled startup pass.
- Remove interrupted scanning from the aggregate tick. Interaction
projection repair remains the next separate duty.

## Verification

Current head: `648d8a9a64ded15deeeb331545b92a12d69e277d`. The final
commit changes no files and requests CI after the master retarget. A
preceding run appeared while that trigger was being published; it was
observational delay, not an established CI outage. No test rerun or
bypass was used to turn a failure green. Source remains identical to the
198-test passing integration, apart from the already recorded ledger
update. Fresh checks and both automated reviews must qualify this
current head before merge.


Current master-targeted head:
`d55e44b376555e4926bb2c2a0e4dcfb431324162`. Parent #17454 landed as
`77295faebc8`; a normal merge reconciles that squash. Every conflicted
master file was checked against the already-integrated parent, and all
matched byte-for-byte. Each interruption-only hunk was retained; the
complete integrated source tree matched the 198-test passing
`848f1b87b3` exactly before the sole final documentation update. All
master migration journal entries are unchanged, with only this PR's
executing-deadline index appended. Server types pass. Fresh CI and
current-head Greptile/Ponytail are required before the next authorized
merge.


Latest published integration:
`848f1b87b3a6646ae4e56e8ab142f64889d7a69c`, normally merging
approved-recovery parent `23868c1751`. All 198
gateway/interruption/startup/deadline cases and server production
typechecking pass. Both parent approval fixes and all interruption tests
are retained. The two merge conflicts preserved the interruption import
alongside the parent's claim-bound hash helper and preserved the
parameterized historical test helpers. This remains stacked on #17454;
fresh CI and both automated review gates are required after integration.


- Review documentation correction at
`c985eb9d1b48a481909de6a1c2d89d7af11bfc03`: the focused real-PostgreSQL
interruption-worker selection passes all 16 cases; 124 unrelated cases
were filtered out. The docs now correctly name 16 cases and six saved
terminal statuses. No production behavior changed in the correction.
Fresh remote CI/review is pending after integrating the verified parent.

Approved delegated Test-tab calls now evaluate caller and target
policies in the existing claim transaction. Real HTTP regressions first
showed a missing caller charge and shared-limit self-rejection. All 78
connection-management cases and 92 gateway cases now pass on the owning
branch, along with server production typechecking. All 78
connection-management cases also pass at combined tip
`d25f830db89c1623bff3547bfc68015c1c66e0cf`. The three new cases cover
shared charges, separate charges, rollback on denial, duplicate
approval, and rejection of a later authorized call. Intermediate
counter/identity fixture failures and a reused-database Slack collision
are recorded in [the owning
audit](https://github.com/paperclipai/paperclip/blob/f6a5949fb726f71c247950714adecb275655284f/doc/idle-approved-tool-recovery.md#delegated-test-tab-admission-after-policy-changes).
The earlier combined checks below remain evidence for their stated
revisions. Current-head CI and review remain required.

Current head: `f6a5949fb726f71c247950714adecb275655284f`. This remains a
single-duty PR stacked on #17454.

- Integrated pinned master `8548173fc9efdc0fa07644fb92dcc0b6851a95e5`
with normal merges. Every branch passed generated-schema checks and its
target-branch migration-order gate. Existing SQL is unchanged; the 25
stack migrations follow master’s new migration.
- Duty-specific producer coverage, lifecycle tests, and earlier failure
records:
[2026-10-24-recurring-server-sleep-stack.md](https://github.com/paperclipai/paperclip/blob/eaac8d343e5ef4d2c13cdf7cb7607cb5a498e9a2/doc/plans/2026-10-24-recurring-server-sleep-stack.md).
- Combined tip `51e6cbe67979b55b340ea9f0f434c5b5c682ccd6`: **355
startup/cancellation/recovery/dispatch/preservation tests**, **75
connection-management tests**, and production server typechecking pass.
These combined checks do not mean every intermediate PR independently
ran the full repository suite.
- Real master-to-stack database upgrade passes: **379 unchanged prior
migration receipts plus 25 new migrations**, with the gateway
responsibility fixture preserved.
- Owning-branch checks: 84 runner installation/settlement cases plus
runner typechecking and generated TypeScript/Rust profile agreement; 253
GitHub review workflow cases. Stop-attribution and held-worker readiness
regressions failed before their fixes.
- Failure accounting: the initial combined run passed 349 cases and
failed six idle assertions in a reused preservation database containing
old cancelled runs. Repeating the combined command with a fresh
preservation database passed. Earlier invalid project selection, merge
import/type errors, and the interrupted full local suite are not counted
as passing checks.
- **Latest-head CI and review remain required.** Prior CI findings
include GitHub idle observation, route teardown, attention cleanup,
touch-picker E2E, workspace health, and voice-cleanup timing; local
passes do not erase those failures. No full local repository-suite,
deployed sleep, fleet cost, or live Copilot qualification claim is made.

## Risks

- Signals cover the existing single-active-writer database owner.
Independent writers and replicas need an explicit wake contract.
- The ten-minute policy is unchanged. A provider call that outlives its
claim remains an uncertain outcome; this worker never retries the
external call.
- Interaction projection follows authoritative settlement outside its
transaction. Its separate existing repair duty still handles an
interrupted projection.
- Non-executing review writes can cause one empty reconciliation.
Without an executing claim or failed attempt, no recurring deadline
remains.
- Cloud eligibility and external-alarm coverage remain conservative.
Worker quiet does not prove actual host 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
- [ ] All Paperclip CI gates are green
- [ ] 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 16:33:30 -05:00
DottaandPaperclip 77295faebc fix(server): recover approved tool actions from committed changes (#17454)
## Thinking Path

> - Paperclip manages agent work and its governed tool calls.
> - A human approval can persist before the approving process dispatches
the tool.
> - Recovery currently scans approved requests on every maintenance
tick.
> - Completed and deliberately inert approvals do not owe another
execution.
> - Test-tab approval also needs a shared execution claim before
recovery can safely race the foreground path.
> - This PR wakes recovery from committed changes and retries only
unresolved approvals.
> - Idle history becomes quiet while existing approval and execution
checks remain with the gateway.

## Linked Issues or Issue Description

Refs #17453. This is the fifth slice of the recurring-server sleep
migration, stacked on `codex/sleep-04-tool-review-expiry`.

**Problem**
Approved tool requests are scanned periodically even when no executable
work remains. The Test-tab foreground path lacks the request claim
needed to admit concurrent recovery safely.

**Proposed Solution**
Use the existing transaction-aware review notification boundary and
shared deadline worker. Keep signed-payload interpretation, current
policy and target checks, and provider dispatch in the gateway. Share a
claim between Test-tab foreground and recovery, then persist both
authoritative outcome rows atomically.

**Alternatives Considered**
A longer interval still scans idle history. A new durable queue would
duplicate existing request/invocation state. Dispatching Test-tab
approvals without a claim risks duplicate external calls.

## What Changed

- Recheck both principals for delegated Test-tab approvals. Save their
rate receipts in the same claim transaction, share only the same bucket,
and roll back new charges on denial.

- Preserve delegated gateway authorization and both issuer/target rate
receipts inside the existing claimed-invocation transaction; retain the
durable actor during approved replay.

- Expire stale managed connector payloads before consuming approved-tool
admission.

- Persist current rate-policy charges and their invocation receipts
atomically before dispatch, preserving exact approval claim fencing and
read-only preflight.

- Recover approved requests at startup and after committed review
changes; retry only unresolved approvals.
- Leave legacy agent approvals without execute-on-approve intent inert.
Repair completed Test-tab history without another provider call.
- Fence Test-tab execution and outcome settlement with an atomic claim
shared by both callers. Revalidate the approved target and current
policy before dispatch.
- Keep provider calls outside database transactions. Retain quarantine
for interrupted execution and unknown outcomes.
- Add a concurrent, idempotent partial index for approved-request
pagination. Reuse the parent's company-scoped review signals and shared
scoped deadline worker.
- Remove approved scanning from the aggregate tick. Preserve the public
combined sweep and scheduler-disabled startup behavior.
- Document producer ownership, quiet states, lifecycle behavior, and
remaining recovery duties.

## Verification

Latest published head: `23868c1751c100a27f58087f7ce16b93c52d4eba`,
targeting master. The review-found delegated gateway pool deadlock is
repaired by passing the admission transaction through token and
issuer-authority reads. Foreground and recovery each reproduced the
deadlock with a one-connection pool; all three constrained-pool cases,
including revoked authority, now pass. All 81 HTTP integration cases
pass. The historical foreground rate-slot regression also reproduced
before its repair; verified preview hashes are accepted only by
read-only preflight, while consuming admission still requires the
complete identity under the exact claim. Five full suites passed 329
cases; the gateway service then passed all 141 cases after two
additional mixed historical hash controls. Production types/build passed
before the final normal integration of master `35820cf398` (including
independent Grok/Runner changes); integration validation is running.
Initial fixture/assertion errors and the prior E2E
restoration/cancellation deadlock remain recorded in [the owning
audit](doc/idle-approved-tool-recovery.md#gateway-admission-uses-its-transaction-connection).
Fresh-head CI, Greptile 5/5, and passing Ponytail are still required
before merge.


- Master landing reconciliation at
`8dfe3958db6e4e8a0b7499f3f5be0a396ca77361` after #17453 merged:
production and server test files are byte-identical to the accepted
320-case repair. The landed migration journal prefix is unchanged, and
#17928's independent eval reporting changes are preserved. Fresh CI and
reviews on this exact head are required before merge.

- Historical-identity repair published at
`096b7c7198c3ca864aca9377e261cbaa0a8aad6f` (production/test source
identical to integrated acceptance
`81231824d773a79fc50c835eccf7d9b3977e79fc`): all 320 accepted gateway,
policy, startup, preparation-timing and authenticated HTTP management
cases pass. Server types and the production server build pass. Four real
historical sensitive-argument regressions failed before repair;
invalid-signature, foreign-hash and stale-claim controls remain denied
without dispatch or identity changes. Both agent and Test-tab calls
deliver the original signed arguments once, and new rate policies still
apply.
- The first integrated combined run passed 319 cases and hit a Slack bot
ownership collision left in a reused HTTP test database. The guard
correctly returned 409. The full 78-case HTTP suite passed on a fresh
dedicated fixture without product or assertion changes. Earlier
fixture-construction errors are distinguished from the four real
regressions in the audit document. Fresh remote CI and reviews remain
required.

- Parent reconciliation at `736e7f56097055e21ce13b4e45d9033391483af8`:
299 cases pass (221 full gateway/policy/startup cases and 78
assembled-server connection management cases), with no skipped cases.
Server types pass. This includes real delegated Test-tab approval
admission for newly added shared and caller rate rules. Fresh remote CI
and Greptile review remain required.

Approved delegated Test-tab calls now evaluate caller and target
policies in the existing claim transaction. Real HTTP regressions first
showed a missing caller charge and shared-limit self-rejection. All 78
connection-management cases and 92 gateway cases now pass on the owning
branch, along with server production typechecking. All 78
connection-management cases also pass at combined tip
`d25f830db89c1623bff3547bfc68015c1c66e0cf`. The three new cases cover
shared charges, separate charges, rollback on denial, duplicate
approval, and rejection of a later authorized call. Intermediate
counter/identity fixture failures and a reused-database Slack collision
are recorded in [the owning
audit](https://github.com/paperclipai/paperclip/blob/bcca5b006976a471f4afd1f70c4031aa40a2b3f7/doc/idle-approved-tool-recovery.md#delegated-test-tab-admission-after-policy-changes).
The earlier combined checks below remain evidence for their stated
revisions. Current-head CI and review remain required.

Current head: `bcca5b006976a471f4afd1f70c4031aa40a2b3f7`. This remains a
single-duty PR stacked on #17453.

- Integrated pinned master `8548173fc9efdc0fa07644fb92dcc0b6851a95e5`
with normal merges. Every branch passed generated-schema checks and its
target-branch migration-order gate. Existing SQL is unchanged; the 25
stack migrations follow master’s new migration.
- Duty-specific producer coverage, lifecycle tests, and earlier failure
records:
[2026-10-24-recurring-server-sleep-stack.md](https://github.com/paperclipai/paperclip/blob/a021467f04d22be42ee5b0c87b6583b2300a62a9/doc/plans/2026-10-24-recurring-server-sleep-stack.md).
- Combined tip `51e6cbe67979b55b340ea9f0f434c5b5c682ccd6`: **355
startup/cancellation/recovery/dispatch/preservation tests**, **75
connection-management tests**, and production server typechecking pass.
These combined checks do not mean every intermediate PR independently
ran the full repository suite.
- Real master-to-stack database upgrade passes: **379 unchanged prior
migration receipts plus 25 new migrations**, with the gateway
responsibility fixture preserved.
- Owning-branch checks: 84 runner installation/settlement cases plus
runner typechecking and generated TypeScript/Rust profile agreement; 253
GitHub review workflow cases. Stop-attribution and held-worker readiness
regressions failed before their fixes.
- Failure accounting: the initial combined run passed 349 cases and
failed six idle assertions in a reused preservation database containing
old cancelled runs. Repeating the combined command with a fresh
preservation database passed. Earlier invalid project selection, merge
import/type errors, and the interrupted full local suite are not counted
as passing checks.
- **Latest-head CI and review remain required.** Prior CI findings
include GitHub idle observation, route teardown, attention cleanup,
touch-picker E2E, workspace health, and voice-cleanup timing; local
passes do not erase those failures. No full local repository-suite,
deployed sleep, fleet cost, or live Copilot qualification claim is made.

## Risks

- Notifications cover the existing database-owner, single-active-writer
contract. Independent SQL writers and other replicas need an explicit
wake path.
- Unknown provider outcomes cannot be replayed safely. Failed result
persistence retains an executing claim and the existing stale-execution
quarantine.
- Interrupted execution and terminal interaction projection repair
remain periodic until their separate slices. This PR changes only
approved recovery.
- A pending-only review write can trigger one empty approved-recovery
pass. Settled idle work schedules no further scan.
- The partial index uses the existing top-level concurrent migration
executor. Snapshot pruning follows repository policy.
- Cloud sleep eligibility and external alarm coverage remain unchanged.
Worker quiet is not proof of deployed instance 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
- [ ] All Paperclip CI gates are green
- [ ] 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 16:10:44 -05:00
DottaandPaperclip d108aeeaaf fix(server): schedule pending tool-review expiry from committed changes (#17453)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Governed tools can wait for a human review before execution.
> - Pending reviews already save a deadline and must reject approval
after expiry.
> - The server currently scans for expired reviews on every maintenance
tick.
> - Empty scans keep the database active without advancing work.
> - This pull request schedules pending review expiry from committed
changes and saved deadlines.
> - Configured idle state and resolved history no longer cause this
expiry scan.

## Linked Issues or Issue Description

**What happened?**

The combined tool review sweep repeatedly queries pending expiry even
when no pending request has a deadline.

**What did you expect?**

Signing or resolving a request should update the expiry schedule. A
saved expiry should trigger settlement. Once no expiry remains, this
duty should stop querying.

**Steps to reproduce**

Start a claimed instance with heartbeat scheduling enabled. Leave it
idle with no pending tool reviews. Observe pending-review expiry
discovery in the maintenance sweep.

Stacked on #17450; earlier slices are #17446 and #17443. These all reuse
the delivery and transaction foundations from #15625. This PR owns
pending-review expiry only. Approved-action recovery,
interrupted-execution quarantine, and terminal interaction projection
repair remain separate migrations. No duplicate pending-review expiry
migration was found in the open PR search; credential lifetime changes
in #17221 and #17274 concern the parent credential slice.

## What Changed

- Replace repeated company review-deadline aggregates with an indexed
single-row lookup. Add and replay a concurrent covering-index migration
and generate combined snapshots throughout the stack.

- Extract pending-expiry selection and settlement into its owning
service. Keep provider execution and interaction publication with the
gateway.
- Signal deadline assignment and pending resolution inside the owning
SQL transaction. Preserve outer commit/rollback and central uncertainty
handling.
- Register company expiry deadlines with the shared scoped coordinator.
Reconstruct on startup; normal producer hints reload only their company.
Preserve writes during drain and concurrent reconciliation.
- Settle request expiry and invocation failure atomically. Keep
interaction projection recovery in the existing separate sweep.
- Remove pending-expiry scanning from the main tick. Preserve the
combined manual API and startup-only behavior when scheduling is
disabled.
- Add a partial pending-expiry index through an idempotent concurrent
migration. No queue table is added.
- Document supported producers, quiet states, remaining duties, and
external wake limits.

## Verification

- CI run `40574661575` at `f5c935d1b89877033eb82bf890f867baeb372ef3`
lost the runners for general-server shard 9/12 and E2E shard 2/8 at
20:09:03 UTC. Both job logs explicitly show a runner shutdown signal
followed by cancellation; no test assertion failure was reported by
those jobs. A single infrastructure retry is justified once the run
completes; it has not been issued yet. Both current-head reviews are
5/5, and CI must still be green before merge.

- Current master landing at `f5c935d1b89877033eb82bf890f867baeb372ef3`:
128 complete gateway expiry, startup, scoped deadline and
preparation-timing cases pass with no skips. Server types and the
production server build pass. Landed tracing changes from #17918 are
preserved. The master migration journal prefix remains semantically
identical, with only this PR's two migrations added. Fresh remote
CI/review is required before merge.

- Parent reconciliation at `17539e2a6cf8ac4922b3cbe06af6df361bfe5665`:
116 tests pass across the full tool gateway service, startup and scoped
deadline suites against a dedicated migrated PostgreSQL database. No
cases were skipped. Server typecheck passes. Fresh remote CI and review
are pending.

Current head: `ec28076f825e76a267f05517ff0c44a3e2283ce7`. This remains a
single-duty PR stacked on #17450.

- Integrated pinned master `8548173fc9efdc0fa07644fb92dcc0b6851a95e5`
with normal merges. Every branch passed generated-schema checks and its
target-branch migration-order gate. Existing SQL is unchanged; the 25
stack migrations follow master’s new migration.
- Duty-specific producer coverage, lifecycle tests, and earlier failure
records:
[2026-10-24-recurring-server-sleep-stack.md](https://github.com/paperclipai/paperclip/blob/ec28076f825e76a267f05517ff0c44a3e2283ce7/doc/plans/2026-10-24-recurring-server-sleep-stack.md).
- Combined tip `51e6cbe67979b55b340ea9f0f434c5b5c682ccd6`: **355
startup/cancellation/recovery/dispatch/preservation tests**, **75
connection-management tests**, and production server typechecking pass.
These combined checks do not mean every intermediate PR independently
ran the full repository suite.
- Real master-to-stack database upgrade passes: **379 unchanged prior
migration receipts plus 25 new migrations**, with the gateway
responsibility fixture preserved.
- Owning-branch checks: 84 runner installation/settlement cases plus
runner typechecking and generated TypeScript/Rust profile agreement; 253
GitHub review workflow cases. Stop-attribution and held-worker readiness
regressions failed before their fixes.
- Failure accounting: the initial combined run passed 349 cases and
failed six idle assertions in a reused preservation database containing
old cancelled runs. Repeating the combined command with a fresh
preservation database passed. Earlier invalid project selection, merge
import/type errors, and the interrupted full local suite are not counted
as passing checks.
- **Latest-head CI and review remain required.** Prior CI findings
include GitHub idle observation, route teardown, attention cleanup,
touch-picker E2E, workspace health, and voice-cleanup timing; local
passes do not erase those failures. No full local repository-suite,
deployed sleep, fleet cost, or live Copilot qualification claim is made.

## Risks

- All supported producers must share the database signal owner.
Independent SQL writers and other replicas need a live notification
contract; startup alone does not cover them.
- A crash after authoritative expiry and before interaction projection
still relies on the existing projection-repair duty. That duty,
approved-action recovery, and uncertain execution recovery remain
periodic in this slice.
- Unknown external outcomes remain quarantined. Provider calls stay
outside SQL transactions. Expiry is still checked before approving a
request even if physical cleanup waits during drain.
- The new index uses the existing top-level migration executor because
PostgreSQL concurrent index creation cannot run in a file-wide
transaction. Snapshot pruning follows the repository rule.
- This establishes worker quiet and reactivation, not deployed host
suspension. Existing Cloud idle-safety and external alarm coverage
remain unchanged.

## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

Latest-head CI is now green after one evidence-backed retry. Greptile is
5/5 and all review threads are resolved.

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 15:26:19 -05:00
DottaandPaperclip 5398275d2a fix(server): schedule gateway credential expiry from committed changes (#17450)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The tool gateway stores credentials for sessions and named gateways.
> - Those credentials already contain expiry dates.
> - The server repeatedly scans their tables even when no expiry work
exists.
> - Creation signals and saved deadlines can drive cleanup without idle
scans.
> - This PR registers that work with the existing coordinator.
> - Authentication continues to reject expired credentials independently
of physical cleanup.

## Linked Issues or Issue Description

Refs #17446, #17221, #17274. This is the third sleep-migration slice,
stacked on #17446.

**What problem does this solve?**
Gateway credential cleanup queries PostgreSQL on every maintenance tick,
including empty tables and retained non-expiring tokens.

**What is the proposed solution?**
Signal expiring token/session creation from its transaction. Restore the
earliest saved expiry at startup. Retry only while cleanup or
reconciliation remains outstanding.

**Why is this useful?**
Configured idle gateways no longer create recurring expiry traffic.
Existing credential data remains the durable source of truth.

Related work: #17221 and #17274 change run-token lifetime and cleanup
eligibility. They remain separate. This PR preserves current master
authorization and deletion rules. When those PRs are combined, update
the deadline query and signals to match their new operator-token expiry
predicate; run-token idle markers must not keep this worker scanning.
Retain the separate run-liveness reaper and run both regression suites.
Text mergeability is insufficient evidence for that integration.

## What Changed

- Make the credential test clock follow real scheduled work without
overshooting future-expiry assertions; production behavior is unchanged
by this fixture follow-up.

- Coalesce sequential credential notifications within the former cleanup
cadence, preserve loaded expiry deadlines and immediate authentication
rejection, and bound scheduled deletion to 500 rows per pass.

- Added transaction-aware intent before the two existing credential
inserts. Non-expiring named tokens do not signal expiry work.
- Added earliest-expiry selection in the gateway service and a narrow
registration wrapper for the shared deadline helper.
- Removed gateway credential cleanup from the aggregate maintenance
tick. Startup reconstructs saved deadlines. Heartbeat-disabled instances
retain their startup-only cleanup behavior.
- Preserved expiry/revocation checks, run authorization, audit behavior,
and the existing 500-token cleanup batch limit.
- Added real PostgreSQL tests and a lifecycle/producer document.
Tool-action review recovery remains a separate slice.

- Drain all expired session/token pages in explicit and
scheduler-disabled startup cleanup, while retaining one-page scheduled
passes and authentication enforcement.

## Verification

- At `907098cd3656bac59c60220c73b085d0349d4b5f`, all 290 Runner
transport tests pass locally (including all seven prepared-environment
variants). CI run `40570815360` attempt 1 had one provider-drain
settlement failure in the `close-attachment` case; the identical source
passed the preceding CI lane. A single failed-job rerun is in progress.
The failure remains recorded; no shutdown assertion or production
behavior was changed to suppress it.

- Master landing reconciliation at
`907098cd3656bac59c60220c73b085d0349d4b5f` after #17446 merged: all
production and test files are byte-identical to
`24318d9b99ee7f044a864ff3981fc1929bf3e509`, where the 150-case
acceptance and server types passed. Only commit ancestry and the plan's
merged status changed. Fresh CI/review is pending on this head.

- Parent reconciliation at `24318d9b99ee7f044a864ff3981fc1929bf3e509`:
150 affected tests pass across gateway credential expiry, the complete
tool gateway suite, custom-image worker and startup. The accepted
PostgreSQL runs skip no cases. Server types pass. Existing direct tests
cover sequential-write coalescing and saved-deadline precedence; the
older Ponytail finding needs a fresh review. Remote CI is pending.

Current head: `10c85a5eaa288867aa2997a27aee5f675c2a713d`. This remains a
single-duty PR stacked on #17446.

- Integrated pinned master `8548173fc9efdc0fa07644fb92dcc0b6851a95e5`
with normal merges. Every branch passed generated-schema checks and its
target-branch migration-order gate. Existing SQL is unchanged; the 25
stack migrations follow master’s new migration.
- Duty-specific producer coverage, lifecycle tests, and earlier failure
records:
[2026-10-08-instance-sleep-and-scheduler-inventory.md](https://github.com/paperclipai/paperclip/blob/10c85a5eaa288867aa2997a27aee5f675c2a713d/doc/plans/2026-10-08-instance-sleep-and-scheduler-inventory.md),
[2026-10-24-recurring-server-sleep-stack.md](https://github.com/paperclipai/paperclip/blob/10c85a5eaa288867aa2997a27aee5f675c2a713d/doc/plans/2026-10-24-recurring-server-sleep-stack.md).
- Combined tip `51e6cbe67979b55b340ea9f0f434c5b5c682ccd6`: **355
startup/cancellation/recovery/dispatch/preservation tests**, **75
connection-management tests**, and production server typechecking pass.
These combined checks do not mean every intermediate PR independently
ran the full repository suite.
- Real master-to-stack database upgrade passes: **379 unchanged prior
migration receipts plus 25 new migrations**, with the gateway
responsibility fixture preserved.
- Owning-branch checks: 84 runner installation/settlement cases plus
runner typechecking and generated TypeScript/Rust profile agreement; 253
GitHub review workflow cases. Stop-attribution and held-worker readiness
regressions failed before their fixes.
- Failure accounting: the initial combined run passed 349 cases and
failed six idle assertions in a reused preservation database containing
old cancelled runs. Repeating the combined command with a fresh
preservation database passed. Earlier invalid project selection, merge
import/type errors, and the interrupted full local suite are not counted
as passing checks.
- **Latest-head CI and review remain required.** Prior CI findings
include GitHub idle observation, route teardown, attention cleanup,
touch-picker E2E, workspace health, and voice-cleanup timing; local
passes do not erase those failures. No full local repository-suite,
deployed sleep, fleet cost, or live Copilot qualification claim is made.

## Risks

- Shared-owner signals cover supported local producers. Independent SQL
writers or another replica need a separate ingress contract; startup
recovery is not live discovery.
- This is scheduling only. The open run-token lifetime PRs need semantic
integration as described above before combining their behavior.
- Expiry cleanup can wait during drain; authentication still enforces
expiry on use. Existing Cloud safety gates are preserved. Local
deadlines do not prove external wake coverage or deployed suspension.
- Revocation does not move expiry. Non-expiring tokens remain retained
by design; this PR does not invent a retention deadline.

## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

Latest-head CI is now green after one evidence-backed retry. Greptile is
5/5 and all review threads are resolved.

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 14:58:11 -05:00
Devin FoleyandPaperclip 1a3f604097 fix(db): honor the prepared-statement opt-in for ORM queries (#17673)
Drizzle calls Postgres.js unsafe queries, which disable preparation unless
per-query options enable it. The existing client-level opt-in therefore
does not prepare ORM queries.

Honor explicit true across root and child clients. Preserve defaults,
per-query options, lazy execution, and driver error handling. Document the
pooler capability requirement.

Test with 92 selected database and connection checks, workspace typechecks,
server and database builds, and a read-only hosted ORM protocol probe.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 18:17:43 -07:00
Devin FoleyandPaperclip a3675ddcdf Add bounded database response size diagnostics (#17504)
Count database response value sizes by public caller and static table for one explicit UTC window of at most 60 seconds. Keep diagnostics off by default and exclude SQL, parameters, returned values, errors, and credentials from output. Preserve lazy execution, transaction boundaries, and no-replay behavior.

Verification: full build and recursive typecheck; 63 focused database and PostgreSQL tests; eight physical disconnect tests with capture enabled.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 15:39:28 -07:00
bbcf5dfe98 fix(heartbeat): renew legacy controller leases on a private pool (#17032)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - A legacy (non-native) agent run keeps a controller lease on its
`heartbeat_runs` row. The server renews the lease every 10 seconds. If
no renewal succeeds in 60 seconds, the watchdog stops the run with
`renewal_deadline`.
> - #16359 added timings to lease-loss reports. The first staging event
with these timings shows `deadlineLateMs=0`, `renewalPendingMs=50000`,
and 108 renewals in about 22.5 minutes, where about 135 are expected.
Thus the event loop was not blocked. One renewal UPDATE was in flight
for 50 seconds, and renewals were slow for a long time before the loss.
> - The agent had exited with code 0. The run was in post-execution
`sync_back`. Other runs on the same host completed. No code path held a
lock on the run row across slow work. A later `instruction_save` UPDATE
on the same row completed while the earlier renewal was still pending,
so a row-lock wait does not explain the stall.
> - postgres.js 3.4.9 sends a new query to a busy connection when no
connection is idle (`handler` → `go(busy.shift(), query)` in
`src/index.js`). The query waits behind the statement that is in flight
on that connection. The renewal had no timeout, and the `pending` flag
stopped all later renewals. Thus one slow unrelated statement on the
shared pool can stop a healthy run.
> - This pull request moves lease renewal to a small private pool,
bounds each attempt with a statement timeout, and retries a timed-out
attempt until the existing lease deadline.
> - The benefit is that slow application queries on the shared pool can
no longer cancel a healthy legacy run.

## Linked Issues or Issue Description

Refs #16359 (lease-loss timings that identified this cause)
Refs #16358 (pre-dispatch lease loss becomes retryable; different path)
Refs #16917 (open: keeps a clean adapter result after a lease lapsed; it
does not prevent the loss)
Refs #13389 (open: host sleep / late timers; different cause)

**What happened?**
A staging legacy `claude_local` run was cancelled with `Legacy
controller lease lost (renewal_deadline)` after the agent exited with
code 0. The timings show that the deadline timer ran on time, and one
renewal UPDATE was pending for 50 seconds.

**Expected behavior**
A live controller keeps its lease. Slow unrelated database work does not
cancel the run.

**Steps to reproduce**
1. Create a database client with one pooled connection.
2. Start `select pg_sleep(5)` on that client.
3. Call `watchLegacyControllerLease(...).assertOwned()` with the same
client.
4. Before this change, the renewal waits about 5 seconds behind
`pg_sleep`. With a long enough slow statement, the lease deadline stops
the run.

## What Changed

- `packages/db`: Add `livenessDb(db)`. It gives each `createDb` client a
private pool of at most 2 connections
(`application_name=paperclip-controller-lease`). The pool is created on
first use. Test doubles and transactions get the original client back.
- `packages/db`: Add `closeLivenessDb(db, timeoutSeconds)`. The server
calls it in `closeDatabaseClients`, so shutdown and failed startup end
the private pool too.
- `legacy-controller-lease.ts`: The watchdog renews in a transaction on
the private pool. Each attempt sets a transaction-local
`statement_timeout` of 10 seconds. A timed-out attempt (`57014`) is
retried. After a successful retry, the local deadline is measured from
that attempt's start, because that attempt's UPDATE wrote the fresh 60
second lease. The existing 60 second deadline, the abort race, and the
`renewal_rejected` / `renewal_error` attribution do not change.
`renewLegacyControllerLease` itself does not change, so the
runtime-selection caller inside its own transaction is not affected.
- Tests: a saturated shared pool no longer delays renewal; a renewal
that times out on a locked run row retries and keeps the lease; the
private pool is cached per client, has its own `application_name`, and
closes; after a 45 second timed-out attempt and a successful retry, the
next deadline is a full 60 seconds after the retry. The existing test
doubles now model the transaction.
- `docs/deploy/database.md`: Count the up to 2 extra connections per
server process when you size `max_connections`.

## Verification

- `cd server && npx vitest run
src/services/legacy-controller-lease.test.ts`: 20 passed, on embedded
Postgres.
- Red/green: with the old `legacy-controller-lease.ts` and the new
tests, the saturated-pool test fails with `expected 5018 to be less than
2000`. With the fix, it passes.
- Red/green for the retry deadline: with the deadline measured from the
first attempt, the retry-deadline test fails (`expected true to be
false`) because the run is aborted 15 seconds after the retry. With the
fix, it passes.
- Mutation check: when the timeout retry is removed, the locked-row test
fails with `canceling statement due to statement timeout`.
- `npx vitest run src/__tests__/heartbeat-process-recovery.test.ts -t
"lease|dispatch|controller|interaction"`: 43 passed.
- `npx vitest run src/services/heartbeat/run-completion.test.ts
src/__tests__/heartbeat-orphaned-active-lease-sweep.test.ts`: 53 passed.
- `npx vitest run src/__tests__/server-startup-feedback-export.test.ts`:
passed.
- `cd packages/db && npx vitest run src/work-signals.test.ts
src/client-options.test.ts src/client-teardown-registry.test.ts`: 24
passed. `npx tsc --noEmit` for `packages/db` passed.
- Not run locally: the full server `tsc` and the full server suite. The
sandbox has a 4 GB memory limit, and `tsc` was killed (exit 137). CI is
the gate for the server typecheck, the full tests, and the build.

## Risks

- Each server process can open up to 2 more Postgres connections.
Deployments that are at their `max_connections` limit must allow for
them. The docs now say this.
- A renewal attempt is now 4 statements (BEGIN, `set_config`, UPDATE,
COMMIT) instead of 1. Each statement is a short primary-key operation on
a dedicated connection.
- If many runs wait on locked run rows at the same time, renewals for
other runs can queue on the 2 private connections. The 10 second
statement timeout limits each wait, which is less than the 60 second
lease.
- Behavior for a real lease loss does not change. A rejected renewal and
a non-timeout error still stop the run at once, and the 60 second
deadline still applies.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, used through Claude
Code in agent mode with tool use (shell, file edit, and test execution).
A read-only subagent helped with code search.

## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

🤖 Generated with [Claude Code](https://claude.com/claude-code)

---------

Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 18:13:53 -07:00
DottaandPaperclip d33b40a5c3 fix: recover saved work and provide reliable task Retry (#16965)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The control plane must retain work when an execution or workspace
export fails.
> - Retained-workspace holds can leave saved input waiting with no
usable Retry action.
> - Users cannot verify process ownership or repair provider
allocations.
> - This pull request gives system recovery a durable budget and gives
each deliberate Retry a durable intent.
> - Safe recovery can preserve files, settle an accepted result, or
prepare an independent workspace.
> - One task state keeps infrastructure diagnostics out of the ordinary
conversation while preserving real permission and approval gates.

## Linked Issues or Issue Description

**What happened?**

A task with a retained workspace can show recovery, failed-run and
saved-message warnings at the same time. Historical runs without native
workspace descriptors cannot use the native restore path. Old recovery
metadata can prevent a new attempt even when an independent workspace
would be safe.

**Expected behavior**

Paperclip owns bounded infrastructure retries and file preservation. A
user can request another attempt with ordinary Retry. Thinking appears
immediately. The service preserves completed effects and current user
controls. It does not claim that starting fresh recovered the old files.

**Steps to reproduce**

1. Retain a failed run and its workspace after export or process-stop
failure.
2. Save a follow-up while the recovery hold is active.
3. Open the task and try to continue without infrastructure credentials
or operator certification.

Refs #16906, #16835, #16883. This extends their recovery and receipt
contracts. Related work: #16962 and #16963.

## What Changed

- Add task-level Retry with company-scoped authorization, all-status
idempotency and durable admission before physical work. Keep exact-run
Retry compatible.
- Add typed recovery phases, finite persisted backoff, historical
adoption and durable local failure diagnostics.
- Preserve native workspaces or verified historical snapshots. Admit an
independent fresh workspace only after execution containment. Keep
old-source preservation and cleanup obligations separate.
- Carry completed publication and API receipts forward. Fence uncertain
mutation retries across replacement runs. Preserve connected-chat
identity and authorization.
- Re-enter normal dispatch for saved input. Reuse accepted results
through the existing finalizer.
- Use one task interruption projection across the thread, composer and
task list. Show optimistic Thinking, ordinary Retry and genuine user
gates.
- Add PostgreSQL, filesystem, API, browser-fixture and controlled
provider coverage. Document the behavior and update the feature/access
maps.

## Verification

- All GitHub Actions CI jobs pass on
`6d8c6d27d50586d8548b31d03b47f6d73a0ee11d`: typecheck, all
server/chat/workspace and serialized suites, Runner checks, eight
browser shards, canary and build. The required merge gate passes.
- Greptile reviewed the final commit at 5/5, with no actionable findings
or unresolved review threads. Its two earlier P1 findings are fixed.
- Full recursive typecheck and build pass locally on the final commit.
All 16 task Retry cases pass against isolated real PostgreSQL. The
unrelated-success case and both stale Retry cases fail on the previous
code and pass after the fix.
- The corrected gateway fixture passes its uncertain-mutation and
independent-read case. Four real HTTP API cases pass for definite
403/422 rejection, completed-effect reuse, and uncertain-effect fences
across deliberate and automatic retries. These focused gateway/API
checks used a temporary database lifecycle harness on an existing
healthy local PostgreSQL process. The standard repository helper is
unchanged.
- Earlier qualification covers 291 task UI cases, saved-file copyback
and ancestry, current Agent Chat ownership races, nine chat Retry API
browser cases, and six assembled browser recovery fixtures. A real
API/PostgreSQL/native-runner journey showed Thinking in 25 ms despite a
delayed response, admitted one successor in a separate workspace,
retained original files, and survived reload.
- The controlled Daytona test interrupts export, joins the controller,
restarts it in a separate process, and verifies one agent start, one
external sentinel operation, retained file contents and no premature
cleanup. It does not establish abrupt-kill or live LLM/GitHub
acceptance.
- The full local `pnpm test:run` was attempted and was not green: an
OAuth row-lock timing assertion failed, and a later suite exhausted
macOS PostgreSQL semaphores. The ten OAuth timing cases passed on rerun
against a separate database. Later embedded database initialization
failures remain recorded; the final CI matrix passes.
- Security scanning passed on production-code commit
`63953edf069b111c6becc3144dffa6deb52f68b5`. Its only successor changes
one test-fixture identity line. The final scan is neutral/inconclusive
because its pinned merge-base checkout failed or timed out. Both rerun
API requests returned 404, so no fresh successful scan is claimed.
Socket and Snyk checks pass on the final head.
- The optional Paperclip Review remains queued behind an earlier run in
workspace finalization. Its prior completed assessment was incomplete
because a comparison tool was blocked by the platform filter. It is not
counted as a completed code review. GitHub does not require this check
for merge.
- Hosted acceptance remains pending deployment. Local and CI results do
not establish recovery of a hosted task or its retained files.

## Risks

- Recovery changes span admission, physical ownership and dispatch.
Incorrect evidence could permit overlapping work. Existing physical
locks and authoritative stop requirements remain in place.
- The migration adds concurrent indexes. It requires the existing
top-level migration runner and retries incomplete index creation safely.
- A fresh attempt can complete while old-source preservation remains
unresolved. The service retains that obligation and does not report
those files as recovered.
- Unknown tool mutations remain blocked until their authoritative
receipt owner resolves them. Generic upstream tools have no universal
result-query protocol.
- Recovery and diagnostics stay inside the stack. No Cloud incident
endpoint, notification worker or new outbound reporting channel is
included.

## Model Used

OpenAI Codex, GPT-6 family. The runtime does not expose a more specific
model version or context-window size. The model used reasoning, shell
execution, source editing and browser tools. No model subagents were
used for this implementation.

## 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 (focused suites; the full
local run limitation is documented above)
- [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-20 19:58:53 -05:00
DottaandPaperclip 96824a71d1 fix(db): migrate connector hooks on SQL_ASCII databases (#16958)
## Thinking Path

> - Paperclip manages work for AI agents.
> - Server startup applies database migrations before accepting work.
> - Some older embedded databases use SQL_ASCII encoding.
> - Migration 0355 uses a Unicode escape literal that PostgreSQL rejects
on those databases.
> - A source rebuild can pass but the updated server cannot start.
> - This change makes that migration portable and preserves its shipped
receipt.
> - Existing databases retain their newer connector triggers during
upgrades.

## Linked Issues or Issue Description

**What happened?**

A source deployment failed at startup with `conversion between UTF8 and
SQL_ASCII is not supported`. Migration 0355 could not compile its
connector policy function on an older embedded database. The previous
app was restored while this repair was prepared.

**Expected behavior**

Supported legacy databases complete startup migrations. Connector
selector trimming matches JavaScript `trim()`. A database that already
applied 0355 must not replay it over the later 0356 connector trigger
definition.

**Steps to reproduce**

Create a PostgreSQL database from template0 with SQL_ASCII encoding,
then apply the current migration chain. The new regression fails with
the observed conversion error before this change.

**Paperclip version or commit**

Reproduced on `92b238137ba716cdf1746dd49969e2f5ab7af8fe`. The branch
includes current master `f57ed25385`.

**Database mode**

Embedded PostgreSQL 18.1; legacy SQL_ASCII database. The UTF-8 path is
also covered.

Related background: #645 fixed encoding for new databases. Existing
SQL_ASCII installations still need portable migrations. #16956 fixes a
separate workspace lock timeout that motivated this deployment.

## What Changed

- Build the whitespace pattern from UTF-8 bytes instead of a Unicode
escape literal.
- Match whole whitespace sequences so SQL_ASCII does not trim partial
multibyte characters.
- Recognize the original 0355 checksum as a valid receipt. This prevents
replay of its older trigger body over 0356.
- Add real PostgreSQL coverage for both encodings, selector behavior,
and preservation of the later trigger definition and receipt count.
- Document the legacy encoding and migration-history contract.

## Verification

- Red: SQL_ASCII migration failed with the same conversion error; UTF-8
passed.
- Second red: fixing only the SQL caused an existing receipt to replay
and overwrite the later connector function.
- Green: 29 focused tests passed across the new regression, migration
replay tests, and database client tests.
- Rebased regression: all 29 focused tests passed.
- `pnpm -r typecheck`, `pnpm build`, and `pnpm --filter @paperclipai/db
check:migrations` passed.
- Linux ARM64: clean build and 37 focused tests passed, including both
encoding and workspace-lock regressions.
- Live acceptance: the same legacy SQL_ASCII database completed all
seven remaining migrations. The server reports the tested commit and
healthy status. The restart report has no lost runs.
- All 55 current-head checks passed; two unrelated Storybook checks were
skipped. Full test, typecheck, build, runner, release, and E2E CI gates
passed. The complete suite ran in CI; local verification used focused
tests.
- Greptile reviewed this exact commit at 5/5 with no findings.
- Preserved failures: the initial unmodified-master startup failed at
migration 0355 and was rolled back. A later build-cache path error in
the deployment commands was corrected before the successful clean build;
it required no source change.

## Risks

The migration content changes, so receipt compatibility matters. The
original checksum remains recognized to avoid replaying historical DDL.
The tests compare complete later function definitions and receipt
counts. The whitespace pattern contains encoded bytes; tests cover every
ECMAScript whitespace character and preserve non-whitespace Unicode
input. Database data and schema shape are unchanged by this repair.

## Model Used

OpenAI GPT-6 through Codex, with repository tools, code execution, and
browser verification. The exact served model variant and context window
are not exposed in this session.

## 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-20 07:56:32 -05:00
DottaandPaperclip e9b6c4d44c perf: remove history scans from task and chat queries (#16278)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task reads and external chat notices must remain responsive as run
history grows.
> - The chat notice sweep casts indexed UUID columns to text.
> - Run redaction and task provenance filter large JSON contexts with
expressions that lack matching indexes.
> - These queries scan history and read large stored contexts on common
requests.
> - This pull request adapts two existing fixes to current master and
adds the missing provenance index.
> - The benefit is less database work with the same query results and
access rules.

## Linked Issues or Issue Description

Refs #15927. This change adapts that chat sweep fix to the extracted
inbound notice module and retains the contributor credit.

Refs #12499. This change adds that missing nested-context index through
a current, concurrent migration. It retains the contributor credit.

This combined change replaces those older layouts for the measured query
paths. It also covers task creation provenance, which those pull
requests do not address.

**What happened?**

The chat notice sweep scans wakeup history. Issue redaction scans run
contexts because only one branch of its JSON condition has an index. The
task creation-source filter also scans contexts because its native and
legacy ID expression lacks an index.

**Expected behavior**

The queries use indexed lookups and return the same rows. Company
access, task origin precedence, secret redaction, and chat notice
ordering remain the same.

**Steps to reproduce**

Run `pnpm --filter @paperclipai/server exec tsx
../scripts/benchmark-slow-queries.ts --baseline-ref=f8af817f8d`. The
benchmark creates disposable PostgreSQL data and compares the old query
with the current query on the same fixture.

## What Changed

- Compare chat UUID columns directly with safely parsed payload IDs.
Reject malformed and noncanonical IDs without a cast failure.
- Add the nested `paperclipIssue.id` run-context index and the exact
native/legacy task-provenance expression index.
- Build both indexes concurrently. Register the migration for top-level
execution and invalid-index retry repair.
- Extend chat payload and migration regression coverage.
- Add a repeatable synthetic SQL benchmark and document its scope and
command.
- Keep the five newest schema snapshots.

## Verification

- Focused local checks: 76 selected tests passed in five files. These
cover FIFO notices, malformed IDs, conversation state, redaction,
provenance, and company boundaries.
- Disposable PostgreSQL 18.1 fixture: 335 tasks, 1,400 runs, 13,000
wakeups, 377 actions, and 192 KiB synthetic contexts.
- Five warm samples per query, after two warmups. Every returned SQL
column matched before and after, compared as positional tuples so
duplicate unnamed projections are retained.

| Query | Before, median | After, median | Improvement |
| --- | ---: | ---: | ---: |
| Chat notice candidates | 26.21 ms | 6.43 ms | 4.1× |
| Task creation-source list | 159.69 ms | 6.51 ms | 24.5× |
| Issue redaction contexts | 131.88 ms | 0.34 ms | 389.1× |

The run-history context projection returned the same 200 rows and kept
the same index plan and buffer counts. Its timings were noisy (37.30 ms
before and 56.78 ms after in the last run; 34.25 ms and 34.14 ms in the
previous run). No improvement is claimed for this control. Its repeated
JSON projection remains a separate optimization opportunity.

The populated plans use the wakeup primary key, the provenance
expression index, and a BitmapOr over both redaction indexes. These are
local SQL measurements. They do not prove an equivalent change in hosted
API latency.

- `pnpm -r typecheck` and `pnpm build` passed locally.
- The full local `pnpm test:run` was started and then interrupted (exit
130) when the Mac reached its PostgreSQL shared-memory limit. It did not
complete and is not reported as passed. The final SQL benchmark passed
after its disposable database resources were released. The full general
and serialized test suites passed in CI.
- All CI checks for final head
`193acca6abaa992acad12f4b631bd31dd3234641` passed, including full Vitest
coverage, E2E checks, typecheck, build, migration order, and lifecycle
boundaries.
- Greptile reviewed that exact head at 5/5 with no actionable comments.


## Risks

- Both indexes add storage and write cost. Initial builds read stored
run contexts and can take time on large histories.
- Concurrent index builds avoid holding a write-blocking table lock for
the whole build. The existing migration retry path removes invalid
partial indexes before retrying.
- The chat parser accepts only canonical lowercase UUID text. This
preserves the prior text comparison, including rejection of uppercase
and malformed values.
- Staging has not been migrated or deployed. Local PostgreSQL and
synthetic data do not reproduce hosted load or network latency.

## Model Used

- OpenAI Codex, based on GPT-6. The exact serving model ID and context
window were not exposed in this session. Used reasoning, repository
tools, browser inspection, and local code execution. The adapted fixes
retain their original contributor credits.

## 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-14 07:37:51 -05:00
Devin FoleyandPaperclip b36960b758 fix(server): permit verified question-delivery history to sleep (#15851)
Treat completed question-response deliveries as history only after their exact same-company handoff finishes. Preserve missing evidence, retries, accounting debt, and idle-sleep fences.

Add a concurrent all-state question history index with interrupted-build repair. Keep migrations serialized without waiting snapshots that deadlock concurrent index builds.

Validated by 118 focused sleep tests, six migration/snapshot tests, full repository typecheck, all CI gates, and Apex 5/5.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-10 21:47:55 -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
DottaandPaperclip fc6304dfe5 feat(runner): add experimental OpenAI Dot provider over MCP Events (#15402)
## Thinking Path

> - Paperclip manages AI agents, tasks, permissions, and execution
budgets.
> - Paperclip Runner gives each provider the same admitted task and tool
authority.
> - OpenAI Dot runs outside the local process tree and needs
asynchronous work delivery.
> - The merged MCP gateway supplies OAuth consent and signed event
delivery.
> - A personal assistant grant cannot safely stand in for an assigned
agent.
> - This pull request adds a separate Dot agent connection and a durable
Rust Runner bridge.
> - The operator can assign work to Dot and inspect its accepted work,
tool receipts, and result.

## Linked Issues or Issue Description

**Agent or provider**

OpenAI Dot, as an experimental provider of the existing Paperclip Runner
adapter.

**Why this adapter is useful**

An operator can assign normal Paperclip tasks to an existing Dot. Dot
can read its mailbox, request work on an assigned task, use admitted
task tools, and submit a result. Paperclip keeps company scope,
checkout, approvals, known budget limits, and activity attribution.

**How the agent is invoked**

A dedicated `/mcp/runner` OAuth resource pairs one Dot grant with one
agent. A signed MCP mailbox event wakes Dot. Dot explicitly accepts the
assignment. The Rust Runner owns the durable turn and operation
receipts. The first release supports self-hosted instances with a local
Runner controller.

**Additional context**

This extends the merged public MCP gateway from #14846 and the assistant
invitation and device-consent work from #14933. This also integrates the
merged assistant tool and configuration expansion in #15380. Dot retains
its dedicated agent resource and cannot receive personal configuration
permission. The public assistant connection remains a personal
connection.

## What Changed

- Add a durable Rust Dot provider and its TypeScript Runner driver.
- Add closed PRP v3 external-provider operations and native execution
input v6.
- Add company-scoped pairing, mailbox, assignment, and operation
records.
- Reuse merged browser/device consent, client metadata verification,
webhook admissions, refresh, secret rotation, and warm-standby gates.
- Keep Dot scopes, issuer, grants, event workers, and tool access
separate from personal assistant access.
- Add Dot configuration, pairing, readiness, and consent UI. Keep agent
grants out of the personal Connections entry.
- Regenerate the Dot-only migration after master. Preserve published
gateway migrations. Make the new migration safe to reapply.
- Document setup, recovery, accounting limits, evidence, and remaining
account qualification.
- Reverify reconnect callbacks and wake outstanding work with a fresh
mailbox reference; preserve the existing assignment and operation
receipts.
- Clean up Dot bindings and waiting runs on OAuth revoke and
refresh-token replay. Old grants cannot revoke replacement bindings.
- Restore the pairing reference when an unsaved agent form is reopened;
document board-only pairing routes in OpenAPI.
- Accept a clean Rust exit after the acknowledged shutdown receipt.
Unexpected exits still require recovery.
- Clear the cached binding after a successful revoke so a failed
connection refresh cannot restore it.
- Add production-component Storybook states and screenshots for pairing
and connection review. All preview account data is synthetic.
- Persist normalized completion, serialize Dot turns and durable work
admission, and poll subscription readiness.
- Serialize mailbox writes and cursor reads; retain paused fence
acknowledgement without task authority.
- Authorize admitted review runs without changing the worker assignee.
Include the fenced assignment ID in production stop notices.

## Verification

- This PR integrates master `4a8178e9c`. Dot migration
`0317_messy_famine.sql` follows the published history and is safe to
reapply. The merge preserves the reserved migration connection,
batch-commit handling, private task checks, task monitors, and native
accounting.
- Local workspace typecheck, full build, and UI token gates pass. The
server typecheck passes after the review fixes. Database and native
executor regressions pass.
- All twelve real Rust/PostgreSQL Dot integration tests and twelve Dot
driver tests pass. The tests cover native document writing and
finalization, durable replay, queue admission, mailbox ordering,
admitted reviews, stale authority, production stop references, and
paused acknowledgements.
- Current head `d0e7e0626` passes all 57 checks: 53 pass and four are
intentionally skipped. This includes full typecheck, build, tests, Rust
Runner verification, browser E2E, release verification, and Canary Dry
Run. Greptile rates this exact head 5/5. All review threads are
resolved.
- The full local root test run is slower than the sharded CI run and has
not completed. The full CI test gates pass on the current commit.
Focused local regressions pass.
- Real-account pairing and event delivery on this base commit remain
unqualified. Live account and setup proof are recorded in the follow-up
#15414.

The following screenshots use synthetic preview data. They show the
production pairing component and do not qualify a real account or the
full agent setup journey.

![Synthetic pairing
preview](https://github.com/paperclipai/paperclip/blob/codex/dot-events-prototype/doc/screenshots/openai-dot-runner/pairing.jpg?raw=true)

![Synthetic connected
preview](https://github.com/paperclipai/paperclip/blob/codex/dot-events-prototype/doc/screenshots/openai-dot-runner/connected.jpg?raw=true)

## Risks

- This base adapter uses `PAPERCLIP_ENABLE_OPENAI_DOT=1` plus Public MCP
and Paperclip Runner. The separate experimental-settings follow-up in
#15414 replaces this environment flag with saved operator settings.
- Dot does not expose provider token usage or cost. The operator must
acknowledge external billing. Known Paperclip budget gates still apply.
- Cancellation fences Paperclip authority. It does not confirm that Dot
stopped all external activity.
- Assigned skill files and third-party MCP bindings are unsupported and
reject admission. There is no mounted workspace, model selector, or
provider thread identifier.
- Hosted agent-broker and remote controller deployments are not
qualified.
- The new migration follows the merged master history. Existing
prototype databases still need the normal master migration history
before this Dot-only migration.

## Model Used

OpenAI Codex, based on GPT-6. The exact deployment ID and context window
size are not exposed in this session. Capabilities used: reasoning,
repository editing, code execution, and test inspection.

## 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 #123` 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 references)
- [x] My branch name describes the change 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-07 19:11:50 -05:00
DottaandPaperclip b67db12d90 feat: add durable storage for private tasks (#14717)
Add private task and project ownership, downward access grants, and immutable run/workspace provenance. Apply migration 0313 with bounded batch commits and concurrent indexes. Keep the enforcement and sharing changes in their dependent PRs.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-07 06:53:20 -05:00
Devin FoleyandPaperclip 98d8a6ccac Stop replaying ambiguous database disconnects (#14773)
## Thinking Path

> - Paperclip stores agent work and control state in PostgreSQL.
> - Its database client must not repeat a mutation after an uncertain
result.
> - The global retry wrapper treated `write CONNECTION_CLOSED` as proof
that PostgreSQL never received a statement.
> - postgres.js also uses that message when the connection closes after
statement delivery.
> - This pull request removes that global replay and tests the actual
driver over a local wire connection.
> - Callers retain control of retries when they can prove the complete
operation is idempotent.

## Linked Issues or Issue Description

Follow-up to #13417. Preserve the transaction disconnect handling from
#13643 and the explicit actor synchronization retries introduced in
#12773. Searched open and closed issues and PRs for database retries,
disconnects, and `CONNECTION_CLOSED`. The open circuit-breaker proposal
#11142 addresses outage queue growth; it does not establish whether an
already-sent statement can be replayed.

**What happened?**
The database wrapper replayed an arbitrary statement up to three times
after `write CONNECTION_CLOSED`. The driver adds `write ` to
connection-close errors even after the peer receives the statement. A
local protocol peer receives the same submitted INSERT three times when
it drops each response. A committed write could therefore execute more
than once.

**Expected behavior**
An ambiguous statement result must fail without automatic replay. A
subsequent operation must be able to reconnect.

**Steps to reproduce**
Run the new wire regression against the parent commit. The six Simple
Query cases and the parameterized Drizzle case receive three executions
instead of one. The named prepared-client case was already safe and
stays covered. The peer reads the entire statement and then closes the
connection. This demonstrates repeated delivery with the real driver; it
does not claim that a historical incident duplicated a committed write.

**Paperclip version or commit**
Reproduced on source commit `018993140f` with the patched postgres.js
3.4.9 dependency.

**Deployment mode**
Built from source with a local PostgreSQL protocol peer. No live
provider or customer database is used.

## What Changed

- Pass the original postgres.js client to Drizzle and remove the global
statement replay wrapper.
- Add eight wire regressions: six Simple Query cases for INSERT,
side-effect-capable SELECT, and a data-changing CTE, plus parameterized
Drizzle and named prepared-client cases. The extended peer processes
Parse, Describe, Bind, and Execute, verifies bound parameters, and drops
the response only after Execute. Each case checks one delivery and
recovery on a fresh query.
- Document ambiguous outcomes and the retry compatibility tradeoff. Keep
explicit idempotent actor-sync retries and disconnected-transaction
handling unchanged.

## Verification

- Before the fix: the six Simple Query cases and the parameterized
Drizzle case failed with three executions instead of one. The named
prepared-client case was already safe. All eight wire cases pass on this
branch.
- Final focused client, pool teardown, configuration, and actor-sync
retry checks: 28 tests passed. `pnpm --filter @paperclipai/db typecheck`
also passed after the test-only follow-up.
- First implementation head, `pnpm exec vitest run --project
@paperclipai/db`: all 158 tests passed across 45 files, including real
PostgreSQL transaction/reserved-connection recovery. The local embedded
dependency's symlinks were hydrated before this run.
- `pnpm -r typecheck`: passed.
- `pnpm build`: passed.
- The complete local `pnpm test:run` did not finish; no complete local
suite pass is claimed. All CI test, typecheck, and build gates passed on
the first implementation head `11f8b22d90`. Final-head CI is pending
after the test-only follow-up.
- `git diff --check`: passed. Reviewed the diff for secrets, personal
data, generated output, and run artifacts.

## Risks

Some transient statement failures that the global wrapper previously
replayed now reach the caller. Operation owners must retry only when
they have an idempotency guarantee or a durable receipt that prevents
duplicate effects. A connection error is not proof that a write failed
to commit. There is no SQL-text retry heuristic, new suppression, schema
change, or migration. This change prevents unsafe replay; it does not
prevent network disconnects.

## Model Used

OpenAI Codex / GPT-6, with reasoning, repository inspection, code
execution, and local protocol tests. The exact backend model ID and
context-window size are not exposed in this session.

## 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge


Final verification (September30): every final-head CI check passed at
`3004c5bda39c985c3557547ec45e33870ce5d010`. Greptile scored5/5 on this
head, all review threads are resolved, and the branch is mergeable.
Eight real-wire regressions cover simple, parameterized Drizzle, and
named prepared queries. Full local suite did not produce a completed
result; the complete CI matrix passed. This public PR remains open for
maintainer merge.

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-30 18:34:00 -07:00
DottaandPaperclip 14795136f5 fix(runtime): finalize and recover sandbox workspace exports safely (#14402)
Serialize native workspace finalization, validate streamed archives within bounded limits, and quietly recover unsafe exports from saved results. Preserve exact allocations for exhausted transient failures and provide export-only retry without rerunning the provider.

Consolidates #14314, #14315, #14329, and #14334 while preserving the already-merged finalization label changes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-28 10:57:32 -05:00
Devin Foley e1d2e279a3 fix(db): replay a query whose socket write failed on a recycled pooled connection (#13417)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - A hosted instance keeps its data in Postgres, and it reaches that
database through a connection pooler
> - A pooler recycles the server side of a connection that sat idle. The
client still holds the socket and believes it is open
> - The next query fails while the driver writes it to that dead socket,
with `write CONNECTION_CLOSED host:5432`
> - The request that happened to draw the recycled connection fails,
although nothing is wrong with the query or the database
> - Only one code path guards against this today, so the same error
keeps reaching users and error reporting from every other path
> - This pull request replays a query when the write itself failed,
because those bytes never reached the server
> - The benefit is that a recycled connection costs one retry instead of
one failed request

## Linked Issues or Issue Description

No existing issue. The problem, in the bug report format:

**What happened**
Requests fail with `write CONNECTION_CLOSED <host>:5432` (driver code
`CONNECTION_CLOSED`). It is most visible on the first queries after an
idle period, when the pool holds connections the pooler already
recycled.

**Expected behavior**
A connection that the pooler recycled while it was idle is not a
user-visible failure. The client notices the dead socket and uses a live
connection.

**Steps to reproduce**
1. Run the server against a pooled Postgres endpoint (for example a Neon
`-pooler` host).
2. Leave the instance idle until the pooler recycles the server side of
the pooled connections.
3. Issue any request that queries the database.

**Paperclip version or commit**
Present on master.

## What Changed

- `packages/db/src/transient-write-retry.ts` (new) wraps the root
`postgres.js` client. When a query fails while the driver writes it to
the socket, the wrapper runs it again on a fresh connection. Three
attempts, 50 ms then 100 ms backoff.
- `packages/db/src/client.ts` gives Drizzle the wrapped client. The
teardown registry keeps the real client, because shutdown must end the
actual pool.
- The wrapper is deliberately narrow:
- It matches only the write phase (`code === "CONNECTION_CLOSED"` and a
message that starts with `write CONNECTION_CLOSED`). The write failed,
so the server never saw the query, and a replay cannot run anything
twice. That makes it safe for reads and writes alike.
- An error after the write propagates untouched, because the server may
have acted on the query.
- `CONNECTION_ENDED` and `CONNECTION_DESTROYED` propagate untouched,
because they mean a deliberate shutdown.
- Queries inside `db.transaction()` run on the scoped client that
`sql.begin()` returns, which the wrapper does not touch. A transaction
that loses its connection must abort, not replay.
- A query runs once however many handlers attach to it, and it still
executes lazily, like `postgres.js` itself.

## Verification

```
npx vitest run packages/db/src/transient-write-retry.test.ts            # 7 passed
npx vitest run packages/db/src/client-teardown-registry.test.ts \
              packages/db/src/client-options.test.ts                    # existing db suites pass
npx vitest run server/src/__tests__/cloud-tenant-transient-db-retry.test.ts  # 6 passed
cd packages/db && npx tsc --noEmit                                      # clean
```

New tests cover the replay, the `.values()` form Drizzle uses, the
attempt budget, an error that must not replay, and one execution per
pending query. A Drizzle round trip runs through a fake wire-protocol
server, which shows the wrapper is transparent to ordinary queries.

## Risks

Low risk.

- The retry only fires for a failure during the socket write, where the
server never received the query. A replay therefore cannot duplicate an
effect.
- A permanently unreachable database costs two extra attempts and 150 ms
before the same error surfaces.
- Transactions keep exactly their current behavior.
- Existing `retryOnTransientDbConnectionError` in the auth middleware
stays. It wraps a broader set of codes for one path, and it is
unaffected.
- No migration. No configuration change. No API change.

## Model Used

- Claude Fable 5 (`claude-fable-5`), 1M context, extended thinking, run
through Claude Code with tool use and code execution.

## 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
- [ ] I have updated relevant documentation to reflect my changes — no
documented behavior or configuration changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green — pending first run
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups —
pending first review
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-09-14 09:33:48 -07:00
Jannes StubbemannandClaude Fable 5.1 023e640a7e fix(db): reap idle pool connections, name the pool, and end it on shutdown (#12956)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The server keeps one postgres.js pool (`packages/db/src/client.ts`,
`createDb`) for every query it runs. #10795 made the pool tunable from
the environment, but the defaults stayed at the driver defaults: an idle
connection never closes, the pool reports itself as `postgres.js`, and
no code path ever calls `sql.end()`.
> - On a hosted Paperclip deployment the server entered a restart loop
(a bundled plugin failure that #12953 describes made every run fail, and
the pool saturated). Each generation opened its ten connections, died,
and left the backends open on the PostgreSQL side until TCP keepalive
reaped them hours later. After about 20 generations the backends
exceeded `max_connections`, and every later boot died on its first
bootstrap query with `sorry, too many clients already`, before
`server.listen()`. The loop could not heal itself. #9555 describes the
same shape on a launchd-supervised self-hosted install.
> - Three properties of the pool combine to make this possible: idle
connections are never reaped, the pool is never ended on any exit path,
and an operator cannot even find the leaked backends in
`pg_stat_activity` because they carry the generic driver name.
> - This pull request gives the pool a 60 second idle timeout and the
`paperclip` application name by default, exposes `max_lifetime` and
`application_name` through the same `DATABASE_*` environment contract
that #10795 introduced, and ends the pool on the orderly SIGINT/SIGTERM
path and on the fail-loud startup path.
> - The benefit is that a restarting or crash-looping server releases
its backends instead of accumulating them, and an operator can see and
count Paperclip's connections.

## Linked Issues or Issue Description

- Refs #9555 — database connection pool leak causes an infinite restart
loop under load. This PR closes the "pool never ends, idle connections
never close" part of that report.
- Refs #12953 — hosted outage report. The pool exhaustion is the second
half of that incident; the first half (a stuck sandbox provider plugin)
has its own PR.
- Related prior PRs: #9597 and #8780 both propose hard-coded
`idle_timeout` / `max_lifetime` values in `createDb`. Both predate
#10795 (merged), which made these options environment-driven; this PR
builds on the merged shape and adds the shutdown `end()` that neither
covers. #4006 and #7481 are closed earlier attempts in the same area.

## What Changed

- `packages/db/src/client.ts`
- New `resolveDatabaseClientOptions()` applies Paperclip defaults on top
of the environment: `idleTimeoutSeconds` defaults to 60
(`DEFAULT_DATABASE_IDLE_TIMEOUT_SECONDS`) and `applicationName` to
`paperclip` (`DEFAULT_DATABASE_APPLICATION_NAME`). `createDb` uses it
for both the environment path and explicit options.
- `DATABASE_IDLE_TIMEOUT_SECONDS` now accepts `0` to restore the driver
default (keep idle connections open). Negative or non-integer values
still throw.
- New environment variables: `DATABASE_MAX_LIFETIME_SECONDS` (positive
integer, maps to `max_lifetime`) and `DATABASE_APPLICATION_NAME`
(non-empty string, maps to `connection.application_name`).
  - `postgresJsOptions()` maps the two new options.
- `server/src/shutdown.ts`
- `finalizeServerShutdown` gains two optional ordered steps:
`closeHttpListener` runs first, before the application services stop;
`closeDatabase` runs after the application services and before the
embedded PostgreSQL stop. A failure in either is logged and does not
stop the teardown. Final order: listener → application services →
database pool → embedded PostgreSQL → instrumentation → Sentry.
- New `closeHttpListenerForShutdown()`: stops accepting requests, closes
idle keep-alive sockets, waits up to 5 s for open connections, then
closes whatever is left. Requests still in flight are drained while
every service is available, and none can reach a route after
`sql.end()`, on the signal path and the programmatic path alike (the
programmatic path's later `server.close` finds the listener closed and
skips).
- `server/src/app.ts`: the app shutdown hook (`shutdownAppServices`) now
stops the plugin job scheduler, whose tick queries the database, so a
programmatic `shutdown()` leaves no timer running against the ended
pool.
- `server/src/index.ts`
- `startServer()` is now a thin wrapper around the boot sequence. When
the boot sequence throws after the pool exists, the wrapper ends the
pool (and the separate migration pool, when configured) before it
rethrows. This covers the `process.exit(1)` path in the main module and
the CLI `paperclip run` path alike.
- The orderly shutdown passes the same `closeDatabaseClients` to
`finalizeServerShutdown`.
- `endDatabaseClient` tolerates a client without `$client` (test
doubles) and uses a 5 second end timeout.
- Docs: `docs/deploy/database.md` gets a "Connection Pool Settings"
table with every `DATABASE_*` pool variable, its default and its effect;
`doc/DATABASE.md` lists the two new variables.
- Tests
- `packages/db/src/client-options.test.ts`: parsing of the new
variables, `0` for the idle timeout, rejection of malformed values,
driver option mapping, and the `resolveDatabaseClientOptions` defaults.
- `packages/db/src/client.test.ts` (embedded PostgreSQL):
`createDb(url)` reports `application_name = paperclip` for its own
backend, and a pool with `idleTimeoutSeconds: 1` has zero backends in
`pg_stat_activity` after the timeout.
- `server/src/shutdown.test.ts`: the listener closes before the
application services, and the database close runs between the
application services and the embedded PostgreSQL stop; a failing
database close is logged while the teardown still finishes;
`closeHttpListenerForShutdown` closes idle sockets and resolves on
close, force-closes after the grace period, and is a no-op when the
listener was never bound.

## Verification

- `pnpm --filter @paperclipai/db typecheck` — passes (`check:migrations`
+ `tsc --noEmit`).
- `cd server && pnpm typecheck` — passes.
- `cd packages/db && pnpm exec vitest run src/client-options.test.ts
src/client.test.ts src/client-teardown-registry.test.ts` — 9 + 18 + 3
tests pass (the `client.test.ts` cases need embedded PostgreSQL; the new
one waits up to 10 s for the idle reap and passed in about 3 s).
- `cd server && pnpm exec vitest run src/shutdown.test.ts
src/__tests__/server-startup-feedback-export.test.ts
src/__tests__/bootstrap-claim-routes.test.ts` — 34 + 11 tests pass. The
startup-feedback suite exercises `startServer()` with a mocked
`createDb`, which is why `endDatabaseClient` tolerates a client without
`$client`.
- Manual check for a reviewer: start the server against any PostgreSQL,
then run `SELECT application_name, state, count(*) FROM pg_stat_activity
GROUP BY 1, 2;`. Paperclip's backends now show `paperclip`. Leave the
server idle for more than 60 s and the idle backends disappear. Send
SIGTERM and the backends close before the process exits.

## Risks

- Behavior change with no environment set: idle pooled connections now
close after 60 s. The next query after an idle period pays a reconnect
(single-digit milliseconds on a local socket). postgres.js reconnects
transparently. Set `DATABASE_IDLE_TIMEOUT_SECONDS=0` to keep the
previous behavior.
- `application_name` changes from `postgres.js` to `paperclip`. Anything
that filtered `pg_stat_activity` on the old name would need an update;
nothing in this repo does.
- The HTTP listener now closes at the start of the final teardown (after
the heartbeat run drain, which still needs the API for running agents).
The pool close runs after the application services. A late query from a
timer that survived the service shutdown would fail with a driver
"connection ended" error instead of running; the known database-backed
timer (the plugin job scheduler) is now stopped in the service shutdown.
- The listener drain adds at most 5 s to a shutdown while long-lived
connections (for example WebSocket clients) are open; after that they
are closed forcibly.
- `startServer()` is split into a wrapper and the boot sequence. The
exported signature and return type are unchanged.
- No migration, no schema change.

## Model Used

- Claude Fable 5.1 (`claude-fable-5-1`) via Claude Code, extended
thinking, tool use (file edits, shell, test runs). The change was
produced with the model and reviewed by the submitting human.

## 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

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_014t3bi2beVNVVHAxK36dmXm

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 08:56:39 -07:00
DottaandDev Agent 25cf079ec5 feat(runner): add Codex-native application integration (#12591)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The runner package is useful only when the application can start,
observe, and recover a native Codex run safely.
> - Existing direct adapters must keep their current execution and
finalization paths.
> - The application boundary therefore needs additive persistence,
authorization, coordination, and recovery behind an explicit
experimental adapter.
> - This pull request adds that Codex-only boundary without activating
generalized providers, remote environments, or the later task/SDK
surfaces.

## Linked Issues or Issue Description

**Subsystem affected**

Shared contracts, database persistence, adapter utilities, server
native-runtime services, and the experimental Paperclip Runner adapter.

**Problem or motivation**

The already-landed runner package has a qualified Codex path, but the
application needs durable native-run state, guarded runtime selection,
authenticated coordination, tool security, finalization, and recovery
before the experimental adapter can be exercised safely.

**Proposed solution**

Add a Codex-only `paperclip_runner` application path behind the existing
default-off native-runner setting. Bind native state and coordination to
company/run identity, preserve persisted-run recovery, and leave every
direct adapter on its existing legacy execution path.

**Alternatives considered**

The earlier stack boundary introduced a generalized executor and
remote-environment lifecycle here. That made this PR depend on
implementations in higher PRs and changed reusable sandbox behavior
globally. Those pieces are now deferred together to #12592.

**Roadmap alignment**

ROADMAP.md does not list a conflicting native-runner integration
project. This change adds the application boundary for the existing
Runner architecture.

## What Changed

- Added native run/result/finalization/provider-trace persistence,
shared validators, and idempotent migration/replay coverage.
- Added guarded Codex-only runtime selection, authenticated PRP
coordination, recovery, finalization, and interaction services.
- Added run/company-bound tool-gateway authorization, credential
redaction, SSRF protections, and replay-safe behavior.
- Added the explicit `paperclip_runner` adapter behind the default-off
rollout setting.
- Preserved legacy answered-question wake projection and direct-adapter
execution/finalization paths.
- Hardened cancellation so only owned in-memory child processes are
signaled; persisted recycled PIDs/process groups are never trusted.
- Retained the narrow Claude ACPX isolated-context security follow-up
discovered after #12590.
- Deferred the generalized executor, provider ingress, remote lifecycle,
SDK/lab/eval work, release-process changes, and lockfile.

## Verification

- Changed-file delta against `master`: 133 files.
- GitHub Actions is the authoritative verification environment for this
PR.
- Full CI, security, and Greptile review will run on this lowest
unmerged stack PR.
- Local tests/build/typecheck were not run because this checkout is
resource constrained.
- Static diff/reference checks pass, and `pnpm-lock.yaml` is unchanged.

## Risks

- This touches central heartbeat and agent-route code, so legacy
compatibility is the primary risk.
- Runtime selection remains Codex-only and explicit; direct Codex,
Claude, OpenCode, process, HTTP, and plugin adapters remain on their
existing paths.
- Fresh native starts fail closed while the rollout flag is off;
persisted native records remain readable and recoverable.
- Cancellation, company/run binding, tool calls, status decisions, and
completion writes are guarded or replay-safe.

> For core feature work, check [ROADMAP.md](ROADMAP.md) first and
discuss it in #dev before opening the PR. Feature PRs that overlap with
planned core work may need to be redirected.

## Model Used

OpenAI Codex, GPT-5.6, with repository tools, code execution, and
parallel agent review.

## 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 linked existing issues or described the issue in-PR
following the relevant issue template
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
Paperclip ticket id
- [ ] I have run tests locally and they pass — GitHub Actions is
authoritative for this resource-constrained checkout
- [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 risks above
- [ ] All Paperclip CI and security gates are green
- [ ] Greptile is 5/5 with no open actionable findings
- [x] I will address all Greptile and reviewer comments before merge

## Stack

- Position: 3 of 5 overall; lowest of 3 currently unmerged
- Base: `master`
- Previous:
[#12590](https://github.com/paperclipai/paperclip/pull/12590), qualified
Claude ACPX runtime — merged
- Next: [#12592](https://github.com/paperclipai/paperclip/pull/12592),
generalized Codex executor, task experience, and developer SDKs

---------

Co-authored-by: Dev Agent <dev@paperclip.ing>
2026-08-31 14:38:38 -05:00
Nicky LeachandPaperclip 036600d922 fix(db): close test database clients before the embedded Postgres cluster stops (#12335)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip uses database clients and embedded PostgreSQL test
fixtures
> - A fixture stopped its embedded PostgreSQL cluster while clients
still held connections
> - The postgres.js driver then scheduled a write on a stopped
connection
> - That write escaped the timer callback and caused a test process to
exit with an error
> - This pull request closes registered clients before the fixture stops
its cluster
> - The benefit is stable test teardown and clear failure reporting in
continuous integration

## Linked Issues or Issue Description

Refs: #10869

**What happened?**

An embedded PostgreSQL test fixture stopped its cluster while database
clients still held open connections. The postgres.js driver then
scheduled a deferred write on a dead connection. The write caused an
unhandled error after the test shard reported success.

**Expected behavior**

The fixture closes all live clients for its cluster before it stops the
embedded PostgreSQL cluster. Tests then finish without a deferred write
on a dead connection.

**Steps to reproduce**

1. Run the database regression test with the embedded PostgreSQL
fixture.
2. Stop the fixture while its database client still has an open
connection.
3. Observe the deferred write and the process exit status.

**Paperclip version or commit**

Branch base: bdd8f1bed. Change head:
93e85d2ba1.

**Deployment mode**

Local dev with the embedded PostgreSQL test fixture.

**Installation method**

Built from source with pnpm.

**Agent adapter(s) involved**

Not adapter-specific. This change covers database test infrastructure.

**Database mode**

Embedded PGlite.

**Additional context**

The change keeps client references weak and keys them by host and port.
It does not retain credentials. It also handles connection URLs that the
driver accepts when the URL parser rejects them.

## What Changed

- Add a registry for live database clients in the database package.
- Close registered clients before the embedded PostgreSQL fixture stops
its cluster.
- Add a regression test for the teardown race.
- Handle driver-compatible URLs that the standard URL parser rejects.
- Add cleanup for the shared route test harness.

## Verification

- Run the full `packages/db` suite.
- Run `tsc --noEmit` in `packages/db`.
- Run the server suite that uses `route-test-harness.ts`.
- Run the teardown regression test five times.
- Confirm that the negative control fails three times.
- Confirm that no shard reports green tests and exits with an error.

## Risks

The registry changes client cleanup for embedded test fixtures. Weak
references limit retained memory in long-lived processes. The registry
uses host and port only, so it does not retain credentials. No
migration, schema, API, telemetry, authentication, or cryptography
change exists.

## Model Used

OpenAI Codex, GPT-5, tool use and code review support, standard
reasoning mode. The implementing engineer supplied the code and
verification results.

## 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-08-27 13:39:42 -07:00
DottaandPaperclip 4d2af732ae feat(runner): add native persistence contracts (#12169)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent runs need durable records so Paperclip can explain results and
final status changes.
> - The current heartbeat tables support direct adapters, but they do
not model native runner evidence.
> - The runner transport and server coordinator must share a strict
finalization contract before they write production data.
> - This pull request adds that contract and its additive database
boundary.
> - It does not select the Paperclip Runner or change any existing
adapter execution path.
> - The benefit is a reviewable persistence layer that preserves all
current behavior and supports later guarded integration.

## Linked Issues or Issue Description

Refs #11962

Refs #12129

## What Changed

- Add native run result, finalization, completion, assessment, status
decision, and status effect tables.
- Add inert native metadata to heartbeat runs and events. Keep `legacy`
as the default runtime mode.
- Bind each evidence relationship to one company, issue, run, contract,
result, assessment, and decision with composite constraints.
- Add a strict `paperclip.native_finalization.v1` shared type and
validator.
- Preserve database functions, triggers, and the unique indexes required
by foreign keys in JavaScript backups.
- Add migration, backup, mixed-owner denial, validator, and
direct-adapter compatibility tests.
- Document the new records and their ownership rules.

## Verification

- Run `pnpm -r typecheck`.
- Run `pnpm build`.
- Run `pnpm db:generate`. The schema output and migration safety checks
remain current.
- Run
`PAPERCLIP_PSQL_PATH=/Applications/Postgres.app/Contents/Versions/latest/bin/psql
pnpm exec vitest run
packages/shared/src/validators/native-finalization.test.ts
packages/db/src/client.test.ts packages/db/src/backup-lib.test.ts
server/src/__tests__/heartbeat-workspace-busy.test.ts
server/src/__tests__/heartbeat-comment-wake-batching.test.ts`. All 52
tests pass.
- The full local `pnpm test:run` run completed 4,688 tests. It found 30
existing macOS test-environment failures. A serial rerun with the
canonical `/private/tmp` path reduced those failures to six existing
listener-diagnostics and skill-browser cases. None of those suites use
files in this change.
- The full Linux GitHub Actions matrix passes. This includes all
general-server, serialized-server, workspace, browser, build, typecheck,
canary, and aggregate verification jobs.
- Greptile passes at 5/5. Contributor trust, Superagent, Socket, and
Snyk pass with no finding from this change.
- Storybook visual regression skips by path because this pull request
has no UI or Storybook change.
- Confirm that the diff contains 25 files. Confirm that it contains no
workflow or `pnpm-lock.yaml` changes.

## Risks

- The migration adds tables, columns, indexes, a function, a trigger,
and ownership constraints. It does not remove or rename existing data.
- Composite foreign keys reject mixed-company, mixed-issue, and
mixed-run evidence even when each ID exists.
- The status-version trigger runs only when an issue status changes.
Backup tests confirm that restore retains this trigger and its
dependencies.
- Native source identifiers are unique when present. Legacy event rows
remain unchanged.
- This change does not add a unique run sequence constraint. The later
native writer must allocate its sequence atomically before that
invariant can be safe.
- Existing adapters keep their current execution and finalization paths.
New heartbeat runs default to `legacy` mode.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

- OpenAI Codex with GPT-5. The exact deployment ID and context-window
size are not exposed. The model used agentic reasoning, repository
tools, code execution, and test execution.

## 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-08-25 13:08:39 -05:00
DottaandPaperclip a2bf936f9a feat(workspaces): sign the workspace login handoff and gate readiness (#11671)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Managed worktree services run isolated Paperclip instances with
cloned databases.
> - A reachable service was reported as ready even when its database,
runtime identity, or login path was not usable.
> - The first candidate added verified database seeding and managed
repair in #11665.
> - This pull request consolidates that candidate with signed login
handoff and a complete readiness contract.
> - Post-QA fixes close five defects in repair identity, repair
responses, UI retry, seed journal handling, and seed-source trust.
> - The benefit is a workspace that either opens safely or reports one
accurate recovery action.

## Linked Issues or Issue Description

No public GitHub issue exists for this work, so the problem is described
here.

**What happened**

Managed workspace URLs could return HTTP 200 and report ready while
login failed. QA also found cases where repair used the wrong instance
identity, returned a generic error, left the UI stuck, rejected a safe
journal lag, or trusted a mutable workspace manifest.

**Expected behavior**

Opening a ready workspace signs the board user in to the correct
isolated instance. Provisioning and repair use a registered source and
report a structured recovery state.

**Actual behavior**

Entry depended on a password copied into the clone. Several failure
paths could publish stale readiness, hide the repair precondition, or
trust state that the workspace could modify.

**Additional context**

This pull request includes the commits first published in #11665. That
pull request keeps the original base head for review history. This
consolidated pull request is the merge candidate. Related open readiness
work includes #11575 and #11621.

## What Changed

- Adds a short-lived, signed, single-use login ticket. It binds the
user, workspace, instance, and runtime origin.
- Exchanges the ticket through Better Auth. It creates the session and
cookie through the supported adapter path.
- Adds protected workspace readiness fields for the database, clone
data, login handoff, seed phase, and runtime identity.
- Fails readiness closed when the guest has no company or
execution-workspace binding.
- Binds ticket issuance to the exact cloned user and active company
membership selected for the handoff.
- Verifies every current active board identity through the exact-user
handoff before publication or reuse.
- Gates managed runtime publication on the readiness contract and the
recorded worktree instance identity.
- Refreshes runtime work products from the live runtime row after a port
change.
- Adds one workspace access card with ready, degraded, repairing, and
failed states.
- Uses the runtime response identity for repair. It returns structured
repair precondition errors.
- Lets a valid source journal lag converge during provisioning.
- Binds seed and repair manifests to a source registered outside the
agent-writable worktree.
- Clears recovered UI errors so a successful retry can open the
workspace.
- Makes runtime tests register canonical sources and avoid ports owned
by live host listeners.
- Keeps Vitest on source suites when compiled `dist` trees exist.
- Isolates CLI and adapter tests from ambient AWS and runtime API
environment variables.
- Preserves a 404 response for cross-company workspace ID lookups before
runtime authorization.
- Makes concurrent single-flight coverage independent of
path-canonicalization scheduling order.

## Verification

The following checks passed on the integrated head:

```sh
pnpm -r typecheck
pnpm build
pnpm check:token-gates
pnpm --filter @paperclipai/db check:migrations
```

- The server source lane passed 420 files and 4,953 tests. Five tests
were skipped.
- The CLI lane passed 57 files and 385 tests.
- The database lane passed 26 files and 97 tests.
- The shared package passed 58 files and 506 tests.
- The adapter utility lane passed 640 tests. Four tests were skipped.
- The Claude adapter passed 220 tests. One test was skipped.
- The Codex adapter passed 323 tests.
- The OpenClaw adapter passed 13 tests.
- The OpenCode adapter passed 42 tests.
- The plugin SDK passed 45 tests.
- The workspace runtime suite passed 124 tests.
- The caller-scoped readiness and handoff suite passed 52 tests.
- The workspace provisioning shell suite passed 7 tests.
- The runtime exposure suite passed 17 tests while live host mappings
occupied fixed test ports.
- `git diff --check` passed and the worktree is clean.

The serialized route lane will run in GitHub CI with its normal shards.
No deployment or active-workspace migration was performed.

## Risks

- This is a medium-risk authentication and runtime-readiness change.
- The login ticket uses exact origin, workspace, instance, and user
binding. It has a short expiry and a one-time nonce.
- Runtime publication is stricter. A real readiness, identity, per-user
handoff, or control-plane database disagreement now blocks publication.
- This pull request supersedes #11665 as the merge candidate. Close
#11665 after this pull request merges.
- No new database migration is included. The lockfile and workflow files
are unchanged.
- Deployment and active-workspace migration are intentionally outside
this pull request.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

Claude Opus 5 (`claude-opus-5[1m]`), 1M context, extended thinking, tool
use, and code execution produced the main candidate. OpenAI GPT-5
(`gpt-5`) through Codex, with agentic reasoning, tool use, and code
execution, integrated the post-QA fixes and hardened the test gates. The
Codex context-window size was 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-08-19 02:37:02 -05:00
Devin Foley cb52f0b750 db: env-configurable client options; parallelize attention feed queries (#10795)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The server stores all state in PostgreSQL through Drizzle and the
postgres.js driver
> - Self-hosted installs run Postgres on localhost, so per-query latency
is near zero; hosted installs often attach Postgres over a network,
sometimes through a transaction-mode pooler
> - The DB client passes no options to the driver, so operators cannot
disable prepared statements or tune the pool without a source edit, and
the deploy docs told them to edit `client.ts`
> - The attention feed also runs its related-data lookups one after
another, so its latency grows as queries × network round trip
> - This pull request adds optional environment configuration for the DB
client and batches the independent attention-feed lookups with
`Promise.all`
> - The benefit is that network-attached deployments get correct pooler
support and a much faster attention feed, while self-hosted behavior
does not change

## Linked Issues or Issue Description

No public issue exists for this; description follows the bug report
template:

**What happened?**

On deployments where PostgreSQL is network-attached (managed providers,
pooled endpoints), the attention feed endpoint is slow:
`attentionService.list()` awaits ~15–20 queries strictly in sequence, so
a 70ms round trip turns into more than one second of pure network wait
per call. Separately, connecting through a transaction-mode pooler
(pgbouncer, Supavisor port 6543, Neon `-pooler` hosts) requires
disabling prepared statements, and the only documented way was to
hand-edit `packages/db/src/client.ts` — which `doc/DATABASE.md` itself
tells operators not to do.

**Expected behavior**

The DB client is configurable from the environment (prepared statements,
pool size, timeouts) with driver defaults when unset, and hot read paths
do not multiply network latency by issuing independent queries
sequentially.

**Steps to reproduce**

1. Run the server with `DATABASE_URL` pointing at a Postgres instance
with ~70ms round-trip latency.
2. Open the attention feed (`GET /companies/:companyId/attention`) and
measure response time — it exceeds one second even with little data.
3. Try to connect through a transaction-mode pooler: there is no
supported configuration to disable prepared statements.

## What Changed

- `packages/db/src/client.ts`: `createDb` accepts a
`DatabaseClientOptions` argument and reads optional env config —
`DATABASE_PREPARED_STATEMENTS`, `DATABASE_POOL_MAX`,
`DATABASE_IDLE_TIMEOUT_SECONDS`, `DATABASE_CONNECT_TIMEOUT_SECONDS`.
When nothing is set, no option is passed to the driver and behavior is
identical to the previous bare `postgres(url)`.
- `packages/db/src/client-options.test.ts` (new): env parsing and
driver-option mapping tests, including malformed-value rejection.
- `server/src/services/attention.ts`: the independent related-data
lookups in each feed section now run under `Promise.all` (issue
summary/image/plan-document maps, decision bundle titles, blocked-issue
maps, the newer-runs scan). Section order, item assembly, and query
shapes are unchanged.
- `doc/DATABASE.md` and `docs/deploy/database.md`: the edit-source
pooling instruction is replaced with the env toggle, plus a short
client-tuning reference.

## Verification

- `pnpm --filter @paperclipai/db exec vitest run
src/client-options.test.ts` — 6 tests pass.
- `pnpm --filter server exec vitest run
src/__tests__/attention-service.test.ts` — 22 tests pass.
- `pnpm --filter server exec vitest run
src/__tests__/decisions-service.test.ts
src/__tests__/decision-training.test.ts` — 45 tests pass; this covers
the call path that runs `attentionService.list()` inside
`db.transaction`, where postgres.js serializes queries on the reserved
connection.
- `tsc` reports no errors in the changed files.

## Risks

- Low risk for self-hosted installs: with no env vars set,
`postgres(url, {})` receives an empty options object, which postgres.js
treats the same as no options — driver defaults throughout.
- The `Promise.all` batches only group queries that had no data
dependency on each other; on the transaction call path the driver still
executes them one at a time on the reserved connection, so transactional
semantics are unchanged.
- Malformed env values now fail fast at startup with a clear message
instead of being silently ignored; this is intentional and only affects
operators who set the new variables.

## Model Used

Claude Fable 5 (`claude-fable-5`), Anthropic — via Claude Code CLI,
extended thinking enabled, tool use (test execution, live latency
measurement against a network-attached Postgres to size the problem).

## 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 (searched "prepared statements", "pgbouncer", "pool",
"attention feed", "lockfile" — closest matches are #10573/#10787
lockfile chores, unrelated to this change)
- [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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-08-04 06:30:36 -07:00