Commit Graph
160 Commits
Author SHA1 Message Date
DottaandPaperclip 09ad9adce2 feat(server): adopt running remote agents through durable controller checkpoints (#17439)
## Thinking Path

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

## Linked Issues or Issue Description

**What existing behavior does this improve?**

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

**Current behavior**

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

**Proposed behavior**

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

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

## What Changed

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

## Verification

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

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

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

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

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

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

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

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

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

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

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

## Risks

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

## Model Used

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

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 21:39:23 -05:00
DottaandPaperclip 50b932a142 fix(server): require approval before onboarding task creation (#17914)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/78/changes/paperclip/78
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip manages agent work and human decisions.
> - Onboarding asks the first agent to propose a task before it starts
work.
> - The first-task skill requires human acceptance before task creation.
> - The native tool could create and schedule that task without
acceptance.
> - REST task creation had the same gap.
> - This change checks saved acceptance in the shared creation
transaction.
> - Valid approvals and ordinary delegation keep their existing
behavior.

## Linked Issues or Issue Description

**What happened?**

An agent could create a task from the canonical onboarding first task
before the user accepted its proposal. A real journey caught this after
the agent read the first-task skill. A local database regression
reproduces the missing check in the native tool and both REST creation
endpoints.

**Expected behavior**

New task creation must wait for saved human acceptance of the latest
ordinary proposal. A different parent or a standalone task must not
bypass that check.

**Steps to reproduce**

1. Bind an authenticated agent run to a server-created onboarding first
task.
2. Leave its proposal unaccepted.
3. Call native `create_task`, `POST /api/companies/:companyId/issues`,
or `POST /api/issues/:issueId/children`.
4. Before this fix, creation succeeds. After this fix, it is denied
before a child or wake is committed.

**Paperclip version or commit**

Reproduced on `3be54534c24fc279533ff421e62a77ad15b72954` before the fix.

## What Changed

- Add a shared check for new task creation from an authenticated
onboarding run.
- Read the latest ordinary confirmation across both confirmation kinds
under the source task lock.
- Require a human card decision or validated conversational acceptance.
Require the current revision for a saved plan.
- Preserve ordinary delegation, committed replay, and dispatch recovery.
- Add database regressions and update execution and onboarding map
documentation.

## Verification

- Real PostgreSQL regression before the fix: 17 failures and four
passing approval/delegation controls. No skips.
- Exact published source: 102 tests passed across admission,
conversational confirmation, and project tools, including all 25
admission regressions. Real Linux PostgreSQL, no skips.
- `pnpm build` and `pnpm -r typecheck` passed locally.
- Map checks and `git diff --check` passed.
- `pnpm test:run` was attempted locally. Existing suites failed because
the host exhausted PostgreSQL semaphore sets. The remaining owned test
process was stopped; its failed log is preserved. No unrelated database
was stopped. Current-head Linux CI passed, including build, typecheck,
and browser E2E. The first CI attempt lost runners; its evidence is
preserved, and interrupted jobs passed one CI rerun.
- The live Cloud attempt remains failed. Local tests do not qualify
Cloud onboarding, artifact delivery, or native tracing. No deployment or
model retry is part of this change.

## Risks

A stricter creation check could reject a valid approval. Tests cover
direct, conversational, checkbox, dismissed-card, and clock-skew cases.
The check applies only to new tasks from the canonical onboarding run.
It does not change existing task updates, execution policy, or wake
recovery.

## Model Used

OpenAI GPT-6 through Codex, with tool use and code execution. The
session does not expose a more specific serving 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-27 12:30:50 -05:00
DottaandPaperclip 585b4e6af8 fix(chat): preserve deferred Retry source batches (#17883)
## Thinking Path

> - Paperclip manages agent work through tasks and governed execution.
> - Connected conversations retain the source and authority for each
request.
> - Task Retry pins the full saved GitHub input batch before it waits
for cleanup.
> - Resuming that wait could append an independent board queue after
authorization.
> - Dispatch then rejected the changed batch before the provider
started.
> - This pull request keeps the authorized batch intact and restores
certified undelivered input from historical failed attempts.
> - Each board message retains a separate turn and its own admission
checks.

## Linked Issues or Issue Description

**What happened?**
A deferred GitHub task Retry could consume a separate board message
while promoting its saved receipt. The new run then failed with
`chat_failed_run_retry_not_authorized`. A second Retry could also fail
because the historical run retained the mixed batch.

**Expected behavior**
Retry must preserve the source batch it authorized. Separate board input
must keep its own execution path. Historical input may be restored only
when durable evidence proves that the rejected attempt never started a
provider.

**Steps to reproduce**
1. Retain ten authorized GitHub requests behind a failed native turn.
2. Start task Retry while the predecessor still lacks its exact
termination receipt.
3. Save an independent board message.
4. Complete cleanup and resume the same Retry receipt.
5. Observe the mixed batch rejected during dispatch. Retry that failed
attempt to reproduce the second refusal.

Refs #17725 for the existing saved-input Retry contract. This change
repairs the later queue merge and its retained failed state. Related
#17878 covers snapshot continuation and later turns in recovered
directories; its workspace repair can be reviewed independently.

## What Changed

- Keep deferred chat Retry promotion scoped to its exact receipt.
Ordinary board Retry coalescing retains its existing behavior.
- Let a new explicit task Retry restore a historical mixed batch only
after strict never-started, source, ownership, allocation, board-comment
and prior-delivery checks.
- Preserve failed-run history and original budget-skipped receipts.
Record restored queue ownership in activity.
- Add production queue and HTTP-route regressions. Update the execution
contract and verification maps.

## Verification

- Before the repair, the deferred ten-input batch failed before identity
initialization. The same regression now reaches the real initializer
with the exact GitHub batch. The independent board message then reaches
its own initializer in a separate turn.
- Before historical recovery, ordinary task Retry refused the persisted
mixed failed batch. The repair passes that case without changing the
failed run. HTTP duplicate requests retain one Retry owner. An
additional compatibility probe restored the old queue candidate
predicate and removed all historical mixed-state fixture writes: the
real producer created the exact batch and the complete historical HTTP
recovery case passed (1/1). The fixed-source historical fixture now uses
the production serializers.
- The affected Retry group passes 86 cases on commit
`325c00158df5569f5b42731cda4ecc4584909d99`, including already-coalesced
external receipts. Refusals cover edited sources, active controllers,
unresolved allocations, changed receipts, foreign external receipt
ownership and prior delivery of either board or GitHub input, including
immutable budget-skipped receipts.
- Full workspace `pnpm -r typecheck` and `pnpm build` pass with these
source changes. Map, module-boundary and diff checks pass.
- Full local `pnpm test:run` was started. The host cannot create
additional PostgreSQL semaphores; database-dependent suites fail or skip
at startup. The affected database group runs against a separate migrated
PostgreSQL 17.10 test database with Node 24.22.0 on macOS arm64.
- The regression deliberately stops after the real identity initializer
to avoid provider allocation. It proves HTTP, queue, source
authorization, identity and separate continuation behavior. It does not
claim a live model result, GitHub publication, deployment or recovery of
the original hosted task.
- Current head `325c00158df5569f5b42731cda4ecc4584909d99`: all checks
are terminal with no failures or pending checks; required `ci / verify`
and `ci / e2e` pass. Conditional Storybook checks are skipped. Greptile
and Paperclip each reviewed this exact commit at 5/5. Both earlier
review threads are resolved. Paperclip retains one optional robustness
suggestion for batch ordering; the verified historical producer order
remains a strict recovery contract.

## Risks

Restoring input could repeat work if the prior turn had executed. The
existing strict bootstrap proof rejects provider output, native state,
process or controller evidence, and allocations. Restoration also checks
current external authority, unchanged board revisions and other run
history. An uncertain executor remains held. No migration, provider
fallback or relaxation of dispatch validation is included.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, repository inspection,
code editing and test execution. The exact runtime model ID and
context-window size are not exposed in this session. A separately shared
regression supplied independent reproduction 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 (affected database
regressions; full local suite has the host limitation described 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-27 11:34:18 -05:00
DottaandPaperclip ac22b1b1f9 fix: preserve recovered input and task directory continuity (#17878)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/60/changes/paperclip/60
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip lets people manage AI agents and their work.
> - Recovery must preserve files and the authority of each human
request.
> - Some failed native runs preserve a snapshot without completing
native copyback.
> - The saved input could remain blocked after recovery settled.
> - A later turn could also treat the new task directory as a Git base
because it inherited the agent default.
> - This change continues the same authorized input once and keeps its
saved directory usable on later turns and Retry after a pre-provider
preparation failure.

## Linked Issues or Issue Description

Refs #14905. Related: #17724 covers cleanup handoff after native
copyback. This change covers snapshot-only preservation and later turns
in its directory. #17814 covers a separate same-run checkpoint restore
defect. The open legacy-adapter change in #13291 does not cover this
native saved-input path.

## What Changed

- Forward the trusted deferred inbound request to native continuation
admission. Verify the exact unconsumed receipt and its source, actor,
company, agent, and task.
- Let historical holds for the same failed run use its settled recovery
strategy. Keep each controller and termination check.
- Keep the old native sync proof unchanged. Attach recovered files
through the verified snapshot path in a fresh attempt.
- Recognize only the current same-task, active local directory in the
canonical task or attempt namespace. Keep it on later turns instead of
applying inherited Git defaults.
- Keep the current healthy directory when canonical Retry follows the
exact pre-allocation Git-base failure. Require the committed source
link, strict server-produced failure events, no execution evidence or
unfinished owner, and the current canonical binding. Recognize the known
configuration warning without accepting arbitrary or provider-origin
warnings.
- Keep owned-directory readiness separate from repository-free strategy
selection. A repaired real Git checkout with explicit
task/project/assignee Git policy uses normal Git validation and
preparation. Preserve explicit fresh, new sources, immutable native
bindings, and locked path/binding checks.
- Add real PostgreSQL and filesystem regressions for admission, file
preservation, and the complete later-turn heartbeat policy chain.

## Verification

- Real PostgreSQL callback/queue regressions are RED on unchanged
production and GREEN with this repair: one successor from the same
authenticated saved request, with filesystem snapshot preservation. The
24-case source/Retry matrix passes and retains termination, allocation,
generation and revocation negatives.
- Actual browser use exposed both later-turn Git-base failure and
canonical Retry selecting an empty sibling. The real heartbeat
selection/configuration/preparation/Retry regressions reproduce each.
All 81 workspace-preparation cases passed before the final repaired-Git
edge; all 17 final affected phase-chain/safety cases pass, including
that new case. Its finalized oracle checks effective Git policy, an
actual Git checkout and saved-file bytes. Known preparation warnings
pass; unknown/provider warnings and execution contradictions do not
certify reuse.
- Full workspace typecheck, full build with matching `5342bd52da` stamp,
maps and diff checks pass. Runtime commit `5342bd52da` passed CI with 53
successful checks and 2 conditional Storybook skips, and Greptile 5/5
with zero unresolved threads. The follow-up `9096fe8f62` removes only
the feature/access map additions as requested; runtime and test files
are byte-identical. Map structural and diff checks pass. Greptile
reviewed this exact final commit at 5/5 with no unresolved threads. All
test and build jobs passed. The final required `ci / verify` and `ci /
e2e` aggregate gates are queued without runners; one bounded gate-only
requeue retained every successful job. GitHub auto-merge is enabled and
waits for those gates.
- Normal UI acceptance on exact final source with actual OpenCode,
configured DeepSeek model and Daytona passes both turns: snapshot
continuation writes a useful review/report; reload and a second ordinary
message retain the same workspace and logical native/provider session.
All six files remain byte-identical, the report's computed SHA and
unpredictable marker are confirmed, the concrete result is actual 250
versus correct 350, native finalization/copyback commit, and normal
lease cleanup succeeds. A separate normal UI canonical Retry on
preceding `9ac1d29147` preserves the current directory/report/marker and
writes a continuity note; the final Git-policy split leaves that
exercised non-Git path unchanged.
- Boundaries: local app/server source with real remote native execution;
seeded historical failure/stop receipts and actual stored snapshot, not
a replay of the original staging allocation or external review
publication. Prior local verification rewrote installed dependency
metadata; its generated lock diff was retained and reverted. Final
launch/end source and dependency metadata hashes match; no installed
dependency byte-identity claim beyond that observed freeze.
- Required full local suite was attempted but remains incomplete:
embedded PostgreSQL bootstrap failures prevented assertions in several
suites, so only its owned test tree was stopped. A prior
workspace-recovery test timed out and the following seed collided with
an active hold; both unchanged tests passed in isolation. Runtime-head
`5342bd52da` hosted CI passed the full gate; the documentation-only
follow-up waits for the aggregate jobs noted above. Its [first Rust
attempt](https://github.com/paperclipai/paperclip/actions/runs/40528918372/job/122276159874)
failed an unchanged ACPX shutdown cleanup assertion; the preceding
same-Rust head passed. [One bounded Rust-only
retry](https://github.com/paperclipai/paperclip/actions/runs/40528918372/job/122281990961)
passed. No test assertion or production guard was weakened.

## Risks

- Incorrect input authority could repeat work. The existing owner
reauthorizes the same durable source; admission verifies its receipt
under the task lock.
- Directory recognition could select unrelated files. It requires the
current scoped binding and canonical namespace, then preparation
rechecks the stored binding and path under the source lock. Missing or
symlinked retained roots are not recreated.
- Snapshot preservation does not certify native copyback or old external
outcomes. The old run and its unknown outcomes stay unchanged.
Unconfirmed controllers or termination remain blocked.
- There is no schema migration or new public API.

## Model Used

OpenAI `gpt-6.1-sol` through Codex, with reasoning, code execution, and
tool use. The host 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 — targeted suites; the full
local attempt limitation and final CI proof 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
- [ ] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 16:02:43 +00:00
DottaandPaperclip 663fe667d6 fix(recovery): reconstruct missing native workspace obligations (#17877)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A failed native run can still own prepared files in a remote
sandbox.
> - Cleanup must preserve those files before another executor can start.
> - Older failure producers can omit the preservation record.
> - A saved message then waits for cleanup without a recovery owner.
> - This change reconstructs that record during user continuation and
orphan cleanup.
> - The existing worker saves the files, verifies stop, and returns
input to normal admission.

## Linked Issues or Issue Description

**What happened?**

A failed native run retained a prepared workspace and an active sandbox
lease. It had no file-preservation obligation. Cleanup protected the
files. The next user message waited for a stop receipt that no worker
owned.

**Expected behavior**

A new message registers the missing maintenance work at once. Existing
orphan cleanup recovers historical saved input. Ordinary Retry remains
available. Saved input can start once, after verified preservation and
containment.

**Steps to reproduce**

1. Persist a failed native run with an exact prepared remote workspace
and its active lease.
2. Leave its finalizer terminal and unowned. Omit the workspace recovery
record.
3. Send a user comment or run orphan cleanup.
4. Observe that no preservation worker starts and the comment remains
deferred.

**Paperclip version or commit**

Reproduced on `bd382eee536f4ada680072b1cc19100163c3d04a`.

**Deployment mode**

Native Runner with a remote sandbox. Local verification uses disposable
PostgreSQL 18 and real temporary workspace files.

Refs #17788 and #17800. Those fixes preserve future deadline failures
and register historical preservation on Retry. This change adds
automatic recovery for new and saved messages. Refs #17839 for the
separate restart-failure producer repair.

## What Changed

- Register missing preservation before an authenticated continuation
waits for remote cleanup.
- Reconstruct the same exact-source obligation during the existing
orphan lease sweep.
- Notify the existing preservation worker in the registration
transaction. Do not add a timer or history scan.
- Test ephemeral and reusable sources, live owners, active executors,
mismatched sources, rollback, lock contention, Retry and saved-input
delivery.
- Document the recovery contract and feature-map behavior.

## Verification

- Red: both new orphan-recovery cases fail on unchanged production code.
Four ownership and source negative controls pass.
- Red: a new comment starts no preservation work without the admission
repair.
- All 461 test cases across five affected suites pass across the local
runs. One existing 30-second timing case timed out under concurrent
verification; its isolated rerun passed with the same deadline and
assertions.
- The three final-head entry journeys pass again: HTTP Retry, HTTP
comment creation, and historical saved-input recovery.
- `pnpm -r typecheck` and `pnpm build` pass on final head
`8fab8b2ba9049f640fd884094a2007a69465e6da`. The affected server
typecheck and build also pass.
- The integration journey holds the provider stop reply, checks that no
successor starts, preserves real files and newer host edits, and checks
one saved-input successor.
- Module-boundary checks, map checks and `git diff --check` pass.
- Native macOS PostgreSQL cannot start because host semaphore capacity
is exhausted. Database verification uses separate disposable PostgreSQL
databases for each suite. An earlier shared-database attempt caused
fixture interference and is not counted as a pass.
- Greptile gives 5/5 with no findings or unresolved threads on
`8fab8b2ba9049f640fd884094a2007a69465e6da`. All final-head GitHub checks
are settled: 55 passed, two Storybook checks skipped, and one neutral
Paperclip review. Full CI test, build, typecheck, Runner and browser
lanes pass. No full local `pnpm test:run` pass is claimed. The fix is
not deployed. This PR makes no claim of a live hosted model turn or
recovery of the original incident.

## Risks

- Registration grants maintenance authority only. Existing coordinator,
descriptor, allocation, stop, file-receipt and execution gates remain
effective.
- A locked source remains held. Its one-second lock timeout does not
prevent an unrelated source from registering recovery.
- New preservation writes wake existing work. Settled sources produce no
new recurring work. Historical discovery uses the existing orphan
cleanup lifecycle and its backoff.
- Provider lifecycle is simulated in local integration. PostgreSQL, HTTP
routing, file export/merge, the worker and queue admission are real.
- No schema, credential, model, trust, retry-budget or assignment
change.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository inspection, tool
use and code execution. The runtime does not expose the exact 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-27 09:50:50 -05:00
DottaandPaperclip db70c5acd9 fix(runner): retain startup Stop and exhaust reconnect recovery (#17812)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators must be able to stop a remote runner while it starts.
> - Remote artifact staging can finish after the transport's bounded
cleanup wait.
> - The launcher discarded an early Stop request and could dispatch
after staging.
> - A separate path missed the runner's plain-text reconnect expiry
receipt.
> - This change retains startup cancellation and ends an exhausted
reconnect incident.
> - Public package imports and a source-bound profile keep the
regressions buildable and admission checks intact.

## Linked Issues or Issue Description

Related work: Refs #16835, Refs #16843, Refs #16855.

**What happened?**

The HTTP Stop route returned 200 and recorded a cancelled run. After
bounded bootstrap cleanup, releasing artifact staging still dispatched
one launch RPC. A separate reconnect test accepted a replacement after
the runner reported that its grace period had expired.

The first PR head also failed CI. The HTTP regression imported Runner
source across the server TypeScript boundary. Its event receipt mock
read a field that is not present on both event forms. The changed
transport also invalidated the pinned Copilot source identity.

**Expected behavior**

A cancelled launch must remain cancelled after staging. An exhausted
reconnect receipt must end that incident. Regression tests must use
public package contracts. The source identity must cover the changed
transport without replacing historical profile bytes.

**Steps to reproduce**

1. Hold the remote launcher's artifact staging promise.
2. Call the board Stop route and let bootstrap cleanup reach its
preparation deadline.
3. Release staging and count launch RPCs.
4. For reconnect expiry, end an owned runner with its coded or
plain-text expiry message.
5. Run the server typecheck and the Copilot profile identity check.

## What Changed

- Retain cancellation through staging, trace publication and an
in-flight launch RPC.
- Apply a deferred signal through the existing identity checks when the
launch identity arrives.
- Preserve an earlier KILL request when a later TERM request arrives.
- Recognize both coded and plain-text reconnect expiry receipts.
- Test the real board Stop route and heartbeat cancellation with
PostgreSQL. Use public Runner exports and handle both event receipt
forms.
- Generate source-bound Copilot profile v65 after integrating current
master. Preserve exact published v62, v63 and v64 fixtures, retain the
earlier v63 reconnect candidate as historical evidence, and reject
historical profiles for a current launch.
- Document process evidence, retained uncertainty and the limits of
cleanup and continuation evidence.

## Verification

- At current integration head
`2b96ee3adcb3be642c538436113e1159d92f843d`, launcher and HTTP Stop
regressions: 21 passed. This includes the existing process-generation
and exact-exit safety cases on the integrated base. The HTTP case
includes bootstrap expiry; it is not isolated Stop-only settlement
proof.
- At current integration head, reconnect expiry regressions: 2 passed,
for coded and plain-text receipts.
- At current integration head
`2b96ee3adcb3be642c538436113e1159d92f843d`, the Copilot installation and
identity suite passed all 63 tests and the canonical generator check
passed. Before regeneration, the merged-tree admission test failed with
a transport source hash mismatch and the generator reported a stale
identity. Published v62, v63 and v64 bytes remain unchanged; the prior
reconnect candidate is preserved separately. The v62 fixture SHA-256
remains
`faa4d1b35267b54e134a8e0c84586fe1196d8fad5525cb53aa195aae53ca6e83`.
- Current integration head passed all 86 focused tests, the full
recursive repository typecheck and the root repository build. The normal
server typecheck also passed, including its full Runner release rebuild.
- Historical full recursive repository typecheck and the root repository
build passed at `187bb85b4fa1fbd2ffaec992650a1c8557df9ebe`. Server
typecheck also passed through the normal package script and a direct
full server TypeScript check. The prerequisite full Runner release build
passed.
- The historical HTTP baseline returned 200 and dispatched one late
launch RPC. The repair returned 200 and dispatched zero in the bounded
fixture.
- Earlier failures remain part of the evidence: the first head failed
TypeScript package boundaries and source identity. One local HTTP run
failed before migrations; it passed after the disposable test database
received normal migrations. Earlier sandbox-wide checks ran out of
memory or lacked Cargo. Those runs did not pass.
- Historical head `187bb85b4fa1fbd2ffaec992650a1c8557df9ebe` received
Greptile 5/5 with no actionable findings and all hosted gates passed in
run `40412261957`: 53 successful check runs, four expected skips and a
successful legacy Snyk status. Those results predate the master
integration and do not establish current-head readiness.
- Current integration head `2b96ee3adcb3be642c538436113e1159d92f843d`
includes master `bd382eee536f4ada680072b1cc19100163c3d04a`. All
current-head hosted gates passed in run `40512885858`: 53 successful
check runs, four expected skips and a successful legacy Snyk status,
with zero pending or failed checks. Build passed at 13:42:36 UTC and the
final `ci / verify` aggregate (job `122224566238`) passed at 13:43:00
UTC on 2026-10-27. Greptile reviewed exact head
`2b96ee3adcb3be642c538436113e1159d92f843d` at 5/5 (updated 2026-10-27
13:36:41 UTC), with no actionable findings and no open inline threads.
The hosted partitions passed full-suite verification, including browser
and release gates. The wider qualification limits below remain in force.
- Local setup on current master initially failed frozen install with
`ERR_PNPM_LOCKFILE_CONFIG_MISMATCH` because `patchedDependencies`
differs from the lockfile. Pinned pnpm 9.15.4 hydrated dependencies with
`--no-frozen-lockfile --ignore-scripts`; its generated lockfile change
was restored. This PR changes no lockfile or workflow.
- Reproduce the Stop tests from `server`: `pnpm exec vitest run
src/services/native-runtime/native-session-executor.test.ts -t 'remote
runner process supervision|HTTP Stop during remote runner staging'`. The
test accepts a guarded disposable `_test` database through
`PAPERCLIP_TEST_DATABASE_URL`, or starts its normal embedded database.
- Reproduce reconnect tests from `packages/paperclip-runner`: `pnpm exec
vitest run src/live/runnerd-codex-transport.test.ts -t 'does not restart
an exhausted reconnect'`. Build the normal test runner first.
- Reproduce profile checks from `packages/paperclip-runner`: `node
scripts/copilot-profile-identity.mjs --check` and `pnpm exec vitest run
src/drivers/acpx/copilot-installation.test.ts`.

## Risks

- The wider qualification is incomplete. Verified-stopped replacement
and concurrent recovery plus Stop each entered two physical provider
processes. Both fail the original requirement of at most one physical
provider entry. Passing fixture assertions do not make all incident
scenarios accepted.
- HTTP concurrent recovery, shared provider-attempt accounting,
server-admitted restored continuation, live-provider behavior and
product cleanup settlement remain unrun or unproved.
- Accepting a deferred signal is not proof that a dispatched process
stopped. The existing monitor remains authoritative.
- The HTTP test uses the real route, heartbeat cancellation and
PostgreSQL state. Execution lease and event ports, plus the launch RPC,
are controlled fixtures. Bootstrap expiry participates in this case.
- The local incident matrix uses an installed runner binary and a
scripted provider. It does not qualify a live provider. Local fixture
teardown does not certify product cleanup settlement.
- This change does not authorize historical turn replay. It does not
change ownership, pause, approval or provider-attempt gates.

## Model Used

OpenAI Codex, GPT-6 family. The runtime does not expose the exact
serving model ID or context-window size. The agent used reasoning,
repository inspection, shell execution and connected GitHub tools.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 08:45:00 -05:00
DottaandPaperclip bd382eee53 fix(recovery): explain retained source verification failures (#17773)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Workspace recovery must prove the identity of retained work before
it uses an allocation.
> - The verifier rejects several different contracts with the same
`source_binding_invalid` code.
> - A saved Retry can therefore stop before file preservation without
enough evidence for a safe repair.
> - Operators need a bounded observation of the selected source without
another attempt or weaker guards.
> - This pull request records closed failure reasons and adds an
explicit read-only inspection on the run page.
> - The benefit is a precise next diagnostic step while the saved work,
intent, and ownership fences remain intact.

## Linked Issues or Issue Description

Refs: #17741, #17053. This is a diagnostic follow-up. It does not change
recovery admission or the retained-source acceptance policy.

**What happened?**

A retained-source recovery exhausted in `verify_execution` with only
`source_binding_invalid`. Run and task diagnostics could not distinguish
an invalid reference, lease policy, source binding, or reopened
allocation. The normal recovery-actions read can also revalidate
recovery state, so it was not a safe substitute for a current read-only
inspection.

**Expected behavior**

Record an allowlisted reason at the rejected guard. Allow a reader to
inspect one exact task/run/action without changing its attempts, saved
input, receipts, or ownership. Clearly separate the recorded failure
from current identity observations.

**Steps to reproduce**

1. Prepare a terminal native run with a retained-source action and a
lease policy of `reuse_by_environment`.
2. Attempt source verification. The guard must still reject that policy.
3. Open the failed run and choose Inspect recovery, then Inspect current
source. The diagnostic should name `lease_policy_not_retained` and the
allowlisted policy.

## What Changed

- Split existing source-binding guards into closed reason codes. Keep
each acceptance predicate and physical recovery action unchanged.
- Persist validated, path-free reason/policy detail with future system
recovery failures. Keep historical generic records compatible.
- Add an explicit query on the existing task recovery-actions GET. Exact
company/task/run/action selection bypasses read revalidation. Run
selection lists at most 20 actions; inspection stops before per-lease
lookup when a record has more than eight sources.
- Add a manual inspection control on native export-failure run pages.
Keep resolved retained actions inspectable. Show recorded failure and
current recorded-source identity separately.
- Update execution semantics and feature/access recipes. The shared type
is the normalized server/UI contract; it contains no provider payload or
secret.

## Verification

- RED: the prior verifier failed both reason assertions for rejected
warm policy and foreign-run lease binding. GREEN: the final focused
source/HTTP, system-state, and UI suites pass 23 tests. The UI follow-up
passes 8 tests after the review fixes.
- HTTP tests prove exact selection, cross-company denial,
resolved-action access, reused-allocation rejection, redaction, and
unchanged actions, runs, leases, tasks, wakeup receipts, workspace
operations, and activity. A nine-source case proves bounded inspection
while the existing worker verifier still accepts all nine valid sources.
- Review follow-up: show allowlisted saved detail beside a changed
current observation, preserve all listed actions, and follow
exact-action selection in verified/limited/error tests. The prior
component fails the saved/current drift regression in an in-memory RED
control; the current component passes. UI typecheck/build pass again.
- `pnpm -r typecheck` and `pnpm build` pass. Token gates, maps, module
boundaries, and `git diff --check` pass.
- Rebased onto master `46de15ff4b` without conflicts. Range-diff proves
both diagnostic commits are unchanged. On new head
`a7e2bc87fcea75e76b6b736304d1287257f71174`, all 23 focused tests, full
typecheck/build, token gates, maps, and module checks pass again. Both
fresh reviews are 5/5 with zero unresolved threads. Final exact-head
GitHub status is CLEAN: 55 success, 2 skipped, 1 neutral, no pending
checks or failures. The [ci / verify
aggregate](https://github.com/paperclipai/paperclip/actions/runs/40387002325/job/121802415824)
passed in eight seconds. All test/build/typecheck/E2E lanes passed.
- Existing full workspace recovery suite: 56/56 pass (103.5 seconds)
before the byte-equivalent rebase. `pnpm test:run` hit repeated embedded
PostgreSQL initialization failure before database-suite assertions and
was stopped. No full local suite pass is claimed. The owned external
`_test` database provided the focused HTTP/source verification. The
prior browser attempt also stalled at Network.enable before its click;
that attempt did not establish browser acceptance.
- Current-head local browser acceptance passes through the actual run
page: Inspect recovery lists the exact action, Inspect current source
shows recorded generic failure separately from the current rejected
policy, and reload resets inspection to the explicit control. Explicit
selection works again after reload. The app used isolated data, an empty
inherited environment, a paused synthetic agent, and no provider
credentials. Final state remains failed/exhausted with zero attempts,
zero wakeup receipts, zero workspace operations, and unchanged original
generic failure. The local app and temporary tab were stopped. This
proves the diagnostic journey, not the deployed source's actual lease
policy or its recovery.
- Final merge verification at
`a7e2bc87fcea75e76b6b736304d1287257f71174`: GitHub reports CLEAN and
mergeable. Both repository-required checks, `ci / verify` and `ci /
e2e`, pass, as does the exact-head Greptile check (5/5, zero unresolved
threads). The separate [Entire
trail](https://entire.com/gh/paperclipai/paperclip/trails/7) has no
recorded reviewer approval; no such approval is claimed. It is not a
configured GitHub merge gate.
- Initial local setup failures were distinct from product assertions:
localhost sandbox EPERM required scoped local access; a browser harness
import path was corrected. A test UUID type inference error and
historical native-export UI selection gap were corrected before the
green run. The live task remains blocked; this diagnostic does not claim
it recovered.

## Risks

- This change improves evidence. It does not prove provider termination,
local descriptor integrity, or successful file preservation, and it does
not repair the original source.
- Current inspection spans multiple database reads. It is an
observation, not a historical cause or execution authority. It performs
no provider/filesystem calls and starts no retry or executor.
- The optional persisted detail is backward compatible. Unknown values
and extra fields are excluded. Oversized source lists remain uninspected
through this explicit diagnostic path; physical recovery rules do not
change.

## Model Used

OpenAI Codex based on GPT-6, with code execution and browser tools. The
exact serving model ID and context window 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
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 08:21:28 -05:00
Devin FoleyandPaperclip 0cb3fc7db5 fix(runner): retain ACPX transport failure details (#17837)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip Runner reports provider failures to the execution and
recovery services.
> - Those services read the terminal event's structured `error` field.
> - The ACPX transport-failure path wrote its code and message only at
the top level.
> - The session facade therefore replaced that cause with `error: null`
and the generic message "Provider turn failed".
> - This pull request uses the existing structured error contract while
retaining the old fields.
> - Recovery can retain the actual redacted cause without reopening the
failed provider or assuming a retry is safe.

## Linked Issues or Issue Description

**What happened?**

A native ACPX run can lose its failure details between runnerd and the
session facade. The transport-failure producer emits a failed turn with
top-level `code` and `message`. The facade reads `error`. The server
receives a generic provider failure and cannot use the original cause to
explain or classify recovery.

**Expected behavior**

Retain the transport code and redacted cause in the structured terminal
error. Keep failed sessions closed, provider termination unproven, and
automatic replay disabled.

**Steps to reproduce**

1. Open a native ACPX session and start a turn.
2. Send malformed activity from the sidecar after it acknowledges the
turn.
3. Poll the native provider events.
4. Observe that `turn.failed.error` is absent even though the producer
has a transport code and cause.

Refs: #16366, #17543. Those changes cover legacy process-session
diagnostics and session-ensure errors. This change repairs the native
ACPX polling terminal contract.

## What Changed

- Add the existing transport code and redacted message to the terminal
event's structured `error` field.
- Mark that error as non-recoverable. Keep the previous top-level fields
for existing readers.
- Add a real sidecar fault fixture and a native-provider regression. It
checks one failed terminal, retained error details, a closed session,
and no provider reopening.
- Document the failure contract and verification scope in execution
semantics and the recovery map.

## Verification

- Red: the new fault test failed because the structured transport error
code was null on the original code.
- Green: all 26 native-provider integration tests pass with the fix.
- Three session-facade terminal tests and three native-runtime
provider-failure classification tests pass.
- `pnpm -r typecheck` and `pnpm build` pass.
- Hosted test partitions, native Runner checks, typecheck, build,
browser shards and canary passed. Two final aggregate jobs received no
runner for several minutes. The first CI attempt was cancelled after
saving its status, and only those two cancelled jobs were resubmitted.
Both aggregate jobs passed on attempt 2. The current head has 55
successful checks, four expected skips and successful Snyk status.
- `git diff --check`, `node scripts/check-module-boundaries.mjs`, and
`node scripts/check-maps.mjs` pass.
- The full local Vitest suite is not run for this patch. The local
filesystem fell below 500 MiB free during verification. The preceding
full-suite attempt crashed embedded PostgreSQL; focused checks and CI
are used without claiming that attempt passed.
- Live incident: verified the exact predecessor sandbox was destroyed
and workspace recovery completed. An audited user retry through the
normal task-retry service started one new native run. That attempt
preserved the original question but failed before provider startup with
a missing workspace-sync descriptor binding. The startup race is fixed
separately in #17838. A later audited retry completed successfully,
saved its useful answer and restored its workspace. The agent API
handoff failed; an audited operator repair through the normal issue
service completed the required user assignment in Review without
repeating the comment or provider work. This verifies recovery admission
on the existing deployment; the new Runner change is not deployed yet.
- Evidence limit: the historical terminal had `error: null` and its raw
sidecar frame was not retained. Fault injection proves this producer
defect and the same loss of diagnostics. It does not establish which
transport fault caused the historical turn to end.

## Risks

- Recovery now receives a specific transport failure code. The error
remains non-recoverable and `providerTerminalObserved` remains false.
Stop proof, effect reconciliation, workspace preservation, retry limits,
and normal execution authority still govern any successor.
- This change preserves diagnostics. It does not make a malformed or
lost provider connection healthy.
- No schema migration, credentials, provider selection, or model
settings change.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, source inspection, tool use,
and code execution. The runtime does not expose the exact 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-26 21:34:29 -07:00
Devin FoleyandPaperclip 94819b6ded fix(recovery): preserve live heartbeat preparation ownership (#17838)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Heartbeat prepares each run before it starts the native provider.
> - Startup recovery can run while that preparation is still active.
> - The native input and coordinator exist before workspace
synchronization saves its descriptor.
> - Recovery checked the later native executor registry but missed the
earlier heartbeat execution registry.
> - It could claim the live run and dispatch a second executor against
incomplete workspace state.
> - This change gives the recovery claim the existing live heartbeat
registry and checks it before changing ownership.

## Linked Issues or Issue Description

**What happened?**

Startup recovery can claim a native run that the same server is still
preparing. The second execution reads the admitted native input before
workspace synchronization has saved its descriptor. It fails with
`native_workspace_sync_descriptor_binding_missing`. The original
preparation then finds a failed run and cannot start the provider.

**Expected behavior**

Leave live preparation with its current executor. Do not change its
native coordinator or dispatch another executor. An inactive run must
still pass the existing recovery checks.

**Steps to reproduce**

1. Start heartbeat runtime selection for a remote sandbox.
2. Hold workspace synchronization after native input and coordinator
persistence.
3. Run native startup recovery with the heartbeat execution still
active.
4. Observe an unintended `bootstrap_incomplete` claim and a second
execution.

Refs #17439 for broader remote controller recovery. This change repairs
the current heartbeat startup race. It does not add checkpoint storage
or remote adoption. A separate diagnostics correction is in #17837.

## What Changed

- Pass the existing heartbeat live execution registry to native recovery
claims.
- Check the current registry after locking the run and before any
takeover or coordinator change.
- Return the existing waiting disposition for live preparation. Keep
workspace descriptor validation unchanged.
- Add a PostgreSQL regression at the real runtime-selection boundary and
an assembled heartbeat recovery regression with the real claim service.
- Document the preparation interval and its recovery behavior.

## Verification

- Red: the new runtime-selection test observed an unintended bootstrap
claim on the original code. The heartbeat regression also failed because
recovery dispatched the live run.
- Green on current head `88bec51f10d61eb851eec849e82a80bb198555c7`: all
144 runtime-selection, heartbeat recovery, workspace-preparation and
restart-policy tests pass.
- These cases check an unchanged coordinator, no duplicate recovery
dispatch, a running source run, and normal recovery after the execution
leaves the registry.
- The full native restart integration file passes all 22 available
cases. Nine real-runner process cases are skipped because local Runner
test binaries were removed to recover disk space. The passing cases
include remote startup, old preparation fencing, ownership conflicts,
Stop, and concurrent recovery.
- Direct full server TypeScript check passes: `pnpm exec tsc --noEmit -p
server/tsconfig.json`. Hosted CI passes on this head, including full
test partitions, typecheck, build, native Runner checks, serialized
suites, browser shards, canary and both aggregates. Apex is 5/5 with no
actionable findings. The current head has 54 successful checks, two
expected skips and successful Snyk status.
- `git diff --check`, module boundary checks, and map checks pass.
- A wider local safety run stopped with PostgreSQL error 53100 and
ENOSPC after 64 tests passed. Recursive typecheck also stopped with
ENOSPC. These attempts did not pass. Generated files in this worktree
were removed to free space. The full local test suite and build have not
been run for this change; hosted CI must supply those results.
- Live evidence: a production startup recovery claimed a new run during
outward workspace staging. The second preparation failed before provider
startup. Its exact sandbox was later destroyed with successful cleanup.
The patch is not deployed. A later audited retry on the existing
deployment completed successfully and saved the requested answer once.
An audited operator repair through the normal issue service completed
the final user assignment in Review after the agent API handoff failed.
This acceptance verifies recovered work, not deployment of the new
guard.

## Risks

- A live heartbeat remains responsible for its own completion and
cleanup. Recovery must not compete with it. The registry is
process-local; existing durable ownership checks still govern other
controllers.
- An executor that is still active remains protected until its cleanup
removes its registry entry. This adds no timer, polling loop, schema or
credential change.
- Missing or corrupt workspace descriptors still fail closed. This
change does not authorize replay of unknown provider actions.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, source inspection, tool use
and code execution. The runtime does not expose the exact serving 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-26 21:34:15 -07:00
DottaandPaperclip db32bdf98b fix(github): recover review replay and preserve passive summaries (#17726)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/8/changes/paperclip/8
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - GitHub tools save review assessments and publish their confirmed
receipts.
> - A timeout before publication staging can leave an invocation with no
saved publication.
> - Exact replay must repeat only the authorized preflight and converge
on one publication.
> - PR requests also share an App-owned summary comment. Progress and
maintenance must preserve its final content.
> - A native turn can intentionally wait for an author without sending
another reply.
> - This pull request repairs unstaged replay and prevents queued or
passive-wait notices from replacing a published assessment.

## Linked Issues or Issue Description

Related repairs: Refs #16883, Refs #16284, Refs #16275 recover saved
publication receipts. Refs #17183 covers unavailable approved replay
output. Refs #17714 fixes a separate publication lock-order problem.

**What happened?**

Exact review retries can return null `begin_review` output or
`github_publication_unavailable` after an upstream read times out before
publication staging. Separately, response maintenance can replace a
completed PR assessment with “Review queued…” or a generic
missing-response notice after an authorized no-new-work reply. The
latter run can correctly commit a passive wait while its external
summary is still overwritten.

**Expected behavior**

Exact retries repeat only the authorized preflight. A saved or uncertain
publication keeps its existing recovery owner. Queued acknowledgements
preserve final App-owned summary content. A committed native passive
wait settles its current acknowledgement without writing a response or
treating an old summary as a new-run reply. Success before presentation
remains held until a durable decision exists; exact current-run
confirmed receipts can retire their placeholders. Genuine missing/error
responses retain fallback handling.

**Steps to reproduce**

1. Hold a GitHub metadata read until an authorized gateway call times
out before publication staging. Repeat the exact request.
2. Publish a valid PR assessment. Send an ordinary author reply that
asks for no new work and no duplicate comment.
3. Commit the native `response_wake` with a no-comment server
presentation decision. Run response maintenance twice.
4. The base returns the invalid replay result or replaces the assessment
with progress/fallback text.

**Paperclip version or commit**

Replay regression: `41c42895fa862c586ff841f8c50200d93ffc83a4`.
Maintenance regression: the previous PR head
`c7553d2325df53d98d9febff7023ade4b1e704eb`. Both use real PostgreSQL and
simulated GitHub responses. The maintenance failure was also observed in
normal hosted use.

**Deployment mode**

Source checkout on macOS arm64 with isolated PostgreSQL 18, followed by
a dedicated QA qualification. No production rollout is included.

## What Changed

- Route exact `begin_review` replay through its typed owner. Repeat
unstaged preflight only with the complete request digest and no
successful invocation result.
- Preserve current policy, immutable assessments, unique action keys,
receipt recovery, and provider leases.
- Preserve a verified App/marker-owned final shared PR comment during
acknowledgements. New canonical assessments can still replace it at the
same or a new head.
- Project only a bounded server-committed native wait decision. Settle
the current main-PR acknowledgement without provider writes or a
fabricated current-run reply receipt.
- Hold succeeded native runs while presentation is undecided, including
the committed-before-presentation window. An exact current-run confirmed
receipt can still retire its placeholder.
- Keep missing/error fallback and uncertain publication checks. Update
the bot guide, execution semantics, maps, and real-database regressions.

## Verification

- **Causal RED:** exact begin replay returned null; exact unstaged
submit replay returned unavailable. The new real-database maintenance
test then reproduced the exact generic summary overwrite. Its first
repair also exposed an earlier queued-acknowledgement overwrite. Both
publication boundaries are repaired. Four additional ordering
regressions reproduced the generic PATCH while native presentation was
undecided; the corrected tests hold until the same row settles.
- **Local GREEN:** 196 complete GitHub workflow cases pass on an empty
migrated PostgreSQL fixture. This includes queued/running preservation,
both success-before-presentation windows, null/scalar/invalid decisions,
exact-current versus predecessor receipts, durable acknowledgement
settlement, repeated no-op maintenance, eventual genuine missing/error
fallback, pending writes and same/new-head canonical replacement.
Workspace typecheck, exact-head build, maps and whitespace checks pass.
The real database respects the actual selected-column projection.
- **Command:** `PAPERCLIP_TEST_DATABASE_URL=<owned-postgres-18> pnpm
--filter @paperclipai/server exec vitest run
src/__tests__/chat-channels.integration.test.ts -t 'GitHub agent review
workflow'`.
- **CI/review:** historical heads
`c7553d2325df53d98d9febff7023ade4b1e704eb` and
`4d00d3af6f2ccbda8f38a98f81d392d82dd3c59c` had Greptile 5/5. Final
readiness head `2d267f1e4aa0b835a11aede97798fbd800d9cd96` passed
independent source review with no actionable finding and fresh Greptile
5/5 with zero unresolved threads; all 54 check runs pass with 2 intended
skips; Snyk is successful and the merge state is CLEAN. The dedicated QA
destination acceptance also passed.
- **Full-suite limitation:** the earlier `pnpm test:run` attempt
produced no final summary. Its retained log reports failures in
unchanged workspace-runtime tests and 15-second gateway-audit and
stale-question timeouts. Their cause remains unknown. Do not treat the
targeted results as a full local-suite pass.
- **Live evidence and limits:** normal native OpenCode/DeepSeek review
delivery published a useful assessment, inline finding and App check at
the exact reviewed head. A separate ordinary author reply delivered a
useful answer and settled. Those are ordinary delivery proofs, not live
injected-timeout proofs. A later no-new-work reply exposed the
maintenance overwrite. The final combined source
`070e9c4ead9341cd497b23f533a91bf194475e79` includes the exact
publication repair and passed normal review → ordinary author reply →
quiet destination acceptance. A normal current-head review restored the
legitimate assessment; the ordinary no-new-work reply then committed a
passive wait without new GitHub tools. The same 1,436-character summary
stayed identical while queued/running, after terminal settlement, and
more than 3 minutes 50 seconds after successful cleanup. Review rows and
receipts stayed unchanged, with no owner, live run or queued input.

The timeout fault fixtures prove replay and single-publication safety.
The historical first upstream timeout producer remains unknown. The live
board reads do not expose the raw acknowledgement action row: its
terminal cancellation and next-pass no-op are real-PostgreSQL fixture
proofs, while the hosted checks prove actual destination stability. The
hosted journey did not inject a timeout or pause the
terminal/presentation writer window. Private destinations, raw receipts
and internal task identifiers remain outside this PR.

## Risks

- Original and repeated metadata reads can overlap. The immutable
assessment, action uniqueness and publication lease must remain
authoritative.
- Missing complete request identity or a deleted completed receipt
remains held. The gateway does not reconstruct receipts or relax
approval.
- Passive suppression requires actual native mode/committed phase,
current source ownership and the server's no-comment decision. It
applies only to the main PR review surface. Uncertain current writes
stay held; other surfaces keep normal placeholder retirement.
- There is no migration, credential change, dependency or new retry
scheduler.

## Model Used

OpenAI GPT-6.1 Sol (`gpt-6.1-sol`) in Codex assisted with diagnosis,
implementation, tests, and source review. It used reasoning, repository
tools and code execution. The context window size 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
- [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-26 22:22:57 -05:00
DottaandPaperclip 208a093d19 fix(chat): preserve resumed review waits and retry newer saved input (#17725)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/36/changes/paperclip/36
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - GitHub review agents publish findings and wait for an authorized
reply or head change.
> - Review tools could retain the requester after resume while
completion lost that same wait proof.
> - A yielded report could then queue its own continuation or repeat a
missing-wait error.
> - Ordinary Retry could also select an older failed turn while a newer
saved request had no admission.
> - This change shares the existing authority proofs and canonical
admission owners.
> - Current access, publication, execution and budget gates remain
required.

## Linked Issues or Issue Description

Refs #17318. Builds on the canonical saved-input retry owner in #17733.
Related #16550 covers deferred legacy comments after ownership waits.
The separate recovery repair in #17724 owns the cleanup-to-saved-input
handoff; this PR does not add a second admission owner.

A resumed review could publish an assessment, then turn its
`response_wake` into an immediate `issue_status_changed` wake. A
provider that omitted its continuation could instead repeat a completion
error. A newer linked-human message saved during a budget hold could
also remain unstarted because Retry selected the older failed source.

## What Changed

- Resolve passive review waits through the accepted requester ledger
used by GitHub tools. Require the latest reviewed head, exact company,
task, agent, repository and processed publication under current access.
- Fill omitted/null yielded continuation from that server proof. Permit
an inherited normal tree resume only through its accepted parent
identity. Permit an ordinary follow-up only after this run has a
processed useful reply linked to its exact inbound message. Reject
acknowledgements, partial assessments, pending or uncertain writes as
that reply proof.
- Recheck the same run/review/head/source proof during feedback,
finalization and final status commit. Preserve explicit malformed-input
rejection and Done policy. Add no monitor permission or publication
grant.
- Keep heartbeat and publication projections narrow. Preserve both
published Copilot v62 identities and generate source-bound v63 from the
pinned merged base. No v63 live qualification is claimed.
- Certify the structured isolated-directory binding failure only at its
pre-allocation boundary and only through canonical task Retry. Other
workspace validation reasons and unknown effects remain denied.
- Route Retry to eligible newer saved input through the existing
canonical owner. Preserve the old result and skipped receipt. Require
current principal, source, App, runtime fence, budget, native ownership
and exact cleanup proof.
- Recover absent historical App metadata only through an earlier trusted
processed response receipt and an immutable non-null endpoint claim.
Explicit malformed or conflicting metadata stays denied.
- Revalidate execution through the exact claimed wake, coalesced task
Retry and resolved user-continuation receipt after admission. A second
normal Retry can recover the exact never-started authorization failure
with unchanged saved input. Require empty execution evidence, exact
diagnostics and no allocated or active executor.
- Explain when a recorded result prevents exact replay. Ordinary board
Retry directs a new task message to continue from saved work. Inherited
requester identity alone never grants replay.
- Keep accepted board-message preparation failures with the canonical
task Retry owner. Certify the exact unchanged comment, original user
wake, accepted same-user identity and absence of provider execution. Pin
that source, recheck it at admission, and bind identity initialization
to the coalesced intent and exact claimed successor receipt.
Uncertifiable sources retain an actionable fresh-message refusal.
- Return safe named authority categories for board Retry refusals.
Update execution documentation and capability maps. Add no schema or
polling loop.

## Verification

- A fresh external review request exposed a separate isolated-directory
preparation failure before allocation. Its first normal Retry saved an
intent but admitted no successor. This follow-up certifies only that
exact structured preparation boundary through the existing canonical
Retry owner, with trusted failed settlement and no executor, lease,
result or output. A real HTTP/queue regression reproduces the missing
successor before the repair. Twenty-three current-source safety cases
and the final 98-case Retry/HTTP group pass, including a fresh click
after a denied intent, source revocation, accepted results and the older
direct API remaining denied. The warning case uses actual configuration
preparation and production event append; unknown warning shape or
provider provenance stays denied. Full workspace typecheck, build, maps
and diff checks pass. Renewed hosted normal Retry now succeeds on the
final combined source: it preserves the exact saved request and linked
requester, starts one native successor in a fresh attempt workspace,
publishes the current-head 2/5 assessment and commits In review with
passive response wake.

- A later hosted normal Retry published a low-score assessment but lost
its accepted source identity because the previous attempt failed before
identity initialization. It repeated continuation errors, then submitted
Done. This is failed acceptance, not a successful qualification. The
initializer now retains the exact source only after execution-time
failed-chat Retry authorization; generic Retry and Done policy are
unchanged. A real queue/initializer regression fails with a null source
on the previous code and passes with the newer saved source. Two real
database completion cases verify that source yields the authoritative
wait and commits In review. Renewed hosted normal Retry now retains the
accepted source and exposes the authoritative current-review wait. It
completed after two semantic feedback corrections (wrong contract
revision, then wrong criterion IDs); the correct contract was present in
emitted input. The third finish was accepted and committed In review,
with no missing-wait loop or Done escalation.
- Repeated board-message preparation failures now walk their canonical
intent, failed dispatch and accepted parent identity, rechecking each
contained predecessor. A production queue regression fails the second
ordinary HTTP Retry before the repair and passes afterward. Changed
source, contradictory execution evidence and cyclic receipts remain
denied; traversal stops after twenty links.

- New inherited omission cases failed before the repair. The final full
PostgreSQL 18 wait/status suite passes **252 tests** under Node 24.22.
It covers omitted/null input, malformed-first recovery, same-head
resume, exact-source replies, changed heads, revoked access,
pending/uncertain/mixed publications, final commit revalidation, no
duplicate review and no self-wake.
- **15 existing publication tests passed before the pinned merge** on a
separate fresh database. They retain default partial-delivery and
failure-fallback behavior. Context reads remain below the unchanged 16
KiB heartbeat and publication response limits with large saved output
present.
- Saved-input tests cover a real signed PR webhook, production
accounting-budget refusal, normal policy recovery and canonical Retry.
Historical producer-shaped tests prove the missing-App case red, then
check trusted prior receipts and malformed, late, revoked or mismatched
proof. A broader order-coupled test was reproduced on the prior source;
its global fault injector now targets its own authorization query. The
43-case sequence passes without changing production behavior or its
safety assertions.
- New signed-ingress and real heartbeat execution tests reproduced the
admitted-successor failure, then the next normal Retry failure. The
final pinned merged source passes 249 full GitHub review/publication and
Retry cases on a fresh database, including all 49 saved-input cases. A
separate 74-case Retry/HTTP run also passes. Exact dispatch negatives
cover source, company, agent, task, successor, receipt, pause,
assignment, accepted success, controller and cleanup proof. Bootstrap
contradictions remain denied. The canonical Retry suite passes 16 cases
through its supported external PostgreSQL test connection.
- The normal board HTTP route reproduced the unusable inherited-result
refusal. It now returns actionable guidance without starting a
successor. The actual UI Retry hook and banner tests pass 12 cases.
- A production-shaped accepted board comment followed by a pre-provider
qualification failure reproduced HTTP 409 before the repair. The final
real HTTP/queue test certifies one canonical successor and preserves the
accepted identity. It rejects changed source, actor, task, agent,
result, controller, process, profile, allocation and receipt evidence. A
post-acceptance source change keeps the same intent pending. Execution
revalidation rejects removed or mismatched proof. The probe stops
deliberately after production identity initialization; it does not claim
provider execution. The final source passes the 75-case Retry group, the
focused two-case board/inherited-policy group and 19 canonical Retry
cases. Full workspace typecheck, build and map checks pass.
- Full workspace `pnpm -r typecheck` and `pnpm build` pass. Map,
module-boundary and diff checks pass. Full local `pnpm test:run` remains
unverified. The 256-case explicit-native suite cannot initialize its
packaged PostgreSQL because libzstd is absent; an existing
command-scoped fallback did not resolve it. Current-head remote
full-suite checks are required. Merged-source full GitHub+Retry (249),
separate Retry/HTTP (74), full wait/status (252), profile/history and
completion (75), and visible Retry hook/banner (12) checks pass. The
fresh owned PostgreSQL 18 databases applied all 377 migrations.
- Actual Rust Runner, OpenCode 1.18.34 and exact DeepSeek used unchanged
production-generated synthetic contexts. Resume finished with an
explicit server wait. The author-reply trial omitted continuation twice:
its first report failed the existing empty-criteria check; the corrected
second report still omitted continuation and received the exact server
wait. It completed once with no missing-wait loop or Done escalation.
Controller acknowledgments were synthetic; these probes do not establish
hosted publication or board state.
- Hosted combined testing proved automatic admission of the exact saved
author reply, a useful GitHub answer and committed passive wait. It
required two malformed-continuation rejections before a nullish fallback
succeeded. A later normal Pause/Resume completed in one finish with
explicit response_wake and no new review/comment. The admitted normal
Retry then exposed the execution guard gap repaired here. Those remain
checkpoint results. On the final combined source, renewed normal Retry
completed actual native provider work and GitHub publication, committed
the passive wait, released ownership and cleaned up its environment. A
subsequent ordinary unmentioned author reply woke once, made no GitHub
tool calls or new assessment, and completed its first finish with an
explicit response wake. Both exact leases expired with successful
cleanup and the queue was empty. However, final destination inspection
found a separate automatic fallback overwrote the existing summary with
a generic no-final-response message. External summary preservation
therefore remains failed acceptance; the separate publication repair in
#17726 owns that producer. Full combined publication preservation has
not passed. These hosted runs include the pinned-base native additions;
they do not qualify Copilot v63.
- Current-head automated review is 5/5 with zero unresolved threads. All
current-head CI gates passed (54 successful checks/contexts, four
skipped, none pending). The earlier login-cleanup idle-work assertion
also reproduced locally while its controlled baseline passed; its cause
remains unknown, but the exact assertion passed in the fresh
current-head CI shard. No timeout, assertion or production workaround
was added. This PR is ready for review within its source, authority,
native finalization, state, wake and cleanup scope. The independent
automatic fallback publication regression is tracked by #17726; no
complete combined workflow pass is claimed.

## Risks

- The latest saved review defines the durable head boundary. Ingress
must record a new head before admission. A reply receipt proves delivery
only for its exact saved source; a shared GitHub comment ID alone cannot
authorize this wait.
- Historical App recovery cannot infer identity without direct proof or
an earlier valid server response receipt. It never replays that
response.
- This does not infer review-score policy from instructions or override
Done. No deployment, merge or repair of historical tasks is claimed by
the PR.

## Model Used

OpenAI `gpt-6.1-sol`, with reasoning, repository inspection, code
editing and test execution. The context-window size 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 database and
native suites; full local suite limitation described 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 (verified on immutable latest
head)
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(verified on the latest head)
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-26 22:22:47 -05:00
DottaandPaperclip f0877a9c7c fix: recover native work and requalify saved computers (#17724)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/32/changes/paperclip/32
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip manages AI agents and preserves their work after an
interruption.
> - Native Stop must confirm termination and save the workspace.
> - Task and run views must show the current recovery receipt.
> - A new external reply can arrive while that cleanup is pending.
> - The reply must return to its original chat owner after cleanup.
> - A successful fresh retry also becomes the task’s bound workspace for
its next ordinary turn.
> - Managed computer updates can invalidate a saved snapshot
qualification.
> - This pull request fixes those instructions, acknowledgements,
handoffs and the existing native-agent Test path.
> - The operator can continue with new input while the stopped turn
remains preserved.

## Linked Issues or Issue Description

**What happened?**

Stopped native runs could keep the initial workspace-repair instruction
after files were recovered. Native Stop could also leave the shared
cancellation state at requested after remote termination was confirmed.
A new GitHub reply saved during cleanup could then remain queued because
cleanup did not ask its original inbound owner to reconsider the
receipt. After a managed snapshot update, an existing native agent could
also need qualification while the generic environment Test required
instance-admin access.

**Expected behavior**

Show the current saved-work state. Acknowledge Stop only after exact
termination proof. Let an authorized operator use the existing
saved-agent Run test to qualify its current computer. After cleanup,
recheck the saved reply's current authority and admit it once as new
input. Preserve the stopped run and uncertain external effects.

**Steps to reproduce**

1. Stop an active native turn before it returns a semantic result.
2. Send a new authorized external reply while cleanup is pending.
3. Let workspace recovery and provider cleanup finish. Inspect the run
instruction, Stop acknowledgement and queued reply.

Related work: #17594 preserves native processes through controller
outages. #17401 addresses a separate bootstrap failure. This pull
request does not replace those fixes.

## What Changed

- Use the exact run's workspace recovery receipt for task and run
guidance, including the Activity ledger.
- Retire an obsolete exact-session notice through the existing bounded
reconciler. Preserve the separate no-replay hold.
- Write the shared native Stop acknowledgement only after the audited
Stop intent and every exact remote lease receipt match.
- Delegate saved external input to the live inbound owner. Recheck its
source, actor, endpoint, principal and conversation generation. Promote
the same receipt in the transaction that creates its new run.
- Use that owner from the cleanup callback and existing stranded-queue
catch-up after a lost callback or restart. A denied source cannot abort
other items.
- Reuse the current private task directory after a successful fresh
retry. Re-read the company/task/workspace binding and validate the
canonical stable or attempt namespace without recreating missing files;
keep a later fresh retry separate.
- Extend the existing agent-scoped qualification owner to native agents.
The clean saved-agent Test qualifies its exact computer before runtime
testing. Reuse a current proof; require a real probe and verified
cleanup for a stale proof. Preserve draft tests and metadata-only
external/pool previews.
- Update execution semantics, capability maps and the recovery
verification recipe. Add a production-page Storybook interaction for the
normal native Test.

## Verification

- **Red:** persisted recovery states returned stale instructions; an
exact terminated native lease left shared Stop requested; the real
GitHub subscribed-message fixture left zero successors after cleanup.
The controlled baseline restored the previous production source,
reproduced the failure, then restored the fixed bytes with matching
SHA-256 hashes.
- **Green:** 382 durable-chat, native-continuation, Stop and recovery
tests pass. The real GitHub/PostgreSQL handoff matrix passes 12/12. It
covers callback, application restart, callback/sweep race, valid input
behind a deleted source, and denial for unknown stop, wrong allocation,
paused endpoint, revoked principal, deleted or changed source, changed
conversation generation and foreign scope. Repeated callbacks preserve
one receipt, its actor, one successor and the unchanged stopped
predecessor.
- **Workspace continuation red/green:** the real PostgreSQL preparation
flow created a fresh attempt and saved a marker, then rejected the next
ordinary turn with the same binding mismatch observed in deployed use.
The repaired preparation and filesystem suites pass 86/86: stable and
attempt-root reuse, saved-marker preservation, a later clean sibling,
changed current bindings, foreign company/task, closed workspace,
traversal, missing path and symlink refusal. No task row reset or
old-turn replay is used.
- **Qualification red/green:** a native saved-agent qualification
returned 422, and its normal Test skipped qualification. The repaired
path passes 224 qualification/immutable-snapshot/environment tests and
176 UI/upgrade tests. It covers revoked authority, foreign or changed
computer, current-proof reuse, failed/missing proof, draft settings,
settings changed during the probe, and external/pool previews. On exact
current source, the real local Harness / Runtime UI also qualified a
newly saved disposable Computer with missing proof through actual
Daytona, then completed the actual OpenCode/OpenRouter hello probe using
`openrouter/deepseek/deepseek-v4.1-flash-1017`. Supported availability
changed from Legacy-only to Paperclip Runner after proof persistence.
Qualification and runtime leases both completed normal release/cleanup.
The five prior task run IDs were unchanged; no task wake was created.
This is macOS source-server/UI with real Linux Daytona and provider
services, not a hosted UI claim. Test probes are archived by the
existing inspection contract; release does not claim physical deletion.
- Full workspace `pnpm -r typecheck`, `pnpm build`, token, map and diff
checks pass at `f6450f4739fc8141815b8b7c35fde22a655c5f90`. The broad
local `pnpm test:run` passed 24,899 tests and reported one existing
nested-clone test timeout at 15 seconds. The complete failing file then
passed 21/21 with the normal timeout; the exact case also passed on the
pinned earlier baseline. The broad runner stopped before its later
groups. No timeout or production workaround was added. Prior head
`f6450f4` has 53 successful checks, two conditional Storybook skips and
Greptile 5/5 with no inline threads. New workspace-reuse head
`fcb13399ec70f92f71bd8250081251dca3f48962` passes workspace typecheck,
full build, 86 scoped tests, maps and diff checks; Current-head CI has
53 successful checks and two conditional Storybook skips; Greptile is
5/5 with no inline findings. Deployed ordinary-turn reuse acceptance
passed as described below.

- **Hosted Stop proof:** the real GitHub issue-mention path used native
OpenCode, `openrouter/deepseek/deepseek-v4.1-flash-1017`, Daytona and an
exact low-trust task boundary. Normal UI Stop interrupted an active Bash
call. Native and shared acknowledgements matched the exact lease.
Automatic recovery saved exactly `STARTED\n` (8 bytes), with no finish
marker. Reload showed Stopped and saved work recovered. Only the
original cancelled run remained. Normal five-minute cleanup completed
its exact provider-destruction transition.
- **Limits:** the provider buffered command output, so the marker was
read after recovery. The approved local provider credential could not
inspect the hosted sandbox; its 404 is not deletion proof. Cleanup
success comes from the exact lease's platform destruction transition. An
earlier hosted stale instruction disappeared after rollout with
unchanged raw run/action data; that historical cause remains
unexplained. Original incident holds and publication receipts were
untouched.
- **Hosted handoff proof:** after QA deployed the combined candidate
containing `46b2e99`, startup admitted the previously saved GitHub
author reply without another comment, Retry or manual action. The new
native run used the exact original wake receipt and saved comment. It
completed once, committed an external-response wait, preserved the
stopped predecessor, emptied the queue and left the task In review. The
answer appeared in the original GitHub conversation. Workspace
finalization and the exact lease's normal destruction transition
succeeded.
- **Hosted workspace proof:** on the combined candidate containing
`fcb13399`, normal Retry preserved the original authenticated GitHub
request and completed a fresh native attempt. One later ordinary
same-head author reply then completed in the exact same workspace ID and
attempt directory, with no Retry/fresh directive. Its input tree digest
matched the Retry's finalized digest; final copyback preserved that
digest. The persisted assessment row and all five prior run records were
unchanged, exactly one new reply run completed, and the task returned to
In review with an empty queue and no live run. Both exact leases
completed normal verified destruction. This used real native OpenCode,
the exact DeepSeek/OpenRouter model and Daytona, without workspace-row
repair or a new remote marker.
- **Publication limit:** the workspace audit compares the persisted
assessment and run history. The ordinary reply exposed a separate
destination-summary fallback that changed the visible GitHub summary
despite the preserved assessment row. The publication track is repairing
that behavior; this proof does not claim the visible summary was
preserved.
- **Deployment boundary:** current-head CI/review and hosted same-source
Retry/ordinary-workspace reuse acceptance passed. The separate
visible-summary publication failure remains owned by the publication
track. Combined hosted UI confirmation of the native-agent Test path is
coordinated separately. Actual missing-proof UI/provider qualification
has passed in the isolated local instance above; prior hosted Stop and
same-input handoff proof remains separate. No merge or deployment is
performed by this pull request.

## Risks

Recovery state and termination receipts must match the exact source run
and allocation. Missing or foreign proof keeps the hold. The chat owner
must retain the original receipt and actor and recheck current
authority. Cleanup cannot grant authority or replay the stopped turn.
Existing native ownership, Stop, budget, permission and uncertain-effect
guards remain in place.

## Model Used

OpenAI GPT-6.1 Sol (`gpt-6.1-sol`) through Codex, with reasoning,
repository tools, source editing and test execution. The context-window
size 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 (affected suites and
isolated failing file pass; broad timeout retained 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 (current fcb has 53 successful
checks/two conditional Storybook skips)
- [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-26 22:21:58 -05:00
Devin FoleyandPaperclip 832c44f26a fix: resume user follow-ups that reassign a task (#17821)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The execution control plane saves user messages while an earlier run
needs recovery.
> - A user comment can also assign a task back to its agent.
> - That update creates an assignment wake, but continuation accepted
only comment wake reasons.
> - The saved question could stay blocked after workspace recovery and
confirmed process cleanup.
> - This pull request admits assignment wakes that carry a valid,
undelivered user comment through the existing continuation checks.
> - The same rule applies to new messages and saved requests already
waiting for recovery.

## Linked Issues or Issue Description

**What happened?**

A human follow-up that also assigns a task back to its agent produces
`issue_assigned`. After a native run stops during handoff, the
continuation policy rejects that wake reason. The question remains saved
behind the execution recovery hold even after the workspace is recovered
and the old sandbox is destroyed.

**Expected behavior**

Deliver the saved human question once after the existing stop,
ownership, authority, and execution gates pass. Assignment alone must
not grant permission to continue.

**Steps to reproduce**

1. Finish task work by assigning it to a human for review. Let the
native run stop with `issue_reassigned` and retain its recovery hold.
2. Recover the workspace and confirm destruction of the old sandbox.
3. Post a new human question while assigning the task back to the agent.
4. Observe that the assignment wake stays deferred instead of delivering
the question.

Refs: #17288, #14156. These related changes cover legacy reassignment
and assignments without comments. This change covers a human comment
carried by an assignment wake behind a native or legacy continuation
hold.

## What Changed

- Share the comment-bearing wake classification between continuation
admission and saved-request recovery.
- Accept `issue_assigned` only through the existing persisted
user-comment validation and execution gates.
- Add database regressions for new and saved assignment wakes,
concurrent recovery, single delivery, and unchanged prior run results.
- Check that bare assignments, invalid authors and comments, changed
ownership, active execution, and pending approvals or questions retain
their holds. Cover native and legacy cleanup paths.
- Update execution semantics and both recovery maps.

## Verification

- Red: both new and saved assignment-with-comment cases stayed deferred
without a successor on the original code.
- Green: the final continuation suite passes all 284 tests. The four
companion recovery, retry, and queue suites pass another 208 tests.
- `pnpm -r typecheck` and `pnpm build` pass.
- `git diff --check`, `node scripts/check-maps.mjs`, and `node
scripts/check-module-boundaries.mjs` pass.
- Apex review is 5/5 on `56c9c2d90dcda55b3f760946c9aba6aee448c361`, with
no unresolved review threads. The only earlier finding concerned the
contribution rule against new source comments; those comments were
removed.
- Full `pnpm test:run` is still running locally. CI is waiting for the
`paperclip-public-pr-x64` runner fleet; the superseded workflow was
cancelled so the current head could proceed. No passing CI claim is made
yet.
- Live recovery: corrected one existing saved wake to the supported
comment reason. The normal server admitted one successor with the exact
saved question and prior-run context, then started real tool calls. This
verifies admission on the existing deployment; the new source change is
not deployed yet. The resumed turn later failed with a separate provider
terminal event whose error was null, followed by a workspace restore
failure. The task is blocked again on that failure. Its useful answer
has not been delivered, so end-to-end completion remains unverified.

## Risks

- Assignment wakes now reach continuation validation when they carry a
comment. The persisted comment must still belong to the task and
requesting human, follow the stopped run, and remain undelivered.
- The existing stop proof, cleanup, current assignment, approval, pause,
budget, and competing-execution checks remain effective. Regression
cases cover both native and legacy paths.
- No schema migration or data rewrite is required for saved requests.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, source inspection, tool use,
and code execution. The runtime does not expose the exact 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
- [ ] 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-26 20:19:16 -07:00
DottaandPaperclip 32ec84db2e fix(native): restore plugin checkpoints for same-run retries (#17814)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Native sessions retain provider state across execution attempts.
> - A same-run retry can receive a replacement plugin task lease.
> - The executor loaded the old task checkpoint only for crash recovery.
> - An ordinary retry could fail its local authority check before
reading that checkpoint.
> - This change restores the checkpoint through the existing authorized
lease transfer.

## Linked Issues or Issue Description

**What happened?**

A native provider turn ended without its completion result. Paperclip
scheduled an exact-checkpoint retry on the same heartbeat run. The retry
acquired a new plugin task lease, then failed with
`runner_state_identity_mismatch: authority_indeterminate`.

The checkpoint restore branch required a `resume_dead_runner` restart
claim. Ordinary same-run retries have no such claim. A controlled
fixture with a retained remote checkpoint and no local lifecycle receipt
reproduces the error. The original controller file contents were
unavailable, so that part of the incident mechanism remains inferred.

**Expected behavior**

Load the retained plugin checkpoint after the existing policy proves the
lease transfer is safe. Keep the original run, session, and saved
control journal. Reject an unverified predecessor or a changed
checkpoint identity.

**Steps to reproduce**

1. Start a native session through a plugin task environment.
2. End its provider turn without a completion result and retain its
checkpoint.
3. Retry the same run on a replacement task lease without a restart
claim.
4. With no local lifecycle receipt, observe the authority error before
the checkpoint read.

**Paperclip version or commit**

Reproduced against source `507dc70be26c191e372f09e58dc0965e73bf8f57`.

**Deployment mode**

Native Runner with a plugin task environment. The observed provider used
ACPX Claude. The controlled executor fixture uses the shared native
boundary.

Related work: #17439 concerns recovery after controller replacement.
This fix uses the existing checkpoint contract and does not depend on
that PR.

## What Changed

- Restore prior plugin state for an ordinary same-run attempt when its
lease changes.
- Keep the existing company, agent, workspace, retained-state, and
termination checks.
- Extend executor regressions for ordinary retries, mismatched
checkpoints, and missing stop proof.
- Exercise the same-run checkpoint read through the database-backed task
API.
- Document the recovery rule and verification recipes.

## Verification

- Failing before: the ordinary retry reproduces
`authority_indeterminate`. Both new denial cases also fail because the
old branch never reaches the transfer policy.
- Passing after: all six focused checkpoint cases pass. The saved
control journal stays byte-for-byte unchanged. Rejected cases do not
construct a provider backend.
- `pnpm -r typecheck` passes.
- Map and module-boundary checks pass.
- All 858 executor, database lease-authority, plugin task, and ownership
tests pass. `pnpm build` passes.
- The broad local `pnpm test:run` attempt did not complete. It hit a
20-second timeout in the existing historical workspace-recovery HTTP
Retry case. That exact case then passed in isolation on the unchanged
head. The broad local attempt was stopped after the full CI matrix
passed; it is not counted as a local pass.
- Greptile reviewed current head
`5bc11fa978ab87d3d9c5d575fa9666185aaafc2c` at 5/5 with no actionable
findings. The full current-head CI matrix passed on attempt 2: 55 checks
passed, no pending or failed checks, and two expected Storybook jobs
skipped.
- The first broader local attempt could not bind embedded PostgreSQL
inside the sandbox (`EPERM`). It was stopped and restarted with loopback
access. This was a setup failure, separate from the observed red
regression.
- The first CI attempt failed two existing checks: the process-adapter
signoff happy path exhausted its run wait budget, and the workspace
export scheduler did not reach `complete` within 20 seconds. Both tests
pass in isolated local runs on the unchanged head. Both failed shards
passed on their single rerun without code changes. The initial failures
remain recorded. Their timing cause is not proven by the later passes.
- Live acceptance at the fixed revision has not run. The hosted
workspace still serves the earlier release. No deployment or retry of
the original task is claimed.

## Risks

The new checkpoint read runs only for same-run plugin recovery with a
replacement lease. Missing termination proof, changed scope, or a
changed Runner identity still stops execution. The lease resolver owns
these checks. The fix does not replay tools or change retry budgets. A
turn that omitted its completion result still needs the existing bounded
continuation to finish useful work.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository tools, and code
execution. The runtime does not expose a more specific backend 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-26 18:01:55 -05:00
DottaandPaperclip 718c73dafe fix(native): require verified remote process exit receipts (#17799)
Require fresh ESRCH or an exact matching zombie generation before treating a remote Runner as stopped. Preserve uncertain ownership through adoption and transport close, retire local controller resources after failed close, and check Node before remote launch.

Bind the shared transport change in Copilot profile 62 while retaining historical profile decoding. Add red-green process, permission, launch, adoption and cleanup regressions.

Thanks to @hsm-lv for the original investigation in #17073.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-26 21:39:04 +00:00
DottaandPaperclip 8373d78679 fix(recovery): register missing native preservation on Retry (#17800)
## Thinking Path

> - Paperclip lets people manage agents and their saved work.
> - Native runs can leave a prepared remote workspace after a control
deadline.
> - The deadline repair in #17788 records preservation for future
failures.
> - Older failed runs can still have an active lease without a
preservation owner.
> - Retry correctly refuses another executor, but nothing can finish the
missing preservation.
> - This pull request registers the exact prepared source through the
existing recovery services when the user retries.
> - The bounded maintenance worker can then stop the provider and save
its files before normal admission continues.

## Linked Issues or Issue Description

**What happened?**

A historical failed native run has a prepared workspace and an active
retained allocation. It has no workspace preservation record. Ordinary
Retry cannot prove that the old executor stopped and has no maintenance
owner to produce that proof.

**Expected behavior**

A deliberate Retry registers missing preservation for the exact unowned
source. Existing recovery proves termination and saves the files.
Existing admission then checks current user authority, effects, pause
and budget gates before one successor can enter the queue.

**Steps to reproduce**

1. Persist a failed native run with a prepared workspace, an active
lease and a terminal, unowned finalization.
2. Omit the workspace preservation record, as an older deadline handler
did.
3. Call the normal task Retry route. Before this fix, preservation
remains absent and Retry cannot continue.

**Paperclip version or commit**

The regression fails on `46de15ff4b99bbee59c38f32f4f30b80ad5bbd24`,
which already includes #17788.

Related: Refs #17788 (future deadline failures), #17741 (committed
recovery wakes), #17733 (connected Retry authority). #17773 adds source
diagnostics and does not repair this missing registration.

## What Changed

- Use native workspace preservation inside the existing issue-locked
Retry transaction. A live finalization owner remains held.
- Adopt a valid unowned preservation obligation through the existing
bounded maintenance service. Bind it to the deliberate Retry intent
without resetting an active generation.
- Add real PostgreSQL regressions for exact, mismatched and owned
sources. Verify that registration cannot create a termination receipt or
a successor.
- Exercise the HTTP route, worker, real temporary file merge and queue
admission with the stop reply held. Duplicate requests admit one queued
turn after preservation.
- Extend connected GitHub and Telegram Retry coverage for missing
historical preservation. Document the contract and access boundaries.

## Verification

- Red: the new historical-state regression fails on the base commit
because `workspaceRestoreRecovery` remains absent. The mismatched and
owned negative controls pass.
- Green: the three focused historical-state cases pass with the fix
against isolated local PostgreSQL 17.
- The local HTTP journey restores saved files and retains newer host
edits. It admits no successor while the provider stop reply is held. It
admits one successor after verified preservation. The real GitHub and
Telegram admission paths wait behind that recovery.
- `pnpm -r typecheck`, `pnpm build`, `node scripts/check-maps.mjs` and
`git diff --check` pass.
- Green: 152 tests across four relevant suites, including all connected
Retry safety cases. The six new cases pass again on final head
`b8900801a3181fa673ea45f1129f968bbb08d1fd`.
- Final-head GitHub checks: 55 successful, two intentionally skipped
Storybook checks, and one neutral Paperclip bot review. Greptile is 5/5
with no unresolved threads. The Paperclip bot stopped before finishing
and supplied no approval.
- The full local command imported an earlier fixture revision before its
assertions were corrected. It reported those two stale connected-fixture
assertions and later hit the host PostgreSQL semaphore limit again. It
was stopped after final-head CI completed. It is not counted as a full
local pass; the complete CI test lanes passed on the final head.
- The first local rerun hit the Mac PostgreSQL semaphore limit before
assertions. Verification moved to an isolated disposable PostgreSQL
container. Early assembled fixtures also exposed the existing
destruction-cleanup gate; the retained-workspace fixture now uses its
actual `keep_running` disposition. This fix does not weaken cleanup.

## Risks

- Retry now registers maintenance for historical native failures. Exact
descriptor, allocation and coordinator checks remain owned by the
existing preservation service. Physical recovery still requires its
existing exclusive owner and provider stop receipt. Registration alone
grants no execution authority.
- No database migration or provider protocol changes. No model, trust,
assignment, retry-budget or external identity changes.
- Local recovery uses real database, filesystem, HTTP, worker and
admission code. Only the provider lifecycle boundary is simulated. No
hosted allocation or live model turn has been verified for this new
change.
- The workspace recovery test file crosses 1,000 lines because this case
shares its real database, filesystem and assembled route fixture.
Keeping it there avoids duplicating that fixture or extracting unrelated
production behavior.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository tools and code
execution. The exact model identifier and context-window size are not
exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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-26 16:22:36 -05:00
DottaandPaperclip 46de15ff4b fix(recovery): preserve native workspaces after control deadlines (#17788)
## Thinking Path

> - Paperclip lets people manage agents and their saved work.
> - Native runs can prepare a remote workspace before provider
execution.
> - A finalization deadline can stop a run outside its normal exception
path.
> - That handler marked the run failed without recording its prepared
files for recovery.
> - Retry then waited for a lease whose preservation had no durable
owner.
> - This pull request records the exact source in the deadline
transaction through the existing recovery service.
> - Existing containment and file recovery can then settle the source
before another executor starts.

## Linked Issues or Issue Description

**What happened?**

A native run failed at its control deadline with a prepared workspace
and no accepted result. Its environment lease remained active. The run
had no workspace preservation record. A saved Retry waited for remote
cleanup without a preservation operation to finish that cleanup.

**Expected behavior**

Record unfinished files when the deadline marks a native run failed. Use
the existing stop-and-retain worker. Keep accepted results, exact source
binding, and current task ownership authoritative.

**Steps to reproduce**

1. Persist a native run with a prepared workspace, an active lease, and
an expired control deadline.
2. Reconcile the deadline twice.
3. Inspect the source lease and the run's workspace recovery record.

**Paperclip version or commit**

Reproduced against `a67a18c171816b097f46eae78fc58c7e55c9c565`.

**Deployment mode**

Hosted native Runner with a remote sandbox. The regression uses real
local PostgreSQL and no provider mocks.

Related: Refs #17741 for committed Retry delivery. Refs #17073 for
remote process-exit verification. This change records file preservation
after a deadline; it does not change either policy.

## What Changed

- Call the existing native workspace preservation service in the
transaction that fails an abandoned run.
- Preserve files after reassignment without changing the current task
owner or state.
- Add real-database cases for prepared, finalized, mismatched,
reassigned, and accepted-result states. Repeated reconciliation retains
one source obligation and creates no successor.
- Document the deadline behavior and its existing access boundaries.

## Verification

- Red: on unchanged production code, the prepared-workspace case failed
because its recovery record was null. Finalized and mismatched controls
passed.
- Green: all 10 deadline reconciliation tests pass. The existing native
workspace recovery suite passes all 48 cases.
- `pnpm -r typecheck` passed.
- `pnpm build` passed.
- Map checks, module-boundary checks, and `git diff --check` passed.
- Current-head CI passes on `23bf2aa4b50d2c4b74a9ee2ee47034ae542d25c2`:
all GitHub Actions CI lanes, including grouped/serialized tests, Runner
verification, typecheck, build, E2E, and Canary Dry Run, succeeded. Two
Storybook-only checks were skipped. Greptile is 5/5 with no findings.
Superagent Security Scan also passed. Entire Gates reports passing
checks/findings and fails only because no reviewer has approved trail
#10.
- The first server shard 11/12 attempt passed all 1,750 assertions but
reported an unhandled early process-exit rejection in the unchanged
workspace-exposure fixture. Its focused local suite passed (28 passed, 3
skipped); one CI rerun passed.
- Full local `pnpm test:run` was started, then interrupted after the
complete CI lanes passed to avoid duplicate work. No full local suite
pass is claimed. Local targeted tests, typecheck, and build passed.
- On the affected staging instance, the existing deployed preservation
helper registered the exact failed source. Normal Retry then drove
actual sandbox stop, file recovery, and a persisted successful recovery
receipt. A successor reached a separate remote process-fingerprint
failure before provider execution. The automatic deadline change is not
deployed; task execution recovery remains incomplete.
- Initial local setup attempts did not reach product assertions: skipped
dependency lifecycle scripts left native PostgreSQL library links
absent, and the restricted shell blocked localhost binding. The
package's link hydration and an authorized isolated listener resolved
those setup prerequisites. One new negative-control assertion also
required null-safe access; the final red run retained only the intended
behavioral failure.

## Risks

- File preservation must not certify provider termination. The existing
stop receipt, source binding, copyback, and admission checks remain in
force.
- Preservation failure rolls back the deadline transaction, leaving its
durable deadline eligible for another bounded attempt.
- Reassignment preserves old files without changing the new owner's
task. Accepted results retain their current finalizer.
- No schema, credential, provider, or polling change. Historical failed
runs still require scoped recovery; this patch does not replay them.

## Model Used

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

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass (targeted suites; full
local suite interrupted after CI passed)
- [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 (CI lanes pass; external Entire
reviewer-approval gate remains pending)
- [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-26 14:18:53 -05:00
DottaandPaperclip 063a498b54 Bound native OpenCode assigned-tool catalog projection (#17740)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native agents use assigned connection tools through a governed
gateway.
> - OpenCode received the gateway catalog directly, outside the bounded
server tool projection.
> - A large catalog can exceed a provider grammar compiler's capacity
even when it fits the transport limits.
> - Synthetic requests reproduced the Fireworks grammar timeout on the
configured DeepSeek model.
> - This pull request gives OpenCode two assigned-tool discovery and
call schemas through the existing server relay.
> - The gateway keeps the same grants, validation, approvals,
revocation, and audit rules.

## Linked Issues or Issue Description

**What happened?**

A native OpenCode run could fail during provider grammar compilation
before its first assigned-tool call. OpenCode advertised the full
assigned gateway catalog. A synthetic catalog of 741 tool schemas
reproduced `Grammar processing took too much time` on Fireworks through
OpenRouter with `deepseek/deepseek-v4.1-flash-1017`. The same model and
backend accepted a compact two-tool discovery/call catalog. This
reproduces the mechanism; it does not replay a historical private
request.

**Expected behavior**

Assigned-tool discovery must keep provider grammar size bounded. Every
call must retain the run's original gateway binding and permissions.

**Steps to reproduce**

1. Give a native OpenCode run a large assigned connection catalog.
2. Use a provider that compiles tool schemas into a constrained output
grammar.
3. Observe the full catalog in the provider tool surface before this
change.
4. After this change, observe only `paperclip_search_assigned_tools` and
`paperclip_call_assigned_tool` for assigned tools. Discover and call a
permitted tool through its pinned alias.

**Paperclip version or commit**

Source checkout based on current `master`.

**Deployment mode**

Native local or sandbox execution with OpenCode 1.18.34.

Related work: Refs #14258 (discovery paging), #16678 (local adapter MCP
launch), and #17688 (gateway capability advertisement). This change
covers native OpenCode catalog projection.

## What Changed

- Route native OpenCode assigned tools through the existing server-owned
authenticated relay.
- Use bounded discovery/call schemas for OpenCode, including small
catalogs. Preserve transport capacity checks.
- Keep assigned gateway credentials on the controller. Ignore gateway
and credential values in tool arguments.
- Preserve exact declared MCP operation names through admission and
driver dispatch. Resolve transport prefixes only against the closed
catalog. Reject unknown or shortened names.
- Refresh former OpenCode direct-catalog and shortened-name checkpoints.
Keep the existing fingerprints for other providers.
- Add local and remote wiring, catalog capacity, alias, scope,
work-mode, approval, and revocation tests.
- Allow the native test files to use a validated dedicated external test
database when embedded PostgreSQL is unavailable.
- Document the assigned-tool execution contract.

- Bound OpenCode discovery pages to 32 KiB and large-schema chunks to
16K characters. Keep other harness budgets unchanged.
- Test medium pages and escaped large schemas through the actual HTTP
MCP bridge on the explicit Runner testing surface.

## Verification

- Red before the catalog fix: OpenCode did not create the server relay,
and forced compact delivery advertised 1 or 741 direct schemas.
- Red before the alias repair: HTTP MCP changed declared operation
names, and the driver sent `search_assigned_tools` instead of the
authorized `paperclip_search_assigned_tools`.
- Current repair tests: 112 OpenCode/proxy tests pass, plus one actual
Rust/proxy/HTTP MCP regression. Two checkpoint regressions fail with the
former scoped revision and pass with the new revision. Other providers
keep their prior fingerprints.
- The combined QA candidate with this exact PR head passes all 916
native tests and all 113 transport tests on supported Node 24.22. An
earlier Node 26.4/macOS run passed the 113 assertions but had an
uncaught networking error. That non-clean run remains recorded; its
stack matches [the upstream Undici
defect](https://github.com/nodejs/undici/issues/5544). No production
workaround or error suppression was added.
- `pnpm -r typecheck`, `pnpm build`, and `git diff --check` pass on
`bc41ba72be2195eabeb0d7727227ee0cd02e8532`. The maps structural check
passes.
- The local `pnpm test:run` attempt stopped after host embedded
PostgreSQL initialization failures. The relevant database suites pass
with isolated PostgreSQL 18. All repository CI gates, including
aggregate `ci / verify`, pass on the current head.
- Synthetic live provider comparison: 741 invented schemas produced the
exact Fireworks grammar timeout in 20.9 seconds. Two compact invented
schemas produced a discovery call in 585 ms on the same model and
backend.
- The first hosted QA attempt exposed the alias failure described above.
On the patched source, a real local Rust runner → proxy → OpenCode
1.18.34 → exact DeepSeek smoke completed discovery, one synthetic
gateway read, and `paperclip_finish`, then emitted `turn/completed`. It
used the production compact projection and unchanged Rust allowlist.
- Two earlier local smoke attempts exposed missing terminal authority
and completion-contract fields in the smoke harness. Those failures
remain recorded. The corrected ordinary bindings produced the clean
result.
- Fresh Greptile: 5/5 on `bc41ba72be2195eabeb0d7727227ee0cd02e8532`,
with no new actionable findings or unresolved threads.
- Hosted QA publication and lifecycle acceptance remain pending. The
separate deployed-agent `Paperclip Review` check is also pending; it
follows a task that failed on the earlier deployed runtime.

- Local OpenCode/OpenRouter acceptance at `bc41ba72`: 530 synthetic
tools were authorized and installed. A simple model-ID reply completed
in 17.3 seconds with no app calls. A discovery task completed in 37.6
seconds, used the search/call relay, made one upstream call, and
returned the exact synthetic value. This is local source acceptance, not
a staging sandbox timing comparison.
- The large-result review finding has a failing-before/passing-after
HTTP MCP regression. Five medium schemas now page without invalid JSON.
A large schema with escaped text reconstructs exactly through bounded
chunks. All 35 assigned-tool tests and the server typecheck pass after
this follow-up.
- Final head `d84ce4856a6e65a35ad5fb8b561492598b525f4e`: all CI gates
pass. Greptile and Paperclip Review both rate the change 5/5. No review
threads remain open. The final full build, server typecheck, and 35
assigned-tool tests pass locally.

## Risks

- OpenCode uses discovery before it calls an assigned tool, including
for small catalogs. This can add a model turn.
- An existing OpenCode provider checkpoint rotates once because it
retains the former tool declarations. Paperclip task history remains
available.
- The historical filtered catalog was not captured. The synthetic
comparison proves the failure mechanism, not the exact cardinality of an
earlier private request.
- No model, provider route, permission, or automatic retry policy
changes.

## Model Used

The bounded-result follow-up also used OpenAI Codex (GPT-6 family). The
exact backend model ID and context-window size were not exposed for that
session. It used reasoning, code execution, and tool use.

OpenAI `gpt-6.1-sol` in Codex, with reasoning, tool use, code execution,
and source inspection. 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-26 10:49:23 -05:00
DottaandPaperclip 0373f9f261 fix(recovery): wake retained workspace preservation after Retry (#17741)
## Thinking Path

> - Paperclip lets people manage AI agents and their saved work.
> - A stopped run can owe workspace preservation before saved input can
continue.
> - Retry saved a recovery request but did not wake the worker that owns
preservation.
> - Older recovery records could also take the bounded scan slots before
a due request.
> - A second Retry could change the revision while the physical owner
was still saving files.
> - This pull request wakes a separate worker after commit, selects due
work first, and keeps the active owner's revision.
> - Saved work can progress without replaying the stopped provider turn
or blocking other finalization.

## Linked Issues or Issue Description

**What happened?**

An operator approved a saved design and selected Retry after native
workspace finalization failed. The API accepted one canonical request.
The source remained scheduled at zero preservation attempts for more
than 30 minutes. No new provider turn started. A local regression also
found that repeated Retry during export invalidated the physical owner's
completion revision.

**Expected behavior**

Committed Retry must wake bounded source preservation. Due recovery must
progress ahead of passive cleanup obligations and unrelated history.
Duplicate requests must keep one physical attempt. Saved input must
still pass normal admission after verified recovery.

**Steps to reproduce**

1. Retain a terminal run with an exhausted workspace recovery and saved
authorized input.
2. Let the delivery coordinator become quiet, then request Retry.
3. Observe the recovery state and physical preservation receipt. Repeat
Retry while the export is held.

**Paperclip version or commit**

Observed on served commit `4b89eec644d6e1a99d58926e77ae58927c3fa87b`.
Local baseline: `a1f77821b9cd5c9c846fdf7a0fa5cc620ad2b6bb`.

Related work: Refs #17053, #17267, #17094. This change repairs explicit
Retry delivery. It is separate from the run instruction projection in
#17724 and historical unsafe-export discovery in #17687.

## What Changed

- Notify a dedicated workspace recovery duty from the transaction that
saves Retry, recovery admission, adoption, or recovery progress.
- Reconstruct work and saved retry deadlines at startup. Fill each free
export slot independently, track physical work for idle drain, and join
the dedicated worker's exports on shutdown.
- Drain one bounded pass through completed cleanup and saved-input
obligations. Restart that pass after committed changes without
repeatedly rechecking paused input.
- Share source eligibility between deadline selection and candidate
consumption. Keep resolved files-only obligations and ignore unrelated
resolved or cancelled history.
- Select due unfinished preservation before passive historical records.
Rotate past source locks held by other workers and retain the indexed
source lookup.
- Bind the canonical Retry intent without changing an active physical
attempt's generation or revision. Preserve that binding across later
progress writes.
- Add real producer, physical preservation, startup, deadline, idle, and
independent finalization regressions. Document the delivery contract.

## Verification

- Red controls: Retry without a notification stayed scheduled; duplicate
Retry invalidated the active export; whole-batch waiting stranded a
third Retry; a scan without cursor rotation starved a source behind two
actual advisory locks. Removing unfinished-first selection left HTTP
Retry scheduled behind five real recovered sources still owing cleanup.
The fixture explicitly verifies their eligibility with the worker's
actual query filter.
- Further red controls: the previous worker visits only two of three
eligible completed sources. Disabling the committed-change cursor reset
misses a new earlier source arriving while two callbacks are held. Both
pass with the final worker.
- Green: the public Retry HTTP route, registered production worker, real
file copyback, duplicate canonical receipts, and public recovery receipt
reload passed on an owned local fixture. Its agent is paused to keep
this files-only check from starting a provider turn.
- Full workspace recovery suite passed 56 tests, including the
foreign-lock regression.
- Execution-control lifecycle suite passed 24 tests. It covers idle SQL,
stored deadlines, cancelled and resolved history, independent
finalization, and shutdown. One, three, and seven paused completed
sources are each visited in a bounded pass, then stay deferred without a
successor or SQL during 90 seconds of fake-clock idle time. A committed
earlier source is evaluated when it arrives during two held callbacks.
- Both GitHub and Telegram `automatic_running` continuation regressions
passed with the new canonical intent binding.
- `pnpm -r typecheck` passed.
- `pnpm build` passed.
- `node scripts/check-maps.mjs` and `node
scripts/check-module-boundaries.mjs` passed.
- Local `pnpm test:run` could not complete: embedded PostgreSQL
repeatedly failed to start. The owned run was stopped after this setup
failure repeated. Latest-head CI supplies broad coverage.
- Earlier setup failures included a localhost sandbox denial and loss of
the owned disposable Docker database. These were separate from the
product red controls above.
- Local database tests use an explicitly named disposable PostgreSQL
`_test` database. That opt-in database is reset before the suite.
Default embedded test behavior is unchanged.
- Exact-head CI is green at `ef7dcf1ba1060864c9d216c33b51f4d9f8f57fbd`;
Greptile and Ponytail both rate it 5/5, with no unresolved review
threads. Earlier CI caught the indexed discovery assertion and two exact
chat intent-binding regressions; all three were fixed and pass locally
and in CI. A separate chat shard lost contact with its self-hosted
runner without a product assertion result. A later shared runner
shutdown interrupted several jobs; one infrastructure rerun passed
before the final test-and-worker follow-up triggered a fresh green run.
- No staging deployment is part of this PR. The original approval
remains saved; deployed recovery still needs acceptance after rollout.

## Risks

- More recovery writes now wake a bounded maintenance duty. Completed
history must not keep it active. Idle and deadline tests cover this
boundary.
- Resolved files-only obligations must remain eligible without granting
replay. The startup regression checks that path.
- Provider stop proof, copyback ownership, source binding, retry
budgets, and normal execution admission remain required. There is no
schema or public API change.

## Model Used

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

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-26 10:28:12 -05:00
DottaandPaperclip ad861a7f94 fix(runner): allow bounded controller admission before welcome (#17735)
## Thinking Path

> - Paperclip manages AI agents and preserves their work across
execution failures.
> - Native Runner authentication exchanges a one-use bootstrap ticket
for a connection lease.
> - The controller must admit the connection and persist that lease
before it sends welcome.
> - Runnerd waited only two seconds for welcome, even when its
connection setup budget allowed more time.
> - A slow valid welcome therefore looked like a consumed-ticket failure
before provider work could start.
> - This pull request uses the existing ten-second connection setup
budget for welcome.
> - The benefit is reliable startup during bounded controller delays,
with the same authentication and replay checks.

## Linked Issues or Issue Description

**What happened?**

A valid native Runner bootstrap can fail while the controller completes
admission. The runner then reports `bootstrap connection failed closed;
provide a fresh one-use ticket`. A later recovery attempt can report an
indeterminate authority and hide the startup error behind a finalization
deadline.

**Expected behavior**

A valid welcome can arrive within the connection setup budget. An
earlier execution or reconnect deadline still ends the wait. A missing
or invalid welcome never permits bootstrap-ticket replay.

**Steps to reproduce**

1. Run the real Runner WebSocket transport against an authenticated
controller.
2. Hold the welcome for three seconds after the client proof arrives.
3. Before this change, authentication fails with `WebSocket
authentication message deadline elapsed` and `bootstrap_maybe_consumed:
true`.
4. With this change, the runner accepts the bound lease and restores the
normal read timeout.

**Paperclip version or commit**

Reproduced against `a1f77821b9` before the production change.

**Agent adapter(s) involved**

Native Paperclip Runner. The transport is shared by its provider
drivers.

Related: #17401 reduces repeated work in warm-recovery admission. This
change covers delayed welcome delivery for fresh authentication too. It
does not replace that optimization. #17594 addresses a separate lifetime
failure after controller outages.

## What Changed

- Use the ten-second connection setup budget for the authenticated
welcome.
- Keep the execution/reconnect deadline as an earlier bound.
- Add real WebSocket regressions for a delayed welcome and a shorter
lifecycle deadline.
- Add a real Runner process test proving a delayed admission starts one
provider and keeps it through two turns.
- Document the startup contract and its recovery verification scope.

## Verification

- Red: the delayed-welcome regression failed against unchanged
production code with a message deadline and `bootstrap_maybe_consumed:
true`.
- Green: all 402 Runner core library tests pass. The suite includes
authentication, ticket fencing after a lost welcome, identity rejection,
replay, and no repeated provider effects.
- Map and module checks pass.
- Workspace `pnpm -r typecheck` and `pnpm build` pass. The new process
integration test passes (one provider process, one thread, two turns,
suspended with an empty outbox).
- Live acceptance at this branch's production Runner build passes on
macOS arm64 with pinned OpenCode 1.18.34 and
`openrouter/deepseek/deepseek-v4.1-flash-1017`: delay admission by three
seconds, create a file, suspend, resume the same provider session under
a fresh run authority, read the first file, and write a second file.
Both useful outputs match exactly; final runner state is suspended.
- Preserved setup failures: a non-pinned executable was rejected, and
the debug binary exceeded cold provider startup admission. The pinned
executable and release binary passed. The first harness also tried to
reuse a completed task object; the follow-up used the normal resume
path. Exactly two paid model turns ran.
- The full local `pnpm test:run` was attempted and stopped after the Mac
exhausted PostgreSQL semaphore capacity (`could not create semaphores`);
it is not a local full-suite pass.
- Current-head CI is green at
`df5d5494c9f9f444114aaf12e095a554110d2e14`: all checks passing or
non-applicably skipped, zero failures or pending checks. The additional
Superagent security scan triggered by ready-for-review also passed.
These include all general/serialized test shards, Runner Rust/Vitest,
E2E, typecheck, build, and the final verify gate. Greptile reviewed that
exact commit at 5/5 with no findings or inline comments.
- This proves the actual native Runner/provider path locally. It does
not prove rollout or recovery on a hosted Linux/amd64 execution
environment.

## Risks

- A stalled welcome can take up to eight seconds longer to fail when the
configured lifecycle allows it. Shorter execution/reconnect deadlines
still take precedence.
- Authentication proofs, credential expiry, identity binding, and
one-use-ticket fencing remain enforced.
- This does not repair provider-side grammar errors or authorize replay
of an indeterminate historical run. Recovery of existing work requires
the normal stopped-owner and retained-state checks.
- No schema or migration change. A new Runner binary must reach the
execution host before this change affects remote runs.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository inspection, code
execution, and browser tools. The exact serving submodel identifier and
context window 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
#` / `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-26 10:25:59 -05: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
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
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 bf223596ab fix(runner): bootstrap fresh sessions in replacement sandboxes (#17320)
Reuse the untouched-session proof and atomic directory claim when a sandbox
replacement carries a provider session that has never run. Retain verified
restore for saved backups and all prior or ambiguous session activity.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-23 15:35:24 -07:00
Devin FoleyandPaperclip 837add0caa fix(recovery): deliver review-path repair instructions to agent prompts (#17353)
## Thinking Path

> - Paperclip keeps agent tasks moving through owned execution and
durable waiting paths.
> - A consumed review path gets one repair wake before board escalation.
> - That wake records an instruction to save a new monitor or another
real review path.
> - The canonical wake builder omitted that instruction from the agent
prompt.
> - This pull request delivers it through the existing
disposition-repair section.
> - The repair limit and execution authority stay unchanged.

## Linked Issues or Issue Description

**What happened?**

An agent opened a pull request and promised to watch CI and external
review. It ended without scheduling a monitor. Its bounded review repair
also ended without a maintained path, so the task became blocked for a
board decision. The saved repair context contained the instruction, but
the canonical wake payload and rendered prompt did not.

**Expected behavior**

The repair prompt tells the agent to save a one-shot monitor before
promising another check. Both fresh and resumed sessions receive that
instruction and the one-attempt limit.

**Steps to reproduce**

1. Create a review-path recovery with `decideIssueReviewPathRecovery`
for a task whose review path was consumed.
2. Pass its recorded context through `buildPaperclipWakePayload` and
wake serialization.
3. Render a fresh or resumed prompt. Before this fix, it has the wake
reason but no repair instruction.

**Paperclip version or commit**

Base: `9c442cf48e6`. Current head:
`f38d65891c8d9f2b38eb76a8578e870debc2b1e6`.

**Deployment mode**

Observed on managed sandbox execution with the legacy Claude ACP
adapter. The corrected canonical payload serves the shared adapter
prompt path.

Refs #15764 introduced the bounded exhausted-repair disposition. This
change preserves that limit. Open monitor-retry work such as #17171
addresses an already scheduled check; this defect occurs before a check
exists. Searches found no duplicate for this missing prompt field.

## What Changed

- Build the review-path repair disposition in its owning recovery
module, using the server instruction and fixed one-attempt bound.
- Project it into the existing canonical `dispositionRepair` field. No
new adapter field or execution protocol is added.
- Ignore arbitrary replacement instructions and attempt-limit values. A
pending repair keeps its instruction across a connection-pool retry that
changes the wake reason. Ordinary wakes without the typed repair marker
ignore raw instruction text. Existing legacy disposition repairs retain
their values.
- Add a regression across the real recovery decision, wake builder,
serializer and fresh/resumed prompt selectors. Update execution guidance
and the affected recovery maps.

## Verification

- Verified red before the source fix: two regression cases failed
because the instruction was absent; the sibling repair case passed. The
same cases pass after the fix.
- Passed 194 focused and safety tests across wake preparation, review
recovery, adapter prompt handling, and database-backed liveness
escalation. The connection-pool retry case also failed before its
correction and passes after it.
- Replayed the recorded incident wake through the fixed boundary. The
old canonical prompt lacked its saved instruction. The corrected
serialized prompt includes it with a 1/1 bound. This replay did not
execute a provider turn or change the source task. Private evidence
stays outside the repository.
- Passed `pnpm -r typecheck` after the pool-retry correction. Passed
`pnpm build` on final head `f38d65891c8d9f2b38eb76a8578e870debc2b1e6`.
Map checks and `git diff --check` pass.
- [Full
CI](https://github.com/paperclipai/paperclip/actions/runs/40005890857)
passed on that head: 54 checks passed and two optional checks were
skipped. This includes the full test shards, typecheck, build, and
browser suites. The local `pnpm test:run` attempt was stopped after the
CI test shards passed. No complete local full-suite pass is claimed.
- [Apex
review](https://github.com/paperclipai/paperclip/pull/17353#issuecomment-6372160694)
is 5/5 on the final head, with no open findings. The first review found
the pool-retry case. Its regression and correction are included. The
branch has no merge conflicts.
- Test setup first required the plugin SDK build, then an empty
credential-history database boundary. Those setup errors are not the
claimed red. The temporary recorded-wake probe was removed before final
validation.
- Hosted follow-up acceptance is not run. The source task remains
blocked and needs a retry after rollout. This PR does not claim that
prompt delivery itself schedules a monitor or that every model will
follow the instruction.

## Risks

- This fixes instruction delivery. The agent must still save a valid
monitor or another waiting path. The existing bounded escalation remains
if it does not.
- The wake stays a normal-model repair. It does not become status-only
recovery and can continue useful work. Existing ownership, approval,
budget, pause, and monitor admission gates remain in place.
- No schema, credential, telemetry, API, model, or retry-budget change.

## Model Used

OpenAI Codex, based on GPT-6, assisted with diagnosis, code editing,
shell execution and verification. The exact serving model ID and
context-window limit 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 (194 focused and safety
cases; full coverage passed in CI)
- [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-23 12:07:07 -07:00
Dotta 46760cc588 fix(chat): honor dismissal for all composer requests (#17218)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task conversations show requests in the feed and above the message
composer.
> - Earlier fixes kept dismissed questions in history, but excluded
completion reviews and other request kinds.
> - Closing those forms left a pending badge, and the server could still
demand a response.
> - This pull request saves human dismissal and removes the request from
composer reminders and ordinary completion gates.
> - People can continue the conversation and reopen the request from
history.

## Linked Issues or Issue Description

Related work: #15229 and #15392.

**What happened?**

Dismissing a completion review closed its form but left it counted as
pending. It could return after reload, block a retry, or cause the agent
to demand another response. Raw runtime requests could also lose their
only usable control when dismissed.

**Expected behavior**

Dismissal leaves an accessible request in the feed and clears the
composer form and pending count. It persists after reload. It does not
submit an answer, approve an action, or wake the agent.

**Steps to reproduce**

1. Open a task with a pending completion review or another composer
request.
2. Dismiss the request, then reload the task.
3. Check that the composer stays clear and the feed entry can reopen the
request.

## What Changed

- Add nullable dismissal fields and a human-only dismissal endpoint with
company, private-task access, resolver, and locked review-policy checks.
- Persist saved-request dismissal on the server. Keep browser
persistence for raw runtime requests and existing question dismissals.
- Keep confirmations, choices, item verdicts, suggested tasks, and
runtime requests accessible in the feed.
- Exclude dismissed ordinary requests from completion and retry gates.
Tell agents not to repeat deferred requests.
- Preserve tool, secret, connection, plan, company-policy, and
configured review authorization gates. Expire deferred non-question
requests when their task completes.
- Preserve the selected form when another dismissal finishes saving.
Keep reopened reviews visible through delayed comment refreshes.
- Add a completion-review Storybook test drive and update design, API,
database, and capability documentation.

## Verification

- `pnpm -r typecheck`: passed. The final UI changes also passed the UI
typecheck.
- `pnpm build`: passed. The final UI changes also passed the UI
production build.
- Focused UI and transcript tests: 550 passed across nine files after
merging the current base branch.
- Focused database, completion, and prompt tests: 105 passed across
three files, including two review-policy regressions that failed before
the fix. The tests reject a review requester excluded by current policy,
preserve the pending gate on denial, and permit an independent reviewer
to defer the request.
- Route regression suite: 81 passed, including denial before reading or
mutating a private-task request.
- `pnpm check:token-gates`: passed.
- Map and Storybook structural checks: passed; 30 map/catalog script
tests passed.
- Browser: used the production task-thread components in the
completion-review fixture. Dismissed, reloaded, reopened from the feed,
and dismissed again. No pending badge or composer takeover remained.
Story callbacks simulate API saves; separate server tests cover
persistence and permissions.
- `pnpm test:run`: stopped after the general-server group reported one
failure in `workspace-runtime.test.ts`: "returns a bounded structured
conflict and exposes only same-company workspace references". The same
test passed in isolation (1 passed, 178 skipped). The cause of the
full-suite failure is not established. The full PR CI suite subsequently
passed on e243cbb001; its two authorization review findings are fixed in
9e1b4478b3. Fresh [full
CI](https://github.com/paperclipai/paperclip/actions/runs/39871944846)
passes on 9e1b4478b3, including the previously failing server and
browser shards. All 54 executed checks passed; two optional Storybook
deployment/visual jobs were skipped. The legacy security status also
passed. Fresh Greptile review is 5/5, the security scan passes, and no
review threads remain open. This full CI run settles the merge gate; the
earlier local workspace-suite failure remains recorded above because its
cause was not established.
- Additional verification: the original failing browser journey passed
locally after the delayed-refresh fix. The expanded page-reload case
also passes on 9e1b4478b3 (52.7s): dismiss, reload, reopen, approve, and
receive the actual tool failure in the continuing task. It exposed a
fixture reading an accepted request before tool execution finished; the
fixture now waits for a stored execution result. The first reload
attempt stopped on a still-loading page at the default five-second
assertion; the reload now uses the suite’s navigation allowance. A
controlled server-restart run timed out waiting for server health before
the UI assertions. Local route-suite runs timed out during concurrent
module loading; sequential imports passed all 81 tests, including the
privacy regression.
- Initial verification failures: a missing SDK build and a story fixture
type error were fixed. PostgreSQL bootstrap then failed because the host
had exhausted its semaphore allowance. After explicit operator approval,
64 verified abandoned PostgreSQL semaphore sets were reclaimed; no live
process was stopped. The database regressions then passed. The
raw-runtime regression exposed a missing feed control; the
implementation now preserves that entry and the regression passes.

## Risks

- Apply the additive database migration before the new server code. It
adds two nullable columns and does not change existing decisions.
- A dismissal is shared task attention state. Only a human with task
read access and current resolver/review authority can persist it. Review
policy is checked under the task lock. The operation is idempotent and
records the dismissing user.
- Raw runtime dismissal remains specific to a browser. Saved interaction
dismissal persists across browsers.
- A deferred review is not an approval. Tests retain protected-action
and configured review gates.
- The browser walkthrough uses a fixture. This branch has not been
deployed to a live instance.

## Model Used

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

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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
2026-10-22 18:37:36 -05:00
6dda8cc2b7 fix: preserve Stop authority and qualify complete recovery journeys (#17105)
## Thinking Path

> - Paperclip lets people manage agents that do useful work.
> - Users need complete tasks, clear questions, retained context, and
dependable recovery.
> - Separate run tests do not prove that the complete user experience
works.
> - Successful retries can hide earlier failures and missing evidence.
> - This PR extends the existing Product E2E harness with three explicit
journeys.
> - It adds a qualification checkpoint over the full retained result
pool.
> - Independent artifacts, state, rendered conversations, and delivery
receipts establish each outcome.

## Linked Issues or Issue Description

Refs #17088, #17089, and #17046. This PR repairs two Stop lifecycle
defects and combines the qualification changes previously reviewed in
#17095, #17090, and #17099. Daily operations remain with #17051. It adds
no second scheduler or recovery engine.

**What happened?**

A completed run can still leave a user without useful output, an
understandable question, or a working continuation. Existing isolated
tests and best-attempt reports do not establish the combined behavior.

**Expected behavior**

A necessary question includes its explanation and resumes once after an
answer. Stop prevents automatic continuation until the user resumes.
Restart during finalization preserves the accepted provider result and
delivers it once. Qualification requires independent evidence for six
journeys on both agents through Slack and Paperclip.

**Steps to reproduce**

List the necessary-question, followup-stop-resume, and
finalization-recovery suites using the documented selectors. Run one
exact cell with the configured model and target. Supply a private
qualification manifest to the existing report command to grade all
retained attempts. Missing proof and incomplete coverage must fail.

## What Changed

- Wake existing accounting in the same committed native Stop closure. A
receipt that arrives before closure must still settle once; incomplete
receipts remain held.
- Require a later user message before native Stop can promote earlier
saved input. Preserve those earlier messages for the authorized
resumption.
- Add explicit necessary-question cells. Verify the actual prompt and
context, answer, one bound continuation, and the delivered document link
and revision.
- Add explicit follow-up, Stop, and later resumption cells. Verify a
live owned provider turn, cancellation acknowledgement, at least five
seconds without an automatic path, retained direction and workspace
context, and an actual browser download.
- Add explicit Daytona finalization-recovery cells. Stop the owned
controller after public result acceptance and during workspace
finalization. Require the original result, restored file hash, resumed
operation, one provider turn, and one receipt-bound delivery.
- Share the receipt-based delivery observer across both recovery
journeys. Keep model prose separate from the authoritative delivered
comment identity.
- Add a 24-cell qualification checkpoint over every retained attempt.
Require three fresh candidate passes per cell and later staging proof
with exact source, runtime, model, settings, instructions, and
independent evidence.
- Reject absent or unsafe manifest evidence. Preserve failed attempts
and incomplete observations in the normal reports.
- Provide four explicit rotating daily selections. Refuse daily
selection until all required live drivers exist. Keep scheduling and
spending with the existing operations owner.
- Document the exact selectors, evidence contract, limitations, and
remaining staging work.

## Verification

- Local Product E2E support: 3,554 passed with two existing skips;
source prerequisites: 128 passed. E2E typecheck passes. The actual
file-chip and Download-control browser regression passes.
- Two real-database regressions fail before the production repair. The
stopped coordinator leaves accounting asleep after a complete receipt;
the saved queue can restart from pre-Stop input. The repaired settlement
and cancellation cohort passes 66 checks. The complete 256-case
continuation suite passes with stronger two-phase Stop expectations, and
66 native executor accounting/Stop safety cases pass. Server typecheck
passes.
- All required CI, repository build/typecheck/tests, and fresh Greptile
5/5 pass at the final head 232d3c52d0,
with no unresolved findings.
- Both actual-model necessary-question development cells passed on the
earlier recorded c4e409d819 source. This is supporting evidence for that
revision, not current qualification.
- Both fresh Stop development cells at 6036cee2f4 stopped, remained
idle, resumed once, reached Done, and settled original and successor
accounting. Their original machine grades remain failed: the observer
counted the generated file-preparation carrier as another final reply.
The corrected observer independently correlates the applied preparation
receipt with the actual attachment/comment binding. Retained-wire
diagnostic replay finds one final delivery for each model without
changing either grade. Those original failures remain retained:
https://github.com/paperclipai/paperclip/actions/runs/39644220458.
- Both fresh actual-model Stop/resume cells pass all five checks at
232d3c52d0:
https://github.com/paperclipai/paperclip/actions/runs/39647010372.
Independent inspection confirms exactly one cancelled original and one
committed successor on the same task, 5.091/5.513-second quiet intervals
before the user resumes, retained workspace/instructions/tool
identities, and one receipt-bound final response. Each visible JSON chip
opens in a tab and its Download control yields verified bytes. The
rendered reference and changed label independently reconstruct the
registered file hash and the original context hash. Both cleanups pass.
These remain local API-key/per-turn development results, not the
required staging/Slack qualification.
- Other retained Stop failures include missed active boundaries, the
real accounting admission failure, and a preparation observer that
required nativeIssueId before runtime selection. They remain recorded at
https://github.com/paperclipai/paperclip/actions/runs/39636796645 and
https://github.com/paperclipai/paperclip/actions/runs/39642060165. The
preparation observer now validates all present task IDs and still
requires selected native execution at the active checkpoint.
- The remote finalization attempt failed during image export with no
disk space before a provider started. Remote crash acceptance remains
unverified.
- Negative calibration rejects wrong or absent ownership/evidence, stale
settings, unsafe files, repeated admissions, unacknowledged Stop,
premature resumption, incomplete receipts, lost context, genuine
duplicate final replies, and missing delivery. A prepared-looking body
cannot suppress a comment; absent, rejected, foreign, or mismatched file
receipts and attachment bindings also cannot suppress it.
- Core staging/Slack drivers, 72 fresh candidate passes, deployed
staging proof, and recovery of the original user-visible task remain
incomplete. No measured reliability percentage or completed
qualification is claimed.

## Risks

These are explicit development cells. Their API-key and per-turn runs do
not qualify deployed subscription authentication, warm reuse, or Slack.
A fast provider can miss the Stop boundary; that attempt fails. The
remote crash boundary depends on independently observed public state and
an owned controller. The checkpoint is opt-in through a private manifest
and cannot replace the missing live observers. The two production
changes affect native Stop settlement and saved-input promotion. They
use the existing transaction signals and admission path. A complete
receipt can settle without another provider turn. Partial accounting and
all ownership, cleanup, pause, and budget gates remain authoritative.

## Model Used

OpenAI Codex, GPT-6 family, with repository tools and code execution.
The runtime does not expose its exact deployment ID or context-window
size. Those values are not inferred.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Paperclip <paperclip@paperclip.ing>
2026-10-21 13:02:27 -05:00
DottaandPaperclip dff63551fe fix: resume unfinished work after repository preparation (#17047)
## Thinking Path

> - Paperclip manages agents that perform work for a company.
> - Task repositories are prepared at normal run admission.
> - A running native task can stage a repository while its work remains
unfinished.
> - The controller did not offer that saved setup as a continuation.
> - The agent could add a timer or wait for a user restart.
> - This change uses the existing same-agent continuation and status
ledger.
> - The accepted explanation and proven conversation remain available
after setup.

## Linked Issues or Issue Description

Refs #17045. That prerequisite has merged. This PR targets master.

**What happened?**

A projectless native task can request a repository for the next
admission. An unfinished task has no server-selected repository
continuation. An explicit same-agent continuation also loses chat
provenance.

**Expected behavior**

An unfinished standard task can yield and resume through the existing
admission queue. Its explanation and final result retain the authorized
conversation. A task that only stages a repository can complete without
another run.

**Steps to reproduce**

1. Start a native standard task in a task directory.
2. Request an authorized repository through prepare_repository.
3. Yield with unfinished work and no explicit continuation.
4. Observe that the former driver rejects the yield, although the
pending repository is saved.

## What Changed

- Offer a saved same-agent continuation for the current task's pending
repository.
- Accept that current server continuation in the shared provider driver.
- Resolve chat provenance through applied status decisions, delivered
effects, and admitted wakes.
- Preserve accepted setup explanations without repeating an owned
question card.
- Document optional reproduction details and configured repository
discovery.
- Extend database regressions and workspace access maps.
- Preserve Copilot profile v52 and bind the shared continuation source
in v53. Include the active identity in the QA image fingerprint.

## Verification

- Five focused server suites: 100 checks pass.
- Shared continuation driver: 12 checks pass.
- Server typecheck passes.
- Map contract and whitespace checks pass.
- Existing API and driver assertions fail on the former implementation.
- Real dispatcher and queue cohort: 43 checks pass. The original
reply-loss regression fails before the dispatch receipt fix.
- Continuation driver and installation identity cohort: 62 checks pass.
- Regenerated capability inventory and contract checks pass.
- Final response precedence cohort: 109 checks pass. The stale-comment
case fails on the former implementation.
- All required CI checks, build, and both source reviews pass on
b9c8677635.
- Live provider journeys and deployed acceptance remain pending. No live
qualification is claimed.

## Risks

A pending repository now offers a next admission when unfinished
standard work yields. Incorrect eligibility could create unwanted work.
The lookup requires the current workspace and task. Explicit completion
does not use it. Existing Stop, admission, approval, and transport
authorization remain authoritative. No schema or public task-status
change is included. The shared continuation change requires a new
Copilot source identity. This PR preserves v52 history and does not
claim fresh Copilot live qualification.

## Model Used

OpenAI Codex, GPT-6 family, with repository tools and code execution.
The runtime does not expose its exact deployment ID or context-window
size. Those values are not inferred.

## 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:17:50 -05:00
f3af9887ca fix(chat): keep each chat conversation in its own wake batch (#17054)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Chat channels let a linked user talk to an agent from Slack, GitHub
and other platforms. Each external thread is a chat conversation that is
bound to one issue.
> - One issue can have many conversations. A GitHub pull request review
makes one conversation for each inline finding thread.
> - When replies arrive behind an active run, the wake queue merges them
into one deferred wake. The merge did not look at the conversation.
> - A reviewed chat run can attest only one conversation. A merged batch
from two threads always failed attestation with `setup_failed` /
`reviewed_chat_execution_binding_not_authorized`, so no reply got an
answer.
> - This pull request keeps each chat conversation in its own wake
batch. The single-conversation attestation check stays the same.
> - The benefit is that replies in different review threads each get
their own attested run, one after the other.

## Linked Issues or Issue Description

Refs #16793 (related: chat origin during coalescing). No public issue
exists, so the bug report follows.

**What happened?**
A linked user replied in three different inline review-finding threads
of one pull request while the agent's review run was still active. The
three wakes merged into one deferred wake. The promoted run failed in
about 2 seconds with `setup_failed` /
`reviewed_chat_execution_binding_not_authorized` and
`providerWorkStarted=false`. None of the three replies got an answer.
The failed-chat retry cannot recover this batch, because a retry source
holds one conversation.

**Steps to reproduce**
1. Bind two chat conversations (two GitHub finding threads) to one
`in_review` issue.
2. Start a run for the assigned agent on that issue.
3. As the linked user, reply once in each thread.
4. Finish the active run.
5. See one promoted run that fails with
`reviewed_chat_execution_binding_not_authorized`.

**Expected behavior**
Each thread gets its own run. Each run attests its one conversation and
answers its reply.

**Cause**
`findExistingDeferredWake` matched on company, agent, issue and actor
only. The active-run coalesce path had the same gap.
`authorizeChatConversationForBoundRun` correctly denies a batch with
more than one conversation.

## What Changed

- `run-dispatch/domain/wake-context.ts`: add the `chatConversationId`
wake-context key, a reader, and `wakeChatConversationsConflict`. A
conflict means that both wakes have a conversation and the two
conversations are different.
- `issue-assignment-wakeup.ts` and `chat-channels/inbound/wakeups.ts`:
stamp the conversation ID on each inbound chat wake.
- `chat-channels/failed-run-retry.ts`: stamp the conversation ID on
failed-chat retry wakes, so later replies do not merge into a retry from
another conversation.
- `wake-queue` admission: do not coalesce into an active run of another
conversation. The deferred-wake lookup now matches only wakes of the
same conversation or wakes without a conversation.
- `heartbeat/queue.ts`: the same-scope queued or running coalesce target
is skipped when the conversations conflict.
- `doc/execution-semantics.md`: describe per-conversation chat wake
batching.
- Tests: an integration regression in the GitHub review block of
`chat-channels.integration.test.ts`, and unit cases for the active-run
branch in `wake-queue/application/use-cases.test.ts`.

## Verification

- Red before the fix: `pnpm --dir server exec vitest run
src/__tests__/chat-channels.integration.test.ts -t "replies in review
finding threads"`. The main case fails with one run: `{ status:
"failed", errorCode: "setup_failed", error:
"reviewed_chat_execution_binding_not_authorized" }`.
- Green after the fix, same command, 3 of 3 pass:
- Replies in two finding threads give two successive runs. Each run
succeeds, has `paperclipExternalChatExecutionBound: true`, and holds
only its own comment and conversation.
- Two replies in the same thread still give one run with both comments.
- After the linked identity is revoked, each separated run is still
denied (`setup_failed`) and the adapter does not execute.
- `vitest run src/modules/wake-queue src/modules/run-dispatch`: all
pass, with the new cases (different conversation, same conversation, run
without a conversation, non-chat wake).
- Related suites pass: `heartbeat-comment-wake-batching`,
`durable-chat-wakeup`, `chat-failed-run-retry-boundary`,
`issue-queued-comments-routes`, `heartbeat-stale-queue-invalidation`.
- Full `chat-channels.integration.test.ts`: 823 passed, 1 timed out. The
timed-out test (`GitHub terminal receipt composes pinned HTTP and
durable source authority (held_token)`) passes alone with and without
this change. The sandbox has a 4 GB memory cap.
- Not run locally: `pnpm -r typecheck` and `pnpm build`. The server
`tsc` process was killed at the 4 GB sandbox memory cap. CI runs both.
- Runtime dependency note: the deferred-wake lookup adds one JSONB
predicate to the existing deferred-wake query. It reads the same rows.
The query count does not change. The integration regression runs it
against embedded Postgres.

## Risks

- Low risk. No schema change and no migration.
- Behavior change: chat replies from different conversations on one
issue now run one after the other, not in one batch. Before this change,
such a batch always failed, so no working flow becomes slower.
- Wakes without a conversation (non-chat wakes, and wakes queued before
this deploy) keep the old coalescing. A deferred wake from before the
deploy has no conversation key, so one later reply can still merge into
it. That window closes when the queue drains.
- The check keeps the single-conversation attestation in
`authorizeChatConversationForBoundRun`. Revoked identities are still
denied.

## Model Used

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), in Claude Code through
the Paperclip agent harness. It used tool use (shell, file editing, test
runs) and extended reasoning.

## Checklist

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

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

---------

Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-20 22:47:24 -07:00
DottaandPaperclip c2daf906d9 fix: preserve task continuation and question delivery (#17045)
## Thinking Path

> - Paperclip manages agent work and delivers its results.
> - One task can need several runs and a saved workspace.
> - A run can succeed while the task waits for its next run.
> - Chat fallback treated that success as completion and dropped useful
status.
> - Monitor runs also lost their conversation origin, and text-question
cards lost their prompt.
> - This change preserves the task outcome and exact delivery authority
across those boundaries.

## Linked Issues or Issue Description

**What happened?**

A Slack task requested a repository, yielded to a scheduled
continuation, and showed only a generic completion message. The next run
could not publish through the original conversation. A later text
question showed its title and Respond button without the question
context. Workspace recovery could commit the saved monitor result
without restoring its explanation.

**Expected behavior**

The original conversation shows the accepted continuation explanation,
receives the resumed result, and displays the full question when input
is needed. A successful provider run does not claim that unfinished work
is complete. Recovery delivers saved output once without another
provider result.

**Steps to reproduce**

Start an authorized chat task, prepare a repository for a later
admission, save a monitor wait, and let that monitor resume the task.
Repeat with workspace finalization interrupted and with a free-text
question. Inspect the originating conversation and the persisted output.

Related merged work: #16806 and #16975. The open monitor and recovery
changes found during duplicate search address different status and
liveness policies.

## What Changed

- Keep committed monitor summaries visible through the shared board and
chat response resolver. Preserve interaction ownership and comment
reuse.
- Recover a saved monitor explanation after finalization, with no
repeated provider result.
- Prove monitor publication origin from the admitted wake and matching
scheduling receipt. Match company, task, agent, requester, and schedule;
reject ambiguous sources.
- Include monitor continuations in the existing publication scan and use
the canonical task binding. Keep current transport authorization.
- Suppress completion fallback for yielded results. Explain missing
final output without claiming completion.
- Include question summaries, prompts, and help text in modal-backed
cards. Keep answered text private.
- Update execution semantics, capability maps, and the bounded
reliability plan.

## Verification

- Original source fails four composed PostgreSQL regressions: monitor
origin, yielded completion, question body, and saved-summary recovery.
The shared response resolver also fails before the repair.
- Repaired focused cohort: 148 tests pass across publication, question
privacy, scheduler, and native monitor suites. These use real disposable
PostgreSQL; they do not qualify live Slack.
- Full workspace typecheck and production build pass locally. Map checks
and whitespace checks pass.
- All CI gates pass at 1701c48f17.
Greptile reports 5/5. Both review findings are resolved. The local
full-suite run remains separate.
- The added 21-admission lineage and cycle regressions pass with the
complete 11-test database suite.
- The larger live Codex/Claude qualification and deployed staging
acceptance remain in progress. This PR does not claim that milestone is
complete.

## Risks

- Incorrect lineage could disclose output to another conversation. The
resolver requires one exact receipt-backed source and the worker still
checks current delivery authority.
- Status recovery could duplicate output or compete with a question. The
source-run presentation marker, saved-comment reuse, interaction
arbitration, and replay tests protect those boundaries.
- No database migration or public task-status change is required.

## Model Used

OpenAI Codex, GPT-6 family, with repository editing, terminal tests, and
browser inspection. The runtime does not expose an exact deployment
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-20 20:43:17 -05:00
DottaandPaperclip 9225796a4e fix(runner): page oversized assigned tool results (#16980)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native agents receive assigned app-tool results through the durable
Runner protocol.
> - The assigned-tool bridge returned complete provider responses
without a size bound.
> - A large response could exceed the encrypted command limit after the
upstream call had completed.
> - The provider then waited for a result that the controller could not
deliver.
> - This pull request saves large governed responses and returns a small
reference with a scoped page reader.
> - Agents can retrieve the full result without repeating the original
action.

## Linked Issues or Issue Description

**What happened?**

Large app-tool responses could leave native tool calls pending even
though the upstream service completed. The durable command limit is
about 510 KiB. The assigned-tool bridge did not account for that limit.
A 720,048-byte regression response failed the delivery-size assertion
before this change.

**Expected behavior**

Deliver a bounded response and a way to retrieve all saved content. Keep
the original action at one execution. Preserve task, agent, company,
responsible-user, and current-run access checks.

**Steps to reproduce**

1. Run a native agent with an assigned tool that returns more than 510
KiB of JSON.
2. Let the upstream tool complete.
3. Observe that the original bridge returns the oversized object
directly, so the durable controller cannot queue its result.

Related: #14734 preserves uncertain tool outcomes across shutdown and
restart. This change keeps that protection. #16517 handles native MCP
content projection on a different transport; it does not bound native
assigned-tool results.

## What Changed

- Save governed assigned-tool responses above 500,000 encoded bytes
using the existing response asset storage, quotas, and audit path.
- Add `read_tool_result` for UTF-8 JSON pages from the same task and
agent. Each encoded page is capped at 500,000 bytes, including JSON
escaping and metadata, with remaining content available through the next
byte offset. It works when generic API tools are disabled.
- Reuse existing authenticated asset reads and responsible-user checks.
Retrieval does not invoke the upstream service.
- Preserve uncertain delivery if storage fails after an action
completes.
- Add regression, fault, authorization, and opt-in live Codex checks.
Document the transport and access contract.

## Verification

- Red: the original integration regression returned 720,048 bytes
against unchanged production code and failed its bounded-response
assertion. The follow-up regression also showed that a 480 KB result was
unnecessarily saved under the original 24 KiB policy.
- Green: results through 500,000 encoded bytes stay inline. Larger
responses are saved in full. Real HTTP integration tests reconstruct the
complete Unicode result after controller recreation in exactly two
pages, in both standard and low-trust configurations, with one upstream
execution.
- The encoded-size boundary and escape-heavy Unicode tests verify that
every page fits 500,000 bytes, cursors advance on valid UTF-8
boundaries, and concatenating pages exactly reconstructs the original
JSON. Storage-failure tests preserve an uncertain outcome without
repeating the action.
- The focused unit/API run passes 1,957 tests; the changed real HTTP
tests pass. `pnpm -r typecheck`, `pnpm build`, `node
scripts/check-maps.mjs`, and `git diff --check` pass.
- Real Codex acceptance passes with `gpt-6.1-sol` through Codex CLI
0.160.0 and native Runner, on macOS ARM64 with `low_trust_review` and
generic API tools disabled. Codex requests every page in order, receives
all 660,098 original bytes, and returns the final verification field.
The two encoded pages are 500,000 and 160,543 bytes; the upstream
operation executes once. Runner closes successfully in a 34.1-second
probe. Only the disposable database's connection/worker counts were
reduced to fit the host's remaining semaphore capacity.
- The native-PRP regression also passes with escape-heavy Unicode pages
over 1 MB in total and a separate inline result containing 200,000
backslashes. A scripted provider verifies both complete results by
SHA-256. It proves the intermediate text wrapper can exceed 700 KB
because the native transport decodes it before queuing the bounded PRP
result; every result object stays within 500,000 encoded bytes. Native
binaries are required for the local run, preventing a silent skip.
- A second real-Codex probe with escaped Unicode, quotes, backslashes,
newlines, and NULs also passes: every original byte of a 720,098-byte
JSON response is retrieved in two encoded pages of 500,000 and 460,543
bytes. The model returns the final field, the upstream call executes
once, and Runner closes in 32.6 seconds. This uses the same production
source with only the synthetic fixture content varied.
- The live probe exercises real PRP, persistence, and authenticated
asset reads. It does not claim the original staging task resumed or full
heartbeat checkpoint acceptance.
- Earlier broader local testing passed 103 tests but could not start the
second database for the existing `rejects a stale source-run question
over public HTTP and accepts it from the next run` test. Extending its
timeout exposed PostgreSQL initialization failure on this
semaphore-exhausted host. The earlier local `pnpm test:run` attempt was
interrupted and is not claimed as passing. Full CI passes on current
head `cd32237ee5429765b8f349402ac3c79cceec3e6b`. GitHub reports 55
successful checks, two skipped Storybook checks, and one neutral
Paperclip agent review. Greptile is 5/5 on this head with no review
threads.
- Additional Paperclip agent review is incomplete: it withdrew the
earlier double-escaping finding after inspecting the native regression,
found no new actionable defects, and reported stalled large-source reads
prevented independent inspection of the transport decoder. Its neutral
assessment is [recorded
here](https://github.com/paperclipai/paperclip/pull/16980#issuecomment-6295579705).
This limitation remains separate from the passing local native
transport, real Codex acceptance, full CI, and Greptile results.

## Risks

- Large responses now consume existing response-storage quota and
require page reads. Larger pages use more provider context. Results are
preserved in full; each encoded page leaves about 22 KiB below the PRP
command limit for the command envelope.
- A storage failure after execution retains an uncertain delivery
outcome. The bridge must not report that the upstream action failed or
invite a replay.
- The dedicated reader grants access only to saved responses for the
current task and agent. The asset route also checks the original
responsible user.
- Runner command limits and receipt rules remain unchanged. No migration
or separate Runner protocol rollout is required.
- This PR does not deploy the repair or resume an existing blocked task.

## Model Used

OpenAI Codex (GPT-6), with reasoning, tool use, and code execution. The
exact serving model ID and context-window size are not exposed in this
session. The opt-in acceptance probe requests `gpt-6.1-sol` through
Codex CLI 0.160.0.

## 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 behavior
(focused unit/API, real HTTP, and real Codex acceptance); the broader
local database limitation is documented above
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-20 11:52:59 -05:00
DottaandPaperclip 98d17da1e2 Fix native monitor dispatch and finalization recovery (#16975)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native runs persist their results and workspace before they release
execution.
> - A task monitor must wait for that settlement before it starts the
next run.
> - The scheduler checked provider status but did not check unfinished
finalization.
> - A timer could dispatch too early, then its old finalizer could no
longer find the consumed schedule.
> - This pull request delays dispatch and reconciles prior delivery from
server records.
> - The task can continue without repeating the provider turn or
removing a newer run's ownership.

## Linked Issues or Issue Description

**What happened?**

A native run saved a yielded monitor result while workspace finalization
needed recovery. Its timer dispatched after the provider stopped. The
follow-up failed because the earlier run still held workspace recovery.
The old finalizer then retried `monitor_wait_authority_lost`
indefinitely.

**Expected behavior**

Timer dispatch waits for native finalization. If an older timer already
dispatched, the finalizer uses its saved scheduling receipt and admitted
successor to settle the old result. It preserves current task state and
ownership.

**Steps to reproduce**

1. Schedule a task monitor from a native run and save a yielded result.
2. Leave native finalization retryable after the provider reaches a
terminal state.
3. Let the monitor become due. Before this change, the scheduler
consumes it.
4. Restore workspace persistence and retry the old finalizer. Before
this change, the missing schedule leaves it retryable.

**Paperclip version or commit**

Reproduced against master `34be1b831bcaba070264ae8d214ceffe6a8f9eab`.

**Deployment mode**

Self-hosted server built from source, with PostgreSQL and the native
runner. Related recent recovery work: #16962 and #16963. No duplicate
repair found for this failure.

## What Changed

- Scheduled and manual monitor dispatch wait for unfinished native
finalization. If the monitor expires during workspace recovery, the
accepted result can settle into the persisted schedule, and the
scheduler then applies its existing expiry policy. New monitor-wait
results still require an unexpired, unexhausted schedule.
- A narrow reader joins the run's server-issued monitor receipt to its
durable wake and admitted successor.
- The finalizer and locked status commit verify that evidence and
preserve current task ownership. They do not create another wake.
- Monitor delivery participates in assessment identity, so recovery can
replace a prior error decision without violating the decision ledger's
uniqueness constraints.
- PostgreSQL regressions cover saved-result replay, active successors,
wrong task or agent, missing receipts, unadmitted wakes, ownership
races, and failed workspace persistence.
- The execution contract and verification maps describe the recovery
boundary.

## Verification

- Red: the saved monitor regression remained `retryable_failure` instead
of `committed` on unpatched code.
- Red: the scheduler admitted a wake while terminal-run finalization
remained retryable.
- Red/green: two PostgreSQL cases reproduce timeout during workspace
recovery for scheduled and manual checks. Before the review repair, both
remain `retryable_failure` with `monitor_wait_authority_lost`.
Afterward, both commit, apply board escalation once, preserve the saved
result, and enqueue zero provider wakes. New completion still rejects
the expired schedule.
- Typecheck: `pnpm -r typecheck` passed.
- Build: `pnpm build` passed.
- Maps: `node scripts/check-maps.mjs` passed.
- Review: Greptile 5/5 and Ponytail Paperclip Review 5/5 on the current
head, with no unresolved findings. Both reviews are static; execution
evidence is recorded separately above and below.
- Linux staging: the original repair passed 170 tests across monitor
integration, scheduling, status arbitration, and workspace finalization.
The expanded suite passes all 172 tests, including both new timeout
regressions.
- Live acceptance: deployed the original repair to the authorized Linux
ARM64 self-hosted instance. The stranded saved result finalized
automatically; the original task then completed through normal
admission, with its final answer persisted after browser reload. The
other original task was already complete and its external delivery was
verified without repeating it.
- The initial Mac regression run passed 68 tests, but three suites
failed during embedded PostgreSQL startup. A subsequent focused run
confirmed the host had exhausted its PostgreSQL semaphore allowance.
Linux verification uses isolated databases; the local failure logs
remain preserved.
- The local full test suite is not claimed as passed. It is blocked by
the same PostgreSQL startup limitation; full-suite CI passed on head
`8bc29d1b7783446f397b858e558083570a96bb66`
([run](https://github.com/paperclipai/paperclip/actions/runs/39447913466)).

## Risks

- A delivery match must not grant new execution authority. The lookup
requires the original run's server receipt and a matching company,
agent, task, wake, and admitted successor. The locked commit rejects
old-run checkout ownership and permits no status effects.
- Workspace persistence must succeed before settlement. Its existing
failure path remains effective.
- The scheduler waits while a native finalizer is unfinished. Workspace
recovery must settle before dispatch; elapsed monitor limits transfer to
the existing scheduler recovery policy instead of stranding the result.
Neither settlement path renews a deadline or changes budget, approval,
pause, or provider authority.
- No schema, migration, credential, or provider configuration change.

## Model Used

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

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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 10:58:41 -05:00
DottaandPaperclip 4b05795712 fix(github): reuse review instructions across native turns (#16971)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - GitHub events continue an ordinary task and can reuse its provider
conversation.
> - Each automatic event appended the full review policy and PR
description.
> - Native GitHub follow-ups also repeated task history because only
Slack used the compact path.
> - This pull request separates admitted review configuration from
commit updates.
> - A verified resume sends the current messages and changed
configuration. A fresh session gets full context.
> - This reduces repeated input while retaining review rules and current
tool checks.

## Linked Issues or Issue Description

Refs #13959, #16645, #16901.

**What happened?**

Opening a PR and pushing twice stored three full review prompts. A later
native turn repeated all three in a retained provider conversation.

**Expected behavior**

A new event supplies its commit and event data. The harness delivers
stable review instructions once per retained conversation and
configuration. Fresh sessions receive full instructions.

**Steps to reproduce**

1. Enable automatic GitHub reviews for a repository.
2. Open a PR, let the review finish, and push two new commits.
3. Inspect task messages and the native submitted envelopes. Before this
fix, the policy and old requests repeat.

## What Changed

- Store compact automatic event messages. Fetch the PR description
through the existing metadata tool when needed.
- Load review instructions from processed deliveries bound to the
company, task, agent, and source comments. Preserve each admitted policy
snapshot.
- Use the existing verified native continuation path for complete GitHub
text batches. Deliver configuration changes using a key over endpoint,
PR conversation, revision, and instructions.
- Keep full bootstrap input for fresh sessions and failed resume,
including board follow-ups with no new GitHub event. Retain existing
framing for incomplete batches, attachments, interactions, and recovery.
- Keep the same configuration identity across automatic review,
discussion, and automatic review. Coalesced requests use the last
accepted request's admitted policy.
- Add regression and database coverage. Update the execution contract
and verification recipe.

## Verification

- Red: the new event-size regression failed because policy and PR body
repeated. The GitHub continuation regression failed because it returned
no compact prompt.
- Green: focused policy, continuation, native input, and adapter prompt
tests passed (231 tests). Native resume/fresh-bootstrap cases passed (9
tests).
- The signed-webhook/database case passed at `6296168253`. It checks
configuration edits, queued older snapshots, coalesced policy selection,
discussion continuity, board follow-ups, and company/task/agent/comment
boundaries. The private PostgreSQL template used 12 connections because
the host reached its semaphore limit with the default settings.
- The earlier broad channel/session integration run passed 1,432 tests.
Later review fixes invalidated that run. The latest focused database
case passed after those fixes.
- Reviewer regressions failed against the previous lookup, then passed
with the fix: a mixed-revision batch delivered multiple policies, and a
discussion lost the configuration identity. The board
wake-to-rendered-prompt regression also failed before repair.
- `pnpm -r typecheck`, `pnpm build`, `node scripts/check-maps.mjs`, and
`git diff --check` passed on the final source.
- Full local `pnpm test:run` exposed follow-up/fixture failures during
development. Those cases are fixed and pass separately. The stale run
was stopped after source edits. Full-suite validation for the final
commit passed in CI.
- All required CI checks passed for `6296168253`, including tests,
typechecks, build, and clean public npm install checks. The two
unrelated Storybook checks were skipped.
- A live two-turn Codex smoke could not start: the local macOS sandbox
helper rejected execution of the installed Codex 0.153.4 binary. No live
GitHub/provider acceptance or deployment is claimed.

## Risks

Skipping instructions is safe only after a verified checkpoint resume
with the correct previous run. The full bootstrap remains available if
resume fails. Changed or previously unknown configuration is sent again.
Tests cover these cases. Tool authorization and publication checks still
run under the current server policy. No database migration is required.

## Model Used

OpenAI GPT-6 through Codex, with tool use and code execution. The exact
deployment model identifier and context window are not exposed in this
session. No delegated agents were used.

## 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 10:39:17 -05:00
DottaandPaperclip d84061b923 fix(runner): recover journaled completion proposals (#16962)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Native runs preserve provider sessions across controller restarts.
> - A completion event can reach durable storage before the next session
checkpoint.
> - Recovery advanced the event cursor but lost the saved completion
proposal.
> - The completed turn then appeared to need another provider turn to
report its result.
> - This change restores the exact active turn's proposal before
advancing the cursor.
> - Recovery can finalize completed work without repeating its external
actions.

## Linked Issues or Issue Description

**What happened?**

After a controller restart, a native run could fail with
`native_finalization_missing` even though `run.result.proposed` was
already durable. The recovered driver skipped that event when its cursor
advanced, so the result was absent from its state. Automatic disposition
retries could then encounter outstanding tool receipts and exhaust
recovery.

**Expected behavior**

Restore the saved result for the exact active turn. Wait for provider
termination, then use normal finalization. Do not start another provider
turn to repeat completed work.

**Steps to reproduce**

1. Start a native Codex task and save its active-turn checkpoint.
2. Persist its completion proposal, then lose the controller before
another checkpoint.
3. Recover the same run after the provider completes. Before this
change, finalization reports a missing result.

**Paperclip version or commit**

Reproduced on `92b238137b`. The repair is based on `7c247243e5`.

**Deployment mode**

Native Runner with Codex. The incident used a remote sandbox; the
regression covers controller event recovery.

Related: #12845 and #15297. The older #12241 addresses provider process
continuity; this change repairs the controller's saved event projection.

## What Changed

- Restore valid completion proposals for the checkpoint's exact active
turn during event replay.
- Reject conflicting, invalid, or incorrectly bound proposals. Ignore
proposals from another turn and prevent adoption after a newer turn
starts.
- Keep provider termination and normal finalization as separate
requirements.
- Add eight behavioral cases using the real Codex driver and a simulated
transport, and document the recovery contract and verification scope.

## Verification

- Red: the new restart regression failed with
`native_finalization_missing` before the production change.
- Green: 254 tests passed across the native runtime, backend adapter,
and Codex recovery suites. The new cases check no repeated provider
work, no early finalization, duplicate proposals, conflicts, invalid
proposals, and stale turn/session bindings.
- `node scripts/check-maps.mjs` and `git diff --check` passed.
- `pnpm -r typecheck` and `pnpm build` passed locally. All CI gates
passed on `2ac025a15c`; Greptile rated that commit 5/5 with no
outstanding findings. The full local `pnpm test:run` was started after
completing the embedded PostgreSQL installation, then stopped while
still running once the complete CI test matrix passed; it is not
reported as a completed local run.
- Real local acceptance passed on `2ac025a15c` with the production Rust
Runner transport, Codex CLI `0.160.0`, and `gpt-6.1-sol` on macOS arm64.
Persisted the completion proposal while retaining the older active-turn
checkpoint; let the provider finish without persisting its later
controller events; stopped the exact owned runner/provider; recovered
the same provider session and turn. Observed one accepted result, one
durable `turn.start` command, and one line in the receipt file. Hosted
sandbox restart and recovery of the original stopped task have not been
performed.
- Initial setup failures are retained: dependency drift caused
diff-viewer type errors, and skipped PostgreSQL postinstall links
prevented database startup. Dependency alignment and the package
postinstall repaired those local prerequisites. Probe setup iterations
failed on a direct-provider response schema, invalid probe input fields,
and an original provider that still held its writer lock. Acceptance
uses the production transport, validated input, and exact-owner
shutdown. No production code changes were made for those probe setup
failures.

## Risks

- A stale proposal could finalize the wrong work. Recovery requires the
checkpoint's active turn and matching run, session, and source. It
rejects conflicting results and waits for provider termination.
- The change uses the existing paginated recovery scan. It adds no
database query per event or new retry allowance.
- No schema migration or change to task authorization is included.

## Model Used

OpenAI GPT-6, running as Codex with reasoning, tool use, and code
execution. The exact served model snapshot and context-window size are
not exposed in this session. The live acceptance probe uses
`gpt-6.1-sol`.

## 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 09:09:22 -05:00
DottaandPaperclip 0b266bd084 fix(workspaces): keep source preparation locks active (#16956)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Each task prepares its workspace before it starts the provider.
> - Source preparation holds a transaction-scoped advisory lock on a
dedicated database connection.
> - Preparation can use other connections and the filesystem for longer
than PostgreSQL allows that lock connection to stay idle.
> - PostgreSQL then closes the connection and releases the lock while
preparation still runs.
> - This pull request reuses the workspace lock keepalive during source
preparation.
> - Slow preparation retains exclusion and returns its original result
or error.

## Linked Issues or Issue Description

**What happened?**

Tasks failed during workspace preparation with `CONNECTION_CLOSED` at
the configured 60-second idle transaction timeout. No provider had
started. A live retry showed the source-lock transaction idle while
authorization reads ran on another connection. Its advisory-lock key
matched the task's source-preparation key. PostgreSQL closed that
transaction at the timeout.

The same defect remains on current master. A real PostgreSQL regression
also shows that another preparation can enter after the timeout while
the original callback still runs.

**Expected behavior**

Source preparation must keep its advisory lock until the callback and
any pending keepalive query finish. The ordinary pool must remain
usable. The original preparation result or failure must survive.

**Steps to reproduce**

1. Set `idle_in_transaction_session_timeout` to one second in a
disposable PostgreSQL database.
2. Call `withIsolatedRepositorySourceLock` and keep its callback active
for longer than one second without querying the lock connection.
3. Try to acquire the same source lock from another operation.
4. On the old code, the second operation enters and the first
transaction fails with `CONNECTION_CLOSED`.

**Paperclip version or commit**

Reproduced on source at `cde944ed75648adfb569e86133adc8b118b55dd6`. The
repair is rebased onto `d855e550ad`.

**Additional context**

Refs #16340. That change introduced the keepalive for workspace
copyback. This PR applies the existing helper to the source-preparation
transaction.

## What Changed

- Read the configured idle timeout in the same query that acquires the
source lock.
- Keep that dedicated connection active during source preparation with
the existing helper.
- Add two real PostgreSQL regressions for exclusion, company
independence, a one-slot ordinary pool, nested acquisition, cleanup, and
preservation of callback errors.
- Update the execution contract and workspace verification maps.

## Verification

- Red before the production edit: both new tests failed. A competing
operation entered after the lock expired. A callback failure was
replaced by `CONNECTION_CLOSED`.
- Green after the repair: 86 tests passed across the source lock, source
admission, keepalive, and heartbeat workspace preparation suites.
- `pnpm -r typecheck`, `pnpm build`, and `node scripts/check-maps.mjs`
passed on final commit `2b5000ce25`.
- `git diff --check` passed. The lockfile is unchanged.
- After rebase, the same 86 focused tests passed. The two source-lock
tests passed again after the review cleanup change.
- Live acceptance on `7df94d7a06`: a fresh local instance on macOS
arm64, Node v26.4.0, native Codex `gpt-6.1-sol`, and permission mode
`never`. PostgreSQL had a one-second idle transaction timeout. A
temporary Git wrapper delayed repository reads by four seconds.
Workspace preparation after environment acquisition took 8,297 ms. The
real task completed, wrote and attached the correct line-count/SHA-256
report, reached `done` with native phase `committed`, and cleared its
execution lock. The attachment matched the workspace file and survived
browser reload. No dedicated lock connections remained.
- The live probe initially used `gpt-5.4`, which the ChatGPT account
rejected. Selecting the reported task model, `gpt-6.1-sol`, resolved
that readiness failure. The local Claude CLI has no login, so no live
Claude run is claimed.
- Full CI passed on `2b5000ce25`: all general, chat, workspace,
serialized-server, Runner, and E2E test shards, plus typecheck, build,
and canary packaging. Both Greptile and Paperclip Review rated this
commit 5/5 with no open findings. The Paperclip check concluded neutral;
two unrelated visual checks were skipped.
- The duplicate local `pnpm test:run` serial invocation was stopped with
SIGINT after the complete CI test shards passed. It did not finish
locally; no local full-suite pass is claimed. Local proof consists of
the focused real PostgreSQL tests, typecheck, build, map checks, and the
real task above.
- The final commit changes test teardown only; production code matches
the live acceptance commit.
- Initial sandbox runs could not open test database ports or build IPC
sockets. The same commands ran with local process access. The frozen
install also found an existing manifest/lockfile mismatch; dependency
resolution was local and the lockfile was restored.

## Risks

- The lock connection sends one small query per keepalive interval. The
helper derives that interval from the configured timeout and caps it at
30 seconds. It does no polling when the timeout is disabled.
- The helper joins an in-flight query before the transaction ends. A
slow database can therefore delay cleanup.
- This change does not disable database deadlines, retry preparation, or
recover an unexpected connection loss. It preserves existing admission
checks and company/source lock keys.
- Deployment and recovery of the reported tasks remain separate from
code verification.

## Model Used

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

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-20 07:01:17 -05:00
DottaandPaperclip bc091f0a21 fix(recovery): recover retained workspaces before saved messages (#16906)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A stopped run can leave useful files in its original sandbox.
> - The server keeps that sandbox when it cannot confirm file copyback.
> - The task can then save new user input but offers no safe way to
recover the files.
> - A retry of the old agent turn can repeat work that already finished.
> - This pull request recovers files through the existing workspace
owner and delivers only saved human input.
> - The user can recover a task without certifying infrastructure facts
or losing completed work.

## Linked Issues or Issue Description

Refs #16883 and #16835 for publication receipts and verified process
termination.
Related: #16478 clears stale holds after copyback already succeeded.
This change performs the missing export and records its proof. #16901
manages the main GitHub response comment and does not perform workspace
repair.

**What happened?**

A failed run retained its workspace. A later user message stayed saved
behind a file-repair hold. The task asked the user to verify process
termination and file recovery but offered no repair control. Sending a
message could show an optimistic Working state and move the task to Todo
before admission succeeded.

**Expected behavior**

The harness should recover the original files when it can prove
ownership and termination. It should preserve newer edits and completed
operations. Only waiting user input may start another turn. Missing
evidence must keep the source held with a specific explanation.

**Steps to reproduce**

1. Interrupt a sandbox run before workspace finalization succeeds.
2. Retain its exact allocation and save a user follow-up.
3. Observe the file-repair hold and missing end-user repair path.

## What Changed

- Add source-bound background recovery for native and legacy runs. An
accepted native result is not required.
- Share the physical copyback lock and durable owner. Resume only the
exact stopped allocation, export files, verify persistence and obtain a
fresh stop receipt.
- Persist retry limits and idempotent requests on the existing recovery
record. Limit each server to two background exports. Add an indexed
candidate scan and a separate cleanup obligation.
- Add a task-editor recovery endpoint. Deny agents and viewers. Reject
caller-supplied stop or repair claims.
- Deliver waiting human input once through ordinary admission. Preserve
pause, approval, budget and assignment gates. Require historical
conversational-adapter evidence for legacy-to-Runner continuation.
- Include exact GitHub publication receipts in continuation context.
Preserve unknown effects and the original failed run.
- Show observed recovery progress, a transient-failure retry action and
specific service blockers. Keep saved messages visible. Suppress false
startup feedback and implicit Todo promotion behind a file hold. Hide
unavailable workspace links.
- Update execution semantics, capability maps and production-component
stories.

## Verification

- Red: the original read model failed the new recovery-progress
assertion; the other 23 checks in that file passed.
- Passed: recovery regressions with 28 cases with real PostgreSQL,
files, tar and Git operations. Lifecycle provider calls are controlled
test boundaries. Coverage includes native/legacy export, concurrent
owners, rollback after export, interrupted archive export with a new
database connection, lost replies, historical discovery, durable retry
limits, separate cleanup and saved-input delivery. The actual candidate
query uses its partial index with 3,000 unrelated history rows. A first
automatic repair can bind verified old-controller stop proof without an
accepted native result. Review also reproduced and fixed export-only
continuation and cleanup lost after a committed repair. Cleanup remains
discoverable without waiting input and after task reconciliation.
- Passed: two GitHub integration cases verify that continuation context
carries the existing publication receipt and does not write another
response. Company and task boundaries are checked.
- Passed: 293 continuation, workspace-sync, platform-receipt and OpenAPI
checks. After file repair, actual dispatch retains pause, cancellation,
approval, real budget-policy and assignment gates.
- Passed: 174 route checks, including editor admission, agent/viewer
denial and rejection of fabricated repair claims.
- Passed: 351 task detail, workspace properties and comment-reopen
checks after updating the new read boundaries and asynchronous
workspace-link expectations. Two new route cases keep held tasks Blocked
when a comment is saved.
- Passed: 265 task-thread and recovery-notice checks. The broader
continuation suite also passed; its new legacy migration case first
exposed a gap that the focused recovery suite now covers.
- Passed after rebase: `pnpm -r typecheck` and `pnpm build`. The later
master merge adds an unrelated permission migration; recovery migration
generation and safety checks passed after the merge.
- Passed: token gates, module boundaries, map structure and migration
checks. The recovery index is migration 0360 after current master.
- Browser: inspected the production recovery component and its
retry-to-queued transition with fixture data. The first dev dependency
load failed; a reload after optimization resolved it. This is UI
evidence, not a live provider recovery claim.
- Every test shard passed on final commit `bbbc09efb4`. The older local
`pnpm test:run` was stopped after the complete CI test matrix passed.
Local full typecheck and production build passed. The first CI build
compiled and booted, then lost its runner during the clean public
npm-install check. An isolated retry passed the build, production boot
and clean public npm-install verification.
- Greptile reviewed final commit `bbbc09efb4` at 5/5. All review threads
are resolved.
- The change is not deployed. Hosted recovery remains unverified until
the served revision includes this change.

## Risks

- Recovery crosses process, provider and persistence boundaries. Missing
or mismatched ownership, stop, descriptor or file evidence keeps the
hold. It never selects a replacement allocation or silently starts over.
- File recovery does not certify external action outcomes or authorize
replay. New input still uses normal execution admission.
- Historical sources without sufficient durable evidence can remain
blocked. Destructive reconstruction stays separate.
- The index migration is idempotent. It changes no task data. Snapshot
pruning follows repository policy.

## Model Used

OpenAI Codex, `gpt-6.1-sol`, with code execution and browser tools.
`gpt-6-astra` assisted earlier investigation. 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-19 21:51:46 -05:00
DottaandPaperclip 16689de291 fix: add one-click retry for restricted workspace setup (#16909)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Tasks need a clear way to resume after a setup failure.
> - A shared repository can contain another task that the assigned agent
cannot read.
> - Paperclip correctly stops before the agent starts, but the user must
then understand workspace settings to recover.
> - This pull request gives that failure a single Retry button and
selects a clean copy of the configured repository through normal
admission.
> - The task keeps its assigned agent and execution settings, while
access checks and existing work remain protected.

## Linked Issues or Issue Description

Related: #16895 classifies the restricted source failure. This change
adds the user recovery action. #16906 handles retained files after
execution; this change handles a setup failure before execution starts.

**What existing behavior does this improve?**

Recovery of a task whose shared repository includes a task workspace the
assigned agent cannot read.

**Current behavior**

The task stops before provider work and asks the user to understand
workspace choices or access grants.

**Proposed behavior**

Show Retry above the task message box, independent of loaded
conversation history. One click requests one attempt using a clean
checkout of the configured remote. Show Retrying while the request is
pending. Show an error only if the request fails.

**Reason and benefit**

The user can resume work without understanding repository isolation or
changing access to another task.

**Breaking changes**

None. The recovery source value is server-owned. The public workspace
selection request does not accept it. No migration is required.

## What Changed

- Add one Retry control above the composer in both task conversation
views. It stays available when an older notice is outside the loaded
comment pages.
- Bind recovery to the latest failed run, current assignee, and recorded
proof that provider work did not start.
- Use the existing atomic recovery route and pending workspace
selection. Normal admission clears the old session and creates the
isolated checkout.
- Choose a fresh task-owned branch for recovery, including fixed project
branches, existing task branches, and adapter branch overrides. Preserve
the model, environment, setup commands, pending user workspace choices,
previous files, and all execution gates.
- Update execution documentation and both workspace maps.

## Verification

- Passed all 461 tests in the eight affected database, Git, HTTP,
workspace binding, and conversation suites after the review fixes. An
additional 292 tests passed after the final compatibility fixes,
including IssueDetail and recovery action routing.
- Concurrent HTTP retries produce one wakeup. Paused projects and runs
that started provider work reject recovery without changing the
workspace.
- Real Git tests exclude private files, private commits, and unpublished
original files from the clean checkout, preserve the original, and
exercise reuse, cleanup, and reopen.
- Verified in a running local app: a task failed twice, displayed only
Retry, and reached Done after one click. A second browser test put the
failure notice behind 501 newer comments and used a fixed project
branch. Retry stayed visible and the task reached Done. The
credential-free process worker used the selected checkout and wrote its
result there. This does not claim a live model-provider run.
- The first manual fixture lacked a responsible-user membership and
failed after successful workspace recovery. Corrected the fixture,
retained that failure evidence, and verified the full flow with ordinary
authorization.
- Full typecheck, build, token gates, and map checks passed. The full
local suite was stopped after review changed its source during
execution; current-head CI provides the complete suite result.
- An earlier unrelated peer-comment read test hit a socket hangup. The
next complete affected-suite run passed; the first failure is retained
in local evidence.
- Current-head CI hit an unchanged runner credential-concurrency test
failure. That lane passed on the preceding commit, and all 42 credential
tests passed locally. The original failed job is retained; the one-time
rerun passed. All latest-head CI checks are green (55 success, 2
skipped, 1 neutral), and Greptile is 5/5 with both review threads
resolved.

## Risks

- A remote repository outage can still prevent the next attempt. Retry
does not grant access or copy unpublished files from the old checkout.
- Recovery accepts only the latest recorded pre-provider failure and
preserves normal execution gates. This limits duplicate work and stale
recovery requests.
- No database migration, dependency, or lockfile change.
- The first review found fixed-branch retry loops and dependence on
paginated notice history. Both had failing regressions before the fixes
and now pass targeted tests and the combined browser journey.
- Full CI then caught assumptions about missing run evidence and
unrelated recovery actions. The final changes tolerate absent evidence
and scope the new lookup to board-owned actions with valid run IDs. The
previously failing suites now pass locally.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository tools, code
execution, and browser verification. The exact serving model ID and
context window are not exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-19 21:49:44 -05:00
DottaandPaperclip 7ec6e1a237 fix(runner): explain invalid arguments for assigned tools (#16905)
## Thinking Path

> - Paperclip coordinates agents through authorized tools.
> - The native runner validates arguments before it dispatches a tool
call.
> - Schema failures give repair guidance for only five built-in tools.
> - Assigned-tool discovery receives a generic rejection, so an agent
can mistake invalid arguments for missing access.
> - This PR applies the existing safe schema feedback to every
authorized tool.
> - Agents can correct their calls while authorization, approval, and
replay checks remain in force.

## Linked Issues or Issue Description

**What happened?**

An invalid `paperclip_search_assigned_tools` call returned only
`Paperclip rejected this semantic tool call`. The call never reached the
assigned-tool gateway. The same feedback gap affects arbitrary assigned
app tools.

**Expected behavior**

Identify argument validation as the cause. Return bounded schema
locations and ask the provider to correct its arguments. Do not expose
submitted values or grant access to unassigned tools.

**Steps to reproduce**

Prepare an authorized discovery tool. Submit an invalid query or
optional argument through the native runner. Observe the generic error
before this fix. Correct the arguments and confirm one dispatch after
this fix.

**Paperclip version or commit**

Reproduced on master at `4970d5ed3d`. The incident did not retain its
rejected arguments, so the tests cover representative invalid inputs
rather than claim to replay that exact payload.

**Deployment mode**

Native runner with the Codex app-server driver. Local verification uses
macOS ARM64 and controlled provider fixtures.

Related: #16868 added bounded monitor validation feedback. #16723
changes runtime MCP catalog loading; this PR changes the Rust runner
validation response and does not depend on that work.

## What Changed

- Supply generic repair guidance for any authorized tool whose arguments
fail JSON Schema validation. Preserve specific built-in hints.
- Keep the existing three-location and 512-byte limits. Do not include
input values or unknown keys. Preserve generic authorization failures
and the existing retry flag.
- Cover missing fields, wrong types, null optional fields, bounds,
unknown fields, unchanged rejected-call state, corrected calls, and
completed-call replay protection.
- Exercise rejection, correction, one dispatch, and successful
completion through the real Codex driver with a controlled provider
subprocess.
- Document the behavior and its verification scope in execution
semantics and the agent access map.

## Verification

- Red: both new runner regressions failed on the original generic
rejection. The existing API-validation regression passed.
- Green: all seven selected validation tests passed. The new Codex
transport regression passed with one rejection and one corrected
dispatch.
- `cargo fmt --manifest-path packages/paperclip-runner/runner/Cargo.toml
--all -- --check`, `node scripts/check-maps.mjs`, and `git diff --check`
passed.
- Full Rust workspace tests, the repository build, and sequential
repository typecheck passed. The existing assigned-tool gateway suite
passed all 30 tests. All hosted test and end-to-end jobs passed on this
head. The duplicate local `test:run` was interrupted after the hosted
suites passed; its local result is incomplete. The hosted build and
clean public npm installation check also passed. All current-head CI
gates are green, including the final verify and e2e aggregates.
- Greptile reviewed commit `3e2743e1e15b3b13a7d803ce4d828510e41fb22a`:
5/5, no actionable findings.
- Setup evidence: master has a stale dependency lockfile. Dependencies
were resolved with the pinned pnpm 9.15.4; lockfile changes are
excluded. The first full typecheck overlapped the build and failed
copying a generated shared-package file. The sequential rerun passed
with no source change. The initial copy failure remains recorded here;
the concurrent build-output race was not separately reproduced.
- The provider is simulated. This proves runner validation and transport
recovery, not that a live model always retries or that GitHub
credentials work.

## Risks

The provider now sees schema locations for all authorized tools. The
existing sanitizer and bounds still apply. Unknown input keys and values
remain excluded. No schema, permission, approval, automatic tool replay,
database, or connection-auth changes are made. Unauthorized tools still
get a generic rejection.

## Model Used

OpenAI Codex (GPT-6), with reasoning, source inspection, and tool
execution. The exact runtime 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-19 17:03:42 -05:00
DottaandPaperclip b928c6698f fix(heartbeat): explain deferred monitor dispatch (#16891)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - One-shot task monitors request future work through the normal
heartbeat queue.
> - Queue admission can hold execution until recovery or ownership is
reconciled.
> - The scheduler retained those monitor schedules but recorded no
monitor diagnostic.
> - Agents could repeatedly reschedule the same blocked check without
seeing why it did not run.
> - This pull request records safe deferral and failure activity while
retaining all execution gates.
> - Agents and operators can inspect the actual blocker and use its
existing recovery path.

## Linked Issues or Issue Description

Related: #15556 handles transient monitor client errors. #14590 handles
parked monitor wakes. This change records the diagnosis when queue
admission defers or fails; it does not change their admission or monitor
consumption policies.

**What happened?**

A due monitor on a task with an unresolved execution-recovery hold was
claimed, passed to queue admission, and released without monitor
activity. The saved monitor continued to look scheduled. Unexpected
dispatch errors had the same audit gap. Repeated rearming could not
clear the recovery hold.

**Expected behavior**

Keep the monitor and the recovery hold. Record the reason that prevented
its wake. Do not execute uncertain work or consume an attempt. Limit
repeated identical diagnostics. If audit storage fails, release the
claim so a later tick can retry.

**Steps to reproduce**

1. Schedule a due monitor on an agent-owned task in progress.
2. Add an active recovery action for an interrupted legacy execution.
3. Run the monitor scheduler twice. No provider may start while the hold
remains.
4. Before this fix, neither attempt writes monitor deferral activity.
5. Resolve the hold through its existing recovery path. The saved
monitor can then request one wake.

**Paperclip version or commit**

Reproduced on master 5739a53f5f with real PostgreSQL and the assembled
heartbeat service.

**Deployment mode**

Built from source. This affects the server monitor scheduler.

## What Changed

- Pass the existing execution blocker cause to the monitor scheduler
through a server-owned diagnostic callback.
- Record issue.monitor_deferred for held admission and
issue.monitor_dispatch_failed for unexpected admission failures.
- Keep activity to known reason codes and allowlisted blocker causes. Do
not copy raw provider messages.
- Fence claim release and activity against replacement, reassignment,
completion and competing scheduler instances.
- Limit identical activity for one schedule and cause to one record per
five minutes.
- Keep claim release functional when diagnostic storage fails.
- Update execution documentation and the task verification maps.

## Verification

- Red: explains held monitor dispatch without replaying uncertain
execution failed because it expected one deferral activity row and
received none. The fixture used the real queue, persisted source run and
recovery action.
- Red: records bounded dispatch failure diagnostics while retaining the
schedule failed because no failure activity was recorded.
- Green: both regressions pass after the fix. The held-source test
proves no successor while held, no monitor attempt consumed, and one
wake after explicit hold resolution.
- Passed: all 43 tests in heartbeat/scheduling.test.ts and
issue-monitor-scheduler.test.ts, with at most two workers.
- Additional coverage uses real PostgreSQL for diagnostic rate limits,
replacement races and an audit-insert trigger failure. Audit failure
must not retain the monitor claim. Private error and unknown blocker
text do not reach activity.
- Passed: node scripts/check-maps.mjs and git diff --check.
- Greptile: 5/5 with no actionable issues on
1d0aec738c.
- Latest-head checks: 55 successful, two skipped, and one neutral. No
check remains pending or failed. The branch has no merge conflict.
Paperclip Review ended neutral because its agent did not return a
complete assessment; it is not counted as a validated review pass.
- The dependency added on deferral is one bounded recent activity query
and at most one audit insert. Successful admission adds no new database
read. Existing queue authority checks still decide admission.
- The process fixtures validate queue admission and persisted state.
They log workspace setup errors after admission; this report does not
claim a live provider turn succeeded or that an existing deployment was
repaired.
- Passed: pnpm --dir server exec tsc --noEmit. The wrapper typecheck was
stopped because it started a redundant cold native Runner build. Full
repository checks run in PR CI; no local full-suite or full-build pass
is claimed.

## Risks

- Audit work must not strand a monitor. The storage-failure regression
verifies that rollback is followed by a claim-fenced release.
- Recovery holds still require their current reconciliation path. This
change makes their effect visible and does not bypass ownership, budget,
pause, Stop or company checks.
- Activity can be stale after a later repair, so each entry includes the
checked time and the exact saved deadline. A replacement cannot inherit
an old dispatch diagnostic.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, tool use and code execution.
The exact runtime model suffix and context window are not exposed 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-19 16:11:31 -05:00
DottaandPaperclip 6f045207bb fix(runner): explain rejected API response windows (#16889)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents use the API fallback when dedicated tools cannot express an
operation.
> - Large responses can be read in bounded text windows.
> - The declared schema rejects windows larger than 24576 bytes.
> - The runner hid the rejected field behind a generic semantic tool
error.
> - This pull request returns safe schema locations and the accepted
byte range.
> - Agents can correct a call without guessing or changing route
permissions.

## Linked Issues or Issue Description

Refs: #16868, which added repair guidance for task monitor arguments.

**What happened?**

A native call_api request with responseText.limitBytes set to 60000
returned invalid_tool_call with no field or size guidance. The declared
4..24576 byte range already matches the server validator. The missing
repair guidance is in the runner bridge.

**Expected behavior**

Reject invalid input before dispatch. Show bounded schema locations and
the accepted window range. Do not echo submitted values or unknown field
names. A corrected request can reuse the rejected call ID.

**Steps to reproduce**

1. Use the generated call_api tool contract to request a GET text window
with limitBytes 60000.
2. Read the semantic tool error. Before this fix, it contains only a
generic rejection.
3. Retry with limitBytes 16000. The corrected call must be admitted.

**Paperclip version or commit**

Reproduced on master 5739a53f5f.

**Deployment mode**

Built from source. This affects the native runner bridge.

## What Changed

- Add call_api to the existing provider-safe schema guidance path.
- Name the text-window byte range and pagination field.
- Exercise the actual generated production schema through the provider
bridge and its error result.
- Keep private values, unknown property names, authorization errors and
response-size limits unchanged.
- Document the response window and repair behavior.

## Verification

- Red: api_window_rejection_explains_limits_and_allows_corrected_call
failed on master because the provider error did not name
responseText.limitBytes.
- Green: the same regression passes with a corrected same-ID call,
unknown-key privacy checks and unchanged authorization rejection.
- Passed: the existing runner-api.test.ts suite, 1935 tests, including
server window validation before dispatch.
- Passed: all 394 Runner core Rust unit tests, cargo fmt --check and the
generated protocol manifest check. The focused test also passed after
extraction into its own Rust test module.
- Greptile: 5/5 with no actionable issues on the final head
95d65e1646.
- The first CI release-registry run found that a compile-time test
fixture was outside the release-cache input set. The fixture now reads
the same generated schema at test runtime. The Rust regression and all
10 runner-release-crate-cache tests pass after that repair.
- This pure error mapping adds no dependency calls, provider retries or
network work. No live provider or staging deployment was needed.
- Full current-head CI passed, including repository typecheck, tests,
build, Runner verification, canary and browser suites. At
95d65e1646 there are 56 successful and
four skipped checks. The separate Paperclip Review bot completed with a
neutral check and a 5/5 static assessment with no findings. It did not
run current-head Rust, full repository or live-provider tests; its older
local cache run is not counted as current-head test evidence. No check
remains pending or failed. No local full-suite or full-build pass is
claimed.

## Risks

- Feedback must not disclose submitted values. The regression checks
private values and unknown field names, a 512-byte bound and unchanged
generic authorization errors.
- Guidance names the current byte range. The regression uses the
generated production schema so a contract change must keep this repair
guidance accurate.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, tool use and code execution.
The exact runtime model suffix and context window are not exposed 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-19 16:10:58 -05:00
DottaandPaperclip 8a3d2cb04f fix(recovery): avoid recursive holds for undispatched waits (#16894)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Queue admission must stop work while a previous execution needs
recovery.
> - The final dispatch gate can cancel a queued run before it starts.
> - Legacy recovery treats this known wait as an unknown provider
outcome and creates another hold.
> - The extra hold can survive repair of the original execution and
leave saved messages stuck.
> - This pull request uses one database proof to prevent the extra
incident and recognize old false holds.
> - Saved input can then pass through normal admission with the original
work and safety gates intact.

## Linked Issues or Issue Description

Refs #15483.

**What happened?**

The dispatcher writes an `execution_reconciliation_required`
cancellation when an existing recovery hold denies a queued run. No
runtime is selected and no provider starts. Legacy recovery later
creates a `legacy_execution_requires_reconciliation` action for that
cancelled admission. Its settled `replay: blocked` record remains an
effective hold after the real source recovery finishes. The task has
saved messages but no eligible Retry or Continue action.

**Expected behavior**

A verified admission denial stays a wait. It must not create another
provider-outcome incident. Existing false incidents must stop blocking
after the server verifies their recorded source. Real execution,
uncertain outcomes and file recovery stay held. The harness determines
these infrastructure facts; the user does not certify them in a form.

**Steps to reproduce**

1. Persist a queued-run gate cancellation with its task-bound
`executionWait` receipt and no execution authority or resource evidence.
2. Run legacy reconciliation. On the old code, it creates a second
incident.
3. Persist the old incident as resolved with replay blocked, then clear
the original source hold.
4. Restart queue recovery with saved user messages. On the old code, no
successor is admitted.

Related work: #15965 covers other pre-start dispatch cancellations but
explicitly excludes this existing-hold code and leaves historical holds
unchanged. #16634 covers stale-cancel classification and queue drain,
without re-evaluating historical effective holds. #15383 covers an
operator Stop during conversation startup. This change addresses the
existing-hold receipt and its persisted false incident; it does not copy
those broader changes.

## What Changed

- Add one database proof for a legacy admission denied by an existing
execution hold. Require the exact dispatcher receipt, matching task
identity, no runtime or process claim, no lease or finalization
coordinator, and no execution events.
- Use the proof before legacy recovery creates an incident and when it
computes effective historical holds. Keep the old audit rows.
- Preserve source file obligations and the original recovery action.
Keep the normal queue, pause, ownership, company and budget checks.
- Add real PostgreSQL regressions for incident prevention, old state,
concurrent queue recovery, message order, conflicting evidence and scope
boundaries.
- Update the execution contract and both recovery maps.

## Verification

- Red: the regression on unfixed source fails on incident
classification, historical hold projection and actual periodic
saved-message admission. The other 24 safety cases pass.
- Green: all 27 new database cases pass. Two safety assertions were
aligned with the existing contract: daily limits stop dispatch after
queue promotion; a human comment can intentionally reopen a terminal
task. The final regression checks the real daily dispatch gate and agent
pause.
- Existing `explicit-native-continuation.test.ts`: 255 tests pass.
Existing legacy recovery and PostgreSQL dispatcher suites: 61 tests
pass.
- `pnpm -r typecheck`, `pnpm build`, `node scripts/check-maps.mjs` and
`git diff --check` pass.
- GitHub PR CI run `39313204660` passed on
`ef6009e656b7c9b4c788480dce322874565960ac`. All 54 successful check-runs
and the Snyk status are green. Two optional Storybook checks were
skipped. The independent Paperclip Review completed with a neutral check
and a 5/5 assessment with no findings. Greptile also reports 5/5 on the
same commit. There are no unresolved review threads.
- `pnpm test:run` is still running locally. Its overall result is
pending; no full local suite pass is claimed. The exact-head GitHub CI
suite passed.
- A read-only probe of an affected hosted task verifies the same
database proof and projects no false blocker with the working source. No
live rows were changed. Deployment and a real resumed provider run
remain unverified.

## Risks

- An incorrect exclusion could admit work with uncertain effects. The
proof fails closed on missing receipts, source mismatches,
runtime/process ownership, any retained lease, coordinator, execution
event or file obligation. Real upstream file recovery remains blocking
in the regression.
- The effective-hold query adds indexed existence checks scoped to the
recorded run and company. It makes no provider calls and performs no
database repair writes.
- Historical audit records keep their recorded outcome. Only their
effective blocking status changes when the server can prove the
admission never ran.
- No schema, permission, credential or UI form changes are included.
Live recovery still needs verification after rollout.

## Model Used

OpenAI Codex, model ID `gpt-6.1-sol`, with reasoning, tool use and code
execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [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-19 15:26:39 -05:00
DottaandPaperclip 985d3b12b6 fix(runner): explain rejected task monitor arguments (#16868)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents use task monitors to resume work after a future check.
> - The runner rejects monitor arguments that fail the tool schema.
> - It returned a generic error with no field or repair guidance.
> - An agent could treat a repairable call as a scheduling outage and
block its task.
> - This pull request adds safe repair guidance and shows the nested
request shape.
> - Agents can correct the call and confirm a saved schedule before they
yield.

## Linked Issues or Issue Description

Refs: #15446, #16686.

**What happened?**

A rejected native `set_task_monitor` call returned `invalid_tool_call`
with only “Paperclip rejected this semantic tool call.” The agent could
not identify the invalid field. A retry could fail again and leave
unfinished work without a scheduled wake.

**Expected behavior**

Reject invalid arguments before any mutation. Give the provider bounded
schema locations and clear repair guidance. A corrected call must save a
monitor and allow one wake without duplicate effects.

**Steps to reproduce**

1. Call `set_task_monitor` with `idempotencyKey`, `nextCheckAt`, and
`notes` at the top level.
2. The schema rejects the call because scheduling fields belong inside
`monitor`.
3. Before this change, the provider received no guidance about the
required shape.

**Paperclip version or commit**

Reproduced against master `35c75b56d4` with a failing Rust regression.

**Deployment mode**

Source checkout. This affects the native runner tool bridge.

## What Changed

- Add monitor-specific repair guidance to the runner's existing safe
schema error path.
- Keep feedback within 512 bytes and omit submitted values and unknown
field names.
- Show the nested monitor request in the production tool description.
Refresh generated contracts and catalog identities.
- Test flat and nested invalid input, private value handling, and
unchanged authorization errors.
- Test rejection, correction with the same key, saved schedule and
audit, receipt replay, and one timer wake with real PostgreSQL and the
native tool authority.
- Update execution documentation and the task verification recipes.

## Verification

- Red/green: the Rust provider error regression failed on master because
monitor repair guidance was absent. It passed after the fix.
- Passed: 29 PostgreSQL monitor and scheduler integration tests. These
use the real tool authority and scheduler, with a no-op process agent
for wake delivery.
- Passed: the native Codex server vertical-slice test. It runs the
rebuilt Runner binary with a fake provider and checks durable results
and session resume.
- Passed: map structure checks, Rust formatting, and `git diff --check`.
- Passed locally: full repository `pnpm -r typecheck`, `pnpm build`, and
all 392 Rust library unit tests.
- Passed on commit `3b667ad24763c1828edd48f56ef7a10766aeb946`: [CI run
39290889768](https://github.com/paperclipai/paperclip/actions/runs/39290889768),
including all server, Runner, serialized, browser, typecheck, build, and
canary gates. Across the PR, 56 checks passed and four conditional
checks were skipped. No check remains pending or failed.
- Greptile: 5/5 on that exact commit. The check concluded success, with
no review comments or unresolved threads.
- The duplicate local `pnpm test:run` attempt did not complete. It was
stopped after the full CI suite passed; it is not counted as a local
full-suite pass.
- The first PostgreSQL attempt could not bind localhost inside the shell
sandbox. It passed with normal localhost access. The first broader Rust
attempt had 37 socket-permission failures and 355 passes; all 392 passed
with localhost access and no code change.
- Live model recovery was not run. Local execution policy did not
authorize an extra acceptance run through the signed-in provider
account. This PR does not include a deployment or recovery of an
existing task.

## Risks

- Error feedback must not reveal submitted values. Tests cover private
values, unknown keys, nested constraints, length bounds, and unchanged
generic authorization errors.
- This changes guidance only. It does not relax the schema, retry calls
automatically, or change scheduling authority.
- A new runner binary and refreshed provider contract must reach the
target instance before agents receive this guidance.

## Model Used

OpenAI Codex, GPT-6, with reasoning, tool use, and code execution. The
exact runtime suffix 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-19 12:37:28 -05:00
DottaandPaperclip d20cb69ce5 fix(heartbeat): preserve active retries when comments arrive (#16855)
## Thinking Path

> - Paperclip manages agents and their work.
> - Heartbeat retries preserve task ownership after a recoverable
failure.
> - The issue read model includes retries that are scheduled, queued, or
running.
> - The human comment route treated every retry in that model as parked
work.
> - A normal status comment could therefore cancel a running
continuation and return its task to Todo.
> - This pull request limits comment supersession to parked retries and
checks that state at the cancellation fence.
> - The agent can keep working when a person adds context. Explicit Stop
and Interrupt remain available.

## Linked Issues or Issue Description

**What happened?**

A Board comment on an in-progress task cancelled its running recovery
continuation. The handler read a live retry from
`getCurrentScheduledRetry` but did not check its status. The
cancellation also forced workspace recovery when copy-back did not
complete.

**Expected behavior**

A comment must preserve queued and running execution. Only a retry that
is still `scheduled_retry` may be superseded. If promotion wins the
race, the comment must keep the task in progress and follow the normal
wake path.

**Steps to reproduce**

1. Let a task retry advance from `scheduled_retry` to `running`.
2. Keep its retry reason and source run identity in the stored row.
3. Post a normal human comment, without an Interrupt request.
4. Observe the old handler cancel the run and move the task to Todo.

**Paperclip version or commit**

Reproduced on master base `9966f31bee`. The staging incident served
`f3cd3f4ea89539b68410841d39ec15a5c626007f`.

**Deployment mode**

Staging server with a remote runner. The defect is in the core comment
and cancellation path.

Refs #16835 and #16843. Those repairs cover runner health and workspace
preservation. This change prevents a normal comment from requesting the
wrong cancellation. Related #9732 promotes human wakes on parked
retries; it does not protect a retry that is already running.

## What Changed

- Check the live retry status in both comment routes: POST comments and
PATCH issue with a comment.
- Add an optional expected-status fence to cancellation. A concurrent
promotion or terminal outcome returns a no-op result.
- Make due-retry promotion honor a cancellation claim under its issue
and run row locks. A scheduler cannot promote work after cancellation
has won.
- Finish persisted cancellations of parked retries through the existing
startup and periodic recovery sweep. Preserve their original request and
retry a failed settlement without starting provider work.
- Keep the task active when guarded cancellation does not win. Do not
record a cancellation that did not occur.
- Add route regressions, real PostgreSQL race tests, and pending-retry
and explicit Stop safety coverage.
- Exercise both normal HTTP comment routes with real services, stored
Board membership, PostgreSQL, and a live child process. Assert that the
process can still perform work, the task keeps its original execution,
and no second executor starts.
- Document the comment supersession contract and update the task
feature/access maps with the local regression coverage and deployed
acceptance recipe.

## Verification

- Before the fix, all four POST/PATCH regressions cancelled queued or
running work.
- Before the fix, the real PostgreSQL race test cancelled a retry that
became running after lookup.
- The comment-route suite passes **103 tests**. The final dispatch,
cancellation, restart recovery and HTTP/live-process suites pass **119
tests**, for **222 affected tests** across these runs. They cover
comment delivery, both sides of the promotion race, concurrent terminal
outcomes, pending retries, explicit Stop, and a live process that can
continue work.
- Command: `node node_modules/vitest/vitest.mjs run
server/src/__tests__/issue-comment-reopen-routes.test.ts
server/src/services/heartbeat/run-control.test.ts`.
- The normal HTTP/live-process acceptance suite passes all **14 tests**,
including both comment endpoints with stored Board membership and
deterministic wakeup teardown.
- Review exposed a second race after the cancellation claim. Its new
PostgreSQL test failed before the dispatch guard with `promoted` and
passes afterward with `not_promoted`, followed by completed
cancellation.
- A real PostgreSQL constraint interrupts the terminal cancellation
write after its intent commits. The assembled recovery service failed to
settle that row before the repair. Afterward a fresh service settles it,
clears its task lock, cancels its saved wake, preserves the original
request and leaves unrelated parked retries alone. A repeated database
failure preserves the intent for a later successful sweep; a second
successful sweep adds no duplicate event.
- Full `pnpm -r typecheck` and `pnpm build` pass. Server typecheck and
build pass again after the final server-only review fix. A local
full-suite attempt started on `f8773bf` did not return an overall
result; its log is preserved and no local full-suite pass is claimed.
The new recovery change passes the focused suites and fresh server
typecheck/build. Latest-head CI on
`8f928adee67c089e1ac405d2ad3b169eedb8da27` is green: the full PR
workflow and commitperclip review passed; only the two optional
Storybook jobs skipped.
- Greptile reviewed final head
`8f928adee67c089e1ac405d2ad3b169eedb8da27` at **5/5**, with no
unresolved findings. All actual latest-head CI checks passed; the two
optional Storybook jobs skipped.
- `node scripts/check-maps.mjs` passes after the map updates (34 areas,
172 capability IDs); it checks structure only.
- Fixed-revision staging acceptance is pending deployment. The original
workspace remains held and its latest source checkpoint is backed up and
verified.

## Risks

- A promoted retry must keep its execution and ownership when a comment
arrives. The database status predicate and route no-op handling enforce
this at the race boundary.
- A cancellation intent must prevent promotion without leaving work
parked forever after a restart. The recovery sweep consumes at most 20
such intents per pass through guarded cancellation, and a failed
settlement keeps its intent for retry.
- A parked retry must still accept fresh instructions. Its existing
cancellation and wake behavior remains covered.
- Ordinary Stop and Interrupt calls omit the expected-status option.
Their cancellation authority is unchanged and remains covered by the
run-control suite.
- No schema migration or API request contract changes are required.

## Model Used

OpenAI `gpt-6.1-sol` in Codex, with extra-high reasoning, tool use, and
code execution. The session does not expose its context-window limit.

## 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
- [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-19 11:34:27 -05:00
DottaandPaperclip 016e40d532 fix(recovery): preserve shutdown export ownership through finalizer drain (#16843)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Legacy adapters must save sandbox files before Paperclip releases
their source.
> - Graceful shutdown gave each adapter a shared 30-second settlement
wait.
> - When that wait ended, cleanup could stop the sandbox while the
adapter still exported files.
> - That race created a restore failure and blocked the next turn.
> - This pull request keeps export ownership through the finalizer drain
and renews its cleanup fence.
> - A successful late export can release its source and schedule one
bounded continuation.

## Linked Issues or Issue Description

Refs #15857. This corrects the timeout path introduced by the shutdown
settlement fence.
Refs #16478. That repair clears verified historical holds; this change
prevents an active export from losing its source.
Refs #16835. That merged fix handles remote native runner ownership.
This change handles legacy shutdown settlement.

## What Changed

- Keep a late adapter settlement in the shared execution finalizer set.
Wait for it before lease release, recovery classification, and retry
scheduling.
- Renew the exact owned settlement fence every 10 seconds. Use a
30-second lease, one renewal in flight, a one-second database lock
timeout, and a five-second statement timeout. Report bounded failure
categories without raw database errors or credentials.
- Refuse ordinary cleanup while the local adapter still owns settlement.
Preserve the explicit owner stop-and-retain path.
- Give the finalizer drain a further shared 30-second wait after the
initial shutdown drain.
- Document the ownership rule, the acceptance recipe, and the
controller-loss limit.

## Verification

- Red before the repair: the database-backed expired-wait regression
expected cleanup denial but received permission to stop the still-owned
source.
- Green on `615a4382c5`: all 526 tests in `recovery.test.ts`,
`legacy-workspace-restore-recovery.test.ts`, `shutdown.test.ts`, and
`heartbeat-process-recovery.test.ts` passed.
- The assembled heartbeat regression passed. It verifies no early
release or retry, joining the late owner, successful cleanup, one
durable scheduled continuation, and no duplicate after service reload.
- Safety coverage includes other-controller fencing, controller loss,
exact source retention, stop-only cancellation, completion winning the
status race, and real database lock contention during renewal.
- `pnpm -r typecheck`, `pnpm build`, and `node scripts/check-maps.mjs`
passed on the final source revision.
- A broad local `pnpm test:run` crossed the rebase and reported eight
companion supervision failures. It was stopped and its log retained. The
complete companion file then passed all 753 tests on current source.
This is not a passing full local suite; current-head CI covers the
complete repository checks.
- The 34 recovery tests include real lock-timeout classification and
credential-shaped error redaction.
- Current-head CI: 53 checks passed; two optional Storybook checks
skipped. Greptile is 5/5 on `615a4382c5`; no unresolved review threads.
- Live sandbox acceptance remains unverified. Three disposable
local-controller/Daytona attempts used legacy Codex ACP, `gpt-6-astra`,
API-key authentication, and the verified image
`ghcr.io/paperclipai/paperclip-daytona-runner@sha256:a2bd08cefb68d9217bdd75cf87a6b51f1705b18a4796eff3f97b2726a548b8fb`.
ACP initialization exited before model execution on macOS Node 26.4.0
and again on Node 24.21.0; one attempt exposed a local process-session
socket `ECONNRESET`. That does not establish the transport failure's
root cause. No attempt reached the delayed export or second turn, so
this PR remains draft pending faithful live acceptance. Probe setup
defects were retained separately from product failures, and all
test-owned sandboxes were cleaned up.

## Risks

- Shutdown can spend up to 30 more seconds joining finalizers. The
shipped Compose stop grace remains 90 seconds.
- Renewal adds at most one database operation per owned run every 10
seconds during shutdown. Lock and statement deadlines bound database
contention. The local adapter still protects its source if renewal
fails.
- Controller death or an export that outlives the whole process still
needs retained-source repair. This change does not reconstruct a legacy
export after process death or clear historical failed-restore holds.
- No schema or public API changes. Native restart ownership, approval
gates, and retry limits retain their existing behavior.
- This PR has not been deployed. It does not recover an existing
retained sandbox by itself.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository inspection, code
execution, and test tools. The exact backend model identifier and
context window were not exposed 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-19 09:19:09 -05:00
DottaandPaperclip f3cd3f4ea8 fix(runner): require confirmed exit before remote replacement (#16835)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Remote runs need one runner to own their process identity and
listener.
> - A health request can fail while that runner continues to work.
> - The active monitor treated a failed request as a process exit.
> - Recovery could then launch another runner against the occupied
listener.
> - This pull request retries health reads and requires proof of exit
before replacement.
> - A brief outage can recover on the same runner without replaying
agent work.

## Linked Issues or Issue Description

Refs: #16341 adds remote recovery diagnostics. Refs: #16686 retries
verification when restoring a saved remote session. This change applies
the shared probe policy to active process supervision.

**What happened?**

The active remote monitor settled its completion promise after a
timed-out or rejected health request. The transport treated that result
as an exit and started process recovery. An existing runner could still
own the listener. Replacement then failed with
`runner_ingress_bind_conflict` until the reconnect deadline expired.
Missing or changed ownership markers also did not prove that the old
process had exited.

**Expected behavior**

Retry transient read failures on the same runner. Require a matching
ownership marker and proof of process exit before replacement. Preserve
uncertainty when identity or liveness cannot be verified.

**Steps to reproduce**

1. Start a remote runner and leave its listener alive.
2. Make one process-health request time out or reject.
3. Observe completion and the restart decision. Before this change, the
monitor settles while the process is alive, and the restart wrapper
permits another launch.

**Paperclip version or commit**

The defect was reproduced on master at `6e89919631`. The patch is based
on current master at `c6a0cf4bce`.

## What Changed

- Reuse the bounded read-only recovery probe for active supervision.
Retry up to three reads within 30 seconds.
- Check the saved process generation when available. Accept only the
specific process-not-running receipt as exit proof.
- Add an exit-confirmation contract to remote process handles. Require
it for restart and shutdown completion. Preserve the cause of
supervision errors.
- Test transient failures, exhausted retries, conflicting identities,
real listening processes, and an authenticated runner with a fake
provider. Keep confirmed-exit replacement covered.
- Document the recovery policy and coverage limits in the execution
contract and maps.

## Verification

- Red: the timeout and transport-error regressions failed before the
patch because completion settled while the runner was alive. The
restart-guard regression also failed because replacement was allowed.
- Green: 14 server supervision cases passed, including a real listening
process. All 120 shared control-plane tests passed. Focused Runner tests
passed for authenticated live-runner fault injection and confirmed-exit
replacement.
- Review regression: a replacement-health test first failed because its
error cause was lost. It passed after preserving the cause and detail.
The four affected transport cases and Runner typecheck then passed.
- `node scripts/check-maps.mjs` passed for 35 areas and 176 capability
IDs.
- `pnpm -r typecheck` and `pnpm build` passed locally. The sequential
local full-suite run was stopped before completion; no local full-suite
pass is claimed. Final commit `bcc1b1d339` passed the complete CI
matrix: 55 checks passed and two optional Storybook checks were skipped.
Greptile is 5/5 with no unresolved conversations. The PR has no merge
conflicts.
- The first real-listener attempt could not bind inside the execution
sandbox. The same test passed with local socket access. The first
package-test invocation used the root configuration and selected no
tests; the package configuration ran all selected tests.
- CI attempt 1: nine test jobs were cancelled together at 12:54:59 UTC.
Their test steps report cancellation. The build and uninterrupted jobs
passed. The interrupted jobs were retried once. That retry exposed one
confirmed-exit message assertion failure in the Runner shard; the other
jobs passed. The compatibility correction preserves the prior message.
The failing case and four recovery cases then passed locally. The
complete CI matrix passed after that correction.
- No live sandbox restoration, deployment, or historical task recovery
is claimed.

## Risks

- A persistent outage now ends supervision with explicit exit
uncertainty. It cannot authorize a competing process. Existing ownership
and workspace recovery checks remain required.
- Health reads can take up to 30 seconds before reporting an unavailable
runner. Retries are read-only and bounded.
- Local launchers keep their existing completion contract. No database
migration or public API change is required.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, tool use, and code
execution. The exact serving snapshot 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-19 08:19:37 -05:00
DottaandPaperclip 1a79c8bdc7 fix(runtime): honor provider startup budgets on sandbox resume (#16802)
## Thinking Path

> - Paperclip manages AI agents and their execution environments.
> - Follow-up runs reuse saved sandbox computers to keep their
workspace.
> - Starting a stopped or archived computer can take more than one
minute.
> - Acquisition already uses the provider's startup budget, but reuse
imposed a separate one-minute deadline.
> - This pull request gives resume the configured or declared provider
startup budget.
> - The benefit is that a healthy slow start can continue on the same
computer.

## Linked Issues or Issue Description

**What happened?**

A follow-up run failed during setup with `ReusableSandboxResumeError`.
The saved computer became ready just after the host stopped waiting. The
environment had no explicit timeout. Its provider declared a five-minute
startup default, which the resume path ignored.

**Expected behavior**

Resume must honor the configured startup budget or the provider's
declared default. It must retain the same lease. Failed or uncertain
resumes must keep the saved computer.

**Steps to reproduce**

1. Use a reusable sandbox provider with a startup budget longer than one
minute.
2. Retain its lease and start a follow-up run.
3. Make provider resume finish after 61 seconds, within that budget.
4. Observe that the old host rejects at 60 seconds.

**Paperclip version or commit**

Reproduced on `aaeff418cf72341c674ff3393cd94f40dc4855f6`.

**Deployment mode**

Hosted sandbox execution. The regression uses a local isolated
PostgreSQL database and controlled worker RPC timers.

Searched open and closed PRs and open issues for sandbox resume and
timeout fixes. No duplicate fix was found. Related acquisition timeout
handling already exists on the base branch.

## What Changed

- Use the provider's declared acquisition timeout when no explicit
startup timeout exists.
- Apply the resolved RPC budget to the shared resume/retry deadline
instead of 60 seconds.
- Add two deterministic 61-second resume regressions for declared and
explicit startup budgets. Assert exact RPC budgets, including the
30-second margin, and check that the same lease remains active without
replacement.
- Document the timeout behavior, provider-author contract, and
verification recipe.

## Verification

- Red: both new cases failed before the production change with
`ReusableSandboxResumeError`, caused by `Plugin RPC timed out`.
- Green: `pnpm exec vitest run --fsModuleCache
server/src/__tests__/environment-runtime.test.ts` passed all 163 cases.
The final review follow-up also passed both slow-resume cases with exact
RPC-budget assertions (330 seconds by declaration; 150 seconds by
explicit config).
- An earlier full run passed the 161 existing cases but timed out in the
two new tests because their fake clock also captured database
scheduling. The corrected test restores real timers before lease
persistence. The complete rerun then passed.
- `node scripts/check-maps.mjs` passed.
- `pnpm -r typecheck` and `pnpm build` passed locally.
- Started `pnpm test:run`, then stopped the duplicate serial run in
favor of complete parallel PR CI. The repository timing data totals
about 126 minutes for server suites alone. Full parallel CI passed on
the final head.
- CI attempt 1 had one unrelated failure:
`FileViewerSheet.copy.test.tsx` expected the asynchronous `Copied link`
confirmation before React rendered it. That test and component are
unchanged from the base branch. All six cases passed unchanged locally.
A single rerun of the failed shard passed; attempt 2 of the full
workflow succeeded. The timing-race diagnosis is an inference from the
test helper, not a claim that this PR repairs that UI test.
- Greptile scored the final head 5/5. Both review findings are fixed and
both threads are resolved.
- The consequential assumption is that provider startup may exceed one
minute. The test preserves the RPC timeout race with controlled timers
and uses real lease persistence. Read-only provider evidence also showed
the retained computer ready just after the failed run ended. The patch
has not been deployed to that instance.

## Risks

- A slow or unavailable provider may hold setup for its configured
startup budget instead of one minute. Retries still share one finite
deadline. Providers with no explicit or declared timeout keep the
one-minute default.
- Lease identity, company scope, backup validation, and replacement
decisions use the existing paths. No schema or dependency changes are
required.

## Model Used

- OpenAI Codex, GPT-6. This session supports reasoning, tool use, code
editing, and test execution. The exact runtime variant and
context-window size are 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-18 22:04:23 -05:00
DottaandPaperclip 3858d2341d fix(recovery): default saved-monitor waits and keep task recovery quiet (#16686)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - A task must keep a durable path to its next action.
> - A provider can save a task monitor, then fail to yield because it
omits internal continuation fields.
> - An interrupted provider-pack check can then be reported as permanent
artifact incompatibility.
> - The task asks its user to certify recovered workspace files, even
though this is an operator responsibility.
> - This pull request derives the saved monitor continuation, bounds
verification retries, and removes that task form.
> - Normal recovery stays quiet. Unresolved holds retain inspection and
the existing safety checks.

## Linked Issues or Issue Description

**What happened?**

A yielded completion without `continuation` is rejected even after the
provider saves an eligible monitor on the task. Repeated attempts cannot
finish. Remote verification also treats timeouts and missing exit status
as artifact incompatibility. The task recovery notice exposes raw
diagnostics and asks the task user to certify infrastructure files.

**Expected behavior**

Use current persisted task state to fill routine continuation fields.
Retry interrupted verification reads within a fixed bound. Keep normal
recovery in the working state. Show a compact notice only when work
cannot safely continue.

**Steps to reproduce**

1. Save an eligible monitor on the active task.
2. Call `paperclip_finish` with `reportedWorkDisposition: "yielded"` and
omit `continuation`.
3. Return a timed-out remote provider-pack command with empty stderr
during recovery. Inspect the resulting task notice.

**Paperclip version or commit**

Source baseline: `aaeff418cf` (master integrated with its current task
visibility and saved-source reconstruction rules).

Related changes: #15824, #16104, #16451, #16493, #16690. This fix
complements saved-result recovery and verified pack reuse. It does not
replace their workspace ownership checks.

## What Changed

- Allow omitted or null continuation at the provider input boundary.
Read current authorized task context and fill a monitor continuation
only for the bound run and an eligible saved monitor. Keep accepted
results strict. Apply this to Codex, OpenCode, both direct and
production-sidecar ACPX paths, and external drivers. Reserve the
semantic call identity during the saved-context read and reject
cancellation or a changed run/turn.
- Bind the shared completion change and continuation helper in a new
Copilot v21 source declaration. Preserve v20 history and its decoder
support. Qualification stays pending.
- Expose the eligible continuation in task context. Completion admission
still checks persisted monitor state. No schedule is created or renewed
by the default.
- Retry read-only provider verification at most twice per command. Hold
exhausted reads with a distinct verification-unavailable cause.
Reattachment cannot renew the read budget through an unchanged
provider-attempt counter. Keep completed verification failures and wrong
versions incompatible.
- Remove the task file-certification form. Hide the notice only for a
matching durable recovery path without an unknown-action replay hold.
Keep missed retries, unsafe outcomes, and unresolved file holds visible.
- Add production-component stories for automatic recovery, blocked and
expired holds, reader access, persisted repair decisions, source
reconstruction, queued continuation, and mobile. Preserve master’s
guarded saved-source reconstruction choice after confirmed source
destruction. Update the execution contract and access maps.

## Verification

- Completion, helper, Codex, OpenCode, ACPX, and profile regressions
pass. After the sidecar review fix, 271 tests across the production
sidecar and three provider suites pass, plus 72 helper/profile tests.
The real sidecar MCP bridge tests cover saved-monitor acceptance,
no-monitor refusal, cancellation, duplicate call identity, and
raw-versus-forwarded semantic receipt hashes. The OpenCode tests also
cover eligible-monitor acceptance and refusal without one.
- The fresh full native executor suite passes all 743 tests. Four
profile, installation-integrity, and ACPX driver suites pass all 168
tests. The earlier CI fingerprint failure required a new immutable
source declaration; its follow-up Docker failure required the matching
context allowlist entry. Both fixes are verified locally. Targeted
server monitor, tool authority, and provider verification suites pass.
The probe tests include a real timed-out subprocess, an unrelated
responsive timer, transport errors, and completed integrity rejections.
- All 90 tests in the merged task notice, reconstruction choice,
recovery display, and lineage suites pass. They cover reader access,
live/missed/mismatched recovery, unknown outcomes, saved messages, retry
refusal, and removal of the file form. Both review findings have
failing-baseline/passing-treatment regressions: identical polling across
retry expiry, and a failed refresh after cached running activity.
- Browser walkthrough passed for automatic recovery, unresolved details,
a 390px mobile story, and the merged saved-source reconstruction and
queued states. The reconstruction control starts disabled and normal
automatic recovery shows only Working. The stories use production
components and fixture API data. They do not prove live cloud recovery.
- Token gates, map checks, and Storybook catalog generation pass. The
exact Docker context integrity command passes locally after adding the
new build declaration to its allowlist.
- Workspace typecheck and build pass on the final source, including the
versioned identity update. The final server and UI guards pass their
focused tests, typechecks, builds, and token gates. The final standalone
Storybook builds. The saved-monitor default retains master’s stricter
child-task visibility filter. Re-review caught an additional
production-sidecar validator before forwarding; the fix now defaults at
that boundary and has passing regression controls. After preserving
master’s new reconstruction capability, 68 targeted reconstruction,
tool-authority, and provider-verification server tests pass. The
current-master integration at `2a80050a0d` has a fresh 5/5 review with
zero unresolved threads. Its complete CI matrix, including Runner tests,
is the merge acceptance record.
- On the final master integration, local typecheck, build, and all 90
recovery UI tests pass. A concurrent runner run hit five 5-second
sidecar startup timeouts; an isolated sidecar run passed 86 tests and
timed out in the unrelated unsupported-bootstrap case. Those local
deadline failures remain recorded; the canonical CI Runner lanes use
their normal deadlines and pass. The unsharded local full-suite attempt
did not complete. It spanned source edits; the fresh native executor run
above passes. An unrelated workspace fixture timed out once and then
failed to reserve 32 contiguous ports on this host. Its unchanged-base
control and CI server suite pass. Full-suite acceptance uses the fresh
latest-head CI matrix; this PR does not claim a passing unsharded local
run.

## Risks

The default applies only to an eligible persisted monitor. An explicit
invalid continuation still fails validation. The added read occurs only
when a yielded report omits its continuation. Admission rechecks current
state after that read.

An interrupted verification command can add one extra 30-second probe.
Completed rejections stay closed. These probes do not change the pack,
launch provider work, or replay external tools. Two inconclusive reads
hold the original run. They do not schedule another reattachment with an
unchanged provider-attempt counter.

This PR’s pending Copilot v21 declaration binds the changed shared
contract, resolver, direct driver, and production sidecar sources. The
previous v20 declaration is immutable and remains decodable, but launch
requires the new exact identity. Qualification remains pending.

Task users lose the file-certification form. Board-authorized
source-only repair remains available through the resolution API with the
original evidence and stop-receipt requirements. This branch does not
deploy or recover historical held runs.

> This is a bug fix and UI polish within existing recovery behavior.
ROADMAP.md was checked.

## Model Used

OpenAI Codex, `gpt-6.1-sol`, xhigh reasoning, with tool use and code
execution. The host does not expose the context-window limit.

## 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 (changed-area suites; the
full local run limitation is recorded 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-18 21:43:15 -05:00
Devin FoleyandPaperclip d30648dee8 Preserve accepted messages for paused recipients (#16679)
Keep accepted deferred requests pending while their recipient is paused and deliver through normal gates after resume. Preserve company, task hold, and cleanup checks.

Verification: 247 focused checks, recursive typecheck, the full CI test and build matrix, and Apex 5/5. The local Git fixture initial-branch limitation is documented in the PR.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-17 18:38:17 -07:00
Devin FoleyandPaperclip 391dd15ff2 fix: recover deferred messages and completed unblock waits (#16673)
Recover accepted deferred messages through the latest finished issue run and
recognize exact completed agent unblock notifications as passive idle waits.

Preserve assignment handoffs, pause and Stop, failed outcomes, and cleanup
ownership. Honor local and SSH release contracts and atomically settle cleanup
claims. Add PostgreSQL recovery, concurrency, and interruption regressions.

Validation: recursive typecheck and build pass; 678 focused tests pass; full CI
passes; Apex review is 5/5 with no unresolved findings.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-17 16:53:29 -07:00
Devin FoleyandPaperclip c7ea5c0216 perf(server): skip settled committed finalizations in unscoped passes (#16485)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - On restart, `/api/health` reports `starting` until startup recovery
finishes, and deploys wait for it
> - Native recovery and the orphan reap each run an unscoped native
finalization reconciliation pass, and readiness waits for both
> - The pass selects every committed finalization with a result,
including runs that ended long ago, and spends dozens of sequential
queries on each one
> - On a busy instance this is about one minute per pass, so about two
minutes of every restart, and the periodic recovery repeats it on every
tick
> - This pull request skips settled committed finalizations in unscoped
passes, and keeps a small rotating upkeep page so their upkeep still
runs
> - The benefit is faster restarts and less steady database load, with
no change for unsettled work

## Linked Issues or Issue Description

Related: #16330 and #16329 (startup and reconciliation timing).

**What existing behavior does this improve?**
Startup recovery time and steady-state recovery load.

**Current behavior**
- The unscoped candidate scan in `reconcileNativeFinalizations` returns
every native finalization with a result and phase other than
`terminal_failure`. Nothing excludes rows that are already settled.
- Each committed row then takes the full per-row path: pending-effect,
assessment, evidence and decision reads, presentation repair (twice),
the workspace barrier read, a `finalizeNativeRun` re-read of the
committed coordinator, and workspace settlement (lease release check and
agent status update). That's about 40–60 sequential round trips per row.
- Startup runs the unscoped pass twice (native recovery, then the orphan
reap), and readiness waits for both. The periodic orphan reap runs it
again on every tick.
- On one instance, all 268 candidates were committed finalizations whose
runs had ended. Native recovery took 50–80 s and the orphan reap 47–80 s
on every restart, with database round trips of about 3.4 ms.

**Proposed behavior**
An unscoped pass skips a committed finalization when:
- its run is `succeeded`, `failed`, `cancelled` or `timed_out`, and
- the run finished more than an hour ago, and
- its decision has no `pending` or `failed` status-decision effect.

Each unscoped pass also includes up to 10 such settled rows in a
rotating order, so the presentation and evidence upkeep still reaches
every row over time. Scoped passes (explicit run IDs) are unchanged.

**Reason and benefit**
These settled rows have nothing left to finish. Recent rows, unfinished
runs and rows with undelivered effects stay in every pass, so
crash-window repairs and effect delivery behave as before. Startup and
periodic passes no longer scale with the full history of committed runs.

**Breaking changes**
None. A late repair on a settled row can be delayed until its turn in
the upkeep rotation instead of the next pass.

## What Changed

-
`server/src/services/native-runtime/native-finalization-reconciler.ts`:
- `settledCommittedFinalization(now)`: the settled predicate above,
including a `NOT EXISTS` on undelivered `status_decision_effects` for
the same company, issue and decision.
- The unscoped candidate scan adds `NOT settled OR run in the upkeep
page`.
- `nextSettledCommittedUpkeep`: up to
`NATIVE_SETTLED_COMMITTED_UPKEEP_PER_PASS` (10) settled run IDs after an
in-process cursor, ordered by run ID, wrapping at the end.
- New constants `NATIVE_SETTLED_COMMITTED_GRACE_MS` (1 hour) and
`NATIVE_SETTLED_COMMITTED_UPKEEP_PER_PASS`, plus
`resetSettledCommittedUpkeepForTests`.
- New
`server/src/__tests__/native-finalization-settled-candidates.test.ts`.
- `doc/execution-semantics.md`: describes the settled rule and the
upkeep rotation.

## Verification

- `pnpm exec vitest run
src/__tests__/native-finalization-settled-candidates.test.ts` (in
`server/`): 4 passed. The test mocks the timing hook to capture the
candidate count and stop the pass after the scan:
- 25 settled rows give 10, 10, 5, then 10 again over four passes
(rotation and wrap).
- A row finished 10 minutes ago, a running row, and a terminal row
without `finished_at` are all candidates.
  - A settled row whose decision has a `failed` effect is a candidate.
  - A scoped pass with an explicit run ID still selects a settled row.
- `pnpm exec vitest run` for `native-finalization-recovery`,
`legacy-finalization-regression`, `native-run-finalizer-telemetry`,
`native-workspace-finalizer`, `native-finalization-reconciler-timing`,
`external-chat-wait.integration` and `native-status-arbiter-corpus`: 316
passed.
- `heartbeat/recovery.test.ts`: the 8 tests that do not need a database
passed. The 20 database tests were skipped locally because embedded
PostgreSQL could not start (the host's shared-memory segments were
exhausted by other processes). CI runs them.
- `pnpm typecheck` in `server/`: passed.

## Risks

- A settled row that needs a late repair (for example a missing chat
presentation) waits for its turn in the upkeep rotation, up to
`ceil(settled rows / 10)` passes, instead of the next pass.
- The upkeep cursor is in memory. After a restart the rotation starts
from the beginning; the work is idempotent.
- A run left `running` with a committed finalization is never considered
settled, so it is still projected before the reaper's active-run loop.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), 1M context, through Claude Code
with tool use (shell, file edits, test runs, database and log analysis).
A separate Claude agent mapped the recovery call tree.

## 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-15 16:42:15 -07:00