Commit Graph
6300 Commits
Author SHA1 Message Date
DottaandPaperclip 612d9931a6 Support per-turn Runner sessions on ephemeral web servers (#18932)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The web server controls native Runner sessions in remote sandboxes.
> - An ephemeral web server must recover without its previous local
filesystem.
> - The existing admission rule accepts warm sessions. Managed Claude,
Grok, and OpenCode sessions resolve to per-turn execution.
> - Per-turn execution also needs durable provider-session bytes after
the sandbox is retired.
> - This pull request adds encrypted backup publication and removes the
warm-only admission rule.
> - Both lifecycle modes can use the existing native ownership and
checkpoint contracts during a web deployment.

## Linked Issues or Issue Description

Refs: #18518, #18529, #18826, #18835, #18841, #18864, #18887.

**What existing behavior does this improve?**

Native Runner execution on web servers with
`PAPERCLIP_EPHEMERAL_SERVER=true`.

**Current behavior**

The admission rule requires a warm Runner lifecycle. Managed provider
credentials can resolve an agent to per-turn execution, so enabling the
ephemeral deployment profile blocks those turns. A database controller
checkpoint does not preserve provider files after the old web home and
per-turn sandbox are removed.

**Proposed behavior**

Admit native execution in either resolved lifecycle. Keep provider and
credential choices unchanged. Publish encrypted provider state to shared
storage before sandbox cleanup. Restore it from a fresh web home for the
next turn. Use the existing controller lease and generation checks for
active takeover.

**Reason and benefit**

Operators can remove the web server volume without excluding per-turn
providers or forcing managed accounts into warm sessions. New native
Daytona profiles automatically enter both rolling-deployment
qualification lanes.

**Breaking changes**

The additive migration creates a backup publication ledger. Existing
volume-backed local-disk installations retain their local backup path.
S3 installations publish encrypted backups and require a stable secrets
master key. Existing-volume enrollment must import surviving session
data before detaching the volume.

## What Changed

- Admit native Runner execution independently of its warm or per-turn
lifecycle. Keep host-owned legacy execution excluded from ephemeral
deployments.
- Store private provider state as encrypted objects. Journal uploads
before I/O, read them back, and fence publication against the current
native owner. Keep the previous published backup when storage or
ownership checks fail.
- Restore exact provider bytes into a private cache. Require an exact
durable backup before sandbox cleanup. Extend the existing bounded
retention pass to retire stored objects without adding idle polling.
- Import selected legacy controller and provider state in the existing
offline maintenance process. Preserve queued work, task pointers,
pending recovery, and original files. Prefer canonical database
checkpoints.
- Preserve already-failed legacy controllers with missing original
artifact proof as encrypted archives. These archives do not become
execution authority. Successful retained sessions still require proof.
Report archival and executable imports separately.
- Verify unmanaged Grok API keys through the existing xAI account
validator. The live test exposed a readiness gap that kept remote Grok
agents paused after platform checks. Preserve failed credentials and the
separate runtime warning.
- Ingest Grok whole-prompt usage at the owning ACP boundary. Bind the
receipt to the exact outbound request/session; reject replay, conflicts,
and incomplete authority. Separate turn cost from cumulative session
cost in the sidecar schema and Rust normalization. Preserve managed
billing identity and identify direct xAI API credentials.
- Preserve published workspace directory names across cold allocations,
including destroyed-allocation cleanup. Keep unpublished epoch/lease
directories isolated and refuse to overwrite an occupied preparation.
This fixes the cold-session identity regression exposed by #18922.
- Increase bounded checkpoint-header inspection to 1 MiB. Retain the
aggregate inspection limit.
- Register all nine native Daytona profiles in per-turn and warm
rolling-deployment suites. Delete the entire departing server home,
exercise ordinary reads and writes, require active ownership transfer,
then check a later turn on another fresh server. Permit only exact
notification replay after reconnect.

Ownership stays with the existing modules: Runner validates its protocol
state; Core validates run scope and controller ownership; storage owns
object I/O; the maintenance process owns offline conversion; the E2E
wrapper owns process replacement and probes. No provider protocol is
added to the shared admission rule. Grok receipt parsing stays in its
provider module. The shared sidecar gains an explicit cost basis because
its existing price field means session-cumulative spend, while Grok
reports prompt totals. Existing consumers retain cumulative behavior
when the new field is absent.

## Verification

- Focused native admission, archive validation, publication fencing,
cleanup, readiness, legacy import, and E2E observer tests pass. Real
PostgreSQL cases verify that queued intent and historical recovery rows
stay unchanged.
- Full typecheck and build pass on the rebased branch. The review
follow-up passes server typecheck, 29 focused
PostgreSQL/archive/readiness tests, and module-boundary checks. Revision
fb8d39a7a1 reached 55 successful CI checks and Greptile 5/5 with no
unresolved threads. The Grok follow-up passes Runner/server typechecks,
238 ACP runtime/projection/usage tests, 16 sidecar-schema tests, the
Rust cost regression, and Core billing regression. The e9aae9cf revision
passed all 57 successful checks, with 5 skips and 2 neutral checks, and
exact-head Greptile 5/5. The latest 9cbec30a4f follow-up changes only
E2E catalog/admission controls; its 291 affected tests pass, all 57
check contexts succeeded (5 skips, 2 neutral), and exact-head Greptile
is 5/5 with no open review threads.
- Migration numbering and safety checks pass.
- A local real PostgreSQL/S3 roundtrip preserved 2,870,101,591 payload
bytes across 89,100 archive entries after deleting the source directory.
Restored SHA-256 matched; upload/readback took 56.5 seconds, restore
74.8 seconds, process peak RSS was 1.40 GB, and all fixture resources
were removed.
- Archives preserve contained relative links, reject link
chains/traversal, and enforce independent byte/entry/object limits.
Offline verification streams hashes. Orphaned deleted-run backups stay
on the original volume. Obsolete object retention preserves the selected
backup, live lease references, and pending uploads with owners.
- Isolated API-key Codex deployment: original active runner/session
transferred to the replacement and committed. All 567 reads, 57 writes,
and 57 readbacks succeeded. A second replacement completed a
same-session continuation with all 287 reads and 29 writes succeeding.
The strict cell failed two agent-authored shell commands; this result is
retained as failed.
- Isolated API-key Claude deployment: original active runner/session
transferred and committed. All 584 reads, 59 writes, and 59 readbacks
succeeded. A second replacement completed a same-provider-session
continuation, with 211 reads and 22 writes succeeding. The strict cell
failed an initial completion call that omitted a registered attachment
citation; the agent corrected it. This result is retained as failed.
- Isolated API-key OpenCode deployment: the same active runner/session
transferred and committed. All 481 reads, 49 writes, and 49 readbacks
succeeded. A second replacement retained the provider session and
completed useful work, with 156 reads and 16 writes succeeding. The
strict cell failed agent tool calls: a denied external-directory write,
malformed artifact hashes, and a second completion after the first
result was committed. There was one submitted/completed turn and one
committed result; reconnect notification replay was identical. This
result is retained as failed.
- Grok's real first turn now completes with settled usage and provider
cost ($0.41397); the next turn is admitted normally. That attempt then
exposed a shared workspace-path identity regression before deployment.
Three cold-restore cases reproduced it before the fix; 106
workspace/transport tests and four focused cold-restore cases pass
afterward, including destroyed allocation and occupied-directory
preservation. Server typecheck and build pass. At b5b0dbb03f both
controller switches passed the read/write probe without failure, and the
active runner retained its process while the controller generation
advanced. The third turn exposed an invalid reasoning-token subset
check; ad07e681ba fixes it with the actual receipt regression. Two later
live attempts failed with an upstream Grok rate limit before deployment.
A tiny preflight prompt succeeded before the second attempt, but the
full workload was still rate-limited. The final live gate remains
incomplete. No accounting or identity guard was bypassed. Earlier failed
attempts and their exact resource-cleanup receipts remain recorded.
- Earlier Codex/Claude/OpenCode receipts predate #18922; they establish
their tested revisions. Current-head live qualification remains pending;
#18943 separately preserves pre-enrollment logical/provider workspace
addresses and adds actual legacy migration proof. Its first Codex
migration attempt stopped safely at skill-version verification.
- Earlier attempts remain recorded: missing local provider-pack setup
blocked one Claude launch, and the observer rejected identical reconnect
replay in another. Setup and the observer were corrected before the
latest run.
- Named staging enrollment has not started. Its serving revision and
post-deployment journeys remain separate delivery gates.

- Broader E2E harness validation exposed deployment-catalog and
installed-lane coverage drift; the affected 291 controls now pass.
Twelve other failures reproduce on unchanged base 1a39dea9aa. They
concern cleanup call expectations, dated operating-overhead fixtures, a
Pi profile-version expectation and workflow-security expectations. No
baseline failure is reported as passing.

## Risks

- Provider archives can contain credentials. Objects use authenticated
encryption with scoped associated data and a wrapped data key. The
original stable master key remains required.
- Storage failure prevents publication and sandbox cleanup. Operators
must keep shared storage available and preserve the previous volume
during enrollment.
- Legacy state without verified identity cannot become executable
recovery authority. Archival preserves evidence and existing failures;
it does not recover already-lost provider files or clear blocked tasks.
- Local overlap probes establish the tested application behavior. They
do not establish Railway routing behavior or live managed-subscription
qualification.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, tool use, code execution,
and live integration tests. The exact runtime variant and context-window
size are not exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
canary/v2026.1104.0-canary.7
2026-11-04 02:12:51 -06:00
Devin FoleyandPaperclip 1a39dea9aa fix(heartbeat): keep a routine claim queued while its slot is held (#18933)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A routine creates one task per tick. Only one open task of a routine
can hold an execution lock (`issues_open_routine_execution_uq`).
> - Since #18879, a failed native run that waits for a scheduled resume
keeps its task's lock. Routine dispatch counts only queued, running or
scheduled runs as live, so it can queue a newer task of the same routine
meanwhile.
> - When that newer run is claimed, stamping its execution lock violates
the unique index. The raw database error is not a deferrable claim
rejection, so it escapes the queue loop. The queue code notes that this
can stop the agent's queue and startup recovery.
> - This pull request turns that exact violation into a 409 claim
rejection, so the run stays queued until the slot is free.
> - The benefit is that a busy routine slot delays one run instead of
stopping the queue or startup.

## Linked Issues or Issue Description

Refs #18879 (keeps a failed run's lock while its resume is pending),
#18916 (defers the same unique-index conflict in restart recovery's lock
reclaim) and #18931 (open; keeps one run's restart-recovery failure from
stopping startup recovery). This is the same index conflict on a
different path: the heartbeat queue claim, not restart recovery. It does
not change #18916 or #18931. Its doc paragraph is separate from the one
#18931 edits.

**What happened?**

While auditing the lock paths from #18879 for this index, I found that a
queued run of a newer routine task can be claimed while an older task of
the same routine still holds the slot through a failed run with a
pending resume. The claim's lock stamp then raises `duplicate key value
violates unique constraint "issues_open_routine_execution_uq"`.
`isDeferrableClaimRejection` accepts only `HttpError` 4xx, so the error
escapes `startNextQueuedRunForAgent`.

**Expected behavior**

The claim is rejected as a conflict and rolls back. The run stays queued
and is claimed after the older task releases the slot. Other errors
behave as before.

**Steps to reproduce**

1. Create two open tasks of the same routine (same origin and
fingerprint).
2. Give the older task an execution lock held by a failed run.
3. Queue a run for the newer task and claim it. On `master` the claim
throws the unique violation.

The new queue test runs these steps against PostgreSQL.

**Paperclip version or commit**

`master` at `0dedbf23c35`.

**Deployment mode**

Any server with routines whose tasks run on native runners.

## What Changed

- `bindClaimedIssueExecution` in
`server/src/services/heartbeat/queue.ts`: if stamping the execution lock
violates `issues_open_routine_execution_uq`, it throws
`conflict("Another execution for this routine is already in progress")`.
The claim transaction rolls back, and the queue loop keeps the run
queued as it does for other 4xx claim rejections. Every other error is
rethrown unchanged. The issue checkout route already maps this violation
to the same 409.
- `doc/execution-semantics.md`: one paragraph on the behavior.

## Verification

- Failing first: `queue.test.ts` "keeps a claim queued while another
task of its routine holds the execution slot". On `master` the claim
raised the `issues_open_routine_execution_uq` violation. Now it rejects
with 409, the run stays `queued`, neither task's lock changes, and
nothing executes.
- Failing first, review round 1:
`heartbeat-queued-run-claim-isolation.test.ts` "keeps a routine run
queued while another task holds the routine slot, then runs it once the
slot is free" goes through `resumeQueuedRuns`. On `master` it rejected
with the unique violation. Now the routine run stays queued with no
error code while another queued run for the same agent executes. After
the slot is released, the routine run executes exactly once (executions
are counted per run, because the finished task can queue its own
follow-up run).
- `cd server && npx vitest run src/services/heartbeat/queue.test.ts
src/__tests__/heartbeat-queued-run-claim-isolation.test.ts`: 70 passed.
- `npx vitest run src/services/heartbeat/recovery.test.ts
src/__tests__/routines*.test.ts`: 4 files, 205 passed.
- `pnpm --filter @paperclipai/server typecheck`: pass.
- Not verified: the queue loop end to end on a real instance. The loop's
handling of a 4xx claim rejection is existing behavior.

## Risks

- While the older task holds the slot, the newer run stays queued and
the loop logs "queued heartbeat run claim was rejected" on each pass. It
is claimed once the older task's resume finishes or its lock is
released.
- Only this one constraint is mapped. Other database errors still escape
as before.
- No schema migration.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`) through Claude Code, with tool use
(shell, file edits, test runs). The runtime does not expose the context
window or reasoning configuration.

