Commit Graph
1344 Commits
Author SHA1 Message Date
Naeem AkhtarandPaperclip 2a45fd95df Reduce newer-run history reads for attention (#15697)
Replace broad newer-run responses with bounded company-scoped existence probes. Read keyless history once per unique agent and preserve existing matching and millisecond rules. Keep all stored run data.

Verify with 31 real PostgreSQL attention tests, workspace typecheck, server build, response-budget counterfactuals, full green CI, and Apex 5/5 review.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 14:15:03 -07:00
Devin FoleyandPaperclip f0b597172b perf(skills): verify bundled releases with current inventory digests (#17652)
Use current scoped database inventory fingerprints and the current on-disk expectation before reusing bounded snapshot verification. Preserve full validation, seeding and access checks when content, expectation or row existence changes.

Verified177 skill and authorization tests, workspace typechecking, server build, all green CI and exact-head Apex5/5. Prior staging release measured and saved Legacy data fingerprints preserved.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 13:37:11 -07:00
Devin FoleyandPaperclip 67aa87a93b perf(recovery): skip unchanged retained workspace evidence writes (#17650)
Guard the retained-evidence update with its existing JSONB comparison. Reuse the full first result when evidence is unchanged, while preserving new evidence delivery, source-bound retention, cleanup, and recovery holds.

Verified with 199 recovery and retention tests, full workspace typechecking, the server build, green CI, and exact-head Apex5/5.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 12:58:50 -07:00
Devin FoleyandPaperclip 1949e726f8 perf(recovery): read only legacy terminal control fields under lock (#17646)
Select only runtime mode, status, complete result evidence and the delivery token from the locked legacy terminal-state read. Keep filters, row lock, guards, writes, full returned rows, source retention and recovery holds.

The synthetic regression reduces a 699,454-byte read to under 4 KB while preserving full stored and returned data. All CI checks, 236 combined tests, workspace typecheck and server build pass. Apex 5/5 after correcting a documentation contradiction, with no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 11:46:29 -07:00
Devin FoleyandPaperclip 82c998233c perf(recovery): omit unused launch payloads from reconciliation reads (#17645)
Project unused launch context and native execution input out of two preliminary recovery reads. Preserve full results, unknown controls, complete legacy profiles, current filters, native rejection and full locked terminalization.

Apex 5/5 with no unresolved comments and all CI checks green. Ten combined local suites pass (370 tests), plus workspace typecheck and server build. Hosted traffic effects require staging verification.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 11:06:22 -07:00
Devin FoleyandPaperclip 332e4c5df9 fix(native): settle completed results that retain a valid monitor (#17641)
Preserve a valid saved monitor after its successful source releases execution and checkout. Recheck accepted result identity, receipts, ownership and governance under the task lock. Notify the existing scheduler once and preserve accepted live and recovered replies.

Apex 5/5 after fixing its reply-presentation finding. All CI checks, 191 focused tests, 293 broader native tests, workspace typecheck and server build pass. Hosted effects require staging verification.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 10:47:02 -07:00
Devin FoleyandPaperclip 19ec787e5b perf(recovery): release native process identity without saved payload reads (#17622)
Keep locked process and terminal-state evidence plus JavaScript-compatible checkpoint presence. Preserve every host, provider, lease, coordinator and stop-receipt check without transferring saved prompts or checkpoint bodies.

53 focused tests, server typecheck, build, all 55 CI checks, and exact-head Apex 5/5 pass.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 10:06:27 -07:00
Devin FoleyandPaperclip b65897f9bf perf(recovery): read workspace contract without native launch prompts (#17621)
Project native input to the complete schema, binding, provider, and workspace evidence used by execution-blocker checks. Preserve absent and malformed fields and retain all saved inputs and recovery holds.

149 focused tests and 123 workspace-recovery tests pass. Server typecheck, build, all 55 CI checks, and exact-head Apex 5/5 pass.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 09:53:27 -07:00
Devin FoleyandPaperclip e302f2098f perf(wake-queue): omit unused native launch context from release reads (#17619)
Read native wake-release control context without saved launch payloads. Keep stored input, full legacy context, cancellation evidence, and release decisions. PostgreSQL response-budget regression, 785 lifecycle tests, typecheck, build, and all 55 CI checks pass. Apex reviewed the exact head at 5/5 with no findings.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 09:40:28 -07:00
Devin FoleyandPaperclip b792165ba4 Expire native runner continuity archives and quarantine (#17616)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Native runner sessions keep durable state under
`<instance>/runtime/paperclip-runner/durable-sessions/`
> - When a session starts a fresh provider session, the retired state is
moved into `<session>/continuity-breaks/<epoch>-<uuid>/`; when state
loses authority, it is moved into
`quarantine/<session>.<reason>.<epoch>.<pid>.<uuid>/`
> - Nothing ever removes either, and each can hold a full Codex home of
several gigabytes
> - A sleep-aware sweep already expires failover backup copies in the
same tree
> - This pull request extends that sweep to remove both kinds of
evidence after a retention window, while keeping anything recovery may
still read
> - The benefit is that native runner disk use stops growing without
bound

## Linked Issues or Issue Description

Bug report described in-PR (bug report template).

**What happened?**
On one instance, one day of native runs produced 20 continuity-break
archives totaling 4.8 GB, one session alone holding 4 GB. The quarantine
folder held 12 entries totaling 2.1 GB, the oldest a month old. Neither
is read again by normal operation (continuity-break archives are written
by `archiveContinuityState` and never read), and nothing deletes them.
They were removed by hand to free space and will regrow.

**Expected behavior**
Evidence older than a bounded window is removed automatically, as
failover backup copies already are, without removing anything a live
session or recovery path still needs.

**Steps to reproduce**
1. Run a native Codex session that hits a continuity break (fresh
provider session), or whose state is quarantined (for example
`identity_indeterminate`).
2. Inspect `durable-sessions/<session>/continuity-breaks/` and
`durable-sessions/quarantine/`.
3. Wait any length of time: the entries stay forever.

**Paperclip version or commit:** `master` at e335265568.
**Deployment mode:** any with native runner sessions.
**Agent adapter(s) involved:** native runner (Codex, and any provider
whose state lands in these folders).

## What Changed

-
`server/src/services/native-runtime/native-harness-backup-expiration.ts`:
the existing tick also retires evidence.
- **Continuity-break archives** (`<epoch ms>-<uuid>` under each
session): removed when older than the window. Age is the epoch in the
name, or the folder mtime when that epoch is in the future.
`archiveContinuityState` refuses to archive state with warm-transition
evidence, and no code path reads `continuity-breaks`.
- **Quarantine entries**
(`<session>.<identity_indeterminate|identity_mismatch>.<epoch
ms>.<pid>.<uuid>`): removed by the same age rule. Native cleanup source
archives (`<session>.identity_indeterminate.cleanup.<uuid>`) are
referenced from recovery history, have no epoch, and keep their original
inode's mtime, so they are always kept.
- Every entry is skipped while its session is executing, initializing,
warm, or closing in this process; this is checked again just before
removal. Symlinked entries and symlinked
`continuity-breaks`/`quarantine` folders are reported as `unsafe_path`
and never followed.
- Removal renames to `.expired-<uuid>` in the same folder and then
deletes; later sweeps finish interrupted deletions. An empty
`continuity-breaks` folder is removed.
- Archives, quarantine entries, and leftover deletions count toward
`maxEntries` and `maxRemovals`, and stop on `shouldContinue`. Paging
resumes inside a session's archives and then in quarantine, so a bounded
or dry-run batch always makes progress and never recounts a session's
backup.
- New result fields: `evidenceCandidates`, `evidenceCandidateBytes`,
`evidenceRemoved`, `evidenceReclaimedBytes`, and skip reasons
`recent_evidence` and `cleanup_archive`. The service also exposes
`noteEvidence(writtenAt)`.
- `server/src/services/native-runtime/native-runner-evidence-signal.ts`
(new): a process-local signal. `native-session-executor.ts` raises it
with the entry's epoch right after `archiveContinuityState` creates an
archive and after `quarantineRunnerdStateRoot` moves state into
quarantine; `server/src/index.ts` subscribes the sweep and unsubscribes
on shutdown.
- `server/src/config.ts`:
`PAPERCLIP_NATIVE_RUNNER_EVIDENCE_RETENTION_DAYS` (default `7`, minimum
`1`).
- `server/src/index.ts`: passes the window and logs a batch when it
found evidence candidates.
- `doc/recoverable-disk-cleanup.md`, `doc/DEVELOPING.md`: document the
variable and behavior.

**Sleep-aware background work**
(`rules/sleep-aware-background-work.mdc`):
- *Trigger:* each entry's own expiry deadline (its epoch plus the
window), plus a direct call from the executor when it writes new
evidence. No timer, interval, or polling loop is added; the pass runs
inside the existing sweep's tick and keeps its drain, warm-standby, and
shutdown handling.
- *Source of truth:* the filesystem entries themselves. After a restart
the first sweep reconstructs the next deadline from them.
- *Pending-work predicate / next deadline:* after a full pass the sweep
keeps the earliest due time of the entries it retained; ticks before
that time skip the evidence pass entirely (no readdir, no SQL). A busy
session, a failed removal, or a full batch makes it due on the next
sweep while that obligation remains.
- *Quiet condition:* no evidence, or only evidence not yet due. The pass
never queries the database, so retained evidence never causes recurring
SQL. Dry run reports each candidate once rather than every hour.

No code comments were added.

## Verification

- `pnpm exec vitest run src/__tests__/native-harness-backup-expiration`
(in `server/`, embedded Postgres): 52 passed. New cases:
- old archives and quarantine entries are removed, recent ones kept; the
empty `continuity-breaks` folder is removed;
- age comes from the name, with mtime used for a future epoch;
non-archive folders are ignored;
- a busy session keeps its evidence, including when it becomes busy
after the first check, and it is retried on the next tick;
- dry run reports candidates and bytes, changes nothing (including
leftovers), and reports each candidate once until new evidence comes
due;
- symlinked entries and symlinked evidence folders are skipped without
touching the target;
- an old quarantine entry is removed even when its session's issue is
still open; cleanup source archives are kept;
- the pass stays quiet until retained evidence is due, and a
`noteEvidence` signal schedules newly written evidence;
- inspected archives and leftovers count against `maxEntries`, and
leftover removal stops on `shouldContinue` and resumes next tick;
- a due pass stopped by drain before any session stays pending;
one-entry batches finish without recounting a resumed session;
- interrupted `.expired-*` deletions are finished; paging across ticks
with `maxRemovals`, in normal and dry run, with no recount of the backup
when resuming.
- `native-session-executor.test.ts`: quarantining a legacy root raises
the evidence signal with the epoch in the entry name.
- `native-harness-backup-expiration-config`: default, explicit, minimum,
and fallback parsing.
- **Mutation checks**, each failing at least one test: removing the age
check, the future-epoch fallback, either busy check, the busy retry
deadline, the recent-entry deadline, the due gate, the `noteEvidence`
update, merging the pass deadline, the entry and both folder symlink
checks, the dry-run return, the dry-run leftover guard, leftover
removal, the leftover budget check and its retry deadline, counting
archives against `maxEntries`, the cleanup-archive skip, the
empty-folder removal, the resume cursor, the per-archive budget check,
the resume skip of the backup pass, not charging a resumed session,
keeping the pass active across drain, and the executor's quarantine
signal.
- `pnpm exec vitest run config native-harness
native-workspace-sync-retention native-session-executor server-startup`
(in `server/`): 1272 passed on the previous commit; `config
native-harness native-workspace-sync-retention server-startup`: 450
passed on the head commit.
- `pnpm run typecheck` (in `server/`): pass.
- Not run: server build and boot smoke. The continuity-archive signal
call is exercised only by a runner integration test that needs the
runnerd binary.

## Risks

- **Quarantine as a fail-closed fence:** recovery
(`recoverQuiescentRunnerdState`) and failed-run retry
(`nativeFailedRunRetryStateIsSafe`) treat a quarantined entry for a
session as a reason not to bootstrap fresh. Once such an entry ages out,
a later run of that session can start a fresh provider session instead
of failing with `quarantine_owner_unverified`. This applies only after
the window, matching what backup expiry already accepts for closed work.
- **Signal coverage:** the signal is process-local. Evidence written by
another process sharing the same state directory is picked up when the
next retained deadline is due or after a restart, not immediately.
- **Legacy-path quarantine:** entries quarantined from legacy roots
carry a legacy digest, so the in-process busy check cannot match them;
they are removed by age only.
- **Remote sandboxes:** archives moved aside on a remote runner
(`<root>.continuity-break-<token>`) are outside this sweep.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), 1M context, through Claude Code
with tool use (shell, file edits, 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-25 09:20:30 -07:00
Devin FoleyandPaperclip 7ebf98eafd fix(recovery): avoid duplicate terminal run status notifications (#17614)
Retain pending status deliveries when Native or legacy terminal recovery projects the same state. Preserve completion time on unchanged Native terminal status. Real state, metadata, error, explicit patch, and retained-file changes still notify. The publisher acknowledgement, accepted results, retry deadlines, recovery holds, ownership checks, and workspace cleanup remain intact.

Correct the queued Retry browser fixture race by releasing its old-process guard at the exact Retry request, requiring a successful response, and retaining fixture cleanup after failure.

Validated by 159 focused tests, 653 wider Native recovery tests, six real Chromium/PostgreSQL browser cases, five queued Retry repeats, build/typecheck, exact-head Apex 5/5, and green required CI. The original detached-button CI timeout is documented in the PR. The additional full local repository Vitest command is still running and is not claimed complete.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 08:22:53 -07:00
Devin FoleyandPaperclip e335265568 fix(ui): stop idle task run polling with live updates (#17608)
Stop successful empty assigned-task run queries from polling only while the matching company and session connection is open. Preserve active, pending, failed, and disconnected cadence. Share one lifecycle refresh owner between the task and chat views, discover reviewer events without task identity, and refresh after older in-flight reads settle.

All 175 focused tests, 9,079 UI tests, build, typecheck, exact-head Greptile 5/5, and required CI pass. Regression mutations catch reviewer discovery, duplicate reads, readiness, failure state, missing refreshes, and scope cleanup. Earlier E2E cleanup deadlock and server cleanup timeout remain documented; failed-only reruns passed on their same commits.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 07:54:47 -07:00
Devin FoleyandPaperclip bf9a8551c3 perf(issues): filter queued comment wakes by task (#17613)
Apply the existing typed task identity filter in PostgreSQL before returning queued-comment wake controls. Preserve queue selection, ordering, payloads, and current run ownership checks.

All 84 route tests, build, typecheck, exact-head Greptile 5/5, and required CI pass. The old source fails the new deferred and queued byte budgets; corrected reads preserve the complete response and all saved rows.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 07:51:28 -07:00
Devin FoleyandPaperclip a09ef344b3 perf(agents): avoid duplicate configuration reads (#17612)
Reuse one complete company roster for list responses and narrow other-agent eligibility checks to their five consumed fields. Preserve full selected-agent configuration, terminated-manager evidence, current spend, authorization, and fresh reads.

Verified with 170 affected tests, four rejected regression mutations, build and typecheck, exact-head Greptile 5/5, and green required CI.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 07:38:18 -07:00
DottaandPaperclip aec86c794b fix(runner): preserve native work through controller outages (#17594)
Keep remote native work under its authenticated connection lease through controller outages. Hold durable ACPX tools through reconnection, align the native Claude HTTP MCP client budget, and wait for the recovered handler before dispatch. Preserve Stop, cancellation, exact identity and artifact checks.

Verified with real-process regressions and a real Claude Opus 5.5 Daytona workflow: 125 seconds offline during useful work, original process and lease retained, no failed native tool calls, queued follow-up completed, independently verified ZIP and committed workspace receipts. All current-head CI gates pass and Greptile is 5/5 with all findings resolved.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 09:01:24 -05:00
Devin FoleyandPaperclip 3536b3649f Read only task creation provenance fields (#17604)
Narrow saved-run provenance reads to current identity, scope, and typed legacy bindings. Preserve binding precedence, current viewer checks, and complete stored runs. Real PostgreSQL and route tests pass 23 cases; legacy full-context and type-coercion regressions are caught. Exact-head required CI is green; Greptile is 5/5 with Low impact and no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 06:30:25 -07:00
Devin FoleyandPaperclip 6aa711f6c6 fix(ui): avoid broad task refreshes after known run progress (#17602)
Use the existing live-status cache patch result to skip redundant task and history reads. Keep unknown-run discovery, lifecycle, activity, visibility, and reconnect recovery.

Verified with full build and typecheck, 9,047 UI tests, final context tests and counterfactual broad-refetch and missing-fallback failures, complete exact-head CI, and Greptile 5/5 Medium with no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 06:04:09 -07:00
Devin FoleyandPaperclip 8c6bd5a83b perf(runner): skip wake payloads in ordinary authority reads (#17600)
Omit saved wake payloads from ordinary unlocked Native authority reads. Load the current full wake for mail restrictions. Preserve stored rows, locked mutation reads, and current authorization predicates.

Verified with full build and typecheck, 2,360 affected tests, real PostgreSQL byte and response checks, counterfactual performance and mail authorization failures, complete exact-head CI, and Greptile 5/5 Medium with no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 05:36:43 -07:00
Devin FoleyandPaperclip 2e47c42fad perf(runner): omit unused launch context from tool authority reads (#17591)
Omit nine unused named launch values from the unlocked Native authority query. Preserve complete wake payloads, other context, result receipts, stored input, and locked execution and mutation readers.

Verified with full build and typecheck, 2,359 affected tests across 20 suites, real PostgreSQL byte/retention/cancellation checks, a counterfactual, complete exact-head CI, and Greptile 5/5 Medium with no unresolved comments. An existing cleanup scheduling test passed on CI retry after its suite passed locally with both base and branch source.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 04:21:50 -07:00
Devin FoleyandPaperclip bd77ffe78d perf(connections): scope profile rule and binding reads (#17589)
Read current company/agent profile bindings and rules matching the exact catalog rows being evaluated. Preserve all five selector kinds, null application matches, exclusions, and the existing access evaluator without a cross-call cache.

Verified with full build and typecheck, 652 affected tests across ten suites, all 18 strengthened effective-assignment tests, exclusion mutation and byte counterfactuals, complete exact-head CI, and Greptile 5/5 Medium with no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 04:12:14 -07:00
Devin FoleyandPaperclip b7dc594bfa perf(recovery): narrow saved-message execution-wait reads (#17588)
Read current ownership, startup, cancellation, task-binding, and adapter evidence without transferring unused saved profiles, launch context, or output. Preserve normal continuation admission, recovery holds, and locked mutation reads.

Verified with full build and typecheck, 537 affected tests across 12 suites, a counterfactual real PostgreSQL byte regression, complete exact-head CI, and Greptile 5/5 Medium with no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 03:54:59 -07:00
Devin FoleyandPaperclip 35d5b0686a perf(runner): narrow current tool authority reads (#17587)
Select the current run fields used by Native tool authority without transferring unused saved profiles or output. Preserve full context, receipts, authorization predicates, and locked mutation reads.

Verified with full build and typecheck, 2,358 affected tests across 20 suites, a counterfactual real PostgreSQL byte regression, complete exact-head CI, and Greptile 5/5 Medium with no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 03:45:25 -07:00
Devin FoleyandPaperclip c4c4f7eecb perf(environments): reuse provider schemas after content validation (#17581)
Add a separate schema-only provider lookup with current plugin identity and SHA-256 content validation in one statement. Retain bounded schema definitions, return independent objects, and preserve current full driver and secret access paths.

Build, repository typecheck, 485 affected tests, the supported-host database tests, and the complete required CI matrix passed. Greptile scored this exact head 5/5 at medium impact with no unresolved findings.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 03:20:45 -07:00
Devin FoleyandPaperclip 3f36947e0c perf(realtime): batch task and run authorization per event (#17579)
Use one fresh authorization batch for each live event task and run binding. Preserve current membership, grant, company, and payload disclosure checks.

Build, typecheck, 70 affected local tests, and the complete required CI matrix passed. Greptile scored the exact head 5/5 at medium impact with no unresolved findings. The separate full local suite remains in progress.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 02:43:17 -07:00
Devin FoleyandPaperclip 50797ea03c perf(issues): narrow queued comment wake display reads (#17570)
Read only typed queue display inputs from saved wake payloads and only runtime mode from queued runs. Preserve complete rows for locked edits, discard, steering, and interruption.

Validation: 93 focused route and policy tests, full build and repository typecheck. PostgreSQL fixtures preserve identical queue responses and saved input with less than 4 KB of read values; restoring the full wake read fails at about 1.8 MB. Exact-head Greptile 5/5 (medium), all CI checks green, and zero unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 00:41:14 -07:00
Devin FoleyandPaperclip 7494b8dd4c perf(auth): batch current run history read decisions (#17569)
Reuse the existing authorization batch owner for current run-history read decisions after company-scoped existence checks. Preserve missing-task denial, per-request freshness, and the decide-only adapter path.

Validation: 97 focused tests, full build and repository typecheck. A real PostgreSQL fixture returns identical decisions with 4 queries instead of 327; the original path fails the query-bound regression. Exact-head Greptile 5/5 (medium), all CI checks green, and no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 00:35:07 -07:00
Devin FoleyandPaperclip 0d73505098 perf(git): narrow operation identity snapshot reads (#17567)
Select only the run context used by GitHub operation credential checks. Preserve the default complete identity capture for Muse and other consumers, current trust checks, grants, and saved run evidence.

Validation: 72 focused GitHub, identity, Slack, chat-project, and project-route tests; full build and typecheck; exact-head Greptile 5/5 (medium), all CI checks green, and zero unresolved comments. Real PostgreSQL tests prove current revocation and response-size bounds. No live external GitHub push was performed.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 00:31:41 -07:00
Devin FoleyandPaperclip f978a521ff perf(heartbeat): narrow workspace operation access reads (#17565)
Read only the current run access fields and typed workspace binding for workspace-operation polling. Keep the current privacy decisions, operation checks, and complete run storage.

Verified focused run-state, live-event, and real database privacy-route tests, full build and typecheck, all CI checks, and exact-head Greptile 5/5 with no unresolved threads.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 00:21:15 -07:00
Devin FoleyandPaperclip efa2724f9a fix(db): pace bounded response capture output (#17564)
Close database response measurement at its existing deadline, then emit the bounded metadata at 64 records per second. Preserve group indices, counts, privacy, and query/transaction outcomes. Stop the unreferenced timer when the output queue is empty.

Validation: 32 capture and real PostgreSQL tests, full build and typecheck, exact-head Greptile 5/5 (medium), full green CI, and no unresolved comments. A 2,200-query fixture reconciles all 2,048 groups and its summary; dedicated-pool coverage awaits deferred output.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 00:11:41 -07:00
Devin FoleyandPaperclip bf142db65a perf(server): narrow external review wait context reads (#17562)
Project current chat authorization fields for external review-wait discovery instead of saved execution input. Preserve typed source bindings, nested question receipts, candidate eligibility, live revocation checks, and stored context.

Validation: 181 native chat integration tests, full build and typecheck, exact-head Greptile 5/5 (medium), full green CI, and no unresolved review threads. The full-context counterfactual fails both direct-chat size regressions.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-25 00:02:32 -07:00
Devin FoleyandPaperclip 1ad8f25c77 perf(heartbeat): narrow queued comment polling run reads (#17560)
Read current control facts and typed issue/adapter bindings for queued-comment polling. Keep full mutation and execution reads, native preparation and steering, authorization, and stored evidence.

Validation: full build and typecheck, 160 focused tests plus two assembled route regressions; exact-head Greptile 5/5 (medium), full green CI, zero unresolved threads. Old-route counterfactual transfers 2.5 MB versus less than 2 KB.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 23:53:39 -07:00
DottaandPaperclip 8415f4a679 fix(github): drain queued pings during active App setup (#17551)
## Thinking Path

> - Paperclip connects external work to an agent and its ordinary tasks.
> - A dedicated GitHub App must prove signed webhook delivery during
setup.
> - Cloud queues the original bytes until the tenant can authenticate
them.
> - An older personal connection can leave a long empty-queue backoff in
that tenant.
> - Active App setup can then time out before the queued ping is read.
> - This change lets active setup request the existing event drain with
a short rate limit.
> - The same webhook handler still owns signature verification and setup
state.

## Linked Issues or Issue Description

Refs: #17457, #17513.

**What happened?**

Cloud accepted a new App's ping, but the tenant read it only after the
setup wait timed out and cleanup archived the draft. An existing
personal GitHub connection had left an empty-queue backoff. The poller
checked that deadline before it read the new App state.

**Expected behavior**

An active manager setup request must be able to read the queued ping
promptly. It must not bypass signature checks or create another App.

**Steps to reproduce**

1. Let a personal GitHub connection build its empty-queue backoff.
2. Start a dedicated App and complete its installation.
3. Return to the waiting setup screen while the signed ping is queued.

**Paperclip version or commit**

This behavior is present on the base commit of this PR.

## What Changed

- Let the authenticated wizard request a queue drain only while its
Cloud App still lacks signed-delivery verification. Existing manager,
credential, and installation checks run first.
- Register a narrow callback to the server-owned GitHub poller. Keep
provider handling in the GitHub services. Remove the callback on server
close.
- Share one in-flight attempt between setup and background requests.
Limit setup attempts to one per five seconds, including after errors.
- Retain the existing passive backoff, sealed queue, acknowledgement
rules, and canonical webhook signature handler.
- Add regressions for the old backoff, concurrent requests, failed
requests, and wizard signature verification. Update the setup
documentation and map.

## Verification

- Observed the backoff regression fail against the old service: active
setup made no additional Cloud lease request. It passes with this fix.
- Three focused event-poller tests pass. They use the real
admission/backoff code with stubbed SQL and Cloud boundaries.
- All 39 startup tests pass. Server TypeScript, map checks, and diff
checks pass.
- The Postgres integration suites could not start locally because this
host exhausted its semaphore limit. They remain required CI checks. This
is not a passing local integration result.
- Full current-head CI passes, including the Postgres and chat
integration suites. Greptile scored this exact head 5/5 with no
unresolved findings.
- The additional Paperclip review check ended neutral because its
execution did not complete. It did not publish a validated assessment.
- A fresh deployed setup attempt remains required before live
qualification.

## Risks

A waiting setup screen now makes a bounded readiness request to Cloud.
The existing connector request timeout still applies. Concurrent calls
share the same drain, and failures release it for a later retry. The
request stops after persisted verification or when the browser stops
advancing setup. No timer, durable queue, fleet setting, or sleep
blocker is added or removed. The existing background reconciliation path
still has its existing sleep restrictions. Live setup-to-review behavior
is not established by these local tests.

## Model Used

OpenAI Codex, based on GPT-6. The exact runtime model ID and
context-window size are not exposed in this session. Used for code
execution and regression analysis.

## 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 <paperclip@paperclip.ing>
2026-10-24 21:58:43 -07:00
DottaandPaperclip 2a9d527c68 fix: preserve native startup during controller restart (#17511)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The native runtime keeps a durable runner alive across controller
restarts.
> - A restart can begin while a provider session is still opening.
> - The old controller fenced read-only run admission before it could
publish that prepared session.
> - Admission failure then closed the runner that shutdown needed to
detach.
> - This pull request lets only the existing startup finish admission
within the bounded shutdown wait.
> - The controller detaches that exact session before it can submit a
turn. New work and state changes stay fenced.

## Linked Issues or Issue Description

**What happened?**

A controller restart during native provider startup could close the
prepared runner with `native control-plane run admission failed`.
Recovery then found no running process and placed the task in a
reconciliation hold. The same admission path follows both fresh startup
and replacement after a failed provider resume.

**Expected behavior**

Shutdown must detach the existing prepared session so the next
controller can recover it through the normal identity checks. Shutdown
must not admit a new executor, submit a turn, or override an explicit
Stop.

**Steps to reproduce**

1. Delay a native backend while it opens its provider session.
2. Request controller restart detachment for that run.
3. Let backend startup finish before the 30-second deadline.
4. Observe that read-only admission fails and shutdown reports the run
as inactive instead of detached.

**Additional context**

Related work: #17073 handles remote process-monitor classification;
#17459 handles macOS process fingerprints; #17097 covers broader hot
upgrade recovery. This change fixes the startup admission fence. It does
not relax process identity or provider launch-profile checks.

## What Changed

- Separate the read-only admission guard from the guard on control-plane
state changes.
- Let shutdown await only the startup already registered for the run.
Close its admission window when the wait ends.
- Reject a new execution after its run is fenced for shutdown.
- Detach the prepared session and exit before runtime-question
attachment or provider turn submission.
- If Stop is pending before or after publication, retain its exact
cancellation acknowledgment and cleanup owner across authority reads and
runtime-question attachment.
- Share one 30-second admission/Stop-settlement deadline across the
restart sweep. Report an unsettled run as pending while healthy runs
continue hot-restart adoption.
- Cover the real runtime and admission path, deadline expiry, new-start
rejection, persisted binding checks, and write fences.
- Document the bounded startup handoff in the restart ownership
contract.

## Verification

- Red: the new actual-runtime/admission regression failed on the
previous code. Shutdown reported the run as inactive and did not detach
it.
- Red: review regressions reproduced detach before a post-publication
Stop acknowledgment and an actual hot-restart preparation error when
startup Stop cleanup timed out.
- Green on `e10d0cb238`: 18 startup/restart tests passed, including Stop
during authority reads and runtime-question attachment, the real
recovery preparation and shutdown coordinator with a stalled Stop plus
healthy peer, and the shared sweep deadline. The earlier 36-case
Stop/restart selection also passed before this follow-up.
- Both complete changed test files previously passed on head
`8e9c2610fe`: 857 tests. They used real PostgreSQL 18 with a fresh
database per fixture. An earlier local harness attempt reused one
database and hit duplicate fixture keys; that harness was corrected
before this complete pass.
- Real PostgreSQL: 7 admission and revocation tests passed. These cover
wrong runner identity, persisted binding mismatch, checkpoint/result
rejection, and in-flight write revocation. The local test harness used a
fresh Docker PostgreSQL 18 instance because macOS embedded Postgres
exhausted the host semaphore limit. Only database provisioning was
replaced; assertions and production port code were unchanged.
- Transport safety acceptance: the current source transport controlled a
real Linux ARM runner and a deterministic Codex test provider in a
disposable Debian container. After startup, detach and generic cleanup
preserved the process. A new controller authenticated the same PID and
provider session. The provider log had exactly one session start and no
turn start. Duplicate launch was forbidden, the retained workspace was
unchanged, and no stop or suspend command was issued.
- The Linux acceptance check tests the shared native transport safety
contract. It is not a live Claude response, a hosted Daytona run, or
recovery of the original task. Those remain unverified. No original
task, sandbox, credentials, or permissions were changed.
- `pnpm -r typecheck` and `pnpm build` passed before the review
follow-up. Server typecheck passed on current head `e10d0cb238`;
current-head CI also passed the full typecheck and build gates.
- The full local `pnpm test:run` command was attempted, then stopped
after repeated embedded PostgreSQL bootstrap setup failures. That broad
local run is incomplete. The current-head four-file Docker PostgreSQL
run passed 879 tests, with one existing warm-copy `claim-uncertain` case
timing out and recovery failing to import because the temporary
provisioning mock omitted a support export. The harness is corrected and
affected cases are being rechecked. The timeout is unexplained; it is
not being labeled an infrastructure failure. No assertions are skipped
by the database provisioning replacement.
- All 55 clean-host CI checks passed on current head `e10d0cb238` (2
conditional skips), including all general test shards and native runner
verification. Greptile reviewed that exact head at 5/5 with no findings.
The previous Paperclip review findings are addressed in [the review
response](https://github.com/paperclipai/paperclip/pull/17511#issuecomment-6391520242);
its new advisory review remains queued.

## Risks

- Admission must stay read-only. All writes retain the original
synchronous controller fence, and both admission checks still validate
the exact persisted binding.
- Startup that exceeds the 30-second shutdown wait still fails closed.
It cannot claim a successful detach or start a turn.
- No schema migration or provider configuration change is included.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, repository tools, code
execution, and test execution. The exact runtime 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-24 21:05:45 -07:00
Devin FoleyandPaperclip 8a85d9d647 perf(heartbeat): coalesce repeated native stranded releases (#17547)
Skip repeated successful native release checks for the same unchanged source within one recovery pass. Preserve every saved request, failure retry, cursor update, source change, legacy path, and current admission gate.

Verified with 201 focused tests, 32 full-control queue cases, build and typecheck. Exact-head CI is green, Greptile is 5/5 with Low impact, and no review threads remain unresolved.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 21:04:36 -07:00
Devin FoleyandPaperclip c8a6b4561b perf(heartbeat): narrow native Stop evidence rechecks (#17544)
Read current native Stop identity, acknowledgement, and process evidence without saved execution inputs, profiles, excerpts, or unrelated result data. Preserve the complete nested acknowledgement and every existing lease, remote-termination, process, ownership, and admission check. The physical-stop helper body is unchanged.

Verified with 101 tests across the affected suites, the full build, and typecheck. The review fix passed 62 focused tests. Large valid saved inputs stay below 2 KB at the raw driver boundary; large malformed string and array results stay below 1 KB and remain stored. CI is green and exact-head Greptile is 5/5 at Medium impact with no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 20:18:23 -07:00
Devin FoleyandPaperclip 2f637fe8c4 perf(tools): scope connection readiness assignment reads (#17541)
Connection readiness reads current permitted connection identities without unrelated catalog rows or complete profile-rule payloads. Reuse profile precedence and selector logic, preserve full public/runtime summaries, and keep installed connections, identity checks, and gateway authorization current.

Verified with 106 focused tests, the full build, and typecheck. The direct catalog inventory regression fails at 102 rows when the scope filter is removed and passes at two rows with the filter. One unrelated CI timing assertion differed by 1 ms; the exact local test and the single failed shard rerun passed on unchanged source. CI is green and exact-head Greptile is 5/5 at Medium impact with no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 20:05:45 -07:00
Devin FoleyandPaperclip 80c1261417 perf(recovery): decline native legacy checks before bulk reads (#17540)
Decline native legacy-continuation checks before loading saved execution input. Preserve exact nonblank task bindings, skip reasons, complete legacy recovery inputs, company scope, and retry accounting.

Verified with 105 recovery tests, the full build, and typecheck; the binding review fix passed 73 affected tests and server typecheck. CI is green and exact-head Greptile is 5/5 at Medium impact with no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 19:50:19 -07:00
Devin FoleyandPaperclip a12329b8a9 fix(db): complete bounded response attribution (#17537)
Raise bounded database-response attribution to 2048 groups and add numbered 128-entry log pages. Preserve the overflow bucket and exact completed-call and byte totals. Document complete page retrieval and validation.

Verified with 30 focused tests, the full build, and typecheck. All CI checks pass. Exact-head Greptile is 5/5 at Medium impact with no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 19:34:31 -07:00
Devin FoleyandPaperclip e0855fe92a perf(wake-queue): narrow native release reads (#17539)
Native wake-queue release reads omit saved profiles and excerpts. Legacy release reads keep their complete inputs. Ownership, cleanup, and file retention checks remain unchanged.

Verified with 275 wake-queue tests, the full build, and typecheck. All CI checks pass. Greptile scored the exact head 5/5 with Medium impact and no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 19:32:51 -07:00
Devin FoleyandPaperclip ef0a8db502 perf(native): narrow reconciliation candidate payloads (#17531)
Select the complete workspace-sync record and native status wake identifier for reconciliation candidates. Preserve candidate policy, JSON types, full later evidence, and stored data. The committed replay regression includes the real reconciliation scan. Full build, typecheck, 114 focused tests, complete CI, and exact-head Greptile5/5 pass.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 19:01:23 -07:00
Devin FoleyandPaperclip 39e5c8c1c3 perf(server): narrow committed chat authorization reads (#17530)
Select current chat authorization fields for committed responses under the existing scoped share lock. Preserve complete evidence for actual retries, source revocation, JSON types, and saved run data. Full build, typecheck, 113 focused tests, complete CI, and exact-head Greptile5/5 pass.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 19:01:19 -07:00
e82d86bfeb fix(server): gate periodic heartbeat recovery to one chain in flight (#11699)
Bind periodic recovery admission to lifecycle tracking. Keep one physical pass active through cleanup, report skipped ticks and long-held ownership, and preserve direct scheduled delivery. Full build, typecheck, 32 focused tests, complete CI, and exact-head Greptile5/5 pass.

Co-Authored-By: Senior Engineer Fable <dotta@paperclip.ing>
Co-Authored-By: Dotta <bippadotta@protonmail.com>
Co-Authored-By: Devin Foley <devin@paperclip.ing>
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 19:01:15 -07:00
Devin FoleyandPaperclip 64a8b927c1 fix(native): narrow committed finalization and cleanup reads (#17525)
Select fresh metadata for committed finalization and cleanup. Preserve full saved evidence for new finalization, cancelled workspace retention, and legacy Stop. PostgreSQL byte-budget and unchanged-row regressions pass, full CI is green, and Greptile scored the exact head 5/5.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 17:48:47 -07:00
Devin FoleyandPaperclip 71f06d84dc fix(db): keep bounded response captures attributable (#17522)
Keep more caller groups in one bounded database response capture and verify public caller paths in the production bundle. All local checks and CI passed; Greptile scored the exact head 5/5 with no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 17:34:41 -07:00
DottaandPaperclip 5c3dce09b5 fix(runner): preserve verified remote artifacts across controller releases (#17512)
## Thinking Path

> - Paperclip manages AI agents and owns their task authority.
> - Remote native agents can keep working while their controller
restarts.
> - The controller must authenticate the same runner and verify its
provider files before recovery.
> - The current path compares a retained provider pack with the new
controller release. A release change can reject intact files.
> - The new controller binary digest can also reject the original daemon
during PRP authentication.
> - This PR records the admitted release authority in private controller
state and uses it for exact process reattachment.

## Linked Issues or Issue Description

**What happened?**

A remote Claude session started and used tools. After controller
shutdown, recovery failed with
`runner_remote_provider_artifact_incompatible`. The transport combined
stderr into stdout, so the error omitted the verifier reason. The
historical rejected manifest is unavailable. This PR reproduces
controller release drift as a cause; it does not claim that the
historical mismatch is proven. Reaching pack verification implies the
earlier exact-process/controller-identity checks passed. That supports a
preserved-state recovery path but is not a direct inspection of the
deployed volume.

**Expected behavior**

A compatible, unchanged remote runner can reattach after a controller
release changes. Full package hashes, exact process scope, protocol
capabilities, and authenticated runner identity must still pass. Fresh
launches use the current release.

**Steps to reproduce**

1. Verify and admit a remote native session with release A.
2. Preserve its runner process and replace the controller with release
B.
3. Reattach the original process. The old path compares its pack with B
and fails at `manifest mismatch`.
4. With this fix, the controller reads its saved A authority and
verifies all A hashes. Changed files still fail.

**Paperclip version or commit**

Reproduced from master `470cb34c0e42cb9d0b232d0bcf5d45cc7b0a81ca`;
rebased onto `a3675ddcdf`.

**Deployment mode**

Native remote runner on a Daytona-backed Paperclip Computer. Local
acceptance uses isolated Linux containers and actual packaged artifacts.

Related work: #17361 and #17448 handle runner upgrade and qualification
permissions. #17097 and #17439 add durable controller checkpoints and
server adoption. That stack can skip remote artifact preparation with a
saved checkpoint. This PR fixes the current file-backed path and retains
full provider-pack verification; it has no checkpoint schema or
empty-disk controller recovery.

## What Changed

- Save the complete verified pack manifest and authenticated runner
version/digest after successful session admission.
- Bind the receipt to company, agent, workspace, session, runner,
environment lease, remote directory, and provider descriptor. Keep it
outside guest backup directories.
- Reuse the saved authority for exact reattachment. Keep current release
selection for fresh launches and explicit session replacement.
- Fail closed for mismatched or corrupt receipts. Historical sessions
without a receipt retain the current release integrity gate.
- Report bounded, redacted verifier output from both transport streams.
Warn on receipt write failure while returning the already admitted
session to its owner.
- Add scope, persistence, replacement, storage failure, and backend
wiring regressions. Update recovery documentation and the feature map.

## Verification

- Test-first red: the original selection chose release B instead of A
after controller recreation. The new receipt regression passes. A
separate red test showed the missing stdout diagnostic; it passes after
the diagnostic fix.
- Authority and diagnostic suites: 49 tests pass. Backend pack, process
adoption, and harness persistence selection: 47 tests pass.
- Actual Linux provider image: complete A manifest/artifact verification
passed; controller source-revision drift failed on the old path; saved A
authority passed; a modified ACPX sidecar failed its SHA-256 check.
- Actual native Linux runner: authenticated a session, detached the
controller, changed the controller manifest and binary, then
authenticated the original PID and provider session with saved
authority. There were zero duplicate spawns, zero new provider turns, no
stop/suspend, and the workspace file remained. This probe used the real
runner with a deterministic Codex fixture; it is separate from the real
ACPX package integrity proof.
- Full workspace typecheck and build passed. The full native executor
suite first hit host PostgreSQL semaphore exhaustion. The complete
changed files then passed 864/864 tests with separate real PostgreSQL
databases in a disposable Linux container. The temporary local fixture
changed only database provisioning and used normal repository
migrations. Current-head CI also passed the full required test, Runner,
browser, typecheck, and build lanes.
- `node scripts/check-maps.mjs` and `git diff --check` passed. These
structural checks do not prove live recovery.
- Current head `48fca2ae33d99f7f6d2fe12f431cf76d25185c70`: 55 successful
checks, two conditional skips, no pending or failed checks; fresh
Greptile 5/5 with zero findings and zero unresolved review threads.
Commitperclip quality review passed.
- No original task was retried or changed. Existing Daytona access could
not resolve the original sandbox or its image; a fresh staging Claude
recovery is not claimed.

## Risks

This prevents recurrence for sessions admitted with the new receipt. It
does not retroactively recover the historical failed run. Saved
authority exists only after this change has admitted a session. It
cannot repair an ambiguous historical launch or restore a receipt on an
empty replacement controller. A stale protocol or modified pack remains
a hard failure. Receipt write errors warn without losing the live
session handle, but later recovery still needs matching authority. The
broader checkpoint stack changes the same server preparation and
runner-artifact option and will require a deliberate integration.

## Model Used

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

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [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 16:44:50 -07:00
DottaandPaperclip d0c8f0b090 fix: explain project access denials and add an authorized repair path (#17509)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Low-trust agents may read only the projects in their allowed scope.
> - Workspace setup correctly stops a task outside that scope.
> - The task previously showed a generic failure, and agent settings did
not offer an additive repair.
> - This pull request explains the denial and lets authorized operators
add projects while preserving other restrictions.
> - The saved task then offers an explicit retry. Saving access does not
start work.

## Linked Issues or Issue Description

**What happened?**
A project access denial appeared as an ordinary failed attempt.
Operators could not add a second allowed project from agent settings.

**Expected behavior**
The task should explain the denied access and link to a working
permission editor. The editor should preserve existing scope and
restrictions, enforce operator authority, and retain drafts after a
failed save.

**Steps to reproduce**
1. Assign a low-trust agent to a task whose project is outside its
allowed projects.
2. Let workspace setup deny project access.
3. Open the task, follow Manage project access, add the project, save,
reload, and return to the task.
4. Confirm that the saved message remains and Retry task is a separate
action.

## What Changed

- Record a typed, non-retryable project denial before provider dispatch.
Project denials do not enter automatic continuation or provider comment
policy.
- Project current and historical setup denials into a safe task notice.
Keep unrelated failures, prior assignees, and approval/pause/budget
gates separate. Recheck current project authority, including standard
trust and root-issue grants. Accepted retries keep their working state.
- Add board-only project-access reads and additive saves under the agent
row lock. Enforce company scope, agent-management authority,
pending-approval restrictions, and an audit record for new additions.
- Add the production notice and searchable multi-project editor,
including read-only, saving, cancellation, and failed-save states.
Preserve the return-to-task query through canonical agent redirects.
- Register board-only project-access routes in OpenAPI with a strict
request schema.
- Add production Storybook examples and API/persistence/concurrency
regressions. Retain the approved design snapshot and update the behavior
maps and implementation contract.

## Verification

- Passed: `pnpm -r typecheck`, `pnpm build`, `pnpm build-storybook`,
`pnpm check:token-gates`, `node scripts/check-maps.mjs`, and `node
scripts/storybook-catalog.mjs`.
- Passed: 11 permission/projection tests and 30 run-completion tests
against an isolated PostgreSQL database. These cover historical
failures, concurrent additions, idempotent audit, preserved
restrictions, foreign projects, read-only users, agent self-grants, and
no automatic wakeup.
- Passed: all 27 OpenAPI route tests, including route coverage and the
new board-only request/response contract.
- Browser: the real app displayed a historical denial; Manage project
access opened the editor; saving retained the original project and
survived reload; Return to task showed the ready notice. Saving created
one audit event and zero runs. Explicit Retry returned 202 and reached
the separate sandbox requirement, which remained enforced. No external
provider was used.
- All five production Storybook interaction checks pass: blocked task,
save, cancellation, failed-save recovery, and read-only settings. These
use fixture callbacks; durable authorization is covered separately
above.
- `pnpm test:run` was attempted locally but stopped after repeated
embedded PostgreSQL startup failures caused by macOS semaphore
exhaustion. Full-suite CI is required before merge. The separate UI
suite passed all 9,095 tests. The 41 focused server regressions also
passed after rebasing onto the latest master. A further 26
recovery-classification tests pass, including a red-to-green regression
for automatic continuation after a project denial.

## Risks

- No schema migration. The new endpoint only adds same-company project
IDs and preserves the rest of the stored policy.
- The historical notice recognizes one exact setup denial message. It
does not display arbitrary run errors or disclose a different source
project's identity.
- The retry still passes all current admission and isolation checks. The
notice does not promise that other execution requirements are satisfied.
- Related open work: #13309 covers inherited secret restrictions; #11234
covers the broader repository catalog. This change does not replace
either policy surface.

## Model Used

OpenAI Codex (GPT-6), with code execution and browser tools. The precise
runtime model ID and context-window size were 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
#` 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 server and full UI
suites; full-suite infrastructure limit 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-24 16:16:29 -07:00
Devin FoleyandPaperclip a3675ddcdf Add bounded database response size diagnostics (#17504)
Count database response value sizes by public caller and static table for one explicit UTC window of at most 60 seconds. Keep diagnostics off by default and exclude SQL, parameters, returned values, errors, and credentials from output. Preserve lazy execution, transaction boundaries, and no-replay behavior.

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

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 15:39:28 -07:00
Devin FoleyandPaperclip 470cb34c0e Read reply repair and ownership evidence without execution payloads (#17501)
Narrow native reply repair and conversation ownership reads to their consumed evidence while preserving full stored history, result receipts, locks, current authority checks, and publication authorization.

Verify successful repair from equivalent unpublished states, exact final replies and decisions, no duplicate repair, preserved history, and 213 recovery/presentation tests. Build/typecheck and all CI pass. Greptile 5/5 Medium.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 15:14:48 -07:00
Devin FoleyandPaperclip a7006a9506 Reclaim finished isolated task directories with restore points (#17472)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents do task work in execution workspaces on the instance disk
> - A task without a project repository runs in a plain (non-Git) task
directory under `<instance>/isolated-workspaces/<company>/<issue>/`
> - Nothing removes these directories after the work is done, so the
disk fills up
> - The directories can hold the only copy of agent work, so plain
deletion is not safe
> - This pull request saves each finished task directory in an encrypted
restore point, removes it, and restores it when the work reopens. It
also removes orphaned directories after a grace period
> - The benefit is that finished task directories stop using disk, and
no agent work is lost silently

## Linked Issues or Issue Description

**What happened?**
On one instance, `instances/<id>/isolated-workspaces/` held 251 task
directories and 12 GB, and it grew every day. 61 directories belonged to
open workspace rows whose issues closed more than 7 days before (3.9
GB). 12 belonged to issues closed 3 to 7 days before (0.7 GB). 4.0 GB
were directories with no open workspace row (the row was archived or
missing).

The cause has two parts:
1. The terminal workspace reaper archives a workspace only when it
proves delivery through Git (a merged pull request or a merged branch).
A task directory is not a Git checkout, so the Git status inspection
fails and the reaper always skips the row as "undelivered". Git cache
expiration handles only `git_worktree` workspaces.
2. When a person archives such a workspace by hand, artifact cleanup
removes a `local_fs` directory only when `metadata.createdByRuntime` is
true. Task directory rows never set it, so the directory stays and the
row becomes `cleanup_failed`.

**Expected behavior**
Paperclip reclaims the task directories of finished work by itself. It
must not lose files that only exist in those directories.

**Steps to reproduce**
1. Run an agent on an issue without a project repository (for example a
low-trust sandbox task, or an issue with no project), so it works in
`isolated-workspaces/<company>/<issue>/`.
2. Let the agent write files there, then mark the issue `done`.
3. Wait any number of days. The directory and its workspace row stay.
The reaper logs the row as skipped (undelivered).
4. Archive the workspace by hand. The directory still stays.

**Paperclip version or commit**
`master` at fe85fd7ac4

**Deployment mode**
Self-hosted, local disk storage.

**Agent adapter(s) involved**
Any adapter that runs in a task directory (seen with local and sandbox
runs).

## What Changed

- Added `isolated-task-directory-snapshot.ts`: a deterministic snapshot
of a plain directory (files with modes, directories, symbolic links as
links). It never follows symbolic links, refuses special files, and
enforces the restore point limits (20,000 files, 64 MiB per file, 256
MiB in total). A `node_modules` directory next to a `package.json` is
left out. Restore writes to a staging directory, verifies the digest,
then renames it into place.
- Added `isolated-task-directory-retention.ts`:
- `expireIsolatedTaskDirectory` saves the directory in a restore point,
archives the workspace with the `isolatedTaskDirectoryRetention` state
in one transaction, checks again that the directory did not change,
renames it to trash and deletes it. A change after the capture rolls the
workspace back to active and drops the restore point. An interrupted
removal finishes on a later sweep.
- It uses the git cache expiration consumer checks
(`isGitWorkspaceExpirationEligible`): no live run, unsettled
finalization, live lease, live runtime service, unresolved recovery
action or reopen fence, and no other open workspace or project workspace
in the directory. It also requires the source issue to be bound to the
workspace, because reopen restores through that binding.
- `restoreIsolatedTaskDirectory` restores the directory on reopen. It
replaces an empty directory and fails closed on a non-empty directory
that does not match. When the restore point expired, it reopens an empty
directory and posts a system comment.
- `sweepIsolatedTaskDirectories` walks `isolated-workspaces/` in bounded
batches (8 per sweep, with a cursor). A closed workspace that its issue
is still bound to is saved and removed like an open one. Other folders
are orphans and are removed after a grace period when no live row,
project workspace, runtime service, run, finalization, lease or recovery
action refers to them.
- Reused the git restore point ledger, storage and encryption.
`storeSealedRestorePoint` and `readRestorePointObject` were extracted
from `storeGitRestorePoint`. `markGitRestorePointExpired` now also marks
a task directory restore point as expired.
- `isGitWorkspaceExpirationEligible` takes `closedSiblingsAllowed`, so
closed rows for the same issue directory do not block a task directory
forever. Git expiration keeps its old behavior.
- Terminal workspace reaper (`execution-workspaces.ts`): handles open
task directory rows after the terminal delay and registers that moment
as a deadline. It finishes pending removals, runs the folder walk at the
start of a rotation (once per server start, then only at real
deadlines), and saves at most 8 directories per sweep.
- Reopen accepts a closed `shared_workspace` row that holds task
directory state. The run path also reopens (and restores) a closed
workspace with that state before preparing the run.
- Settings: `PAPERCLIP_TASK_DIRECTORY_RETENTION_ENABLED` (default
`false`), `PAPERCLIP_TASK_DIRECTORY_RETENTION_DRY_RUN` (default `true`),
`PAPERCLIP_TASK_DIRECTORY_ORPHAN_GRACE_DAYS` (default `7`, minimum `1`).
The delay is `PAPERCLIP_GIT_RESTORE_POINT_TERMINAL_DELAY_DAYS`. Removal
needs `PAPERCLIP_GIT_RESTORE_POINTS_ENABLED=true`.
- Logs: `execution_workspace.task_directory_retention`,
`execution_workspace.task_directory_cleanup` and
`execution_workspace.task_directory_restore`, with outcome, skip reason,
candidate bytes and reclaimed bytes.
- Documented the behavior in `doc/recoverable-disk-cleanup.md` and the
settings in `doc/DEVELOPING.md`.

**Preservation decision.** The code shows that these directories are not
scratch space. Nothing pushes or copies their contents, and the next run
of the same issue reuses the same path. So the files are saved before
removal, with the same ledger, encryption and retention as git restore
points, and restored on reopen. Orphans are not saved: no workspace row
can reopen them. The docs and the Risks section state this.

**Scheduling.** The work follows
`rules/sleep-aware-background-work.mdc`. It adds no timer and no
polling. Open rows use the existing reaper and its per-workspace
deadlines (the end of the terminal delay, or one hour after a retryable
skip). The folder walk runs once at startup. After that, workspace
archive and issue close notifications (the existing terminal workspace
hints) point the reaper at the affected folder, and a kept folder is
checked again only at its own deadline (end of a grace period or
terminal delay, or one hour after a busy lock or live consumer). When
the tracked state is full, the reaper schedules another walk instead of
dropping work. With nothing pending, it registers nothing.

## Verification

- `cd server && pnpm exec vitest run
src/__tests__/isolated-task-directory-retention.test.ts
src/__tests__/isolated-task-directory-retention-config.test.ts
src/__tests__/heartbeat-task-directory-restore.test.ts` (embedded
Postgres): 48 tests pass. They cover:
- a closed issue's task directory is archived and removed after the
delay by the reaper, and reopen restores files, modes and symbolic links
exactly;
- the same through reopen for a `shared_workspace` row, and through a
real heartbeat run (the run restores the files and runs in them);
- the directory stays before the delay (and the deadline is the end of
the delay), while the issue is open, and when retention is off;
- the directory stays while each consumer check fails: live run, live
runtime service, reopen fence, unsettled finalization, live lease,
unresolved recovery action, issue bound elsewhere, live sibling
workspace in the directory;
- no removal without restore points, with a non-durable key, before
`notBefore`, or over the limits;
- dry run changes nothing; a change after the capture rolls back; an
interrupted removal finishes on the next sweep; an expired restore point
reopens empty with a warning; a diverged directory fails the reopen and
stays untouched;
- folder sweep: an orphan is removed after the grace period, kept when
recently modified, in dry run, when a live row points into it, and when
its issue is open; an archived unbound row's folder is removed; an
archived bound row's folder is saved, removed and restored on reopen;
the walk is bounded and continues from its cursor;
- review follow-ups: a file name with a backslash saves and restores;
after the startup walk, a workspace archived later is found through its
change hint; an orphan used by a live run is retried; skipped rows do
not starve later rows of the per-sweep budget; overflowing tracked
deadlines schedule another walk instead of being dropped; a closing
child sends the reaper back to its archived parent's folder; a pending
lease cleanup on a closed workspace keeps an orphan; a deep path at the
platform path limit restores; an expiry recorded during removal survives
and the archive and removal are recorded as activity; a task closed
beyond retention keeps its new restore point for the full period;
- symbolic links: a task directory that is a link, an orphan that is a
link, and a link inside a directory are never followed; the link targets
stay untouched.
- Mutation checks. Each change below made at least one of the tests
above fail, and the test passed again after revert:
- drop the run-path reopen condition; drop the skipped-row cache; drop
hint tracking; reject backslashes again; drop the live-consumer retry
for folders; evict overflowing folder deadlines; skip ancestor hints;
ignore workspace-attached leases for orphans; stage restores under the
old longer name; overwrite the whole metadata on the final write; retain
task directory restore points from the tree close only; drop the removal
activity; force `closedSiblingsAllowed` off; make live siblings not
block; skip the digest recheck before rename; skip the issue binding
check; remove both symbolic link refusals (task directory); remove the
orphan symbolic link refusal; ignore live rows in the folder walk; skip
the orphan modification grace; ignore dry run for orphans; skip the
restore on reopen; skip the terminal delay; skip the restore points
requirement; skip the orphan open-issue check.
- Removing only one of the two task directory symbolic link checks did
not fail a test, because the other check still refuses the link.
Removing both fails the test.
- Related suites: `pnpm exec vitest run execution-workspaces
isolated-task-directory workspace-runtime git-restore-point
git-workspace-expiration terminal-workspace workspace-reaper
heartbeat-git-restore heartbeat-task-directory
isolated-repository-rehome nested-clone-migration environment-selection`
in `server/`: 34 files, 749 tests pass, 4 skipped. Two
`workspace-runtime.test.ts` tests fail (`does not diagnose a missing
remote branch as a rejected credential`, `rejects explicit main on a
master-only remote ...`). They fail the same way on unchanged `master`
on the local machine, so they are not caused by this change.
- `pnpm run typecheck` in `server/`: passes.
- Not run: the full server suite, a live instance rollout, and a check
that the host disk usage drops after removal.

## Risks

- The feature is off by default, and it starts as a dry run when
enabled. It needs restore points to remove anything. Rollback: set
`PAPERCLIP_TASK_DIRECTORY_RETENTION_ENABLED=false`. Saved restore points
and restore on reopen keep working.
- Orphan folders are removed without a restore point. This applies only
when no workspace row is bound to the issue at that path. If such an
issue is reopened later without another workspace, its next run starts
in an empty directory. The grace period (default 7 days of a closed tree
and no changes) and the dry run limit this risk.
- A directory over the restore point limits stays on disk (logged with
its reason). Special files keep a directory too.
- `node_modules` next to a `package.json` is not saved. A reopened task
must install its packages again.
- The restore point lives as long as git restore points (90 days by
default), counted from the later of the tree close and the removal. Only
task directory restore points use the removal time; git restore points
keep their retention. After that, reopen starts with an empty directory
and posts a warning comment.
- Reopen now accepts a closed `shared_workspace` row, but only when it
holds task directory state written by this feature.
- No schema migration. The ledger table and its columns are unchanged.

> 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

## 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-24 14:16:58 -07:00
Devin FoleyandPaperclip cb6c878c38 Read execution blocker evidence without full execution payloads (#17491)
Read the lifecycle, process, and typed context evidence consumed by execution blockers. Preserve full result evidence, native workspace receipts, current state checks, and stored history.

Validated with 458 real PostgreSQL and recovery tests, full build, recursive typecheck, all55 CI checks, and Greptile 5/5.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-24 14:14:46 -07:00