Commit Graph
37 Commits
Author SHA1 Message Date
DottaandPaperclip ea3dd40cdd fix(server): schedule approval retention from durable source deadlines
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 14:43:07 -07:00
DottaandPaperclip 755254893e fix(server): wake Grok dispatch from committed reservations
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 13:56:46 -07:00
DottaandPaperclip 5062efb368 fix(server): invalidate decision targets from committed changes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 13:47:32 -07:00
DottaandPaperclip a5b5994c1d fix(server): recover decision effects from saved claim deadlines
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 13:32:27 -07:00
DottaandPaperclip 18c11e3110 fix(server): schedule decision TTL expiry from saved deadlines
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 13:09:47 -07:00
DottaandPaperclip 66bbef521e fix(server): wake decision continuations from committed changes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 13:03:17 -07:00
DottaandPaperclip 692e3ee02c fix(server): wake archive notification delivery from committed changes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 12:22:58 -07:00
DottaandPaperclip 0956883a84 fix(server): wake tool-review card repair from committed changes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 12:14:33 -07:00
DottaandPaperclip fd2d848a7b fix(server): schedule interrupted tool claims by their deadlines
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 10:44:45 -07:00
DottaandPaperclip 5dcd193d35 fix(server): recover approved tool actions from committed changes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 10:38:39 -07:00
DottaandPaperclip 5b9ec3b11f refactor(server): isolate pending tool review expiry
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 10:04:56 -07:00
DottaandPaperclip 75332b9641 fix(server): schedule gateway credential expiry from committed changes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 09:54:24 -07:00
DottaandPaperclip fac808eabc fix(server): drive secret-proposal expiry from committed changes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 09:41:03 -07:00
DottaandPaperclip a592550336 fix(server): schedule custom-image setup expiry from committed changes
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 09:33:06 -07:00
DottaandPaperclip 71c04c0bfb fix: stop idle environment cleanup polling (#17145)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent environments can leave pending teardown or unverified provider
resources.
> - Recovery must preserve those resources until ownership and workspace
safety allow cleanup.
> - Empty maintenance scans keep an idle instance and its database
active.
> - The existing rows, transaction signals, and scheduler can represent
the actual recovery obligation.
> - This pull request schedules pending cleanup and historical
verification only while work remains.
> - An empty or fully verified instance stops these scans without losing
cleanup recovery.

## Linked Issues or Issue Description

**What happened?**

The server scanned pending environment cleanup on its maintenance tick.
The run reaper also scanned it even when it recovered no runs or active
leases. Historical resource verification scanned every five minutes,
including when all history already had valid cleanup proof.

**What did you expect?**

Writes should wake cleanup. Pending rows, deferred orphan buffers, and
unverified history should retain recovery deadlines. When none remain,
these workers should make no periodic database or provider calls.

**Steps to reproduce**

Start a configured instance with no pending leases and fully verified
history. Leave it idle. Observe repeated pending-cleanup and
historical-verification reads from the maintenance paths.

Related: #17106 supplies the preceding login-cleanup migration. #13523
improves lease-reaper query indexes. #16809 addresses reusable-sandbox
recovery authority. This PR keeps those authority checks in their
existing services.

## What Changed

- Register two workers with the existing delivery coordinator and shared
scheduler. Remove their unconditional maintenance-tick calls. Run the
reaper's inline pending cleanup only after it recovers actual work.
- Add commit-aware signals to the audited lease and custom-image
writers. Existing transactions notify directly; other writers use one
small transaction helper. No tables or migrations are added.
- Derive pending cleanup deadlines from the existing row backoff and
retry or claim metadata. Retain a historical retry only while an
unverified candidate exists.
- Register unwritten orphan buffers across runtimes that share the
database handle. Buffer registration wakes recovery without a database
connection. Load the existing spool at startup and retry failed or
capacity-limited loads while needed.
- Isolate spool failures per runtime. Attempt all buffers and persisted
pending rows, then retain a retry for any failed spool.
- Document producer coverage, idle behavior, and remaining blockers.
Update the sleep inventory.
- Repair the inherited CSS-recovery E2E test: the current build no
longer emits task-page CSS. Exercise the actual lazy environment
stylesheet, preserving real network failure, recovery, and bounded
reload assertions.

## Verification

October 24 integration update: merged current master and resolved the
runtime/spool conflicts. Preserve serialized ingestion of records
appended after startup. Plugin replacement failures now register
deferred recovery with an idle hold; concurrent callers share one
persistence attempt. The producer audit includes this new writer.

- Focused cleanup selection: 270 tests now pass across five suites. The
initial run lacked a built plugin SDK and executed no tests; building
the SDK fixed setup. The new concurrent-flush assertion then reproduced
duplicate rows; a single-flight guard fixed it and all 18 plugin
integration tests passed on rerun.
- Server typecheck passed. After review fixes, all 211 tests across the
cleanup worker, real orphan spool, environment runtime, and plugin
integration suites passed. These overlap the earlier focused selection.
The rate-limit test initially expected a write after twenty idle minutes
to wait; corrected it to assert that subsequent writes cannot accelerate
the five-minute cadence.
- Review fixes preserve retries for unreadable spool
directories/records, keep historical provider checks off the startup
wait while joining them at shutdown, and restore their independent
five-minute minimum spacing. The complete [CI
matrix](https://github.com/paperclipai/paperclip/actions/runs/40108905033)
passed on final commit `79ae1589c3c7b924db4bedb337d0c7ed3d95020b`,
including all E2E shards, server/workspace tests, runner checks,
typecheck, build/public-install verification, and security checks.

- CI found a stale E2E asset matcher: `issue-detail-*.css` is no longer
emitted. Both corrected browser tests passed against a fresh production
UI build and disposable local instance. Fresh Greptile review is 5/5 on
that same final commit, with all review threads resolved.

Previous implementation verification:

- `pnpm -r typecheck` and `pnpm build` passed.
- New PostgreSQL worker suite: 12 tests passed. Covers quiet configured
state, verified history, writes, outer commit and rollback, retry
deadlines, restart, drain, deferred orphan buffers, spool failure,
pagination, provider uncertainty, shutdown, and the idle run-reaper
path.
- Worker and environment runtime suites after review fix: 183 tests
passed. A new assertion reproduced buffer starvation before the fix.
Updated transaction fault injection and isolated simulated-instance
database handles. Server typecheck also passed after the fix.
- Run-recovery suites: 58 tests passed. Pending-cleanup suite: 38 tests
passed after updating the logging fixture to seed real orphan recovery;
an idle reaper no longer invokes cleanup.
- Stop and workspace-recovery integration selection: 268 tests passed;
two live-provider tests skipped by their environment gate. This overlaps
the worker suite and is not an additional unique-test count.
- Started the full local `pnpm test:run`, then stopped that older
duplicate run after CI found the fixture mismatch. The corrected 38-test
suite passed locally. The full sharded [CI
matrix](https://github.com/paperclipai/paperclip/actions/runs/39678174479)
passed on final commit `fc0ff3c4554ca4190a8b0b81ec06ef7065b8ad2c`,
including build, typecheck, runner, server, workspace, and E2E checks.
This is not a claim that the local full run completed.

## Risks

- Missing a future producer signal could delay discovery until restart.
The producer audit is documented in `doc/idle-environment-cleanup.md`.
Independent SQL writers and another server process are outside the
existing process-local notification contract.
- Historical candidates blocked by ownership or provider uncertainty
keep a five-minute retry until they can be verified. Provider
destruction, claim fencing, Stop retention, and workspace safety remain
controlled by their existing services.
- This does not remove orphan-active discovery or legacy shutdown
settlement from run recovery. Active custom-image expiry is also still
periodic. External wake coverage does not change.
- Local tests prove these workers become quiet and recover. They do not
establish deployed instance suspension or fleet cost savings.

## Model Used

OpenAI Codex (GPT-6), with code execution and reasoning. The exact
serving model ID and context 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
- [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-24 09:04:49 -07:00
Devin FoleyandPaperclip dfe7df0015 fix(native): expire settled native workspace sync folders (#17358)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Native runs sync their workspace with a remote sandbox through a
per-run folder under `<instance>/native-workspace-sync/<run>/`
> - The folder holds a descriptor, a manifest, and workspace and git
seed archives, and a seed can be more than a gigabyte
> - Only a few finalization commit paths delete the folder, so many runs
leave it on disk forever
> - Instance disks fill up with folders that no code will read again
> - This pull request adds a bounded, deadline-driven retention worker
that removes a folder only when no reader can still need it
> - The benefit is that instance disks stop growing with stale sync
folders, and recovery and export paths keep their files

## Linked Issues or Issue Description

**What happened?**

`cleanupNativeWorkspaceSync(runId)` is the only code that deletes
`native-workspace-sync/<run>/`. It runs only when finalization commits
through specific paths. Runs that end in `terminal_failure`, retained
recovery, reconstruction, superseded runs, commits through other paths,
and server crashes leave the folder forever. On one instance, 22 folders
older than 7 days held 6.8 GB. Single seed archives were up to 1.4 GB.

**Expected behavior**

A run's sync folder is removed after a retention window, once the run
and its finalization are settled and no recovery or export path can
still read it. The shared `generations` seed cache stays.

**Steps to reproduce**

1. Run a native agent with a remote sandbox and let the run end in
`terminal_failure` (or crash the server during finalization).
2. Wait more than 7 days.
3. Look in `<instance>/native-workspace-sync/`. The run's folder and its
seed archives are still there.

**Paperclip version or commit**

`master` at 837add0caa

**Deployment mode**

Any deployment that runs native agents with remote sandboxes.

**Agent adapter(s) involved**

Native runtime with remote sandbox workspace sync (for example Codex on
Daytona).

## What Changed

- Added
`server/src/services/native-runtime/native-workspace-sync-retention.ts`.
It removes `native-workspace-sync/<name>/` when `<name>` is a UUID, the
entry is a real directory, its newest file is older than the window, and
one of these holds:
  - no `heartbeat_runs` row has that id (orphan);
- the run has no valid `nativeWorkspaceSync` reference and is settled:
terminal status, `finished_at` before the window, no live recorded
process (see below), no live controller lease, no
`nativeWorkspaceFinalizationOwner` receipt;
- the run is settled as above, its `native_run_finalizations` row (if
any) has no `lease_owner` and no `next_attempt_at` and is `committed`
(any recovery state) or `terminal_failure` with no or `blocked` recovery
state, no active/escalated export-cause or owner-unverified recovery
action names it, no lease carries `nativeWorkspaceExportResume` or
`pendingCleanupInFlight`, retained recovery cannot still use it
(referenced lease destroyed, or no scheduled/running/awaiting_evidence
system recovery and no queued/recovering workspace recovery), and
historical recovery adoption (`adoptHistoricalWorkspaceRecoveries`) has
no pending candidate for it.
- Each folder is decided in one transaction that holds
`pg_try_advisory_xact_lock` on the run's finalization ownership key. The
rule is evaluated while the lock is held. A held lock skips the folder.
The folder is renamed to `.expired-<uuid>` in that transaction and
deleted after commit. The next sweep removes leftover `.expired-*`
folders. `generations`, non-UUID names, symlinks and files are never
touched. Each tick tries at most 50 leftovers, then checks at most 200
folders and removes at most 50. Leftovers and folders each continue from
their own cursor, so undeletable leftovers do not block run folders. A
leftover that cannot be deleted schedules the 15-minute retry. Deadline
discovery reads run state 200 IDs at a time and stops when asked to. It
supports dry run and `shouldContinue`.
- A recorded `process_pid` / `process_group_id` blocks removal only
while it can still be the run's own process. This reuses the rule from
`conversation-continuation.ts`: PIDs are ignored when every lease of the
run is remote (they are sandbox PIDs), and a local PID or process group
blocks only while it exists on this host and its start time matches
`process_started_at`. An unreadable start time keeps the folder. A
recycled PID does not.
- Separate finding, not changed here: native runs do not clear
`process_pid` when they finish. It is cleared only by the status
arbiter's `release_run_resources` effect (cancellations and commits) and
by the recovery paths. A failed run gets a `schedule_retry` effect
instead, so its PID stays set. For remote targets the stored value is
the sandbox PID. Other readers already treat these PIDs as liveness
hints only, so this PR does not change that write path.
- Exported `nativeWorkspaceFinalizationLockKey` from
`native-workspace-finalization-ownership.ts` and used it in
`withNativeWorkspaceFinalizationOwnership` and the retention worker, so
both use the same key derivation.
- Exported `nativeWorkspaceSyncStateRoot()` from
`native-workspace-sync.ts`; `descriptorDirectory` now uses it.
- Registered the worker on the shared delivery-work coordinator
(`DELIVERY_QUEUES.nativeWorkspaceSyncRetention`) in
`server/src/index.ts`, next to the git restore point worker. It follows
`rules/sleep-aware-background-work.mdc`: no interval. It runs at startup
and at real deadlines: the earliest settled run's finish (or an orphan's
newest file) plus the window; a recheck one window later for folders it
kept; and a 15-minute retry after a failure or a held lock. With no
folders, `nextRunAt` is `null`. The run folder is now created through
one helper in `native-workspace-sync.ts` that notifies process-local
listeners; the worker wakes on that signal only while it has no
deadline, because any scheduled deadline is never later than a new
folder's own deadline. `index.ts` unsubscribes the listener on shutdown.
It pauses passive maintenance during idle drain.
- Added `PAPERCLIP_NATIVE_WORKSPACE_SYNC_RETENTION_ENABLED` (default on
unless `false`), `PAPERCLIP_NATIVE_WORKSPACE_SYNC_RETENTION_DRY_RUN`
(default off) and `PAPERCLIP_NATIVE_WORKSPACE_SYNC_RETENTION_DAYS`
(default `7`, minimum `1`) in `server/src/config.ts`. Documented them in
`doc/DEVELOPING.md` and added a "Native workspace sync folders" section
to `doc/recoverable-disk-cleanup.md`.

## Verification

- `pnpm exec vitest run
src/__tests__/native-workspace-sync-retention.test.ts
src/__tests__/native-workspace-sync-retention-config.test.ts` in
`server/`: 62 tests pass. They cover:
- deletes a settled committed run (also with a left-over
`resuming_session` recovery state), a blocked `terminal_failure` with no
recovery owner, an orphan, and an unreferenced settled run;
- keeps runs whose local process or process group is still alive, or
whose live process start time cannot be read; keeps a running run with a
PID; deletes terminal runs whose recorded local process exited, whose
PID was recycled, or whose PID came from a destroyed sandbox;
- keeps recent files, recently finished runs, unsettled runs, a live
controller lease, an owner receipt, `observed` and `retryable_failure`
phases, a live `lease_owner`, a scheduled `next_attempt_at`, and each
non-blocked recovery state on `terminal_failure`;
- keeps runs with an open export-cause or owner-unverified recovery
action, `nativeWorkspaceExportResume` or `pendingCleanupInFlight` lease
metadata, retained recovery in flight on a lease that was not destroyed,
or pending historical recovery adoption; deletes when the retained lease
was destroyed;
- never touches `generations`, non-UUID names, symlinks or files; dry
run deletes nothing; leftover `.expired-*` folders are removed; an
advisory lock held by another transaction makes it skip; ticks are
bounded and resume from a cursor;
- `nextDeadlineAt` returns `null` with no folders, now with leftovers,
the settled finish plus the window, the orphan mtime plus the window,
and one window later for kept folders; the registered worker goes quiet
with no folders, retries after a held lock or an undeletable leftover,
and the real delivery-work coordinator wakes again when the real
producer (`writeDescriptor`) creates a run folder; leftover removal is
bounded and undeletable leftovers do not block run folders.
- Mutation checks: I removed each guard one at a time (advisory lock,
owner receipt, process pid, finished_at window, lease owner, next
attempt, phase, recovery state, export recovery, owner-unverified,
export resume, retained recovery, backfill pending,
symlink/real-directory check, dry run, and both mtime checks together,
plus the folder-creation wake, the process liveness rule (restoring the
old `process_pid IS NULL` rule, the sandbox exemption, the process group
check, the recycled-PID check, the unreadable start time), the leftover
batch limit, the leftover cursor and the leftover retry delay). Each
mutation made at least one test fail. I restored the code after each
one. Removing only the first mtime check does not fail a test, because
the second check inside the transaction still keeps the folder.
- `pnpm exec vitest run` on related suites in `server/`:
`native-workspace-sync.test.ts`,
`native-workspace-sync-history.test.ts`,
`native-workspace-finalizer.test.ts`,
`native-workspace-finalizer-failure.test.ts`,
`native-harness-backup-expiration*.test.ts`,
`git-restore-point-ledger*.test.ts`,
`delivery-work-coordinator.test.ts`, `chat-delivery-work.test.ts`. All
pass.
- `pnpm run typecheck` in `server/` passes.
- Greptile review: earlier rounds; all findings fixed with tests; final
score 5/5 with no open threads.
- Not run: the full server test suite and an end-to-end run against a
real instance. I did not measure disk savings on a live instance.

## Risks

- Deleting a folder that a reader still needs would make that path fall
back or fail. Same-run resume and export run under the finalization
ownership lock or before the run settles, and the worker checks the rule
while it holds the same lock. Retained workspace recovery already
handles a missing folder (ENOENT) by snapshotting the sandbox. The rule
keeps the folder for every in-flight or reopenable recovery state listed
above.
- A committed finalization row with a stale recovery state and no lease
owner is treated as settled. Nothing reads the recovery state of
committed rows.
- The worker is on by default. Operators can set
`PAPERCLIP_NATIVE_WORKSPACE_SYNC_RETENTION_DRY_RUN=true` first to see
candidates in the log, or turn it off with
`PAPERCLIP_NATIVE_WORKSPACE_SYNC_RETENTION_ENABLED=false`.
- The first sweep on a large instance does more filesystem work. It is
bounded to 200 folders checked and 50 removed per tick.
- The folder-creation wake is process-local. A folder created by another
server process sharing the same instance directory is picked up at the
next deadline or restart.
- A stale `process_pid` no longer blocks removal. If a run's local
process is still alive with its recorded start time, the folder stays.
Live work is also still guarded by the run status, controller lease,
finalization lease and ownership lock checks.
- No schema migration.

> 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.5 (`claude-opus-5-5`), 1M context, through Claude Code
with tool use (shell, file edits, test runs).

## 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
- [ ] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-23 14:05:55 -07:00
Devin FoleyandPaperclip 12bb7e1976 feat: track git workspace restore points and apply their retention (#17241)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Closed issues' git worktrees pile up on disk because they can only
be removed once the remote reproduces them exactly
> - #17238 adds restore points: encrypted saves of the unpushed and
uncommitted parts of a worktree, so the worktree itself can be removed
> - Those saves live in file storage and must be deleted on a schedule,
and promptly when their issue or company is deleted
> - This pull request adds the table that tracks restore points and a
deadline-driven retention worker, with nothing creating restore points
yet
> - The benefit is a reviewable lifecycle foundation before capture is
wired into expiration

## Linked Issues or Issue Description

Refs #17238 (restore point library). Bug report described in-PR (bug
report template).

**What happened?**
Closed issues' worktrees accumulate without bound. For example, one
instance's last 111 git cache expiration checks removed nothing, because
each worktree held unpushed commits or uncommitted files. Restore points
(#17238) will let those worktrees be removed safely. That needs a
durable record of every stored restore point and a retention policy for
deleting them.

**Expected behavior**
- A restore point is kept while its work can still resume.
- It is deleted a fixed time after its issue tree closes.
- It is deleted promptly when its issue or company is deleted.

**Steps to reproduce**
Not applicable on its own: this adds the lifecycle that the capture
integration will use.

**Paperclip version or commit:** `master` at def4726bea.
**Deployment mode:** any.
**Agent adapter(s) involved:** not adapter-specific (core).

## What Changed

**New table `execution_workspace_restore_points`** (migration `0372`,
schema `packages/db/src/schema/execution_workspace_restore_points.ts`):
- **Columns:** storage key, sha256, size, generation, source issue,
`status`, closing time, review/deletion time (`delete_after`), a
`review_generation` counter and last error.
- **Statuses:** `pending`, `active`, `expiring`, `superseded`,
`delete_requested`, `deleted`.
- **No foreign keys:** a row outlives the deletion of its workspace,
issue or company long enough to remove the stored object.
- **Indexes:**
  - the storage key is unique;
  - live generations are unique per workspace;
- an index on status and deadline serves the worker, and a partial index
on active rows' source issue serves the trigger.

**Issue tree trigger** (same migration):
`paperclip_restore_point_tree_changed` runs after an issue's `status` or
`parent_id` changes, and after an issue is deleted. It walks up from the
issue and from its old and new parents (at most 64 levels). For every
active restore point whose source issue is in that set, it makes the row
due now (keeping an earlier due time) and increments
`review_generation`. This happens in the same transaction, so a child's
deletion or move still reaches its former parent's tree.

**New `server/src/services/git-restore-point-ledger.ts`:**
- **`gitRestorePointRetentionService(db, { storage, retentionDays,
beforeExpire })`.** Each `tick` handles only due work, in batches of 50:
- **Review:** an active restore point is checked when its `delete_after`
time arrives. The due check compares against the later of the worker's
clock and the database clock, so a row the trigger just marked is never
skipped. At review:
- **Tree still open:** clear `delete_after`. The trigger marks it due
again on the next relevant issue change, so open trees cause no
recurring checks.
- **Tree `done` or `cancelled`:** expire
`PAPERCLIP_GIT_RESTORE_POINT_RETENTION_DAYS` (default `90`, minimum `1`)
after the tree's latest close. Expiry moves the row to `expiring`, then
runs an optional `beforeExpire` hook, which the capture integration will
use to mark the workspace no longer restorable. Only then does it
request deletion. An `expiring` row's object is never deleted: after a
restart, or if the hook fails, the hook is retried (after 15 minutes
when it failed).
    - **Source issue gone:** delete.
- **Concurrency:** a review result is written only if
`review_generation` is unchanged since the worker read the row. If the
trigger fired in between (for example, the tree closed or reopened), the
write is skipped and the row stays due for the next run. Expiry and
orphan deletion claim the row under the same check before the expiry
hook runs.
- **Abandoned captures:** a `pending` row older than one hour is
deleted.
- **Deletion:** `superseded` and `delete_requested` rows have their
object removed. They become `deleted` only once `headObject` reports the
object gone. Failures, including a local unlink error the provider
swallows, are recorded in `last_error` and retried after 15 minutes.
- **`nextDeadlineAt`** returns the earliest real deadline, or `null`
when nothing is due.
- **`requestRestorePointDeletion(tx, { sourceIssueId } | { companyId
})`** registers a delivery-work notification and marks the matching
`pending`/`active` rows `delete_requested` in the caller's transaction.

**Deletion hooks:**
- `issueService.remove` and `deleteCompany` call
`requestRestorePointDeletion` in their transactions, so a deleted
issue's or company's objects are removed promptly.
- Child issues cannot be deleted with their parent (the parent link
restricts deletion), so each issue removal goes through this hook.

**Scheduling** follows `rules/sleep-aware-background-work.mdc`:
- `registerGitRestorePointWork` registers a `git-restore-points` queue
with the shared delivery-work coordinator.
- The worker keeps no in-memory inputs. Due work is recorded in the
table, by the trigger or by deletion requests. Each run handles it and
schedules only `nextDeadlineAt`, so a failed run loses nothing and is
retried by the coordinator.
- It runs at startup, at real deadlines, and when terminal-workspace
notifications settle after issue changes, but only while restore points
exist. With nothing due, it schedules nothing. A full batch continues
immediately. There is no timer.

**Config and docs:**
- `server/src/config.ts` adds
`PAPERCLIP_GIT_RESTORE_POINT_RETENTION_DAYS`.
- `doc/recoverable-disk-cleanup.md` gains a "Git workspace restore
points" section; `doc/DEVELOPING.md` documents the variable.
- `drizzle-kit generate` pruned `meta/0367_snapshot.json`, following the
repository's snapshot-retention convention.
- No code comments were added.

## Verification

- `pnpm exec vitest run src/__tests__/git-restore-point-ledger.test.ts`
(in `server/`): 26 passed against embedded Postgres, stable across
repeated runs. The cases:
- **Review outcomes:** an open tree clears its deadline and schedules
nothing; a closed tree is scheduled 90 days after its close; a
long-closed tree expires and is deleted; a missing source issue is
deleted.
- **Expiry durability:** a failing hook keeps the row `expiring` with
its object until a retry succeeds; an `expiring` row left by a restart
resumes the hook before deletion; an `expiring` row's object is never
deleted while its hook fails.
- **Trigger:** the last open child closing, a child being deleted, a
child moving to another parent, and the source issue's own status change
all mark the restore point due. Unrelated issues, inactive rows and
already-due rows are untouched. End to end, an open tree's restore point
waits without a deadline, then is scheduled 90 days after its tree
closes.
- **Races:** closing the tree, or reopening it, between the worker's
read and write leaves the row due, and the next run resolves it
correctly. A reopened tree's restore point is not deleted.
- **No rescanning:** 60 retained restore points with future review times
are not evaluated. A backlog of 60 due ones is processed as 50, then 10,
then nothing.
- **Captures and deletion:** abandoned pending rows are deleted while
recent ones are kept; superseded rows are deleted; an object still
present after deletion is not marked deleted; a failed deletion is
retried only after its 15-minute deadline.
- **Deadlines:** the next deadline is correct for each obligation, and
`null` when idle.
- **Deletion hooks:** `issueService.remove` marks only that issue's
restore points, and `deleteCompany` marks all of the company's; the
worker then deletes their objects.
- **Worker:** schedules nothing when idle, and only the next deadline
otherwise; wakes on issue changes only while restore points exist.
- **Mutation checks:** each of these fails at least one case:
  - removing the issue hook or the company hook;
  - making the trigger ignore former parents;
- removing the worker's generation check or the trigger's generation
bump;
- letting storage cleanup delete `expiring` rows, or not resuming the
hook;
  - ignoring open trees;
  - removing the deletion verification;
  - evaluating rows that are not due;
  - dropping the retry deadline.
- `pnpm exec vitest run company-deletion issue-delete issues-service
delivery-work issue-tree` (in `server/`): 190 passed.
- **CI note:** one earlier `Typecheck + Release Registry` run was
cancelled when the GitHub runner received a shutdown signal; it is not a
code failure.
- `pnpm exec vitest run` (in `packages/db`): 211 passed. One run had a
single failure in `issue-comment-derived-attribution-migration.test.ts`,
an older migration's test unrelated to this change. It passed alone and
on the full re-run.
- `pnpm exec vitest run
src/__tests__/git-restore-point-ledger-config.test.ts`: 5 passed.
- `pnpm run check:migrations` (in `packages/db`): numbering and safety
checks pass.
- `pnpm run typecheck` (in `server/`): pass.

## Risks

- **No behavior change today:** nothing creates restore points yet. The
worker runs once at startup, finds no rows and schedules nothing. The
capture integration follows in a separate PR.
- **Trigger cost:** every issue status change, parent change or deletion
now runs a short ancestor walk, plus an `UPDATE` on the new table that
matches only active restore points through a partial index. With no
restore points, the `UPDATE` matches nothing.
- **Missed wakes:** if a wake is ever missed, for example because the
process exits after commit, the due row stays in the table. The worker's
startup run or next deadline processes it.
- **Deletion paths:** issue and company deletion each gain one `UPDATE`
on the new table inside their existing transactions.
- **Migration:** `0372_mature_the_initiative.sql` creates one empty
table, its indexes, and the trigger function with two triggers on
`issues`; nothing is rewritten.
- **Rollback:** revert. The table can stay, since nothing else reads it.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), 1M context, through Claude Code
with tool use (shell, file edits, tests, read-only measurements on live
instances).

## 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
- [ ] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-22 15:52:04 -07:00
DottaandPaperclip efc2ddc7bf fix: stop idle login cleanup polling (#17106)
**Dependency landed:** PR #17129 is merged. This branch is rebased onto
master `0f15f82b19` and preserves its provider-owned completion
contract. The shared save path calls `notifyLoginSessionChanged(tx)`;
the cleanup module invalidates both existing sandbox cleanup workers
without selecting a provider.

## Thinking Path

> - Paperclip manages work for AI agents.
> - Subscription login can allocate a temporary sandbox.
> - Existing cleanup workers scan the database every heartbeat tick,
even with no login work.
> - Empty scans keep idle databases active.
> - This PR uses committed session changes and saved expiry times to
schedule cleanup.
> - Existing rows still drive recovery after failure and restart.

## Linked Issues or Issue Description

**What happened?**
On master `898144f4b3`, the enabled heartbeat scheduler runs both login
reapers every 30 seconds by default. An instance with no pending logins
still queries sessions and leases.

**What did you expect to happen?**
After startup reconciliation, idle login cleanup should make no
recurring SQL or provider calls. New sessions, expiry, failed cleanup
and restart must still recover.

**Steps to reproduce**
Start a claimed instance with heartbeat scheduling enabled and no
pending login sessions. Observe repeated adapter-login and setup-token
cleanup queries at the heartbeat interval.

Related sleep migration: #17094. Searched open issues and PRs. Related
setup-token cleanup work: #15774, #14236 and #16648. This PR does not
change their release-result or null-lease behavior.

## What Changed

- Register separate adapter-login and setup-token workers with the
existing delivery coordinator and scheduler.
- Notify before writes in the owning stores, the lease-tag write, and AI
connection save. Shared save uses a provider-neutral notification; each
cleanup worker checks its own durable predicates. The shared transaction
protocol retains intent until the outer transaction settles.
- Reconstruct session expiry, promotion expiry and concrete cleanup
retries from existing rows. Preserve configured heartbeat spacing to
bound scan bursts.
- Recover tagged orphan leases even with terminal or missing sessions.
Protect live acquisitions that have not attached their lease yet.
- Exclude local-subscription rows from sandbox reapers. Their existing
local-login worker owns cleanup.
- Preserve deferred work across drain and shutdown. Add no tables, Cloud
alarms or host-sleep exemptions.
- Update the inventory and document producer coverage, writer scope and
recovery boundaries.

## Verification

- Updated head: `1d57e13606`. Seven focused suites passed (282 tests):
AI connections, login cleanup, device-login service/reaper,
login-session runtime, and setup-token session/reaper. `pnpm -r
typecheck` and `pnpm build` passed. The duplicate local `pnpm test:run`
was interrupted after CI passed all server shards; it is not counted as
a complete local pass. Full-suite evidence comes from the exact-head CI
run. Greptile reviewed this exact head at 5/5 with no findings or
unresolved threads. [Full CI run
39669285577](https://github.com/paperclipai/paperclip/actions/runs/39669285577)
passed on attempt 2. Both aggregate gates passed; no checks remain
failed or pending.
- Extended the real PostgreSQL AI connection tests for both Anthropic
and OpenAI. The tests check that neither cleanup observer wakes inside
the completion callback, both wake after commit, and an idempotent save
does not emit another wake.
- Existing cleanup tests cover idle/history states, producer wake,
expiry, promotion, cancellation, retry/restart, orphan recovery, nested
commit/rollback, drain, concurrency, and shutdown.
- Current-head CI attempt 1: the unchanged
`sandbox-callback-bridge.test.ts` HTTP/2 preface test failed because its
child process could not bind `127.0.0.1:34033` (`EADDRINUSE`). Its
single unchanged rerun passed. The exact test also passed locally
without changing code or timeouts. This establishes recovery from the
bind collision; it does not establish which process owned the port. No
login code ran in that package test.
- Previous-head failures remain recorded: the login-runtime transaction
fixture was corrected; a workspace-runtime readiness port-bind failure
passed in isolation and fresh CI, but its first failure cause remains
unconfirmed. An earlier combined AI suite timed out in local-login
isolation and passed alone; its timing cause remains unconfirmed. An
earlier local full suite was interrupted and was not counted as passing.

## Risks

- Process-local signals cover one active server with the instrumented
shared database handle. Independent SQL writers or replicas need their
own signal integration.
- Provider resources that never became persisted tagged leases remain
the responsibility of the unchanged general lease/spool recovery.
Setup-token acquisition retains its required provider expiry.
- Cleanup starts are bounded by the configured heartbeat interval.
Writes during a sweep can cause a final follow-up scan before quiet.
- Existing login and resource blockers still prevent unsafe host sleep.
This is worker polling removal, not provider sleep qualification.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository tools and test
execution. 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 references)
- [x] My branch name describes the change and contains no
internal/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-21 15:39:23 -05:00
DottaandPaperclip 898144f4b3 fix: stop idle execution-control recovery polling (#17094)
Replace idle execution-control polling with workers driven by committed writes and persisted deadlines. Preserve startup recovery, bounded retries, and shutdown safety. Stabilize the Slack setup browser regression by waiting for the restored setup screen before simulating its webhook.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-21 09:54:28 -05:00
DottaandPaperclip c6ee96a3df fix: stop idle MCP event polling (#17036)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - MCP task subscriptions and Dot mailboxes notify external clients
when work changes.
> - Their delivery workers queried SQL every two seconds, including when
no work existed.
> - Those queries prevent idle databases from becoming quiet across many
hosted instances.
> - Existing activity, mailbox, subscription, and delivery records
already preserve the work.
> - This pull request uses committed-write signals and saved retry
deadlines to drive delivery.
> - Idle subscriptions become quiet while startup, retry, drain, and
shutdown recovery remain supported.

## Linked Issues or Issue Description

Related sleep migrations: #15625, #17000, #17008. Searched open PRs and
issues for MCP event sleep/polling work; no duplicate found.

**What happened?**

Each public MCP and Dot event service started a two-second interval. It
checked settings, scanned subscriptions, created delivery receipts,
cleaned expired records, and attempted delivery even without changes.

**Expected behavior**

An empty or subscribed-but-idle service produces no recurring SQL or
network traffic. Committed events trigger prompt delivery. Only
unfinished work retains a retry deadline.

**Steps to reproduce**

Configure a public MCP origin and start the server. Observe recurring
settings queries with MCP disabled, or subscription/queue queries with
it enabled. No incoming task changes are required.

**Paperclip version or commit**

Initially inspected master at
`8b4ab65b1c3d863d282752e83307943720a5c85c`; rebased onto `c2daf906d9`
after master advanced. The only conflict was overlapping imports in the
heartbeat queue; both were preserved.

## What Changed

- Register separate MCP and Dot workers with the existing delivery
coordinator. Remove both unconditional intervals.
- Notify from committed activity, comment, mailbox, subscription, and
experimental-settings writes. Centralize comment insertion in one small
helper so normal callers do not manage recovery state.
- Preserve outer transaction ownership, uncertainty reconciliation, and
requests deferred during drain or standby.
- Restore delivery retry/abandoned-claim deadlines on startup. Scan
subscriptions in pages of 20; drain full source and delivery batches
without another external event. Scope subscription refreshes to their
subscription or shared Dot binding, issue activity to its task, comment
discovery to its company, and mailbox/assignment changes to their
binding. Retry deadlines do not rescan unchanged sources.
- Run expired-record cleanup opportunistically. Preserve on-use expiry
checks and the existing one-time automatic Dot challenge behavior.
- Add real PostgreSQL lifecycle coverage and guarded external test
database options for existing MCP/Dot integrations. Document producers
and the supported writer model.

## Verification

- Final head `e54fb34485921016ef988f7df9cca173a9ebdfc8`: all CI checks
passed (55 successful checks/statuses, one neutral review, two
intentionally skipped Storybook checks). Mergeable with no unresolved
review threads. Greptile: 5/5 on this exact commit; security scan
passed.
- All 284 affected tests passed in one local run after rebasing: 16
real-PostgreSQL lifecycle tests, 24 coordinator/scheduler/standby tests,
84 public MCP tests, 54 Dot runner tests, and 106 interaction-service
tests.
- Server TypeScript passed after refreshing generated Runner/plugin SDK
dependencies from master. Full workspace typecheck and build passed
before the rebase; final CI repeated typecheck and build successfully on
the rebased head.
- Coverage includes SQL/network quiet with configuration and history,
committed writes, outer commit/rollback, drain/standby, retry
restoration, reenable/unsubscribe, concurrent writes, shutdown,
source/delivery/subscription batch boundaries, Dot challenges and
reconnects, native broker fences, comment discovery, and
company-isolated scans.
- CI exposed an existing lock-order test whose insert proxy missed
nested transactions and waited indefinitely. The fixture now observes
nested inserts, preserves the lock assertion, and fails explicitly if
interception is missed. The complete 106-test interaction suite and the
formerly hanging CI shard pass. The warm-standby/settings test doubles
were also updated for the new coordinator/transaction boundaries.
- Earlier fixture failures (required timestamps, cleanup, external
database selection, async waits) were corrected. One existing OAuth
first-poll timing assertion and one E2E company-cleanup race passed on
single reruns; the final full CI run passed without a rerun. No OAuth or
company-deletion behavior was changed.
- The local full `pnpm test:run` attempt could not initialize embedded
PostgreSQL after five attempts. It was stopped; affected integrations
were verified with separate disposable PostgreSQL 17 databases, and
final CI supplied the full-suite result. Embedded-only local skips are
not counted as passes.

## Risks

- Notifications use one live server/database scope. Independent SQL
writers and separate live replicas do not trigger these process-local
workers. Startup recovers durable state; continuous multi-writer
discovery needs a notification/ingress contract.
- Idle physical cleanup waits for startup or another enabled sweep.
Subscription and authorization expiry remain enforced on use.
- Existing delivery claims, retry bounds, signatures, and receipt
deduplication remain in place. No new tables or migrations.
- Host sleep-safety checks remain conservative. MCP/Dot retry deadlines
are not yet covered by Paperclip Cloud's scheduled-wake contract. This
proves worker quiet, not deployed host suspension or fleet savings.

## Model Used

OpenAI Codex (GPT-6), with reasoning, local code execution, and tool
use. The runtime does not expose a more specific model version 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>
2026-10-21 07:55:09 -05:00
DottaandPaperclip 30b753f614 perf(budgets): scope accounting and deliver enforcement asynchronously (#17046)
## Thinking Path

> - Paperclip manages AI agents and their paid work.
> - Budgets must prevent new work and stop reported overruns.
> - Cost settlement also ran serial lifecycle reconciliation for every
eligible company agent.
> - That made request latency grow with unrelated idle agents and
delayed results on stop delivery.
> - This change separates atomic admission and accounting from durable
asynchronous enforcement.
> - Scoped live usage detects overruns without writing provisional
ledger charges.
> - The result is constant query scope for ordinary settlement and
recoverable stop delivery.

## Linked Issues or Issue Description

**What happened?**

Settlement and budget checks entered company-wide lifecycle
reconciliation. A company with 1,000 unrelated idle agents performed
1,001 accounting transactions for one ordinary cost evaluation. User
requests could also wait for provider termination. Running usage
checkpoints did not enforce reported monetary exposure.

**Expected behavior**

Keep the final reservation gate authoritative. Commit charges, scope
blocks, and enforcement intent before returning. Deliver stop requests
after commit. Read only applicable policies and execution scopes for
ordinary charges. Stop long-running work when supported reported or
priced usage establishes an overrun.

**Steps to reproduce**

1. Create a disposable company with one company budget and idle agents.
2. Evaluate a charge below the budget with 0, 17, and 1,000 unrelated
idle agents.
3. Compare query counts, company accounting transactions, and duration.
4. Use the controlled partial-usage and delayed-stop fixtures to check
enforcement and request responsiveness.

**Paperclip version or commit**

The measured baseline is `6c9b87d0922f1ebfc43e8b0c36889ac5ba7b9837` from
`master`.

Related public work: Refs #16976, #2006, #3539, #13763, #14296, #16853,
#13075. These cover the affected service charge, cancellation, and
accounting paths. This change removes their shared reconciliation
overhead.

## What Changed

- Batch applicable policy exposure under the existing company accounting
lock. Preserve exact fractional money and zero-reservation dispatch
fences.
- Commit a durable final budget dispatch claim before provider I/O.
Retain a separate final issue/run ownership transaction through
synchronous handoff, without holding the company accounting lock or
awaiting provider work. Preserve native recovery lease checks and
duplicate handoff fencing. Browser handoff rechecks current policy; it
reuses a consumed rate slot only for the identical policy generation.
- Separate ledger spend, live observed spend, and effective held
capacity. Use the maximum of reservation and live observation for each
unfinished operation.
- Deliver budget cancellation through the existing coordinator.
Revalidate generations and persist queued/running stop fences before
external termination. Bound stop concurrency to four.
- Apply agent holds only to the affected agent. Project budgets leave
other projects running. Project real company transitions in bounded
lifecycle batches.
- Replace periodic broad reconciliation with durable pending work,
startup repair, receipt replay, and concrete retry/window deadlines.
Retain drain and idle-safety checks.
- Enforce accepted cumulative usage checkpoints without provisional
charges. Final accounting atomically replaces live exposure and settles
reservations.
- Add operator-gated budget spans, regression coverage, map updates, and
a real-PostgreSQL comparison harness. No schema or first-party telemetry
change.

## Verification

- Full workspace `pnpm -r typecheck` passes on the final ownership fix,
rebased onto `bbcf5dfe98`. Production `pnpm build` passes on final head
`4c5f4de8b1`. Map, module-boundary, token, and Storybook catalog checks
pass.
- Focused real-PostgreSQL coverage includes the 292-case rebased
accounting/dispatch run, 63 browser cases, 52 run-control/cancellation
cases, 42 policy-generation cases, 77 lifecycle/commentary cases, and
the exact Muse confirmation regression. Native executor unit coverage
passes all 768 cases. Final ownership coverage passes: 74
adapter/use-case cases, nine public regression cases, and the additional
deleted-project race. These exercise competing ownership, budget changes
and charges between phases, zero/nonzero duplicate claims, rollback,
recovery contention, read-only capacity rechecks, and controlled HTTP
startup after commit. Another 74 pure/startup/tracing cases pass. The
complete CI matrix and both 5/5 reviews passed on `1ac9b58`; its
external security scan identified a stale-owner handoff race. The final
fix restores synchronous issue/run ownership fencing after the
accounting transaction commits. Final head `8e84f55ce8` has fresh
Greptile 5/5 with zero new comments, Paperclip review 5/5, and a passing
security scan. The complete latest-head [CI
matrix](https://github.com/paperclipai/paperclip/actions/runs/39535189808)
passes, including budget, browser, Runner, build, and canary jobs. The
unchanged Runner close fixture passed on its sole same-head job retry.
- Broad local `pnpm test:run` invocations were stopped before required
source updates (rebase and review fixes). They are not claimed as full
passes. Final full-suite coverage comes from the complete latest-head CI
matrix; local focused coverage and the incomplete broad invocations are
reported separately.
- Reproduce performance with `pnpm --filter @paperclipai/server exec tsx
../scripts/benchmark-budget-enforcement.ts
--baseline-ref=6c9b87d0922f1ebfc43e8b0c36889ac5ba7b9837`; repeat with
`--reverse`. The harness uses disposable real PostgreSQL, 20 warm
samples per variant/count, and records raw timings, SQL/lock counts,
agent reads, unchanged agent state, and query plans. Final
production-source measurement revision:
`eff713a4f72c7f4b7a4002a701cb481d4bfccd95`; the extended harness is
committed in `4c5f4de8b1`.

| Unrelated idle agents | Baseline median / p95 | New median / p95 |
Baseline SQL / locks | New SQL / locks | New agent reads |
| --- | ---: | ---: | ---: | ---: | ---: |
| 0 | 1.80 / 2.33 ms | 2.99 / 3.88 ms | 5 / 1 | 7 / 1 | 0 |
| 17 | 15.53 / 19.55 ms | 2.24 / 2.60 ms | 90 / 18 | 7 / 1 | 0 |
| 1,000 | 978.35 / 1,790.43 ms | 2.16 / 2.52 ms | 5,005 / 1,001 | 7 / 1
| 0 |

- These measurements use local PostgreSQL 18.1 and Node 26.4.0 arm64.
They isolate the cost-evaluation core and complete admission path,
without hosted-region or provider latency. Prior-revision reversed-order
controls also preserved the constant query counts; the final
measurements above use the final production source.
- Complete reserve → final ownership gate → immediate offline provider
result is measured separately, with one responsible agent and one active
operation at each unrelated-agent count. Admission p95 baseline/new: 0
agents **11.54 / 29.77 ms**; 17 agents **9.31 / 27.62 ms**; 1,000 agents
**24.51 / 74.05 ms**. Baseline admission was already scoped; the
stronger final fence adds bounded overhead. New counts stay constant at
53 Drizzle statements, three transactions, two company-accounting locks,
and five agent-reading statements bound to the responsible agent. Zero
unrelated-agent reads, exactly one provider callback per sample, and no
leftover runs or holds. All admission scenarios stay below 250 ms. No
external provider calls are included.
- The partial-usage/delayed-termination fixture passed 20/20: receipt
persistence p95 13 ms, receipt return to observed durable Stop fence p95
28 ms, complete receipt-start-to-fence p95 50 ms, unrelated database
request p95 1 ms while termination was blocked. After releasing the
controlled termination fixture, acknowledgement p95 was 14 ms. These are
separately measured phases; fixture acknowledgement is not live-provider
termination latency.
- Models and provider deadlines are unchanged. No deployment, live
inference, or staging acceptance is claimed. A quiet 30-minute follow-up
waits for the merged revision on staging, then repeats classifier and
controlled budget-stop acceptance in staging, recording hosted
bookkeeping, queue delay, and actual provider acknowledgement
separately.
- Development failures retained: finalizer notification regressions
(fixed; 27/27 affected cases passed), invalid browser admission fixture
assumptions (fixed; browser coverage now passes 63/63), and first-head
lifecycle/dispatch CI regressions (reproduced and repaired). A
prior-head Cases E2E test hit its unchanged announcement-dismiss handler
race while opening a filter; the same journey passed on `1ac9b58`. The
external security review then found a stale-owner race between
accounting commit and provider entry; final verification covers a
competing run in that gap while retaining provider I/O outside the
accounting lock. Early PostgreSQL startup attempts hit local semaphore
pressure; serial reruns succeeded. The final-head Runner lane had one
failure among 1,823 tests: the unchanged `timeout-attachment` fixture
failed at its first process close after 6.28 seconds. The sole same-head
job retry passed all 1,819 active tests (four skipped), including that
fixture in 1.85 seconds. Three isolated local probes could not run
correctly because the Rust fake-provider fixture binary was absent;
cleanup masked the original startup failure. CI builds that fixture
through its normal lane. These probes are inconclusive and are not
counted as passes.

## Risks

- This changes accounting, lifecycle, and cancellation ordering. Durable
generations, source/sequence checks, ownership leases, and company
locking remain required for correctness.
- Provider stop acknowledgement can be slow or fail. Stop intent remains
pending until existing termination proof succeeds; cancelled row status
alone does not acknowledge delivery.
- Mid-run limits depend on provider usage reports or supported price
calculations. Providers that report only at completion cannot provide
exact mid-call detection.
- Signals are process-local wake hints for the supported
single-active-writer model. Durable state and startup recovery remain
authoritative.
- There are no migrations, new queue tables, or cached balances. Repeat
the classifier and controlled budget-stop acceptance after normal
rollout.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, tool use, code execution,
and parallel coding agents. The host does not expose the exact backend
model revision or context-window size, so those values are not asserted.

## 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-21 07:23:00 -05:00
DottaandPaperclip 15176963b1 fix: stop idle skill-source polling (#17008)
## Thinking Path

> - Paperclip manages agent work and the skills that agents use.
> - Project repositories create durable import jobs, and installed
sources can refresh automatically.
> - The server queried these records every five seconds, even when no
work existed.
> - Idle tenant databases need to stop receiving those discovery
queries.
> - This change uses the existing transaction notifications and delivery
coordinator.
> - Saved refresh, retry, and lease deadlines determine when the worker
runs next.
> - Cloud does not yet wake these deadlines, so outstanding skill work
retains the instance.

## Linked Issues or Issue Description

**What existing behavior does this improve?**
Skill-source background synchronization. This continues the idle-work
migrations in #15625 and #17000. A search found no overlapping open
polling-removal PR.

**Current behavior**
On master `b11010b0d4`, startup installs a five-second skill-source
interval and also calls it from the external-object scheduler. Each
sweep queries active companies, import capacity, pending imports, and
automatic refreshes.

**Proposed behavior**
Run at startup, after a relevant transaction settles, or at a saved
deadline. Stay SQL-quiet when no eligible work remains. Preserve import
admission, authorization, atomic claims, retry backoff, and the existing
five-minute upstream check.

**Reason and benefit**
Remove recurring database activity from inactive tenant instances
without requiring new tables or a new scheduler.

## What Changed

- Replace both recurring skill-sync calls with one delivery-coordinator
registration.
- Notify from attachment/detachment, source creation/settings/manual
refresh/disconnection, project/company deletion, and company status
changes, including budget resume.
- Restore the earliest eligible `nextSyncAt` or lease-expiry deadline.
Due backlogs and conflicts retry after five seconds; no idle recovery
cadence remains.
- Admit deferred imports again after a batch frees capacity. Preserve
requests through drain, standby, restart, and commits during a sweep.
- Add sleep-safety predicates for imports and eligible automatic sources
until Cloud supports these deadlines.
- Document the lifecycle and process-local writer contract. No schema or
API changes.
- Guard external test database URLs before migrations or cleanup:
require a `_test` database name and reject database/options overrides.

## Verification

- 147 targeted tests pass: 39 server-startup tests, 14 new
real-PostgreSQL lifecycle tests, 20 shared coordinator/scheduler tests,
63 existing import/refresh/retry regression tests, and 11 test-database
guard cases.
- CI exposed a stale startup mock without the new `start` method. The
fixture is updated and all 39 startup tests pass locally.
- Tests exercise real enqueue/settings/company-reactivation producers,
nested commit, rollback, saved retry/lease restart, manual refresh
failure, capacity admission, concurrent commit, shutdown cancellation,
and SQL quiet after startup or pause.
- Sleep-safety tests reject sleep for future imports and automatic
refreshes, and accept inactive retained state.
- `pnpm -r typecheck` and `pnpm build` pass.
- `pnpm test:run` was attempted and stopped after repeated
embedded-PostgreSQL startup failures in unrelated suites. The two
existing import/refresh suites initially skipped for the same host
limitation; after adding explicit disposable-database options, all 63
regression tests passed against PostgreSQL 17. Full CI passed on
`ddb3ee51369baa8c7153bae9fd88efb8b903f2bc`: 55 successful checks, one
neutral review check, and two intentional skips.

## Risks

- Notifications are process-local. Independent SQL writers and separate
live replicas do not wake this worker; startup reconstructs persisted
work.
- Enabled automatic sources still contact GitHub every five minutes.
Their upstream changes have no event ingress here.
- Cloud scheduled wakes currently cover routines and issue monitors, not
skill sync. This change retains instances with outstanding skill
obligations. It does not claim measured host/database suspension or
fleet savings.
- No change to source authorization, installed content on failure, or
existing claim semantics.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository tools, and code
execution. The host does not expose an exact model revision or
context-window size to 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 16:21:42 -05:00
DottaandPaperclip cb2d6e80d3 perf: stop polling idle Muse maintenance (#17000)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Muse connects an external worker to a Paperclip agent.
> - Its maintenance loop scans the database every five seconds, even
without bindings.
> - These empty scans prevent an otherwise idle database from sleeping.
> - The shared delivery coordinator can react to committed writes and
saved deadlines.
> - This change uses that coordinator and retains recovery for
unfinished assignments.
> - Idle Muse maintenance can now stop querying the database.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Muse qualification, OAuth authority, and terminal assignment
maintenance.

**Current behavior**

A claimed instance runs two empty scans every five seconds even when
Muse has no bindings. This produces about 34,560 unnecessary queries per
day. Warm standby suppression from #16974 does not cover claimed
instances.

**Proposed behavior**

Recover once at startup. Wake after relevant transactions settle.
Schedule qualification expiry at its saved deadline. Retain five-second
recovery only for failures and unfinished assignments.

**Reason and benefit**

Remove recurring database traffic from idle Muse maintenance while
preserving authority checks and recovery. Reuse the database work signal
foundation from #15625 without adding tables.

## What Changed

- Register Muse maintenance with the shared delivery coordinator and its
idle admission rules.
- Signal pairing, OAuth attachment and revocation, qualification
changes, and new assignments before transaction writes. The existing
transaction layer waits for outer commit.
- Restore qualification deadlines from persisted bindings. Retry failed
sweeps and unfinished assignments.
- Add PostgreSQL tests for idle quiet, nested commit, rollback,
deadlines, standby, drain, and failure recovery. Verify signals from
actual Muse producers. Preserve startup, newly committed work, and
failed sweep retries across idle drain without SQL polling.
- Document the remaining recovery cadence and sleep limitations.

## Verification

- `pnpm -r typecheck`: passed before rebase. Server typechecking also
passed after rebase and the drain fix.
- `pnpm build`: passed before rebase.
- Final change: 33 maintenance, delivery coordinator, and scheduler
tests passed. Maintenance used a fresh disposable PostgreSQL 17
database.
- Muse control-plane tests: 254 passed against the same disposable
database.
- `pnpm test:run`: attempted, then stopped after repeated embedded
PostgreSQL startup failures from this host's semaphore limit.
Native-session, Muse, and AI-connection suites could not create local
clusters. The complete suite runs in CI.
- `git diff --check`: passed.
- Final commit `e106cbf20d`: all CI gates passed, including complete
test suites, E2E, build, production bundle boot, public-install smoke
test, and canary dry run. Greptile and Paperclip source reviews both
rated 5/5, with no unresolved findings.

The focused suites accept `PAPERCLIP_MUSE_MAINTENANCE_TEST_DATABASE_URL`
and `PAPERCLIP_MUSE_CONTROL_TEST_DATABASE_URL`. Use a disposable test
database; fixtures remove test data.

## Risks

- Unfinished assignments retain five-second recovery. This change
removes idle polling, not all Muse polling.
- Signals are process-local. Independent database writers need explicit
integration. Startup recovers persisted state.
- Qualification deadlines depend on the running process. Existing
durable Muse sleep blockers remain unchanged.
- The receiver keeps its batching timer. An empty buffer already makes
no database calls.
- No schema migration or API change.

## Model Used

OpenAI Codex, GPT-6 family, with code execution and repository tools.
The exact model version is 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 (focused suites; full local
run has the environment limitation 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 14:20:59 -05:00
DottaandPaperclip 505e1a5582 feat(cases): add persistent workflows with same-task agent handoffs (#16959)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - An agent workflow needs persistent facts and a safe way to transfer
work.
> - Creating a task for each phase splits the objective, discussion,
workspace, and review policy.
> - A case can retain the facts while one primary task moves between
agents.
> - Transfers must survive process failure and preserve private access,
budgets, and completion gates.
> - This pull request adds durable case handoffs, native case tools, and
read-only case pages.
> - Prompts describe the workflow. Existing task controls decide when
work can run and finish.

## Linked Issues or Issue Description

**Problem or motivation**

Prompt-driven agent teams need a persistent record that can move through
triage, implementation, verification, and correction. Ordinary
reassignment does not provide a durable instruction, safe release
barrier, or recoverable delivery receipt for an agent handing off its
own running task.

**Proposed solution**

Extend standalone cases with one primary task, a descriptive phase,
retained access sources, and a handoff ledger. Apply assignments after
source execution and workspace settlement. Deliver each transfer through
the existing durable wake queue. Provide `create_case`, `read_case`,
`update_case`, and `handoff_case` in the native runner. Creation
requires kickoff and explicit workflow text. An explicit handoff to
`done` closes the case when normal task completion is accepted.

**Alternatives considered**

Do not add a workflow engine, filesystem queue, configured state graph,
or one task per phase. The workflow document combines transition prose
with an optional Mermaid `stateDiagram-v2` map. The server does not
execute the diagram. The Pipelines experiment removed on master stays
removed.

**Roadmap alignment**

This extends Agent Reviews and Approvals, Enforced Outcomes, Bounded Run
Recovery, and Activity Log attribution. Existing approval, review,
budget, privacy, workspace, and responsible-user rules remain
authoritative.

Related work: [ordinary handoff wake
recovery](https://github.com/paperclipai/paperclip/pull/15019). This
feature adds a separate persisted case-handoff receipt and release
barrier.

## What Changed

- Add case routing fields, retained access sources, durable handoff
history, delivery leases, and replay-safe migrations.
- Keep the same primary task across agent phases. Preserve each
returning agent's own session and the task's workspace.
- Fence outgoing mutations, wait for provider and workspace release, and
recover delivery without duplicate task comments or runs. Recheck
current assignment permissions as the original caller before applying a
delayed transfer. Scope a run Stop to that run’s own handoff intents, so
repeated old Stop requests preserve newer work. Cancelling terminal
intent without a source run also requires fenced current task ownership,
including native retries and preparation with an old checkout. For Cloud
callers, revalidate current stack membership with an uncached Cloud RPC
and derive owner elevation from the current managed setting.
- Keep case reads, documents, assets, Audit, and live events inside
retained task/project permissions.
- Add native case creation/read/update/handoff tools. Store required
workflow text and initial documents on the first task. Page oversized
case facts and referenced document bodies through the dedicated tool
with bounded UTF-8 JSON, current routing versions, snapshot conflicts,
and current source authorization on every page.
- Add read-only case lists and detail pages, task-style list controls,
compact dotted field tables, linked handoff tasks, and case filters in
Audit.
- Add native/legacy integration tests, migration/restart tests, browser
coverage, and bounded live receipt fixtures. Extend the trusted sandbox
condition to cover the new subscription fixture profile.
- Hide obsolete paused-agent warnings on completed or cancelled task
threads.
- Synchronize the company-switch settings test with React effects after
a CI failure. Preserve its security assertions and production remount
behavior.

## Verification

The branch directly includes master `9ef434fe79`. The required `ci /
verify` and `ci / e2e` gates pass on head `765fccd67c`. [Current-head
CI](https://github.com/paperclipai/paperclip/actions/runs/39464348614)
passes typecheck, build, tests, and all eight browser shards. Security
checks pass. [Greptile gives this exact head
5/5](https://github.com/paperclipai/paperclip/pull/16959#issuecomment-6291156549),
with no unresolved review threads. The optional Paperclip Review check
remains queued because its assigned agent is paused. It is not a
required branch check. No check result was overridden.

Local workspace typecheck and build pass after the master merge, along
with database migration checks, token gates, map checks, Storybook
catalog checks, and the Copilot profile identity check. Standard builds
refresh Runner vendor output. Focused checks pass 2,008
API/paging/context tests and 7 strict case protocol tests. The final
commit changes one test fixture only; its ten tests, UI typecheck, and
token gates pass. No production source changed in that commit.

On current head `765fccd67c`, all **194 critical workflow tests**
executed and passed with no skipped tests in those files:

- 38 Stop/ownership tests, including board completion intent, native
retries, stale Stop, and preparation with an old checkout.
- 2 actual legacy process/HTTP handoff tests, including scratch failure,
a closed-stdio child that continues writing, and recovery through a
fresh worker after the process group stops.
- 5 migration tests, including SQL column metadata, preserved null
authority context, and all six prior-numbering receipts.
- 3 process-settlement and 5 scratch tests.
- 88 handoff-service, 18 heartbeat handoff, and 21 native case-tool
integration tests.
- 7 bounded case/document-read projection tests and 7 strict
protocol-contract tests.

The current CI checkout was `fed65d49`, merging `765fccd67c` into master
`9ef434fe79`. Its new document regressions reconstruct exact
Unicode/escaped bodies and revisions through real native authority and
authenticated HTTP with generic API tools disabled. They cover
case-owned, primary-task, and linked-task documents; routing on every
page; stale document snapshots; access loss and unlinking;
unrelated/foreign/private sources; system documents; and quarantined
content. Active-task document tools retain their existing behavior.
These are current-head test results from the first CI attempt; the
browser-only retry did not rerun these suites.

The full `64fdd3403f` CI failed one separate settings test: after a
company switch, it observed an unsettled API-key draft. The production
component and test were unchanged from prior green source. Production
switches remount both keyed settings layers; the test directly reused an
unkeyed view and used a timer tick instead of waiting for React effects.
The final commit replaces that fixture synchronization with React `act`.
The exact secret-clearing assertions and all timeouts remain unchanged.
The original assertion failure remains recorded; no unchanged retry was
used. The narrow unchanged baseline and fixed fixture each pass 10
tests.

The upstream result-paging merge preserves case capture namespaces and
current asset privacy. Its API integration suite executed 39 of 41 tests
successfully, with two skipped; the optional assigned-result PRP
integration test was skipped. Neither skipped suite is counted in the
194 critical tests or as live provider proof.

All 361 upstream migration entries and SQL bodies remain unchanged. Six
case migrations follow master as 0363–0368 with identical SQL hashes.
Official Drizzle generators produced the combined metadata and five
retained snapshots. Generation reports no schema changes, and the
snapshot drift test passes. The earlier `ce361ae5d0` CI passed on
attempt 1, including its then-current 179 critical tests. Fresh review
later identified unusable document references with generic API tools
disabled; the bounded document reads and ten additional authenticated
HTTP tests address that finding.

The full local `pnpm test:run` attempt was cancelled after this Mac
exhausted PostgreSQL semaphore capacity; it is **not** counted as a
pass. Later database attempts also stopped before assertions or skipped,
including one sandbox socket denial. Two original stale-Stop regressions
did reproduce the bug against the old source. A REST serialization
assertion passed after two preserved database-startup failures. No test
timeout was increased. All 194 assertions listed above executed and
passed in current-head CI. The complete 385-file, 3,963-test UI shard
also passed after the fixture repair.

A configured-provider receipt workflow passed 13 backend checks across
triage → implementation → verification → correction → done, using one
task and four work runs. The saved receipt is revision 2 with total 6
USD; case and task both closed. The original canonical attempt remains
failed because its browser assertion confused the terminal destination
with completed-task tooltips. The corrected full browser verifier passed
on the same retained database and original runs, with no additional
provider calls. The last clean-build browser readback is on
`6a86c54a53`; later changes affect recovery, authorization, bounded
reads, and settlement. Accounted work cost is a $1.010574 rate-card
estimate; provider billing and setup-probe usage are unavailable.

Copilot profile 49 passed the unchanged seven-fixture private protocol
pack (46/46 checks, no retries) and the product file-edit canary after
master's completion-replay change. All 60 bound implementation hashes
remain unchanged on the current source. Historical profile-47/48
identities and results remain intact. See the [profile-49 compatibility
record](https://github.com/paperclipai/paperclip/blob/codex/persistent-case-handoffs/doc/plans/2026-10-20-copilot-completion-recovery-compatibility-v49.json).
This is bounded compatibility evidence, not a fresh full matrix,
installed-package product test, or Daytona rollout.

Preserved CI failures and retries:

- On `20662930ac`, Slack managed setup failed a five-second success
assertion after reload. Its test/application path was unchanged from a
prior green head. One unchanged-source retry passed all 25 tests in that
shard; no source or timeout changed.
- On `9402b4f17a`, two CI workers received shutdown signals at 15:24:19
UTC. Neither log had an assertion failure before interruption. One
unchanged-source retry of each passed all Runner static checks and all
32 tests in browser shard 6; both aggregates passed. The next head,
`7b4374edaf`, passed all CI without retries.
- On `295f289b5a`, serialized shard 9 timed out during its first
device-login test, then the next test found an active login lease.
Direct login/test paths were unchanged from a prior green head. Cold
import plus a timed-out async body using the next test's mutable
actor/store is the supported fixture-race explanation; no request trace
proves that sequence. The original failure remains recorded. One
unchanged-source retry passed all 294 tests in 19 files, including all
41 device-login tests. No login source or timeout changed. The
subsequent `ce361ae5d0` CI passed without retries.
- On `64fdd3403f`, one company-switch settings assertion observed an
unsettled draft. The final test-only commit uses React `act` without
changing assertions, timeouts, or production code. On `765fccd67c`, all
ten settings tests and the complete 385-file, 3,963-test UI shard
passed.
- On `765fccd67c`, browser shard 3 completed the split-handoff
assertions, then cleanup received HTTP 500 from run cancellation after a
one-second PostgreSQL row-lock timeout. The extra chat run was reporting
completed work while teardown began. Cleanup stopped before restoring a
global experiment flag, causing a later assertion to fail. Relevant
production and test paths are unchanged from an earlier green commit.
Stop's lock order is consistent, and no nested self-lock was found; the
exact PostgreSQL blocker was not recorded. One unchanged-shard retry
passed. The original failure, trace, and downstream flag failure remain
recorded. No Stop implementation, cleanup assertion, or timeout changed.
The retry supports intermittent cleanup contention; it does not identify
the original blocking transaction.


## Risks

- Local process transfers require host-owned PID/group absence. Missing
or uncertain termination evidence keeps delivery pending, independently
of scratch setup.
- Handoff admission and task cancellation share execution ownership. New
regression tests cover temporary holds, late source writes, and
uncertain provider termination.
- Retained private sources use an intersection of existing permissions.
This intentionally fails closed when a source is missing or an existing
task cannot preserve that boundary.
- Migrations add tables and indexes. They preserve master history and
can replay over prior feature installs. Migration 0368 adds nullable
internal authorization provenance with no default or authority backfill.
Historical transfers still awaiting release without this context fail
closed; an operator must cancel them and request a new handoff.
- Delayed Cloud handoffs depend on the current Cloud membership RPC. An
unavailable, ambiguous, expired, or mismatched response holds the
handoff for retry; it does not reuse cached authority or grant access.
- Bounded reads reload and reauthorize the selected projection per page.
Underlying work grows with the data size and number of pages. Changed
data or routing requires restarting from offset zero without the old
snapshot.
- Phase names and workflow diagrams remain advisory. They do not satisfy
review or approval gates.
- This is a large, cross-cutting PR. The maintainer requested one PR for
the whole feature; the commits separate data, runtime, UI, and
qualification work.

## Model Used

OpenAI Codex, GPT-6 family, with tool use, code execution, and delegated
code review. The exact deployment model ID and context-window size were
not exposed in the session. Live fixture agents used the explicitly
configured provider model recorded in their test evidence.

## 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 12:46:14 -05:00
Devin FoleyandPaperclip 78b833c495 Dispatch routine and monitor work at saved deadlines (#16514)
## Thinking Path

> - Paperclip manages work that agents run for a company.
> - Routines and issue monitors store their next run time in the
database.
> - The app currently waits for the heartbeat interval to find that
work.
> - This can delay work after a workspace wakes before its saved
deadline.
> - This pull request uses the existing work coordinator to schedule
each known deadline.
> - It also parses each cron expression once, so sparse schedules do
less work on the event loop.
> - Agents can start due work sooner while the existing execution checks
stay in place.

## Linked Issues or Issue Description

**What happened?**

Routine and issue-monitor dispatch depends on the heartbeat scan
interval. A workspace can wake early and still wait for the next scan. A
sparse cron search also parses the same expression for every minute it
checks. In a local annual-schedule test, the deadline callback ran on
time but schedule calculation delayed the routine record by 13.6
seconds.

**Expected behavior**

Use a saved future deadline to request the existing dispatch scan.
Restore deadlines at startup and update them after a producer
transaction settles. Do not let future timers keep an idle workspace
active. Parse a cron expression once for each search.

**Steps to reproduce**

1. Set an active routine and an assigned issue monitor to the same
future deadline.
2. Choose a deadline between heartbeat scans. Use an annual routine
expression to exercise a sparse search.
3. Compare the saved deadline with the routine firing and agent start
times.

**Paperclip version or commit**

Source checkout based on c7ea5c0216.

Related public changes: #16215 reports routine deadlines for external
wake. #8034 caches time-zone formatters in the cron search. This change
reuses those contracts.

## What Changed

- Register separate routine and issue-monitor workers with the existing
delivery coordinator after startup housekeeping.
- Restore the earliest eligible future deadline from the database after
each scan.
- Notify the matching worker when routine or monitor writes settle,
including the native `set_task_monitor` tool. Keep the heartbeat
interval as a recovery scan for blocked or externally written work.
- Retain the scan-start cutoff so a deadline crossed during a slow scan
receives an immediate follow-up scan.
- Retain the coordinator gates for standby, idle drain, and shutdown.
Future timers do not count as active work. Bound expired deadline
retries during idle reconciliation so a paused worker cannot spin.
- Parse a cron expression once during a next-run search. Skip impossible
UTC dates before time-zone formatting. Preserve the existing calendar
and time-zone matching.
- Add clock, transaction, failure, SQL eligibility, and producer
notification tests. Document the scheduler behavior.

## Verification

- `pnpm -r typecheck`: passed.
- `pnpm build`: passed.
- Full CI, browser tests, and security scan: passed on final head
9fc857dcec. Greptile Apex is 5/5 with no
unresolved comments.
- Final focused startup, deadline, routine, SQL eligibility, native
monitor, cron, and coordinator tests: 159 passed across seven suites.
The crossed-deadline and native-notification regressions failed before
their fixes.
- CI caught the existing startup suppression assertion. It failed
locally before restoring the outer timer-dispatch guard; all 38 startup
tests now pass.
- A canceled-drain regression also failed before the fix. Future timers
keep their saved time; only expired deadlines use the bounded idle
retry. Server typecheck and build passed again.
- The idle-drain regression reproduced 22 checks in 20 ms before its
fix. After the fix, 43 coordinator and related worker tests passed.
Server typecheck and build passed again after the review fixes.
- The broad local `pnpm test:run` attempt reported existing
host-sensitive failures. Two Git fixture tests fail identically on
unchanged c7ea5c0 because the host default `main` leaves an extra branch
in an intended master-only remote. Both pass with a command-scoped
`init.defaultBranch=master` setting. No Git settings were changed on
disk.
- A diagnostics test also fails on both unchanged master and this branch
because its broad `private` substring assertion matches the canonical
macOS temporary source path in an error stack. A local Anthropic hire
assertion failed only in the broad run; all 70 tests in that file pass
on both source revisions when run separately. Its broad-run cause is not
determined. These failures remain recorded. The full exact-head CI
matrix passed; the broad local development attempt was then stopped.
- An earlier full run was interrupted after PostgreSQL reported host
shared-memory exhaustion. One retry was stopped when timing required the
UTC optimization. Acceptance then used an owned database on the existing
local PostgreSQL service. No shared services were stopped or reset.
- Local dispatch: routine record at +0.102 s, routine agent start at
+0.171 s, monitor agent start at +0.159 s. Both scripted agents
succeeded using real run-scoped API authentication. No model provider
was called. No duplicate runs occurred during the 70-second follow-up.
Platform: macOS, Node 25.6.1, isolated database on local PostgreSQL 16,
source checkout. The final run used a fresh database and a clean
checkout of 9fc857dcec.
- The first timing run exposed the repeated cron parsing described
above. A diagnostic run showed the timer fired 1 ms after the deadline
while the annual search blocked the event loop. Parsing once reduced
local dispatch to 5.9 seconds, which still missed the five-second test
target. The next change skips impossible UTC dates before formatting. A
second regression failed before that fix. The final timing result and
duplicate check passed.
- A separate acceptance setup failed because the direct local server had
no agent signing key. That owned fixture was removed. The retry uses an
ephemeral key in its isolated process environment.
- Owned fixtures were deleted, the local runtime exited normally, and
the disposable databases were dropped. Shared services remain running.
- No production deployment or npm publication is part of this PR.

## Risks

- Known deadlines now trigger a scan between heartbeat ticks. Existing
atomic claims, suppression checks, budgets, and execution rules still
decide whether work runs.
- Direct database writers rely on the heartbeat recovery scan. Overdue
blocked work also retains that recovery interval to avoid a tight retry
loop.
- This is not a hard real-time guarantee. Provider startup, queue load,
and other synchronous work can still delay agent execution.
- No database migration or API schema change. Revert this commit to
restore interval-only dispatch.

## Model Used

OpenAI GPT-6 (Codex), with reasoning, repository tools, terminal
execution, and automated tests. The session does not expose a more
specific model version 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 and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass for the changed areas;
full-suite CI and local exceptions are 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-15 22:44:17 -07:00
DottaandPaperclip f69c6a8a35 fix(github): retry and recover review publication (#16284)
## Thinking Path

> - Paperclip manages agent work and its published results.
> - GitHub review bots publish task-bound assessments through governed
tools.
> - A review can survive a caller timeout because its publication intent
is durable.
> - A transient provider failure still returned a failed receipt before
recovery.
> - Gateway replay could return an empty cached result, and publication
retries had no dedicated scheduler deadline.
> - This pull request adds bounded retries, receipt recovery, and an
independent durable worker.
> - A temporary GitHub failure can recover without another review or
duplicate publication.

## Linked Issues or Issue Description

Refs #16275. That change increased the GitHub tool deadline. This change
repairs retry and recovery behavior.

**What happened?**

A one-off GitHub HTTP 503 or 429 left the caller with a failed
publication receipt. The saved retry did not have its own scheduler
worker. An identical gateway call after a timeout could return an empty
cached result instead of the saved publication receipt.

**Expected behavior**

Temporary failures should retry under current authority. Pending work
should resume at its saved deadline after restart. Exact retries should
return the current receipt. Lost responses must not create duplicate
comments, findings, or checks.

**Steps to reproduce**

1. Admit an advisory PR review request and begin its assessment.
2. Return HTTP 503 or 429 for the first summary-comment PATCH, then let
GitHub succeed.
3. Submit the assessment through the run-scoped tool gateway.
4. On master, the result says publication failed. With this change, the
same call returns the processed receipt and neutral check.
5. For receipt recovery, use a shorter caller deadline and repeat the
exact call after publication finishes.

**Paperclip version or commit**

Reproduced on master at `6cb6ac3b8e40cb90d734ba115a2f22f850ff9477`.

## What Changed

- Classify transient transport, server, and rate-limit failures. Honor
Retry-After and primary reset deadlines. Stop retries for permanent
permission and validation errors.
- Retry safe reads and installation-token issuance within a bounded call
window. Retry publication writes through the existing outbox and
provider receipt checks.
- Wait for up to three publication attempts within a 45-second retry
window. Keep the durable eight-failure cap and enforce saved deadlines
on explicit retries.
- Register an independent GitHub publication worker with
transaction-owned wake notifications, startup recovery, and per-PR lease
deadlines.
- Recover exact comment, progress, and assessment gateway calls from
their stored publication. Require the same company, agent, task, run,
connection, tool, arguments, and current policy. Clear stale timeout
errors after recovery. Approved exact retries read the current receipt
through the existing signed replay path; the worker retains the verified
approval for deferred writes and still rejects revocation.
- Add regression coverage and update the connector guide and
feature/access maps.

## Verification

- Red: both one-off HTTP 503 and 429 regressions failed on master with a
failed publication receipt.
- Green: the same tests passed after the change.
- GitHub review workflows: 133 covered against isolated PostgreSQL 18.
The final combined run passed 132 and hit one fifteen-second timeout
under concurrent build load. All three variants of that workflow passed
in isolation. The earlier combined run passed all 131 workflows before
the two approval regressions were added.
- Retry policy and delivery coordinator tests: 35 passed.
- Additional focused checks passed for the actual eight-failure cap,
exact-argument conflicts, policy revocation, and timeout receipt
recovery. The approved deferred-publication and revocation regressions
failed before the review repair and passed after it, with one approval
request preserved.
- `pnpm -r typecheck`: passed. `pnpm build`: passed. `node
scripts/check-maps.mjs`: passed.
- The broad local `pnpm test:run` path remains blocked by the host's
exhausted PostgreSQL shared-memory slots. The changed database-backed
workflows use an isolated Docker database instead. All Paperclip CI jobs
passed at a6d81b67cb, including the required verify and E2E gates.
Greptile is 5/5 at the same head, with the approval-recovery finding
resolved. The external Superagent Security Scan also passed.
- Live acceptance passed on a dedicated single-repository local test App
and native Paperclip Runner. A test-only preload returned one HTTP 503
before the final summary PATCH. One submit_review call completed in
17.305 seconds; the bot published its 3/5 advisory review and neutral
check, then automatically finished the task. [Published
review](https://github.com/paperclipai/paperclip-permissions-smoke-20260926-pap57-fee7428e/pull/9#issuecomment-6168809007),
[neutral
check](https://github.com/paperclipai/paperclip-permissions-smoke-20260926-pap57-fee7428e/runs/115863564014).
This live test ran at de6862072c; approval-specific recovery is covered
by the later regressions. The preload is excluded from the repository
and removed from the running local instance.

## Risks

Retries make additional bounded provider requests. A provider write with
a lost response is recovered through its existing App-owned identity
before another write is permitted. The worker rechecks authority and the
PR head; revocation and superseded work stop publication. Calls can
still exceed their deadline, and repeated failures eventually exhaust
the retry cap. A pending publication is not a completed task. The agent
still owns task completion after receiving a confirmed receipt. No
schema migration is needed.

## Model Used

OpenAI Codex (GPT-6 family), with code inspection, editing, and test
execution. The exact deployment 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-14 08:40:32 -05:00
DottaandPaperclip 7c016c049b perf(workspaces): wake terminal cleanup from committed changes (#16282)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents use execution workspaces to retain their files and Git
branches.
> - A reaper closes eligible workspaces after their task tree is
terminal.
> - The reaper scanned every heartbeat tick and read Git before cheap
eligibility checks.
> - Permanent scans also query idle databases when no cleanup work
exists.
> - This pull request wakes cleanup after transaction settlement and
restores bounded work at startup.
> - The benefit is fewer scans while fresh cleanup guards continue to
protect retained work.

## Linked Issues or Issue Description

Related work: Refs #13619, Refs #11753. Those PRs bound scans or back
off unchanged candidates. This PR uses the transaction work coordinator
to remove the permanent reaper tick. It overlaps their early
terminal-state filtering but does not change their runtime restart
behavior or introduce a second Git scheduler cache.

**What existing behavior does this improve?**

Terminal workspace cleanup and Git inspection for workspace detail
reads.

**Subsystem affected**

Server orchestration, execution workspaces, and idle sleep admission.

**Current behavior**

The heartbeat tick scans workspace candidates every thirty seconds. It
runs Git before rejecting nonterminal trees, cooldowns, and active runs.
Display inspections expire after five seconds.

**Proposed behavior**

Recover workspace rows in bounded startup pages. Wake after relevant
issue, workspace, and pull-request work-product transactions settle.
Schedule the terminal tree cooldown or a five-minute eligibility retry.
Back off unchanged candidates across unrelated wakes, and invalidate
their backoff after relevant committed changes. Park empty and
nonterminal-only lanes. Reuse display inspections for thirty seconds.
Read fresh Git before destructive cleanup.

**Reason and benefit**

Avoid recurring database and Git work when no cleanup is possible.
Retain transaction admission holds, restart recovery, and fresh deletion
checks. Retention eligibility can wait for the next host wake; active
and failed recovery work still blocks sleep.

**Breaking changes**

No schema or API shape changes. Display Git state can lag by thirty
seconds. Cleanup eligibility retries can take five minutes.

## What Changed

- Add a terminal-workspace worker to the existing transaction work
coordinator and scheduler.
- Signal relevant writes before mutation on the owning transaction.
Outer transaction settlement wakes cleanup.
- Derive cooldown deadlines from the full company-scoped terminal task
tree. Continue bounded recovery pages without a permanent sweep.
- Bound unchanged candidate inspections to five minutes, retain at most
256 entries, and invalidate affected entries with scoped transaction
hints. Repeated wakes preserve the earliest inspection deadline.
- Collect deadlines from task trees already read by bounded pages.
Restore the cursor after a failed page and rescan commits received
during a rotation.
- Check terminal state, cooldown, and active runs before Git. Preserve
lifecycle locks, generation fences, and fresh final checks.
- Keep failed or incomplete recovery visible to idle admission. Preserve
the passive retention exception.
- Extend the bounded display cache to thirty seconds and document the
scheduling and wake limits.

## Verification

- Focused workspace, coordinator, cache, and local sleep suites: 111
tests passed with real local PostgreSQL and Git worktrees where
applicable.
- Final worker, display cache, and local sleep suites: 28 tests passed.
- The regressions prove three ineligible workspaces invoke zero Git
scans, cached clean display state cannot remove new untracked files, and
real producer notifications wait for outer transaction settlement.
- `pnpm -r typecheck` and `pnpm build` passed locally. Map and
module-boundary checks passed.
- Work-product and sleep-safety suites: 221 tests passed. Reopen and
issue-tree control suites: 46 tests passed. Final backoff, workspace,
worker, and work-product suites: 87 tests passed.
- Final workspace and worker suites: 81 tests passed. Deadline selection
performs zero database reads. A failed full page retries its unread
terminal workspace before recovery can park.
- Full CI coverage passed on head `2c397970f7`: general tests,
serialized server suites, runner verification, E2E shards, typecheck,
build, and the final verification gate. The duplicate serial local `pnpm
test:run` was stopped after full CI coverage passed; it is not reported
as a completed local pass.
- Greptile: 5/5 with both findings resolved. The security scan also
passed. No failing or pending PR checks remain.

## Risks

- Display state can be stale for thirty seconds. Destructive guards
bypass the display cache.
- Undelivered or otherwise ineligible terminal work retries after five
minutes instead of each heartbeat tick.
- Transaction signals cover the owning `createDb` scope. Direct SQL and
independent server processes require startup recovery.
- Local cooldown timers are maintenance hints. They do not establish an
external wake alarm or whole-instance sleep safety. Existing blockers
remain in force; failed bounded recovery keeps the host awake.

## Model Used

OpenAI GPT-6, model ID `gpt-6`, through Codex. Reasoning, repository
inspection, shell execution, and browser tools assisted the change. The
runtime does not expose the 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>
2026-10-14 08:28:45 -05:00
Nicky LeachandPaperclip db65a0863e Deliver durable resource lifecycle events to plugins without polling (#16203)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Plugins keep external resources aligned with Paperclip projects and
agents.
> - The durable resource journal stores changes, but its plugin
interface requires polling.
> - Project deletion has no journal entry, so consumers must scan for
missing projects.
> - This PR delivers committed journal events through a native plugin
callback and records deletion.
> - Plugins can recover unfinished work without periodic change scans.

## Linked Issues or Issue Description

Related: #15343 introduced the durable inbox. #15631 added required
agent lifecycle participation. This change keeps both contracts and adds
automatic delivery for the resource inbox.

**What happened?**

Plugins must schedule reads of the durable inbox. A deleted project has
no durable deletion event. Consumers must scan saved bindings to detect
its removal.

**Expected behavior**

Core should deliver committed resource events and replay unfinished
work. Empty queues should have no polling timer. Project removal should
leave a durable event.

**Steps to reproduce**

1. Install a plugin that consumes project lifecycle events.
2. Create or update a project. The plugin must call `listLifecycle` to
discover it.
3. Delete the project. The journal has no deletion entry.

## What Changed

- Add `resourceLifecycle` to the manifest and `onResourceLifecycle` to
the SDK. Require `events.subscribe` and a configured, enabled company
scope.
- Reuse transaction-aware work signals and the delivery coordinator.
Wake after commit, on startup and plugin activation, and after
configuration changes.
- Acknowledge only a matching successful result. Retry unfinished or
interrupted work. Retain deferred events without an idle retry timer.
- Wake again after successful worker configuration application, and
retain failed scans through idle drain (both Apex findings fixed).
- Keep ordering per resource and deliver past failed or deferred
resources. Keep the manual inbox methods for existing consumers.
- Record project deletion in the removal transaction. Extend the journal
constraint with a generated migration.

## Verification

- Apex follow-up: 129 focused worker, delivery, coordinator, and
application tests passed; server typecheck passed. Regression coverage
verifies configuration-application races and failed scans across idle
drain.

- Focused lifecycle, transaction coordinator, manifest, and SDK dispatch
suites: 105 tests passed, including single-connection saves, redacted
failure diagnostics, wakeups retained during idle drain, and per-event
retry deadlines.
- Complete SDK suite plus delivery database tests: 128 tests passed.
- Shared, database, and SDK typechecks passed. Direct server `tsc
--noEmit` passed against rebuilt SDK declarations.
- Full local `pnpm test:run` finished with 19,491 passed, 148 skipped,
and 6 failed tests across 5 files (plus two suite setup errors). This
run overlapped branch updates and is not final-head evidence: failures
included missing Cargo, local Git default-branch assumptions, and stale
in-process lifecycle modules. Final Apex-fix commit `75d028788` passed
all CI gates, including the full test shards, and received Apex 5/5 with
no unresolved comments.
- `pnpm -r typecheck` and `pnpm build` were attempted. Both stopped
because Cargo is unavailable locally. CI supplies the Rust toolchain.
- Local installation used the repository's documented lockfile fallback
because master has dependencies missing from its lockfile. No dependency
changes are included here.

## Risks

- Delivery is at least once. Plugins must re-read current state and make
external effects idempotent.
- Signals are process-local, like the other delivery queues. Startup
recovers committed rows. Independent database writers or multiple active
server processes require an explicit ownership and wake protocol.
- Apply migration `0340_open_warlock.sql` before running code that
records project deletion. Earlier deletions cannot be reconstructed.
- Automatic delivery selects plugins with company configuration rows.
`deferred` waits for a later startup, configuration change, or other
work notification.
- This does not replace required agent lifecycle participation or
establish that external mounts are unused.

## Model Used

OpenAI Codex (GPT-6), with tool-assisted code editing, code inspection,
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
- [ ] 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-13 12:00:30 -07:00
Devin FoleyandPaperclip 833769ab55 perf(chat): schedule Telegram maintenance from commits and deadlines (#16094)
Replace periodic Telegram discovery and maintenance scans with bounded startup recovery, connection-specific commit notifications, and durable action deadlines. Preserve ownership, inactive subscription parking, retry and cleanup recovery, and atomic credential binding updates.

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

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 07:55:27 -07:00
DottaandPaperclip e168512f7b perf(chat): stop polling failed run retries (#15871)
Stop one-second scans of failed chat-run retries. Wake the existing retry queue after transaction commits and at saved retry or stale-claim deadlines. Preserve claim fences, authorization, recovery, and idempotent agent admission.

Resolve conflicts with the current chat-service extraction by applying the retry signals and shared deadline predicate in the failed-run retry module.

Verified with pnpm typecheck, pnpm build, all 1,328 chat and coordinator regression tests, and current-head CI. Greptile reviewed the final head at 5/5 with no unresolved threads.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 19:49:47 -07:00
DottaandPaperclip e733bddc45 perf(chat): stop polling provider action and session sync queues (#15822)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Chat stores pending provider actions in durable database rows.
> - Three action queues still scan every second when empty.
> - These scans keep idle databases active.
> - This pull request wakes those queues after a commit or at their next
deadline.
> - Empty queues stop querying while existing recovery rules remain in
place.

## Linked Issues or Issue Description

Refs #15808, #15802, #15625. Related sleep work: #15750, #15757.

**What existing behavior does this improve?**

Provider messages and notices, accepted GitHub webhook receipts, and
Slack session status synchronization.

**Current behavior**

Chat maintenance scans these three queues every second. It also rechecks
paused Slack sync rows every 30 seconds.

**Proposed behavior**

Reuse the transaction-aware action workers and existing chat_actions
rows. Wake after commits. Restore unfinished work on startup. Use stored
retry and stale-claim deadlines. Stop scanning when empty. Park paused
Slack sync rows until an endpoint change.

## What Changed

- Add three topics to the existing action worker registration. No
tables, migrations, triggers, or external services.
- Signal producers, claims, retry updates, terminal settlements, and
operator resolution inside their owning transaction. Provider I/O
remains outside transactions.
- Share pending-state and deadline predicates between dispatch and
scheduling. Exclude terminal history and uncertain provider sends.
- Preserve GitHub receipt attempt fences and provider-send quarantine.
Preserve Slack's 30-minute refresh while a session is processing.
- Remove these workers from periodic app maintenance. Update the sleep
inventory and add regression coverage.

## Verification

- Passed: 13 focused tests across scheduler, app wiring, and real
PostgreSQL integration. Covers actual commit wakes with deferred fast
paths disabled, GitHub retries and startup recovery, stale-send
quarantine, Slack publication-to-sync delivery, paused deadlines, and
idle queues.
- Passed: pnpm typecheck.
- Passed: full chat integration and related coordinator suites, **1,274
tests across five files**.
- Passed: pnpm build.
- Greptile: **5/5**, no actionable findings, completed on
`dc284cd275e5f6df624d60b27954421f2bcdbee2`.
- Hosted CI: **54 checks passed**, two intentionally skipped Storybook
checks, no failures or pending checks on
`dc284cd275e5f6df624d60b27954421f2bcdbee2`.

Provider responses are simulated. PostgreSQL, transactions, service
dispatch, and scheduling are real. No live Slack or GitHub App was used.

## Risks

- Writers must emit the existing process-local transaction signal.
Independent database clients and direct SQL still need explicit
integration or startup recovery.
- Accepted GitHub receipts are covered. Finding callbacks GitHub never
delivered still uses the separate provider recovery lane.
- Other chat maintenance and persistent transports still run. This does
not establish whole-instance sleep.
- Existing claim, revision, credential, and authorization checks remain
authoritative during concurrent foreground and scheduled dispatch.

## Model Used

OpenAI Codex (GPT-6), with repository inspection, code execution, and
automated tests.

## 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-11 06:08:05 -05:00
DottaandPaperclip b1ebe3de3c perf(chat): wake action queues from commits and deadlines (#15808)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Chat keeps durable rows for provider actions and setup messages.
> - Five action queues still scan the database every second when empty.
> - Empty scans add idle database traffic and prevent cost savings from
sleep.
> - This pull request wakes these queues when their writes commit or
their next deadline arrives.
> - Existing rows retain retry and restart recovery. Empty queues stop
scanning.

## Linked Issues or Issue Description

Refs #15802, #15625, #15750, #15757. Related allocation work: #15153.

**What existing behavior does this improve?**

Idle chat reconciliation for Slack Board messages, Slack slash-command
task starts, Slack setup verification messages, receipt reactions, and
Slack session stops.

**Current behavior**

The app runs these five queue scans through chat maintenance every
second, including when no work exists.

**Proposed behavior**

Each queue has an independent worker on the existing scheduler.
Producers and foreground claim/retry/settlement writes announce work
inside their database transaction. The worker wakes after the outer
transaction settles. Startup restores durable work. Shared dispatch and
deadline predicates select real retries and abandoned claims. Empty
queues disarm.

**Subsystem affected**

Server chat services. No schema or migration changes.

## What Changed

- Register five action workers alongside the three delivery workers from
#15802. Remove their scans from periodic app maintenance.
- Share action eligibility between dispatch and deadline selection. Park
paused Board messages and task starts until endpoint changes. Preserve
uncertain-send quarantine.
- Wake setup verification after account, welcome, registration, or
authenticated callback changes. Exclude prerequisites that need another
event.
- Persist a retry time for failed provider-confirmed task admission. Its
own commit notification cannot cause an immediate retry loop.
- Reuse the existing transaction signals and scheduler registration
code. Provider calls remain outside database transactions.
- Update the instance-sleep inventory and add scheduler and PostgreSQL
regression coverage.

## Verification

- Passed: 37 scheduler, coordinator, and app-wiring tests, including an
idle hour with no further scans or timers.
- Passed: seven focused PostgreSQL chat cases. These cover deadline
eligibility, startup recovery, outer-commit wakes, actual producer
signals, and signed-callback verification without maintenance polling.
- Passed: `pnpm typecheck`.
- Passed: full chat integration suite and related
publication/coordinator tests, **1,278 tests** across six files,
including **1,228 chat integration cases**.
- Passed: `pnpm build`.
- After the review fix, seven focused PostgreSQL integration cases and
the server typecheck passed again. Deadline and dispatch queries now
exclude completed action history.
- Greptile: **5/5** on `35a979fce6bb9ab910fab3373ec8e537fc2d01a9`, with
the prior finding resolved and no new findings.
- Fixed a CI timing failure in the GitHub mention integration test:
asynchronous admission now has an explicit ten-second assertion deadline
instead of the default one second. The failure reproduced locally; all
five mention variants and all 398 cases in the matching CI shard pass
after the fix.
- Hosted CI passed on `35a979fce6bb9ab910fab3373ec8e537fc2d01a9`: **54
successful checks**, two intentionally skipped Storybook checks, no
failures or pending checks.

Provider responses are simulated in the integration suite. PostgreSQL,
transactions, service dispatch, and scheduling are real. No live Slack
workspace was used.

## Risks

- Every producer that changes runnable state must emit a transaction
signal. This change covers the five queues and their foreground
dispatch/retry paths. Direct SQL and independent DB owners still need
explicit integration or startup recovery.
- Missing retry or claim deadlines can strand work. Tests compare the
dispatch and deadline behavior, including paused and uncertain states.
- Other provider maintenance, sockets, and scheduled jobs remain. This
change does not establish full instance sleep.
- No new tables, migrations, or provider credentials are required.

## Model Used

OpenAI Codex (GPT-6), with repository inspection, code execution, and
automated tests.

## 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-10 13:14:23 -05:00
DottaandPaperclip a7dd5284b5 feat: add company fast-response acknowledgements (#15748)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Human messages start agent work through task, chat, and
external-channel intake.
> - Agent startup can delay the first visible response.
> - A small direct model call can acknowledge the request while normal
work starts.
> - This response must remain separate from evidence that the agent
answered or completed work.
> - This pull request adds optional company-wide fast responses with
durable receipts, access checks, and cost accounting.
> - Users receive prompt acknowledgement without changing the agent
execution path.

## Linked Issues or Issue Description

**Subsystem affected**

Cross-cutting: database, shared contracts, server orchestration,
connections, and UI.

**Problem or motivation**

Users can wait for an agent to start before they receive a contextual
response. Generic progress messages do not confirm what the user asked.
An acknowledgement must not falsely complete work or satisfy an
unanswered turn.

**Proposed solution**

Add a disabled-by-default experimental fast-response model using an
existing shared API connection. Generate one short acknowledgement for
each accepted human turn. Use the existing agent identity and external
publication outboxes. Retain platform provenance and account for the
inference independently.

**Alternatives considered**

Running the acknowledgement through the agent runtime adds startup
latency. Restricting implementation to OpenRouter leaves other existing
API connections unsupported. This change supports all existing API
connections and uses OpenRouter for live verification.

**Roadmap alignment**

This extends existing agent communication and connection settings.
ROADMAP.md and open pull requests were checked. No duplicate
fast-response implementation was found. The maintainer supplied the
feature plan directly.

## What Changed

- Add Experimental fast responses to instance settings. Keep the feature
off by default. Enforce the flag at intake, dispatch, and publication.
- Move Decision model and Fast response setup to company Settings →
Connections when the experiment is enabled. Place Connections below
Environments. Separate the model sections with clear headings and a
divider. Keep Decision model in General when the experiment is off.
Route Costs setup links to the available settings page.
- Make the acknowledgement prompt a multiline template string. Ask for a
brief, positive response in the user’s language.

- Add settings, connection/model selection, sponsorship, fixed-sample
testing, and Costs history.
- Add a Vercel AI SDK text service for existing native, routed, Bedrock,
and local API connections.
- Add durable per-turn requests, a bounded worker, a three-second
admission deadline, and one inference attempt.
- Integrate task creation, task and agent chat, steering, queued
follow-ups, experimental board chat, shared chat intake, and email.
- Preserve receipt provenance and exclude receipts from completion,
recovery, interaction resolution, and agent wakeups.
- Recheck authorization, source validity, real replies, budgets, and
destination binding before publication. Preserve accounting for
suppressed or uncertain outcomes.
- Add provider contracts, database race tests, channel outbox tests, UI
tests, Stories, and documentation. Keep decision-model execution
unchanged.

## Verification

- Local `pnpm -r typecheck`, `pnpm build`, and UI token gates pass after
rebase. The UI typecheck and build also pass after the final navigation
fixes. All 37 focused navigation tests pass. The first full local `pnpm
test:run` invocation reported seven chat failures. All seven passed in
isolated reruns after fixture updates and host cleanup. This invocation
was stopped after the full final-head CI suite passed.
- Initial local database tests hit macOS shared-memory exhaustion. A
diagnostic confirmed `shmget: No space left on device` with the system
limit of 32 IDs. Three abandoned, unattached PostgreSQL IPC headers were
reclaimed. The isolated rerun passed all 31 fast-response and channel
tests. The two additional Slack regression cases also passed in a
separate rerun. No active database was stopped.
- UI token gates pass. Provider contract tests cover all supported API
connection types. Fast-response tests cover uniqueness, authorization,
budgets, expiry, cancellation, restart, real-reply races, and the
experimental flag.
- The test drive verified that disabling the experiment hides
Connections and redirects its direct URL. Enabling it restores the saved
OpenRouter setup. After restart on the final commit, the OpenRouter
settings sample returned “Sure, I’ll take care of fixing the border
around the settings panel.” in 428 ms. It used 260 input and 31 output
tokens and recorded $0.0000576 in reported cost.
- Earlier live OpenRouter verification produced a receipt in 1.013
seconds for a new task. The real agent then supplied its substantive
answer. A separate settings test took 1.34 seconds, used 246 input and
23 output tokens, and recorded $0.0000361.
- Live Slack verification on final commit
`24016fa525961ebc7a4acffdc5019ce123970549` passed for a fresh initial
task and a follow-up in a disposable bot DM. The initial acknowledgement
appeared 2.405 seconds after the Slack message and 1.413 seconds after
durable acceptance; the follow-up appeared in 2.322 seconds and 1.160
seconds respectively. The recorded generation/settlement durations were
898 ms and 697 ms. The initial call used 291 input / 39 output tokens
and cost $0.00006705; the follow-up used 281 / 47 and cost $0.00007035.
- The Slack acknowledgements retained the assigned agent identity and
`origin: fast_response`, source-turn references, and no fabricated run
ID. A status snapshot after the initial receipt showed the task still
`in_progress` with its real execution run. The agent then read the CSV
fixture and correctly reported two ready items, two blockers, and their
owners. Both replies stayed in the originating DM, and no duplicate
generic working message followed the delivered contextual
acknowledgements. The `new` command started a fresh conversation session
without generating a fast response.
- An earlier eligible Slack turn generated a response but suppressed
publication. Its reported cost was retained, generic progress remained,
and the real agent completed the requested result. This is recorded
separately from the two delivered acknowledgements; the history did not
expose a precise suppression reason.
- Slack verification used real signed provider events, the existing
intake/publication paths, and a user-approved temporary webhook-only
HTTPS tunnel. Subscriptions were limited to direct-message events. The
test bot was paused and the tunnel stopped afterward. Live
channel/group-thread and GitHub journeys remain unverified; their mocked
tests cover intake, destination binding, publication, and continued
underlying work.
- Greptile reviewed final commit
`24016fa525961ebc7a4acffdc5019ce123970549` at 5/5. Both new review
findings are fixed and all threads are resolved. [Final-head CI
run](https://github.com/paperclipai/paperclip/actions/runs/38060015730)
passed. This includes grouped server/chat/workspace tests, serialized
suites, runner checks, browser journeys, typecheck, build, release
registry, policy, and the canary dry run.
- Manual check: enable Experimental fast responses. Open company
Settings → Connections. Select a shared API connection and model. Save
and use Test response. Submit a task message and confirm that its
receipt does not complete the task.

## Risks

- The migration adds receipt and configuration tables plus accounting
references. It uses idempotent statements and follows the current
migration sequence.
- A slow provider, authorization change, or real-agent reply can
suppress an acknowledgement. Existing progress feedback remains
available.
- Unknown provider billing retains a reservation until reconciliation.
External deliveries with uncertain outcomes must not be blindly
repeated.
- Live Slack initial-task and follow-up DM journeys pass. Live Slack
channel/group-thread and GitHub journeys remain unverified. The PR
remains a draft and records these gaps. Final-head repository CI and
automated review pass.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, repository tools, code
execution, and browser interaction. The runtime does not expose an exact
model snapshot ID or context-window size; these are not asserted.

## 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-10 13:09:17 -05:00
DottaandPaperclip 19e9facc5b perf(chat): wake delivery queues from commits and deadlines (#15802)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Chat connectors store incoming messages and outgoing publications in
durable queues.
> - The app checks these queues every second, even when they are empty.
> - Empty scans keep the database active and hinder instance sleep.
> - This pull request wakes three queue workers after transactions
settle and at known retry or recovery deadlines.
> - Empty queues stop scheduling checks. Existing rows retain their
ordering, retry, and recovery rules.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Idle database activity from incoming chat deliveries, outgoing
publications, and Slack upload receipt recovery.

**Subsystem affected**

`server/` — chat delivery and background work scheduling.

**Current behavior**

The app's one-second chat timer scans the three queues together with
provider and action maintenance. Empty queues continue to query
PostgreSQL.

**Proposed behavior**

Register three independent workers with the existing delivery
coordinator. Producers announce work inside their database transactions.
Commit wakes the corresponding worker. Startup restores outstanding
work; persisted retries and stale claims supply the next deadline. An
empty queue has no recurring scan.

**Reason and benefit**

Remove avoidable queue reads without requiring a new table, an external
scheduler, or a persistent PostgreSQL listener.

**Breaking changes**

No schema or public API change. Work notifications are scoped to the
app's shared database owner. Direct SQL and independent database roots
do not send these notifications; startup still scans durable work.

**Additional context**

Refs #15625, #15750, and #15757 for the preceding sleep work. Related:
#15153 reduces chat query allocation while retaining polling. This PR
changes when the three queue workers run; it does not add query caching.

## What Changed

- Register independent incoming delivery, publication, and Slack receipt
workers on the shared scheduler.
- Add transaction work signals at queue writes, endpoint state changes,
and action transitions that unblock publications. Keep provider I/O
outside the added transactions.
- Restore deadlines from durable retry times, abandoned claims, and
publication lease expiry. Share publication and receipt eligibility
rules between dispatch and deadline queries. Respect incoming
conversation order and lifecycle dependencies.
- Exclude publications blocked by unresolved Slack tool sends. Wake them
when the tool receipt changes.
- Wake publication dispatch when an endpoint send finishes so free slots
refill. Preserve per-endpoint concurrency, conversation order, and
conservative handling of uncertain sends.
- Wake receipt recovery when a stale publication becomes
`delivery_unknown`, and wake queues when a provider connection recovers.
- Commit interaction publications with their callback tokens so dispatch
cannot see an incomplete card.
- Keep provider runtimes, other action outboxes, milestone and
wake-notice projection, Teams protocol maintenance, GitHub recovery, and
Slack session synchronization on their existing maintenance cadence.
This PR does not make the whole chat subsystem idle.
- Update the instance sleep inventory with this partial migration and
its remaining work.

## Verification

- All 1,225 PostgreSQL chat integration cases passed across the three
hosted chat shards. These cover retry and lease deadlines, blocked
followers, unresolved Slack sends, provider recovery, commit visibility,
and endpoint slot refill.
- 32 local app-wiring, queue-worker, and delivery-coordinator tests
passed after updating the wiring assertion for event-driven dispatch.
Fake timers cover an hour of idle time without queue scans.
- Related publication, interaction, receipt, and wake-notice tests
passed locally. The 13 interaction integration cases include atomic
rollback when callback-token storage fails.
- Local `pnpm typecheck`, `pnpm build`, and final server typecheck
passed. All hosted checks passed on the final commit, including build,
typecheck, test shards, release registry checks, and the canary dry run
(54 successful checks; two Storybook checks skipped for this backend
change).
- The last local PostgreSQL rerun was skipped because the embedded
database startup probe failed after five attempts. The hosted PostgreSQL
cases ran successfully; their skips only partition cases across shards.
- `git diff --check` passed. Greptile is 5/5 on the final commit with no
unresolved findings.

## Risks

- Every producer that makes queued work eligible must send a transaction
work signal. The tests exercise commit visibility, concurrent wakes,
worker failures, and restart recovery. Future direct SQL writers must
participate in this contract.
- The existing database work-signal layer reconciles uncertain
transaction outcomes. This PR adds no work-signal table or schema
migration.
- Provider and action maintenance still polls. Further changes are
required before an idle chat-enabled instance can sleep.
- Tests use real PostgreSQL with simulated provider responses; they do
not send messages through live provider accounts.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, tool use, and code
execution. 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
- [ ] 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-10 09:13:44 -05:00
DottaandPaperclip 35511366dc perf(server): schedule browser cleanup only while sessions exist (#15757)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Hosted instances must become quiet when they have no work.
> - Browser Use queried its session table every three seconds, even when
empty.
> - Existing session rows already store the next poll time and cleanup
lease.
> - This pull request uses commit signals and those saved deadlines to
schedule cleanup.
> - Empty instances stop browser database scans. Live sessions keep
their safety checks.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Browser Use background cleanup and recovery. This follows #15625 and
#15750.

**Subsystem affected**

server/ — REST API and orchestration services.

**Current behavior**

The app scans for due sessions every three seconds, including instances
that have never used Browser Use.

**Proposed behavior**

Scan once at startup. Wake after session creation, provider-create
settlement, stop requests, or credential-removal cleanup. Schedule the
next check from `nextPollAt` and `leaseUntil`. Stop scheduling when all
sessions are terminal.

**Reason and benefit**

Remove empty database traffic so idle stacks and their database
connections can become quiet.

**Breaking changes**

No API or schema change. Running and idle sessions retain provider
polling, cost accounting, access checks, expiry checks, and cleanup
retries.

## What Changed

- Register browser cleanup with the existing work coordinator. Remove
the app's three-second interval.
- Signal existing session transactions before writes. No new tables or
provider infrastructure.
- Restore deadlines from live sessions and their leases. Wake after a
credential-removal sweep releases its lease.
- Wait for concurrent cleanup instead of returning while it still runs.
Drain and standby preserve deferred work.
- Add scheduler tests and database-backed commit and restart coverage.
Update the connector guide and sleep inventory.

## Verification

- Passed: focused browser worker and shared coordinator tests, 17 tests.
- Passed: browser lifecycle, route-scope, viewport, and transport tests,
50 tests. The lifecycle suite uses real local Postgres and a simulated
provider.
- Passed: `pnpm -r typecheck` and `pnpm build`.
- Passed: all hosted test groups for this commit. Started full local
`pnpm test:run`, then stopped its duplicate serial run after the
complete hosted sharded suite passed; no full local-suite pass is
claimed.
- Passed: direct server TypeScript check. Greptile reviewed
`99c3217a2dea505be94081e729941e542e6fbc03` at 5/5 with no findings. All
54 applicable hosted checks passed; the two Storybook checks were
skipped because this PR has no UI changes.

## Risks

- Process-local signals cover services that share the instrumented
database owner. Independent processes and direct SQL writes require
startup recovery; this does not add cross-process notification.
- Live sessions still poll. This removes empty scans; it does not make a
running hosted browser sleep.
- Cleanup uses the existing provider protocol and lease fences. Tests
simulate the provider; no paid live browser was created for this
scheduling change.

## Model Used

OpenAI Codex, GPT-6, with reasoning, tool use, and code execution. The
exact deployment 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-10 07:06:31 -05:00
DottaandPaperclip ce024457f3 perf(server): wake email work from commits and deadlines (#15750)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Email connects agent tasks to durable incoming and outgoing
messages.
> - The email worker scans the database every second, even with no
inboxes.
> - These queries prevent idle database connections from closing.
> - This pull request wakes email work from committed writes and known
deadlines.
> - Empty email installations stop polling. Existing delivery and
catch-up guarantees remain.

## Linked Issues or Issue Description

Refs #15625. Related standby work: #15314.

**What existing behavior does this improve?**

The AgentMail background worker and its database traffic when idle.

**Current behavior**

Every instance queries active email endpoints each second. Active
inboxes also scan delivery queues each second even when no work is due.

**Proposed behavior**

Scan once at startup. Wake after endpoint and queue transactions settle.
Restore the next retry or catch-up deadline from existing rows. An
instance with no active inboxes has no recurring email queries or timer.

**Reason and benefit**

Thousands of empty instances should not keep their databases awake to
discover that email is still unconfigured.

## What Changed

- Use the shared transaction-aware coordinator for email work, including
changes made by separate service objects on the same database owner.
- Signal endpoint activation and control changes, incoming events, and
outgoing sends inside their transactions.
- Add an optional deadline to the coordinator. Email uses persisted
retry times, mailbox catch-up times, and socket reconnect times. Only
the first pending send in each conversation determines its send
deadline.
- Preserve startup recovery, lease fences, idempotency, standby
suppression, idle drain, and shutdown.
- Update the sleep inventory and add quiet-idle, commit, activation,
retry, and lifecycle tests.

## Verification

- Focused coordinator, email idle, and warm standby tests: 20 passed
locally.
- Database-backed email integration suite: 41 passed locally with real
PostgreSQL and a simulated AgentMail provider.
- After review, the updated delayed-reply regression passed separately:
1 selected test passed, 40 intentionally filtered out. It verifies no
scans before the first reply deadline and delivery of both replies in
order.
- `pnpm -r typecheck` and `pnpm build` passed locally.
- The local `pnpm test:run` was interrupted after its checkout was
removed externally. Completed local checks above are preserved. The
[final-commit hosted CI
matrix](https://github.com/paperclipai/paperclip/actions/runs/37997072212)
passed: 54 successful checks, 2 expected Storybook skips, no failures.
It includes the full general and serialized test matrix, runner checks,
browser tests, build, typecheck, and canary packaging.
- Greptile: 5/5 on `ec98f3b86ee476d75e0edf3366b357377ea09d69`, with no
actionable findings.

## Risks

- New email producers must register work inside their write transaction.
No new tables or migrations are required.
- Signals are local to one database owner and its dedicated pools.
Independent processes and direct SQL do not emit them. Startup recovers
existing work; configured inboxes also retain their recovery deadline.
- Active inboxes still catch up each minute. WebSocket mode still renews
its lease every 20 seconds. This change does not provide external waking
or make active sockets sleep.
- Provider calls were simulated in tests. No live email was sent.

## Model Used

OpenAI Codex, GPT-6, with reasoning, repository inspection, code
execution, and test tools. The exact deployment 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-09 17:19:41 -05: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