## 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-11-03 21:33:00 -08:00
DottaandPaperclip b4c69ded9f test(e2e): attribute native OpenCode skill loads (#18929)
## Thinking Path

> - Paperclip lets people manage agents and assign saved company skills.
> - Daily QA must prove that an agent loads the saved procedure it uses.
> - The saved-skill journey accepts several native source-read paths.
> - OpenCode's native proxy changes its built-in skill loader's
normalized operation to unknown.
> - The grader expected execute and rejected a real completed skill
load.
> - This PR accepts that one observed native shape with strict source
and identity checks.
> - Genuine loads can qualify while missing, foreign or incomplete
evidence still fails.

## Linked Issues or Issue Description

Refs: #18783.

**What happened?**

A combined QA run on `0884e73b46ee064b3d242e467035dde6c32e5cb5` failed
`saved-skill-native-read-attributed`. The agent's successful native
skill load exposed the complete saved procedure. The exact immutable
assignment and source checks passed. Core recorded the built-in loader
as `operation: unknown` and `readOnly: false`. The grader accepted this
loader only with `operation: execute`.

**Expected behavior**

A complete native OpenCode skill load can qualify when its actual run,
session, immutable source and paired receipt all match. A generic
unknown operation cannot qualify.

**Steps to reproduce**

Run the saved-skill journey through Core
`09162fe893b464dd2ccdafd14bd3ef1611202a86` with OpenCode. Let the agent
use its native `skill` loader. The direct TypeScript converter emits
execute, but the real proxy forwards a `builtinToolCall` without that
operation. Core's native converter records unknown. The former
TypeScript-only fixture missed this boundary.

## What Changed

- Add one closed native OpenCode skill-loader branch. Require the actual
run, runner, native session, item, turn and paired call.
- Bind the API assignment, admitted native skill, immutable version map
and materialized runtime directory to the same canonical skill.
- Require the exact wrapper name, heading and complete saved procedure.
Require an absolute local base directory and an empty file inventory.
Reject traversal, URI encodings, backslashes, query or fragment markers
and all ASCII controls. Keep valid spaces.
- Require a successful, complete and untruncated native receipt with the
exact output byte count. Reject missing, duplicate, reordered, foreign,
failed, dynamic or MCP carriers.
- Add 65 loader and boundary controls. Drive the real
TypeScript-to-proxy input and use the observed Core native carrier
shape. The JavaScript test does not claim to run the Rust converter.
- Document the native loader path. Preserve every existing accepted
execute, filesystem, first-party and Codex parser path byte-for-byte.
Keep all seven checks, one-run limit, 500-cent guard, task prompt, model
and ordinary Beta-off Latest assignment unchanged.

## Verification

- The sealed original Core receipt reproduces RED: the former grader
returns zero reads. Changing only its normalized operation to execute
reaches the old accepted branch. The narrow correction returns one
attributed read in separate local analysis. The original QA grade
remains failed.
- The actual TypeScript, proxy and native normalization source regions
match between Core091 and the tested baseline0884. The proxy emits
`builtinToolCall` without an operation. The native converter supplies
unknown.
- 334 focused tests pass across nine files, including 65 new loader
controls and all existing saved-skill evidence, flow, contract and
catalog controls. Run `node node_modules/vitest/vitest.mjs run --config
tests/runner-e2e/vitest.config.ts
tests/runner-e2e/product-journeys/saved-skill-use-native-loader.test.ts`
for the narrow regression.
- TAB, ESC and DEL controls fail before the review hardening and pass
after it. The valid-space positive remains green.
- Canonical Runner TypeScript prerequisites, own server declarations and
E2E types pass. The final E2E-only check reuses those same-commit
declarations after only the two test-scope files changed. The checked
candidate blobs match this committed head.
- Credential-free discovery lists 25 catalog journeys, including
saved-skill use. Registration and selection do not change.
- `git diff --check` passes. No Rust build or product-model call ran
locally. Full CI
[41773295359](https://github.com/paperclipai/paperclip/actions/runs/41773295359)
passed on `fb537b598da40176fa1752b49a4f676f468de1f7`: 48 successful jobs
and one skip. Fresh Greptile is 5/5 with zero unresolved threads. The
earlier same-head canceled run remains retained; no retry was requested.
The optional native review ended neutral with a 5/5 source assessment
and explicit execution/qualification coverage limits. Fresh staging
qualification
[41777787338](https://pages.paperclip.ing/evals-qa-smoke-41777787338-1/)
passed on this exact head: all seven checks and all five phases, exactly
one native OpenCode run, independent saved-document reopen/source
preservation, and normal cleanup complete. TestOps recorded a new passed
result and the automatic Slack report succeeded.
- The failed original stopped before independent document, reload and
source-after checks. Those checks remain unqualified for that run. The
original failed grade remains unchanged. A separate genuine fresh QA
pass now verifies the complete journey before daily re-admission.

## Risks

This path depends on OpenCode's observed native wrapper and materialized
directory contract. Missing or changed evidence fails closed. The fix
adds no runtime service, schema or API behavior. The native-loader
branch is covered by strict local regression controls and the original
sealed Core receipt; a separate fresh staging run also passed the
complete saved-skill journey.

## Model Used

OpenAI GPT-6 through Codex with reasoning, tool use and code execution.
The runtime does not expose a more specific deployment ID or
context-window size. Local checks made no product-model calls.

## 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>
canary/v2026.1104.0-canary.6
2026-11-03 23:21:17 -06:00
DottaandPaperclip 652cd76026 fix(ui): expose Stop for failed native retries (#18899)
## Thinking Path

> - Paperclip manages agents and their tasks.
> - A native run can fail while it still owns a same-run retry.
> - The server supports Stop for that pending execution.
> - Run details show Cancel only for queued or running records.
> - A failed pending retry therefore has no ordinary cleanup control.
> - This pull request exposes the existing Stop eligibility and uses the
existing cancellation endpoint.

## Linked Issues or Issue Description

Refs #14743 and #14733 for correlated native Stop. Those changes cover
backend cancellation and protocol qualification. They do not add the
failed-run control to run details. Searches of current master and open
PRs found no matching UI repair.

**What happened?**

A native run can show failed while its coordinator still owns a
retryable failure with no accepted result. The backend can cancel that
retry. Both run-detail implementations hide the cancellation button
because the record is terminal.

**Expected behavior**

The selected run offers Stop when the server confirms that pending
native retry. Unknown, legacy, terminal and accepted-result states do
not gain this control. Existing queued and running cancellation stays
available.

**Steps to reproduce**

Render either run-detail implementation for a failed native run with an
eligible retry. The original UI has no Stop button. The original batched
execution projection has no eligibility field. The regression tests
exercise both real components and the actual PostgreSQL projection
query.

**Paperclip version or commit**

The missing control and projection field were reproduced on
44a4ca1c61. Fresh master 8fd917aedf has
identical affected production blobs before this repair.

**Deployment mode**

Board run details for a native Runner task. Local verification uses
rendered React components and PostgreSQL. No hosted task mutation or
provider retry was performed.

## What Changed

- Add optional `execution.canStop` to the existing batched projection.
Read the matching retryable coordinator and absence of an accepted
result. The cancellation endpoint remains the authority owner. Add no
database queries.
- Exclude Grok failed retries from the new hint and both consumers. The
existing Grok Stop endpoint cannot accept a failed retry before
assignment. Keep this limitation explicit and preserve its active-work
authority checks.
- Show Stop for eligible failed runs in both run-detail consumers. Keep
the existing cancellation endpoint and its company, actor and row-lock
checks.
- Use the existing request-correlation contract for failed-native Stop.
Persist one scoped identity before sending and recover it after remount
or reload. Read a matching canonical claim only for the same board
actor, company and run. Use the existing UUID fallback on ordinary HTTP.
Never retry Stop automatically. Refresh the selected run and history
after either outcome.
- Report a stale conflict or a true no-op200 response. Accept a matching
durable pending cancellation intent without requiring terminal
acknowledgement. Keep running, queued and Grok calls unchanged.
- Add actual rendered interaction tests, real batched-query controls and
a short execution contract. Keep maps unchanged for this bug fix.

## Verification

- Untouched UI and real Postgres producer reds cover missing Stop and
missing eligibility. A valid Grok review found an unsupported failed-run
action; actual broker, projection and rendered controls reproduce it
before exclusion. No backend authority was expanded.
- The original60-case focused suite passed, including26 rendered cases,
the batched producer, and actual Grok broker/assignment fences. The
related57-case scope passed before the Grok correction; it is not
presented as final-head proof.
- A fresh native4/5 review found a stale200 no-op. Both actual rendered
consumers failed that new assertion. The real authenticated
HTTP/Postgres fixture proves `canStop=true`, coordinator revocation,
uncorrelated unchanged200 with a cancellation activity, and existing
correlated409 with no new activity or run mutation.
- The correlated repair passed82 affected cases before master
reconciliation. Exact `74c60c9ffe11c05bc85f3d0fa5a136f7e9d445cd`
passed80 rendered/API, HTTP and native-correlation cases. The render
scope verifies request bodies, stable identity after an unknown outcome,
no-op200 error, pending intent acceptance, wrong-run/request denial and
existing active/Grok behavior.
- Subsequent current-head reviews found remount identity loss and
unavailable `crypto.randomUUID` on ordinary HTTP. The corrected helper
passed 52 rendered cases across both consumers, including an unknown
response, initial scoped storage persistence, a prior browser record
with a unique never-mounted run identity, exact ID reuse, canonical
claim recovery, actor switch, wrong run and HTTP UUID fallback. A real
local-board identity mismatch was reproduced with two additional reds
before using the actual server-projected actor ID.
- Real authenticated HTTP and existing native-correlation suites passed
45 cases for canonical claim recovery and actor fences. Production UI,
server and vendored Runner builds and all server/UI type layers passed
at the prior reviewed helper head; exact final
`e0f723b1c86f1529029bf141943323764de28c22` UI types, token gates and
production build passed.
- Fixture failures are retained separately: a missing local SDK build
dependency, and a missing coordinator FK teardown in the new HTTP
fixture. Neither is a behavior red. Both fixtures were corrected and the
affected tests passed.
- Previous-head3d CI required one diagnostic rerun after a Runner
shutdown and a real account-list migration assertion. Two
untouched157-test runs did not reproduce the assertion. Complete
original row equality is unavailable. The assertion remains unresolved;
no test was sorted, skipped or changed to green CI.
- Previous-headf12 CI lost eight child runners. Its one authorized
failed-job retry also lost e2e5 to an explicit shutdown. No second retry
was issued. Those failures remain recorded. Current-head CI and both
substantive reviews are required; older checks and grades are not
credited.
- Previous-head74c CI found a real monitor scheduler timeout. An
untouched local replay and diagnostic-only replay reproduced it: the
original manual-monitor run was still running at the unchanged
three-second deadline. The cause remains unresolved; temporary
diagnostics were removed, and no assertion, timeout or production
monitor policy was changed. No CI retry was issued.
- Exact local `906af5c942ee0963eb4b64552160b57a18bcd2d1` integrates
normal master after #18930 merged. All reviewed Stop production and test
blobs remain unchanged from `e0f723b1c86f1529029bf141943323764de28c22`.
The four rendered, projection, HTTP and correlation files passed 99
cases after integration. All UI/server type layers, token gates and
production UI/server/Runner builds passed at that exact head; the server
build stamp records it.
- Previous-head e0 CI reported a Discord ownership error. That original
failure is preserved. Separate #18930 proves and repairs receiver
construction/renewal races with the same error; the original CI
scheduling order remains unproved. This branch now contains that
ordinary master merge and requires fresh CI rather than rerunning the
old failed attempt.
- Full repository checks were not run locally for this narrow slice. No
hosted task, provider execution or Stop receipt was created by this
work.

## Risks

Grok failed retries remain unsupported by this UI change. The existing
Grok running and queued Stop action, revoke path and assignment fences
are unchanged. A valid initial review caught the overbroad hint; actual
broker, projection and rendered regressions reproduce it before
correction. No backend cancellation authority was widened.

The optional hint can become stale. It grants no permission. The
existing cancellation path rechecks current authority and can reject the
request. Run details show that rejection. Older servers omit the hint,
so failed runs keep their existing behavior until the server updates. No
retry, task hold, accepted-result decision or provider receipt is
changed.

## Model Used

OpenAI GPT-6.1 Sol (`gpt-6.1-sol`) in Codex, with source editing,
terminal execution and tool use. The host did not report its
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
- [ ] 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-11-03 23:18:08 -06:00
Devin FoleyandPaperclip 6c7b136b16 fix(native): keep startup recovery running when one run's claim fails (#18931)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - At startup, the server runs native restart recovery before it
reports ready. Recovery claims each interrupted or retryable native run
in its own transaction.
> - On one instance, one run's claim hit the routine execution unique
index. The error escaped restart recovery, startup recovery failed
closed, health stayed "still starting", and two deploys rolled back.
> - #18916 fixed that exact conflict: it defers the run when its claim
hits `issues_open_routine_execution_uq`. Any other error in one run's
claim still aborts the whole pass, and so does a failed per-run audit
write.
> - This pull request handles every per-run claim failure the way #18916
handles the routine conflict, and keeps a failed audit write from
stopping committed claims.
> - The benefit is that one bad row cannot keep the server from becoming
ready, and no run is claimed or changed when its claim fails.

## Linked Issues or Issue Description

Refs #18916. This change generalizes its catch: #18916 keeps the
routine-conflict disposition (`routine_execution_busy`) exactly as it
is, and this change adds a separate reason for any other per-run
failure. It does not change how the routine conflict is detected or
deferred. Refs #18879, whose lock reclaim exposed the startup failure.
Refs #18877 (open), which edits other parts of the same files. This
change touches only the claim's error path and the audit loop, so a
second merge needs at most a small rebase.

**What happened?**

Two deploys never became healthy. About 25 seconds after the listener
bound, the boot log showed `startup native runner restart recovery
failed closed` and `startup heartbeat recovery failed`, both from a
failed `update "issues" set "execution_run_id" = ...` that violated
`issues_open_routine_execution_uq`. #18916 now defers that violation.
Any other error in one run's claim (another constraint, a trigger, a
timeout) still escapes `claimNativeRestartRecoveries` and fails startup
the same way. #18916's own test asserts this: "Other database failures
still fail the recovery pass."

**Expected behavior**

One run's recovery failure is logged and recorded on that run, and the
rest of startup recovery continues. The server becomes ready. Only a
failure of the candidate scan itself fails the pass.

**Steps to reproduce**

1. Create a native run that is due for restart recovery, and make its
claim fail (the new test uses a trigger on `native_run_finalizations`).
2. Add a healthy run that is due for restart recovery.
3. Run restart recovery for both. On `master` the batch throws and the
healthy run is not claimed.

**Paperclip version or commit**

`master` at `4a83ba4050f`.

**Deployment mode**

Any server that runs startup native restart recovery.

## What Changed

- `claimNativeRestartRecoveries`: #18916's `.catch` on each candidate's
claim transaction no longer rethrows other errors. The whole claim has
rolled back, so the run's state is unchanged and nothing is claimed. The
run gets `awaiting_evidence` with the same 30-second retry deadline. The
reason stays `routine_execution_busy` for the routine slot conflict, and
is the new `restart_recovery_failed` for any other error, which is
logged at error level. The batch continues.
- `recoverNativeRunsAfterRestart`: records a `restart_recovery_failed`
deferral as a warning transition on the run ("Native restart recovery
failed for this run and will retry"). A failed `getRun` or transition
write is logged and no longer stops the dispatch of claims that already
committed.
- `doc/execution-semantics.md`: replaces "Other database failures still
fail the recovery pass" with the per-run rule.

## Verification

- Failing first: `native-restart-recovery-isolation.test.ts` (new,
PostgreSQL). It seeds the reported shape (two open tasks of one routine:
the older with a due scheduled resume and a cleared lock, the newer
holding the lock), a run whose claim fails through a trigger, and a
healthy run. On `master` the batch threw `injected recovery claim
failure`. Now the routine run is deferred with `routine_execution_busy`
and the failing run with `restart_recovery_failed`, and both keep their
coordinator, attempt and locks. The healthy run is claimed.
`recoverNativeRunsAfterRestart` completes and records an info transition
for the routine run and a warning transition for the failing run.
- Two existing tests expected the pass to reject on one run's failure:
#18916's unrelated-constraint test, and failed-workspace retention's
rollback test. They now expect the per-run deferral. Their rollback
checks (no claim, unchanged coordinator, lease, lock and recovery
actions) are unchanged, and the unrelated failure still does not become
a routine wait.
- `cd server && npx vitest run` over restart recovery (isolation,
integration, diskless, preparation), controller recovery, heartbeat
recovery, scheduled resume lock, failed-workspace recovery, startup
cleanup, native resumption, live recovery work, startup feedback export,
recovery deadlines, runtime selection, stop accounting, warm transition
restart, workspace reconstruction, remote workspace migration and
heartbeat process recovery: all pass except one.
`heartbeat-process-recovery` "redacts opaque environment-bound
credentials from Sentry diagnostics: result" fails the same way on
unmodified `master` on this host (a local environment value is
redacted).
- Review round 1: two `heartbeat/recovery.test.ts` cases make `getRun`
and then `appendRunEvent` fail for a deferred run while a committed
claim is in the same batch. Recovery completes and dispatches the claim
exactly once; the `appendRunEvent` case also checks the warning
transition. Both fail when the catches are removed. In one of four full
runs of that file, the unrelated orphan-cleanup case "reconstructs
missing native preservation during orphan cleanup without replay:
reuse_by_environment" failed; it passed alone and in the other three
full runs.
- After the rebase onto `4a83ba4050f`: the isolation and
failed-workspace recovery tests and `pnpm --filter @paperclipai/server
typecheck` pass.
- Not verified: a deploy to a real instance.

## Risks

- A persistent per-run error now retries every 30 seconds and logs an
error each time, instead of stopping startup. The run stays unclaimed,
so ownership does not change.
- If the database fails for every candidate's claim, startup recovery
now completes with every run deferred, instead of failing closed. The
candidate scan still fails closed, and the deferred runs retry on their
deadlines.
- No schema migration.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`) through Claude Code, with tool use
(shell, file edits, test runs). The runtime does not expose the context
window or reasoning configuration.

## 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-11-03 21:05:27 -08:00
DottaandPaperclip 0dedbf23c3 fix(chat): retain receiver ownership during runtime construction (#18930)
## Thinking Path

> - Paperclip manages agents and their work.
> - Chat receivers hold a durable lease before they accept messages.
> - Runtime construction can outlast a lease renewal interval.
> - Renewal previously required an attached runtime before construction
had returned.
> - That check could revoke the same receiver that was still starting.
> - This change uses the exact pending initialization as proof during
construction.
> - The existing durable token and endpoint checks still fence every
renewal.

## Linked Issues or Issue Description

Related: #18899 exposed the existing receiver failure in its chat CI
shard. This is a separate lifecycle repair. #18795 provides the relevant
production and sleep fixture context. Searches found no overlapping open
initialization repair.

**What happened?**

The receiver acquired its lease and started its renewal timer. The
service then awaited credentials, command preparation and runtime
construction before attaching the runtime to its context. A renewal
during that interval treated the missing attachment as lost ownership.
Configuration later failed with a Gateway ownership error, although its
original durable token was still current.

**Expected behavior**

The exact pending initialization can retain its own lease while
construction runs. Missing attachment alone must grant no ownership. A
changed token, paused or removed endpoint, changed generation or
credentials, and shutdown must still fence the receiver.

**Steps to reproduce**

1. Use the real configure and PostgreSQL ownership producers with a
60-second lease and 50-millisecond renewal.
2. Hold the actual runtime construction call for 200 milliseconds.
3. Release construction. Untouched code returns the Gateway ownership
error.
4. With the same construction delay and a five-second renewal control,
configuration succeeds.

**Paperclip version or commit**

The causal regression fails on unchanged production source at
`e7549ab613`. The original CI error was observed in the chat shard of
#18899. That CI run did not record the precise setup and renewal order,
so this local proof does not establish its historical scheduling.

## What Changed

- Store the runtime context identity in the existing pending
initialization entry.
- Allow pre-attachment renewal only for that exact tracked
initialization and current local ownership.
- Recheck local ownership inside the existing renewal transaction.
Preserve company, endpoint, generation, credential and original-token
checks.
- Before SDK listener initialization, wait for any older renewal,
recheck local ownership, then obtain fresh durable proof. Preserve the
existing checks after initialization.
- Keep attached runtime identity checks, lease deadlines, renewal
intervals, shutdown and listener retirement unchanged.
- Add actual configure and database race controls. Document the shared
Discord and Photon receiver contract.

## Verification

- Untouched production red: the actual construction gate crosses a
renewal and returns the same Gateway ownership error. The otherwise
identical slower-renewal control passes. No lease, exception or provider
outcome is injected in these positive cases.
- A subsequent review found a second real race: final runtime removal
did not prove that SDK initialization had never started. Four actual
initialization-hook controls failed before the pre-initialize fence. The
final matrix asserts zero hook calls for lost or changed authority,
including a foreign-token control with no periodic renewal due. An
attempted cross-company endpoint move is rejected by the existing
database foreign key; the valid original initialization still succeeds.
- A third actual-producer red holds completion of a real committed
renewal, changes the durable token, and proves that joining that old
promise could incorrectly accept configuration. The corrected startup
check waits for that renewal and then obtains fresh proof; it cannot
credit the earlier result alone.
- Final relevant full Discord registration group, takeover/callback
cases, buffered host-pause recovery, failed construction,
credential-fenced pause and shared Photon consumers: 34 passed across
two files; 1,840 other cases were deselected. Four executed cases cover
Photon transport recovery, failed authentication, duplicate allocation
ownership and checkpoint fencing. The tests use the real
configure/database producers with fake provider transports and observe
SDK initialization; they do not prove physical network behavior.
- All server type layers, the module-boundary gate, and production
server/vendored Runner build pass at exact final
`38cf82ec114bd12c0aa7bbc125ed72b1ae941b4e`. The production build stamp
records this exact commit.
- Earlier fixture mistakes are preserved separately: a test initially
changed every lease key, including the credential lease, and another
attempted an invalid cross-company row move. Neither is counted as a
production behavior red. The corrected controls target the Gateway key
and assert the existing company constraint.
- An initial isolated dependency graph resolved an older shared
telemetry build. Correct workspace links restored the all-layer
typecheck without changing telemetry source.
- Full repository checks were not run locally for this narrow slice.
Fresh current-head CI and substantive reviews remain required. No hosted
task, provider work, fleet action or deployment was performed.

## Risks

The ownership helper is shared by Discord and Photon. The selected tests
cover both consumers. The pending entry grants only permission to renew
the original token; it grants no message, callback or publication
authority and adopts no listener. Missing or changed initialization
identity fails closed. There is no schema change, historical backfill,
new worker or additional idle scan.

## Model Used

OpenAI `gpt-6.1-sol` in Codex, with reasoning, repository inspection,
source editing, shell execution and test tools. The host did not report
the context-window size. A separate coordinator reviews the source
before delivery.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 22:57:39 -06:00
DottaandPaperclip c6fe9b143c test(evals): pin reviewed chat repairs and restore UI test setup (#18921)
## Thinking Path

> - Paperclip agents must describe recorded work and respect human
review decisions.
> - The catalog must test the user's request without adding an
unrequested format rule.
> - Timeline case 466 wrongly required date and timezone tokens in every
valid answer.
> - Missing private evidence and stalled review admission also need
bounded failure facts.
> - The reviewed changes correct that contract and add diagnostics
without changing product behavior.
> - A new retained secure-entry reply also exposed an observer false
positive for an explicitly excluded chat destination.
> - A retained team-install reply also omitted every resource link, so
the reviewed service now returns canonical hrefs.
> - This PR pins the coherent corrections and fixes the observed UI-test
interruption helper.

## Linked Issues or Issue Description

Refs #18914. Refs #18923. Refs #18927. Refs #18780.

This prepares the next diagnostic selection. It does not change the
three-case run already in progress. The prior case-466 failure remains a
failure because its answer omitted the required handoff. The prior
case-53 original grade remains unknown.

## What Changed

- Pin immutable candidate `25cd5e4ebb6b955701778d2d1d702f86a573cfaa`.
- Update the existing descriptor snapshot and matching timeline oracle
declaration to v3. Only case 466's expected outcome is clarified:
respect a timezone when explicitly requested.
- Update case 487's existing source-file declaration to include its
diagnostic helper. Its oracle, request, run counts, and clocks stay
unchanged.
- The candidate adds fixed carrier/reason counts for case-53 evidence
failures. It also records bounded, issue-bound delivery/wake/run facts
only when case 487's existing settled check fails, then rethrows that
original error.
- Correct the shared chat-credential matcher for secure-form
instructions that explicitly exclude chat. Mixed or later chat requests
still fail. Give case 580 an explicit personal-secret v2 oracle identity
and update its existing snapshot and manifest declaration.
- Include the reviewed additive team-install resourceLinks response, its
shared type and actual 201 OpenAPI schema. Preserve the full existing
partial-install/repeat help and synchronize the existing typed Storybook
result. The core PR remains unmerged.
- Fix the UI test announcement handler that timed out during the two-tab
attachment check. Only a detached target with no current replacement is
ignored; real remaining-target errors still fail. The original
functional assertions and deadlines remain. This changes no catalog case
550 behavior.
- Keep all 244 case identities, original requests, model, deadlines,
private scanner limits, and broader runtime qualification holds
unchanged.

## Verification

- All 47 existing TestOps contract, adapter, and manifest controls
passed on this pin.
- Integrated timeline and private-diagnostic controls passed: 162
timeline checks, 14 evidence checks, and full 242-declaration admission
with zero fixture callbacks.
- The source author ran 53 real PostgreSQL/review controls and the
declaration-backed runner typecheck on the exact accepted review
diagnostic. Those passed.
- The integrated replay passed 179 tests. Its three PostgreSQL observer
cases initially could not start because the local native package lacked
a shared library. After the existing same-version runtime was resolved
through owned dependency links, those three real PostgreSQL cases
passed. The setup failures are retained; no source, test, timeout,
scanner, or assertion was weakened.
- The retained secure-entry consumer replay accepts the truthful
secure-form/not-chat handoff with the correction. All 25 shared consumer
checks and the fresh full 242-declaration admission passed (28 tests
total). The earlier failed result is unchanged. This does not yet prove
human entry or worker delivery.
- A cheap prefreeze check on actual master passed the existing
reporting-lock guard with unchanged reviewed lock bytes.
- The resource-link type changes exactly one of 506 SDK inputs. The
existing owning helper freshly compiled shared and plugin SDK outputs.
Measured SDK runtime JavaScript is unchanged; the type declaration, its
map and two build receipts changed. The emitted resource-link consumer
compiles and a foreign resource kind fails with TS2322. The old
registry/default proof rejected the change; the existing generator
updated only that input and the new default proof passed. All 51 SDK and
20 input/build custody controls passed. Compiler, parser, Zod and canary
certificate pins are unchanged; held runtime cases remain held.
- Core service/wire/OpenAPI controls passed (72 tests), plus four real
PostgreSQL checks. The typed story synchronization passed UI typecheck,
catalog checks and 30 controls. Candidate token gates passed.
- The prior CI two-tab check failed in the announcement locator handler.
A controlled real-browser disappearance reproduces it before the fix and
passes after it; a genuine remaining-target error still propagates. That
prior failed check is retained. Fresh normal CI and review run on this
actual correction.
- The review-requested real detached-ElementHandle control now covers
the catch branch and asserts the target is absent. Removing that
predicate fails with the real browser detachment error; the restored
three controls pass. This is test coverage only; helper behavior and
deadlines are unchanged.
- Fresh exact-head normal [PR
CI](https://github.com/paperclipai/paperclip/actions/runs/41774814211)
and [Windows
Runner](https://github.com/paperclipai/paperclip/actions/runs/41774814064)
passed. Greptile and the completed Paperclip review both report 5/5 with
no actionable feedback; all review threads are resolved. The optional
security scan is neutral, not a security assessment PASS. No new catalog
runtime PASS is claimed for this candidate.

## Risks

The timeline contract correction accepts valid answers without extra
date labels. It still rejects explicit dates outside the requested
window, missing attribution, failed-run misstatements, hidden work, and
unsupported claims. Balanced owning controls retain those negatives.

The diagnostics add evidence rather than changing delivery behavior.
Case 53 can still fail closed at the existing archive limit. Case 487
can still fail its original settled check. The additive resource links
are a product affordance and still need a real agent rerun. Earlier
failed grades and attempts remain unchanged. The full 244 runtime
catalog is not merged into master; its existing 196/244 gate remains.

## Model Used

OpenAI `gpt-6.1-sol` through Codex. Capabilities used: source analysis,
code integration, and local validation. The host does not expose the
context-window limit. The model under evaluation remains
OpenCode/OpenRouter `deepseek/deepseek-v4.1-flash-1017`.

## 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-11-03 22:56:20 -06:00
DottaandPaperclip 4a83ba4050 fix(workspaces): isolate unpublished preparation directories (#18922)
Bind new remote preparation directories to the writer epoch and environment lease. Preserve earlier unpublished files and the recorded path for published warm workspaces. Run the interrupted reused-allocation and saved-file lifecycle regressions with owned Postgres by default, and retain the warm-path recovery checks.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
canary/v2026.1104.0-canary.5
2026-11-03 22:30:48 -06:00
DottaandPaperclip f211d44c00 fix(heartbeat): preserve queued coalesced comments at claim (#18913)
## Thinking Path

> - Paperclip manages agents and their work.
> - The wake queue records task comments before an agent starts.
> - A queued run can accept another comment through coalescing.
> - Claim rebuilds that run's input from its primary wake.
> - Coalescing updated the run and its receipt but left the primary
batch unchanged.
> - This change saves the accepted batch atomically in the owning
writer.
> - The benefit is complete input at claim without repeating old work.

## Linked Issues or Issue Description

Related: #18887 preserves offline queued turns. #13948 addresses
equivalent automatic wake deduplication. This change repairs the
accepted comment batch at the existing claim boundary.

**What happened?**

A queued run started with comment A. Admission accepted comment B, saved
`[A, B]` in the run context, and finished B's coalesced receipt. The
primary wake still held `[A]`. Actual claim rebuilt input from that wake
and dropped B.

**Expected behavior**

Claim must retain every accepted live comment in order. Stop, changed
source authority, deleted comments, and company boundaries must still
apply. Already claimed wakes must retain their existing recovery source.

**Steps to reproduce**

1. Queue a task run whose primary wake has an authoritative comment
batch `[A]`.
2. Admit comment B through `createHeartbeatQueue.enqueueWakeup` and the
real Postgres admission writer.
3. Confirm that admission accepts `[A, B]` and records B's coalesced
receipt.
4. Claim the original queued run. Before this fix, the claimed input
contains only A.

**Paperclip version or commit**

The untouched producer regression fails on
`d7283e875772ca8c684dc86e471e6b48d543b7b1` with both process and native
Runner/Codex adapter selection. Provider execution is disabled in the
fixture.

## What Changed

- Save the accepted comment batch in the unclaimed primary wake, run
context, and coalesced receipt in one transaction.
- Lock the primary wake before the run. Recheck the expected run status
and same-company, same-agent and run bindings; apply the existing actor
equality fence only to durable receipts before mutation.
- Preserve incoming input as normal deferred work when the locked source
changed. Preserve receipt identity and actor fields.
- Preserve the original primary actor and trigger comment while updating
its accepted batch. Each incoming receipt retains its actual actor. Keep
claimed source payloads and legacy envelopes on their existing recovery
paths.
- Safely defer a same-named different agent without rewriting the
original agent's run, wake or receipt. Missing and foreign-company
targets remain rejected. Do not rebuild input from historical receipts.
- Add actual process/native producer, real database rollback, Stop,
deletion, foreign-source, and fallback controls. Update three direct
writer fixtures for the required expected-status contract.

## Verification

- The untouched process and native producer red on
`d7283e875772ca8c684dc86e471e6b48d543b7b1` loses accepted comment B at
actual claim. The initial incomplete mock is retained separately and
excluded from behavioral proof.
- Current process/native matrix: 22 passed, including mixed authors,
system primary, same-named different agents, changed status, claimed
sources, Stop, deletion and foreign-source boundaries. Queue, Postgres
admission and use-case suites passed 234 cases before the
source-preserving serializer follow-up; its 22 actual producer cases
passed again afterward.
- CI on the previous head found a real ordinary mixed-author
workspace-copyback regression. The earlier 13-case selection omitted
this test; that coverage gap is preserved. The first correction exposed
a second real source-provenance/copyback failure. After preserving the
primary trigger and actor while updating only its batch, the complete
workspace recovery file passed all 188 cases, including ordinary
mixed-author input and native copyback.
- Real HTTP queued-comment consumption, discard/reorder and Stop
boundaries: 101 passed before these review corrections. Existing chat
and Slack consumers each passed their selected real writer case.
- Previous-head CI also timed out in the CLI child-server startup
fixture and a ZIP transfer case. The full untouched transfer file passed
17 cases locally. Startup remains unclassified; its intentionally
reserved port is part of the fixture and is not diagnosed as the cause.
No CI retry was issued.
- An earlier queue run passed its 231 assertions but timed out during
teardown; its preserved replay passed all 60 queue cases. This is
distinct from the behavioral reds.
- Exact `ab59d944895774d89f950248e356e93af00f4832` server type layers
and production server/Runner build passed. The unchanged-default
serializer file passed all 11 cases. Current-head CI and both
substantive reviews remain required; previous grades and checks do not
qualify this head.
- No hosted task was retried or changed. Local tests do not establish
any hosted deferred event's cause, provider execution, native
acknowledgement, physical cleanup, or serving revision.

## Risks

- Admission now performs bounded row reads and locks for each coalesced
wake. It adds no idle scan or remote request. The existing task
transaction owns the operation; lock order follows queued claim.
- Claimed-wake recovery uses source identity. Its payload remains
unchanged, and the existing actual native copyback tests cover that
boundary.
- A source that changes before the lock can defer the new input. This
preserves work instead of consuming it into a stale run. No migration or
historical backfill is required.

## Model Used

OpenAI `gpt-6.1-sol`, with reasoning, repository inspection, shell
execution, and test tools. The runtime does not report the
context-window size. A separate OpenAI coordinator reviewed the source.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 22:09:58 -06:00
DottaandPaperclip e7549ab613 fix(runtime): defer native routine recovery collisions (#18916)
Treat the exact routine execution unique conflict as a pending recovery obligation after the complete losing transaction rolls back. Preserve the current routine owner, provider attempt and checkpoint, and continue the startup batch. Cover real constraint failures, row preservation, one reclaim after release and unrelated database failures.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
canary/v2026.1104.0-canary.4
2026-11-03 21:47:56 -06:00
DottaandPaperclip cb75c9510c test(evals): restore chat setup and pin secure-entry guidance (#18914)
## Thinking Path

> - Paperclip manages agent work and keeps credentials under human
control.
> - Agents discover supported operations through existing API and
connection help.
> - The measured secret-entry case chose external provider setup instead
of native secure entry.
> - The reviewed help distinguishes these actions and keeps the same
permissions.
> - Two related help changes explain recorded progress and partial team
installation.
> - The first remote setup found stale reporting-lock metadata on
trusted master.
> - This PR pins the candidate and renews that existing lock correction
for narrow remote checks.

## Linked Issues or Issue Description

Refs #18909. Refs #18780.

The previous measurement did not complete the requested native
secret-entry handoff. The next representative is case 580 on the
existing TestOps plan 102 and job 100. Case 610 can use the same source
for a separate representative. The progress-help change does not claim
that case 466 passes.

## What Changed

- Pin immutable candidate `b1ea76b286b870bb62a80eebf3fe00feed6f12c1`,
which retains the approval-link fix from #18909.
- The candidate adds only reviewed secret-entry guidance, three API help
changes, and the matching SDK source registry entry. Its delta from the
approval-link candidate covers three files.
- Update the launcher snapshot target and expected target test.
- Record the actual new SDK compilation and its one changed runtime
JavaScript file. Preserve the previous qualification as history.
- Refresh only the existing reporting-lock policy and patch from the
prior reviewed lock to current master. The helper, actual lockfile,
credential rules, and candidate source stay unchanged.
- Keep all 244 case rows, inputs, clocks, model, private evidence
controls, and held runtime cases unchanged.

## Verification

- The owning secret help checks passed: 47 server/shared tests and 31
Secrets UI tests. The real discovery search failed before the help
change and passed after it.
- The owning progress and team-install search regressions each passed
with their full 2057-test runner API suite.
- In the candidate, the existing `scripts/ensure-plugin-build-deps.mjs`
freshly compiled shared and plugin SDK outputs and exited zero. The
emitted guidance consumer passed. Measured output changes contain only
the guidance JavaScript, its declarations and maps, and two build
receipts.
- The old registry and default source proof rejected the changed input.
After the existing registry generator updated that one entry, the
506-input check and default source proof passed. Runtime, compiler,
parser, Zod, scanner, and canary certificate pins did not change.
- All 51 affected SDK tests and all 20 input/build custody controls
passed. One initial suite failed to load a missing local dependency;
that setup failure is retained. The run with the existing package
dependency links passed.
- The three existing TestOps contract, adapter, and manifest test files
passed: 47 tests, zero failures.
- The first case-564 attempt stopped before credentials or model
execution: the trusted controller lock had moved through normal
dependency updates while its reporting-lock metadata remained stale.
That attempt has no original native grade and is not a product failure
or a PASS.
- The existing lock-refresh command generated the exact reviewed
correction from lock `54a81b2e` to current master lock `7540075e`. All
seven correction, tamper, and refresh tests pass. The actual
current-controller frozen install with lifecycle scripts disabled and
both reporting/private-transfer imports pass. The frozen candidate still
accepts its unchanged own lock. An initial offline install found an
uncached package; the regular frozen install passed.
- Fresh CI and review for the renewed head are pending. Local help
checks do not establish a native agent outcome. No new TestOps PASS is
claimed.

## Risks

Help can improve discovery without changing the agent's final choice.
The representative runs must test that remaining gap. The candidate
retains the prior catalog application base plus the reviewed fixes. It
does not refresh or qualify the full catalog. Automatic model retries
remain zero and the authorized concurrency cap remains eight.

## Model Used

OpenAI `gpt-6.1-sol` through Codex. Capabilities used: source analysis,
code integration, compilation, and local validation. The host does not
expose the context-window limit. The model under evaluation remains
OpenCode/OpenRouter `deepseek/deepseek-v4.1-flash-1017`.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 21:28:49 -06:00
DottaandPaperclip 0884e73b46 test(e2e): verify saved assigned skill use (#18783)
## Thinking Path

> - Paperclip lets people manage agent work and reusable company skills.
> - A saved assigned skill must affect a task's actual execution.
> - Existing QA coverage creates and edits skills.
> - The daily target needs coverage for ordinary saved-skill use.
> - The task receives its receipt data only through one saved skill.
> - The admitted immutable version, native read and saved document
establish the outcome.
> - This PR adds one bounded journey for that gap.
> - A shared lease test also needs a stable clock to assert its exact
timeout.

## Linked Issues or Issue Description

Refs: #18305.

Supporting repair #18885 merged as
`057898530c702ec14c1e14d163e96dfb045cd8c0`. It closes the
recovery-conflict prerequisite. This branch does not duplicate that
production patch.

**What existing behavior does this improve?**

Product E2E coverage for ordinary assigned company skill use on an
existing deployment target.

**Current behavior**

The deployment journey catalog can create and edit a company skill. It
has no separate case that requires a native task to load its assigned
saved skill and use data available only inside that source.

**Proposed behavior**

Create one synthetic managed company skill through the public API.
Assign its canonical key with the normal Latest selection. Create the
task through the production browser composer. Require one native work
run, the exact created immutable version in its admitted skill-version
map, a successful native source read or load, and an agent-authored
saved receipt. Reload and verify the result, revision and original
source.

## What Changed

- Add `saved-skill-use` with one native work run and seven required
checks. The first check is `saved-skill-assignment-bound`.
- Use ordinary `desiredSkills: [canonicalKey]` assignment. Require the
returned stored assignment to have `versionId: null`. This works with
Beta skills off and reads or changes no experimental setting.
- Bind the creation receipt, pre-dispatch source read and version file
inventory. Require a managed skill with the exact company key. Require
the run's authoritative `skillSelectionSnapshot: true` and admitted
version map to match the created immutable version.
- Keep the random receipt reference and numeric inputs only in the saved
source. The task prompt names the skill without supplying its answer.
- Require exact company, task, agent, run, turn and runner bindings for
a complete successful native source receipt. Accept the deployed
OpenCode read wrapper only when both the saved assignment and native
execution input bind its runtime directory to the created immutable
skill version. Also accept the first-party assigned-skill read with a
provider-independent canonical key/version binding from both the saved
API assignment and admitted native input. Require the exact full source,
checksum, byte count and complete page. Keep the materialized
directory-name guard specific to the OpenCode filesystem path. Reject
claims, shell output, foreign or incomplete reads and wrong skill loads.
- Add a narrow Codex reader for the retained controller
`semantic_tool.input` and `semantic_tool.result` pair. Require the
actual native provider, runner, session, call, turn and item; the
admitted immutable key/version; the read-only `read_assigned_skill`
operation; and its successful active-task receipt. Recompute the input
digest and the exact complete seven-field read-result digest from the
saved source. Raw dynamic or MCP names grant no authority. Keep the
builtin OpenCode/filesystem reader body unchanged.
- Require authoritative native commit before qualifying the saved
receipt. Independently check its value and same-run document revision
authorship. Reload and verify the result and unchanged source. Retain
those existing readback receipts in the private checkpoint for
independent verification.
- Keep the exact raw campaign ID in the journal, API name, frontmatter
and saved name. A lost successful creation response remains recoverable
by name.
- Register discovery, source fingerprints, closed checks and existing
feature associations. Update the case and fixture docs. Feature-map
files are unchanged.

- Stabilize one existing shared sandbox lease regression with a scoped
`Date.now` spy and `finally` restoration. Keep its exact timeout,
no-destroy, no-replacement and recorded lease assertions. Change no
production service code.

## Verification

- **Genuine QA staging qualification passed on this exact
`2fb654f7be0bac3bfeb4ec6b7a2eb7a06b8de7e4` head**:
[workflow41763421118](https://github.com/paperclipai/paperclip-cloud/actions/runs/41763421118),
[TestOps
launch2257/result4034](https://paperclip.testops.cloud/launch/2257/tree/4034?treeId=0),
[sanitized
report](https://pages.paperclip.ing/evals-qa-smoke-41763421118-1/). All
seven required checks and all five phases passed, with exactly one
configured OpenCode/DeepSeek native run succeeded and committed. Owned
cleanup closed. A separate source-pinned replay verified25
original-evidence controls and16 rejection controls without altering
originals. Automatic #testops PASS was independently read back. Both
earlier failed QA grades remain unchanged.

- Current-head CI `41758727042` attempt 2 passed with 48 successful jobs
and one skip. The coordinator authorized one unchanged-source
failed-jobs diagnostic. Start and end timestamps prove three fresh
executions (Runner package, server shard 7 and aggregate verification),
45 retained successful executions and one skipped non-execution. The
prepared-context test passed in 2,534 ms. All 28 repository rehome tests
passed. The original failed attempt remains sealed. Its timeout causes
are still unknown. No source, timeout or assertion changed, and no
reliability fix is claimed.
- Fresh exact-head Greptile review is 5/5 with zero unresolved threads.
All 58 current check-runs and the status rollup are terminal and clean.
The native Paperclip review ended neutral and incomplete, so it is not a
validated assessment. The PR remains mergeable after the dependency case
merged in #18901. The source remains frozen. A coordinator-only
qualification draft is prepared with approval false. A genuine QA
staging pass is still required before daily enrollment.
- Current head `2fb654f7be0bac3bfeb4ec6b7a2eb7a06b8de7e4`: 269 focused
tests across eight files passed. These include 42 Codex producer,
converter and false-positive controls. The canonical E2E typecheck
passed with its own declaration prerequisites. Credential-free discovery
lists 24 journeys. Fresh exact-head Greptile review passed at 5/5 with
zero unresolved threads. CI `41758727042` attempt 1 failed. It completed
45 successful jobs, one skip and three failed jobs. The primary failures
were one existing OpenCode prepared-context test at its unchanged
5-second timeout and three existing repository rehome tests at their
unchanged 15-second timeouts. The third failed job aggregates those
results. The timeout causes are unknown. The original attempt remains
sealed. A direct passing twin ran the prepared-context test in 2,533 ms.
All package, script, CLI, UI, workflow and root inputs match between the
actual tested merges. Three server files differ, including
`task-retry.ts` inside the broader rehome import graph; no full-server
equality or causal fix is claimed. The later single diagnostic pass is
recorded above. The native Paperclip review ended neutral with an
incomplete execution, so it is not a validated assessment. No live QA
pass is claimed.
- The valid review on `e1a474` found that Codex displays its first-party
tool as dynamic. Source inspection confirmed that its raw name has no
authority. The retained controller semantic input/result pair supplies
the real operation authority. Core
`09162fe893b464dd2ccdafd14bd3ef1611202a86` and current source have
identical relevant envelope, digest, result-unwrapping and
immutable-read implementation regions.
- Three real-producer positive controls failed against the old grader.
All 42 now pass. The tests call the actual immutable-file reader,
production protocol digest builders, Codex display converter and API
redaction. They validate the event schema and cover omitted defaults,
the exact runtime alias and Unicode bytes. Missing, duplicate,
reordered, failed, foreign, MCP, unbound, partial-page, wrong-digest,
wrong-key/version and nonce-only evidence fails. Generic dynamic output
fails even with copied full text and invented read-only markers.
- The original builtin reader body and source-only fixture function are
byte-identical to `e1a474`. All seven checks, the one-run limit,
500-cent guard, ordinary Latest assignment and Beta-off behavior remain
unchanged. The independent verifier is pinned to this new head. Its
synthetic semantic receipt passes 24 negative controls and its
filesystem receipt passes 16. Older helpers and both original failed QA
seals remain unchanged.
- Earlier integrated head `e1a47438b1c7e51a5bb2896a4ec9b6ed6560318b`:
376 focused checks across eleven files passed, including Saved, settings
and Chat overlap controls. All seven canonical server type layers and
exact-head E2E types passed. All four lease controls passed.
Credential-free discovery lists 24 catalog journeys. No new staging run
has occurred.
- Normal integrations consume actual master
`fbb7055b9020e505cd1bf903b1b1c301ba8f3eef` and actual Chat squash
`d7283e875772ca8c684dc86e471e6b48d543b7b1`. Preserve all five Saved
case/parser files byte-for-byte and retain settings and Chat
registrations. The only manual resolutions were two additive
handler/type anchors. All seven checks, one-run limit and 500-cent guard
remain unchanged. The independent verifier is pinned to this exact head.
- Earlier dbdf CI passed after one authorized failed-jobs recheck of a
confirmed Spot-terminated Build runner. Timestamp comparison proves two
fresh executions (Build and verify), 46 retained successful executions
and one skip. The original failed attempt remains sealed. Its native
Paperclip Review completed neutral with an incomplete execution and is
not a validated assessment.
- New master entries made the earlier dbdf PR conflict. The frozen
qualification guard correctly blocked dispatch before any pin or
receipt. Preserve its unapproved draft as history; it did not authorize
a QA run. This normal integration resolves the observed conflict. Fresh
current-head CI/review and one genuine staging pass remain required.
- Earlier merged head `dbdf1f3cbeaacc7cf9a2a457618035c945bd09f0`: 227
focused checks passed across seven files. All seven canonical server
type layers passed. The E2E typecheck passed using the declarations
built in this same checkout on this exact head. All four existing clock
and lease controls passed. Credential-free discovery lists 22 catalog
journeys, including both saved-skill use and task lifecycle. These are
local checks, not a live staging qualification.
- Normal merge of master `8771606fd87f69ed4057779e3dce3a9bca8088e6`
retains the reviewed task-lifecycle case and its Cursor fixture repair
from #18782. Resolve six shared journey registrations by preserving both
cases. The five saved-skill case files remain byte-for-byte equal to
reviewed `ff3814`. All seven required checks, one-run limit, 500-cent
bound and ordinary Beta-off Latest assignment remain unchanged. No
feature-map delta was added.
- The independent sealed-run verifier is now pinned to this exact merged
head. Synthetic semantic evidence passed with 24 negative controls.
Synthetic filesystem evidence passed with 16 negative controls. Foreign
document authorship and missing reopen/source readback still fail. The
old `ff3814` verifier and both original failed QA seals remain
unchanged.
- Common generated outputs were copied only after all 4,225 package,
script and root inputs matched, installed lock/TypeScript hashes
matched, and all 4,955 source output paths and hashes passed before and
after copy. No server declarations or editable source/module links were
copied. The server declarations were rebuilt locally. A full frozen
workspace install disabled lifecycle scripts and used the same locked
packages. No Rust build ran locally.
- Previous `ff3814` CI `41733864320` exposed a real wrapped PostgreSQL
uniqueness error in a recovery server test. That production defect is
independently fixed and tested in #18885. The later supporting PR merged
separately. This branch does not duplicate its production patch or
regrade the original failure. Preserve the original failed result and
the separate Spot interruption. New full CI and a fresh exact-head 5/5
review remain required.
- Previously, **225 tests passed across six files** in the focused
evidence, flow, contract, catalog and report set after the semantic-read
correction. The prior 181-test proof and all original failed attempts
remain retained. This correction changes only the saved-skill evidence
helper, flow argument, their tests and case documentation. The shared
clock repair remains unchanged.
- E2E TypeScript check passed on Node **24.22.2**.
- Earlier credential-free discovery listed 21 cases and exposes the
ordinary saved-skill journey without a Beta prerequisite. These local
checks make no provider or model calls.
- Source inspection of Core `09162fe893b464dd2ccdafd14bd3ef1611202a86`
confirms key-only selection becomes a stored null Latest selection.
Managed execution resolves its current immutable version and records the
admitted map in `contextSnapshot`. The public run API preserves that map
independently of optional result presentation data.
- Seven invalid admitted-version fixtures wrongly qualified without the
new guard. The regressions now reject them before the read or document
can qualify. Beta-off and Beta-on ordinary request controls use the
production sync validator. Wrong assignments, missing null selection,
source drift, creation-version mismatch and another skill's complete
native output cannot pass.
- Existing native commit, null/compact result data, read provenance,
nonce, saved-result, reload and lost-creation-response controls remain
required. Their original red results are retained.
- The earlier experimental version-pin variant remains unqualified. Its
missing-prerequisite regression and the denied target-setting attempt
remain separate failure evidence. This case qualifies ordinary
assignment and the version actually admitted by the server.
- Normal merge of master `5f1b59ba2c9adfb40a681bf419a8b7e7837d1125`
includes the reviewed inbox snapshot fix #18798 and App/Shell
access-fixture fix #18799. The merge has no conflicts. All 17 journey
source, helper, registration and documentation files remain
byte-identical to reviewed head
`eab260955ff43eeacfdafc9b75dfc9b116f0eb79`.
- Previous CI `41672686614` failed two UI test shards with obsolete
user-ID fixtures; its aggregate verify job also failed. The merged
access-fixture repair addresses those failures. This is a new source CI
run; the failed run is retained.
- Dependency output reuse follows exact path and byte checks for 10,977
tracked build inputs and 3,411 generated outputs. The first copy guard
detected 12 bundles regenerated after its initial manifest and stopped
before any destination mutation. The stable replacement manifest passed
all checks before copying. Final focused tests, E2E typecheck and
discovery passed after the verified copy. No editable source or module
link was shared.
- An earlier integration found stale generated Cursor Cloud
declarations. Canonical dependency builds refreshed them, then typecheck
passed. The original typecheck failure remains recorded.
- The initial report support failure came from a blocked `tsx` IPC pipe
before report execution. The unchanged test passed with normal local IPC
permission. The original failure and diagnostic remain recorded.
- `git diff --check` passes. Full workspace recursive typecheck, unit
run and build remain assigned to hosted CI and coordinator verification.
Full CI `41679720041` passed on the prior
`f3f288a54177f2b9a7b225b1d1ab35440dfc597c` source. A clean 5/5 review
passed on `0f445cf1c622afb25a1af92398784209df542696`. Fresh CI and
review are required after the shared clock fixture repair.
- The first real QA staging attempt on
`f3f288a54177f2b9a7b225b1d1ab35440dfc597c` failed
`saved-skill-native-read-attributed`. Assignment, immutable admitted
version and cleanup passed. The test stopped before document, reload and
source-after observations. The original failed attempt and its complete
evidence remain sealed.
- The original successful builtin `read` event contained the full saved
procedure. OpenCode returned its file wrapper with an unknown normalized
operation, null target and a hashed runtime skill name. The old parser
rejected that shape. A synthetic regression failed before this
correction. The corrected parser recognizes that exact retained read in
memory, with no change to the original grade. Wrong paths, copied shell
text, missing starts, mismatched calls, failed/truncated reads and wrong
version/name bindings still fail. A later staging attempt is described
below. Neither failed original was regraded.
- Original CI `41703535552` failed one shared server test and its
dependent verification job. The lease-resume test expected an RPC
timeout of 31,234 ms, but the runtime correctly supplied 31,233 ms after
one elapsed millisecond. The test and runtime service were
byte-identical to the previous passing source. The failed run remains
recorded.
- A scoped advancing clock reproduced that exact 31,233-versus-31,234
failure locally. A stable clock around only the acquisition call now
preserves the exact original assertion. The repaired test and three
related sandbox lifecycle controls pass on disposable embedded Postgres.
No production timeout, lease preservation, cleanup or replacement
behavior changed.
- The first local reproduction stopped before test execution because six
same-package native Postgres library aliases were missing. Their exact
existing versioned libraries matched the canonical dependency. Restoring
only those internal relative aliases allowed the separate deterministic
red and green measurements. This prerequisite failure remains recorded.
- The second real staging attempt on
`02582aadd23fc128ae29daa69482113cf0f95ad1` also failed
`saved-skill-native-read-attributed`. Authentication, setup, assignment,
source-only input, collection and cleanup passed. It completed one
native run. Later document, reload and source-after checks were not
executed. The complete original remains sealed as failed.
- That attempt used the ordinary first-party
`paperclip_read_assigned_skill` tool. Its complete native receipt
contained the exact canonical skill key, created version, `SKILL.md`
bytes, SHA-256, byte count and null next-page offset. Core verifies the
pinned manifest and file checksum before returning that receipt. The old
grader recognized only filesystem and native skill-load output. An
actual-shape regression failed before this correction. The corrected
parser recognizes the retained read in memory. Wrong keys, versions,
source bytes, hashes, partial pages, semantic failures, copied shell
output and foreign or unpaired native calls still fail. No model prompt,
product source or original grade changed. A live pass on this corrected
source remains required.
- CI `41708636059` on the previous `02582` source passed at attempt 3.
The first two interrupted attempts remain recorded. AWS evidence
identified Spot capacity termination for the second Rust runner. The
targeted retry executed Rust and aggregate verification; 46 successful
jobs retained their exact prior execution timestamps and one job
remained skipped. New metadata IDs do not establish new executions.
Fresh full CI and review remain required for this source.
- Earlier CI `41730743279` passed on
`1ee86838ea528edd8c60fb9ddd858faf3c83e783`. A later review finding
correctly showed that the semantic read still depended on an
OpenCode-only directory-name helper. That finding remains recorded.
Three local Codex, ACPX Cursor and Cursor Cloud flow controls reproduced
the rejection before this correction.
- The semantic path now binds the canonical key and immutable version
through the common native runtime skill selection contract. Its
checksum, complete source, page and native call attribution checks stay
unchanged. Missing, duplicate, wrong-key and foreign-version API/native
assignment controls still fail for all three provider configurations.
The OpenCode filesystem wrapper still requires its exact provider and
materialized directory name. These local controls test the existing
canonical receipt shape; they do not claim live provider qualification
or add a guessed provider encoding. Fresh CI and review are required
after this correction.
- This public PR does not enroll the case in the daily plan or change
protected QA configuration.

## Risks

The target must supply a disposable native agent with normal company
skill-write access and cleanup. It must preserve the authoritative
admitted version map and provide a complete source read or named native
skill-load receipt. Missing evidence fails the case even when the
receipt document has the right value. The OpenCode wrapper path requires
matching assignment and native input records for the same immutable
version; it does not accept a generic targetless read. The builtin
first-party path requires the same version binding and exact full-source
receipt. Codex must provide its authoritative controller pair and a
digest that commits to that exact complete result. Partial pages,
unknown operations and semantic failures do not qualify.

Local flow controls simulate the target API and browser boundaries. They
do not establish live Cloud qualification. Experimental assignment pins
need separate qualification. The journey changes only its owned skill,
assignment and task. It changes no shared agent, setting, model,
connection or budget.

## Model Used

OpenAI GPT-6 through Codex with reasoning and code execution. The
runtime does not expose a more specific model identifier or
context-window size. Local checks made no product-model calls.

## 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>
canary/v2026.1104.0-canary.3
2026-11-03 21:13:40 -06:00
DottaandPaperclip ce7766dc11 fix(recovery): resume retries with remote workspace storage (#18910)
## Thinking Path

> - Paperclip manages work performed by AI agents.
> - Native recovery must resume an interrupted run under its saved
ownership.
> - Remote workspace storage records that ownership in
`runnerProfileJson.remoteWorkspace`.
> - Recovery recognized only the older workspace record and plugin task
leases.
> - A retained remote PID could therefore leave a due retry without a
dispatch.
> - This PR routes the new record through the existing remote recovery
verifier.
> - Checkpoint, lease, process and single-executor checks remain
authoritative.

## Linked Issues or Issue Description

**What happened?**

After a native sandbox turn returned without a semantic result, same-run
recovery was saved with a retry deadline. The run retained its sandbox
PID and the new remote workspace reference. The recovery selector
skipped it. Later task attempts could then fail on the unfinished
workspace writer.

**What did you expect to happen?**

The due retry must enter existing remote checkpoint and ownership
verification. Recovery must resume the original run once or expose the
real verification blocker.

**Steps to reproduce**

Persist a retryable native failure with a remote PID, a prepared
`paperclip.remote-workspace-run.v1` reference, an existing lease and
checkpoint, then run heartbeat recovery. The added PostgreSQL cases
reproduce this shape.

**Deployment mode**

Native Paperclip Runner in a sandbox with remote workspace storage.

Related: #18841 handles legacy workspace recovery during storage
migration. #18908 handles failed native preparation. This repair covers
a later, provider-started retry.

## What Changed

- Recognize the current remote workspace record when classifying process
location and selecting remote retries.
- Extend the existing database and real-process fixtures to cover the
new record, including a retained PID without a process group.
- Reuse existing restart recovery. Add no new retry loop, ownership
bypass, migration or workflow change.

## Verification

- Before the repair: three new behavioral cases failed. Two retries
never dispatched; a missing checkpoint never reached its visible
recovery hold.
- After the repair: all 82 heartbeat recovery tests pass.
- All 57 neighboring resumption-binding, restart-recovery and
remote-probe tests pass.
- Safety cases retain retry deadlines, live controller ownership, active
execution, missing-checkpoint rejection, single dispatch and zero host
signaling of the sandbox PID.
- Local platform: macOS arm64, Node 24.22.2, PostgreSQL 18.1. The tests
use disposable databases and an owned live process as the remote PID
collision control. Provider execution is outside this routing
regression.
- Full `pnpm -r typecheck` and `pnpm build` pass at
`34aa2d08d38e8afec7f8d98ce984a74551caeda6`.
- Current-head GitHub checks: 53 successful; `ci / verify` passed.
Paperclip Review completed neutral with a 5/5 assessment and no
findings. Two Storybook jobs were skipped by scope; the external
security scan was neutral. No failures, pending checks, or merge
conflicts remain.
- Greptile: 5/5 at the same commit, with no review threads.
- The duplicate full local `pnpm test:run` was interrupted after
current-head CI passed. Its first two chat shards passed (574 cases); it
has no complete local full-suite verdict. This is separate from the
passing targeted recovery suites and required CI test lanes.
- Staging diagnosis used read-only run, finalization, workspace and
lease evidence. The fixed revision has not been deployed. The original
task's useful continuation remains unverified.
- Initial local attempts stopped before test execution because generated
SDK output and PostgreSQL library aliases were missing. Those setup
failures are retained separately from the behavioral red/green evidence.

## Risks

A wrong remote classification could send a run to the wrong ownership
path. The existing verifier still checks the saved checkpoint,
controller lease and target. Tests require one dispatch, preserve
process identifiers, and prove that recovery never inspects or signals a
coincident host PID. Legacy workspace and plugin task coverage remains
green. This repairs missed continuation; it does not establish the cause
of the earlier missing semantic result.

## Model Used

OpenAI GPT-6 through Codex, with reasoning and tool execution. The exact
model snapshot and context window were not exposed.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 21:06:50 -06:00
27a825da2f feat(telemetry): track the server OS with the registered contract (#18897)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Its first-party telemetry uses a generated event contract and typed
helpers.
> - The existing events do not identify the server's OS family.
> - A proposed event is dropped until the generated contract registers
it.
> - This PR syncs the registered contract and emits the OS family when
an enabled server telemetry client starts.
> - The benefit is a way to compare Windows, Linux, and macOS
installations while keeping machine details out of the event.

## Linked Issues or Issue Description

**Problem or motivation**

We cannot compare Paperclip installations by host OS. Counting task or
install events does not provide this dimension.

**Proposed solution**

Send one `server.started` event with Node's raw `process.platform` as
`os_platform` each time the server initializes its enabled telemetry
client. Compare distinct install identifiers per platform, rather than
restart event totals.

**Alternatives considered**

A proposal-only emitter would send no data. A separate later
contract-sync PR would leave the feature incomplete. This PR includes
the canonical generated SDK and promotes the emitter together. CPU
architecture and OS versions are outside this feature's scope.

**Roadmap alignment**

This is a narrow telemetry addition to existing server startup, with no
new user flow. The full contract copy also adopts the three existing
connector proposals described in #13578; their schemas are already
registered. The generated file stays an exact copy, so their helpers and
tests must be promoted in the same change.

## What Changed

- Export `trackServerStarted` and call it once when server telemetry
initializes. Repeated initialization returns the same client and emits
no duplicate event.
- Copy the canonical generated telemetry SDK byte for byte from schema
source commit `5369e6b`. Remove the startup proposal marker. Normal
opt-out and CI suppression apply before initialization.
- Promote the three connector events included in the canonical SDK.
Derive enum types from the generated map and preserve raw values. Update
their real-client transport and disabled-state tests.
- Assign startup to the existing `operational_enum_count` retention
class. Document OS privacy, container/WSL interpretation, and
distinct-install counting.
- Add real-client startup transport tests and server initialization
tests, including file opt-out, environment opt-out, and CI suppression.
- Keep startup running when telemetry state storage fails. Publish the
client and start periodic flushing only after the startup event
succeeds. On failure, clean up the client, disable telemetry for the
process, and log a generic warning without the storage path or
underlying error.

## Verification

- `pnpm --filter @paperclipai/shared typecheck`: passed.
- `pnpm --filter @paperclipai/shared build`: passed.
- `pnpm --filter @paperclipai/shared exec vitest run`: 98 files, 1,055
tests passed.
- `pnpm --filter @paperclipai/shared exec vitest run src/telemetry`: 6
files, 39 tests passed.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/telemetry-startup.test.ts
src/__tests__/shared-telemetry-events.test.ts`: 2 files, 19 tests
passed.
- Exact generated artifact comparison, `git diff --check`, and local
secret/PII review passed.
- Proposal inventory contains no startup or connector proposals. The
repository extractor fails with TypeScript 7's missing compiler API;
rerunning it with the installed TypeScript 5.9 compiler API passed. No
extractor source change is included.
- Initial local server typecheck attempts found missing workspace
dependencies and a missing plugin SDK link in this fresh checkout.
Dependencies and the local SDK link were repaired; `pnpm --filter
@paperclipai/server typecheck` then passed. Full repository typecheck,
tests, and build are covered by the PR CI rather than claimed as local
passes.

Greptile found that a telemetry state-file error could abort startup.
New regression tests reproduced this on the previous head with both an
injected permission error and the real state store blocked by a file.
Both now pass, including no published client, no periodic flushing, no
send, and no retry after failure. Shared telemetry tests and server
typecheck were rerun after the fix.

Full PR CI passed on `a41540b7899bc8a0aedbd78d72f8b69ad1a4b318`,
including typecheck, build, all selected test shards, and the final
verify gate. [Successful workflow attempt
2](https://github.com/paperclipai/paperclip/actions/runs/41752039595/attempts/2).
Attempt 1 was interrupted when the server (1/12) runner received a
shutdown signal; its incomplete result was retained in the PR
discussion. The same-commit shard retry and dependent gate passed.
Greptile reviewed this exact head at 5/5 with all threads resolved.

## Risks

This change starts sending one OS family value per server startup when
telemetry is enabled. It sends no OS version, architecture, hostname,
path, prompt, or machine identifier beyond the existing envelope.
Containers and WSL report the runtime's OS. Event totals reflect restart
frequency, so use distinct install identifiers for OS adoption analysis.

The complete generated SDK also enables the previously registered
connector proposals. Those events keep their existing approved payloads;
no connection names, credentials, tool arguments, or results are added.
Privacy review is requested for both newly active paths.

Deploy the registered schema before merging this app change. Live
production delivery has not been tested. No schema migration is needed.
Revert this PR to stop the new startup emitter and restore the prior
contract.

## Model Used

- Original implementation: Anthropic Claude Opus 5.5, as recorded in the
original commit.
- Completion: OpenAI `gpt-6.1-sol`, xhigh reasoning, through Codex in T3
Code; shell, Git, and automated tests. Context window size is not
exposed by the runtime.

## 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 described the issue in-PR following the feature issue
template
- [x] The final change and description contain no private issue links or
proposal markers
- [x] The existing PR branch is preserved to retain its review history
- [x] I have run the targeted 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: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 21:00:47 -06:00
DottaandPaperclip 9e61fdebf9 test(evals): pin approval-link fix for case 564 (#18909)
## Thinking Path

> - Paperclip manages work through agents and explicit approval gates.
> - The hiring revision case requires a usable link to the saved pending
proposal.
> - A rendered unprefixed approval link currently misses the company
redirect.
> - The reviewed fix adds one existing redirect route. It changes no
approval authority.
> - The TestOps workflow reads its candidate source from a protected
master pin.
> - This PR pins that one-fix candidate so one remote case can test the
original journey.

## Linked Issues or Issue Description

Refs #17858. Refs #18780.

The source candidate applies only the approval-detail redirect from the
existing fix. The core fix PR remains unmerged. This PR changes the
evaluation source pin.

## What Changed

- Pin candidate `abbde061c7ec9181021858f7b1af21a48637f97f`, a direct
child of the prior `fd5e3b1e` candidate.
- Update the matching launch snapshot target and the expected-pin test.
- Keep all 244 case rows, original inputs, clocks, model, and private
evidence rules unchanged.

## Verification

- The candidate differs from its parent in one line in `ui/src/App.tsx`.
- The owning browser regression follows a real rendered link to a saved
pending proposal. It fails before the route fix and passes after it. The
18 App tests, UI build, typecheck, and token gates passed.
- The candidate token gates passed. The existing source SDK input check
reports 506 unchanged inputs and unchanged runtime pins. No SDK registry
or certificate was renewed.
- The three existing TestOps contract, adapter, and manifest test files
passed: 47 tests, zero failures.
- The first local pin invocation lacked the YAML dependency. It is
retained as a setup failure. The invocation with the existing locked
dependencies passed without assertion changes.
- Exact-head normal PR CI
[41763017485](https://github.com/paperclipai/paperclip/actions/runs/41763017485)
completed successfully: 48 successful jobs and one selector skip. Fresh
Greptile and Paperclip reviews both report 5/5 with no findings; all
review threads are resolved. The optional Superagent security check is
neutral, so no security assessment PASS is claimed. No new provider run
or TestOps PASS is claimed. The intended next check is only
`questions-and-approvals.formal-hire-revision` on existing plan 102 and
job 100.

## Risks

The route regression does not prove the full agent journey passes. The
first remote run will test that remaining gap.

The candidate keeps the prior catalog application base plus this one
route fix. It does not refresh the whole runtime or qualify all 244
cases. The existing broader qualification holds remain. Automatic model
retries stay zero and the authorized concurrency cap stays eight.

## Model Used

OpenAI `gpt-6.1-sol` through Codex. Capabilities used: source analysis,
code integration, and local validation. The host does not expose the
context-window limit. The model under evaluation remains
OpenCode/OpenRouter `deepseek/deepseek-v4.1-flash-1017`.

## 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-11-03 21:00:36 -06:00
DottaandPaperclip 630ed6624c test(e2e): add verified dependency continuation QA (#18901)
## Thinking Path

> - Paperclip lets people manage work assigned to AI agents.
> - Task dependencies must hold assigned work until its blockers are
complete.
> - The reusable Product E2E set needs a browser journey for that full
path.
> - The journey must verify saved records, automatic wake identity, and
the result after reload.
> - The original implementation passed staging QA, but its automated
review workspace has an export hold.
> - This replacement integrates current master and earns a separate
normal review while preserving the original attempt.

## Linked Issues or Issue Description

Refs #18789, the original dependency QA pull request. Related: #7542 and
#17524 for dependency wake and explicit resume behavior; #18782 for the
merged lifecycle registration layout.

**What existing behavior does this improve?**

The Product E2E catalog can verify the complete blocked-task
continuation path against a configured native Cloud target.

**Current behavior**

The main catalog lacks this full browser journey. The original PR's
automated review task has a retained workspace export hold
(`native_workspace_sync_out_retry_exhausted`), with neither Retry nor
Continue available. Its task, files, checks, and failed export attempts
remain preserved.

**Proposed behavior**

Add the same independently checked `task-dependency-resume` journey
through this replacement. Its new commit normally merges the actual
fetched master `4e29f001396c9fc76075850165ccfa3a6743e3ef` into verified
source `e53dd51407b901b14bd582eeb2071fabe6955d9d`. That integration
contains 39 real upstream path updates. The replacement must pass fresh
CI and automated review on its own head.

**Reason and benefit**

Daily QA can detect premature work, a missing or duplicate continuation,
and a result from the wrong run. A separate review workspace lets the
normal review process inspect the current integration while the original
held workspace stays intact.

**Breaking changes**

The change adds an explicit journey. Existing journeys and their
registrations remain present.

## What Changed

- Add a browser journey that creates a human-owned blocker and dependent
task, saves their relation, selects Blocked, and assigns the configured
native agent.
- Require a measured ten-second blocked wait, a board-owned blocker
completion, and exactly one automatic successful native run.
- Independently verify the saved wake source and blocker identity,
configured provider/model/environment, committed native phase, document
revision authorship, and reloaded result.
- Return `unsupported` before observations or task creation when the
native provider or model identity is missing or blank.
- Attempt both evidence writes, preserve an existing exact error object
and outcome, and fail otherwise successful work when required evidence
cannot be saved.
- Grade runtime identity, document authorship, reload visibility, and
retained history from their own evidence bound to the same company,
task, agent, and run. All eight checks remain required.
- Keep the random review reference in the blocker description. Save
allowlisted private observations and read failures through the existing
evidence pipeline.
- Register the case at the distinct dependency anchors in the catalog,
types, checks, source hash, dispatcher, features, and documentation.

The delta against fetched master is exactly the original 15 QA paths.
The fixture, contract tests, shared registrations, and dispatch remain
unchanged. The browser actions and observations are byte-identical to
`d41558b5`; only evidence finalization changes in that flow. Six
shared-file AST comparisons still match fetched master after removing
only the dependency registrations. All upstream integration changes
remain preserved.

## Verification

Prior acceptance remains bound to its actual source:

- [Staging QA run
41731870131](https://pages.paperclip.ing/evals-qa-smoke-41731870131-1/)
passed all eight checks and all five phases, with closed cleanup, on
`dc9ea42d46d3015c010d0532b48e0da973d3611a`.
- [Original full CI run
41745149524](https://github.com/paperclipai/paperclip/actions/runs/41745149524)
passed on `e53dd51407b901b14bd582eeb2071fabe6955d9d`: 48 successful jobs
and one skip on attempt 1.
- The original failed staging result and the original held review remain
intact. These prior passes are not new checks for this replacement.

Passed locally on the current replacement source with Node 24.22.2 and
pinned pnpm 9.15.4:

- One complete `install --frozen-lockfile --ignore-scripts`, followed by
the maintained `node scripts/link-plugin-dev-sdk.mjs` for links inside
this checkout.
- `node scripts/ensure-plugin-build-deps.mjs` and `pnpm --filter
@paperclipai/db run build`.
- `node scripts/typecheck-test-projects.mjs --low-memory
tests/runner-e2e`, including the current Runner TypeScript build, fresh
server declarations, and E2E typecheck.
- 239 focused controls across eight files. These cover
missing/null/empty/blank execution identity, real filesystem write
failures, synchronous writer failures, frozen original errors, and
independent defect attribution.
- 5,302 comparisons against the prior grader, with zero aggregate
acceptance changes or new throws. The sealed actual staging observations
from run 41731870131 still produce the same eight passing checks. This
is finite calibration of retained evidence, not a new staging run.
- Credential-free discovery: 22 unique cases, including dependency and
lifecycle.
- Source-preservation checks and whitespace checks for both the normal
integration and the replacement delta.

The focused command is:

```sh
node node_modules/vitest/vitest.mjs run --config tests/runner-e2e/vitest.config.ts tests/runner-e2e/product-journeys/task-dependency-resume-evidence.test.ts tests/runner-e2e/product-journeys/task-dependency-resume.test.ts tests/runner-e2e/product-journeys/task-dependency-resume-contract.test.ts tests/runner-e2e/product-journeys/contracts.test.ts tests/runner-e2e/product-journeys/observation-read.test.ts tests/runner-e2e/catalog.test.ts tests/runner-e2e/journey-features.test.ts
```

The old generated cache failed current-source equality and was not
copied. Canonical current outputs were built and sealed with their input
hashes, including CLI, UI, and provider inputs. No Rust build, QA
dispatch, model call, or action on the held workspace was performed for
this replacement.

Review follow-up: the first replacement head
`b4408c5732e1800f7ccf66c639cf382b5ff2f01a` passed full CI run
[41749940687](https://github.com/paperclipai/paperclip/actions/runs/41749940687),
with 48 successful jobs and one skip. The native review then found the
missing execution-identity preflight. The preflight now rejects that
setup before work. Completed current-head checks are recorded below.

The current E2E typecheck reused the same canonical declarations only
after all 11,273 build inputs, two installed toolchain files, and 6,747
generated files matched their saved hashes. The five changed case files
are outside that build-input set. One sandbox test run passed 216
controls and lacked the report subprocess output. A direct `tsx`
diagnostic confirmed a temporary pipe `listen EPERM`. The unchanged 217
controls then passed with ordinary local IPC permissions. Both results
remain recorded.

The `d41558b5807e609032bbadddb103d6b0a7db4a12` attempt
[41753398775](https://github.com/paperclipai/paperclip/actions/runs/41753398775)
is retained: 45 successful jobs, one skip, two failed server shards, and
a failed aggregate. The runtime readiness case refused connections
within its ten-second window; the issue-deadline and large repository
cases hit their existing test limits. Their causes remain unknown. A
source-matched Linux CI run executed and passed all three cases, and one
unchanged local runtime case passed. No timeout or assertion changed and
no old-head diagnostic recheck was issued.

The native review then identified error masking during evidence
persistence and cumulative diagnostic flags. The current head fixes
those two owned-case issues. Its completed current-head checks are
recorded below. Seventeen of twenty new regression controls failed
before these fixes; all now pass. Two additional controls exercise
synchronous writer failures. Both original review and CI evidence remain
intact.

The eight required checks, one-run limit, 500-cent configured budget,
and bounded ten-second observation are unchanged. Fresh full CI,
current-head Greptile 5/5 with no open findings, and every actual review
context must complete before merge. Prior statuses are not copied or
overridden.

Current-head verification is complete for
`d9bc055f1f6609463679dc78fdcb96034d2c3727`:

- [Full CI run
41758005698](https://github.com/paperclipai/paperclip/actions/runs/41758005698),
attempt 2, completed successfully at 02:26:19 UTC on November 4: 48
successful jobs and one skip.
- Attempt 1 lost one exact Spot runner. AWS confirmed
`Server.SpotInstanceTermination` and `instance-terminated-no-capacity`;
its log recorded the shutdown. That failed job and the failed aggregate
remain preserved.
- One normal failed-jobs recheck ran only the interrupted static checks
(02:19:13–02:25:54 UTC) and aggregate (02:26:13–02:26:18 UTC). The other
46 successful start/finish pairs remained unchanged. The skipped
selector was not an execution.
- Greptile and Paperclip Review both scored this exact head 5/5. There
are no unresolved review findings.

## Risks

- The staging pass remains attributed to `dc9ea42`; final admission must
retain that source provenance and the current preservation proof.
- The case uses one paid work run when executed. Agent setup usage
remains separate from task-run accounting.
- The ten-second blocked observation proves a finite wait. Missing,
incomplete, foreign, premature, or duplicate evidence fails the
independent checks.
- The original held workspace still needs its own disposition. This PR
leaves it preserved for its owner.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, code editing, and code
execution. The exact 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-11-03 20:35:43 -06:00
DottaandPaperclip a194166017 perf(ci): repair dependency caches and parallelize transport fixtures (#18883)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Required CI checks protect changes to its native Runner and control
plane.
> - Recent Runner jobs restored a Rust cache but still rebuilt
dependencies for 74–92 seconds.
> - New workspace install hooks disabled the materialized JavaScript
dependency cache.
> - A single transport file also kept 326 test cases on one worker.
> - This pull request corrects the cache inputs, replays known install
hooks, and separates independent transport test groups.
> - The same checks can finish sooner without removing test cases or
widening runner access.

## Linked Issues or Issue Description

Refs #18794 and #18544. Related dependency-action version update:
#17698; this change fixes the cache input contract used by the currently
pinned action.

**What happened?**

Recent PR jobs such as #18783 and #18825 restored an exact Rust
dependency cache from October 16. The virtual workspace's development
profile changed on October 30. Rust Cache 2.9.2 hashes member manifests
but omits that virtual manifest. Rebuilding cannot replace an immutable
exact cache entry. The JavaScript cache also rejected the new server and
Runner Copilot postinstall hooks.

**Expected behavior**

Changing a Cargo profile selects a new dependency cache. An exact
JavaScript dependency restore still performs required workspace install
hooks. Independent transport fixtures use the existing file workers
while retaining all assertions and real deadlines.

**Steps to reproduce**

Inspect the Rust dependency cache hit and following `build:rust` in the
recent Runner package jobs. Run `node scripts/ci-dependency-cache.mjs`
on the previous revision to see `cacheable: false`. Run the original
transport file to measure the serial test work.

## What Changed

- Add the virtual workspace manifest and ancestor Cargo configuration to
all shared Rust dependency keys. Keep reader/writer identities aligned
and preserve master-only cache writes and CODEOWNERS-only AWS routing.
- Recognize only the exact reviewed root, server, and Runner postinstall
commands. Include the server/Runner installer scripts in the dependency
key. Replay workspace postinstall hooks after a materialized restore.
Unknown lifecycle hooks still disable reuse.
- Move attachment recovery, warm attachment, live adoption, and
suspension fixtures into separate files. Keep the large event-backlog
cases in the original file to avoid starting another expensive fixture
group at the same time. Share five stateless test helpers outside
production source.
- Extend the opt-in `profile-ci` benchmark with a PR-scoped Rust cache
producer and a fresh-host consumer. Require exact dependency/workspace
hits and reject any Cargo unit that is rebuilt. Retain the JSON build
evidence. Existing setup qualification checks real JavaScript cache
restores.
- Schedule the ten measured files over 50 seconds first, in approximate
duration order. Package CI uses up to four workers, bounded by available
CPUs; local runs retain Vitest defaults. The AWS runner has four CPUs
and 16 GiB RAM; the three-worker run averaged 44% CPU and peaked at 15%
memory. Preserve Vitest selection and shard membership and default
ordering for other files. Job count and AWS authorization are unchanged.
- Scope the optional paired server benchmark to changes in its measured
inputs. Cache-only changes qualify exact restores and run the shared
package tests using restored dependencies.
- Update the cache and test documentation and the existing native cache
contract guard.

## Verification

- All 586 `.github/scripts/tests/*.test.mjs` policy tests passed,
including cache trust and runner routing guards. Actionlint passed for
all three changed workflows.
- All 169 original top-level transport test definitions remain
byte-identical. The original file passed all 326 cases in 317.99 seconds
on this macOS host. A clean preliminary split passed the exact same
named cases in 183.01 seconds with the same four-worker cap.
- Preserve the first failed experiment: its parallel run overlapped the
policy suite and timed out in the 1,024-event settled backlog case at 60
seconds; 325 cases passed. A clean repeat passed, but that does not
establish the earlier failure's sole cause. The final layout keeps the
backlog cases in the original file. The final layout passed the same 326
cases in 186.35 seconds (41.4% faster than the 317.99-second baseline).
The original test bodies and all timeouts remain unchanged.
- Fresh-host Rust qualification passed: all 308 compilation units were
fresh, and Cargo completed in 1.59 seconds on a new runner ([final-head
run](https://github.com/paperclipai/paperclip/actions/runs/41745082837/job/126296908816)).
JavaScript setup dropped from 46 seconds cold to 23 seconds with exact
dependency/build restores; all 1,052 shared-package cases passed against
the restored tree ([final-head
job](https://github.com/paperclipai/paperclip/actions/runs/41745082837/job/126295937797)).
The previous qualification also passed (43 to 19 seconds) and confirmed
all three postinstall hooks replayed. These are qualification results,
not an end-to-end PR timing claim.
- Initial full AWS package run passed 4,907 cases with 10 existing
skips. The original-file remainder fell to 118.7 seconds, but the
100-second deadline file started last, leaving the full package at
417.43 seconds. Starting only three long files first passed 4,911 cases
(including four new sequencer guards) in 422.54 seconds, so reordering
alone did not improve full-package time. Summed file execution was 1,202
seconds across three busy workers. The final four-worker configuration
passed **4,912 cases, with the same 10 existing skips**, in **324.99
seconds**, a **23.1% reduction** from 422.54 seconds. The five added
cases guard ordering, selection, sharding, and path handling. Peak
memory was 17.95% of the 16 GiB runner; average CPU was 49.14%
([final-head package
job](https://github.com/paperclipai/paperclip/actions/runs/41745082974/job/126296339480)).
- Local workspace typecheck and build passed. The complete local `pnpm
test:run` passed all six chat partitions but reported failures in one
existing OAuth redirect case and one gateway authority case in the
general-server group. Both focused repetitions passed (three OAuth cases
and one gateway case), and a complete repeat of both files passed all
593 cases in 42.89 seconds; that does not explain the original failures.
The broad local run was stopped after 61 minutes once full final-head CI
had passed; its remaining general/workspace/serialized work was not
completed locally, and it is not reported as a pass. Original failure
logs were retained. Full final-head CI provides the complete suite
validation. Sequencer selection/shard guards and the 11-case transport
suspension smoke passed. The corrected release workflow guard passed all
71 tests.
- The first CI run failed only the policy guard that still expected the
old native cache key; downstream aggregate checks also failed. That
guard now checks the new profile/config prefix and existing
host/target/protocol suffix. A second policy run found that the new
sequencer test imported Vitest in the dependency-free policy lane; it
now runs in the Runner package lane, where all four cases pass. Greptile
reviewed final head `8011f84189` at 5/5 with no actionable findings and
confirmed this issue fixed. Final-head policy, typecheck, build, and
review passed. At 23:52:46 UTC on November 3, several AWS jobs were
terminated together: the Runner package process received SIGTERM and
browser logs recorded a runner shutdown signal. The failed/cancelled
jobs passed on the same-head retry, including required `ci / verify`; no
assertion or timeout was changed in response. The final concurrency
adjustment at `fe5a69bb0b` also received Greptile 5/5 with no actionable
findings. Final-head required CI, including `ci / verify`, passed
([aggregate](https://github.com/paperclipai/paperclip/actions/runs/41745082974/job/126300554711)).
Local timings exclude install, build, cache transfer, and CI queue time.

## Risks

A new Rust key needs one successful canonical master build before
ordinary PRs can hit it. The fresh-host qualification uses separate
PR-scoped entries. Parallel test files can increase I/O contention; case
execution inside each file, timeouts, and assertions are unchanged. Two
extracted suites exceed 1,000 lines because they retain existing
multi-stage recovery fixtures unchanged; one parameterized fixture alone
is 1,059 lines. No production execution logic, lockfile, credentials,
runner trust gates, or required check is removed.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository editing, command
execution, and GitHub inspection. The host does not expose a more
precise model snapshot or context-window size.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 20:17:08 -06:00
Devin FoleyandClaude Opus 5.5 925965a09b fix(task-retry): wait out brief chat authorization contention (#18895)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A user can click Retry on a task that is connected to an external
chat conversation.
> - That Retry prepares the chat retry inside one admission transaction.
The chat step takes a per-principal authorization lock with
`pg_try_advisory_xact_lock`.
> - When another chat transaction holds that lock for a short time, the
chat step throws `NativeChatReviewPresentationContentionError`. This
error is not an `HttpError`. The route returns HTTP 500, and the error
handler reports a server crash.
> - The contention is routine and short. The admission transaction does
not contact a provider, and it rolls back completely. The owning service
can safely try it again.
> - This pull request retries the admission a bounded number of times.
If the lock stays busy, the service returns a retryable 409 instead of a
500.
> - The benefit: the user's click usually succeeds without an error, and
Paperclip stops reporting expected lock contention as a crash.

## Linked Issues or Issue Description

**What happened?**

A hosted staging server reported
`NativeChatReviewPresentationContentionError: Chat response
authorization is temporarily busy; no provider delivery was attempted`
from `POST /api/issues/:id/retry`. The stack is `requestTaskRetry` →
`writeExecutionControl` → `prepareFailedChatRunRetry` →
`failedChatRetrySource`. The error came from the
`chat-identity:<company>:<principal>` advisory try-lock. The route
returned HTTP 500 for each occurrence.

**Expected behavior**

Short lock contention must not fail a deliberate Retry click. The
service must wait it out. If the lock stays busy, the service must
return a clear, retryable client error and must not report a server
crash.

**Steps to reproduce**

1. Bind a task to a restricted chat endpoint and conversation. Make the
latest task run a failed run.
2. In a different database transaction, hold
`pg_try_advisory_xact_lock(hashtextextended('chat-identity:<company>:<principal>',
0))`.
3. Call `POST /api/issues/:id/retry`. Before this change, the chat retry
step throws `NativeChatReviewPresentationContentionError` and the route
returns 500.

**Paperclip version or commit**

Reproduced on master `8771606fd87f69ed4057779e3dce3a9bca8088e6`. The
server release that reported it was
`d59791828482f213713ca55000c40e6c9c6c03e5`.

**Deployment mode**

Hosted staging, Node 24, PostgreSQL.

Related: #18845 and #18621 changed nearby Retry admission. They do not
handle this contention.

## What Changed

- `server/src/services/task-retry.ts`: `requestTaskRetry` now calls the
unchanged admission transaction (`admitTaskRetry`) in a bounded loop: 10
attempts, 100 ms apart. It retries only
`NativeChatReviewPresentationContentionError` and lock-not-available
contention (`isExternalChatWaitAuthorizationContention`, which matches
`55P03`). Every other error, including 403/404/409 decisions, propagates
unchanged on the first attempt.
- If the lock stays busy for all attempts, the service throws
`conflict(..., { code: "task_retry_busy", retryable: true })`. The UI's
existing 4xx handling resets the Retry button, and the error handler
does not report a 4xx as a crash.
- `server/src/services/task-retry.test.ts`: three PostgreSQL cases:
- Real advisory-lock contention. A second transaction holds the same
`chat-identity` lock that production checks, then releases it. Retry
succeeds and persists exactly one wake request. A replay with the same
idempotency key returns the same request.
- Persistent contention returns 409 `task_retry_busy` after exactly 10
attempts and persists no wake request.
  - A 403 from the chat step does not retry.

The transaction body is unchanged. It was moved into `admitTaskRetry`
without edits.

## Verification

- RED: with the old `task-retry.ts` and the new tests, the contention
case fails with the exact production error
(`NativeChatReviewPresentationContentionError: Chat response
authorization is temporarily busy…`). The persistent case fails because
the error is not an `HttpError`.
- GREEN: `pnpm exec vitest run src/services/task-retry.test.ts` passes
all 39 cases against embedded PostgreSQL.
- `pnpm --filter @paperclipai/server typecheck` passes (all layers,
including tests).
- `src/services/heartbeat/workspace-preparation.test.ts` (another
`requestTaskRetry` caller): 165 of 167 cases pass. The 2 failures
(`remote_workspace_migration_source_changed` in "resumes an imported
path/file-url Git source…") also fail on unchanged master in the same
environment, so this change does not cause them.
- Runtime assumption (rule `runtime-dependency-boundaries`): the retry
is safe only if a failed attempt leaves no persisted state. The
contention test uses a real PostgreSQL advisory lock held by a separate
connection and checks that only one wake row exists after the failed
attempts. Each attempt is a separate transaction, so the loop holds no
row lock or advisory lock while it waits.
- The full repository `pnpm test:run` and `pnpm build` were not run
locally. I rely on CI for those.

## Risks

- Low risk. The worst case adds about 1 second of request time when the
lock stays busy, and then returns 409 instead of 500.
- An attempt can fail after an earlier step in the same transaction (for
example, workspace preservation registration). The whole transaction
rolls back, so the next attempt repeats those steps from a clean state.
Those steps run in the same transaction and do not contact a provider.
- If `requestTaskRetry` is called inside an outer transaction, each
attempt rolls back only to its savepoint. Locks that the outer
transaction holds stay held. In that case the loop ends with the same
409 after its bounded budget.
- No schema, API shape, or UI change.

## Model Used

Claude Opus 5.5 (`claude-opus-5-5`, Anthropic), through Claude Code with
tool use (shell, file editing, test execution) 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 (focused suites; see
Verification for the two pre-existing failures)
- [x] I have added or updated tests where applicable
- [ ] I have updated relevant documentation to reflect my changes (no
behavior or command documentation changes are needed)
- [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: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 18:05:40 -08:00
DottaandPaperclip 057898530c fix(server): handle wrapped recovery action conflicts (#18885)
## Thinking Path

> - Paperclip coordinates durable agent work and recovery.
> - Independent workers can find the same task needs recovery.
> - The database permits one active recovery action for that task.
> - A losing insert must read the action that already committed.
> - Drizzle wraps the PostgreSQL unique violation in its cause.
> - The current classifier reads only the outer error and misses that
conflict.
> - This PR recognizes the same allowed conflict through a bounded cause
chain.
> - Both workers can then return the one durable action through the
existing retry path.

## Linked Issues or Issue Description

Refs: #18783.

**What happened?**

The saved-disposition-debt recovery regression failed in CI
`41733864320`. Two recovery workers collided on
`issue_recovery_actions_active_source_uq`. The fixture captured an
unhandled PostgreSQL `23505` error. A separate canceled shard in that
run lost its AWS Spot instance; that interruption is a different
failure.

**Expected behavior**

Two service instances that race to create the same active action must
return that one action. Unrelated database failures must still
propagate. Existing transaction and ownership rules must stay in force.

**Steps to reproduce**

Hold a PostgreSQL SHARE lock on the recovery-action table. Start the
same source-scoped upsert on two independent service instances. Confirm
both real INSERTs are waiting in `pg_stat_activity`, then release the
lock. The original code persists one action but rejects one caller with
a `DrizzleQueryError`. Its outer code is absent; its `PostgresError`
cause has SQLSTATE `23505` and the expected active-source constraint.

**Paperclip version or commit**

Supporting branch starts at master
`54782706e1d83d92295246e499627930df03662d`. It now contains a normal
merge of master `8771606fd87f69ed4057779e3dce3a9bca8088e6`. The
classifier correction stays unchanged. The PR diff against that master
still contains only the recovery service and its tests.

## What Changed

- Inspect up to eight error causes with cycle detection.
- Require SQLSTATE `23505` and one of the same two recovery-action
constraint names on the same error. Support the PostgreSQL driver's
`constraint_name` field.
- Reuse the existing source-scoped retry path and its unchanged
three-retry limit.
- Add a deterministic real-database collision regression and no-retry
controls for unrelated uniqueness, other SQLSTATEs and cyclic errors.
- Keep the original recovery regression, assertions and timeouts
unchanged. Keep the saved-skill journey separate in #18783.

## Verification

- Review follow-up: the real collision test now checks the intended
retry merge. The surviving action has attempt count 2, active status,
the default unlimited budget, the expected owner, supplied evidence and
supplied attempt timestamp. Its wake and monitor fields remain null.
Existing identity and convergence assertions run first.
- A disposable counterfactual returned the winner unchanged after the
lost insert. It passed the original barrier, one-row, no-rejection and
same-ID assertions, then failed the new attempt-count check with 1
instead of 2. The counterfactual was not committed. The production
service was restored byte-for-byte. The actual service passed all seven
focused race, no-retry and sequential controls. The affected canonical
test-tree-2 typecheck passed against verified unchanged local
declarations.
- Parent `4f8abfe4f2244649219ef6ced3534634193f0b53` full CI
`41746314291` passed all 48 selected jobs with one intended skip. Its
5/5 summary also had two duplicate unresolved reliability comments about
the missing state assertions. This test-only follow-up addresses both.
Fresh full CI and current-head review are required again.
- The deterministic real-database regression failed before the
correction. Both inserts reached the lock and exactly one active action
persisted. The losing error was a `DrizzleQueryError` with a
`PostgresError` cause, SQLSTATE `23505` and constraint
`issue_recovery_actions_active_source_uq`.
- **11 focused checks passed across two files on the integrated head**
(181.72 seconds). This includes the real-database collision, no-retry
controls and all four unchanged recovery producer/dependency
regressions, including the original failed real-clock saved-debt case.
Both callers return the one persisted action.
- Current-head canonical server typecheck passed on Node24.22.2 with all
seven layers: below, cycle, above, tests and test-tree-0 through
test-tree-2. Canonical shared, SDK, DB and Runner TypeScript
prerequisites passed. The first filtered installs lacked CLI and UI
imports required by the new test layers. Those module-resolution
failures remain recorded as setup failures. A full frozen workspace
install with lifecycle scripts disabled supplied the same locked
dependencies. No source or type-configuration workaround was used. No
Rust or broad workspace build ran locally; full repository typecheck,
tests and build remain hosted-CI gates.
- The actual supporting master baseline reproduced the same wrapped23505
failure before the correction. Earlier configuration-resolution, local
IPC and native-library prerequisites remain separate setup failure
evidence. No test failure was reclassified as a pass.
- First supporting CI `41739489620` was canceled before its build/test
jobs completed. Its later automatic full-CI run `41739747947` failed.
The recovery server shard passed. The Runner failure was the Cursor
fixture case repaired upstream in #18782, now included by the normal
master merge. That CI also observed an attachment-browser timeout, a
repository-rehome timeout and a lost-runner connection. Their underlying
causes remain unknown. A later pass will establish recovery, not explain
those earlier failures. These original results remain retained.
- Current `fbd392b9b65dbcf4ac078825096037ed0d490a0b` full CI
[41751706634 attempt
2](https://github.com/paperclipai/paperclip/actions/runs/41751706634/attempts/2)
passed: 48 successful jobs and one intended skip. Attempt 1 remains
retained: ten primary jobs lost their bound Spot capacity and the verify
job failed as a dependent. A single ordinary failed-job recheck produced
eleven fresh successful executions (ten primaries and verify);
timestamps prove 37 successful executions were retained. The skip is not
counted as execution. Current Greptile is 5/5 with both addressed
reliability threads resolved and no unresolved findings. All 57 current
check runs are successful, neutral or skipped, and Snyk is successful.
This is CI recovery; it does not explain the separately retained older
attachment and repository timeouts.
- The original CI failure and both failed saved-skill QA attempts remain
unchanged. This PR does not establish a live QA pass or deploy a
production change.

## Risks

Conflict classification must not turn unrelated errors into retries. The
new controls require those errors to propagate after one insert attempt.
The traversal is bounded and stops at cycles. Transaction, company
scope, ownership, notifications, retry count and database constraints
are unchanged. No schema or public API change is required.

## Model Used

OpenAI GPT-6 through Codex with reasoning and code execution. The
runtime does not expose a more specific model ID or context-window size.
These checks make no model or provider calls.

## 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-11-03 19:51:37 -06:00
Michael NguyenandPaperclip 58d1b21098 fix: separate fresh setup from archived personal connections (#18902)
## Thinking Path

> - Paperclip helps people manage AI agents and their service access.
> - Each personal connection belongs to one user.
> - Setup can recover an archived connection by its name and provider.
> - A fresh setup can therefore select another user's old identity and
fail its owner check.
> - This change creates a separate connection for fresh setup after
personal archival.
> - Explicit reconnect still requires the existing owner and preserves
its history.

## Linked Issues or Issue Description

**What happened?**

A fresh GitHub setup can return 403 after a personal connection with the
same name was archived. The request does not include a reconnect target.
Setup still selects the archived identity by name and provider.

**Expected behavior**

Fresh setup creates a separate personal identity. It leaves the archived
connection, credentials, grants, and activity unchanged. Explicit
reconnect rejects a different user.

**Steps to reproduce**

1. Create a personal GitHub connection in a test company.
2. Archive that connection and its application.
3. As another authenticated member, start fresh personal setup with the
same name and provider.
4. Observe the owner-check failure before this change.

**Paperclip version or commit**

Base: db9be4c5a. Tests use isolated database and HTTP fixtures.

**Deployment mode**

Authenticated. The legacy owner can be a local board identity.

## What Changed

- Exclude implicitly recovered archived personal connections from fresh
setup.
- Use the existing company-scoped name allocator to create a separate
application and connection.
- Test three owners: a legacy board identity, another user, and the
current user.
- Check credential isolation, unchanged archived records and activity,
missing-key rejection, and explicit reconnect denial.
- Keep managed revival and generic MCP rollback/reconnect fixtures on
explicit reconnect targets.
- Verify generic personal URL setup creates an independent identity and
preserves the archived records.
- Document fresh setup and reconnect behavior.

## Verification

- Final head: `96184e3179aab2eeb88df87d442691741a4875a2`. Full CI
recovered and completed successfully: [run 41752164958, attempt
2](https://github.com/paperclipai/paperclip/actions/runs/41752164958/attempts/2).
PR rollup: 53 successful checks, 3 skipped, none failing or pending.
Typecheck, build, all server shards, Runner lanes, and E2E gates pass.
- Exact-head Greptile: 5/5, zero inline comments or review threads.
Independent engineering/security reviews approved the production
implementation; engineering approved the test-only follow-up. Browser
acceptance review approved with the timing qualification below.
- Final-head local generic MCP suite: 84/84 passed (23.26 s); server
typecheck passed. Reviewer independently reran the three changed
scenarios: 3/3 passed (15.74 s).
- Production head `587631230`: local tool-access suite 504/504,
recursive typecheck, and full build passed. Broad local checks were not
repeated for the test-only delta. Full local `pnpm test:run` remains
incomplete after interruption during the first chat shard; no
full-local-suite pass is claimed.
- Initial full CI at `587631230` failed three generic MCP fixtures that
still relied on implicit archived personal recovery. All three
reproduced locally (15.01 s). The test-only follow-up gives owner
reconnect/rollback scenarios explicit targets and verifies independent
fresh URL setup without relaxing their assertions. Corrected server
shard 12/12 passes on final head. The unfinished earlier run was
cancelled after the follow-up push.
- Initial current-head E2E shard 3/8 failed a 5000 ms task-heading wait
at `tests/e2e/cloud-auth.spec.ts:46` (40 passed, one failed). Its
screenshot/accessibility snapshot contains the expected heading. The
single authorized no-code rerun passed 41/41 with zero test retries,
including all URL, handoff, and login-form checks. The first-heading
assertion took 3774 ms on rerun. Both attempts tested the same merge
commit for this PR head. These observations support a timing concern;
the original cause remains unknown. The unchanged Cloud-auth path does
not invoke archived-connection setup. Recovery is established; an
infrastructure cause is not. If it recurs, capture an instrumented
first-navigation trace and compare an unchanged baseline under
comparable conditions.
- PR remains draft. Merge and deployment are outside this request.
Synthetic provider/browser fixtures do not prove live GitHub
authorization or a deployed fix.

## Risks

- Fresh setup after personal archival now creates a new ID and a
suffixed name, including for the same owner. Clients must supply an
explicit target to reconnect the old identity.
- The guard applies to all personal gallery connections, not only
GitHub.
- No schema migration is required. Explicit owner checks remain in
place.
- Security review approved the unchanged production implementation.
Merge and deployment are outside this change request.

## Model Used

OpenAI GPT-6 through Codex. The runtime does not expose a more specific
model snapshot or context window. Used reasoning, code editing, and
shell 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-11-03 17:51:29 -08:00
DottaandPaperclip e2dc2cc0be fix(native): retire idle shared-sandbox ingress owners before dispatch (#18893)
## Thinking Path

> - Paperclip manages agents and their tasks.
> - Native tasks can share one reusable sandbox workspace.
> - A completed child can keep its provider process warm.
> - A parent has its own logical session, but the sandbox has one
ingress listener.
> - Retirement by task identity leaves the child's listener in place.
> - A new parent Runner then fails to bind that listener.
> - This pull request retires a proven idle physical owner before the
parent's distinct session starts.

## Linked Issues or Issue Description

Refs #15353 and #17097 for listener configuration and warm transport
rotation. Refs #18682 for owned process retirement. #18860 repairs a
different resume-source failure. Searches found no existing repair for
retained warm sessions on different tasks that share the same physical
ingress allocation.

**What happened?**

A completed child and a later parent can use the same reusable sandbox.
Warm-session retirement compares logical task ownership. It does not
find the child's distinct task scope. The child keeps the fixed ingress
listener, and the parent's new process fails with
`runner_ingress_bind_conflict` / address already in use.

**Expected behavior**

A parent must start in its own logical session after the proven idle
prior listener closes. Active, foreign or unverified owners must remain
protected.

**Steps to reproduce**

Run the `retires the completed child listener` cases in
`native-session-executor.test.ts`. The actual executor registers a warm
child. PostgreSQL holds its committed, accounted result and clean
expired lease. A distinct parent receives the same allocation and
current workspace writer. The provider boundary uses real TCP listeners.
The original executor reaches the second bind with the child still
listening and fails with EADDRINUSE. The changed executor waits for the
owned close, then starts the distinct parent session.

**Paperclip version or commit**

Reproduced on ab2d2098a2. The branch
includes current master 44a4ca1c61 before this PR was opened.

**Deployment mode**

Native Runner with provider ingress on a reusable sandbox workspace.
Local regression uses real PostgreSQL and local TCP with a controlled
provider-process boundary. No hosted provider call was made.

## What Changed

- Keep physical ingress identity separate from logical task/session
ownership. Never attach the parent to the child's session.
- Reserve a physical allocation during local admission. Retire a
distinct warm owner only after canonical company, agent, workspace,
provider, allocation, lease, task and coordinator checks pass.
- Require the current remote workspace writer and location to match the
admitted run. Require the prior run to be committed and accounted, with
its original controller generation and a clean released or expired
lease.
- Use the existing close, checkpoint and stopped-file collection path.
Wait for close before parent dispatch, then recheck canonical authority
before staging files or calling the parent backend. Retain the entry
when close is uncertain.
- Add actual executor/PostgreSQL/TCP safety cases and document the
contract. Keep maps unchanged for this bug fix.

## Verification

- Untouched production at ab2d2098: actual executor + PostgreSQL + TCP
fails EADDRINUSE. Same positive case passes after the change.
- Final focused suite: 20 passed. It covers distinct parent session
identity; foreign company, allocation and workspace; active or
unaccounted owner; changed controller; reassignment; competing
lease/writer; stale allocation metadata; Stop before and after claim;
workspace-lock race; failed close that leaves the listener and accepted
result intact; and controller transfer, Stop or writer change during
awaited close. These last cases reject the stale parent before backend
invocation after the old listener closes.
- Untouched head3425acbe: transferring the incoming coordinator during
close still reaches the parent backend. The same actual
executor/PostgreSQL/TCP case passes after the post-close authority
check.
- Broader `native warm session supervision` suite: 160 passed. This run
preceded the final two-line logical-session collision guard; the final
20-case suite includes that guard.
- `pnpm --filter @paperclipai/server typecheck`: all source and test
layers passed, including `src/__tests__`.
- `pnpm --filter @paperclipai/server build`: production server and
Runner build passed at current head572acfef79. The exact-head build
stamp is recorded.
- The provider/close boundary is controlled. The fixture seeds canonical
committed/accounted finalization data in PostgreSQL. These tests do not
claim a live model result, operator ACK, hosted PID-to-listener
identity, or recovery of a historical task.
- Full repository pnpm checks were not run locally for this server-only
slice. Required current-head CI and reviews are pending.

## Risks

The new retirement path can close a process from another task. It
requires the same canonical company, agent, allocation and workspace, a
current exclusive writer, and a completed idle owner. Missing or changed
evidence leaves admission blocked. Active owners, changed generations,
reassignment and cancellation remain fenced. A failed close retains the
owner and saved result.

This covers current-controller warm entries with recorded physical
identity. Historical processes outside the registry keep the existing
exact-owner recovery requirements. No port shuffle, port kill, task
capability expansion, new polling loop or invented receipt is added.

## Model Used

OpenAI GPT-6.1 Sol (`gpt-6.1-sol`) in Codex, with source editing,
terminal execution and tool use. The host did not report its
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
- [ ] 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-11-03 19:44:12 -06:00
DottaandPaperclip d7283e8757 test(e2e): cover chat reload and fresh sessions (#18784)
## Thinking Path

> - Paperclip keeps agent work attached to saved company records.
> - Agent Chat keeps one conversation for each person and agent.
> - Reload must preserve context without starting another turn.
> - The ordinary `/new` command must create fresh provider context and
preserve history.
> - Existing local chat journeys cover these behaviors, but the
deployment-targeted daily catalog does not.
> - This PR adds one reusable journey with independent saved-state and
rendered-reply checks.

## Linked Issues or Issue Description

Refs: #2242, #18655.

**What existing behavior does this improve?**

Product E2E coverage for Agent Chat persistence and session reset on an
attached deployment.

**Current behavior**

The deployment-targeted catalog has no bounded check for recall after
reopening and a fresh session in the same saved conversation.

**Proposed behavior**

Remember a random phrase, reopen and reload the chat, then ask for the
phrase without supplying it. Send `/new` through the ordinary composer.
Require a new native session and a fresh-context answer while the
original chat and history remain saved.

## What Changed

- Add the `chat-continuity` flow and independent evidence grader.
- Reuse the existing chat sender and run-bound native reply reader.
- Require exact board-user message attribution, three successful native
provider turns and one synthetic reset row. Count all four persisted run
IDs. A reset with provider input or a native session cannot pass as
synthetic work.
- Use the original bound issue-run summary to prove reset completion
when the detail result is null or compact. Preserve the raw detail and
require its reset context, company, agent, issue and wake comment to
agree. Native provider turns require committed native state and
independent output evidence; an optional duplicate result field is not
required.
- Require an empty agent-filtered run history before the first send.
Reused agents are unsupported for this case. Validate every agent-scoped
activity row and reject mixed or truncated responses; do not filter
global history into a pass.
- Require the fresh native detail to contain an explicit null
`sessionIdBefore`. Missing or empty admission evidence cannot prove a
fresh provider session. Keep optional result blobs optional.
- Check provider, model, environment, native session, finalization and
reset generation. Reject stale replies, duplicate sends, extra runs,
missing history and unexpected work.
- Permit authenticated chat screenshots through the existing same-origin
and mounted-Layout guard. Authentication and unrelated settings pages
remain excluded.
- Register the case, exact checks, source hash inputs, discovery and
documentation. Feature-map files are unchanged.

## Verification

- Normal merge commit `ddc37bb05b87cbc1fa449e4b2fc5a4677f0fe3b6` has
exactly two real parents: the staging-qualified chat source
`4c6cc9d67d2ae577e3a8ae7856bd6ae22a8489c1` and lifecycle squash
`8771606fd87f69ed4057779e3dce3a9bca8088e6` from #18782. Thirteen
dedicated case, support and reused runtime/oracle files are
byte-for-byte preserved. The eight shared registry placements match the
finite integration forecast. The nine required checks and four-run limit
are unchanged.
- The integrated source passes 99 focused tests across three files and
925 related Product E2E/catalog/native-reply tests across 42 files.
Credential-free discovery lists 22 unique journeys, including chat and
lifecycle exactly once. The PR diff against the actual lifecycle squash
passes its whitespace check and remains the original 17 chat paths.
- The current canonical `node scripts/typecheck-test-projects.mjs
--low-memory tests/runner-e2e` passes, including Runner TypeScript and
server declaration prerequisites. The DB migration checks and build pass
against the actual squash's execution-admission schema and migration
0428. No Rust build was run locally. Canonical generated outputs are
bound to current tracked inputs and were rechecked after tests.
- The first direct typecheck found a stale generated Runner export,
`taskDocumentKeySchema`; the current canonical prerequisites resolved
it. A frozen offline install lacked a cached locked tarball, and an
initial child command reached the global pnpm version switch and failed
signature retrieval. Fetching only locked packages and pinning child
pnpm to 9.15.4 corrected the setup. The original diagnostics remain
retained; no tracked source was changed to bypass them.
- The unchanged chat implementation passed a genuine staging campaign,
workflow 41725181387, at source
`4c6cc9d67d2ae577e3a8ae7856bd6ae22a8489c1`: all nine checks, all five
phases, three provider turns plus one synthetic reset, all four usage
IDs, and closed cleanup. This evidence remains bound to that original
source. The new integration head requires its own current gates and
coordinator qualification decision; this PR does not enroll anything in
the daily plan.
- The earlier staging workflow 41701828968 remains failed. Its nine
behavior checks passed, but private collection rejected the valid
reset's capture of the old session and cleanup failed. The private
adapter fix and evidence-first recovery were handled separately. The
original failed grade and archive are preserved. Reset prior-session
capture is distinct from the third native turn's explicit-null fresh
admission.
- Three missing/undefined/empty fresh-admission controls reproduced
false passes on `247827a066af01fa583ff030df79b61cb2c87730`, then passed
after the strict null fix. Four earlier controls reproduced
compact-detail, mixed-activity, missing fresh-agent receipt and
unscoped-activity problems. Their original failures remain retained.
Positive and wrong outcomes still enforce bound reset summaries,
committed native state, configured identity and independent rendered
output. Board provisioning uses the acting-agent filter; it does not
grandfather earlier work by the test agent.
- The first broad support attempt passed 861 tests and failed one
report-generator check. A separate reproduction showed `listen EPERM`
for the tsx local IPC socket before report output; the unchanged support
suite passed with normal local IPC permission. An early flow fixture
also returned an object where the events API returns an array; the real
event reader rejected it, and only that fixture was corrected. Both
original failures remain recorded.
- Original commit `d6226b30acaf5a2dad28030f5b295ac1dd119565` failed full
CI
[41653171240](https://github.com/paperclipai/paperclip/actions/runs/41653171240):
a CLI child-server runtime-file wait and two 15-second
attention-retention timeouts, followed by database setup/cleanup errors.
Their exact causes remain unresolved; the failed jobs and logs were
retained and not retried. A later pass does not explain those timeouts.
- Strict-admission source `055a7fa99e7cb8aad12b85fccf23880f0dc98a3f`
failed full CI
[41669527762](https://github.com/paperclipai/paperclip/actions/runs/41669527762):
private-invitation and pending-shell access fixtures. The retained
assertions match the account-identity fixture repair in #18799, now
included through master, together with #18798 and #18650. No old-head
job was retried. Subsequent source
`4c6cc9d67d2ae577e3a8ae7856bd6ae22a8489c1` passed full CI 41680411215
and current-source review at 5/5.
- Current full CI [41743580793, attempt
2](https://github.com/paperclipai/paperclip/actions/runs/41743580793/attempts/2)
completed successfully on unchanged
`ddc37bb05b87cbc1fa449e4b2fc5a4677f0fe3b6` at 2026-11-04 00:34:52 UTC:
48 successful jobs and one intentional selector skip. Attempt 1 remains
failed: three run-bound runners were independently confirmed to have
lost Spot capacity, and the two aggregate failures reflected those
interrupted lanes. The build and boot health smoke had passed before
interruption. Exactly one failed-job recheck was used; no source change
or further retry was made. Fresh exact-head Greptile review is 5/5 with
no new actionable issues and both review threads resolved. The separate
Ponytail assessment says Ship with no must-fix or should-fix item; its
optional direct board-row grader control remains documented.
Whole-workspace recursive typecheck, full tests and full build were
covered by hosted verification and were not repeated locally; the
bounded local checks above passed. These gates do not claim that a later
master merge tree has been live-qualified.

## Risks

The attached target must supply a fresh disposable native agent and
durable pre-send ownership for its chat. Its private adapter must
collect all four run IDs, distinguish the synthetic reset from the three
provider turns, and stop and clean up owned resources after evidence is
saved.

This case verifies browser reload, reopen and explicit session reset. It
does not verify controller restart. Unknown setup or provider cost
remains unknown. No shared QA source agent, model setting, production
behavior or budget policy is changed by this public PR.

## Model Used

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

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
canary/v2026.1104.0-canary.1
2026-11-03 19:20:34 -06:00
DottaandPaperclip fbb7055b90 fix(runner): keep activity FIFOs out of Codex writable roots (#18886)
## Thinking Path

> - Paperclip manages agents and their work.
> - Runner owns each provider process and its activity channel.
> - Codex Plan mode uses a restricted filesystem policy.
> - Runner adds its activity FIFO as a writable filesystem root.
> - Codex adds protected metadata paths beneath each writable root.
> - A FIFO cannot contain those paths, so the Linux helper fails before
useful work starts.
> - This pull request grants access through the private activity
directory and keeps discovery files read-only.

## Linked Issues or Issue Description

Refs #18050. The activity channel came from that change. No existing
FIFO-mount repair was found. #18853 repairs interrupted preparation
recovery; it does not change this provider filesystem policy.

**What happened?**

A native Codex Plan session can fail while loading AGENTS.md
instructions. The filesystem helper tries to inspect
`activity.fifo/.aws` and fails with `Not a directory`. This happens
before the provider can produce the requested plan.

**Expected behavior**

The sandbox must load permitted instructions and allow activity-channel
writes. Plan mode must still reject workspace writes and private state
access.

**Steps to reproduce**

Use the ignored `read_only_codex_opens_with_runtime_activity` regression
in the Runner Rust suite with the source-pinned Linux Codex executable
and its matching bubblewrap resource. The fixture calls the actual
activity channel producer and policy projection. On the original
producer it exits 101 with the FIFO metadata-mount panic. With this
change it exits 0.

**Paperclip version or commit**

Original producer reproduced at
208bda1a0e. The local native artifact is
official Codex 0.160.0 for Linux arm64, which matches the source pin.
The failing hosted executable version was not independently available
from its normal run records.

**Deployment mode**

Native Runner with Linux Codex. Verification used an isolated local
Linux container with no network, credentials, or model call.

## What Changed

- Grant writes to the Runner-created private activity-channel directory
instead of using its FIFO as a filesystem root.
- Keep the lock, descriptor, and helper read-only. Keep the existing
network, workspace, environment, and active-run authority policies.
- Add actual channel isolation coverage and a pinned executable
regression. The latter also checks FIFO bytes, instruction reads,
workspace write denial, private-state and synthetic credential-home
denial, descriptor and lock write denial, lock removal denial, and
ambient-variable filtering.
- Document the native sandbox check and the separate fixture-export
path. Export alone does not verify the sandbox.

## Verification

- Actual producer plus official Linux Codex 0.160.0 and matching
bubblewrap: original producer exits 101 with the FIFO `.aws` panic;
changed producer exits 0 with all permission checks and exact FIFO
output verified.
- `cargo test --manifest-path
packages/paperclip-runner/runner/Cargo.toml runtime_notification --lib
--offline`: 8 passed, 2 manual sandbox fixtures ignored.
- `cargo test --manifest-path
packages/paperclip-runner/runner/Cargo.toml codex_provider::tests --lib
--offline`: 30 passed.
- `cargo build --manifest-path
packages/paperclip-runner/runner/Cargo.toml --bin paperclip-runnerd
--offline`: passed.
- Cargo format and diff checks passed.
- A broader optional local Rust library sweep did not pass in the
restricted macOS host. Three filesystem-monitor tests failed and another
did not finish; the owned sweep was bounded and stopped. A
socket-identity fixture also failed with EPERM. A representative monitor
failure and the socket failure were repeated with the unchanged original
producer. Their failures are retained and are not claimed fixed by this
PR.
- The existing ignored
`publisher_crosses_real_codex_tool_boundary_without_runner_state_access`
fixture was not executed successfully on the final head. Its stale
channel-directory denial was corrected after Greptile review. The
separate actual-producer Linux sandbox fixture verifies exact FIFO bytes
and permission denials; it does not call `record-activity`.
- Full local pnpm typecheck/test/build was not run for this Rust-only
change. Required technical CI passes on
fd4da0bf5e. Greptile is 5/5. The automatic
Paperclip Review remains queued; merge is held for that review. No
hosted task recovery or provider turn is claimed.

## Risks

The writable grant now covers the process-private channel directory. The
descriptor remains outside that grant. The lock has an explicit
read-only mount inside it. Native sandbox checks verify the lock cannot
be written or removed. Existing tests retain authority rotation,
stale-context rejection, malformed input bounds, and fixed-path cleanup
after endpoint replacement.

The retained startup error also mentions a loopback permission error.
This change fixes the independently reproduced FIFO-root defect. It does
not prove that every hosted namespace capability is healthy.
Preconfigured-home and custom policies retain their existing explicit
deny rules.

## Model Used

OpenAI GPT-6.1 Sol (`gpt-6.1-sol`) in Codex, with source editing,
terminal execution, and tool use. The host did not report its
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-11-03 18:58:31 -06:00
Devin FoleyandClaude Opus 5.5 ad5cd785b1 fix(wake-queue): keep promoted Retry runs scoped to their issue (#18876)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The wake queue starts the next run for a task when the task's
current run releases its execution lock.
> - A board Retry saves a receipt with only `{ issueId, taskRetry }`.
This receipt has no saved wake context.
> - When the release drain promotes that receipt, the promoted context
gets `taskKey` from the payload, but it does not get `issueId` or
`taskId`.
> - The new run takes the task's execution lock, but the database saves
it as a company-scoped run with no issue.
> - Low-trust workspace preparation then has no issue reference. The run
fails before provider work with "Low-trust execution issue is outside
the active trust boundary."
> - This pull request restores the issue scope of a payload-only receipt
during promotion.
> - The benefit is that a Retry on a low-trust task runs in its task
boundary and does not fail during setup.

## Linked Issues or Issue Description

**What happened?**
A user clicked Retry on a low-trust task while a run was busy. When the
busy run ended, the release drain promoted the saved Retry receipt. The
promoted run had `issue_id = NULL`, `scope_kind = company`, and a
context snapshot with only `taskKey`, `wakeReason: task_retry`,
`wakeSource`, and `wakeTriggerDetail`. Workspace preparation failed with
`low_trust_boundary_mismatch` (HTTP 422) in the setup phase.

**Expected behavior**
A promoted run for a task must keep that task's issue scope. This is
true also when the deferred receipt has no saved wake context. Workspace
preparation must then evaluate the task's own trust boundary.

**Steps to reproduce**
1. Use a task with a low-trust execution policy whose assignee is an
agent.
2. While a run on that task is busy, click Retry on the task.
3. Let the busy run end. The release drain promotes the deferred Retry
receipt.
4. The promoted run is company-scoped with no `issueId`. It fails in
setup with `low_trust_boundary_mismatch`.

**Paperclip version or commit**
`master` at 51d1d4ce07.

**Agent adapter(s) involved**
Not adapter-specific (core bug).

## What Changed

- `enrichPromotedWakeContext`
(`server/src/modules/wake-queue/domain/context.ts`) now restores
`issueId` and `taskId` from the payload when the deferred context does
not have them. This is the same rule that the original wake enrichment
uses. The function does not overwrite a value that the queued context
already has.
- I added a domain test for the payload-only receipt and for the
no-overwrite rule.
- I added a Postgres adapter test
(`adapters/promoted-retry-scope.test.ts`). It runs the real release
drain with a Retry receipt that has no saved wake context. It checks
that the promoted run is issue-scoped and holds the task's execution
lock. The database trigger `set_heartbeat_run_scope_kind` derives
`issue_id` and `scope_kind` from the restored context `issueId`.

## Verification

- Red before the fix: both new tests fail on unchanged `master`. The
adapter test shows the incident shape: `issueId: null`, `scopeKind:
"company"`, and `taskKey` equal to the issue UUID.
- Green after the fix:
- `pnpm vitest run server/src/modules/wake-queue/` passes (10 files, 289
tests).
- The related suites pass (11 files, 468 tests): `heartbeat/queue`,
`heartbeat/run-control`, `heartbeat/run-lifecycle`,
`deferred-execution-receipt-work`, `execution-lock-orphan-cleanup`,
`heartbeat-comment-wake-batching`, `issue-stale-execution-lock-routes`,
`chat-failed-run-retry-boundary`, `task-retry`,
`execution-control-work`, and `workspace-recovery`.
- Server typecheck (`node server/scripts/typecheck-layers.mjs`) passes.
- I did not run a live low-trust Retry on a deployed stack. The adapter
test uses the real release drain, the real database trigger, and the
persisted receipt shape that `requestTaskRetry` writes.

## Risks

- Low risk. The change only adds `issueId` and `taskId` when they are
missing and the payload has them. Deferred wakes that already have a
queued context do not change.
- The release drain still promotes a board Retry receipt directly. It
does not go through `resumeTaskRetry`. This PR does not change that
path. A promoted Retry therefore does not get `forceFreshSession` or
`taskRetryRequestId`. This behavior existed before this PR. It is a
possible follow-up, not part of this fix.

## Model Used

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), used through Claude
Code with tool use (code search, shell, and test execution).

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 16:57:44 -08:00
DottaandPaperclip 985b175e83 test(e2e): verify saved agent instructions on the current QA harness (#18891)
## Thinking Path

> - Paperclip lets users configure agents and verify their work.
> - Daily QA must exercise saved instructions on the QA staging
instance.
> - A fresh owned agent can test an instruction change without changing
a shared agent.
> - The test must prove that saved instructions caused the next run's
output.
> - It must also prove that the original instructions were restored.
> - This pull request brings the qualified settings journey onto the
current shared registry layout.
> - The benefit is a reusable daily QA candidate with independent
evidence and fresh current-source checks.

## Linked Issues or Issue Description

Refs #18788. This is the replacement review PR for the same settings
journey after integration with the merged lifecycle registry layout in
#18782.

**What happened?**

The settings journey passed its original staging qualification at
source898ae8ab4e49d2408688088ce82e3316b91cc09c. The earlier PR's review
workspace remains retained for file preservation. That workspace and its
recovery evidence remain intact.

**Expected behavior**

Review the integrated settings source through this PR's own normal CI
and fresh reviews. The journey saves instructions through ordinary UI
controls, verifies their effect on one current native run, and restores
the original bytes.

**Steps to reproduce**

Use the documented dedicated agent-settings-persistence campaign. The
provider must supply a fresh owned agent and the agent-settings-write
capability. Save the instruction edit, reload it, complete the ordinary
assigned task, verify its independently saved result and current-run
attribution, then restore and verify the original instruction entry.

## What Changed

- Add the settings browser journey, its independent evidence grader,
fixtures, retained evidence contract and focused controls from #18788.
- Require one fresh dedicated agent. Select its exact configured
instruction entry through the file tree. Confirm both board saves
through persisted source and audit evidence.
- Require one committed native run on the configured provider, model and
environment. Keep the synthetic instruction reference out of the task
request.
- Restore the original instruction bytes through the same ordinary UI
and verify the saved result after reload.
- Normally merge actual lifecycle
squash8771606fd87f69ed4057779e3dce3a9bca8088e6 once. Place only settings
registrations at the documented distinct anchors. Preserve all eight
dedicated source/oracle/fixture/docs blobs from qualified898 exactly.

## Verification

- Current integrated head: 330 focused checks pass across eight
settings, catalog, contract, observation, result/report and operations
test files.
- Current E2E types pass: `node scripts/typecheck-test-projects.mjs
--low-memory --reuse-declarations tests/runner-e2e`.
- Declaration reuse was bound to all11091 matching
package/script/server/UI/root inputs, installed lock and TypeScript,
plus all source and target generated-output hashes. No source or
module-resolution links were copied. All copied output hashes remain
unchanged after local checks.
- Maintained product-journey discovery passes and includes both
lifecycle and settings. The integrated catalog has22 entries, including
the separate fresh-cloud onboarding case.
- Static preservation checks pass for seven shared registry ASTs after
only declared order changes. The settings dispatch body, required seven
checks, one-run bound and all dedicated implementation/oracle blobs
remain intact.
- Historical staging evidence: [QA
workflow41689186453](https://github.com/paperclipai/paperclip-cloud/actions/runs/41689186453)
passed all seven settings checks and all five phases at
source898ae8ab4e49d2408688088ce82e3316b91cc09c. This is the original
qualification receipt. It is not a new run of this integrated head.
- This PR must earn its own current-head full CI, fresh5/5 review,
resolved threads and terminal passing normal check contexts. Those
checks are pending. No prior review or check status was copied.
- Full workspace checks and a new live QA/model run were not run
locally. The coordinator owns current-source acceptance and daily-plan
admission.

## Risks

- The journey has one paid provider run and needs a fresh dedicated
agent. It rejects a campaign that shares this agent with another paid
journey.
- Browser saves and restoration must persist before navigation. Wrong,
missing, foreign or incomplete evidence fails the independent grader.
Failed or active work is preserved for normal owned cleanup.
- Registration and hash-input order changed during integration, so the
definition/source identity changes. The historical QA receipt remains
bound to its original source.
- The retained earlier review workspace is still held for file
preservation. This PR does not recover, reset, replace or destroy it,
and submits no turn to it.
- No product runtime, permission, model, shared agent, configuration,
feature map, QA pin or TestOps-plan changes are included.

## Model Used

- OpenAI GPT-6 (Codex), with reasoning, tool use and code execution. The
host does not expose the exact deployment identifier or context-window
size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub issue/PR references; historical QA workflow is
linked as qualification evidence)
- [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-11-03 18:55:49 -06:00
DottaandPaperclip 7b1c53f8f1 fix(recovery): continue verified preparation after lease interruption (#18892)
Use recorded system interruption and the existing execution, controller, stopped-lease, and saved-file proof instead of a shutdown-only error label. Preserve bounded retries, user Stops, task authority, and exactly one continuation.

Keep the chat recovery fixture faithful to the preparation stage and terminal time written by the real lifecycle. Retain missing-field safety controls.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-11-03 18:54:16 -06:00
Dotta db9be4c5a6 fix(workspaces): preserve safe one-shot failure messages (#18888)
## Thinking Path

> - Paperclip manages agent work and keeps saved work safe.
> - Remote workspaces use provider commands to build and copy a verified
archive.
> - Some providers return a one-shot command's combined output in
stdout.
> - The workspace transport checked only stderr for its two safe failure
messages.
> - This hid those messages behind a generic error on that provider
path.
> - This pull request checks both streams for the same exact messages.
> - The benefit is a useful existing error message without exposing
other output.

## Linked Issues or Issue Description

**What happened?**

Daytona one-shot execution returns combined output in stdout and leaves
stderr empty. The remote workspace transport checked only stderr after a
failed command. It therefore lost the existing full-disk and
unfinished-Git-operation messages.

**Expected behavior**

Preserve those same two exact safe messages from either returned stream.
Keep all other output private. Keep the existing stderr preference when
both streams contain different safe messages.

**Steps to reproduce**

Run the real workspace capture helper with a conflicted Git merge or an
injected ENOSPC write. Apply the provider's one-shot output contract to
the real child result. Before this fix, all three new cases return only
remote_workspace_operation_failed.

Related provider contract: #10941. The duplicate search found no PR for
this exact transport classification gap.

## What Changed

- Check stderr and stdout for only the two existing exact safe failure
messages.
- Add real helper regressions for one-shot Git and full-disk failures.
Keep the conflicted files and original saved result intact.
- Add privacy, unknown-output, split-message, stderr-preference and
healthy verified capture/restore controls.

## Verification

- RED: three real helper cases fail on the unchanged transport. They
cover a conflicted Git merge and ENOSPC during helper installation and
archive creation.
- GREEN: 73 tests pass in remote-workspace-transport.test.ts and
remote-workspace-snapshot-archive.test.ts.
- Server layered typecheck passes: `cd server && node
scripts/typecheck-layers.mjs` with Node24.22.2. All seven source/test
layers pass.
- Full CI is requested with the full-ci label. Current-head CI and
review are pending.
- The local install used the frozen lock with scripts disabled. The
maintained root SDK-link helper restored its skipped local postinstall
links. The original prerequisite failure is retained. Generated package
outputs were copied only after complete input, installed-toolchain and
output-hash equality checks; server declarations were checked in this
worktree.
- The full workspace typecheck, test suite and build were not run
locally. This two-file change uses focused real-helper tests and server
typechecking; full CI supplies the broader checks.

## Risks

- A failed command can now show one of the existing safe messages when
the provider puts it in stdout. Unknown, prefixed, suffixed and private
output still produces the generic error.
- This change does not repair an archive failure or prove that a
retained workspace was saved. It changes no capture, ownership, archive
validation, stop, save or retry guard. No live provider command or
recovery action was run for this PR.
- No API, permission, schema, configuration, map or documentation
contract changes are required.

## Model Used

- OpenAI GPT-6 (Codex), with reasoning, tool use and code execution. The
host does not expose the exact deployment identifier or context-window
size.

## Checklist

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

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native Runner sessions keep a provider conversation across task
turns.
> - When a later run gets a new task lease, recovery must prove that the
prior run settled.
> - Recovery required `run.terminal` to be the last committed event.
> - A provider can send a late notification for a settled turn. The
Runner then records a `provider.notice.recorded` warning after
`run.terminal`.
> - This false denial stranded a clean session. The notice stays in the
journal, so each later run on that session meets the same denial.
> - This pull request lets recovery accept a recoverable notice after
the terminal event. All other new events still block recovery.
> - The benefit is that a late warning no longer ends session
continuity. A denial now also names the check that failed.

## Linked Issues or Issue Description

No issue exists. I searched open and closed pull requests and issues for
the error code, the event name and the function name. I found no
duplicate.

**What happened?**

A run failed in about nine seconds with `runner_state_identity_mismatch:
plugin_checkpoint_recovery_unproven`. No provider work started. The
prior run had finished and was committed as succeeded. Its journal had
one `provider.notice.recorded` warning, "ignored a notification for a
settled Codex turn", 1 ms after `run.terminal`. A second session had the
same warning about 61 seconds after a normally completed turn.

**Expected behavior**

Recovery accepts the session and continues the provider conversation. A
recoverable notice does not start work, so it must not block recovery.

**Steps to reproduce**

1. Start a warm native session in a plugin sandbox environment.
2. Finish a run so that a provider notice follows `run.terminal` in the
journal.
3. Wait until the idle timeout stops the Runner task and releases the
lease.
4. Start the next run on that session. It gets a new task lease.
5. The run fails with the error above. The notice stays in the journal,
so a retry on the same session meets the same denial.

The `local-receipt-late-notice` test mode in
`native-session-executor.test.ts` reproduces this failure on the unfixed
code.

**Paperclip version or commit**

Observed on `d8a9b3eb0c`. The same check is on `master` before this
change.

**Deployment mode**

Cloud deployment, with a native Runner in a plugin sandbox environment.

## What Changed

- `isSettledRemoteWarmControlState` accepts a `provider.notice.recorded`
event after the terminal event. The notice must be recoverable and have
`info` or `warning` severity. It must come from the same runner, run,
session and lease.
- Error notices, unrecoverable notices, notices from another source and
new work still block recovery.
- Checkpoint recovery and local quiescent recovery now use this
function. They no longer have separate checks that require
`run.terminal` to be the last event.
- Each checkpoint recovery denial adds a specific reason after the
existing code, for example `plugin_checkpoint_recovery_unproven:
journal_not_settled`. The `runner_state_identity_mismatch: ` prefix does
not change.
- I reworded the existing comment on `isSettledRemoteWarmControlState`
so that it stays correct. I added no new code comments.

## Verification

- **Failure before the fix:** I added the tests first and ran them on
the unfixed code. 38 tests failed. In the 4 `local-receipt-late-notice`
cases, checkpoint recovery rejected with
`runner_state_identity_mismatch`. In the `late provider notice` case,
quiescent recovery rejected with `quarantine_owner_unverified`. The new
unit test for notices also failed. The other 32 failures were assertions
for the new denial reasons.
- **Targeted tests after the fix:** 134 tests passed, with 987 skipped
by the filter.
- `cd server && pnpm exec vitest run
src/services/native-runtime/remote-warm-session-recovery.test.ts
src/services/native-runtime/native-session-executor.test.ts -t "settled
remote warm control evidence|recovers a blocked|automatically recovers
only a proven settled local session"`
- **Type error found in review:** Vitest does not check types, and my
first version passed these tests. Review of the diff showed an
incompatible `identity` parameter type. I changed that type to
`Record<string, unknown>`.
- **After the type change:** `remote-warm-session-recovery.test.ts`
passed (23 tests) and `pnpm run typecheck` in `server` passed. I did not
run the 134 tests again, because this change affected only types.
- **Real data, read-only:** I ran the new function on 285 session
journals from a test deployment. The result changed for 2 journals.
These are the 2 stranded sessions, and each has only provider notices
after `run.terminal`.
- **Not run locally:** I did not run the full test suite. I stopped a
run of the wider native-runtime test files before it reported a result.
CI provides the full regression coverage.
- **Not verified:** no live run has reached `read_state` on a stranded
session after this fix. The tests use a fixture for that call.

## Risks

- **Stricter identity check:** checkpoint and quiescent recovery now
also require the journal's saved identity to match. Recovery reads this
identity from the same file, so only damaged state should fail this
check.
- **Wider goal snapshot rule:** these two paths now accept idle goal
snapshots after the terminal event. Warm handoff already used this rule.
- **Wider notice rule:** warm handoff and the settled projection check
also accept recoverable notices now. A notice does not start work, and
the checks for pending commands and unanswered tool input stay in place.
- **Longer error text:** code that compares the full
`plugin_checkpoint_recovery_unproven` message for equality would stop
matching. I found no other reference to this code in the repository.
- **Stranded sessions:** these sessions need no data repair. They can
still fail with `checkpoint_not_settled` if the sandbox checkpoint is
not clean.
- **Migrations:** this change has no migration.

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

## Model Used

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), through Claude Code,
with tool use and code execution. A human directed the diagnosis and
approved the change.

## Checklist

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

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

Co-authored-by: Claude <noreply@anthropic.com>
2026-11-03 16:37:39 -08:00
Dotta 82dd3bb702 Preserve unstarted queued turns during offline enrollment (#18887)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Existing-volume enrollment moves server files to durable object
storage.
> - Enrollment stops application deployments before it changes file
authority.
> - A task drain keeps unstarted turns queued in the database.
> - The migration rejected every queued row, so a busy instance could
never finish enrollment without discarding requests.
> - This change preserves unstarted queue intent and continues to reject
running or previously started executions.
> - Users keep their pending turns and saved files through the
conversion.

## Linked Issues or Issue Description

**What happened?**

A preserving enrollment found queued turns after task admission stopped.
The workspace, instruction-file, and historical-log guards treated these
rows as live writers. The drain also prevented the rows from starting,
so waiting could not satisfy the migration guard.

**Expected behavior**

After every old deployment has stopped, maintenance can move files while
unstarted queued turns remain unchanged. The replacement claims those
turns through normal admission. Actual executions and retained execution
ownership continue to block migration.

**Steps to reproduce**

1. Queue a turn behind another task.
2. Start a task drain and let the current task finish.
3. Stop the application deployments and run offline enrollment.
4. Observe `active_execution_requires_drain` for the unstarted queued
turn.

**Paperclip version or commit**

Reproduced on the migration contract through #18864. This branch starts
at `ab2d2098a2764c511978d1c3e7a3fc5dfcef07f4`.

Related: #18826, #18835, #18841, #18864. Searches for queued enrollment
and queued migration found no duplicate fix.

## What Changed

- Share one offline execution predicate across workspace, agent-file,
and historical-log migration.
- Reuse the existing pre-dispatch evidence predicate; queued turns with
execution markers, saved checkpoints, source events, coordinators, or
environment leases remain blockers.
- Keep task bindings and normal live-migration restrictions unchanged.
- Exercise normal replacement admission after deleting the old
filesystem: the same queued run reads restored workspace bytes and
instructions, completes its original wake, and cannot execute twice.
- Document the offline boundary and queued-turn behavior.

## Verification

- **111 tests passed** across the final 110-case migration suite and
assembled queued-turn heartbeat proof, with real PostgreSQL and local
S3. This includes workspace/log migration, queued ownership blockers,
ordinary agent-file controls, and the actual bundled maintenance
process.
- The maintenance process preserves the queued row while archiving
historical output. The new assembled heartbeat test creates a normal
wake under drain, migrates without changing either row, deletes the
original filesystem, and resumes through normal admission. It reads
saved workspace bytes and the restored instruction bundle, persists one
successful native result, completes the same wake, and verifies a second
sweep does not duplicate execution. Only sandbox transport/provider
execution are scripted; no live model or Daytona calls.
- An earlier optional run-evidence worker integration invocation was
skipped without its dedicated environment; it is outside the changed
offline path.
- Server typecheck, server build, and runtime bundle passed.
- The first maintenance test exposed the separate agent-file guard; that
guard now uses the same offline predicate and the test passes.
- Initial local typecheck attempts lacked CLI dependency links in this
isolated checkout. Restoring dependencies fixed setup without source
changes.
- Full repository checks run in CI. The first head lost spot runners in
Build and serialized tests; one unchanged idle-deadline fixture crossed
UTC midnight and failed its timestamp assertion. Final-head CI passed on
run 41744678994 after retrying a spot-runner shutdown. Greptile reviewed
the exact final head at 5/5; all inline findings are resolved. The
separate Paperclip Review app remains queued. Hosted enrollment remains
pending release qualification and compatibility verification for the
existing provider configuration.

## Risks

The offline predicate relies on Cloud stopping every old application
deployment. It does not make online migration safe. Running executions
always block. Requeued executions with retained ownership also block. No
queued request, task binding, or recovery state is removed or rewritten.
No database schema changes.

## Model Used

OpenAI Codex, GPT-6 based, with reasoning and tool-assisted code
execution. The exact deployed 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
2026-11-03 18:33:41 -06:00
Devin FoleyandPaperclip 4e29f00139 fix(native): keep settled ACPX sessions across Runner upgrades and run scheduled resumes (#18879)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native Runner keeps one ACPX provider conversation across the
per-turn runs of a task. Each run cold-attaches the session that the
previous run settled.
> - The saved ACPX state binds the digest of the launch profile that
wrote it. Every deploy ships a new provider pack, so that digest
changes.
> - After a deploy, the next run of such a task fails `run.attach` with
`ACPX durable launch profile digest does not match runner startup`.
Every same-run retry fails the same way.
> - On one instance this happened seven times. Six tasks used their
retry budget and ended blocked, or in progress with no runner. In the
seventh, the scheduled same-run resume never ran at all: the task stayed
`in_progress` with nothing running, and its monitor did not fire.
> - This pull request re-binds a settled session to the upgraded launch
profile, types the remaining mismatches so the server stops retrying
them, and makes a scheduled remote resume survive the stale-lock sweep.
> - The benefit is that a task continues its conversation after a
deploy. When it cannot, it stops with a clear next action instead of
stalling.

## Linked Issues or Issue Description

Refs #18846, #18847, #18854 and #18860 (nearby native recovery changes).
Refs #15795, an open change to the same stale-lock sweep function. It
changes run teardown, not lock cleanup, so a second merge needs at most
a small rebase.

**What happened?**

A per-turn native task used provider `acpx` with Claude, driver
`acpx_runtime`, on a remote sandbox.

1. Run A succeeded and settled its provider session. The next run with
the same build cold-attached it.
2. The instance deployed a new build.
3. A board approval woke run B in a fresh sandbox. Run B failed after
about five minutes: `native_session_recovery_failed: Error: PRP command
run.attach failed: {...
"result":{"code":"command_execution_failed","message":"ACPX durable
launch profile digest does not match runner startup"}}`.
4. The coordinator recorded `retryable_failure`, attempt 1,
`native_session_interrupted`, recovery mode `exact_checkpoint_resume`,
recovery state `resuming_session`, and a retry 30 seconds later.
5. Two seconds after the failure, the stale-lock sweep cleared the
task's execution and checkout locks, because run B was `failed`.
6. Forty minutes later the attempt was still 1. The scheduled monitor
had not fired. The task stayed `in_progress` with nothing running.

**Expected behavior**

A session that the previous run settled continues after a deploy. If it
cannot continue, the run stops at once with a clear next action. A
scheduled resume either runs or fails visibly.

**Steps to reproduce**

1. Save a suspended ACPX session under one launch profile.
2. Change the sidecar bytes and the provider-pack authority at the same
paths.
3. Cold-attach the next run in a new runner. Restore fails with the
digest mismatch.
4. For the stall: leave a remote native run `failed` with a due
`retryable_failure` coordinator, run the stale-lock sweep, then run
restart recovery for that run. It returns `blocked:
issue_execution_lock_changed` and writes nothing.

The new Rust and PostgreSQL tests run these steps.

**Paperclip version or commit**

`master` at `b0c4e345267`.

**Deployment mode**

Native Runner, per-turn lifecycle, remote sandbox. The re-bind applies
to every ACPX agent with an ordinary startup.

## What Changed

### Root causes

- **Upgrade failure.** `AcpxLaunchProfile::canonical_digest` hashes the
provider-pack authority, the sidecar command and arguments, each
artifact path and SHA-256, and any candidate admission. For a remote
sandbox the authority is the provider-pack manifest digest, which covers
the Runner source revision and dist digest. So every deploy changes it.
`AcpxDurableState` stores only the digest, and `validate()` rejects any
difference. Only a Kimi candidate run binding had a carve-out.
- **Silent stall.** The failure write keeps the task locks for
`retryable_failure` and clears them only for `terminal_failure`. The
stale-lock sweep treats any `failed` run as cleanable, so it cleared
them. A remote retry goes through `claimNativeRestartRecoveries`. That
claim requires `issues.execution_run_id` to name the run, so it returned
`blocked: issue_execution_lock_changed` with no coordinator write and no
deadline. The live recovery queue then dropped the entry. The local
claim (`claimNativeSessionResumptions`) already reclaims a free lock, so
only remote runs stalled.
- **Monitor.** `noActiveNativeMonitorRun` holds a task's monitor while
any native run for it has a coordinator that is not `committed` or
`terminal_failure`. Run B's `retryable_failure` held it. That is by
design; it ends when the resume runs.

### Runner (Rust)

- `rebinds_settled_session` in `acpx_provider_backend.rs`. Restore
re-binds a saved session to the current launch profile only when all of
these are true:
- The lifecycle is `suspended` and an identity exists. There is no
active turn, pending runtime request, undelivered provider event, or
unconfirmed provider exit.
  - The startup has no candidate admission.
- The saved descriptor's sidecar command and arguments equal this
startup's.
- Any other digest mismatch keeps failing with the same message, plus
`classification: "provider_state_incompatible"` in the durable command
result. `DurableRunnerError` carries an optional closed classification
for this.
- A saved descriptor that names an agent release this build does not
qualify (ACPX version, agent server package or version, or command
digest) also gets the class. `validate_session()` and its message are
unchanged; restore only adds the class.

### Runner (TypeScript)

- `runnerRecoveryFailureClass` reads the classification from the
authenticated `runnerCommandResult`. The Codex app-server driver (which
also drives ACPX through runnerd) and `HarnessDriverBackend` pass it on
as `failureClass`.
- If replacement after a failed resume is not allowed,
`executeNativeSession` throws `NativeProviderStateIncompatibleError`
(message unchanged: `native_session_recovery_failed: ...`). If
replacement is allowed, the existing path opens a fresh session in the
same run, as before.

### Server

- `nativeSessionFailureSourceCode` maps that error class to
`native_provider_state_incompatible` with `instanceof`, not text. The
disposition is permanent: `terminal_failure` at once, the task blocks,
and the board owns the recovery action. Its next action says that a
Retry continues in a fresh provider session with the same workspace.
- `findNativeSessionResumeRun` also returns the barrier's `errorCode`.
`rebindNativeSessionCheckpoint` does not resume a source with
`native_provider_state_incompatible`. The existing fallback in
`buildNativeExecutionWithCheckpoint` then starts a fresh normalized
session with the full task bootstrap.
- `sweepStaleIssueLocks` keeps both locks while a referenced run's
coordinator owns a result-less `retryable_failure`, unless the task is
done or cancelled. Its update also requires that no referenced run has a
live status at the write, because a resume claim can return the run to
running after the status read.
- `claimNativeRestartRecoveries` reclaims a free execution lock for a
`retryable_failure` resume, like the local claim does, when the checkout
lock is free or held by this run and no other run for the task is
queued, running or scheduled. It records
`native.recovery.execution_reclaimed`. A lock held by another run still
blocks.
- Docs: `packages/paperclip-runner/docs/durable-recovery.md` and
`doc/execution-semantics.md`.

### Safety argument for the re-bind

What the binding protects, and what this change keeps:

- **Candidate admission** carries company, model, platform and expiry
authority. A candidate startup never re-binds. The Kimi run-binding
carve-out and all candidate rejections are unchanged.
- **The agent that loads the conversation** stays exact. `validate()`
runs on the saved descriptor before the digest check and requires this
build's qualified agent profile: ACPX version, agent server package and
version, and `commandDigest`. The saved identity must still match that
profile and the provider mode.
- **In-flight provider work** is never re-bound. A turn, runtime
request, undelivered event or unconfirmed exit keeps the strict binding,
so no old provider process or request continues under a new build. The
next launch only loads a settled conversation.
- **Launch shape.** A changed sidecar command path or arguments still
rejects. Only the content at the same paths may change.
- **Artifacts** are not trusted from the saved state. The current
profile comes from the trusted controller at startup, and session start
verifies every artifact's bytes and the restored provider identity, as
before.

What changes: a new provider-pack authority, or new sidecar or Node
bytes, at the same paths no longer invalidates a settled conversation.
The saved digest cannot show which part changed, so the rule does not
depend on it. I did not add state fields to tell authority changes from
artifact changes, because the rule would treat both the same.

One existing assertion changes.
`released_kimi_cold_restore_rejects_candidate_injection_and_changed_full_profile`
expected a settled ordinary Kimi session to reject a changed authority,
artifact digest, or saved digest. Those three cases moved to a new
positive test. Command, arguments, every candidate grant field, a
missing profile and run binding still reject.

### How a task in the reported state recovers after this deploys

The task has old-digest saved state, a `retryable_failure` coordinator
at attempt 1, and cleared locks.

1. Startup recovery, and the live recovery queue, run restart recovery
for the due run. It reclaims the free execution lock and claims the run.
The resume runs now.
2. Run B's failed `run.attach` is a durable outcome of B's exact run
authority. If the resume replays that outcome instead of executing it
again, it fails with the old, untyped text. The remaining attempts then
fail the same way, and the run ends `terminal_failure` with
`native_session_retry_exhausted`. The task blocks with a board recovery
action. This takes about a minute.
3. If the task blocks, press **Retry** once. Retry selects run B as the
source (it has process identity and controller evidence, so the #18860
skip does not apply) and rebinds B's checkpoint, which holds A's
provider session. The new run cold-attaches under the new Runner, and
restore re-binds the old digest because the session is suspended and
quiescent. The conversation continues. Run B's failed attach never wrote
the state file, so the state is still A's settled state.
4. Tasks that already exhausted their retries (blocked, or in progress
after `native_session_retry_exhausted`) need the same single Retry.
Their earlier attempts ran under the old Runner.

Wait for the task to show blocked before pressing Retry. A Retry pressed
while the resume is running joins that run.

## Verification

- Rust, failing first:
`settled_session_rebinds_to_the_upgraded_runner_launch_profile` failed
on `master` with the production message.
`unsettled_or_relocated_session_keeps_the_strict_launch_binding` failed
on the missing classification. Both pass now.
- `cargo test -p paperclip-runner-core --no-fail-fast`: all 41 test
binaries pass. The library has 471 passed and 1 ignored.
- `cargo fmt --check`, and `pnpm --filter @paperclipai/paperclip-runner
typecheck` (TypeScript, generated contracts, `cargo check`): pass.
- Server, failing first: `native-scheduled-resume-lock.test.ts`
(PostgreSQL). On `master` the sweep cleared the locks and restart
recovery returned `issue_execution_lock_changed`. Both negative controls
(another run's lock, a queued run) already passed. All four tests pass
now.
- `pnpm --filter @paperclipai/server typecheck`: pass.
- Runner vitest (`src/native-session-runtime.test.ts`, `src/backends`,
`src/drivers/codex`, `src/drivers/provider-failure.test.ts`,
`src/live/runnerd-codex-transport*.test.ts`,
`src/control-plane/acpx-controller-recovery.test.ts`): 31 files, 1,011
tests pass. New: the typed error when same-session recovery is required,
and the recovery class reader and pass-through.
- Server vitest: the native executor, status corpus and provider-pack
admission suites (1,268 tests) pass. New: the classifier makes the typed
error permanent, and the same text stays retryable. Checkpoint selection
returns `errorCode`, and an incompatible source starts a fresh session
with the same workspace (unit and PostgreSQL).
- Server vitest over the recovery area: stale-lock sweep, native
resumption, process recovery, failed-workspace recovery, restart
recovery (integration, diskless, preparation), controller recovery,
startup retry, startup cleanup, recovered-owner follow-up, session
resume, runtime selection and recovery deadlines. 926 passed. Four
`heartbeat-process-recovery` tests failed in that loaded run. Three pass
when I run them alone. `redacts opaque environment-bound credentials
from Sentry diagnostics: result` fails the same way on unmodified
`master` on this host (a local environment value is redacted).
- Review round 1: a new sweep test returns the run to running before the
clear. It cleared the locks without the write recheck and keeps them
with it. The strict Rust restore test covers an older agent server
release and an older ACPX release. The ACPX backend and durable runner
unit tests, the lock, sweep, resume projection and controller recovery
suites, `cargo fmt --check` and the server typecheck pass. In the second
full cargo run, the two `stdio_mcp` relay tests timed out waiting 5 s
for a child exit on this loaded host. They do not use the changed code
and passed in the first full run.
- After the rebase onto the current `master`: the new lock test,
failed-workspace recovery, stale-lock sweep and session resume suites
pass (189 tests). Server typecheck, `pnpm check:module-boundaries` and
`git diff --check` pass.
- CI on `929ef4508d2`: in the first run, `isolated-repository-rehome`
hit its 15 s test timeout, `idle-completed-history` could not reach its
fixture runtime service, and e2e shard 3 was cancelled by a runner
shutdown. A rerun of the failed jobs passed. All 55 checks pass.
- Not verified: a live remote sandbox deploy, and whether run B's
same-run resume replays its recorded `run.attach` failure or executes it
again. The Retry path is tested at source selection and checkpoint
rebind (server) and at cold attach (Runner). It is not tested end to end
through a sandbox.

## Risks

- A settled session now continues under new sidecar bytes. If a sidecar
change made an old ACPX record unreadable, session start fails as any
failed resume does. It does not corrupt state, and the run follows its
normal recovery policy.
- The sweep now keeps locks for a failed native run with a scheduled
resume. If that resume can never be claimed (for example the agent stays
paused), the task keeps the lock until the coordinator settles or the
task closes. Before, the sweep cleared it, and the resume still never
ran.
- If another run holds the task's lock, a scheduled remote resume still
stays blocked and the coordinator stays `retryable_failure`. This change
does not settle that case. It needs a separate decision about
superseding the resume.
- `native_provider_state_incompatible` is a new failure code. No UI or
migration change is needed. Older servers never see it.
- No schema migration.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`) through Claude Code, with tool use
(shell, file edits, test runs). The runtime does not expose the context
window or reasoning configuration.

## 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-11-03 16:30:58 -08:00
DottaandPaperclip 44a4ca1c61 fix(ci): remove the admission CLI import cycle (#18889)
Move the unchanged Actions read transport into an independent module so the admission CLI can evaluate consumers without awaiting a circular import of source verification. Preserve the existing source verification export and all verification and placement contracts.

Five child-process cases reproduce the original exit-13 failure before the repair and pass afterward. All 59 source/admission/image checks and 665 workflow contracts pass. Both CLI modes also pass read-only smoke checks against real GitHub metadata. Complete latest-head CI passed on its first attempt, with Greptile 5/5 and no unresolved comments.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
canary/v2026.1104.0-canary.0
2026-11-03 18:21:55 -06:00
Devin FoleyandClaude Opus 5.5 523775f78f fix(ui): skip keyless document rows instead of crashing the task panel (#18870)
Keep issue-document rows without a string key out of the task panel list and the document caches, so one malformed row cannot crash the page.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 16:06:53 -08:00
DottaandPaperclip f774992832 perf(ci): admit release candidates and isolate native image capacity (#18869)
Admit full source verification only for exact-source Docker or npm release work, while superseded pending sources complete metadata-only admission. Restart only strictly deferred runs; failed or cancelled checks require an explicit request.

Route canonical master native images to dedicated 32-core Spot profiles and keep source checks on protected verification capacity. Use the four-slot GitHub-hosted publication pool for registry work and signing, with proof waits outside that pool and a fresh source-proof check before tagging. Trim historical checkout blobs and prepared-runner disk cleanup while preserving tag and fork publication behavior.

Validation: release/source and source/artifact regressions, 665 workflow contracts, actionlint, complete latest-head PR CI, and Greptile 5/5 with all comments resolved. Existing CLI and large-file repository timing tests also passed independently; their CI shards received one scoped retry.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-11-03 18:01:07 -06:00
DottaandPaperclip 8771606fd8 test(e2e): cover browser task editing and reopening (#18782)
## Thinking Path

> - Paperclip lets people manage tasks and agent work.
> - Daily QA uses reusable Product E2E journeys against a named target.
> - Existing task journeys cover creation and assignment.
> - They lack saved title and label edits across completion and
reopening.
> - This pull request adds one browser-only lifecycle journey with
independent saved-state checks.
> - It also puts shared registry entries on separate lines so
independent case PRs can use distinct insertion points.
> - The registry formatting preserves every token, value, entry order
and case body.

## Linked Issues or Issue Description

Refs: #18655.

**What existing behavior does this improve?**

Product E2E coverage of ordinary task editing and completion.

**Current behavior**

The reusable catalog covers task creation and assignment. It lacks one
journey that edits a task, reloads it, completes it and reopens it while
retaining its saved context. Several shared registries put all entries
on one line, so independent case additions conflict.

**Proposed behavior**

Create a task assigned to the signed-in person. Edit its title and
create one label through the visible UI. Change its status to In
Progress, reload it, mark it Done, reload it, reopen it to Todo and
reload it again. Require the exact saved task and label throughout.
Preserve its description, assignee, project and priority. Allow no agent
run or model usage. Put the existing shared registry entries on separate
lines without changing their contents or order.

## What Changed

- Add `task-edit-lifecycle` and six required checks for browser
creation, saved edits, reload, completion and reopening, preserved
context and no agent work.
- Add valid, wrong and missing evidence controls. Retain allowlisted
observations and bounded GET failure receipts in private evidence.
- Register the case, feature associations, catalog expectation,
source-hash inputs and browser-only result validation.
- Extend target ownership for run-owned labels and one exact predeclared
title change. Require compatible scoped cleanup before selecting the
case.
- Open the real side panel or Properties control when needed. Exclude
inert and aria-hidden Labels rows and require visible readback. Document
inline title editing and the case prerequisites.
- Keep priority editing disabled. Verify that the saved priority remains
unchanged.
- Keep Cursor test observations outside its existing Stop deadline,
preserve the full rejection/Stop overlap matrix and add pre-release
no-provider-call controls. Production behavior and deadline values stay
unchanged.
- Format five shared registry files with one entry per line: the
JourneyId and AgentJourney unions, definition filenames, expected
catalog IDs and agent run counts. No registry entry moves in this PR.

## Verification

- Current head is `d1af3aae3712455c90db937fa7a5141555710506`. Its only
change from integrated head d40f28 is the Cursor driver test-fixture
ordering repair described below. The prior normal merge consumes actual
master `d59791828482f213713ca55000c40e6c9c6c03e5`, including reviewed UI
repair #18848 and the existing idle-history fixture repair from #18853.
It has both real parents, no conflicts and the required trailer. All
seven lifecycle case/oracle/catalog/documentation blobs and six selected
registry constructs remain exact 10625b. The idle-history fixture is
byte-identical to master. The PR diff has 13 paths, including the one
Cursor test file, and no feature-map or production-driver changes.
- 167 focused tests pass across catalog, target contracts, agent
evidence, lifecycle evidence and observation reads. E2E TypeScript and
credential-free discovery of21 Product E2E cases pass. The real merged
idle-history and exposure-reservation suites pass all 109 tests in132
seconds, including the owned HTTP-process adoption assertion and
explicit unknown-listener denial/drift controls. Canonical shared/SDK,
database and Runner TypeScript dependencies were rebuilt for the changed
master inputs. All4,220 tracked package/script/root inputs and 4,954
dependency outputs are recorded; the outputs remain unchanged after the
tests and browser journey. The first database build stopped because
child PATH selected a different global pnpm and its version switch could
not fetch signed metadata. The preserved attempt made no package or
source change. Using the already-installed verified pnpm9 on child PATH
passed. No install, Rust or provider-build global setup ran.
- One fresh canonical managed-local browser journey passes on exact
d40f28: all six checks and authentication, setup, journey, collection
and cleanup pass. Cleanup releases the disposable instance. The journey
takes 12.059 seconds and has explicit zero-run readback at all five
saved states. Its runtime commit isd40f28. Definition hash is
`f262707de2e544cecdb9c5c4c5222d61f04a122575b438658dec069e1a93f9cb`. The
prior10625b local6-check/5-phase pass remains separately recorded with
its own source and definition hash.
- A real closed-panel control previously reproduced the original
label-interaction failure using only the presentation preference
`paperclip:panel-visible=false`. The corrected journey passed all six
unchanged checks through an ordinary Show properties click. A separate
canonical743 local run passed all six checks and five phases with
released cleanup. No domain outcome was supplied by that control.
- A separate QA staging qualification passed all six checks and five
phases on exact743, with closed cleanup and verified zero agent work.
That original pass remains bound to743. It is not regraded or relabeled
as a staging run of10625b. Daily admission remains a separate
coordinator action.
- Earlier material failures remain part of the verification record. The
first managed-local setup failed before authentication with an embedded
PostgreSQL exit code of null; its original cause remains unresolved. A
later loader diagnostic found missing library aliases in this isolated
checkout. Existing bundled libraries were hash-verified and the aliases
were restored only there. Subsequent real runs exposed an overlong label
name, a status picker name mismatch and the closed-panel issue. Each
failed result and cleanup record is retained. The fixes preserve the
full campaign name, validate the production48-character label limit
before mutations, and use the actual visible controls.
- Original full CI
[41652807327](https://github.com/paperclipai/paperclip/actions/runs/41652807327)
failed an unchanged Discord fixture assertion, attention-retention
fixture limits and the CLI import/export startup limit. One bounded
recheck recovered Discord but repeated the other failures. The reviewed
merged retention-fixture repair was consumed normally through #18770.
Full CI
[41667811850](https://github.com/paperclipai/paperclip/actions/runs/41667811850)
passed all E2E shards but exposed inbox/account-fixture failures. This
branch consumed the actual reviewed repairs from #18798 and #18799.
Original logs and failed assertions remain retained; no timeout or
oracle was weakened.
- Original743 CI
[41682757346](https://github.com/paperclipai/paperclip/actions/runs/41682757346)
failed the unchanged CLI suite's30-second child-server startup hook. A
bounded local phase probe passed the same startup oracle but did not
prove the original CI cause. One coordinator-approved official recheck
followed. Timestamp comparison shows46 non-skipped jobs retained their
original start and end times; only the failed CLI job and final
aggregate have new start and end times. The skipped selection job
remains unclassified. New GitHub job IDs alone did not prove new
executions. The original startup failure remains preserved and its cause
remains unproven.
- Formatting-head CI
[41726104217](https://github.com/paperclipai/paperclip/actions/runs/41726104217)
failed the upstream idle-history process-adoption test and dependent
verify job:46 jobs passed, one was skipped and two failed. Its actual
synthetic checkout had parents masterf187 and10625b; the failed test is
absent from 10625b itself. The assertion expected one adopted process
and received zero. A pure production-function control proves a
conditional reserved-port drift path, but the actual failed CI port and
full reconciliation counters were not logged. The actual CI cause
remains unproven. This branch consumes the already-reviewed bounded
fixture-port repair in #18853 through its normal actual-master merge.
The original failure is retained. No unchanged-code retry, timeout
increase, skipped assertion or ownership-policy change was used.
- Integrated-head CI
[41730231620](https://github.com/paperclipai/paperclip/actions/runs/41730231620)
passed the formerly failing server1/12 lane, Typecheck, Build and E2E.
It failed the Cursor package fixture below and the dependent verify job.
A separate server4/12 job lost runner communication; the exact runner
instance and Spot-request readback confirm a capacity interruption. Its
unavailable log is not treated as a test pass. The original failures
remain retained. No old-head retry was made.
- The Cursor fixture sets a 300 ms cancellation budget, holds a
rejected-create checkpoint and originally performed directory/file
observations while that clock ran. Controlled400ms observation latency
on the original fixture reproduces the same fenced Stop error. The same
diagnostic passes after moving file observations before the cancellation
clock, or while the initial Stop-checkpoint gate still prevents it from
starting. The driver,300ms configuration and timeout negatives are
unchanged. Every original assertion AST remains, and three new
assertions forbid provider observation/cancel calls before releasing the
checkpoint. All81 driver tests,12 client tests and43 checkpoint-archive
tests pass; Runner TypeScript build and E2E TypeScript pass. The actual
CI inner error and observation duration were not logged, so its precise
original cause remains unproven.
- Final d1af3a full CI
[41733938939](https://github.com/paperclipai/paperclip/actions/runs/41733938939)
attempt3 is successful. Attempt1 lost four exact-bound Spot runners;
attempt2 recovered the Runner and both server lanes but lost the
replacement E2E runner to another confirmed Spot capacity interruption.
Two separate coordinator-authorized official failed-job rechecks were
recorded. Attempt3 passed the E2E path and final aggregate. Successful
executions retain their original timestamps; new job IDs alone were not
used as proof of re-execution. All original failures remain retained.
- Fresh Greptile check126258142754 completed successfully on exact
d1af3a with 5/5 and zero unresolved threads. Full recursive workspace
typecheck, all workspace tests and the full build were not repeated
locally; the corresponding hosted full CI gates pass.

## Risks

The case requires target support for `label` ownership and scoped
`taskEdit.revisedTitle` cleanup. Older target adapters must not select
it. Cleanup must reconcile lost creation responses and exact predeclared
title saves without touching other resources.

Registry formatting changes the harness definition hash because that
hash includes source bytes. Historical runs must keep their original
source and hash. The formatting does not change runtime values, order,
dispatch, case checks or product behavior. Later case PRs must verify
their actual merge resolutions and final-head checks before admission.

## Model Used

OpenAI GPT-6 through Codex with reasoning, tool use 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-11-03 17:47:13 -06:00
DottaandPaperclip ab2d2098a2 test: verify sleep producer readiness and CI contracts (#18795)
Reviewed and verified at 9f87b490d1.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-11-03 17:41:15 -06:00
ce332ecec0 fix(execution-admission): keep admitted project sets after settlement (#18871)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - #18746 added execution-admission storage: one admission row per
attempt and one charge row per admitted project.
> - Settlement deletes every charge row. The admission row keeps only
the primary project and an opaque context revision.
> - Thus, after settlement, the complete admitted project set is lost.
> - A retained provider session still carries the references of its
previous admission. Example: a task mentions A and B, the admission
settles, and then a user removes B from the task text. The next
admission must still count B, or start a fresh context. The primary
project or the context token cannot supply B.
> - This pull request keeps the complete admitted set on the admission
row after settlement. Charge rows stay the only active capacity.
> - The benefit is that later retained-session resolution can read the
previous set. Settled history does not hold capacity or block project
deletion.

## Linked Issues or Issue Description

Refs #18746 (corrects its storage). Related: #2831, #14333.

**Subsystem affected**

Server execution admission (`server/src/modules/execution-admission/`)
and the `packages/db` schema.

**Problem or motivation**

`settleAdmission` deletes all charges of an admission. No other storage
records the complete project set. After settlement, the set cannot be
recovered. Current task text is not a replacement, because it can change
after admission.

**Proposed solution**

Add `execution_admissions.admitted_project_ids uuid[]`. It holds the
sorted, distinct set. It is written once at admission and a trigger
blocks changes. It has no project foreign key. `{}` is a known empty
set. Null means unknown. Only settled rows can have null. Migration
`0428` fills the set of unsettled `0426` rows from their charges,
because those charges are still complete. Settled `0426` rows stay
unknown.

**Alternatives considered**

- Keep charge rows after settlement with a `released_at` column and a
partial active index. The project foreign key would then block project
deletion permanently, or the key must be removed from active charges.
One table would also hold two different facts.
- A separate history table with one row per project. This is more rows,
joins and constraints for data that is only read as a whole set by
admission ID.
- Encode the set in `context_revision`. That token is opaque and owned
by the context owner. The database cannot validate it.
- Use `heartbeat_runs.context_snapshot`. It is mutable and is per run,
not per admission attempt.

## What Changed

- Schema and migration `0428_retained_admitted_project_sets`: add
`admitted_project_ids`, a check (null only when settled, no null
elements, at most 100 entries) and an immutability trigger. The backfill
fills unsettled rows from their charges and leaves settled rows null.
The migration is safe to run again. Migration `0426` is not changed.
- `recordAdmission` writes the set. A replay compares against the stored
set. `settleAdmission` still deletes the whole charge set in one
transaction and does not change the stored set.
- Add `readAdmittedProjects` (company-scoped, by admission ID). It
returns `known` with the set, or `unknown`.
- Add a pure `retainedSessionProjects` rule: a retained session uses
previous ∪ current. An unknown previous set returns
`fresh_context_required`. No consumer uses it yet; C1b integrates
retained sessions.
- Update `doc/DATABASE.md` and `server/src/modules/README.md`.

Not changed: no public cap writes, no claim or dispatch integration, no
session integration, no provider activation, no `serialize` → cap 1
migration. Active counts still use only the indexed charge rows.

## Verification

Red, on unchanged `master` code, with the new tests:

- `server`: `npx vitest run
src/modules/execution-admission/adapters/postgres.test.ts`. The new
tests fail. With a temporary reader that uses the only storage `master`
has (charges), the settled A+B admission reads `[]` instead of `[A, B]`.
- `packages/db`: `npx vitest run
src/execution-admission-migration.test.ts` fails, because no `0428`
migration exists.

Green, with this change:

- `server`: `npx vitest run src/modules/execution-admission`: 27 passed.
The new PostgreSQL test covers: A+B admission → settlement → set still
readable, charges and counts zero, settlement replay keeps the set, a
second attempt with a different set, primaryless and empty sets, company
isolation, rollback, immutability, the check constraint, and deletion of
a project that has only settled history (its ID stays in the set). It
also checks that a project with an active charge still cannot be
deleted.
- `packages/db`: `npx vitest run
src/execution-admission-migration.test.ts`: 2 passed. The upgrade test
starts from the shipped `0426` schema with an unsettled A+B row, an
unsettled empty row and a settled row. After the upgrade the sets are
`[A, B]`, `{}` and null. A second run of `0428` changes nothing.
- `pnpm --filter @paperclipai/db typecheck` and `check:migrations`
passed. `node scripts/check-module-boundaries.mjs` passed.
- Not run locally: the server layered typecheck. The local process was
killed for memory (a 5.8 GiB limit shared with other jobs). CI runs it.

This PR does not prove C2 claim races or C3 lifecycle authority.

## Risks

- Migration: one `ADD COLUMN`, an `UPDATE` that only touches unsettled
rows (none in production, because nothing writes admissions yet), a
check and a trigger. It does not delete or rewrite data.
- Settled rows created before `0428` stay unknown. A future consumer
must start a fresh context for them. It must not treat them as empty or
primary-only.
- The stored set has no project foreign key. Company scope is checked at
admission time, under the same project locks and the charge foreign
keys.

Size: about 70 production lines (schema 8, migration SQL 22, adapter 28,
domain 15), 105 test lines and 24 doc lines. Generated metadata
(`0428_snapshot.json`, `_journal.json`, removal of `0422_snapshot.json`
by the standard pruning) is separate.

## Model Used

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with
tool use (shell, file edits, embedded PostgreSQL tests). Default context
window and reasoning mode.

## Checklist

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

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

Co-authored-by: nickyleach <331803+nickyleach@users.noreply.github.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:31:10 -08:00
Nicky LeachandClaude Opus 5.5 11ac90e31f fix(build): typecheck server/src/__tests__ and fix every type error (#18739)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The server has about 1,100 test and helper files in
`server/src/__tests__`
> - `server/tsconfig.json` excludes that directory, Vitest does not
check types, and no script runs the tsconfig in that directory
> - So a test can call a function with the wrong arguments and still
pass, and no tool reports it
> - This gap hid 2,168 real type errors, and some of them hid tests that
did not test what they claim
> - This pull request fixes all of these errors and adds the directory
to the server layers from paperclipai/paperclip#18737
> - The benefit is that a type error in a server test now fails the
typecheck, with no baseline and no backlog

## Linked Issues or Issue Description

Refs paperclipai/paperclip#18737, which is merged and added the server
layers. Refs paperclipai/paperclip#18738, which is merged and checks the
test projects outside `pnpm -r`, so it is not a duplicate. Refs
paperclipai/paperclip#18137, which records that this directory has no
check.

**What happened?**

No tool checked the types of 1,074 of the 1,101 files in
`server/src/__tests__`. I searched `package.json`, the workflows, and
all tsconfig files. 27 files are in the program of another tsconfig, and
these 27 have 0 errors. 11 of the 27 are in
`helpers/maintenance/tsconfig.json`. No script ran that project when I
measured this. paperclipai/paperclip#18738 now runs it.

**Expected behavior**

The server typecheck checks these files, and a type error fails the
check.

**Steps to reproduce**

1. On the base commit, run `tsc -p server/src/__tests__/tsconfig.json
--noEmit`. It reports 6,536 errors and needs 12.3 GiB.
2. Add a type error to any file in `server/src/__tests__`.
3. Run `pnpm --filter @paperclipai/server typecheck`. It passes.

**Paperclip version or commit**

I measured the error breakdown below on commit `8dfd632452`, with
TypeScript 7.0.2. The branch now includes master up to
paperclipai/paperclip#18865.

**Deployment mode**

Not applicable. This issue applies to development and CI. No production
source file in the server changes.

### Error breakdown

Each row adds one change to the row above.

| Step | Total errors | Change | Cause |
| --- | --- | --- | --- |
| Config as committed | 6,536 | | |
| Add `src/types/*.d.ts` | 4,447 | -2,089 | Config. The Express request
types were missing. |
| Set `rootDir` to the repository root | 2,436 | -2,011 | Config. |
| Set `allowImportingTsExtensions` | 2,181 | -255 | Config. Vitest
accepts `.ts` imports. |
| Add `ES2024.Promise` to `lib` | 2,138 | -43 | Config. Node 24 has
`Promise.withResolvers`. |
| Set `allowJs` for `.mjs` imports | 2,148 | +10 | Config. It removes 8
`TS7016` errors and shows new real errors. |
| Set `declaration` to false | 2,142 | -6 | Config. Tests emit nothing.
|
| Count only files in `server/src` | 2,058 | -84 | Other packages: 53
Daytona, 29 Runner test, 2 UI. |

So 4,478 of the 6,536 errors (69%) came from config or scope, and 2,058
are real type errors in 314 files. The 29 Runner test errors are also
real, which gives 2,087 errors. Tests that master added after this
measurement had 81 more errors, so this pull request fixes 2,168 errors.

This table classifies the first 2,058 errors.

| Kind of real error | Errors |
| --- | --- |
| Wrong argument or assignment type | 1,023 |
| Stale or partial fixture objects | 525 |
| Property that the type does not have | 317 |
| Missing narrowing of null or unknown | 101 |
| Implicit any | 60 |
| Missing name or module | 16 |
| Other | 16 |

### What the type errors hid

Most errors were stale fixtures and loose mocks. These errors hid real
test problems, and this pull request fixes them:

- **A test that never ran.** `instrumentation.test.ts` required
`sdk-trace-base`, which is not installed. So 3 span tests were skipped.
They now run and pass.
- **Assertions that could not fail.**
- `issue-thread-interactions-service` used a company ID as an issue ID.
- `status-card-evaluation-work` compared `[]` with `[]` in a rollback
check.
- `slack-conversation-lifecycle` read `item.issueId`, which does not
exist.
  - `email-channels` used a permission key that does not exist.
- `heartbeat-project-env` imported `LOW_TRUST_REVIEW_PRESET`, which is
not exported. The value was undefined.
- **Postgres processes that did not stop.** 11 test files called `stop`
on the test database helper. That method does not exist, and the helper
method is `cleanup`.
- **Arguments that the service ignored.**
- `methodKey` is now `connectionMethodKey` in the Daytona, Unikraft,
1Password, and Railway tests.
- 108 calls passed `"owner-user"` to a parameter that expects an options
object.
- 9 chat wizard fixtures passed `managementContext` as `source`, so they
ran `existing_app` and not `manifest`.
  - 2 tests passed a `{ userId }` actor that the function ignores.
- Inserts into `environments` passed a `companyId` column that the table
does not have.
- **Problems in tests that master added during this work.**
- `sandbox-creation-operations` read `database.connectionString` when an
external test database was set. In that mode `database` is undefined, so
the fixture would stop with a TypeError. It now uses the external URL.
- `idle-completed-history` set a Dot operation to `dispatched`. That is
a Muse status, and the Dot type does not have it. The fixture now uses
`admitted`, which is a real in-flight Dot status.

### Possible product problems, not changed

These may be real problems in production code or types. I did not change
them, because this pull request does not change production behavior:

- `decisions-service` `service.create` does not validate the lenient
`cancel_issue_tree` input.
- `createConnection` ignores `input.connectionPurpose`.
- The `createConnection` route schema default stores `{}` for
`transportConfig`.
- `prepareManagedAiRuntime` accepted the invalid value `mode:
"connection"`.
- `createBinding` accepts invalid `projectionClass` strings outside
class 3.
- `ensureManagedProjectWorkspace` forwards an untyped `repoRef` by
spread.
- `routineVariableSchema.label` is optional, but `RoutineVariable.label`
is required.
- `ToolRunContext.projectId` may be too strict.
- `withdrawInteraction` reads `issue.status`, which its type omits.
- `issueService.list` and `reconcileNativeFinalizations` omit fields
that they return at runtime.
- `createGitHubExternalObjectProvider` handles `tokenProvider: null`,
but its type forbids this value.

## What Changed

- Fixed the 2,168 type errors. The diff changes 322 test and helper
files.
- Corrected `server/src/__tests__/tsconfig.json`. Editors and the
typecheck now read the same options and report the same errors.
- Added three `test-tree` layers to
`server/scripts/typecheck-layers.mjs`. They read the server layers as
declarations, and any error fails them. A hash of the path selects the
shard, so a new file does not move other files.
- Added `server/scripts/typecheck-stubs/daytona-sdk.d.ts`. Server tests
reach Daytona plugin source, and the root install does not have
`@daytonaio/sdk`.
- Changed two imports in `ui/src/lib/issue-execution-policy.ts` to use
`.js` extensions, because server tests import this file.
- Made `declarationsConfig` and `--declarations-only` skip the
`test-tree` layers, because these layers emit nothing. Other projects
read only the source layers. Added a test for this.
- Updated `doc/DEVELOPING.md`.

## Verification

Run these commands:

```sh
pnpm --filter @paperclipai/server typecheck
pnpm exec vitest run server/scripts/typecheck-layers.test.mjs
```

Results on macOS (arm64), Node 24, 16 cores. Each check ran after the
merge of paperclipai/paperclip#18738 unless the row says otherwise:

| Check | Commit | Result |
| --- | --- | --- |
| Server typecheck, `PAPERCLIP_TYPECHECK_LOW_MEMORY=1`, all 7 layers |
`6e45a08573` | Exit 0, 0 errors, 62 seconds, peak 3.52 GiB |
| `node scripts/typecheck-test-projects.mjs --low-memory` | `a3306bd13e`
| Exit 0, 0 errors, 42 seconds, peak 3.25 GiB |
| The same script with `--reuse-declarations` | `1b878aabc0` | Exit 0, 0
errors |
| `typecheck-layers.test.mjs` and `live-recovery-work.test.ts` |
`01db5634e4` | 90 passed. These include real compiler runs for a pass, a
type error, and an invalid tsconfig. |
| `tsconfig.declarations.json` after a full server typecheck |
`0fb1e4fb0a` | It references only `below`, `cycle`, and `above`. No
generated config names a `tsconfig.layers.json` file. |
| Owned runtime test in `idle-completed-history` | `e8f2f448ac` |
Passed, with the fix that master later replaced |
| `idle-incoming-wake`, `plugin-idle-manager`, and the changed
`chat-channels` test | `6e45a08573` | 30 passed |
| Runner `native-execution.test.ts` | `eab4dcc1f4` | 114 passed |
| The 19 server test files that changed since `eab4dcc1f4`, and
`typecheck-layers.test.mjs` | `259737b700` | 4,275 of 4,277 passed. The
2 failures are in `chat-channels` and are in the list of 7 below. |

The peak is the largest single process, from `/usr/bin/time -l`. Peaks
for the server typecheck were between 3.42 GiB and 3.56 GiB across 8
runs. Peaks for the test projects were 3.25 GiB and 3.37 GiB across 2
runs. I did not measure a cgroup.

**Runtime check of the fixes.** Types do not prove that a test still
works. So I ran all 309 changed server test files, 11,929 tests, before
the rebase onto paperclipai/paperclip#18737:

| Result | Tests | Detail |
| --- | --- | --- |
| Passed | 11,913 | Includes the 2 tests and the 54 `dot-runner` tests
that passed in a later run, as described below. |
| Failed | 7 | The same 7 tests also fail on the commit before the
fixes. |
| Skipped | 9 | These tests have their own skip conditions: `sentry` 5,
and 1 each in `bundled-plugin-registration`, `customer-success-cloud`,
`onepassword-connection`, and `workspace-runtime-https-live-exercise`. |

The 7 tests that fail with and without this change, on my machine:

- 3 tests in `chat-channels` and `email-channels` stop with `ENOENT` on
a `SKILL.md` file. The path is in the parent checkout, above the git
worktree that I used, so I expect that the worktree is the cause. I did
not prove this.
- `workspace-instance-cleanup` compares `/private/var/...` with
`/var/...`. This is a macOS symlink difference.
- 2 tests in `git-reproducible-managed-checkouts` get
`unknown_base_git_files` and expect `unpushed_managed_checkout`. I did
not find the cause.
- 1 more test in `chat-channels`, `materializes direct external-chat
finals once`, gets `undefined`. I did not find the cause.

CI is the check for these 7 tests. `ci / verify` passed on `eab4dcc1f4`.

Failures during this work:

- My fix to `agent-runtime-connector-access` changed an invalid
permission key in the setup, but not in the assertion. The runtime check
found this. I corrected the assertion, and the file passes.
- `heartbeat-plugin-environment` failed one wait in the full run. It
passed three times alone, so I think that load caused the failure. It
can fail again under load.
- `dot-runner` skipped all 54 tests in the full run because `cargo` was
not on my `PATH`. It then passed 54 of 54 with the toolchain on the
`PATH`.
- My first `lib` value removed the default DOM types, and the error
count went up by 375. I added the DOM libraries.
- My Daytona stub with bare `any` left 48 errors, because generic
helpers then inferred `unknown`. I added shapes that return promises.
- One fix to `agent-secrets-routes` used `class_3_static_lease`, and the
test failed. That class allows only specific paths. I reverted to
`unclassified`.
- `ci / General tests (server (8/12))` failed on `259737b700` because of
the flaky port in `idle-completed-history`. paperclipai/paperclip#18853
fixed this test on master, so this pull request keeps that fix.

**Audit of the diff.** I counted these patterns in added and removed
lines, to find fixes that only hide an error:

| Pattern | Added | Removed |
| --- | --- | --- |
| `@ts-ignore` | 0 | 0 |
| `@ts-expect-error` | 0 | 1 |
| `.skip` | 0 | 0 |
| `as any` | 9 | 9 |
| `: any` | 11 | 2 |
| `as never` | 8 | 2 |
| `as unknown as` | 35 | 4 |
| `Promise<unknown>` | 16 | 5 |
| `expect(` | 128 | 113 |

No hunk removes more `expect(` calls than it adds. Three removals of
test content were intentional, and I checked each one:

- `muse-control` removed `signalToken`. Line 1012 already asserts that
it is absent.
- `merged-pr-confirmation-sweep` removed legacy aliases. Other tests
cover them.
- `heartbeat-workspace-branch-containment` removed a `finalize` branch
that no test row can reach.

Some partial fixtures stay unchecked through `as unknown as` and
`Promise<unknown>`, mostly in `issue-agent-mutation-ownership-routes`
and `issue-comment-reopen-routes`.

**Accuracy of the check.** One program over the tree and the three
shards reported the same errors at the same positions. Memory, as the
peak of the largest process on one thread, was 3.93 GiB for one program
and 2.43 GiB for the largest shard. I measured these two values before
the fixes.

Not run or not proven:

- I did not run the full `pnpm test:run`, or a root `pnpm build`. I
expect CI to run the full suites.
- I did not run the 4 GiB container check, or the full
`scripts/typecheck-low-memory.mjs`. The peaks above come from macOS, so
they are not a Linux cgroup measurement.
- I did not run the UI typecheck after the merges.
- `workspace-runtime-https-live-exercise` is skipped, so its `cleanup`
fix did not run.

## Risks

- **A large diff in test files.** 328 files change. A pull request that
edits the same tests can get merge conflicts. New tests on master can
also add type errors before this merges. Any such error fails this check
and needs a fix here.
- **A higher cost for changes to source types.** A change to a type can
add errors in test files that the author did not touch. The author must
then fix them. This is the purpose of the check, but it is new work.
- **Changed test behavior.** Some tests now run paths that they did not
run before, such as the 9 chat wizard fixtures and the 3 span tests.
They pass here, but they can be more sensitive to timing or to the
environment.
- **The Daytona stub can go stale.** The stub is written by hand. If the
plugin uses a new SDK member, the typecheck reports an error until
someone updates the stub.
- **The release path.** `release-verify.yml` and `scripts/release.sh`
run `pnpm -r typecheck`, so they now run this check.
- **The tsconfig change.** The test transform reads
`server/src/__tests__/tsconfig.json`. The options that I changed are for
type checks, and the runs above passed.
- **A gap that remains.** Tsconfigs in `packages/paperclip-runner` and
in most sandbox provider plugins exclude `*.test.ts`. This check does
not read most of those test files, so they can still drift. That work is
separate from this pull request.

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

## Model Used

Anthropic Claude Opus 5.5 (`claude-opus-5-5`) through Claude Code, with
reasoning, tool use, code execution, and subagents. The runtime does not
show 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
- [ ] I have run tests locally and they pass (7 tests fail locally, with
and without this change. See Verification.)
- [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: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 15:27:38 -08:00
DottaandPaperclip f1fb1bcb8a fix(ui): let Done close completed Slack setup (#18878)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Slack connections let people talk to their assigned agents.
> - Setup saves a completed state after the connection is ready.
> - A saved setup link can still open Success after completion.
> - Done submits completion again, which the server rejects.
> - This pull request opens settings when the Slack setup is already
complete.
> - Users can close the saved setup without changing the bot's
lifecycle.

## Linked Issues or Issue Description

**What happened?**

Reopening a completed Slack setup shows Success. Done then shows
“Couldn't finish setup: This connection is not waiting for a setup
test.” The user stays in the wizard.

**Expected behavior**

Done opens the saved connection settings. A paused bot stays paused. A
setup that is still awaiting completion must use the existing
verification request.

**Steps to reproduce**

1. Finish a Slack connection.
2. Reopen its saved chat/connect link with provider=slack and its resume
parameter.
3. Click Done on Success.
4. Observe the completion error instead of the settings page.

**Paperclip version or commit**

Reproduced live on Core da77c1bcfa. The
handler is unchanged on this branch's base
51d1d4ce07.

Related: #18837 and #18722. Also inspected #15828; it changes required
tests for other providers and explicitly excludes Slack. This fix covers
reopening a completed Slack setup.

## What Changed

- Done opens settings for a Slack setup whose saved step is complete.
Both the provisioned Success view and manual Try view use the same
action. Unfinished setups retain the existing completion request.
- Add active and paused completion cases for managed, automatic and
manual setup. Keep positive cases for unfinished setup.
- Extend the managed and automatic browser journeys to reopen the saved
link, reject duplicate completion requests, click Done, and reload
settings.
- Add completed-state previews and make the Slack fixture reject
duplicate completion, as the real server does. Update the provider
guide. Maps are unchanged.

## Verification

- Six component regressions failed on the original handler because it
submitted completion again.
- All 38 wizard/clipboard component cases passed after the handler
change. All 284 related chat UI tests across 19 files passed. The final
fixtures add the required welcome-status field.
- Repository typecheck and build passed. UI token gates and Storybook
catalog checks passed (3076 stories; structural only). All 9 Slack
browser cases passed in 1.5 minutes on Node 24, including completed-link
exit and settings reload for managed and automatic setup. Their provider
responses are mocked. Fresh deployed acceptance remains pending. Full CI
[41734669956](https://github.com/paperclipai/paperclip/actions/runs/41734669956)
passed on attempt 2 for exact head
`4a3bf180929df57350389f45e4c6acfa822e8ddc`. Greptile and Ponytail both
give this head 5/5 with no actionable findings.
- Completed-state production previews passed for managed and automatic
setup. These previews check the rendered state and Slack handoff; exact
Done navigation is covered by the browser journeys. One preview
text-locator wait timed out; fresh accessibility state and the saved
image then showed Story status Pass with all three assertions passed.
- CI attempt 1 lost its typecheck runner while unpacking the Rust
dependency cache. Typechecking had not started. The log is retained. All
test jobs passed in attempt 1; its verify summary failed only because
typecheck had failed. The failed-only retry passed on the same commit.
- The initial typecheck found two new fixtures missing welcomeStatus.
Those fixtures were corrected; the failure log is retained.
- The live returning-user attempt is preserved as failed. The Slack bot
answered the fresh probe, but Done returned the error above. The
original toast expired before its journey screenshot; a separate
ordinary-UI reproduction captured the visible toast. This is not a live
pass of the new fix.
- The full local server Vitest suite was not repeated for this UI-only
change. Full CI passed before merge.

## Risks

The action uses the server-reported completed setup state to leave the
wizard. It does not activate, unpause or repair a bot. Settings load the
saved connection through the existing access checks. Unfinished setups
still use server verification. API contracts, credentials and provider
permissions do not change. Concurrent completion from another tab is
unchanged: a stale test-step tab can still need a reload after a
conflict.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository editing, browser
use and command execution. The runtime does not expose a more precise
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-11-03 17:20:07 -06:00
dependabot[bot] 208bda1a0e build(deps-dev): bump drizzle-kit from 0.31.10 to 0.31.11 (#15166)
Bumps [drizzle-kit](https://github.com/drizzle-team/drizzle-orm) from
0.31.10 to 0.31.11.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/drizzle-team/drizzle-orm/releases">drizzle-kit's
releases</a>.</em></p>
<blockquote>
<h2>drizzle-kit@0.31.11</h2>
<ul>
<li>Warn about silent migration skips on drizzle-kit checks - thanks <a
href="https://github.com/shaneikennedy"><code>@​shaneikennedy</code></a></li>
</ul>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/drizzle-team/drizzle-orm/commit/15454dbe49d827c6081f3d0231e2e7985e517295"><code>15454db</code></a>
+</li>
<li><a
href="https://github.com/drizzle-team/drizzle-orm/commit/54e436feea82c856d5644152cc9d186e237e2e95"><code>54e436f</code></a>
exclude gel from pull</li>
<li><a
href="https://github.com/drizzle-team/drizzle-orm/commit/0fd1cc61f1ff6553234d29042461fc5e9aa551c3"><code>0fd1cc6</code></a>
remove gel</li>
<li><a
href="https://github.com/drizzle-team/drizzle-orm/commit/d028db74f20cbf3740bcaaf136beb9eb27f0928c"><code>d028db7</code></a>
skip gel</li>
<li><a
href="https://github.com/drizzle-team/drizzle-orm/commit/93dc01e355724e666b656377cb07cf9613153787"><code>93dc01e</code></a>
[All-kit]: Warn when journal timestamps can cause migrations to be
skipped (#...</li>
<li><a
href="https://github.com/drizzle-team/drizzle-orm/commit/b7862528fd8fc39bc2653a6c18dad7c1f4e68d10"><code>b786252</code></a>
Merge pull request <a
href="https://redirect.github.com/drizzle-team/drizzle-orm/issues/6049">#6049</a>
from drizzle-team/drizzle-kit-announcements</li>
<li><a
href="https://github.com/drizzle-team/drizzle-orm/commit/f9fc5bf30bc14e84f2416349acbd6c04001a497e"><code>f9fc5bf</code></a>
Add drizzle-kit announcement manifest and schema doc</li>
<li><a
href="https://github.com/drizzle-team/drizzle-orm/commit/9d6453215d18705986c2081124437bb6a03fb943"><code>9d64532</code></a>
Merge pull request <a
href="https://redirect.github.com/drizzle-team/drizzle-orm/issues/6004">#6004</a>
from drizzle-team/pin-npm-11-main</li>
<li><a
href="https://github.com/drizzle-team/drizzle-orm/commit/0af2f2ea5b75f0831a36874e23df9c294c49e26e"><code>0af2f2e</code></a>
Pin the release npm self-update to major 11: the npm 12.0.0 tarball is
missin...</li>
<li><a
href="https://github.com/drizzle-team/drizzle-orm/commit/696863804d9bd9c2511680aa05f5f3a516aa299c"><code>6968638</code></a>
Merge pull request <a
href="https://redirect.github.com/drizzle-team/drizzle-orm/issues/6001">#6001</a>
from drizzle-team/release-router-dispatch-inputs</li>
<li>Additional commits viewable in <a
href="https://github.com/drizzle-team/drizzle-orm/compare/drizzle-kit@0.31.10...drizzle-kit@0.31.11">compare
view</a></li>
</ul>
</details>
<br />

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2026-11-03 17:14:29 -06:00
DottaandPaperclip 54782706e1 fix(ui): render confirmation prompts as Markdown (#18851)
## Thinking Path

> - Paperclip helps people manage work by AI agents.
> - People use confirmation cards to review work before an agent
continues.
> - A prompt can include a Markdown link to the work that needs review.
> - Confirmation cards showed that link as text, which made review
harder.
> - The details section already uses the shared Markdown renderer.
> - This pull request uses that renderer for confirmation prompts too.
> - People can read formatted prompts and open their review links.

## Linked Issues or Issue Description

**What happened?**

Confirmation prompts show Markdown links as text in the task composer
and classic cards. Checkbox confirmations and expanded receipts have the
same problem.

**Expected behavior**

Render links, emphasis, code, lists, and paragraph breaks with the
shared Markdown renderer.

**Steps to reproduce**

1. Create a confirmation with this prompt: `Review the [two
screenshots](/issues/EXAMPLE-42#document-preview).`
2. Open the request above the task composer.
3. Observe the raw link text. Expect a link with the label “two
screenshots”.

**Paperclip version or commit**

Source checkout at `1f55073f9`.

**Deployment mode**

Built from source. The defect is in the shared board UI.

Related work: #17014 covers question prompts. This change covers
confirmation prompts.

## What Changed

- Render confirmation and checkbox prompts with `MarkdownBody` in
compact and classic cards.
- Keep prompt images as references and Mermaid diagrams as source text.
- Use the rendered checkbox prompt as the options group’s accessible
name.
- Use the same renderer in expanded confirmation receipts.
- Pass external link metadata to the prompt renderer.
- Add eight regression cases for document links, emphasis, code, lists,
paragraphs, and inert media. Check the checkbox group’s accessible
label.
- Replace CSS-specific paragraph checks with checks of the rendered
paragraphs.
- Document the prompt rendering rule in `DESIGN.md`.

## Verification

- Before the initial fix, all eight new regression cases failed because
the document link did not render. Before the review fix, the same cases
also proved that prompt images were live. Both defects are fixed.
- After the fix, 180 tests pass across the two card suites and two
Markdown suites.
- `pnpm check:token-gates` passes.
- `pnpm --filter @paperclipai/ui... build` passed for the initial
change. `pnpm --filter @paperclipai/ui build` passes after the review
fixes.
- Full `pnpm -r typecheck` and `pnpm build` could not complete. The
workspace does not have Rust `cargo`.
- `PAPERCLIP_TYPECHECK_LOW_MEMORY=1 pnpm --filter @paperclipai/ui
typecheck` passes. The first UI typecheck hit the workspace memory limit
before this successful retry.
- Full `pnpm test:run` started but did not complete locally. It was
stopped during the first chat integration shard when the task moved to a
scheduled CI check. No full-suite pass is claimed. GitHub CI runs the
full checks.
- Browser suites were not run locally. In the first CI run, build and
full typecheck passed. Server shard 8/12 and browser shards 1/8 and 7/8
failed because their self-hosted runners lost communication. Other jobs
were cancelled. See [the first CI
run](https://github.com/paperclipai/paperclip/actions/runs/41713671579).
The current head has fresh 5/5 reviews from Greptile and Paperclip. On
the first attempt of [the new-head CI
run](https://github.com/paperclipai/paperclip/actions/runs/41717410154),
the Runner package test job received a shutdown signal. One unchanged
CloudAccessGate test also failed: the reconnect banner was absent at its
assertion. Its root cause is not confirmed. The latest attempt of that
run passed on commit `591078ca568056369ee9067c9535845bd47d8dc1`. Build,
full typecheck, all test shards, all eight browser shards, and `ci /
verify` passed. The earlier assertion failure remains part of the
record; its root cause is not confirmed. All 58 current-head check runs
are complete: 54 passed, one review is neutral, and three checks were
skipped. The Snyk status passed. No review threads remain open.

## Risks

- Prompts use the shared Markdown and URL handling. Prompt images remain
inert, and Mermaid diagrams display as source text.
- Rich prompts can take more vertical space than plain text.
- Existing plan previews, selection rules, and approval callbacks remain
covered by the card tests.
- This change does not require an API or database migration.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, tool calls, 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>
canary/v2026.1103.0-canary.14
2026-11-03 17:06:39 -06:00
DottaandPaperclip dcc0878b32 perf(agents): save healthy account settings without a runtime probe (#18690)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The agent runtime page saves account, model, and environment
settings.
> - The save handler starts a computer and runs a live account test
before it writes the edit.
> - A slow computer or provider can make a healthy account save take
tens of seconds.
> - Saved account health, access, installation, and compatibility can be
checked without a runtime test.
> - This pull request uses those checks for ordinary configuration
saves.
> - Legacy adoption, task adoption, and Copilot model checks keep live
verification.
> - The benefit is a fast save while Test and execution retain runtime
checks.

## Linked Issues or Issue Description

**What happened?**

Saving a new AI account selection on an existing agent can take 20–30
seconds. Model edits can take the same path. The configuration PATCH
acquires a computer, prepares account credentials, runs a
provider/runtime probe, and releases the computer before it saves.

**Expected behavior**

A healthy installed account can be saved after current access and
compatibility checks. A save confirms persistence. Test verifies the
runtime. An unverified legacy account must still pass live adoption
checks.

**Steps to reproduce**

Run the PostgreSQL regression with a 20-second controlled delay at
computer allocation. On the original route, a successful save takes
20,191 ms and calls allocation and the runtime probe once. With the
first patch, the same fixture takes 51 ms; with the environment-guard
correction, it takes 37 ms and calls neither operation. Each timing has
one sample. This is a local fault experiment with real HTTP and
PostgreSQL. It is not a hosted latency measurement.

Related: #17760 addresses binding comparison and prospective probe
environment resolution. This change deliberately removes the live probe
from ordinary healthy-account saves. It keeps live checks for legacy and
task adoption. #17965 fixes the result of an explicit managed Runner
test. Neither change removes this save-time wait. #18687 optimizes a
different Apps configuration read.

## What Changed

- Replace the internal probe boolean with explicit none, legacy, and
always modes.
- Use legacy mode for ordinary configuration saves. Keep current account
health, access, installation, compatibility, environment, audit, and
revision checks.
- Keep the environment company check before the live-probe branch. Keep
the live task-adoption transaction and Copilot model checks.
- Add twelve real PostgreSQL save-route regressions, including
company-bound environment checks. Keep the existing inherited-sandbox
adoption fixture for legacy and task adoption.
- Cover healthy and legacy settings changes separately. Keep provider
rejection and reconnect tests on the legacy save path.
- Update the specification and feature/access maps to distinguish saving
from runtime verification.

The configuration route owns this policy. The account service still owns
account access and health. The existing environment test service still
owns live probes. No new endpoint, migration, dependency, background
job, or provider protocol is added.

## Verification

Base: `3f8ebfff8aa9febd0733c6f65b07485dcadfd0e2`.

- Original route with initial regressions: three failures and two
passes. Healthy saves reached computer allocation. The controlled
20-second baseline saved successfully but failed the no-allocation
assertion.
- Initial save/adoption/Copilot selection: 21 passed. The name filter
excluded 141 unrelated tests. The seven new cases cover persistence,
revision and activity records, model edits, roster edits and stale
retries, missing installation, revoked grants, legacy verification, and
controlled allocation latency.
- Explicit Test-route suite: 55 passed. The first attempt reached the
30-second import hook limit. The same suite passed with a 90-second hook
limit.
- Saved harness preflight suite: six passed. Account revocation,
compatibility, probe failure and target release checks remain.
- `node scripts/check-maps.mjs` and `git diff --check`: passed.
- Review revision: both full save/account suites passed, 96 tests total.
The controlled save took 37.08 ms with zero computer allocations and
zero provider probes. The five new environment checks passed after the
three foreign-environment cases failed against the first patch.
- Local full `pnpm -r typecheck`: unavailable because cargo is missing.
- Local full `pnpm build`: unavailable at the verified Copilot runtime
prerequisite. Server typecheck preparation also reaches missing cargo.
- Local full `pnpm test:run`: stopped after concurrent broad checks
exceeded the container memory limit. No full-suite pass is claimed.
Direct server TypeScript checking and its bounded retry were also killed
by the container memory limit. The first remote Typecheck + Release
Registry check passed; the next head still requires fresh CI.

Reproduce the controlled timing:

```sh
cd server
PAPERCLIP_CONFIG_SAVE_PROFILE_DELAY_MS=20000 pnpm exec vitest run src/__tests__/agent-configuration-save.test.ts --hookTimeout 90000
```

The fixture keeps real Express, authorization, account selection,
PostgreSQL persistence, revisions and audit writes. Only remote
allocation and the provider probe are controlled. The default tests do
not contact a provider. Successful configuration saves no longer depend
on either controlled operation. A later explicit Test or task must still
verify the actual runtime.

Review follow-up: the first Greptile review found that the new
healthy-account path skipped the environment company guard. The
corrected route checks the effective environment before it decides
whether to probe. Five real PostgreSQL cases cover foreign, same-company
and global environments, inherited defaults, and an existing account
binding. Against the first patch, the three foreign-environment cases
returned 200 and failed their expected 403 assertions; the two allowed
cases passed. Initial fixture attempts hit unique environment
constraints; the fixture now uses distinct sandbox rows and names.

The first CI server shard found 11 tests that required a live probe for
every save. Those expectations are changed to the documented
healthy/legacy split, with additional healthy-selection cases. The build
and Runner jobs were cancelled after this test failure. Their logs show
no separate compile or test verdict. No cancelled check is counted as a
pass.

Latest commit `6257e9e67f289e1c650a8b4c2b818fcca37e2800`: [CI run
41542980091](https://github.com/paperclipai/paperclip/actions/runs/41542980091)
passed typecheck, build, all test shards, Runner checks, and E2E. All 55
check runs are complete: 52 passed and 3 were skipped by their workflow
conditions. The commit status check also passed. [Greptile
review](https://github.com/paperclipai/paperclip/pull/18690#issuecomment-6590555746)
is 5/5 for this commit with no new findings. The earlier environment
ownership thread is resolved. These remote results complete the checks
that the local tool and memory limits prevented. The earlier local
failures remain recorded above.

## Risks

A healthy saved account can become invalid at the provider before the
next runtime use. Ordinary saves no longer detect that through a live
test. They still reject unhealthy, revoked, incompatible, or uninstalled
accounts in current storage. Explicit Test and execution retain provider
authentication and project-authentication checks. Legacy-account
adoption, task connection adoption, and Copilot account-specific model
checks keep their existing live validation.

There is no migration. Reverting the route change restores the earlier
synchronous probe behavior.

## Model Used

OpenAI Codex, GPT-6 family, with repository tools and code execution.
This runtime does not expose the exact model ID, context-window size, or
reasoning setting. 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 and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass (focused suites; full-suite
limits are 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-11-03 16:56:11 -06:00
Dotta 049c1eacf5 Preserve historical native logs during ephemeral enrollment (#18864)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Ephemeral web servers need retained run history in shared storage.
> - Some old native runs have surviving local logs but no completion
digest or verifiable native source coverage.
> - Enrollment must preserve those bytes without calling them a complete
native transcript.
> - This pull request adds a read-only archive path to offline
enrollment.
> - Users can still read old output after the original server filesystem
is removed.

## Linked Issues or Issue Description

**What happened?**

Offline enrollment blocks on interrupted runs and unfinalized historical
native logs. The current importer requires canonical native-event
coverage even when the original local file survives.

**Expected behavior**

Preserve the exact surviving file in shared storage. Keep the original
run status, completion metadata, and source events. Do not allow writers
or native repair to modify archived history.

**Steps to reproduce**

Use an S3-configured instance with a terminal native run whose local log
lacks a final size or digest. Stop all app deployments and run ephemeral
maintenance. Before this change, interrupted status or unproven native
coverage prevents enrollment.

Related preservation work: #18826, #18835, #18841. This is a bounded
migration fix, not a new live logging protocol.

## What Changed

- The run-evidence migration owner can archive unfinalized native local
files during offline enrollment.
- Require the offline migration environment, zero active or queued runs
across the instance, and expired controller/finalizer leases. Check
global quiescence at each page boundary. Recheck each run's ownership
and metadata before publication.
- Verify stable local file identity and metadata, any recorded
size/digest, uploaded prefixes, and remote SHA-256.
- Store raw history in a separate `<run>.archive` journal with source
cursor zero and backend `s3_archive`. Leave completion fields and native
events unchanged.
- The existing log store reads archives but rejects begin, append,
enqueue, flush, and finalize. Native source repair does not run for
archives.
- Keep the default background importer's coverage checks. Continue to
reject incomplete mirrors and corrupt files.
- Add real PostgreSQL/S3 and packaged maintenance coverage. Correct a
pre-existing large-chunk fixture: the current verifier can prove exact
compactor output, so the negative case now uses a different canonical
source.

## Verification

- Passed on final head b96e80dbb3: 13
migration/storage/worker tests against disposable local PostgreSQL and
S3.
- Passed: 76 log-store, cancellation, object-store, native watermark,
and maintenance-service tests.
- Passed: server typecheck and server build.
- The archive journey removes original log files, reads exact saved
bytes through the normal recovery-aware reader, checks unchanged
run/source metadata, and verifies read-only behavior.
- Negative cases cover ordinary-process use, another company's running
execution, live controller/finalizer leases, an invalid recorded digest,
an incomplete mirror, and a changed local source.
- Review follow-up: move the global run-table scan from every item to
page boundaries. A real PostgreSQL trigger admits new execution during
publication; the page then fails, retains verified copies, and resumes
after quiescence. All 5 migration cases passed. After rebasing onto
merged #18841, all 13 migration/storage/worker cases and all 7 packaged
maintenance cases passed, along with server typecheck, build, and
runtime bundle.
- Preserved failure: the old large-chunk expectation failed with both
this change and the unmodified base importer. The updated fixture checks
the verifier's current compaction contract and still blocks mismatched
source output.
- Passed: all 7 real PostgreSQL/S3 enrollment cases, including the
bundled maintenance process under plain Node. Its interrupted native log
is archived, the original log directory is removed, and the saved bytes
remain readable.
- Passed: packaged runtime build and full repository CI (54 successful
checks). Four CI jobs lost their runners; a separate voice-recovery
timing assertion failed. The unchanged voice case passed locally, and
all failed/interrupted CI jobs passed on retry. Greptile reviewed the
final head at 5/5 with no unresolved threads. The separate Paperclip
Review app remains queued/paused. No live tenant migration or volume
detachment is claimed by this PR.

## Risks

- Rollback after enrollment requires a reader that supports
`s3_archive`; old binaries cannot read the new backend.
- Offline mode relies on the deployment owner stopping all old app
processes. Database and file checks are additional fences, not a
substitute for that prerequisite.
- Archives may be incomplete transcripts. The implementation preserves
that uncertainty and does not fabricate a successful run or completion
digest.
- No schema change, new background polling, or telemetry change.

## Model Used

OpenAI Codex (GPT-6 family), with reasoning, repository tool use, and
code execution. The exact serving model ID and context window are not
exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-11-03 16:52:30 -06:00
Devin FoleyandClaude Opus 5.5 ccee3e4493 docs(release): prepare v2026.1103.0 stable notes before publish (#18842)
<!-- Write all pull request text in Simplified Technical English
(ASD-STE100): short sentences, one instruction per sentence, simple
approved vocabulary, and the active voice. -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Each stable release publishes curated release notes as its GitHub
Release body.
> - The stable release workflow reads
`releases/beta/v2026.1023.0-beta.0.md` from master when it starts. It
publishes that exact revision.
> - The beta notes have a guessed version and date (v2026.1026.0,
2026-10-26). That version did not ship.
> - An audit of the notes against the commit range also found a wrong
contributor count and three missing fixes for self-hosted users.
> - This pull request corrects the notes before the stable release
starts.
> - The benefit is that the v2026.1103.0 release page shows the correct
version, date, counts, and fixes.

## Linked Issues or Issue Description

Refs #17388 (the curated notes for the `2026.1023.0-beta.0` soak).

**Problem**
The stable notes for the promotion of `2026.1023.0-beta.0` show
v2026.1026.0 and the date 2026-10-26. `scripts/release.sh stable
--print-version --date 2026-11-03` on master gives `2026.1103.0`. The
notes also have a contributor count that does not match the release
convention, and they do not include three user-facing fixes.

**Expected result**
The notes show `# Paperclip v2026.1103.0` and `> Released: 2026-11-03`.
The counts and the cited fixes match the range
`v2026.1023.0..d69631b01b9c658f80bf8bed389863dce82643e1`.

**Impact**
The stable workflow publishes these notes as the GitHub Release body. If
this pull request does not merge before the release starts, the release
page shows the incorrect version and date.

## What Changed

- Set the H1 to `# Paperclip v2026.1103.0`, the release line to `>
Released: 2026-11-03`, and the version in the intro paragraph.
- Changed the contributor count from 26 to 27. `git shortlog -sn
v2026.1023.0..d69631b` without `github-actions[bot]` gives 27 authors.
This is the same rule as the v2026.1023.0 and v2026.1012.0 notes, which
count `dependabot[bot]`. The list of 21 credited handles does not
change.
- Added three fixes that self-hosted users can see and that the notes
did not cite:
- #16357: when you stop a queued run before it starts, its saved
follow-up stays saved and does not run (Runs, recovery, and Retry).
- #17259: if a sandbox has a preinstalled Claude CLI that is too old for
the selected model, setup installs the required version privately
(Adapters and CLI).
- #16494: the Slack eyes reaction also works in direct messages (Slack
highlight).
- No other text changes. The diff is 7 lines in one file.

## Verification

- `git rev-list --count
v2026.1023.0..d69631b01b9c658f80bf8bed389863dce82643e1` = 515. This
matches the notes.
- All 255 cited PR numbers are in that range. Eight squash commits in
the range have no `(#N)` suffix. The commits-to-pulls API maps them to
#16356, #16976, #16441, #16403, #16404, #16401, #14180, and #16215.
- Of the 260 PRs in the range that the notes do not cite, the audit
found them to be tests, CI, refactors, docs, performance internals, or
follow-up fixes to features that this release adds and the notes already
describe. Six are Paperclip Cloud-only (#16953, #16907, #16876, #16254,
#16033, #16259), and the notes do not include them. One is a hidden,
default-off pilot (#16984).
- The migrations in the range are `0334`–`0374` (41 files). This matches
the Upgrade Guide. The defaults for the new environment variables in
`server/src/config.ts` and `server/src/sentry-run-failure-policy.ts`
match the Upgrade Guide.
- The handles @lucktastic and @MindSyncHub come from the commit author
records.
- The file on this branch has blob
`ef11a77a4b267b9f8d459900ba7b43098841c410`, which is identical to the
locally checked edit.

## Risks

- Low risk. This is a change to documentation only, in one file.
- After this pull request merges, the stable workflow publishes the
merged revision as the release body. If the release starts after
2026-11-03 UTC, the workflow uses `stable_date=2026-11-03` so that the
version stays the same as the header.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`) through Claude Code, with tool use
(shell, git, GitHub API). Used for the notes audit and the edits.

## 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 (documentation only; no
tests apply)
- [ ] I have added or updated tests where applicable (not 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: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-03 14:49:19 -08:00
commitperclip[bot] a00109aa35 fix: Preserve sanitized ACP JSON-RPC error details and numeric codes in run errors, summaries, and acpx.error records (#18087)
> [!NOTE]
> Opened by Prospector's automated issue-fix pipeline. A maintainer
reviews and decides; nothing here merges on its own.

> [!WARNING]
> This change crosses a trust boundary. See Risks.

## Thinking Path

- Issue #16034 reports a defect in paperclipai/paperclip.
- Root cause, as the fixing agent read it: emitAcpxFailure in
packages/adapter-utils/src/acpx-engine/execute.ts built the run error,
summary, and acpx.error message from Error.message alone. It ignored the
JSON-RPC error's 'data' explanation and numeric 'code', so every ACP
phase failure (create runtime, ensure session, configure session, turn)
showed only 'Internal error'.
- This pull request: Preserve sanitized ACP JSON-RPC error details and
numeric codes in run errors, summaries, and acpx.error records

## What Changed

- `packages/adapter-utils/src/acpx-engine/execute.ts`: This change is
kept from the earlier attempt. The shared failure handler reads the
explanation from a string 'data', 'data.details', or 'data.message'. It
appends that text to the message unless the message already contains it,
and runs the result through sanitizeTerminalSessionFailure. A numeric
JSON-RPC code is recorded as errorMeta.rpcCode. Errors without these
fields keep their old message.
- `packages/adapter-utils/src/acpx-engine/execute.test.ts`: The existing
harness keeps the earlier it.each case, which covers all four phases,
all three detail shapes, redaction, and rpcCode in the acpx.error
record. A new case in the same harness covers the checks that only the
separate file had: detail text already in the message is not repeated,
and a plain Error stays unchanged with no rpcCode.
- `doc/run-log-events.md`: Documents the change to the acpx.error
record: which detail formats are read and in what order, the rule
against repeating detail text, the redaction and 4,096-character title
limit applied through the existing sanitizer, and the optional integer
'rpcCode' field.

## Verification

- Reproducing test(s), added in this PR:
`packages/adapter-utils/src/acpx-engine/execute.test.ts`
- They fail on the base commit and pass with the fix, each twice in a
fresh sandbox.
- The repository's compile command passes with the fix applied.
- 9 existing test file(s) near the change passed.
- The full test suite passes except for the 11 file(s) that already fail
on the base commit.

## Risks

- Written by an automated pipeline, not a person: review it as you would
an outside contribution.
- Risk tier of the touched paths: 2.
- The scope reviewer found it also changes, beyond the report: Plain
Error, non-object throw, string or Node errors with string codes
(ENOENT), and data that is empty, missing, non-string or malformed
- The scope reviewer found it also changes, beyond the report:
messageOverride callers (turn timeout, handshake guard)
- It crosses a trust boundary. Check each crossing against what the
issue asked for, and against who asked:
- data-exposure
(packages/adapter-utils/src/acpx-engine/execute.ts:3626): Text the ACP
child process controls (JSON-RPC error 'data', 'data.details' or
'data.message') is now added to the run error message and summary that
users see; it passes through sanitizeTerminalSessionFailure redaction
first.
- logging (packages/adapter-utils/src/acpx-engine/execute.ts:3651): The
'acpx.error' run-log record now carries the provider's explanation in
'message' and a new integer 'rpcCode' field, both supplied by the child,
on every failure route that calls emitAcpxFailure without a
messageOverride, including the handshake-guard route that suppresses
stderr.
- data-exposure
(packages/adapter-utils/src/acpx-engine/execute.ts:3639): The numeric
JSON-RPC error code from the child is now copied into
'classified.errorMeta.rpcCode', which reaches the adapter result's
errorMeta and the places that store or show it.

## Model Used

- opus (Anthropic Claude, via Claude Code), writing and checking the
change in an isolated sandbox.
- opus (Anthropic Claude, via Claude Code), reviewing it independently.

## Linked Issues

Fixes #16034

A search of paperclipai/paperclip's open and closed pull requests found
no other pull request that names #16034.

## 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)
- [ ] 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)
- [ ] My branch name describes the change (e.g. 'docs/...', 'fix/...')
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] I have updated relevant documentation to reflect my changes
- [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

<!-- prospector:issue-fix v1 issue=16034 base=5cfa2bf26d3e
report=1b07160d9492357f -->

Co-authored-by: commitperclip[bot] <284812584+commitperclip[bot]@users.noreply.github.com>
2026-11-03 14:43:24 -08:00
51d1d4ce07 fix(ui): refine coordinator navigation and task list defaults (#18831)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Coordinator workspace puts chat, tasks, and human decisions in
one navigation.
> - Its task menu exposed only two views and always opened All tasks.
> - Its sidebar hid search under More and showed an empty Recents
section.
> - This pull request restores the four task views and saves each user's
last selection.
> - It also groups the main navigation and makes the task list use the
page background.
> - Users can return to their preferred task view and find search from
the sidebar.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The Coordinator sidebar and task collection.

**Subsystem affected**

ui/ — React and Vite board UI.

**Current behavior**

Coordinator opens All tasks and offers only All tasks and Needs
attention. Search lives under More. Empty Recents content takes sidebar
space. The task list inherits a darker root background.

**Proposed behavior**

Tasks starts in Inbox for new users. The menu contains Inbox, Unread,
Needs attention, and All tasks. The selected view persists per user in
browser storage. The sidebar groups Chat, Tasks, and Needs you between
New task and More. Search sits beside the organization menu. Recents
appears only when entries exist. The list surface and edge fades match
the local page background.

**Reason and benefit**

The page starts with personal work and restores the user's last choice.
Navigation gives search a fixed location and removes empty content.

**Breaking changes**

Unread task-view links now stay on Coordinator Tasks. Retired
merged-Inbox views keep their existing Inbox routes. Organization-filter
links without an explicit view still open All tasks. Needs attention
keeps the existing pending-decision feed. The initial Inbox rule applies
to Coordinator. Task lists now default to newest updates, including
task-detail subtask lists. Saved and URL sort choices stay intact.

**Additional context**

Related merged work: Refs #18703, Refs #17983, Refs #18323, Refs #18492.
The GitHub search found no duplicate open PR for these changes. This is
focused UI polish within the existing workspace and does not add a
roadmap feature.

## What Changed

- Default task lists to Last updated descending and show that active
header. Keep descendants with their parents while nesting is on; use a
flat newest-first order when nesting is off. Task-detail subtask lists
use this default and retain checklist controls. Saved and URL sort
choices keep precedence.

- Add space after New task and before More. Add header search with the
existing command palette action.
- Use “Needs you” in desktop navigation, mobile navigation, and the
decision page.
- Hide Recents when no entries exist.
- Restore the shared four-view task menu. Default Coordinator to Inbox
and save the last view per user.
- Read unread tasks through the server's viewer read filter. Preserve
task hierarchy and existing filtered links.
- Use the resolved local-board identity for Inbox. Keep Inbox and Unread
selected when a status filter changes. Load accessible read ancestors as
context for unread subtasks.
- Resolve task-list and edge-fade surfaces against the Coordinator
background token.
- Update view, routing, navigation tests, and DESIGN.md.

## Verification

- Passed for the sorting update: 344 tests across the task-list, classic
and streamlined nesting, Coordinator task page, and task-detail suites.
- Passed for the sorting update: 30 Storybook catalog and map checks,
token gates, UI typecheck, and UI production build.
- Browser check: a task-list URL without sort parameters shows Last
updated descending as the active sort.

- Passed: `pnpm -r typecheck` after rebase onto master.
- Passed after review fixes: 91 tests across seven affected task-view,
routing, identity, navigation, and decision-page suites.
- Passed after review fixes: UI typecheck and UI production build.
- Passed: `pnpm check:token-gates` and `git diff --check`.
- Browser checks: Inbox opens on the first task visit. The menu shows
all four views. Unread survives leaving Tasks and returning, and
survives reload. Header search opens the command palette. Empty Recents
is absent.
- Browser style check: the page, task-list section, row groups, and
edge-fade token resolve to the same local background.
- Passed: `pnpm build` after rebase onto master.
- Local full-suite run: all six chat integration shards passed (1,834
tests). Stopped the remaining local run after all latest-head CI test
gates passed. The local full suite did not finish.
- Greptile: 5/5 on current commit `5fc50d616c`, with all three earlier
review findings fixed and all threads resolved. The sorting update added
no new review findings.
- Initial CI failed in [browser shard
1/8](https://github.com/paperclipai/paperclip/actions/runs/41702707260/job/126156263776).
The cross-organization UUID task-link test reached its 5-second heading
assertion timeout. The failure screenshot and page snapshot show the
correct task heading. The precise cause of the timeout remains unknown.
- Passed during CI diagnosis: six focused Chrome browser runs, then all
35 tests from the same shard on the unchanged branch. Task links
resolved to the target organization and attachment uploads completed.
- Passed on the single unchanged-head CI retry: [all 35 tests in browser
shard
1/8](https://github.com/paperclipai/paperclip/actions/runs/41702707260/job/126184223560),
including the original UUID task-link case. This establishes recovery;
it does not establish the original timeout cause.
- Passed: [all CI gates for commit
`5fc50d616c`](https://github.com/paperclipai/paperclip/actions/runs/41702707260).
The final aggregate gate passed. No check is pending or failing. Three
checks are skipped as expected, and the optional Superagent scan is
neutral.
- Review: confirm the sidebar order, four task views, saved view after
navigation, and matching list background.

## Risks

- Task-list defaults change from Task ID order to newest updates.
Task-detail subtask lists use the same default instead of workflow
order. Existing saved and explicit sort choices remain available.

- Low risk. New Coordinator users start in Inbox instead of All tasks.
- The view preference stays in this browser and does not sync across
devices.
- The task collection keeps its existing paging behavior.
- Unread URLs now select the native Coordinator task view.

## Model Used

OpenAI Codex, GPT-6 family. The session did not expose a more specific
runtime model ID or context-window size. Used code editing, terminal
tools, and browser verification.

## 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: Scott Tong <scott@scottsmbpm5max.lan>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 14:39:09 -08:00
DottaandPaperclip 683fa4725e fix(evals): refresh native runtime and pin reviewed catalog repairs (#18780)
## Thinking Path

> - Paperclip lets people inspect and manage their agents’ work.
> - The timeline must report canonical ownership history, and OpenCode
tool failures need bounded diagnostics.
> - The measured candidate also had independently reproduced
question-navigation and oracle defects.
> - This change promotes the reviewed production fixes and pins one
immutable, qualified source composition.
> - Original queries, model, counts, clocks, privacy controls and
qualification holds remain intact.

## Linked Issues or Issue Description

Refs #18775, #18777, #18779, #18711, #18713, #18718, #18699, #18798,
#18799.

**What happened?** The measured candidate predates merged native-runtime
fixes and contains fixture defects. The timeline omitted recorded
reassignment/removal history. Finish-input errors lacked a closed
correlation and shape witness.

**Expected behavior:** project recorded ownership transitions without
inventing facts, distinguish successful native execution from failed
verification steps, and retain content-free finish diagnostics.

**Steps to reproduce:** repeat an unchanged assignee while editing
another field; then reassign or unassign it. Only recorded transitions
should appear. Invalid OpenCode finish input should produce a correlated
rejection witness without values or schema text.

Related #18699 adds gated company OTLP diagnostics. This change uses the
existing local callback; it adds no exporter or capture policy.

## What Changed

- Promote canonical timeline creation, reassignment and removal
projection from #18777. Initial child ownership requires a recorded
fresh-creation receipt; delegation requires a readable recorded parent.
Unknown legacy facts fail closed. User actors/owners require active
company membership; agent actors/targets must exist in that company.
Spans, events and edges share that admission filter. Remove the
introduced all-history absence query and use guarded indexed UUID
lookups.
- Add the shared/OpenAPI `unassigned` event kind, preserving newer main
contracts and issue-route/service code through reviewed semantic joins.
Existing UI consumers remain generic; no new visual marker is claimed.
- Add bounded OpenCode finish diagnostics: schema digest, hashed call
identity, closed shape enums and validation category. A throwing sink
does not change admission or cancellation.
- Pin immutable candidate `fd5e3b1e5a379524c0d1651ed8273e75ffd86541`,
composed from combined #18777 source
`b734393eff0d7ef85b7ad8abd556d2a66ca3a4e6` plus the reviewed MCP
diagnostic delta. It preserves actually merged #18775
`97c07d149b5670088faf15317dcf603c75e2af87` and #18779
`a352c057d475067b4fe67375f06785287dfbe0ce`, and merged runtime fixes
#18711, #18713 and #18718.
- Qualify narrative handling for adjacent repair/verification sentences,
intervening native-failure wording, and negated repair objects.
Questions, denials and hypotheticals retain their controls. Preserve
Previous question beside the qualified Entity type import; separately
restore the already-called `internalResultLink` goal helper binding.
That import is a runtime fixture repair, not a type-only change.
- Include the narrow older-baseline CI typecheck closure and causal
heartbeat test observers in the candidate. All four default typecheck
projects execute. Main already includes the protected upstream tooling
and newer heartbeat fixture, so neither is overwritten with an older
candidate file.
- Preserve all 244 identities, original queries, model, native/human
counts and clocks, including the explicit prior Studio accounting
exception. The full catalog stays on its candidate branch; the 196/244
gate and historical SDK runtime holds remain.

## Verification

The primary’s f478 retry repeated the real composer-hidden five-second
assertion (1 failure,18 passes,1 skip). Investigation established a
setup readiness gap: an outer route response can delay mounting the chat
thread beyond the assertion. The successor adds the existing real-thread
`aria-busy="false"` readiness barrier before the unchanged composer
assertion. The original120-second scenario and digest, unread,
source-link, history, and reply checks are unchanged. The full owning
browser scenario passed naturally in10.2s and with its first real chat
response held6000ms in12.0s. This synchronizes fixture setup; the
underlying AWS late-mount latency remains unproved.

**Current main gates passed:** exact head
`823dab49fcd8470140c178771e5c0c6b78725e97` has [successful normal
CI](https://github.com/paperclipai/paperclip/actions/runs/41724620555),
including both required aggregates, fresh Greptile 5/5 and zero
unresolved review threads. Paperclip Review is neutral with a completed
substantive 5/5 and no actionable defect. The current optional
Superagent Security Scan is neutral/inconclusive: Security Graph context
retrieval returned HTTP503, so its assessment did not start. That result
supplies no security PASS credit. The primary source PR retains its
separate current-head gate before Root sequences normal protected
merges.

- Reviewed MCP controls: 130 on main and 129 on the older candidate.
Meaningful parent comparisons failed before implementation/correlation
correction. Controls cover actual offered schema identity, null/omitted
admission, bounded rejection categories and throwing-sink cancellation.
- Exact unchanged production postimages retain 63 Postgres/public-route
controls, nine plan controls and the focused index-plan proof. Owner
OpenAPI passes 32 controls; main’s newer contract passes 33. Earlier
setup/sandbox failures remain recorded; unchanged database controls were
not replayed.
- Narrative owner controls pass 216, retaining all prior 207 controls.
The independent peer passes 144 balanced cases; earlier 108 and 39-case
matrices remain retained. Actual consumer parent failures reproduced the
review findings before repair.
- Main’s server NoEmit passed on its protected baseline; only two pin
files changed afterward. The candidate’s prior full server and
four-project checks passed. All server/package/script/root-lock inputs
remain byte-identical; the changed Runner project’s guard passes on the
current source. Compatibility driver controls pass 47 and six owning
suites pass 82. The one explicitly reviewed Docker-only unit omission
concerns an unimported image-build feature; all four intended project
guards still execute.
- Pin/adapter/contract controls pass 47. All 242 expanded declarations,
the two legacy dispatch rows, and all 244 singleton private policies
retain their authenticated identities. Descriptor, launcher and
private-protocol bytes remain exact; declaration loading invoked zero
fixture callbacks.
- All 506 SDK input blobs match the meaningfully compiled `6abe5739`
source vector. Prior real shared/SDK builds measured declaration/map
changes and zero runtime-JavaScript changes; a compiled consumer accepts
the new kind and rejects an unknown kind. The reviewed registry remains
unchanged. Default source proof, 51 SDK controls, 20 custody controls
and 16 UI consumer controls remain qualified;
runtime/compiler/parser/Zod/canary pins and held runtime grades are
unchanged.
- The full Mac heartbeat run on prior observer source retained **58
passes / six failures in 802.78s**. Two nonassignee controls
subsequently pass focused checks. Four late timeout causes remain
unresolved locally; no flakiness/infrastructure cause or complete local
pass is claimed. [Actual AWS
CI](https://github.com/paperclipai/paperclip/actions/runs/41712023737/job/126182620050)
ran this exact heartbeat file with **64 passes in 107.209s**. Its
server/package inputs are unchanged in the current narrative-only
successor. The source-identical f478 heartbeat file also passed all64
AWS tests in105.812s; source inputs remain unchanged. Current main
ordinary CI passed; the primary source PR must independently close its
latest-head ordinary CI gate.
- Historical main [run
41713049555](https://github.com/paperclipai/paperclip/actions/runs/41713049555)
recorded six synchronized runner-shutdown failures and cancellations;
the shutdown cause is unknown. A separate chat passive-control assertion
observed one `db.transaction` call where zero was expected. Finite local
probes passed 2+2 but did not establish a cause or late stack. No
external publication, cleanup cause, source fix or flakiness is
inferred. The primary’s separate composer-visibility CI failure also
remains an inconclusive historical result; no guessed fix is credited.
No old-head retry was used.
- Earlier UI/inbox fixture failures and the CLI startup timeout remain
historical. UI/inbox repairs are now upstream in #18799/#18798 and main
preserves those files verbatim. The CLI timeout cause remains unknown.
Broader local repository/Rust qualification is not claimed; fresh hosted
CI supplies broader coverage.
- All four actual repair merges, an exact merged-tree/diagnostic source
join, and fresh controller/source/private-policy/active-run guards
precede the ten-case TestOps measurement. No paid or model eval was
launched for this PR; the original three passes/seven failures remain
unchanged.

## Risks

The next ten-case model result is unproved. Earlier MCP timeout and
continuation-shape causes remain unknown; diagnostics provide future
evidence without relaxing validation or rewriting grades. Prior local/CI
failures are retained, including the unresolved chat assertion and local
heartbeat timeouts.

Initial ownership requires canonical in-window audit facts. Missing
legacy facts remain unknown. The additive `unassigned` contract is
synchronized across shared/OpenAPI/server consumers, with no new UI
marker.

SDK runtime holds and the broader 196/244 qualification gate remain.
Normal protected merges require fresh checks and resolved feedback; no
CODEOWNER or branch-protection bypass is authorized here.

## Model Used

OpenAI `gpt-6.1-sol` through Codex. Capabilities used: repository
analysis, code changes, source validation, and local test execution. The
context-window size is not exposed by this harness.

## 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 — current focused/type/pin
controls pass; the prior full Mac heartbeat failures remain disclosed
and receive no local PASS credit
- [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 main normal CI and
required aggregates passed; optional neutral reviews and separate
primary gate are disclosed above
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups —
exact current main head; full thread pagination verified
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 16:17:51 -06:00
DottaandPaperclip 0ab3ce7a03 fix(workspaces): preserve legacy recovery across storage migration (#18841)
## Thinking Path

> - Paperclip manages durable tasks and agent work through the control
plane.
> - An ephemeral web server needs existing workspace files in shared
storage.
> - Failed historical runs can still own recoverable sandbox files or
unfinished coordinator state.
> - Moving local files must preserve those obligations without replaying
the old agent turn.
> - Migration journals the exact source and fences old copyback; the
recovery owner saves verified sandbox files separately.
> - New user input can continue only after matching preservation
receipts and independent execution-stop checks.
> - Accepted results retain their finalization owner and must have its
durable workspace receipt before storage moves.

## Linked Issues or Issue Description

Builds on merged #18826 and #18835.

**What happened?**

Existing workspaces with failed native copyback could not migrate to
remote storage. Simply changing their storage authority would let later
recovery target a retired local directory, lose child-task attachments,
or leave a recovered task unable to accept a new turn.

**Expected behavior**

Preserve local files, exact sandbox provenance, task bindings,
checkpoints, and unresolved history. Recover surviving sandbox files
into immutable task-scoped attachments after migration, and let a new
authorized user turn use those files without replaying the old
execution.

**Steps to reproduce**

Create a terminal native run with a prepared copyback descriptor and a
retained sandbox. Migrate its local workspace, remove the old host
filesystem, recover the sandbox, and submit a new user turn. Also
exercise a child task reusing its ancestor's workspace and an expired
observed coordinator.

## What Changed

- Journal exact legacy run, task, descriptor, and unfinished coordinator
bindings during offline migration. Descriptor-free runs retain the
complete declared lease/allocation/directory identities; publication
locks and revalidates them.
- Publish a run-scoped fence that prevents automatic restart or copyback
into the retired local authority.
- Release only the exact journaled expired coordinator lease under the
offline deployment fence and locked revalidation, recording the prior
owner in recovery history. Preserve phase, checkpoint, result, and run
status.
- Save recovered sandbox files through the existing immutable snapshot
owner, retaining allocation and provider-stop checks.
- Attach recovered files using the current task's identity when a child
reuses an ancestor workspace.
- Accept matching migration and snapshot receipts for workspace-repair
admission and explicit new-user continuation. Changed coordinator
bindings and invalid receipts remain blocked.
- Require the finalizer's successful workspace_finalize operation before
moving a workspace whose accepted result has finalized copyback; the
existing owner can then finish assessment/status effects.
- Document unresolved workspace-policy, task-ownership, and
missing-provenance cases as separate owner actions that block
detachment.

## Verification

- 569 tests across migration, retained recovery, and explicit
continuation passed on ffcde13f57.
- The final saved-result follow-up passes all 75 migration tests. Four
receipt cases cover prepared copyback, a successful finalization
receipt, a missing receipt, and a failed receipt. The successful case
invokes the actual finalizer after migration and completes the existing
saved operation.
- Five real-file journeys cover a retained host, removed host,
child-task reuse, an expired observed coordinator, and a descriptor-free
run with two separate sandbox allocations. They recover sandbox files,
clear the repair blocker, admit an explicit /new user comment, reserve a
new remote workspace, and verify recovered attachment contents.
- Negative cases reject changed coordinator phase/owner, invalid
migration fences, changed source sets and allocations, mismatched
task/allocation/action receipts, malformed archives, and unfinished
snapshot operations. Descriptor-free continuation requires every
journaled source; one missing archive keeps the hold.
- Twelve PostgreSQL/S3 and maintenance-service tests pass on the exact
final rebuilt runtime, including a plain-Node bundled process,
authenticated original-key export, overlapping task-folder restore, and
complete predecessor filesystem removal. The final server and runtime
bundle builds also pass.
- Head 9ab6a38fa4 passed all 573
migration/recovery/continuation tests, server typecheck, server build,
runtime bundle, and 12 PostgreSQL/S3/bundled-maintenance tests.
- Verification caught an expired-owner continuation rejection (160
recovery cases passed, one failed); the admission fix and subsequent
569-test run passed. Earlier fixture failures and the parent PR's
unrelated full-suite failure remain documented in their verification
history.
- Greptile and Paperclip Review both scored final head
fa58d4a5ce 5/5, with no remaining
findings.
- CI exposed 15 blocker-read projection regressions: descriptor-free
checks performed an extra full run read for ordinary unmigrated tasks.
The follow-up gates the lookup on the migration marker, includes that
marker in the bounded blocker projection, and selects only recovery
fields. All 31 blocker-projection and real-file journey cases plus
server typecheck pass. Final-head CI passed all 52 successful checks,
including repository typecheck, build, server tests, and browser suites.
- Preserved CI failure: the unchanged Runner package had one Cursor
Cloud Stop/checkpoint timing failure (4,903 cases passed). All four
focused local cases passed. Retrying only the failed job and its
verification gate passed without source changes.
- This PR does not claim tenant enrollment or a hosted rolling-deploy
result.

## Risks

Cloud must stop every predecessor deployment before enabling the offline
migration contract. Migration preserves uncertain work; it does not
certify provider termination, invent a completed result, or authorize
sandbox deletion. Missing or ambiguous sources remain held. Preserved
attachments are separate files rather than an automatic merge into newer
task edits. Parallel shared-write policy, stale task ownership, missing
task provenance, and accepted results without a successful finalization
receipt remain enrollment blockers. Original folders and the original
volume remain retained.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository tools, and code
execution. This 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 available 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 described the issue in-PR using the bug template
- [x] I have not referenced internal or instance-local issues or links
- [x] My branch name describes the change and contains no internal task
ID
- [x] I have run the focused tests locally and they pass
- [x] I have added tests for the changed behavior
- [x] I have updated relevant documentation
- [x] I have considered and documented risks
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address reviewer findings before merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 16:17:13 -06:00
DottaandPaperclip d597918284 docs(skills): clarify failed-run diagnostics and guard repairs (#18865)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The failed-run skill guides agents from evidence through a tested
repair.
> - A review of repeated investigations found two small guidance gaps.
> - The skill does not link the existing bounded diagnostics reference.
> - Guard repairs can add qualification steps before identifying what
the guard protects.
> - This proposal adds the reference and makes that safety purpose
explicit.

## Linked Issues or Issue Description

**Where is the issue?**

`.agents/skills/fix-failed-paperclip-run/SKILL.md`, sections 1 and 4.

**What's wrong?**

Agents have to rediscover the authorized run, event, log, and recovery
reads. When a compatibility guard blocks progress, they can also add
qualification machinery even when runtime checks already establish
compatibility.

**Suggested fix**

Link the existing diagnostics reference. Require the agent to name the
guard's invariant, remove redundant prerequisites only when existing
runtime checks establish compatibility, and retain authority and
integrity checks.

Related: #16830 introduced this skill. #18066 is a concrete example of
removing a redundant saved-qualification prerequisite while keeping
runtime checks. #18602 proposes a separate CI-disposition skill and does
not contain these edits.

Refs #18861, #18862, #18863. Those issues track structural reliability
and review improvements from the same retrospective. This small skill
proposal does not close them.

## What Changed

- Link `skills/paperclip/references/run-diagnostics.md` from incident
discovery.
- Require a named invariant before changing a failing guard, and prefer
existing runtime compatibility checks over redundant qualification
steps.

## Verification

- The installed `skill-creator/scripts/quick_validate.py` validator
passes for the changed skill. Its PyYAML dependency ran in an isolated
temporary environment after the default Python lacked it.
- Both relative Markdown links resolve from the skill directory.
- Skill metadata is unchanged. The diff contains one file and seven
added lines.
- `git diff --check` passes.
- Full workspace typecheck, tests, and build were not run for this
documentation-only review proposal. Greptile reviewed commit
`e9eb102e92a7bcfdde6b87ba02949356b8602c46` at 5/5 with no findings.
GitHub CI is still running; this remains a draft for maintainer review.

## Risks

The new guidance could be read too broadly when removing a guard. It
explicitly retains authority and integrity checks. The surrounding skill
still requires a regression for the negative case protected by the old
guard. This change does not alter runtime behavior or grant recovery,
merge, or deployment authority.

## Model Used

OpenAI `gpt-6-astra` through Codex, max reasoning effort, with shell and
GitHub tools. The host reports an effective context window of 258,400
tokens. The preceding retrospective also used OpenAI `gpt-6.1-sol` and
`gpt-6-astra` reviewers at xhigh effort.

## 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 (skill validation and link
checks; application suites not run)
- [x] I have added or updated tests where applicable (no executable
behavior changed)
- [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-11-03 16:11:06 -06:00
DottaandPaperclip 451d06e2fc fix: resume interrupted Runner preparation and confirm failure report delivery (#18853)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for work.
> - The server owns run recovery and task continuation after a restart.
> - A shutdown can interrupt Runner preparation before any provider work starts.
> - Workspace recovery can save the files but leave that task behind a legacy execution hold.
> - Retry then accepts a request without starting a successor. Startup also leaves the task blocked.
> - This pull request verifies the interrupted preparation, preserves its files, and admits one bounded continuation.
> - It also keeps a dropped Sentry capture eligible for a later report.

## Linked Issues or Issue Description

**What happened?**

A hosted task remained blocked after a graceful server shutdown interrupted Runner preparation. Its remote workspace was recovered. Retry returned an accepted request, but no successor started. Startup did not resume it. The task also had no matching Sentry event.

The recovery cause is verified. The exact cause of the missing historical Sentry event is unknown. A separate regression with the real Sentry SDK proves that processing backpressure can drop a capture while the task cache marks it as reported forever.

**Expected behavior**

Retry must continue eligible interrupted work from its saved workspace. Startup must schedule one bounded retry when durable evidence proves that no provider started. A later task read must be able to retry an unconfirmed Sentry report.

**Steps to reproduce**

1. Interrupt Runner preparation during a server shutdown before native runtime selection.
2. Retain and recover its remote workspace under the legacy recovery hold.
3. Press Retry, or restart after file recovery has completed.
4. Observe that the task remains blocked without a successor.

For the reporting case, hold the first Sentry event in its asynchronous enrichment step. Fill the SDK processing buffer. Capture a second task failure, release the first event, and read the second failed task again later.

**Paperclip version or commit**

Reproduced against source commit `4d31588dea`. The hosted incident served `6ef37398c8d799c5142a3a6590ecc70ad352b663`.

**Deployment mode**

Hosted Linux server with remote workspaces and external PostgreSQL. Local regressions use the source checkout on macOS arm64, Node 26.4.0, PostgreSQL 17.10, and the audited optional Sentry SDK 10.71.0.

Related public changes: #18541 added task-state failure reports. #18678 retained remote exports before Retry. #18803 fenced remote workspace writers before continuation.

The small backend-port fixture correction follows #18739. It is included here because the same existing setup defect blocked CI.

## What Changed

- Verify pre-native shutdown interruption against the immutable adapter claim, controller ownership, run events, native records, stopped leases, and exact workspace recovery receipts.
- Retire only the server's unchanged bootstrap failure block. Admit explicit Retry through the existing continuation path.
- Discover completed workspace recovery at startup and schedule one retry under the existing failure budget. Let saved input own its continuation.
- Apply the existing safe replacement dispatch checks to preparation retries. A later human block, competing execution, dependency, pause, approval, or budget gate must still prevent dispatch.
- Keep execution evidence available through the bounded run projection.
- Confirm Sentry task-report delivery from a successful SDK HTTP response. Keep unconfirmed captures pending for 60 seconds before a later task read can retry them. Both caches remain bounded and start no timer.
- Document recovery and report delivery semantics.
- Align existing handoff/budget assertions with the guarded preparation retry reason. Keep the owned-runtime fixture outside reserved proxy ports, with bounded allocation attempts.

## Verification

- Red before the recovery fix: all three new Retry/startup cases failed because no successor was created. Green after the fix: all three passed with real saved workspace archives and PostgreSQL.
- The pre-merge revision passes all 384 tests in the six affected recovery, run-control, run-state, retry, and dispatch suites.
- The late-block regression failed before its repair. Its four focused recovery/dispatch cases then passed, including final dispatch cancellation.
- Review found a second stop check that still rejected verified local and lease-free preparation. Both new cases failed before that repair. All 17 interrupted-preparation regressions passed on the pre-merge revision.
- The Sentry regression failed with the real SDK before the fix. All 91 affected reporting tests then passed. The transport stays in memory. No test sends events to Sentry.
- Full repository typecheck passed with `node scripts/typecheck-low-memory.mjs`. The full `pnpm build` and module boundary check passed.
- CI found untyped delivery-hook parameters when the optional Sentry SDK was absent. Explicit callback types fix that build. The SDK-absent server build and all 91 SDK-present reporting tests pass.
- Server typecheck passes with the SDK installed and the real Sentry SDK contract CI job passes. Greptile reviewed the pre-merge commit `103e175c44` at 5/5 with no open findings.
- The local full-suite attempt initially overlapped the rebase and stopped at test-line selection. Its unchanged-head rerun passed the first 309-test chat shard. I stopped the old-head run before applying review fixes. Current-head CI owns the remaining full-suite verification.
- CI exposed two old retry-reason expectations. The handoff and retry-budget checks pass with the new guarded reason. A separate existing process-adoption fixture chose port 42953 in the reserved 42000–42999 proxy range. The fixture now rejects reserved ports, following #18739. Its real process/adoption/termination test passes locally.
- The final Runner package job [initially failed](https://github.com/paperclipai/paperclip/actions/runs/41718507919/job/126205912033) one unchanged Codex credential case after it could not acquire its port quorum, then timed out. The complete unchanged 42-case credential suite passes locally. One unchanged CI retry passed; the original competing port owner was not identified. No Runner implementation or credential test was changed.
- Required CI passes on `103e175c4490e1bdf804c64a8635b0e4568545ae`, including `ci / verify` and `ci / e2e`. That revision has Greptile 5/5 with no open findings. The optional Paperclip Review check on that earlier head remained queued, waiting for its assigned agent.
- Merged current `master` without replacing published commits. The retry gate retains both shutdown interruption checks and the new workspace preparation timeout policy. All 311 tests in the five combined recovery, retry scheduling, completion, and native startup suites passed after the conflict resolution. Server typecheck passed. Final head `005d380b00d45a2393c4a2a190eab27611168d3c` includes the next conflict-free master update. All 17 interrupted-preparation regressions passed again on that final head. Greptile reviewed that exact head at 5/5 with no new findings. Required CI passes on that exact head, including `ci / verify`, `ci / e2e`, typecheck, build, the full test shards, and the real Sentry SDK isolation check. The optional Paperclip Review remains queued, waiting for its assigned agent; it is not a required branch check.
- On the final head, six test jobs were cancelled after their spot runners received shutdown signals at the same time. Another server shard passed 2,419 tests, but its unchanged 103-record cleanup backlog case exceeded the 15-second limit. The unchanged full cleanup suite then passed all 29 tests locally. One unchanged retry of only the seven unsuccessful jobs passed. The [original cancellation and timeout logs](https://github.com/paperclipai/paperclip/actions/runs/41724898038/attempts/1) remain in the workflow history. No cleanup implementation or test was changed.
- The signed-in hosted UI reproduced the failed Retry before the fix. The fixed path is covered through the real HTTP Retry route and assembled heartbeat/recovery services. This PR has not been deployed to the hosted incident. A live repaired Retry remains unverified until rollout.

## Risks

- Automatic continuation must not repeat provider work. It requires positive preparation evidence and rejects retained provider, process-launch, coordinator, result, live-controller, unsafe lease, or mismatched workspace evidence.
- Repeated recovery calls must create one successor. Tests cover concurrent requests, repeated startup visits, pending Retry, retry exhaustion, and changed task ownership.
- Sentry delivery state stays local to the process. A restart or cache eviction can report a failure again. An unconfirmed event needs a later task read to retry.
- No schema or API contract changes are required.

## Model Used

OpenAI GPT-6 through Codex. This session does not expose a more specific deployment ID or context window size. Reasoning, code editing, test execution, and browser diagnostics assisted the change.

## 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-11-03 16:10:49 -06:00
DottaandPaperclip 2920d88034 fix: preserve cancelled runs through native finalization (#18857)
## Thinking Path

> - Paperclip helps people manage AI agents that do work.
> - Native runs can accept a result before workspace file finalization
ends.
> - A user can Stop the run while file finalization is still pending.
> - The direct finalizer replaced that cancelled run with the earlier
provider status.
> - The restart projection already preserves a cancelled run.
> - This pull request preserves cancellation in the direct projection
too.
> - Saved results and normal task decisions remain available.

## Linked Issues or Issue Description

**What happened?**

An accepted successful result can wait for workspace finalization. If
run-only Stop completes first, the run and its source wake become
cancelled. Late finalization then changed the run to succeeded, cleared
the cancellation error, and replaced its finish time. The wake remained
cancelled.

**Expected behavior**

Preserve the winning run cancellation, finish time, errors and audited
acknowledgement. Keep the accepted provider result and complete file
finalization. The server task arbiter still decides task status from the
accepted result. A valid result can finish the task even though the
execution was stopped.

**Steps to reproduce**

Accept a native result through the control-plane port. Hold an actual
workspace-finalization operation. Stop the run through the heartbeat
service with a board request identity. Release the operation and call
the direct finalizer. The new database regression exercises this
sequence.

Related work: #18804 settles exact claimed native wakes. This change
preserves a wake that Stop already cancelled. #15212 and #12301 address
the opposite race in legacy cancellation. They do not change this native
finalizer projection.

## What Changed

- Evaluate run status and errors against the current locked database
row. Preserve a cancelled run when a late accepted result finishes
finalization or when workspace/status-decision retries exhaust. Merge
finalization failure metadata into the current row so concurrent
receipts survive.
- Keep finalization receipts and accepted results. Preserve existing
server task-arbiter behavior and accounting fields.
- Add real database tests for successful and failed accepted results,
delayed workspace work, and Stop after the task decision but before run
projection.
- Check cancelled wakes, audited acknowledgement, no successor, restart
projection and idle recovery. Update execution documentation.

## Verification

- Before the production change, the actual producer sequence failed: the
cancelled run became succeeded, its error code became null, and its
finish time advanced. This failure remains saved privately.
- The focused projection, cancellation-request and Stop-accounting
suites pass all 67 tests. These include the same delayed-finalization
sequence, both task status orderings, and complete and late accounting
receipts without another provider turn.
- Independent review found a second terminal writer in retry exhaustion.
On the unchanged previous head, two real database cases changed a
cancelled run to failed or succeeded after workspace or status-decision
retries. A third case let actual board Stop win after the permanent
workspace failure admission snapshot and still changed cancelled to
failed. All three now pass. Existing uncancelled exhaustion controls
still pass. No mock supplied the canonical cancellation state.
- Initial review found that routine regression notes do not belong in
the feature maps. Both map additions were removed. Production and test
code are unchanged by that correction.
- Runner TypeScript build and all server typecheck layers pass. Map,
module-boundary and diff checks pass.
- An additional direct typecheck of the normally excluded projection
fixture reports two existing notification mocks whose transaction
parameter is typed as `object`. The same check with the previous test
file reports the same two errors. The new regression cases add no type
errors. The standard server typecheck remains green.
- The previous head passed required CI after one targeted retry of jobs
that received the same runner shutdown signal. Those original failures
remain recorded. The exhaustion correction passes fresh required CI on
exact head `84404d34b9d982d8bca24b63ae2046051f5169b8` (workflow
41726120483, attempt 1). Both required aggregate gates pass. Greptile
reviewed this head at 5/5 with no new comments; all review threads are
resolved. An independent source review found no blocking issue in the
correction. Runner TypeScript and all server typecheck layers pass
locally; the exact production server and Runner build passes with the
server stamp bound to `84404d34b9d982d8bca24b63ae2046051f5169b8`. The
full required CI workflow passes.
- This is local fixture evidence. It does not establish hosted recovery,
a remote provider stop, or a deployed version.

## Risks

- Accepted result finalization can still finish the task after run-only
Stop. This is existing server policy; cancelled run API write authority
stays revoked.
- The fix protects cancelled execution history. It does not invent
provider termination, saved-file or accounting proof.
- No schema, endpoint, retry budget, provider call or new background
worker changes.

## Model Used

OpenAI `gpt-6.1-sol`, Extra High reasoning. The model used code
execution, repository tools and test output. 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
- [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-11-03 16:10:21 -06:00