Commit Graph
6266 Commits
Author SHA1 Message Date
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
Devin FoleyandPaperclip b0c4e34526 fix(native): resume from the owner after an unstarted failed follow-up (#18860)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native Runner sessions keep one conversation across task turns.
> - A follow-up turn resumes from the newest earlier run that holds a
provider checkpoint.
> - A follow-up can fail during admission, before provider work, and
still hold the checkpoint that it inherited.
> - A later Retry then resumes from that failed run. Controller recovery
finds no controller checkpoint there and fails.
> - This pull request makes resume source selection skip a failed
follow-up that proves that it never started provider work.
> - The benefit is that a board Retry resumes from the last committed
owner, and the task can continue.

## Linked Issues or Issue Description

Refs #18846, which lets a follow-up start after a recovered per-turn
owner. Refs #18854 (same-run retry of failures before provider work) and
#18718 (controller projection refresh).

**What happened?**

Run 1 was a per-turn remote sandbox run. Recovery committed its result,
but it saved no Runner suspension receipt. Run 2 was a follow-up that
was admitted before #18846. Admission failed with
`runner_state_identity_mismatch:
prior_owner_terminal_state_indeterminate`, before any provider work. Run
2 has these properties:

- Status `failed`, and the result has `executionRecovery: { kind:
"bootstrap", providerWorkStarted: false }`.
- The coordinator is `terminal_failure`, with no result, no lease owner,
and no controller process.
- No controller checkpoint rows and no controller recovery pointer.
- A `sessionCheckpoint` bound to run 2 with a null cursor. The provider
session in it is copied from run 1.

A board Retry then started run 3. Resume source selection treats any
checkpoint as a progress barrier, so it selected run 2. Controller
recovery then failed with
`native_controller_recovery_unsupported:metadata_format`, because run 2
has no controller recovery pointer. The task stayed blocked. Every later
Retry selects the newest failed follow-up and fails the same way.

**Expected behavior**

A Retry must resume from the last committed owner (run 1) when the newer
follow-up provably never started provider work. If the follow-up might
have started provider work, or holds its own controller evidence, it
must stay the source.

**Steps to reproduce**

1. Commit a per-turn remote owner through recovery, without a suspension
receipt.
2. Admit a follow-up, and make its admission fail with
`prior_owner_terminal_state_indeterminate`.
3. Settle that follow-up as a failed bootstrap: `providerWorkStarted:
false`, `terminal_failure`, and no result.
4. Admit a Retry. It selects the failed follow-up and fails with
`native_controller_recovery_unsupported:metadata_format`.

The new regression test runs these steps against PostgreSQL.

**Paperclip version or commit**

Reproduced on `909d86b59a`.

**Deployment mode**

Native Runner with a per-turn lifecycle on the built-in remote sandbox
transport. Source selection is shared by all native providers.

## What Changed

- `findNativeSessionResumeRun` skips a newest barrier that is an
unstarted inherited follow-up and selects again without it. The selector
repeats this inside the existing 32-run window.
- A run is skipped only when all of these are true:
- It is a native run with status `failed` and a `bootstrap` receipt with
`providerWorkStarted: false`.
- Its coordinator is `terminal_failure`, with no result, lease owner, or
controller process.
- It has no process identity, provider event, controller checkpoint row,
run result row, or controller recovery pointer.
- Its checkpoint is bound to this run and session, and it has no cursor.
- After the skips, every skipped run must name the same backend, driver,
and provider session as the selected source. Otherwise the selector
returns the original barrier.
- Runtime selection and controller recovery both call this selector.
Both now use the committed owner. Admission then reaches the #18846
path, which retires the old authority and starts a new Runner.
- Add four cases to `native-recovered-owner-followup.test.ts`, with one
negative control for each guard.
- Document the rule in `doc/execution-semantics.md` and the test in
`doc/testing-native-remote-continuity.md`.

### Why source selection

- Source selection made the wrong choice. The failed follow-up has no
provider progress. Its checkpoint is a copy, and the provider never saw
its turn.
- One shared selector feeds runtime selection (checkpoint rebind,
previous turn, runner instance) and controller recovery. A fix in only
one of them would let them disagree about the source.
- Controller recovery keeps its rule: it never searches backward when
the selected run is incomplete. The selector skips a run only with
positive proof, so an incomplete run with any evidence of work still
stops recovery.
- A fix in the executor could not help. The executor sees only the
source that the selector gives it.

## Verification

- `cd server && npx vitest run
src/services/native-runtime/native-recovered-owner-followup.test.ts`: 22
tests pass.
- Before the change, the two new retry tests failed with
`native_controller_recovery_unsupported:metadata_format`.
- After the change, the retry selects the committed owner and is
admitted. It is also admitted after two failed follow-ups.
- Eight controls keep the failed follow-up as the source: process
identity, provider event, controller checkpoint row, a receipt with
`providerWorkStarted: true`, a coordinator with a controller process, a
run result row, a different provider session, and a non-null cursor.
- I disabled each guard in turn. Each change made its matching control
fail.
- These suites pass: native session resume, resume projection,
controller recovery (unit and integration), native startup retry,
runtime selection, execution workspace service, native status corpus,
task workspace binding, and all 1,088 native executor tests. The
container and Runner binary cases skip locally.
- Server typecheck, `node scripts/typecheck-low-memory.mjs`, `pnpm
check:module-boundaries`, and `git diff --check` pass.
- I did not run the full `pnpm test:run`, `pnpm build`, or the opt-in
Linux Runner container cases locally. CI runs them.
- The tests start at the selector and executor admission. They do not
exercise the Retry route, the task retry queue, or a live sandbox
provider.

## Risks

- A wrong skip could resume from an older checkpoint. Each guard
requires positive evidence of no provider work. A skipped run must also
name the same provider session as the selected source. If any check
fails, the selector returns the original barrier.
- Only `failed` runs qualify. Cancelled, interrupted, and timed-out runs
keep their existing handling.
- The skip adds one proof query per candidate. It runs only when the
newest barrier is a failed native run whose checkpoint is bound to
itself and has no cursor.
- The rebind now uses the owner's tool-contract fingerprint. If the tool
contract changed since the owner ran, the Retry starts a new provider
session. The provider history in this failure mode is already
unavailable.
- No migration or configuration change is required.

## Model Used

- Anthropic Claude Opus 5.5 (model ID `claude-opus-5-5`) in Claude Code,
with extended thinking, repository tools, shell and code execution, and
local test runs. The runtime does not expose the context window size.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 14:02:36 -08:00
DottaandPaperclip a80eec1164 fix(recovery): continue Retry after completed file preservation (#18845)
Reuse completed file preservation for task Retry and show bounded admission explanations, including newer saved messages and identifier routes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-11-03 16:01:27 -06:00
DottaandPaperclip 519b345d69 fix(ui): expose governed retained-file recovery (#18848)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The control plane must preserve saved work before it admits another
attempt.
> - Retained-file recovery already has an authorized API and a bounded
worker.
> - The task UI hid that operation when workspace repair blocked
execution.
> - The run inspector could check source identity but could not request
preservation.
> - This pull request exposes that existing operation with its exact
source binding.
> - The worker still owns file verification, stop receipts, and
execution release.

## Linked Issues or Issue Description

Refs #18678 and #18803 for the existing retained-source and continuation
contracts.

**What happened?**

A failed native run could leave an active, system-owned export recovery
action. The task showed a generic Retry control. Its workspace repair
gate correctly refused another provider turn. The run inspector offered
only read-only source inspection. The authorized file-preservation API
had no production UI caller.

**Expected behavior**

An authorized board operator can request preservation of the exact
retained files. The UI keeps the repair hold until the server records
verified preservation. A lost response must keep the same request
identity.

**Steps to reproduce**

1. Use an active retained-export action whose workspace recovery is
blocked and retryable.
2. Open its source task while `workspaceRepairRequired` is true.
3. Open the exact failed run and inspect recovery.
4. Before this change, neither surface can request the existing
file-preservation operation.

**Paperclip version or commit**

The focused regression fails on master
`1f55073f96b2485f49f98ee8c261aa14ec2f79ac` before the production UI
edits.

I checked the roadmap and searched open public PRs for this UI repair.
No duplicate was found. This is a repair to the existing recovery
capability.

## What Changed

- Add Recover saved files to the existing task recovery area and
exact-run inspector. Reuse the current board task-mutation access check
and the existing `recoverWorkspace` API.
- Read the exact action, run, company, task, and source observation
again before sending. Refuse changed, retired, foreign, unverified, or
non-retryable sources.
- Save one account-scoped request before POST. Keep its key through a
lost response and reload. Reconcile with GET before an explicit resend.
Use a new key only after an explicit click and a fresh, confirmed
exhausted attempt; do not resend automatically.
- Show queued, recovering, blocked, and verified receipt states
separately. A request acknowledgement does not mark files saved or
resolve the task hold. Hide generic Retry for native export holds. Keep
the existing Retry path for legacy restoration.
- Cover both mounted surfaces and negative access, source, account,
persistence, duplicate-click, and lost-response cases. Update the
existing design guidance and correct the affected run-detail recipe
only.

## Verification

- RED: the new component regression fails with `missing Recover saved
files` before production changes. The original failure is retained.
- GREEN: 265 tests pass across
`WorkspaceExportRecovery.control.test.tsx`,
`WorkspaceExportRecovery.test.tsx`,
`RunWorkspaceRecoverySurface.test.tsx`, and the full
`IssueDetail.test.tsx` suite.
- Review RED/GREEN: the prior UI reused the failed key after a confirmed
exhausted attempt. A focused regression now proves a different key for
an explicit new attempt, while the lost-response controls still prove
key reuse. Mounted task controls prove that verified native preservation
stays available, unverified or stale native sources never offer generic
Retry, and viewers cannot mutate. A separate RED against the exact prior
UI proves the lost legacy control. Its GREEN clicks the existing legacy
Retry API while the workspace hold remains active.
- Full UI TypeScript check passes: `node scripts/run-tsc.mjs -p
ui/tsconfig.json --noEmit`.
- `node scripts/check-token-gates.mjs`, `node scripts/check-maps.mjs`,
and `git diff --check` pass. The map check proves structure only.
- One early legacy test probe used an unsigned fixture and a readonly
array; those setup failures are retained. The corrected same-user
fixture proves the source RED and GREEN. The first test command used an
unnamed project filter and stopped before loading tests. The corrected
package configuration produced the recorded RED. One initial storage
fault probe did not intercept jsdom's storage method; the corrected
prototype fault proves that unavailable storage sends no POST.
- Dependencies use Node 24.22.2 and a frozen pnpm 9.15.4 install with
scripts disabled. The maintained shared and SDK prerequisite builds pass
on this base. No dependency or lockfile changes are included.
- A later Paperclip Review follow-up found the plain-HTTP UUID gap. Its
focused regression fails before POST with the prior direct call, then
passes with the existing UUID helper. The same storage, lost-response,
retry and source controls remain green.
- Original full CI `41715060582` fails two Cloud-auth task-entry browser
checks at their existing heading assertion. The retained original
artifacts show a late task pane (one already contains the heading; one
still shows Loading conversation). The cause remains unproven. No auth,
fixture, timeout or assertion change is included. Fresh Greptile is 5/5
with zero open threads and Paperclip Review passes on the UUID fix head.
Current full CI `41720068249` attempt 1 failed. Three workers were
terminated by AWS Spot capacity loss. The CLI startup and dense-ZIP
checks also timed out at their unchanged deadlines. The CLI/shared input
trees match a previous passing CI job, and the dense-ZIP case passes one
focused local check. The CLI timeout cause remains unknown. The
coordinator requested one bounded recheck. Attempt 2 succeeds on the
same source, with all current checks green. No source, fixture, timeout
or assertion changed for that recheck. The worker issued no retry. This
pass does not establish the original CLI timeout cause.
- Full recursive typecheck, repository tests, and application build were
not run locally under the bounded UI verification scope. Full CI is
requested.
- No live preservation request, provider turn, deployment, or sandbox
cleanup was performed for this repair. Original saved-file recovery
remains unverified until rollout and real acceptance.

## Risks

The client access check is a display guard. The existing server
authorization and physical source, ownership, stop, and snapshot checks
remain authoritative. Unavailable browser storage blocks the request
before POST. A failed acknowledgement retains the request for its owner.
The UI never calls task Retry, recovery resolution, reconstruction,
provider execution, or destructive cleanup. The existing worker retains
its bounded recovery policy and may finish the saved result after real
preservation.

## Model Used

OpenAI Codex, GPT-6 family, with repository inspection, code editing,
and test execution. The exact runtime model ID and context-window size
are not exposed to this agent.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 16:00:03 -06:00
DottaandPaperclip f187de99aa Preserve domain deadlines across owned idle sleep (#18656)
Reviewed and verified at 9b4acc456d.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-11-03 15:40:56 -06:00
DottaandPaperclip 0a1e4c3d0f fix(workspaces): retry pristine remote setup timeouts (#18852)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A remote task needs a workspace before its model can start.
> - Fresh workspace preparation can reach its transport deadline.
> - Failure handling records that timeout as a generic setup failure and
stops the task.
> - An unpublished workspace has no saved task work to repeat.
> - This change routes that narrow failure through the existing bounded
retry scheduler.
> - Saved work, retained executions, and normal admission gates keep
their existing protection.

## Linked Issues or Issue Description

Refs #18827, which preserves remote timeout diagnostics. This change
builds on that merged repair. Related #16727 concerns controller-managed
clones and optional partial clones; this change keeps remote clone
contents and deadlines unchanged.

**What happened?**

A fresh remote workspace reached its 300-second command limit before
model execution. The task then stopped with `setup_failed` and no
scheduled continuation. A separate full clone completed in 137 seconds
on a disposable Daytona sandbox. The original delay remains unexplained.
This change repairs the missing recovery path; it does not claim to
remove the underlying delay.

**Expected behavior**

A fresh, unpublished workspace preparation timeout can use the existing
two automatic retries. Retry scheduling remains durable and company
scoped. Stop, pause, dependencies, budgets, task ownership, saved work,
and retained native execution must still prevent unsafe work.

**Steps to reproduce**

1. Start preparation for a fresh remote workspace with no saved version
or published allocation.
2. Make its initial directory/clone command return a transport timeout.
3. Observe that the base code records a generic setup failure without
bounded recovery.
4. With this change, the owner validates current workspace authority and
preserves a specific bootstrap failure for the normal retry scheduler.

**Paperclip version or commit**

Base: `e0276e0166`, including #18827.

**Deployment mode**

Native Paperclip Runner with sandbox workspace storage. Verification
uses real PostgreSQL, filesystem commands, and a separate live Daytona
transport probe.

## What Changed

- Recognize only the initial preparation timeout while the same company,
writer, and epoch still own a pristine workspace.
- Keep restore, additional repository, provisioning, save, changed
ownership, dirty workspace, and retained native execution failures
outside this recovery policy.
- Persist the typed failure before using the existing retry scheduler,
admission gates, and attempt budget.
- After successful terminal lease cleanup, reclaim an unpublished
bootstrap-only reservation even when a provider returns no stop receipt.
Preserve receipt requirements for saved/published work, unknown
execution, and native identities; do not synthesize termination
evidence.
- Prevent generic recovery from granting another budget after
exhaustion. Keep a specific recovery notice.
- Add regressions for persistence, concurrent scheduling, restart,
exhaustion, Stop, pause, dependencies, fresh allocation, lost replies,
and saved output after cold restoration.
- Document the recovery contract.

## Verification

- Red: the real-PostgreSQL workspace regression first failed because the
timeout had no recovery classification. The completion regression then
failed because it became `setup_failed`. A retained-native negative
failed before the admission guard. The review follow-up reproduced a
failed successor reservation after production lease release returned
void.
- Green: all 127 transport/archive/real-PostgreSQL workspace cases pass
after the review repair. All 12 receipt/void handoff cases pass against
the final query. The 141 complete completion/retry/recovery cases and
the fresh-allocation selection case pass. Safety cases cover concurrent
scheduling across restart, the two-attempt cap, Stop/pause/dependency
gates, native identities, saved/published files, active leases, failed
cleanup, and cold-restored output.
- `node scripts/typecheck-low-memory.mjs` and `pnpm build` pass at
`ba96cff771707415e4a5e893c8b3a97499ead25b`. `git diff --check` and the
module-boundary check pass. The full local test wrapper completed all
1,835 chat cases across six partitions, then was interrupted during its
serial general-server phase after the complete current-head CI suite
passed. Its interrupted run is not a full local-suite pass; its log is
retained. The owned process tree and database children exited.
- The [complete GitHub Actions
run](https://github.com/paperclipai/paperclip/actions/runs/41718108375)
is successful at this exact head, including all server/workspace/chat
partitions, all nine serialized groups, all eight browser shards, build,
typecheck, native Runner, package, Rust, and release-policy checks. The
first Rust worker shut down during cache restore before tests; one retry
passed without source changes. Expected Storybook skips remain skips.
- Earlier failures remain accounted for: the first typecheck found two
incomplete process-result fixtures, now corrected. An intermediate
completion run exposed a test double that returned no promise and a
teardown timeout; the complete 141-case suites subsequently passed. The
first-head CI had four worker shutdowns and one worker communication
loss. A three-second monitor settlement wait failed in CI and locally,
then all 21 cases passed unchanged at base and head. An earlier CI retry
found a real-process history adoption assertion failure; that exact case
passed locally at base and head. Both server groups pass in final-head
CI. These timing causes remain unknown. [Earlier CI
evidence](https://github.com/paperclipai/paperclip/actions/runs/41713712522).
- Live supporting evidence: unchanged production preparation code
completed a full repository clone in 137 seconds using Daytona SDK
0.203.0, Node 24.22.2, Git 2.39.5, and Linux x86_64, with 1 CPU, 1 GiB
memory, and 1 GiB disk. The approved diagnostic account could not access
the original fleet snapshot, so the probe used public
`node:24-bookworm`. Both probe allocations were deleted and absence
verified. Full task acceptance at the fixed revision remains unverified:
the original fleet snapshot and an explicitly configured disposable
Codex subscription fixture were unavailable. No CLI login or hosted
subscription was copied.
- Greptile is 5/5 at the final head with no actionable findings. The
Paperclip review finding is repaired, replied to, and resolved. The
separate **Paperclip Review** check remains queued. Its task failed
workspace export after publishing the first assessment; the staging
service was then observed showing a workspace recovery page after an
update failure. Subsequent browser rechecks timed out, including a fresh
tab, so the normal recovery control could not be used. Reconcile that
existing export after service access returns, then let the saved
current-head review request run. That external review is the remaining
check; this PR is not yet fully green.
- The original hosted task was last verified blocked. This PR has not
been deployed and the task has not been retried by this work.

Reviewers can run `pnpm test:run`, `node
scripts/typecheck-low-memory.mjs`, and `pnpm build`. The
remote-workspace integration suite additionally needs an owned
disposable PostgreSQL database named `remote_workspace_*test` through
`PAPERCLIP_REMOTE_WORKSPACE_TEST_DATABASE_URL`; otherwise that suite is
skipped.

## Risks

Each retry keeps the existing preparation deadline, so persistent slow
clones can consume the bounded budget before intervention. A retry
receives fresh allocation admission; it does not replay the command
against the old allocation. The workspace owner checks persisted
authority before classifying the failure, and normal admission still
rechecks that authority. Historical generic setup failures do not gain
automatic replay. No database migration, provider-pack update, model
fallback, credential change, or deployment is included.

## Model Used

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

## Checklist

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

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native runs start the provider through Runner. `session.open` starts
the provider session before any prompt.
> - When `session.open` fails, the server records the generic
`native_session_interrupted`. Recovery evidence then reports
`ambiguous_state`, so the first failure blocks the task with no
automatic retry.
> - The failure is often temporary. On one instance an agent's provider
account hit its usage limit. For about 14 hours every later run failed
at `session.open`, and 47 tasks stayed blocked. Outside limit windows,
the same error also occurs once and a later retry succeeds.
> - The server already has a bounded same-run bootstrap retry for
failures that are proven to come before provider work. It did not
recognize this failure, and it did not consider a known provider limit.
> - This pull request classifies a definite `session.open` failure as a
pre-session failure. Bootstrap retries back off and wait out a known
provider limit. The task is blocked only after the shared budget is
used, with a clear reason.
> - The benefit is that a temporary startup failure or a provider limit
no longer strands tasks on the first failure, and every existing replay
guard stays in force.

## Linked Issues or Issue Description

Refs #18847. That pull request keeps the provider cause on
`session.open` rejections and names typed categories (for example
`classification=provider_rate_limit`). This change does not depend on
it. With #18847, a typed limit at `session.open` uses the same cooldown
path, because the server reads the cause with
`providerFailureFromError`. Refs #18622, which added the
`ProviderFailure` cause. Refs #13993, an open change about legacy
adapter startup backoff; it does not touch the native recovery path.

**What happened?**

A native run (`paperclip_runner`) failed at `session.open` before any
prompt: `ACPX sidecar command session.open was rejected
(retryable=false, classification=session_ensure_failed)`. The server
recorded `native_session_interrupted`. Recovery evidence had no
checkpoint and no provider event, but the cause was not in the
pre-session list, so the mode was `ambiguous_state`. The coordinator
went to `terminal_failure`, a board-owned `active_run_watchdog` action
was created, and the task was set to `blocked` on the first failure.
During a provider usage limit, every later run of the agent failed the
same way, so each task was blocked.

**Expected behavior**

A run that provably failed before provider work retries automatically,
with backoff and a bounded budget. If the agent has a known provider
limit, the retry waits for the cooldown. The task is blocked only when
the budget is used, and the recovery action explains why. A run where
provider work may have started is never replayed.

**Steps to reproduce**

1. Make Runner reject `session.open` for a native run (for example a
provider at its usage limit).
2. Observe the coordinator: `terminal_failure`, `recoveryMode:
"ambiguous_state"`, `providerSessionEstablished: false`. The task is
`blocked`.
3. The new `native-startup-retry.integration.test.ts` reproduces this
with a real database and the executor.

**Paperclip version or commit**

`master` at `5877d751c0e`.

**Deployment mode**

Native Runner with an ACPX Claude provider. The fix is in the server and
is not specific to one deployment mode.

## What Changed

- `nativeSessionFailureSourceCode` returns a new cause,
`provider_session_open_rejected`, when Runner reports that
`session.open` failed or was rejected. Those outcomes are definite, and
the command runs before any prompt. An `indeterminate` outcome stays
`native_session_interrupted`. Earlier, more specific causes (for example
a missing runtime package) keep their classification.
- `nativeProviderRecoveryEvidence` treats the new cause as pre-session.
The durable evidence still decides. A bootstrap retry happens only with
no provider session, no provider event and no checkpoint. Otherwise the
mode stays `ambiguous_state` and the task is blocked as before.
- New module `native-runtime/native-startup-retry.ts` plans each
bootstrap retry. It is called only for `bootstrap_retry` mode.
- Backoff: the existing `infrastructureRetryDelayMs` (about 30 s, then
about 60 s, plus or minus 20 %). The jitter derives from the run and
attempt, and it is persisted with `nextAttemptAt`.
- Limit cooldown: evidence is a `rate_limit` or `quota_exhausted`
provider failure on the current failure (the #18847 shape), or on an
agent run that started in the last 24 hours. A
`native_provider_usage_limit` or `provider_quota` error code also
counts. The cooldown is the provider's `retryAt` (or `retryNotBefore`),
or one hour after the failure when no reset time is known. A later
successful run of the agent clears older evidence. The cooldown is
capped at 24 hours. The retry runs after the cooldown plus the backoff
delay.
- A failed evidence read is logged and falls back to plain backoff, so
it cannot stop the failure from settling.
- The budget does not change: three provider attempts, shared with
checkpoint resumes and replacements. When it is used, the coordinator
records `native_session_retry_exhausted`. The task is blocked, and the
board-owned action says: "Provider startup failed on 3 attempts before
any provider work began. Automatic retries are stopped." When a limit is
known, the message includes the expected reset time.
- While a retry waits, the coordinator detail, the recovery action
evidence and the scheduled wake record `providerCooldownAt`. The next
action names the reset time, and the task projection shows "Retry
scheduled".
- `PROVIDER_LIMIT_DEFAULT_COOLDOWN_MS` (one hour) moves to
`system-recovery-state.ts`. The legacy provider-quota recovery default
now refers to it. The value does not change.
- A chat retry of an exhausted `provider_session_open_rejected` run is
accepted like the other pre-provider causes. The existing proofs still
apply.
- `doc/execution-semantics.md` describes the backoff, the cooldown and
the exhausted state.

## Verification

- Red: before the fix, the first new integration case failed. The
coordinator was `terminal_failure` with `recoveryMode:
"ambiguous_state"`, `originalFailureCode: "native_session_interrupted"`
and `providerSessionEstablished: false`, and the task was `blocked`.
Four more cases failed the same way. The two never-retry cases passed
before and after the fix.
- Green: `npx vitest run
src/services/native-runtime/native-startup-retry.integration.test.ts` in
`server` (embedded PostgreSQL, the real executor, and the real resume
claim). 8 cases pass:
- a rejected `session.open` is retried with backoff (attempt 1 in 24 to
36 s, attempt 2 in 48 to 72 s), and the task stays `in_progress` with an
agent-owned action and a `resume_native_run` wake;
- after three attempts the task is blocked, the action is board-owned
with the clear reason, and the resume claim refuses another attempt;
- a recent limit failure holds the retry until one hour after that
failure, and the claim refuses it before the cooldown;
- a provider reset time from an earlier run, or from the startup failure
itself, sets the cooldown;
  - a distant reset time holds the retry for at most one day;
- a later success, another agent's limit, and an expired limit do not
delay the retry;
- a durable provider event, or an indeterminate `session.open` outcome,
still blocks the task with `ambiguous_state` and no retry.
- `native-session-executor.test.ts`: all 1,087 tests pass, including a
new classification case for `failed`, `rejected`, `indeterminate` and a
different command.
- `chat-channels.integration.test.ts -t "requires terminal-safe native
ownership for an exact chat retry"`: 23 pass, including the new
`session_open` case. That case fails without the list change.
- Related suites pass: native session resumption, native provider
failure, capacity and overload integration tests, provider failure
classification, native status arbiter corpus, native restart recovery.
- `npx tsc --noEmit -p server` passes. `git diff --check` passes.
- [GitHub
CI](https://github.com/paperclipai/paperclip/actions/runs/41713988964)
is green on head `32561aa335d`. This includes `ci / verify`, `ci / e2e`,
typecheck, build, all server, chat and workspace groups, all serialized
server suites, and all Runner checks, which run the native-runtime
server tests. In the first attempt, eleven jobs stopped because their
runners received a shutdown signal; no test reported a failure. A rerun
of those jobs passed.
- A local run of the whole `server/src/services/native-runtime`
directory was stopped before completion because it runs serially for a
long time. CI ran those files.
- Runtime dependency: the new work is one indexed query (`company_id`,
`agent_id`, `started_at`) with `LIMIT 20`. It runs only when a bootstrap
retry is planned or exhausted, not on the normal path. No new timer or
polling: the wait uses the existing persisted `nextAttemptAt` deadline
and the existing resume dispatch.
- Not verified: a live provider account at its usage limit, and a live
remote sandbox that waits through a full cooldown. The full repository
suite was not run locally; CI ran it.

## Risks

- A task that waits for a cooldown stays `in_progress` with its
execution lock, for up to one day. This is the existing same-run retry
state. Stop, reassignment, pause and an explicit Retry keep their
current behavior through the existing claim checks.
- During the wait the failed run keeps its environment lease in the same
way as any same-run retry. A remote lease with an unexported prepared
workspace is retained by the orphaned-lease sweep until the retry runs.
Other active leases of the failed run are released after the usual five
minutes, and the retry acquires a new one.
- A `rate_limit` failure without a reset time (for example a short HTTP
429) delays only startup-failure retries of that agent, by up to one
hour. Normal runs are not delayed, and a later success clears the
evidence.
- A startup failure that needs user action (for example a sign-in
failure) now uses two automatic retries, about 90 seconds in total,
before the task is blocked. Before, it was blocked at once with an
ambiguous reason.
- The agent's other queued runs are not held during a cooldown. Each one
fails at `session.open` without provider work and then waits for the
same cooldown. An agent-wide admission hold has no clean extension point
in the current queue, so it is not part of this change.
- Re-arming consumed scheduled monitors is out of scope. This change
makes it matter less: a run that consumed a monitor wake now retries
itself on the same run. A monitor that a run consumed before its retry
budget was used stays consumed.
- No schema or API change. The new error code appears on runs and in run
error codes.

> 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
extended reasoning and tool use (shell, file editing, test execution).
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 13:24:59 -08:00
Devin FoleyandPaperclip 9fb06afbfa fix(runner): explain ACPX session-start failures and name typed causes (#18847)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native Runner starts ACP providers (Claude, Codex, Pi and others)
through the ACPX sidecar. `session.open` starts the provider session.
> - When the provider session does not start, Runner reports
`classification=session_ensure_failed`. If the provider failed with a
plain error and not with an ACP `RequestError`, no provider cause
crosses the sidecar. Then the run error, the run events and the run log
do not say why the session did not start.
> - A provider can also type the failure, for example with ACP's
authentication-required code. Runner reported that failure with the same
generic classification. The server could not tell a limit or a sign-in
failure at session start from other startup failures.
> - On one instance, a Claude agent hit its provider usage limit. The
first run failed in the turn with a typed limit failure. Each later run
for many hours failed at `session.open` with only
`session_ensure_failed`. Operators could only guess the cause from the
timing.
> - This pull request keeps a redacted, bounded cause for every session
ensure rejection. It names a typed provider category in the closed
classification.
> - The benefit: an operator can read why a session did not start, and a
later recovery change can treat a typed startup failure by its category.

## Linked Issues or Issue Description

Refs #18622, which added the `ProviderFailure` cause for startup and
turn failures. This change closes the remaining startup gaps. Refs
#17543, which is a related open change to the same classifier. It keeps
the JSON-RPC code range as a closed code. The two changes edit the same
lines, so the second one to merge needs a small rebase.

**What happened?**

A native ACPX run failed before its first prompt with `ACPX sidecar
command session.open was rejected (retryable=false,
classification=session_ensure_failed)`. When the ensure rejection was
not an ACP `RequestError`, the run had no other cause data. When the
provider typed the failure (for example ACP `-32000` "Authentication
required"), the classification stayed the same generic value.

**Expected behavior**

Every session ensure rejection keeps a redacted provider cause that an
operator can read on the run. A typed provider failure keeps its
category in the cause and in the closed classification. Provider text
never goes into the closed error text.

**Steps to reproduce**

1. Make `runtime.ensureSession` reject with a plain `Error`, or with an
ACP error that has code `-32000`.
2. Open a qualified ACPX runtime through the sidecar.
3. Before this change, the plain error has no `providerFailure`, and the
`-32000` error has category `unknown` and
`classification=session_ensure_failed`.

**Paperclip version or commit**

`master` at `e0276e0166c`.

## What Changed

- `acpxProviderFailure` reads ACP's reserved authentication-required
code (`-32000`) as category `authentication`. It also reads the
`errorKind` that Claude ACP puts in ACP error data (`rate_limit`,
`billing_error`, `authentication_failed`, `overloaded` and others). The
category table is now a `Map`, so an `errorKind` such as `constructor`
cannot select an inherited object key.
- The session ensure path in `codex-runtime-adapter.ts` keeps a cause
for every rejection. A rejection without an ACP error keeps its own
message, redacted with the launch environment and the existing
credential patterns, with category `unknown`. An abort reason is Runner
cancellation, so it does not become a provider cause. A non-`Error`
rejection now keeps its cause on the wrapper error.
- `acpx_sidecar_transport.rs`: for `ACPX_SESSION_ENSURE_FAILED`, a
schema-valid cause with a typed category gives
`classification=provider_<category>` (for example
`provider_quota_exhausted`, `provider_rate_limit`,
`provider_authentication`). An untyped or invalid cause keeps
`session_ensure_failed`. Only the validated enum value is used. The
cause's text never goes into the error string.
- `runnerd-codex-transport.ts`: when a command result has a provider
cause, the reported error now keeps Runner's closed ACPX rejection text,
so the classification stays in the run error. Before, it changed that
text to "Provider request failed". Any other message (for example a
Codex `RPC failed: ...` message) still gets the generic text. Provider
text still travels only in `providerFailure`.
- The credential-free Pi probe (`pi-closed-startup.test.mjs`,
`scripts/pi-public-install-probe.mjs`) now expects
`provider_authentication`. Pi reports its missing credential with ACP's
authentication-required error.
- Docs: `doc/run-log-events.md` and
`doc/architecture/runner-pi-capabilities.md`.

**What the Claude ACP agent returns at session start**
(`@agentclientprotocol/claude-agent-acp` 0.73.0 with the repository
patch, and the ACPX 0.13.1 patch):

- `newSession` and `loadSession` call `createSession`. It throws
`RequestError.authRequired` (`-32000`) only for a claude.ai subscription
when gateway auth is hidden. It throws `invalidParams` for a bad `cwd`
and `resourceNotFound` for a missing resumed session.
- Any other error, for example a failed SDK `initializationResult()`,
becomes a generic `-32603 Internal error`. The ACP SDK puts the raw
message in `data.details`, or the parsed JSON body in `data` when the
message is JSON.
- The typed AIR `sessionFailure` (category `limit`, `access` and others)
is sent only on prompt responses and on `session_info_update`
notifications. `loadSession` history replay also sends one for each
earlier usage-limit message, but that notification does not show whether
the limit is still active. The `errorKind` data is attached only by the
prompt loop.
- Thus a usage limit at session start has no typed signal on the ACP
error. The cause keeps the provider's message, and the existing `API
error: NNN` status parse can type it. This change does not add more
free-text matching.

**Shape for a recovery consumer:** the server reads the cause with
`providerFailureFromError(error)`, as it does now. A startup cause has
`phase: "startup"`, a `category` from the `ProviderFailure` schema and
its `retryability`. The run error keeps
`classification=provider_<category>` for a typed ensure failure, or
`session_ensure_failed` when the category is `unknown`.

## Verification

- `npx vitest run src/drivers/acpx src/cli
src/drivers/provider-failure.test.ts
src/live/runnerd-codex-transport.test.ts
src/live/runnerd-codex-transport-settlement.test.ts` in
`packages/paperclip-runner`: 103 of 104 files passed, 2214 tests. The 2
failures are in `installation-integrity.test.ts`. They fail the same way
on unmodified `origin/master` in this checkout (a temporary `server.js`
cannot resolve a module).
- New tests: a plain ensure error keeps its message with the
launch-environment secret and an `sk-ant-` value redacted; a `-32000`
ensure error gives category `authentication`; a non-`Error` rejection
with `errorKind: "billing_error"` gives `quota_exhausted`; an aborted
handshake gets no cause; typed and untyped ACP errors; a closed ACPX
rejection keeps its classification and a changed one gets the generic
text.
- `npx vitest run src/control-plane/acpx-controller-recovery.test.ts`: 8
passed.
- `cargo test -p paperclip-runner-core --no-fail-fast`: all passed. New
unit test: typed categories give `provider_<category>`; `unknown`, no
cause, a category outside the schema and an extra field each keep
`session_ensure_failed`; other closed codes keep their own
classification. New pipe test through the fake sidecar: the cause
survives the rejection, the classification is typed or generic, and the
cause text is not in the error string. In the first full run, two
`stdio_mcp` relay tests timed out. They passed in the second full run.
They do not use the changed code.
- `node --test test/acpx-sidecar-contract.test.mjs
test/protocol-contract.test.mjs test/pi-closed-startup.test.mjs`: 29
passed. The Pi probe is skipped without the Pi distribution.
- `pnpm --filter @paperclipai/paperclip-runner typecheck` (TypeScript,
`cargo fmt --check`, `cargo check`): passed. `pnpm --filter
@paperclipai/paperclip-runner build`: passed.
- Server, after the runner build: native provider failure, run
completion, task interruption presentation, Antigravity probe and the
related native session executor tests: 639 passed.
- Not verified: a live Claude account at its usage limit. I could not
run the credential-free Pi probe, because this checkout has no Pi
distribution. Its new expectation follows from the Pi patch, which
raises `RequestError.authRequired` for the missing credential.

## Risks

- The run error for an ACPX startup rejection with a cause now shows
Runner's closed rejection text, not "Provider request failed". This text
has only closed codes and is the same text that the run error showed
before #18622. Provider text stays in `providerFailure`.
- A plain ensure error message now becomes a displayed provider cause.
Redaction applies before the 4,000-character bound. A new credential
format that the patterns do not know can still need a new pattern. The
same risk applies to all provider causes.
- The classification of a typed ensure failure changes from
`session_ensure_failed` to `provider_<category>`. No server code matches
`session_ensure_failed`. The Pi probe expectation is updated.
- The category and retryability are evidence only. This change does not
change retry policy.

## 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
- [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 13:24:36 -08:00
Devin FoleyandPaperclip 80c308f872 fix(runner): admit follow-ups after recovered per-turn owners (#18846)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native Runner sessions keep one conversation across task turns.
> - After a per-turn sandbox turn, the controller saves a Runner
suspension receipt or a failover backup.
> - A full sandbox disk can stop that save after the turn is complete.
> - Recovery then commits the result, but the controller has no proof
that the Runner suspended.
> - Each follow-up rebuilds the old controller journal from the database
and rejects it again.
> - This pull request lets admission retire that old authority when the
prior owner is provably finished.
> - The benefit is that the task continues with its saved workspace and
does not stay blocked.

## Linked Issues or Issue Description

Refs #18705 (checkpoint save on a full sandbox disk). Refs #18829,
#18718 and #18343 (related admission and controller recovery). Refs
#18811 (a related warm continuation wait).

**What happened?**

A per-turn native run in a remote sandbox finished its turn. The
post-turn `harness_state.backup.persist` and
`session.checkpoint.persist` steps failed because the sandbox disk was
full. The controller got no Runner suspension receipt and no failover
backup. Workspace recovery later saved the files. The run committed its
result and ended as `succeeded`. A board comment then started a
follow-up in a new sandbox. Admission failed after about two minutes
with `runner_state_identity_mismatch:
prior_owner_terminal_state_indeterminate`. Execution recovery then
blocked the task.

The cause is in durable-root admission.
`restorePerTurnRunnerdController` rebuilds the prior controller journal
from the database checkpoint. `verifyPriorRunnerdStateForSessionScope`
finds a terminal owner in the same scope with no suspended lifecycle and
no retained warm owner. It returns `terminal_state_indeterminate`.
Admission quarantines the root and fails. The database checkpoint does
not change, so every later admission rebuilds the same journal and fails
again.

A board Retry after that failed follow-up does not repeat the same
error. Runtime selection picks the failed follow-up as the resume
source, because it carries an inherited provider checkpoint. Controller
recovery then fails with
`native_controller_recovery_unsupported:metadata_format`. The task stays
blocked. This pull request prevents that failed follow-up. It does not
change source selection (see Risks).

**Expected behavior**

When the prior owner is provably finished, the follow-up must start. It
must keep the saved workspace. It must not start while the prior owner
can still be alive, while terminal delivery or provider cleanup is
unsettled, or while quarantine holds evidence from a different owner.

**Steps to reproduce**

1. Complete a per-turn remote sandbox run. Commit its result through
recovery and release its controller lease.
2. Keep its database controller checkpoint and recovery pointer. Do not
save a suspension receipt or a failover backup.
3. Admit a follow-up in a new sandbox with no local controller
projection.
4. Admission rebuilds the old journal, quarantines it, and fails with
the error above.
5. Admit the same run again. It rebuilds the journal and fails the same
way.

The new regression test runs these steps against PostgreSQL.

**Paperclip version or commit**

Reproduced on `e0276e0166`.

**Deployment mode**

Native Runner with a per-turn lifecycle, the built-in remote sandbox
transport, and the ACPX provider. The admission path is the same for
other per-turn sandbox providers.

## What Changed

- Add `retireFinishedPerTurnRunnerdOwner` to durable-root admission in
`native-session-executor.ts`. It runs only when the existing verifier
returns `terminal_state_indeterminate`, and only without a restart claim
or a retained warm owner.
- The function requires all of these. Otherwise the existing quarantine
and rejection apply.
- Per-turn lifecycle on the built-in remote sandbox transport, with a
`created` or `replacement` sandbox. A resumed sandbox, a warm session,
and a plugin task keep their existing paths.
- No local Runner lifecycle evidence for the old owner. The session root
contains only known authority entries.
- A journal for that owner with its terminal delivered and only idle
goal snapshots after it. All commands are completed. Each tool input has
a completed result. No warm transition or staged attachment exists.
- A terminal prior run in the same task. Its coordinator is `committed`
with a result, has no lease owner, and has no recovery state.
- A stopped prior controller, from `evaluateNativeControllerTakeover`
with `requireStoppedController`. A durable controller is fenced by its
released database lease, as in remote warm recovery.
- Each quarantined root for the scope passes the same journal and local
Runner evidence checks for the same owner. A retry rebuilds only the
database journal, so quarantined Runner evidence must stay decisive.
- When the proof holds, the function reads the journal again, moves the
old authority into `continuity-breaks/<time>-<uuid>` in the session
root, and logs one line. The follow-up then starts a new Runner
authority. The provider session follows the existing resume-or-replace
policy.
- Share the list of continuity archive entries with provider replacement
(`archiveContinuityState`). Provider replacement behavior does not
change.
- Add `native-recovered-owner-followup.test.ts`. It uses real
PostgreSQL, a real encrypted controller checkpoint, and the production
source selector and checkpoint rebinder.
- Document the rule in `doc/execution-semantics.md` and the test in
`doc/testing-native-remote-continuity.md`.

### Design choice

I considered two approaches:

1. Start a new Runner authority for the scope at admission (this pull
request).
2. Make the recovery path that committed the prior run also settle its
Runner and controller projection.

I chose the first approach for these reasons:

- The provider history stayed in the unreachable sandbox. A settled
projection cannot make it resumable. Admission would pass, but a
replacement sandbox would then fail on the missing backup, and a new
sandbox would only try to resume a session that is gone.
- A settled receipt written by recovery would claim a suspension that
the Runner never wrote. Other checks rely on that receipt as Runner
evidence.
- Several paths can commit a result. Only runs that commit after a
settle-at-commit fix would benefit. The admission fix also unblocks
tasks that were committed before it.
- Admission already owns this decision. It already has the scope claim,
the journal, and the database rows.

### Safety argument

- **No live prior owner.** The coordinator has no lease owner, and the
controller must be proven stopped. The follow-up runs in a different
sandbox than the old Runner. A resumed sandbox keeps the existing path,
which reads the old Runner's own lifecycle there.
- **No unsettled terminal delivery or effects.** The controller
acknowledged the terminal and completed every command, including the
suspend command. Every tool input has an answer, and the coordinator
committed the result. The unreadable Runner outbox can then hold only
post-terminal telemetry.
- **No unsettled cleanup.** Any local Runner receipt, including an
unsuspended one, keeps the hold. A coordinator recovery state keeps the
hold.
- **No ambiguous quarantine.** A quarantined root from a different owner
keeps the hold. A quarantined root with Runner lifecycle evidence,
unknown entries, or an unsettled journal also keeps the hold.
- **No lost evidence.** The move keeps the old authority in the session
root. Existing retention expires it like other continuity breaks. A
journal change during the database reads cancels the move.

## Verification

- `cd server && npx vitest run
src/services/native-runtime/native-recovered-owner-followup.test.ts`: 12
tests pass.
- Before the change, the four admission tests failed with the exact
error above. This includes a re-admission that rebuilt the journal from
the database and failed again.
- After the change, `created` and `replacement` sandboxes are admitted.
A re-admission is admitted. A task with an earlier quarantined copy of
the same owner is admitted.
- Eight controls keep the hold: uncommitted result, live controller
lease, unexpired controller lease, recovery state, pending command,
unanswered tool input, a different owner in quarantine, and an
unsuspended Runner receipt in quarantine after a retry rebuilds the
root.
- I disabled each new guard in turn. Each change made its matching
control fail.
- An exploratory run of the same fixture before the change answered the
Retry question above. The second follow-up selected the failed follow-up
as its source and failed with
`native_controller_recovery_unsupported:metadata_format`.
- `cd server && npx vitest run
src/services/native-runtime/native-session-executor.test.ts`: all 1,087
tests pass.
- Related suites pass: `task-workspace-binding`,
`native-controller-recovery` (unit and integration),
`native-harness-backup-expiration`, `native-session-resume`, and the
restart and diskless integration suites. Their container and Runner
binary cases skip locally.
- `node scripts/typecheck-low-memory.mjs`, `pnpm
check:module-boundaries`, and `git diff --check` pass.
- CI passes at `879bb6a5c7`. Two attempts lost jobs when CI runners
received a shutdown signal; the third attempt passed every job.
- I did not run the full `pnpm test:run`, `pnpm build`, or the opt-in
Linux Runner container cases locally. CI runs them.
- I did not test a live sandbox provider or a live model. I could not
inspect the original checkpoint contents. If a stranded run's journal
does not show a delivered terminal and completed commands, its follow-up
stays held.

## Risks

- A follow-up after this path cannot use the lost provider history. The
provider session then follows the existing resume-or-replace policy, and
the task context comes from Paperclip. A checkpoint that requires the
same session, such as an unfinished goal, still fails at provider
recovery.
- Post-terminal Runner telemetry in the lost outbox, such as a late
usage report, stays lost. It cannot carry a workspace effect.
- A task that already has a failed follow-up from this error can still
be blocked on a board Retry. Source selection picks that failed run and
fails with `native_controller_recovery_unsupported:metadata_format`.
This pull request does not change source selection. A same-run retry of
the failed follow-up is admitted.
- The change is limited to per-turn runs on the built-in sandbox
transport. Warm sessions, plugin tasks, resumed sandboxes, and restart
recovery do not change.
- No migration or configuration change is required.

## Model Used

- Anthropic Claude Opus 5.5 (model ID `claude-opus-5-5`) in Claude Code,
with extended thinking, repository tools, shell and code execution, and
local test runs. The runtime does not expose the context window size.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 13:24:16 -08:00
DottaandPaperclip da77c1bcfa fix(runner): align ordinary document key contracts (#18824)
## Thinking Path

> - Paperclip helps people manage AI agents that do work.
> - Native agents use tools to read and write task documents.
> - A document key is a stable identifier. The service trims it and
converts it to lowercase before it checks the key.
> - The tool schemas accepted filenames and long keys that the service
rejects.
> - This pull request makes ordinary document tool schemas match the
existing service contract.
> - Agents can now see the key rules before they call the tools.

## Linked Issues or Issue Description

**What happened?**

The schemas for `read_document`, `write_document`, and
`list_document_revisions` accepted `reconciliation.md` and keys longer
than 64 characters after normalization. The task document service
rejects these values. The descriptions did not explain that the key is
an identifier rather than a filename.

**Expected behavior**

The model catalog and the live and scenario tool definitions must
describe the existing document key rules. Values accepted after trimming
and conversion to lowercase must remain compatible. Historical
case-document keys use a separate contract.

**Steps to reproduce**

Validate `{ "key": "reconciliation.md" }` against the previous
`read_document` schema. It passes. Call the document service with that
key. It rejects the key before it reads the database. The added
regression repeats this comparison for all three tools.

## What Changed

- Apply the same document-key pattern to `read_task_document` and
`write_task_document` at the API bridge. Keep their existing raw-input
limit of 100 characters; ordinary tools keep 120. Export the shared
schema through the existing Runner package and server vendor boundary.
Route permissions, normalization and document locks stay with their
current owners.

- Add one shared schema for ordinary task-document keys. Keep the
existing 120-character raw input cap and the 64-character normalized
identifier limit.
- Preserve uppercase input, ECMAScript trim whitespace, and the Kelvin
sign, which converts to ASCII `k`.
- Apply the schema to the model catalog and the three live and scenario
definitions. Regenerate the catalogs, seeded fixture identity, and
fixture manifest.
- Test schema parity against the real task document service. Keep
historical case keys and the backend validator unchanged.

## Verification

- Current-head required CI is green at
`e67d3f1f49393660f0c70b3d1d136e6f86af821d`: 56 successful checks, four
intentional skips, and two neutral checks. [CI attempt
2](https://github.com/paperclipai/paperclip/actions/runs/41715924252)
passed after one targeted rerun of five jobs interrupted by the same
runner shutdown; the original interruption is retained. Fresh Greptile
and Paperclip reviews are both 5/5 with no unresolved review threads.
Hosted recovery remains unverified until automatic deployment and
replay.

- Normal master refresh to `e67d3f1f49393660f0c70b3d1d136e6f86af821d`
includes the already merged external-object startup fixture correction.
The previous required canary failed downloading
`mdast-util-gfm-table@2.0.1` with npm E404; later registry metadata and
tarball HEAD both returned 200. A separate general shard failed the
ten-thousand-future-objects startup assertion. Both original failures
remain recorded; neither was bypassed or retried. At the refreshed
baseline, 122 document/bridge cases and the exact large-object startup
case pass locally. Six document cases skip by platform. Fresh required
CI and reviews must pass on this head.

- The current-head Paperclip review found the same mismatch in the
task-scoped bridge. Before the correction, the real advertised schemas
failed 22 parity cases while 87 controls passed. After the correction
and a normal master refresh, the document-key and bridge suites pass 122
cases, with six platform-specific skips. The tests compare advertised
JSON Schema, runtime Zod validation, and the actual normalized document
service. They retain the bridge raw 100/101 boundary and historical
case-key controls.
- Runner TypeScript build, all server typecheck layers, and the actual
production Runner and server build pass. The final server build stamp
binds commit `80c880709c5f5630799fb8a48d54ff11ebc16610`. Full
current-head CI and fresh review are required after the correction. The
earlier CI run was externally cancelled and rerun; its cancellation
remains recorded.

- The regression failed in 33 of 64 cases before the fix. The remaining
cases passed as controls.
- The fixed regression passes all 67 cases, including every ECMAScript
trim character.
- The four focused Runner contract suites pass: 116 tests. They cover
all three tool surfaces and existing case behavior.
- Generated catalogs and manifest checks pass. Module-boundary and diff
checks pass.
- All server production typecheck layers pass. The test layer passes
after correcting the new test’s Ajv import. The full production server
and Runner build passes at a3e6191590.
- Full repository checks run in required CI. No hosted replay or
provider behavior claim is made.

## Risks

- The tool schema now rejects unsupported keys before the service does.
Older callers that used invalid filenames must use a stable key such as
`reconciliation`.
- The compatibility pattern must track the service normalization rules.
The regression checks the real service and includes Unicode
compatibility controls.
- No database, authorization, document revision, idempotency, or
case-document rules change.

## 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 15:21:45 -06:00
DottaandPaperclip ff80845756 fix(ui): show Slack workspace reconnect on saved setup errors (#18837)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Slack setup connects a saved agent draft to a workspace.
> - Workspace authorization can fail before the agent's Slack app
exists.
> - The error requested reconnection while its primary action said
Continue.
> - Older grants could also make retry provision instead of reopening
authorization.
> - This change names the recovery action and keeps it on the
authorization path.
> - People can reconnect from the same setup page and resume the saved
agent.

## Linked Issues or Issue Description

Refs: #18722.

**What happened?** A managed Slack draft showed “Reconnect your Slack
workspace to continue setting up this agent.” Its primary action was
Continue. The reconnect label depended on a registration that might not
exist, and a saved error or failed explicit authorization could be
ignored when an older grant remained available.

**Expected behavior:** Offer one enabled Reconnect Slack workspace
action that opens ordinary Slack authorization and resumes the saved
setup.

**Steps to reproduce:** Resume a managed Slack draft before app creation
with a saved manager reauthorization error, or fail an explicit
workspace authorization request. Inspect the recovery action with zero,
one or multiple older workspace grants.

## What Changed

- Show Reconnect Slack workspace for saved and local authorization
failures before app creation and for existing-app recovery.
- Honor the saved manager error and retain explicit authorization
intent, so retry authorizes rather than provisioning with an older
grant.
- Hide irrelevant workspace controls during recovery and allow
authorization before choosing an old grant. Use one recovery action.
- Cover zero, one and two older grants in component regressions. Update
the existing browser test to use the explicit action; add
production-wizard desktop/mobile recovery previews and provider
guidance.

The Slack provider UI owns this change. It uses the existing
authorization endpoint and shared footer; API contracts and provider
permissions are unchanged. Maps are unchanged.

## Verification

- Final head: `02234bd1afc61d404841a9fc2b7e457b0dc021bf`. Full CI
[41715544834](https://github.com/paperclipai/paperclip/actions/runs/41715544834)
passed on attempt 2, including all browser, server/chat, Runner,
typecheck, build, policy and canary checks. Greptile and Ponytail both
give this exact head 5/5; all review threads are resolved.
- All 20 managed setup tests and 276 related chat UI tests across 19
files passed. All 9 Slack provider browser cases passed on the final
source in 1.3 minutes using Node 24.22.2 and unchanged Playwright
configuration. Provider responses are mocked in those cases.
- Full repository typecheck and build passed earlier; final UI
typecheck/build, token gates and Storybook catalog checks passed after
the source refinement. All 30 map/catalog Node checks passed.
Catalog/map checks are structural. The full local server Vitest suite
was not repeated for this UI-only change; full CI covered it.
- Production-wizard browser previews covered saved recovery on desktop
and at 320 × 568, older grants, and failed explicit authorization. On
the final source, the visible retry with older grants returned to an
error-free workspace chooser, where Continue requires a choice.
Selecting a workspace and continuing was also exercised through the
message step during the preceding refinement. All provider actions in
these previews are simulated.
- The existing recovery path was independently exercised live in staging
through ordinary controls: Continue → Slack Allow → Success → Open Slack
→ real agent DM reply → Done → full reload. The new action label still
needs deployed acceptance. No live grant was revoked or duplicate bot
created to force the error.

Failures retained through acceptance:

- New regressions failed on the earlier sources for missing recovery
labels, ignored saved errors with active grants, a duplicate action,
disabled multi-workspace recovery, and lost explicit authorization
intent. The final tests require enabled recovery, authorization
navigation and no provisioning on that action.
- The first preview assertion raced workspace-options loading; it now
waits for the same enabled condition. A later multi-workspace return
assertion incorrectly expected the message step; it now checks the
required workspace choice and disabled Continue before selection. The
fixture's authorization flag is reset by a normal full reload. An
unsupported Testing Library `exact` option was caught by typecheck and
replaced with an anchored name regex. Failure screenshots remain
preserved.
- Earlier CI
[41707550300](https://github.com/paperclipai/paperclip/actions/runs/41707550300)
found the existing browser test still clicking Continue and an npm
canary-version collision. The locator was updated without removing
authorization-count/redirect assertions; subsequent packaging checks
passed. CI
[41709833527](https://github.com/paperclipai/paperclip/actions/runs/41709833527)
had runner shutdown/lost-communication interruptions; those earlier
successes were not used to qualify the final head.
- Final-head attempt 1 had a native Runner shutdown while unpacking its
cache, plus a distinct Cursor Cloud Stop assertion failure in the
package lane (4890 passed, 10 skipped). The Cursor Cloud case passed
unchanged in isolation, with its source subtree matching the CI merge
tree; the CI cause is not established. Both full Runner lanes then
passed on retry of the same reviewed commit, with successful checks
retained.
- One local browser invocation timed out during server startup on Node
26 before assertions. The first Node 24 wrapper invocation failed a
nested-npx package selector before assertions. Direct Playwright
execution with that wrapper variable cleared and unchanged timeouts
passed the final 9-case suite.
- Greptile flagged a routine regression paragraph outside feature-map
scope; it was removed. Ponytail then found the active-grant mismatch; it
was reproduced and fixed at the authorization decision. Both reviewers
now report 5/5.

## Risks

Saved reauthorization errors and retries of explicit authorization
requests now reopen existing Slack consent even when older grants
remain. Provider availability or browser consent can still prevent
reconnection. This change does not establish why the original workspace
grant became unusable; simulated previews are not live Slack proof.

## 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 15:16:53 -06:00
Devin FoleyandPaperclip f7fae6b738 fix(runner): preserve native Claude GitHub broker transport (#18850)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Shared agents use managed GitHub launchers to acquire credentials
for the active run.
> - Native Claude runs through the ACPX provider environment boundary.
> - That boundary keeps the launcher paths but removes their broker
transport settings.
> - Shell commands then report `capability_missing`, even when connector
tools work.
> - This pull request retains the three transport settings for explicit
Claude managed launchers.
> - Shell commands can use the existing session broker and its current
access checks.

## Linked Issues or Issue Description

**What happened?**

Native Claude through ACPX cannot authenticate shell `git` and `gh`
commands through an eligible managed GitHub connection. The launcher
reports `GitHub capability_missing`. Connector tools can still
authenticate through their own path.

**Expected behavior**

Managed shell commands acquire credentials for the active run. Provider
processes do not receive raw GitHub tokens or ambient host credentials.

**Steps to reproduce**

1. Configure an eligible managed GitHub connection and a native Claude
ACPX execution.
2. Start a task and run `gh api user` through its terminal.
3. Observe `capability_missing`, although the corresponding connector
identity request succeeds.

**Paperclip version or commit**

Reproduced from source at `6ef37398c8d799c5142a3a6590ecc70ad352b663` and
current master `0b87308d5de93b4b8f18eeea89afc0bb4d9480a7` with
controlled local dependencies. The affected runtime is native Claude
through ACPX in a managed sandbox.

Related: #18212 preserves Claude launcher selectors. This change
supplies the missing broker transport. #18081 and #17599 address a
separate Legacy Codex shell-policy boundary. #15354 addresses Legacy
ACPX session hashes. The duplicate search found no open PR for this
native Claude boundary. ROADMAP.md already lists managed personal GitHub
identities; this restores that existing behavior.

## What Changed

- Retain the broker URL, session capability and bridge capability within
the existing explicit Claude managed-launcher allowlist.
- Continue to reject raw GitHub tokens, ambient broker settings,
task-supplied overrides and transport for other ACPX providers.
- Add real Bash and launcher subprocess controls with a loopback broker
and fixture Git/gh executables. Cover isolated, prepared and
preconfigured homes; changed operation identities; revocation; and
recovery.
- Confirm that broker capabilities stay out of persisted session
environment data. Update the execution guide.

The ACPX environment owner retains this behavior. Broker authorization
and credential resolution do not change. No shared API or schema changes
are needed.

## Verification

- Passing: 84 focused tests across `environment.test.ts`,
`github-launcher.test.ts`, `runtime-sandbox.test.ts` and
`configured-environment.test.ts` in the Runner package.
- Passing: `pnpm --filter @paperclipai/paperclip-runner
typecheck:typescript` and repository-wide `pnpm -r typecheck`.
- Passing: repository-wide `pnpm build`.
- Passing: 60 existing adapter-utils launcher tests; one unrelated test
is skipped.
- Passing: `node scripts/check-maps.mjs`, `node
scripts/check-module-boundaries.mjs`, and `git diff --check`.
- The new regression failed before the fix: both shell commands ran
anonymously and the unit check showed the three missing broker settings.
After the fix, the local broker receives each operation, changes
identity for later commands, and denies revoked access without reusing a
prior token.
- The prepared-home fixture first rejected a macOS temporary-directory
alias. It now resolves that fixture directory to its canonical path. A
launcher-control command also named a nonexistent package config; the
corrected run uses the root Vitest config.
- Both `pnpm test:run` attempts stopped in chat-suite setup when
embedded Postgres initialization exited with no numeric status. No chat
tests ran. An independent database-support probe between attempts
succeeded. The startup cause is not established; the full local suite is
not verified. No further identical retry was made.
- [CI run 41713841404, attempt
2](https://github.com/paperclipai/paperclip/actions/runs/41713841404)
passed on `92fd931722486c111030a7efe19dfc73f530b4c8`: 48 successful jobs
and one conditional skip. This includes browser shards, general and
serialized tests, Runner static/Rust/package/native-server checks,
build, typecheck and the canary dry run. No CI jobs remain pending or
failed.
- The first CI attempt was interrupted on multiple runners. Browser and
server/chat jobs logged runner shutdown signals followed by
cancellation. The Runner static check reported lost communication with
its runner. No failing browser assertion was recorded before
termination; the summary gates failed on interrupted jobs. The
successful retry used the same source revision. It shows recovery but
does not establish why the hosts stopped.
- Greptile reviewed `92fd931722486c111030a7efe19dfc73f530b4c8` at 5/5
with no outstanding findings. All review threads are resolved, and
GitHub reports the branch as mergeable with no conflicts.

The source tests use fixture credentials and a loopback broker on macOS
arm64 with Node 25.6.1. They do not qualify a live Claude process,
deployed sandbox, Windows shell or GitHub write. No existing run or
deployed configuration was changed.

## Risks

- Claude shell commands can read the explicitly supplied broker
capabilities. Managed launchers need those capabilities. The existing
broker still enforces active session/run binding, responsible identity,
trust and connection access.
- The exception applies only when the controller supplies a Claude
managed-launcher directory. Raw GitHub tokens remain excluded, and
broker settings remain absent from persisted session environment data.
- No migration is required. Existing provider processes need their
normal retirement and restart to receive a new launch environment.
Deployment and live sandbox verification remain separate steps.

## Model Used

OpenAI `gpt-6.1-sol` through the Codex harness, with xhigh reasoning.
Used repository inspection, code editing and shell execution. The
context window size is not exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 13:15:01 -08:00
DottaandPaperclip 61e8d5c7eb fix(ci): keep trusted full-ci label runs on AWS (#18855)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Public pull requests need CI without access to private runner
capacity.
> - Trusted CI CODEOWNERS use the AWS runner fleet after identity and
revision checks.
> - The PR caller accepts both `profile-ci` and `full-ci` label events.
> - The runner gate accepted only `profile-ci`, so `full-ci` sent
trusted work to GitHub.
> - This change accepts both labels and keeps the existing authorization
checks.

## Linked Issues or Issue Description

Refs #18821. Observed on #18848 in [run
41711436968](https://github.com/paperclipai/paperclip/actions/runs/41711436968).

**What happened?**

A CODEOWNER added `full-ci` to a trusted PR. The metadata gate ran on
AWS, then sent the test matrix to GitHub with the notice `only the
profile-ci label can request a profiling run`.

**Expected behavior**

Both supported labels keep authorized CI on AWS. A label must not grant
an outside contributor access to AWS.

**Steps to reproduce**

Add `full-ci` to an open PR whose author and event sender satisfy the CI
owner and numeric allowlist checks. Before this change, the runner
decision returns `ubuntu-latest` before it checks those identities.

## What Changed

- Accept `full-ci` and `profile-ci` label events in the trusted runner
gate.
- Test both labels with trusted users, unlisted users, and
numeric-allowlisted users who are not CI owners.
- Document that label selection changes CI scope and preserves runner
authorization.

## Verification

- The new trusted `full-ci` regression failed before the fix with
`ubuntu-latest` and passes after the fix.
- `node --test .github/scripts/tests/ci-routing.test.mjs
.github/scripts/tests/ci-plan.test.mjs
.github/scripts/tests/ci-profile.test.mjs
scripts/__tests__/e2e-shard.test.mjs`: 147 passed.
- `actionlint -shellcheck='' .github/workflows/ci-routing.yml`: passed.
- `git diff --check`: passed.
- This isolated checkout has no installed workspace dependencies.
Repository-wide typecheck, tests, and build are delegated to this PR's
CI; local verification covers the changed routing and its callers.
- A read-only replay against this PR's real GitHub identities and
commits returned GitHub with the old `full-ci` guard and AWS with the
corrected guard. No PR code was executed by the replay.
- Greptile reviewed current head
`47f7159f67c8633830d7732cb68c6d43d8243fe5`: 5/5, successful check, no
unresolved review threads. Both incremental-CI qualification jobs and
commitperclip review passed.
- Full CI is not green. Attempt 1 lost workers to verified Spot
reclamations in `m5.xlarge/us-east-1c`. After excluding that type from
new placements, one failed-job retry retained 19 successful checks and
reran 27 interrupted checks on AWS. The policy check passed, including
these regressions. AWS then confirmed seven more reclaimed
`m8a.xlarge/us-east-1c` workers and two reclaimed
`m8i-flex.xlarge/us-east-1b` workers. The affected checks show runner
shutdowns; unaffected jobs remain running. [Recovery
attempt](https://github.com/paperclipai/paperclip/actions/runs/41714303495/attempts/2).

## Risks

This changes the event allowlist before authorization. The author, event
sender, rerun actor, repository, CODEOWNERS, numeric allowlist, and
immutable revision checks still apply. Unrelated labels remain denied.
Separate helper workflows that explicitly choose GitHub runners are
outside this change.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, shell, Git, and GitHub API
tools. The runtime does not expose a more specific model build ID or
context window size.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
native-ingestion-benchmark-2026-11-03-b0-current
2026-11-03 15:09:36 -06:00
4bdcd1cffc fix(recovery): release holds for unstarted dispatch cancellations (#18838)
Release false recovery holds for legacy dispatch cancellations whose retained
evidence proves execution never started. Keep audit history, current admission
gates, review-wait Retry, and retained workspace obligations.

Share the execution-absence proof across cancellation policies and make the
dispatcher error-code policy exhaustive. Add PostgreSQL coverage and stabilize
the browser history fixture by waiting for pause cleanup before seeding it.

Fixes #15483
Refs #18085

Co-authored-by: commitperclip[bot] <284812584+commitperclip[bot]@users.noreply.github.com>
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-11-03 14:54:59 -06:00
DottaandPaperclip 2a16dcf629 fix(workspaces): preserve shared directory trees during enrollment (#18835)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A volume-free web server needs existing task files in durable object
storage.
> - Historical workspace records can share a physical folder or contain
other task folders.
> - A task-level migration hold cannot fence every alias, and capturing
a parent recursively duplicates nested task data.
> - Offline enrollment imports children first and verifies their durable
coverage before omitting them from a parent archive.
> - Snapshot retention preserves those child versions for as long as a
covering parent depends on them.
> - Existing files, task bindings, and archived states remain intact
through the storage migration.

## Linked Issues or Issue Description

Builds on merged #18826.

**What happened?**

The per-workspace importer could capture a shared project folder while
another workspace record still owned the same path. It also recursively
included runtime-created nested task workspaces in each parent import.
Packaged enrollment rejected existing workspace data instead of invoking
its migration owner.

**Expected behavior**

Require offline maintenance for overlapping paths. Import nested tasks
before their parents, verify every excluded directory against its saved
snapshot, preserve dependencies through retention, and resume
interrupted imports without replacing existing remote authority.

**Steps to reproduce**

Create a project workspace and another task workspace below its
`.paperclip/worktrees/` directory. Try importing either while ordinary
writers are running, then run offline enrollment. Interrupt a parent
import after upload and resume it.

## What Changed

- Add physical-overlap admission checks and an offline entry-point
guard, including global running and queued executions.
- Import pending records in leaf-first pages through the existing
migration owner.
- Verify nested snapshots by downloading, restoring, and comparing the
original source before and after parent upload; check transitive
dependencies.
- Publish parent dependency receipts under child snapshot row locks.
Retention pins those children and revisits them when the parent is
deleted.
- Index imported-child dependency lookups with a partial GIN index; a
PostgreSQL query-plan regression covers 2,500 unrelated snapshots.
- Propagate cancellation through capture and object storage operations
and preserve existing retry journals.
- Wire workspace migration into packaged maintenance and document its
preservation contract.

## Verification

- The original overlap and duplicate-parent regressions failed before
implementation.
- 73 migration and retention tests pass against real PostgreSQL and
files, including interrupted uploads, resumed batches, equal-path
aliases, changed or missing child data, nested descendants, and active
or queued executions.
- Six S3-compatible storage integration tests pass, including migration
through the maintenance owner and restoration of both parent and child
after deleting the original filesystem.
- The standalone process case was initially skipped because the worktree
had no bundle. After rebuilding the server and runtime bundle, that test
passed against PostgreSQL and S3: authenticated ready/export receipts,
preserved decision secret, blocked application routes, and clean SIGTERM
shutdown.
- Server typecheck, server build, runtime bundle build, and
module-boundary checks passed. Full repository CI passed on
97136a20b9 after targeted retries of
infrastructure-interrupted jobs.
- Review fixes resolve physical paths through symlinked ancestors and
persist each parent journal’s original capture scope, including
pre-change journals. All three new regressions failed before their
fixes. An initial abort/retry test exposed JSON object-key ordering in
the saved scope; deep equality fixes that case, and the full migration
suite passed.
- Three new fixtures initially omitted required workspace fields; one
also omitted its task binding. The fixtures now satisfy the existing
contract; production validation was not relaxed.
- The parent PR's full local suite stopped on a chat retry-notice
timeout and a following fake-timer failure. All four relevant cases
passed separately without source changes; this is not a claim that the
full local suite passed.
- No staging enrollment or live rolling-deploy result is claimed by this
PR.

## Risks

The offline flag is an internal maintenance contract: Cloud must
actually stop every predecessor deployment before starting the process.
Deep or large trees require additional object downloads and source
comparisons and may exceed one maintenance window; import journals
support retry. Dependency lookups are company-scoped and event-driven,
with no added idle polling. Unavailable archived sources and unresolved
recovery/copyback still block migration and require separate owner work.
Unsupported local providers and cleanup_failed workspaces deliberately
remain enrollment blockers: filtering them from both the batch and
eligibility would abandon local files when the volume is detached. Two
regression tests prove that rejected records keep their files and do not
acquire remote authority. Original folders and the original volume
remain retained. The additional review request to ignore parallel-write
policy, stale task ownership, or a missing task binding in offline mode
is intentionally not applied: stopping deployments does not establish
those semantics. #18841 documents the separate owner actions and adds
three offline regressions preserving their files and blocking detach.
The batch reports the first failed workspace and supports journaled
retry; it does not promise aggregate diagnostics.

## 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 14:50:14 -06:00
DottaandPaperclip b5dac661e9 refactor(runner): clarify workspace-relative artifact destinations (#18816)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native agents can copy an authorized saved artifact into their
execution workspace.
> - The installer accepts workspace-relative destinations and rejects
absolute paths.
> - The API documentation states this rule, but the model-facing tool
description and destination field did not.
> - An agent could therefore attempt an unsupported absolute path before
using a supported relative path.
> - This pull request states the existing rule and gives a relative
example in the tool contract.
> - The benefit is clearer instructions with the same containment and
file identity checks.

## Linked Issues or Issue Description

Refs #18494, which introduced verified artifact installation.

**What happened?**

The tool description said the destination belongs inside the workspace
and its parent must exist. It did not say that the destination must be
relative. The installer correctly rejected an absolute path, including
one lexically inside the workspace. A relative retry could succeed.

**Expected behavior**

The model-facing tool definition must explain the same
workspace-relative path rule as the existing API documentation. It
should show a relative example and state that absolute paths are denied.

**Steps to reproduce**

Inspect the exported download_artifact definition and destination JSON
schema before this change. Compare them with the installer guard and
doc/runner-api-tools.md. The description omitted the relative-only rule
even though the installer enforced it.

## What Changed

- State the workspace-relative destination rule, source.zip example and
absolute-path denial in the tool description.
- Add the same guidance to the destination field metadata emitted in its
JSON schema.
- Keep parsing, installation, authorization, byte size/hash checks and
all path containment guards unchanged. The existing documentation
already states the intended rule.

## Verification

- The first required Linux run failed in the unchanged Kimi model-route
setup, before its HTTP assertion. The failure remains recorded. After
#18828 merged, this branch was refreshed by an ordinary merge of master.
The same five HTTP cases pass on that baseline. Local artifact suites
report 20 platform-specific skips on macOS; the earlier required Linux
jobs passed all 19 artifact cases and the artifact authority case. Fresh
exact-head CI and review passed at
`c7b3226c745caaaedad569369b71383a9a6e89ee`.

- Inspected the real exported tool definition and JSON schema after the
change. Both now expose the relative-path guidance and example.
- A source-only probe used the real schema and installer with synthetic
bytes and a stubbed command runner. Absolute in-root, absolute
outside-root, traversal and reserved paths remain denied before
dispatch. A relative path reaches the stub runner. This is a
contract/path-guard probe, not real byte-installation proof.
- No functional red is claimed. The original unsupported tool attempt
remains a retained failure even though the relative retry recovered. The
exact provider-seen descriptor and the model's counterfactual behavior
are not proved.
- Server typecheck, module boundaries and whitespace checks pass.
Production server build also passes at exact head
b6084c5312.
- The existing artifact filesystem and authority suites skip all 20
cases on macOS because they require Linux. Actual current-head Linux CI
passed all 19 boundary cases and the authority integration case (jobs
126115146096 and 126115146332). Their shard membership is bound by the
uploaded test-selection manifest. No wording-only tests were added.
- Full CI retains an unrelated model-route timeout. A separate test
setup repair must land before this branch is updated and rechecked.
- No provider call, live task edit, tenant probe or security-gate
relaxation was performed.

- The refreshed workflow was externally cancelled after serialized shard
1 reported its Turbo task successful. One targeted retry passed. A
distinct general server shard had a failed runner record with zero
steps, no logs, no output and no annotations; its one targeted retry
also passed. The original records remain preserved. These retries
establish later check recovery, not a proved cause for the runner
failure.
- Final current-head checks: 52 success, two skipped and two neutral.
Required `ci / verify` and `ci / e2e` passed. Both current-head reviews
report 5/5, with no open threads.

## Risks

- The model can still choose an invalid path. This change clarifies the
instructions; it does not claim a counterfactual model result.
- The broad string schema still accepts inputs that the installer
denies. The installer retains authority over path safety.
- Absolute paths, traversal, symlink escapes, reserved paths,
conflicting files and unauthorized assets retain their existing denial
behavior.
- No schema migration, package dependency, API permission, scheduler or
deployment configuration changes are required.

## Model Used

OpenAI Codex, `gpt-6.1-sol`, Extra High reasoning, with source
inspection, code execution and contract verification. Context-window
capacity is not exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used with version and capability
details
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have linked existing public issues or described the issue in
this PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
ticket ID
- [x] I ran the available local verification and reported the Linux-only
test skips
- [x] I considered test coverage and kept existing boundary tests
without wording-only tests
- [x] I checked the existing documentation and aligned the tool guidance
with it
- [x] I have considered and documented 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 merge

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 14:34:54 -06:00
DottaandPaperclip 5877d751c0 feat(server): bind idle credential eligibility to incoming wake proof (#18639)
Reviewed and verified at d1e3afa4af.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-11-03 14:30:07 -06:00
DottaandPaperclip 0b87308d5d fix(native): defer warm continuation during predecessor finalization (#18811)
## Thinking Path

> - Paperclip manages agent work and preserves its saved results.
> - Native warm sessions can continue on a retained remote Runner.
> - A completed predecessor can still have a live finalization lease.
> - The continuation must wait for that owner before it can start work.
> - Two admission checks treated that temporary lease as ambiguous
state.
> - This pull request uses the existing deadline-based wait for both
checks.
> - The same business run can continue once, with its saved context and
gates.

## Linked Issues or Issue Description

**What happened?**

A compatible remote warm continuation can fail before provider work
starts. Its predecessor has completed, but a finalizer still holds a
live lease. The warm-session check reports
`runner_state_identity_mismatch:
prior_owner_terminal_state_indeterminate`. The earlier retained-sandbox
check has the same problem on the command transport.

**Expected behavior**

Keep the unstarted continuation on its existing run. Wait until the
recorded owner deadline. Recheck ordinary ownership, Stop, credentials
and checkpoint evidence before execution.

**Steps to reproduce**

1. Complete a remote warm turn and retain its controller records and
provider session.
2. Give that completed predecessor a live finalization lease.
3. Admit a compatible follow-up with full local controller projections.
4. Before this change, admission fails permanently. It must instead
persist a bounded pre-provider wait.

**Paperclip version or commit**

The PostgreSQL regression failed before production edits on
`adb49a4434`. The current head is `5762dfbe4e`, based on master
`e491b23eb5`.

**Deployment mode**

Native remote sandbox execution. No database migration or provider
artifact change is required.

Related: #18718 refreshes stale warm-controller projections. #18343 adds
controller replacement recovery. #18554 restores suspended remote
sessions. This change handles a live predecessor finalizer after those
source checks.

The shared historical error does not identify its failed predicate. This
regression proves the finalizer race; it does not prove that every
historical instance of that error had this cause.

## What Changed

- Defer a compatible warm continuation while its completed predecessor
has a live finalization lease.
- Use the same wait in the preceding retained-sandbox read check.
- Preserve the current run, provider attempt count and checkpoint. Reuse
the existing resumption scheduler.
- Add PostgreSQL cases for both finalization phases, lease expiry,
company scope, incompatible configuration, unfinished commands, Stop and
duplicate resumption claims.
- Add an opt-in three-turn Linux Runner case. Update the
child-controller fixture to own and register a local API listener.
- Document the waiting behavior and acceptance case.
- Repair two readiness barriers in the refresh-worker integration
fixture after a required CI shard failed twice: wait for paginated
startup and for active worker promises to settle before checking idle
SQL.

## Verification

- **Native regression RED:** before production edits on `adb49a4434`,
the PostgreSQL boundary case received the permanent identity error
instead of `NativeControllerRecoveryWaitingError`.
- **Regression GREEN on the current head:** all **71** focused recovery,
settlement, heartbeat safety and refresh-worker cases pass on
`5762dfbe4e` with Node 24.21.0 and PostgreSQL 18. They cover
incompatible identities, company/configuration mismatches, expired or
missing deadlines, unfinished commands, Stop, retained files and
duplicate resumption claims.
- **Real process acceptance on the current head:** the three-turn
`finalizer-wait` case passes in 58.01 seconds. PostgreSQL, TLS, a
rebuilt Linux Runner and the deterministic Codex app-server fixture
confirm zero attempts and no provider turn during the wait, one claim
resuming the same run, retained history, a healthy third warm turn and
final suspension. The sibling `warm-suspended` case from master also
passes, in a fresh container, in 46.02 seconds. Both runs use
`5762dfbe4e`; the artifacts were rebuilt from that checkout with the
repository's pinned Rust 1.97.1 toolchain.
- Acceptance used Node 24.21.0 and glibc 2.41. Earlier probes exposed
the missing fixture API listener and the preceding retained-sandbox
guard; both are corrected. A stale disposable listener and an
incompatible libc image failed setup before the successful
fresh-container run. These setup failures are not the original
regression's RED.
- Repository typecheck/build passed locally before the rebase. Server
typecheck passes after the rebase. **Full CI passes on the current
head:** 48 successful jobs and one expected scope skip, including
typecheck, build, Runner checks, all serialized-server shards, general
tests, E2E and the final aggregate. Module-boundary and whitespace
checks also pass.
- Broad local inventories are incomplete: six chat shards passed (1,834
tests), but untouched server/UI/adapter cases hit timeouts. The exact
shared-base expiration case passes alone; a full-file rerun passed all
89 assertions but timed out in teardown. A Node 24 rerun timed out in
another lifecycle case and teardown. A later PostgreSQL recheck could
not allocate shared memory; this task's redundant broad controllers were
stopped and their logs retained.
- CI attempt 1 failed a Cursor Cloud cancellation deadline, an E2E
cleanup cancellation with a PostgreSQL lock timeout, and the
refresh-worker startup settling limit. One failed-only retry passed
Runner and E2E; the same refresh startup case failed again. Two
aggregate jobs had previously stalled in the hosted-runner queue and
blocked the retry; that obsolete attempt was cancelled. No repeated
blind rerun is being used.
- **Readiness repair RED/GREEN:** real PostgreSQL 18 behind a loopback
proxy with 20 ms delay per direction reproduces the exact startup error
before the test change; the same probe passes afterward. An initial
controlled probe hit the dedicated-database naming guard before tests
ran and is excluded from RED evidence. The full-file check then exposed
a separate false-idle barrier: a worker could be running while its
scheduler had no timer. The fixture now also checks the existing
active-work tracker. All **24 cases pass** with both repairs. The
at-most-six scoped SQL reads and zero recurring idle-SQL assertions are
unchanged; startup alone has an explicit bounded condition wait. No
production behavior or timeout changes.
- Greptile and Paperclip Review are both **5/5 on the current head**,
with no open findings. Both reviews used source inspection; the runtime
evidence above was obtained separately. The previous review workspace
was recovered on the first production attempt after repairing its
partial-clone export. Its preserved result and real follow-up both
finalized successfully, saved snapshots and released their writers and
finalization leases. Three current-head CI shards received hosted-runner
shutdown signals and were cancelled. All three passed their one
justified retry. The queued aggregate had to be cancelled to schedule
that retry; the final aggregate also passed. [Current-head
CI](https://github.com/paperclipai/paperclip/actions/runs/41706617909).
- This acceptance does not qualify a live Codex model or Daytona API run
of the native repair. No deployment or recovery of the original task is
claimed.

## Risks

- A wait must not grant a second executor. The current attempt stays at
zero until the existing scheduler claims it. Expired or missing
deadlines retain their existing hold.
- Saved state must survive the wait. Admission still requires the same
company, task, session, configuration and physical sandbox evidence.
- Current Stop, budget, credential and permission gates remain with
ordinary resumption. No polling loop is added.

## Model Used

OpenAI Codex, GPT-6 family. The runtime does not expose the exact
serving model ID or context-window size. Capabilities: reasoning,
repository inspection, code editing, shell tools and test execution.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 14:18:18 -06:00
DottaandPaperclip e0276e0166 fix(workspaces): identify remote command timeouts (#18827)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Remote workspaces prepare repositories and save files through
sandbox commands.
> - Each command has a timeout and a known operation.
> - The transport discarded that information when a command timed out.
> - A failed clone then appeared as the same generic error as other
failures.
> - This pull request keeps the operation and timeout in a fixed
diagnostic.
> - Operators can identify the failed stage without exposing command
output.

## Linked Issues or Issue Description

**What happened?**

A remote workspace setup command reached its 300-second transport limit
before provider work started. The workspace layer reported only
`remote_workspace_operation_failed`. That message concealed the timeout
and the affected operation. A later full clone on the same sandbox image
passed in 107 seconds, so the original clone delay remains unexplained.
This PR repairs the loss of diagnostic information. It does not claim to
prevent the underlying delay.

**Expected behavior**

A transport timeout must remain a failure. Its diagnostic must identify
the operation and configured limit. It must exclude raw output and must
not automatically repeat a command whose remote outcome is unknown.

**Steps to reproduce**

1. Return `timedOut: true` from the sandbox command boundary during
workspace preparation or cleanup.
2. Observe that the base implementation reports only
`remote_workspace_operation_failed`.
3. Apply this change. Preparation reports
`remote_workspace_operation_timed_out: prepare (configured timeout
300000ms)`. Cleanup retains its separate 10-second limit.

**Paperclip version or commit**

The regression was reproduced on master base
`367f29478d14003cc5c0f161b7ce8f3164993634`. The fixed current head is
`be45ce6594254bd6b94023a550d8b655dd704bb0`.

**Deployment mode**

Hosted Paperclip with native Runner and Daytona sandbox execution. Local
acceptance also uses disposable PostgreSQL and filesystem commands.

Related work: #18403 introduced remote workspace authority. #18705 added
a fixed full-disk diagnostic. I searched recent clone and
remote-workspace PRs and found no duplicate timeout-reporting repair.
This is a defect fix and does not add a roadmap feature.

## What Changed

- Give each internal workspace command a closed operation name: prepare,
repository, archive helper, capture, restore, or cleanup.
- Preserve that operation and the configured limit in a typed error when
the transport reports a timeout. Do not claim a measured elapsed time.
- Keep other failure diagnostics and exact safe-message checks
unchanged.
- Preserve typed cleanup timeouts in the operation cleanup journal while
retaining generic diagnostics for untrusted errors.
- Add preparation, cleanup, and real-PostgreSQL lifecycle regressions.
Document the timeout and the need to reconcile saved receipts before
recovery.

## Verification

- Red: both transport regressions failed on the base implementation. A
separate real-PostgreSQL cleanup regression reproduced the review
finding: the operation journal replaced a typed timeout with a generic
error.
- Green: all 62 remote transport and archive tests and all 50
real-PostgreSQL workspace lifecycle cases passed. The lifecycle suite
ran with an explicit disposable database; it was not skipped. Command:
`pnpm exec vitest run --project @paperclipai/server
server/src/services/remote-workspace-transport.test.ts
server/src/services/remote-workspace-snapshot-archive.test.ts`.
- Safety: a timeout with exit code zero still fails. Raw stdout and
stderr stay out of the error. A timeout cannot claim the fixed
unfinished-Git diagnostic. No command retry occurs. Existing saved files
remain unchanged. Existing archive, restore, receipt, symlink,
credential-isolation, and full-disk controls pass. The cleanup lifecycle
cases retain the acknowledged snapshot, preserve working files, avoid
duplicate uploads, record only a safe typed timeout, and settle cleanup
on the next normal reconciliation. An untrusted error that mimics the
timeout prefix stays generic.
- Live acceptance: production transport code ran against actual Daytona
SDK 0.203.0 timeouts on the incident image, with 4 CPUs, 8 GiB RAM, and
a 10 GiB disk. A controlled one-second SDK limit produced the generic
baseline error and the fixed exact preparation timeout. The current
`be45ce6594` code was rechecked for both preparation and cleanup. Each
returned the typed error and its own configured limit (300 seconds and
10 seconds) while the controlled SDK fault remained one second. Each
case made one command call. Each disposable sandbox was deleted before
the next case.
- The first live-probe attempt used an incorrect error-name check. It
failed its acceptance check and remains recorded. The probe was
corrected to use the same SDK timeout base class as production. Its
first sandbox was deleted. This was a probe setup failure, not red
evidence for the repair.
- A separate same-image probe ran the real full repository preparation
successfully in 107 seconds. The affected command path is unchanged
except for error reporting. The original task was retried once through
the product on deployed source
`adb49a44346849e751548b2fb640693dbfad4b6f`. The requested memo and its
first document revision persisted after reload and matched the
downloaded attachment. The run succeeded, the task reached Done, its
execution lock cleared, and final workspace save and result commit
succeeded. The diagnostic PR is not deployed.
- Recursive typecheck and build passed on `be45ce6594`. `git diff
--check` passed. The first full-suite attempt stopped before tests
because the offline install had skipped an embedded PostgreSQL native
postinstall. The package's official symlink hydration fixed `initdb`.
- The next Mac run passed 350 chat cases and hit one unchanged 15-second
chat deadline. That case then passed in isolation, and all six full chat
shards passed. The broader Mac matrix was later stopped as partial when
CI results became available. No full single-command Mac pass is claimed.
- Full Vitest file coverage uses the exact successful CI merge tree,
`d264fd98e7fb6eff4a9e6b00d402f88894cc0e57`. Its canonical inventory has
2,899 files. Passing CI covered 2,057 files. The remaining 842 ran
locally through the standard exact-file harness. The inventory and
selection checks show no unassigned files. All four changed source/test
files are byte-identical between the PR head and that merge tree.
- The first remaining-file run passed 771 files. The database group
passed 68 files and reported four deadline/readiness failures in three
unchanged files. Those same three files passed all 12 cases when run
sequentially on Node 24.22.2, matching CI's major version. Assertions,
readiness limits, and test deadlines did not change. The first attempt
used Node 26.4.0 with a parallel UI workload; this does not isolate the
contribution of runtime version versus load. Both attempts remain
recorded.
- [GitHub
CI](https://github.com/paperclipai/paperclip/actions/runs/41699549115)
is green on current head `be45ce6594`: `ci / verify`, typecheck, build,
all selected server/chat/workspace groups, Runner checks, and all eight
browser shards passed. Greptile is 5/5 with no open threads. The prior
P2 was resolved by removing new code comments.
- Structured Paperclip review remains queued because its own previous
accepted result exhausted workspace export recovery. A fresh
current-head request is saved, but normal Retry returns HTTP 409
(`accepted_result_without_new_input`). Its advisory cleanup finding is
fixed in `be45ce6594` and covered by red/green lifecycle evidence. This
check is informational. The branch requires `ci / verify` and `ci /
e2e`, and both pass. The independent review-service limitation remains
recorded; this PR is not deployed.

## Risks

Timeout error text changes. Consumers that compare the old generic
message may need to accept the new diagnostic. No such production
comparison was found in this repository. API shapes, deadlines, retries,
ownership, credentials, and model selection do not change. A timeout
still does not prove that the remote process stopped or that a save
failed before publication. Existing recovery must reconcile that state.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, code execution, and browser
tools. The host 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 14:13:16 -06:00
DottaandPaperclip 1f55073f96 fix(cursor-cloud): support API-only controllers on managed stacks (#18310)
## Thinking Path

> - Paperclip helps people manage AI agents at work.
> - Cursor Cloud hosts execution behind its API.
> - Product setup still expected a Paperclip Computer.
> - Managed-only tenants therefore rejected valid setup.
> - This admits the validated API-owned controller.
> - Authority, Stop proof, budgets and cleanup remain enforced.

## Linked Issues or Issue Description

Refs #18068 and #18069; both dependencies have merged.

**What happened?**

Managed-only setup still requested a Computer. Live tests exposed
missing Cursor Stop proof and duplicate replay.

**Expected behavior**

Use Cursor’s API controller. Verify provider/controller termination and
replay each log identity once.

**Steps to reproduce**

Enable native Runner/managed-only. Configure Cursor model/repository/key
without a Computer, then Test, finish setup, run and Stop.

## What Changed

- Derive API-only placement from validated native Cursor configuration;
reject placement/command/workspace/home overrides and keep
Kubernetes-only execution closed.
- Skip Computers/checkout/workspace realization. Keep scoped tools,
completion, private staging, budgets, leases and recovery. Persist
placement; reject contradictions.
- Align Cloud cards, Test, Save and Finish setup; hold unready drafts.
Legacy keeps its Computer, credential and policy checks.
- Verify Stop from exact API ownership, audited intent,
provider-terminal proof, no Computer/process ownership and controller
retirement. Recheck immediate/deferred retirement; wake existing
recovery on durable evidence.
- Clear warnings when the actual stopped target later has authoritative
confirmation. Keep missing/other-run negatives and the existing
predicate.
- Publish safe status refresh after full-proof ACK. Retain bounded
native/physical cancellation history; add no poller or weaker proof
path.

## Verification

Core merged as `3910352c923a`; Product merged as `27d35314be8e`. Public
branch `5f86eb66b081` has tree `67344151d303`. Its exact-head Greptile
summary is 5/5; the fresh audit found zero unresolved threads and
complete pagination. The controlled workspace retry and required summary
passed. All 59 current-head contexts are terminal and clean. Prior
green-36 evidence keeps its identity.

[Current
CI](https://github.com/paperclipai/paperclip/actions/runs/41680511731)
first failed workspaces-a 1/2: the CLI company-import/export beforeAll
30-second runtime-info/child-PID/HTTP readiness wait expired. All 431 UI
files/4,714 tests and 285 other CLI tests passed (one skip). The cause
remains UNKNOWN. Child output records startup recovery complete, but
buffered CI prefixes do not establish its relation to the waiter
deadline; runtime-file/PID/last-health state was not captured. Both
unchanged local probes were RED. The second observed dependency
preflight still pending at the deadline; observer overhead was not
quantified. Neither proves the CI cause. After all functional jobs
finished, only the queued metadata summary was cancelled; the other 48
records stayed unchanged. Attempt 1 retains 46 successes, the original
failure, one skip and the cancelled summary. One same-head debug retry
was admitted at 18:42 UTC on November 3. Its workspace job passed at
19:23 UTC on the same checkout: UI 431 files/4,714 tests and CLI 33
files/286 tests, including company import/export. Test caching was
disabled. The new `ci / verify` summary passed; workflow attempt 2
completed SUCCESS. Required `ci / e2e` also passed. The final check at
19:53 UTC on November 3 found all 59 current-head contexts terminal and
clean: zero pending and zero failing. The 47 inherited completed/skipped
rows are not new executions. No code, assertion or timeout changed;
success does not explain the original failure or erase the local REDs.

CI checkout is `1ea9d8608ced`/tree `78cb1ec13d59` (parents
`2e37c73c05fe`, 5f86); both workflows use 2e37. Its 20 extra paths add
low-memory build/Rust task-memory. Scripts cohere; later matrix changes
only labels, with no newer debug-cache steps. Static audit found no
blocker. Supported API arguments omit the memory flag: no default
monitor/instruction changes. Flagged external launchers can change
Cursor instructions/digests and remain unqualified.

The merge added 41 master-identical paths, retained the green-36 inbox
hunk and found UI fixtures matching upstream. Startup/backup, recovery
projection, sleep diagnostics and binary backup change production.
Static audit found eight Stop/ACK owner blobs and 37 sleep-work SQL
templates unchanged, with sleep fences/single-flight accounting intact.
Exact precommit tree: 416/416 across eight server files, binary backup
1/1, server types PASS; repeated scopes are not summed.

The deferred-retirement P1 reproduced three real-DB/owned-process
failures; missing proof stayed unverified. Recovery passed 47 process
cases/33 proof negatives. Warning repair binds actual targets to
same-run proof. Validation passed 729 scoped UI cases, a separate
359-case thread run, 111 backend cases (84 CAS, 6 assembled, 21
activity/privacy), types/tokens. Proof validators/predicate are
unchanged; no new poller or increased timeout.

The d8d3 attempt stays FAILED: 45 passes, two workspaces-a failures,
derived verify failure and one skip. Both shards shared three failures:
two successful mocks omitted the account guard’s required userId. The
two-line fixture repair changed no guard, assertion or timeout. Three
failures reproduced; 54 access/auth tests then passed, including
account-change/denial negatives, plus UI types/tokens.

The 69285 attempt stays FAILED: 49 jobs, 46 passed, serialized shard 9
failed, its verify aggregate failed and one selection job skipped. The
shard passed 575/576 cases. Its aborted-snapshot case expected nine
computes but saw eight: a final HTTP 200 coalesced while seven ordinary
reads had not proved the aborted compute settled. Earlier CI and the
unmodified local baseline passed with a miss; no local RED is claimed.
The repair awaits the snapshot, allows exactly one extra GET if
coalesced, then requires a miss and retains nine computes/all HTTP429
assertions. No production code, hook, loop, sleep or timeout changed.
Six inbox and seven cache/admission cases passed locally, plus server
types; 30 unrelated cases were filtered. Green 36 passed 576/576 without
taking the extra-GET branch; local runs also missed that schedule.
Current CI event merge 1ea9 now passed 576/576 and naturally exercised
the former failure: coalesced (count 2, 1ms), then exactly one fresh
miss (count 1, 16ms), with strict nine-compute/HTTP429 assertions
intact. No instrumentation or retry was added.

Earlier compatibility passed scoped remote/inbox, routine, API-placement
and UI tests, plus types/migrations/snapshots/maps/tokens. The 89-path
adoption restored fleet routing without workflow overrides or added
skips. Each result retains its source identity.

Live qualification remains actual `9af1a5de42a2`: unchanged Artifact
grader PASSED 17/17 and active-Stop grader PASSED 21/21. All three
provider test resources were deleted canonically with GET404;
post-delete download returned the exact 27 bytes. Histories stayed
stable (Artifact 121 and Stop 67 canonical identities), with 21 company
runs, zero active work and no accounting/cancellation/reservation debt
or integrity findings. Saved settings reopened; no-prompt Test/Linux
admission passed with `claude-sonnet-4-5`. No new Save is claimed.

The first Stop sampling case stays FAILED on a 10.373-second gap.
Unchanged retry kept all 24 samples: 35.076 seconds quiet, maximum gap
4.584 seconds, plus a separate CANCELLED/artifact-absent read after 180
seconds. Initial grading rejected locator spelling before functional
checks; metadata changed only after same-file/inode/byte proof, with raw
evidence/oracles unchanged. Original Question/Plan/reconnect and
historical deleted-artifact results keep their identities.

Git trees verified 11 history-only conflicts and six earlier mainline
paths. Hosted 9af to the public BRANCH differs in 222 paths; 13,500
other mode/blob leaves match. Runner runtime implementations and six
observer/driver pins match 9af on that branch only; inherited
`drivers/acpx/runtime-host.test.ts` differs. No all-Runner/src,
CI/latest-master runtime or image equality is claimed. Later static
audits through `8ab89f41e499` found no concrete API-only blocker. That
base adds completed-history sleep proofs and an MCP cleanup migration;
protected Stop/ACK owners are unchanged. These later changes are outside
this CI and have no live qualification here. Host duplicate-card and
later recovery/UI repairs retain separate proof, not deployed 9af
identity. Direct-key coverage requires absent aiConnection; API-only
placement skips inherited remote-workspace disk-full paths, and
config-read gates match their parent.

Other retained failures: stale fixture 132/133; E8 48/135 with 87
reset-hook failures; Mac 17 SQL failures/skips;
title/startup/custody/Stop/deletion unknown causes and bounded
recoveries; superseded warning/50 duplicate identities; pre-repair
ACK/Core CI failures. Fixture RED/GREEN passed 7/7 browser and 121/121
shutdown tests with a failing broken-unsubscribe control; stable WSP
wrapper passed 128 after physical teardown failure. No full local-suite
pass is inferred. All paid attempts remain recorded; vendor dollar cost
is UNKNOWN.

## Risks

Experimental: Cursor independently acknowledges neither the effective
model nor instruction digest before the first prompt. Explicit values
are submitted and behavior checked live; unpriced provider dollar cost
stays UNKNOWN.

The placement exception applies only to validated native Cursor API
execution. Other local work remains denied under managed-only policy.
Missing/stale Stop or cleanup proof stays pending; damaged archives
remain failed. Explicit Legacy keeps its existing policy owners.

## Model Used

OpenAI Codex/GPT-6 family: tools, execution, browser checks, delegated
review. API ID/context window unavailable.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used and the limits of available model
metadata
- [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 linked public issues or described the issue in this PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch describes the change and contains no internal ticket ID
- [x] I have run the scoped local tests reported above; retained
failures are stated separately
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation
- [x] I have considered and documented the risks
- [x] All required CI gates pass on the current head
- [x] Fresh Greptile is 5/5 with no unresolved findings on the current
head
- [x] I will address all reviewer comments before requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 13:55:39 -06:00
DottaandPaperclip 4d31588dea test(server): load model route setup before test deadlines (#18828)
## Thinking Path

> - Paperclip uses native agents and local provider adapters to do work.
> - The server model route must keep provider catalogs separate.
> - Its tests use the real HTTP route with controlled service and
adapter fixtures.
> - Each test reset the module graph and imported the route again inside
short test deadlines.
> - Slow imports timed out. Unfinished work could then change mock call
counts in a later case.
> - This pull request loads the stable route graph during collection and
keeps each case's fixture resets.

## Linked Issues or Issue Description

Refs #18816 (affected required CI run).

**What happened?**

The pending Kimi model-route test exceeded its 15-second deadline in
required Linux CI. The unchanged test file also failed locally: the
first setup hook exceeded 30 seconds, later tests exceeded 15 seconds,
and the last OpenCode case saw two calls instead of one after unfinished
work reached the shared mock.

**Expected behavior**

Route import and transformation must finish before the case deadline
starts. Each case must still test the real HTTP route with its own mock
state, environment and profile values, and adapter registry cleanup.

**Steps to reproduce**

Run `pnpm exec vitest run --project @paperclipai/server
server/src/__tests__/adapter-model-refresh-routes.test.ts` with a cold
module graph. On a loaded worker, repeated module resets and imports can
exceed the hook and test deadlines.

## What Changed

- Register stable service mocks once and load the real route and
middleware during test collection.
- Remove per-case module invalidation. Keep all five original HTTP
assertions, fixture resets, profile and environment restoration, and
adapter unregister operations.
- Keep the existing hook and test deadlines. Product source code does
not change. No user-facing documentation changes are needed.

## Verification

- The unchanged file reproduced the hook timeout, test timeouts, and
leaked call-count failure locally.
- All five original cases pass with the fix. The cold graph takes about
47 seconds during collection; the cases complete within the unchanged
deadlines.
- All five cases also pass at the same head in a shuffled order with
seed `20261103`.
- Module-boundary and diff checks pass.
- Full repository typecheck, tests, and build run in required CI. No
hosted product behavior is claimed from this test-only change.
- Current-head CI attempt 2 failed in the typecheck harness because the
trusted master workflow called `scripts/typecheck-test-projects.mjs`,
which the older source head did not contain. The log is retained. A
normal merge of master supplies the helper from #18738. All five
original HTTP cases pass again on that merged baseline. Fresh CI and
exact-head reviews passed at `86f0bf27267fe3f44ab813fb0a57ebbb94bae7fe`.

- The refreshed run passed all server checks but one browser shard
failed: `task-reload.spec.ts:63` waited five seconds for a task heading
after the intentional bundle failure. The trace showed the saved task
breadcrumb and properties, with the conversation still loading. The
exact failure log and trace were retained. One targeted retry of that
shard and its aggregate passed without source changes. This establishes
recovery of the check; the original browser loading delay remains
unexplained.
- Final exact-head checks: 52 success, two skipped, and two neutral.
Both Greptile and Paperclip reviews are 5/5. No review threads remain.

## Risks

- Stable imports share module state across cases. The test retains
explicit mock resets, adapter unregister, and profile/environment
restoration.
- The affected model route reads profile qualification and environment
fixtures when it handles the request. These cases do not require a new
module instance for each environment change.
- No runtime or schema changes. No test deadline increase.

## Model Used

OpenAI `gpt-6.1-sol`, Extra High reasoning. The model used repository
tools, code execution, 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 13:50:15 -06:00
DottaandPaperclip f836fec59e perf(budgets): avoid company lock for routine eligibility checks (#18830)
## Thinking Path

> - Paperclip helps people manage AI agents at work.
> - The budget service checks whether an agent can start work.
> - Preliminary checks acquired the company accounting lock even with no
budget policy.
> - Independent checks then waited behind accounting writes for the same
company.
> - This pull request reads one consistent snapshot before it decides
whether reconciliation is needed.
> - Healthy checks avoid the writer lock. State changes and final paid
admission retain their existing authority.
> - The benefit is less company-wide contention during routine
eligibility checks.

## Linked Issues or Issue Description

Refs #17697 (the preliminary-check portion). Refs #17817 (related
serialization in agent configuration).

Related approach: #17966. That PR adds a precheck for policies that
cannot block. This PR also avoids the lock for healthy active hard-stop
policies and manual pause reads. It uses a repeatable-read snapshot and
leaves public transactional admission locked. It does not add the
inactive-policy spend filtering proposed in #17966.

**Current behavior**

Every public `getInvocationBlock` call takes the company writer lock.
Healthy checks can wait behind an unrelated receipt or settlement
operation.

**Proposed behavior**

Read current scope state, policies, exposure and relevant incidents in a
read-only snapshot. Return directly when no transition is needed.
Otherwise discard the snapshot and reconcile current state under the
company lock.

**Reason and benefit**

Independent preliminary checks can finish while another transaction owns
the company row. Final reservations and paid-work admission still
serialize against current accounting state.

## What Changed

- Separate preliminary eligibility inspection from mutating
reconciliation in the budget service.
- Share scope loading, pause decisions and soft-incident resolution
conditions between the two paths.
- Keep no-policy checks free of exposure scans and incident reads.
- Add inspection and reconciliation spans to the existing operator-gated
Observability path.
- Add PostgreSQL contention tests and regressions for pauses, holds,
incidents, recovering native accounting and company boundaries.
- Use the shared fixture startup timeout. Per-test deadlines and the 500
ms lock timeout stay intact.
- Update the implementation contract and tracing documentation. Add
dated investigation and future-design notes. The future accounting
architecture remains a proposal.

## Verification

- 73 targeted tests passed across eight files on implementation commit
`244db565`. No tests were skipped. The files cover budget service,
enforcement scoping and delivery, live receipt attempts, budget history,
run settlement, heartbeat accounting and budget tracing.
- Real PostgreSQL tests hold the company accounting lock while routine
checks finish. The same reader's authoritative admission instead fails
with PostgreSQL lock timeout `55P03`.
- Full workspace typecheck passed before the rebase: `node
scripts/typecheck-low-memory.mjs`.
- Full workspace build passed before the rebase:
`PAPERCLIP_TYPECHECK_LOW_MEMORY=1 pnpm -r --workspace-concurrency=1
build`. The Runner release binary was rebuilt from source.
- `node scripts/check-module-boundaries.mjs` and `git diff --check`
passed.
- Initial local setup failed before assertions: cold fixture setup
exceeded 20 seconds, then a missing SDK build was exposed. The fixture
now uses the shared 90-second startup allowance. The SDK was built
before the successful test run. An incomplete local Copilot artifact
also blocked the first build; the pinned installer repaired it before
the successful canonical retry.
- The branch rebased cleanly onto master `83b5f7d486`. All 73 targeted
tests then passed again on clean head
`244db565ae206502b100c3cdf5cd86a0ae91b372` (eight files, no skips). The
full local Vitest suite was not run; remote CI must verify the wider
suite.
- Initial remote CI failed three monitor-scheduler cases at the suite's
three-second heartbeat-idle wait. The unchanged suite then passed all 21
tests locally (`pnpm exec vitest run
src/__tests__/issue-monitor-scheduler.test.ts`, 70.13 seconds). The one
failed-job rerun was cancelled after the review follow-up was pushed; it
produced no pass/fail result. No scheduler assertions or timeouts were
changed. On updated head `ba2ab2a0`, the unchanged monitor suite passed
all 21 tests in 38.381 seconds in [server shard
10/12](https://github.com/paperclipai/paperclip/actions/runs/41702687492/job/126151059714).
That shard passed 115 files and 2,677 tests, with no failures (one file
and 20 tests skipped by existing suite configuration).
- The independent review identified missing recovering-run coverage. The
added contention case requires the locked fallback while another
accounting writer holds the company row, then verifies the recovery
block without pausing or cancelling recovery. The regression is in
follow-up commit `ba2ab2a0b001f7bd69e0f86fb89a842e79b0e2b8`. All 19
budget-service tests passed locally (no skips, 48.89 seconds), and `pnpm
--filter @paperclipai/server typecheck` passed after the follow-up.
- The updated CI run then hit a separate plugin-refresh fixture timeout
at its final re-enable settle. The unchanged
`external-object-refresh-work.test.ts` suite passed all 24 tests locally
(52.02 seconds). Inspection identified a possible fake-clock race: its
quiet helper can observe no scheduled deadline while reconstruction is
still active, then encounter the existing 30-second reconstruction
cooldown. The one same-head failed-job retry
[passed](https://github.com/paperclipai/paperclip/actions/runs/41702687492/job/126158227876):
refresh suite 24/24, shard 115 files and 4,125 tests passed (one file
and four tests skipped by existing configuration). No assertions,
timeouts, or product code were changed.
- [Full
CI](https://github.com/paperclipai/paperclip/actions/runs/41702687492)
passed on exact head `ba2ab2a0b001f7bd69e0f86fb89a842e79b0e2b8`: build,
typecheck, Runner checks, all server/workspace suites and E2E. Both
required aggregates are successful. All 59 checks are terminal: 54
successful, two advisory neutral and three expected skips. The PR is
mergeable with a clean merge state.
- Deployment latency and CPU effects have not been measured.
- Greptile reviewed updated head
`ba2ab2a0b001f7bd69e0f86fb89a842e79b0e2b8`: 5/5, no actionable findings
or unresolved Greptile threads.
- Independent Paperclip review on the updated head: advisory 4/5, no
blocker, recovery gap confirmed resolved. One canonical maintainability
advisory remains visible and is documented below.

## Risks

- Preliminary inspection can precede a concurrent policy or pause
change. It grants no paid dispatch authority. The final locked admission
and reservation checks remain required.
- Healthy configured policies add an incident lookup. An unheld blocking
scope can compute exposure during both inspection and locked
revalidation. Already-held scopes skip the first exposure computation.
- Holds and incidents must retain their existing transition effects. The
fallback reads them again under the company lock, rather than mutating
from the earlier snapshot.
- Eligibility inspection and reconciliation still maintain separate
transition lists. Future reconciliation changes must update the
inspection predicate and its contention coverage together; consolidating
the effects model remains a nonblocking review follow-up.
- No schema or API response change is required. This change does not
relax the current budget enforcement contract.

## Model Used

OpenAI Codex with model `gpt-6.1-sol`. Used reasoning, repository
inspection, code execution and delegated review. The session did 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 13:48:56 -06:00
DottaandPaperclip e491b23eb5 fix(runner): retire sandbox suspension receipts after admission (#18829)
## Thinking Path

> - Paperclip helps people manage AI agents and their work.
> - Native Runner sessions preserve a conversation across task turns.
> - Remote suspension records a local receipt for the stopped Runner
identity.
> - The built-in sandbox path now writes the same receipt as plugin
tasks.
> - Admission cleared that receipt only for plugin tasks.
> - A later warm turn could reject a clean journal because the old
receipt named an earlier run.
> - This pull request clears the receipt after successful admission for
both sandbox paths.

## Linked Issues or Issue Description

Refs #18343 and #18554 for the shared suspension receipt producer. Refs
#18718 for the related controller projection refresh.

**What happened?**

After a suspended built-in sandbox session resumed, its next warm turn
could fail with `runner_state_identity_mismatch:
prior_owner_terminal_state_indeterminate`. Its journal named the
completed resumed run. Its local suspension receipt still named the
older run.

**Expected behavior**

Successful admission must retire the prior suspension receipt. The next
compatible warm turn must reuse the admitted session and provider
history once. A failed admission must retain recovery evidence.

**Steps to reproduce**

1. Complete and suspend remote turn A.
2. Resume turn B through the built-in sandbox transport.
3. Keep the controller warm, with full Runner state owned by the
sandbox.
4. Admit turn C. The old local receipt conflicts with B's journal
identity.

**Paperclip version or commit**

Reproduced in the executor regression on base `367f29478d`.

**Deployment mode**

Native Runner with the built-in remote sandbox transport and a warm
lifecycle.

## What Changed

- Clear the local suspension receipt after a remote sandbox session
passes provider recovery and durable admission. Apply this to built-in
runners and plugin tasks.
- Extend the existing three-turn warm admission regression to cover a
suspended receipt and remote-only Runner state.
- Keep an active-owner control that rejects admission and retains its
receipt. Keep the existing foreign-owner, retirement, configuration,
plugin, and unchanged-session controls.
- Add a real Linux Runner continuity case that suspends A, resumes B,
and keeps B warm for C. Register the test fixture's owned local API
listener.
- Document receipt ownership and the new continuity case.

## Verification

- The new behavioral regression failed before the production change.
Turn C raised the exact identity mismatch above. The active-owner
control passed and retained its receipt.
- All 1,087 native executor tests pass after the fix.
- Both opt-in Linux process cases pass at the final commit: ordinary
warm reuse and suspension followed by warm reuse. They use PostgreSQL,
TLS, current-source Rust binaries, and a deterministic Codex fixture.
Each preserves one conversation, starts exactly three turns, and reuses
the warm Runner and provider on turn C.
- Full repository typecheck passes with `node
scripts/typecheck-low-memory.mjs`. Full production build passes with
`pnpm build`.
- All current-head CI checks pass at `a95a6fdfe8`, including the full
test shards, browser shards, Rust checks, build, and typecheck. The
policy job needed one retry after repository checkout timed out before
checks ran.
- Greptile and Ponytail each give 5/5 at the final head. There are no
open review threads or findings.
- The local full `pnpm test:run` attempt passed 351 cases in its first
chat shard. The session interruption stopped it during the second shard,
so it has no complete local verdict. The complete CI test run passes.
- Module boundary checks and `git diff --check` pass.

## Risks

- A receipt removed before admission could discard recovery evidence.
Removal stays in the successful `onSession` callback. The negative
admission control checks retention.
- A stale owner must not gain authority. Journal, run, lease, and scope
checks remain in place before the callback.
- Historical contradictory or quarantined state still requires the
existing verified recovery path. This change does not delete those
records.
- No migration or provider configuration change is required.

## Model Used

- OpenAI Codex, based on GPT-6. The runtime does not expose a more
specific model ID or context limit. Used reasoning, repository tools,
code execution, and tests.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` 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 13:42:21 -06:00
DottaandPaperclip 2035118526 fix(workspaces): preserve archived data during remote migration (#18826)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Remote workspace storage lets the web server run without a
persistent volume.
> - Existing deployments also contain archived and historical task
workspaces.
> - Retention can remove their folders while preserving encrypted
restore points or Git reconstruction records.
> - The importer rejected those records and only accepted the task's
current workspace.
> - This pull request imports verified saved data without reopening or
rebinding tasks.
> - The benefit is preservation of existing work during an explicit
storage migration.

## Linked Issues or Issue Description

Refs #18403 and #17472.

**What happened?**

An explicit remote-workspace import rejected archived records,
historical task bindings, and removed folders with valid retained data.

**Expected behavior**

Import the recorded files or exact Git commit. Preserve archived state
and the task's current binding. Refuse missing, expired, corrupt, or
changed sources.

**Steps to reproduce**

Archive a task directory with a restore point, remove its folder, and
run the remote-workspace import. Repeat with a Git restore point
containing an unpushed commit and staged and unstaged edits. The
previous importer reports an inactive workspace or missing source.

## What Changed

- Preserve lifecycle state and current task binding while importing
archived and historical records. Recheck the binding before publication
and on resume.
- Expose the task-directory owner's validated restore-point reader.
Reuse it for ordinary restoration and migration.
- Restore archived directories and Git data into private scratch space.
Support saved Git recipes and exact legacy branch-deletion receipts.
- Recover pruned unpushed Git commits from the encrypted bundle and its
explicit prerequisites.
- Keep independent snapshot download, restoration, source checks, writer
guards, encryption, and atomic publication.
- Document the supported import sources and the remaining
tenant-enrollment gaps.

## Verification

- Before production edits, two new regressions failed on archived status
and historical task binding.
- Final focused verification: 58 tests pass across remote-workspace
migration and isolated task-directory restoration.
- Real PostgreSQL, Git repositories, encrypted storage, and restored
file bytes cover cold restoration after removing the old folder and
source object, pruned unpushed history, index and worktree edits,
corrupt/missing/expired sources, binding changes, and source-folder
races.
- A Git fixture initially lacked runtime ownership metadata. Correcting
the fixture made the valid retained-source case pass; no production
validation was weakened.
- Full repository typecheck and build passed. Full `pnpm test:run`
stopped in chat integration shard 2 after two failures: a retry-notice
timeout and a subsequent fake-timer error. All four cases in that
retry-notice group passed in a separate focused rerun without source
changes; the complete suite has not passed locally. All exact-head CI
jobs passed. The separate Paperclip Review app check is still queued;
all existing review threads are resolved. Server typecheck passed after
the review fixes.
- Both review findings were reproduced: the real cleanup owner's
verified-tip receipt was rejected, and a lost upload reply for a legacy
Git source failed with `operation_content_changed`. The fixes accept the
owner's scoped receipt metadata and rebuild reconstructed Git indexes
from their staged trees. Both regressions now pass, including retained
staged and unstaged edits.
- Additional coverage restores from a fresh HTTP clone after deleting
the original repository, imports a nested local checkout with encrypted
settings, and rejects an unapproved nested source without publishing
remote authority.
- This PR does not claim a completed tenant enrollment or a new
zero-downtime deployment proof.

## Risks

Imports can fail when old receipts or repository objects are
unavailable. They retain their migration journal and leave the original
state intact. Operators must resolve pending writers and recovery before
importing. Shared physical roots, nested worktrees, and unresolved
sandbox recovery require separate migration work. This change does not
remove those blockers or alter automatic retention.

## 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 version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either linked existing issues or described the issue in-PR
following the relevant issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal task
ID or instance-derived details
- [x] I have run the focused tests locally and they pass; full
typecheck/build passed and the full-suite failure is documented
- [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 13:36:22 -06:00
fef86e32e9 feat(execution-admission): add project concurrency policy and admission storage (#18746)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Projects group tasks. Agents run tasks as heartbeat runs.
> - Today, workspace mode and shared-workspace flags decide whether
tasks run at the same time. A project cannot set its own limit on
concurrent task executions.
> - A correct project limit must count every project that a task uses,
including @-mentioned projects. It must also keep the count when a
project is archived or deleted, and when a run is admitted more than one
time.
> - This change cannot go into one pull request with claims and
settlement. Thus this pull request adds only the policy semantics and
the durable accounting storage that later claims will use.
> - This pull request adds a new `execution-admission` module, three
tables and the deletion guards. No route writes a cap and no claim path
enforces it.
> - The benefit is a tested storage contract (constraints, idempotency,
rollback, indexed counts, lifecycle preservation) before any behavior
changes.

## Linked Issues or Issue Description

Refs #2831, Refs #14333 (related agent-level and adapter-level limits;
not duplicates).

**Subsystem affected**

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

**Problem or motivation**

A project cannot limit how many of its task executions are active. The
current shared-workspace flags mix resource safety with capacity. A
future project limit needs storage that records each admission attempt
and every project that the attempt charges. Run IDs cannot be the
identity, because a run can be released before dispatch and admitted
again.

**Proposed solution**

Add a separate policy table that keeps "never configured" apart from
"explicit no limit". Add admission epochs with one charge row per
distinct project. Settlement deletes the charge set in one transaction.
Later pull requests integrate claims, settlement, waits and the public
control.

**Alternatives considered**

Columns on `projects` would leak an unenforced cap through every project
reader. A project column or a `uuid[]` on `heartbeat_runs` cannot
enforce per-project uniqueness or company scope. `budget_reservations`
has no project set. A counter would be a second source of truth.

## What Changed

- Added `project_task_execution_policies`. No row means never
configured. A null maximum means an explicit "no limit". A check accepts
only positive maximums. A composite foreign key keeps the project and
company together. The row is deleted with its project.
- Added `execution_admissions`. Each row is one admission attempt of a
run: `(run_id, attempt)` is unique, and a partial unique index allows
one unsettled attempt per run. It keeps the context revision, the
nullable primary project (provenance, no foreign key) and the settlement
identity. A check sets `settlement_id` and `settled_at` together.
- Added `execution_admission_project_charges`. It has one row per
distinct admitted project, unlimited projects included. Composite
foreign keys keep the admission and the project in one company. The
project foreign key has no cascade, so a charge cannot disappear with
its project. `(company_id, project_id)` is indexed for active counts.
- Added migration `0426_absent_guardian.sql` (renumbered from `0423`,
then `0425`, as master added `0423`–`0425`). The generated SQL was
reordered so the unique indexes exist before the composite foreign keys
that need them. Snapshots `0419` and `0420` were pruned to keep five
snapshots.
- Added the `execution-admission` module (domain → application →
adapters):
- Domain: policy patch parsing and application (omitted keeps, null
clears, partial updates keep other fields, malformed values are
rejected), canonical project-set preparation (primary ∪ references,
deduplicated, ascending, bounded with an explicit `too_many_projects`
result), and the settlement replay decision.
- Application: internal `readTaskExecutionPolicy` and
`updateTaskExecutionPolicy` commands. No route, tool, CLI or import
calls them.
- Adapter: the project-set lock (`FOR NO KEY UPDATE`, ascending, one
statement), the policy store, and the storage transitions
`recordAdmission`, `settleAdmission` and `countActiveProjectCharges`.
These transitions are not exported from the module entry point. Claims
(later) must add the capacity check and the lifecycle evidence.
- Project deletion returns `409 project_execution_admission_pending`
while the project has a charge. Agent deletion returns `409
agent_execution_admission_pending` while its runs have an unsettled
admission, and removes settled history. Company deletion calls a new
module participant before it deletes runs and projects.
- Documented the tables, the lifecycle rules and the proposed global
lock order in `doc/DATABASE.md`. Added module ownership to
`server/src/modules/README.md`.

## Verification

- `cd server && npx vitest run src/modules/execution-admission` → 2
files, 26 tests pass (the Greptile finding on an empty primary ID added
an assertion to an existing test). The adapter tests use embedded
PostgreSQL and cover:
  - policy semantics, the maximum check and the company foreign key;
- charges for every distinct project, a primaryless set and an empty set
(no rows);
- replay of one admission ID, conflict on a changed record, refusal of a
second live attempt and the partial unique index;
- settlement of the whole set once, replay, a different settlement, then
attempt 2 for the same run;
- company isolation of projects, runs and admission IDs, the
duplicate-charge key, and rollback of an aborted transaction;
- active counts per project, and an `EXPLAIN` that uses
`execution_admission_project_charges_project_idx`;
- archive keeps charges, `projectService.remove` returns 409, a direct
project delete fails on the foreign key, and delete succeeds after
settlement;
- `deleteAgentDependencies` returns 409 while unsettled, and
`deleteCompany` removes only that company's admissions.
- `cd packages/db && npx vitest run
src/execution-admission-migration.test.ts` → pass. It removes the new
tables and receipt, seeds a legacy project and run, applies pending
migrations, and compares the existing rows.
- `pnpm --filter @paperclipai/db check:migrations` → pass. Five
snapshots remain (`0421`, `0422`, `0424`, `0425`, `0426`; `0423` is
SQL-only on master).
- `node scripts/check-module-boundaries.mjs` → pass.
- `packages/db` `tsc --noEmit` → pass. A scoped `tsc` of the module
entry points and domain tests → pass.
- Existing tests for the changed deletion services → 6 files, 161 tests
pass (`resource-lifecycle-events`, `prompt-injection-prevention`,
`agent-lifecycle/lifecycle`, `case-deletion`,
`project-workspace-root-provenance`, `companies-service`).
- Not run locally: the full `server` typecheck. `tsc` and `tsgo` were
stopped by the out-of-memory killer on the 7 GB machine (exit 137, also
single-threaded). The full `pnpm -r typecheck`, `pnpm test:run` and
`pnpm build` were not run locally; CI ran them on head `520b69d553`: all
gates pass, including Typecheck, Build and the 12 server test shards.
After merging master (`1c9ca5829f`) into head `c088729361`, the
migration check, migration upgrade test, module tests, module-boundary
check and `packages/db` typecheck were rerun locally and pass; CI is
rerunning the full suite.

## Risks

- Migration risk is low. It creates three empty tables and indexes. It
does not change existing tables or rows.
- Behavior changes only when admission rows exist. Nothing writes them
yet, so project, agent and company deletion behave as before.
- Later claim work must take the project-set lock in the documented
order. A claim that takes project locks after issue locks could deadlock
with project deletion.
- Settlement deletes charge rows, so a settled admission keeps its
primary project and context revision but not its full project set.

> 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`) 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: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 11:35:28 -08:00
DottaandPaperclip d8a9b3eb0c fix(ui): show task identifiers in activity toasts (#18802)
Project the company-scoped identifier for authorized task events and use it in toast titles, fallback bodies, and links. Retain authorized delivery when the optional identifier lookup fails.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-11-03 13:28:15 -06:00
DottaandPaperclip fb6554ac62 fix(native): settle source wake with terminal projection (#18804)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Each agent wake request can bind to one heartbeat run.
> - Native recovery can finish and project a run after its live executor
has stopped.
> - The legacy completion callback settles the wake, but native terminal
projection did not.
> - A succeeded run could therefore leave its exact wake claimed with no
finish time.
> - This pull request settles that claim in each native terminal
projection transaction and reuses durable projection recovery for old
committed rows.
> - The benefit is consistent run and wake receipts without a provider
replay or a new sweeper.

## Linked Issues or Issue Description

Refs #13856 and #10109. These open PRs describe orphan claimed wakes and
cleanup after terminal persistence. This change fixes the native
projection owner and uses its existing durable worker. It does not
modify those contributor branches.

**What happened?**

A native run can be accepted and committed, then reach a terminal state
through recovery. Its source wake stays claimed because the live
completion callback does not return to settle it.

**Expected behavior**

Terminal projection must settle only the same company's exact source
wake bound to that agent and run. Completed, failed and cancelled
outcomes must retain their meaning. Other live, queued, cancelled,
unbound or newly rebound wake records must remain unchanged.

**Steps to reproduce**

Commit an accepted native result in the real database fixture. Leave its
source wake claimed. Call native finalization with run-status projection
enabled, without the live completion callback. Before this change, the
run becomes succeeded and finished while the exact wake remains claimed.

## What Changed

- Settle the exact source claim inside native finalization, committed
recovery, and exhausted-finalization projection transactions. Fence it
by wake ID, company, agent, run and current claimed state.
- Lock the matching claim and register settlement notifications before
the write. A failed transaction rolls back both run projection and wake
settlement.
- Let the existing durable projection worker discover a terminal run
with an unfinished exact claim. A repaired committed row leaves the
pending set. No new timer, sweep or table is added.
- Add real database regressions for native recovery, all three terminal
outcomes, old committed receipts, idempotence, scoped denial controls,
notification ordering and rollback. Cover provider/workspace
finalization exhaustion and denial when the caller does not own terminal
projection.
- Preserve the existing provider, accounting, workspace, permissions and
result contracts. No API, schema, user command, access-map entry or
documentation contract changes are needed.

## Verification

- The new native coordinator/finalizer regression failed before the
production change: the run was succeeded and committed, but the bound
wake was still claimed.
- The initial focused database run did not reach the assertion because
the SDK was unbuilt. A second attempt hit sandbox loopback restrictions.
A third exposed skipped package library hydration. These setup failures
were corrected before the meaningful red was captured.
- Real PostgreSQL projection and response suites passed 89 tests before
the independent review correction. The reviewer requested notification
intent registration before mutation. The correction adds a
notification-order and rollback case. All 90 tests now pass across both
suites.
- Server typecheck passed after building the isolated checkout's Runner
and SDK prerequisites. Fresh corrected-source server typecheck and
production server build also pass. Full repository checks run in
current-head CI. The earlier serialized-9 CI failure was the
independently repaired inbox snapshot fixture in #18798 (expected nine
reader calls, observed eight). A normal merge of the containing master
supplies that fix; the native suites plus the repaired inbox suite now
pass 96 tests together at merge head
`456c92ef8d60c3fcbeaf66ff8010a7ebd3d1109d`. Server typecheck passes.
That head was superseded by the reviewed retry-exhaustion correction
below. The subsequent review found a third terminal write in retry
exhaustion. Real database provider and workspace exhaustion regressions
failed with claimed/null-finished wakes before the fix. The same fenced
helper now runs inside that transaction. At final source head
`9718d4c2ec6066accf549dc6fea795891f6b90ec`, all 100 focused tests,
server typecheck and production server build pass, including
projection-disabled denial and rollback of run/coordinator/wake on
notification failure.
- Final current-head CI passed: 54 success, 2 skipped, and the neutral
Paperclip Review at 5/5. Greptile also reports 5/5. Required ci / verify
and ci / e2e passed. All review threads are resolved.
- No live task write, provider work, accounting repair, deployed replay
or manual rollout was performed.

## Risks

- A stale wake binding must never be settled. The update requires the
exact wake ID, company, agent, run and claimed state. Tests cover
unbound and newer-run bindings, foreign company and agent, queued and
cancelled records, and retained live ownership.
- Old claimed debt now participates in the existing projection
predicate. It uses the source wake primary key and stops matching after
settlement. Existing quiet-worker tests remain in the focused suite.
- Wake settlement must share the projection transaction. Notification
failure and outer rollback tests preserve both original rows.
- Interruption, timeout, retained historical terminal-failure
coordinators, and delayed response presentation keep their current
owners. This change does not infer or replay provider work.

## Model Used

OpenAI Codex, `gpt-6.1-sol`, Extra High reasoning, with source
inspection, code execution and regression testing. Context-window
capacity is not exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used with version and capability
details
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have linked existing public issues or described the issue in
this PR
- [x] I have not referenced internal or instance-local Paperclip issues
or links
- [x] My branch name describes the change and contains no internal
ticket ID
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation where needed; existing
contracts are restored
- [x] I have considered and documented 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 merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 13:17:16 -06:00
DottaandPaperclip e0c6992426 fix(heartbeat): prevent saved input starvation during queue reloads (#18819)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Accepted messages and task Retry requests use durable wake receipts.
> - The recovery worker loads receipts in pages of 100 and checks
batches of 25.
> - Company activity can reload those pages before the pending batch
drains.
> - Reloads reset ready receipts' dispatch times and can repeatedly
favor receipts already checked.
> - This pull request preserves the priority of unchecked ready receipts
so saved input can reach normal admission.

## Linked Issues or Issue Description

**What happened?**

Saved input can remain deferred after the preceding run finishes.
Repeated company eligibility changes reset the dispatch times of pending
receipts during paginated reconstruction. A PostgreSQL regression checks
only 75 of 178 receipts on the original code despite twelve eligibility
changes.

**Expected behavior**

Each ready receipt must reach its admission check. Existing wait
deadlines, retry backoffs, authorization, and execution gates must still
apply.

**Steps to reproduce**

1. Seed more than one page of deferred receipts.
2. Keep those receipts pending after their checks.
3. Send company eligibility notifications between scheduler passes.
4. Check that every receipt is reconsidered, including receipts later in
the backlog.

**Paperclip version or commit**

Reproduced on `367f29478d`.

**Deployment mode**

Built from source with PostgreSQL. The defect occurs before provider
execution.

Related: #10195 reports a similar starvation symptom in a different
promotion loop. #18621 added saved-input Retry after a successful run.
This change fixes fairness in the deferred-receipt worker.

## What Changed

- Add an optional entry merge callback to scoped deadline
reconstruction.
- Use it in the deferred-receipt worker to retain unread ready receipts'
priority while refreshing their state.
- Place receipts whose wait has expired into the ready queue during
reconstruction.
- Keep real wait, claim, and dispatch deadlines authoritative.
- Add a paginated starvation regression, real process dispatch coverage,
and GitHub lock-collision recovery coverage.
- Document the recovery fairness rule.

## Verification

- The PostgreSQL starvation regression checks only 75 of 178 receipts
before the fix. It checks all 178 afterward, both with ordinary receipts
and with expired execution waits.
- Seven focused scheduler cases pass, including moved interrupt grace,
moved native error deadline, fresh claim/wait deadlines, and
unchanged-gate grace.
- GitHub endpoint lock-collision recovery, retained board authority,
HTTP Retry, and five current admission-gate cases pass.
- Real process acceptance passes on the final code: task Retry crosses
the repeatedly invalidated backlog, reconstructs its persisted queued
run after a worker restart, saves `receipt-proof` in run output, and has
one successor for the Retry receipt. The test holds executor capacity
while advancing queue time, then starts fresh workers at PostgreSQL time
before releasing a real process lease. The defect is in admission
scheduling before provider execution; this acceptance uses PostgreSQL
and a local process adapter.
- The full workspace build and typecheck (`node
scripts/typecheck-low-memory.mjs`) pass locally. The affected worker
suites pass 52 of 53 cases together; the existing native multi-company
batch case times out in that combined run and passes in isolation
without code changes. All new regression and acceptance cases pass.
- Local `pnpm test:run` stops in its first chat shard on three 15-second
timeouts (Teams long native HTTP picture delivery, direct final-response
materialization, and contended inbound notices). All three pass in
isolation without code changes. Later full-suite groups did not run
locally after that shard failure.
- [The full GitHub Actions matrix
passes](https://github.com/paperclipai/paperclip/actions/runs/41697489374)
on `ac91df2cc6db6c77529d923177b375b8c92efd45`, including all
general-test, serialized-server, browser, build, and typecheck jobs.
Server shard 8 initially failed an upstream process-adoption test and
passed its one rerun without code changes.
- [Greptile is
5/5](https://github.com/paperclipai/paperclip/pull/18819#issuecomment-6612091535)
on the same current head, with no actionable findings or unresolved
review threads. The separate Paperclip Review check is neutral; its
previous request was bound to an older commit.

Focused command:

```sh
pnpm exec vitest run server/src/__tests__/deferred-execution-receipt-work.test.ts server/src/__tests__/scoped-deadline-work.test.ts
```

## Risks

- Ready receipt order changes during repeated reconstruction. Admission
still reads current durable state.
- A ready receipt must not inherit an obsolete priority after it
acquires a future deadline. The merge applies only when both pending and
loaded entries are immediate. Deadline safety tests cover that boundary.
- The merge callback is optional. Other scoped workers retain their
current reconstruction behavior.

## Model Used

OpenAI `gpt-6.1-sol` in Codex, max reasoning effort, with tool use and
local code execution. The runtime does not expose the context-window
size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass (focused regressions and
acceptance; full-run timing limitations 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 13:17:00 -06:00
Nicky LeachandClaude Opus 5.5 8ab89f41e4 fix(build): typecheck test projects in 4 GiB (#18738)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Contributors and agents typecheck this repository, often on machines
with limited memory
> - paperclipai/paperclip#18737 made `pnpm -r typecheck` fit in 4 GiB
because the server now checks in layers
> - The test projects outside `pnpm -r` still import server source
through relative paths, so each one checks most of the server as one
program
> - These projects peaked between 6.9 GiB and 14.9 GiB, so they fail on
a 4 GiB machine
> - This pull request makes these projects read the server layers as
declarations, and adds a CI step that keeps them that way
> - The benefit is that every TypeScript project in the repository can
now be checked in 4 GiB, and a later change cannot silently undo this

## Linked Issues or Issue Description

Refs paperclipai/paperclip#18737, which is merged. This pull request
builds on the server layers that it added. No issue exists for this gap.

**What happened?**

`tsc -p tests/runner-e2e/tsconfig.json` peaked at 14.9 GiB at default
settings. `tests/speko-native` peaked at 12.2 GiB,
`server/src/__tests__/helpers/maintenance` at 9.6 GiB, and
`tests/lifecycle-baseline` at 6.9 GiB. On one thread, `tests/runner-e2e`
still peaked at 7.7 GiB.

**Expected behavior**

Each project passes with 4 GiB of memory.

**Steps to reproduce**

Run `tsc -p tests/runner-e2e/tsconfig.json` in a Linux container with
`--memory=4g --memory-swap=4g`. The kernel stops the process.

## What Changed

- Added `tsconfig.layers.json` to the four projects. Each file extends
the project's `tsconfig.json` and references the generated server
declarations.
- Added `scripts/typecheck-test-projects.mjs`. It builds the server
declarations and then checks the named projects, or all four. It takes
`--low-memory`.
- Changed `test:e2e:runner:typecheck` and
`test:lifecycle-baseline:typecheck` to call that script.
`docker-runner-check.yml` calls the first script by name, so that
workflow needs no edit.
- Added `--declarations-only` to `server/scripts/typecheck-layers.mjs`.
The script now also generates `tsconfig.declarations.json`, so
hand-written configs do not name individual layers.
- Exported `runPnpm` from `scripts/typecheck-low-memory.mjs`, so the new
script can use it.
- Added a step to the `Typecheck + Release Registry` job. It runs
straight after the server typecheck and reuses its declarations through
`--reuse-declarations`.
- The script now fails if a project loads server source, even when the
types pass. Test support files, declaration files and JSON files do not
count. This check does not enforce a memory limit.
- Added a unit test that fails if a new project under `tests/` is
neither in `PROJECTS` nor named as a single program.
`tests/runner-acceptance` is the one named project.
- Added `server/src/types/express.d.ts` to
`tests/lifecycle-baseline/tsconfig.json`. That plain config failed alone
with 11 TS2339 errors, and it now passes.
- Exported `isTestFile` from `server/scripts/typecheck-layers.mjs`, so
the source check uses the same rule as the layers.
- Turned off `declaration` in the layered configs. These projects emit
nothing, and declaration checks gave a false TS2883 error when
`tests/runner-e2e` read the server as declarations.
- Added tests, one of which runs the real compiler. Updated
`doc/DEVELOPING.md` and `tests/lifecycle-baseline/README.md`.

The references are not in each project's `tsconfig.json` because Vitest
reads that file. A test showed that Vitest fails when a referenced
config does not exist yet. The `tsconfig.json` files do not change, so
editors and Vitest see the same configs as before.

No new `package.json` scripts were added, as `.greptile/rules.md`
requires.

## Verification

I tested commit `9763880fab`, which is based on `1c9ca5829f`. I copied
the commit into a named Docker volume and ran a fresh install. Each row
ran in its own Linux arm64 container with `--memory=4g --memory-swap=4g`
and 8 CPUs. The first row started with no build output. All rows used
`--low-memory`. CI does not set a memory limit. I used the limit only
for this measurement.

| Project | Errors | Peak (cgroup) | Max process RSS | OOM kills | Time
|
| --- | --- | --- | --- | --- | --- |
| `tests/runner-e2e` | 0 | 3.34 GiB | 3.06 GiB | 0 | 57 s |
| `tests/speko-native` | 0 | 3.60 GiB | 3.27 GiB | 0 | 51 s |
| `tests/lifecycle-baseline` | 0 | 3.43 GiB | 3.15 GiB | 0 | 50 s |
| `server/src/__tests__/helpers/maintenance` | 0 | 3.39 GiB | 3.13 GiB |
0 | 52 s |

`PAPERCLIP_TYPECHECK_LOW_MEMORY=1 pnpm test:e2e:runner:typecheck` and
`PAPERCLIP_TYPECHECK_LOW_MEMORY=1 pnpm
test:lifecycle-baseline:typecheck` also passed in the same limit, with
peaks of 3.55 GiB and 3.52 GiB.

I also ran the CI sequence in one container with the same limit: `pnpm
--filter @paperclipai/server typecheck`, then `node
scripts/typecheck-test-projects.mjs --low-memory --reuse-declarations`.
Both passed, with a peak of 3.46 GiB. The new step took 16 s.

The server layer build sets the peak in each row. Against the
declarations, the projects peaked between 0.9 GiB and 2.2 GiB at default
settings on macOS.

Server source files that each project loads, without test support,
declaration or JSON files:

| Project | Plain `tsconfig.json` | `tsconfig.layers.json` |
| --- | --- | --- |
| `tests/runner-e2e` | 1,345 | 0 |
| `tests/speko-native` | 1,647 | 0 |
| `tests/lifecycle-baseline` | 1,343 | 0 |
| `server/src/__tests__/helpers/maintenance` | 1,716 | 0 |

I counted these on `3f0f31b2f9`. The source check in the script
confirmed 0 for all four projects on this commit.

**Limits of this proof:**

- Without `--low-memory`, the 4 GiB run does not pass. The kernel
stopped the layer build for `tests/runner-e2e` on `3f0f31b2f9`, and the
script exited with status 1. I did not repeat this run on the final
commit. The script does not turn on low-memory mode by itself.
- The Linux runs used arm64, not x64.
- The new CI step has not run in CI. `pr-trusted.yml` runs from
`@master`, so the step first runs after this pull request merges. Check
its first run.

**Other checks on this commit:**

- `server/scripts/typecheck-layers.test.mjs`: 38 tests pass. One test
uses the real compiler to show that the source check tells a layered
config from a config with no references.
- With the references removed from
`tests/speko-native/tsconfig.layers.json`, the script exits with status
1 and reports 1,644 server source files.
- With the declarations removed, `--reuse-declarations` exits with
status 1 and reports TS6053.
- A type error in a test project makes the script exit with status 1.
- `tests/lifecycle-baseline/tsconfig.json` passes with 0 errors.
- `tests/lifecycle-baseline` Vitest suite: 533 tests pass.
- Workflow tests that read `pr-trusted.yml` and CI routing: 114 tests
pass.

**Earlier checks:**

- `server/src/__tests__/server-package-build-script.test.ts` passed 10
tests on `3f0f31b2f9`. This test failed in `ci / General tests (server
(9/12))` on an older head. It tested a build script split that the final
#18737 removed.
- A changed signature in `server/src/services/run-liveness.ts` failed
`tests/lifecycle-baseline/authority.test.ts` through the rebuilt
declarations, before the rebases. When I restored the signature, the
check passed.

**Not run locally:**

- `pnpm test:run` and `pnpm build`. CI covers these.

**Failures that this pull request does not cause:**

`pnpm test:e2e:runner:unit` has 13 failures on this commit, in
`cursor-cloud-replay-fault`, `first-task`, `operating-receipts`,
`pi-controls-evidence` and `workflow-security`. The same 13 tests fail
on `master` at `6ef37398c8`. A comparison of the test names shows no
differences. PR CI does not run this suite.

## Risks

- Low risk to runtime behavior. No shipped code changes.
- **Slower check:** `pnpm test:e2e:runner:typecheck` now builds the
server layers on each run. Before the rebase, it took 30 s on macOS at
default settings, about the same as the old single program for that
project. On this commit it took 57 s in low-memory mode in the 4 GiB
container. The new CI step reuses the declarations, so it took 16 s.
- **Narrower check:** Server source is now checked only by the server's
own layers, not again under each project's compiler options. The
projects also skip declaration-portability checks. These projects emit
nothing, so those checks had no output to protect.
- **Unchanged path:** `tests/runner-e2e/native-completion-checks.mjs`
still runs `tsc -p tests/runner-e2e/tsconfig.json` as one program. It
needs about 15 GiB at default settings. I did not change it because that
file and its inputs are part of a fingerprinted source contract, and no
workflow calls it.
- **Layer growth:** The new checks do not measure memory. If the layers
or the import cycle grow, the checks still pass and the peak rises. This
is intentional. Crossing 4 GiB does not fail a build.
- **Project list:** The CI step checks only the projects in `PROJECTS`.
The unit test covers new projects under `tests/`, but not a new project
in another directory.
- **Reused declarations:** `--reuse-declarations` trusts the
declarations on disk. CI uses it straight after the server typecheck on
the same commit. If that typecheck fails, the job stops first. Stale
declarations can pass locally, so `doc/DEVELOPING.md` warns against
this.
- **Job dependency:** A type error in a test project now fails
`Typecheck + Release Registry`, and so `ci / verify`.
- **Not covered:** The new step runs no tests. `node
scripts/typecheck-low-memory.mjs` and the Docker image build also have
no new guard.

> 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

- Provider: Anthropic. Model: Claude Opus 5.5 (`claude-opus-5-5`),
through Claude Code with tool use and code execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [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 Opus 5.5 <noreply@anthropic.com>
2026-11-03 11:09:12 -08:00
DottaandPaperclip 4a38ff9c92 fix(ci): make Cloud readiness reruns and fixtures reliable (#18823)
## Thinking Path

> - Paperclip manages agent work and preserves task state.
> - Cloud readiness verifies an exact source revision before release.
> - Its workspace tests must wait for execution and database cleanup to finish.
> - Two attempts hit the same ten-second polling limit and read a running row.
> - The second attempt also lost AWS runners and exceeded a fixture cleanup limit.
> - This pull request waits for owned execution, bounds client shutdown, and gives verification reruns an independent runner pool.
> - Assertions, source identity and required verification gates stay in place.

## Linked Issues or Issue Description

**What happened?**

[Cloud readiness run 41684102136](https://github.com/paperclipai/paperclip/actions/runs/41684102136) failed twice on master `367f29478d14003cc5c0f161b7ce8f3164993634`.

Attempt 1 failed `heartbeat-workspace-busy.test.ts:473`: expected `succeeded`, received `running`. Attempt 2 failed the same helper in the inherited-workspace resume case at line 744. The helper polls for ten seconds, then returns the active row as if the wait finished.

Attempt 2 also exceeded the 30-second `workspace-preparation.test.ts` teardown limit. Its client close had no deadline, and database plus directory cleanup shared the default hook budget despite the fixture's existing 90-second startup budget.

Other attempt-2 failures show runner shutdown or lost communication. Several shutdown signals arrived at 18:05:17 UTC. Read-only AWS inspection confirms that all eight infrastructure-failed instances carry `runs-on-workflow-job-interrupted=true`. Five have explicit `Server.SpotInstanceTermination` reasons, including the server-1 runner that lost communication. Three were terminated through the fleet's shutdown path. CloudTrail records the Rust runner's interruption tag at 18:03:37 UTC and its termination by the RunsOn fleet role at 18:05:31 UTC, despite EC2 showing a user-initiated termination. Repeating the entire workflow reused the same Spot fleet.

**Expected behavior**

Wait for owned heartbeat execution before reading final state or scheduled retries. Bound database-client shutdown and allow the fixture's cleanup cost class to finish. Rerun verification on GitHub-hosted runners after an interrupted fleet attempt. Preserve all checks and report prior failures.

**Steps to reproduce**

1. Delay the test adapter beyond the helper's ten-second polling window.
2. Invoke the ordinary local shared-workspace run or resume the deferred sandbox run.
3. Observe the old helper return `running` before the execution completes.
4. Evaluate the old runner expressions with `github.run_attempt = 2`; they still choose the interrupted AWS pool.

**Paperclip version or commit**

Base: `367f29478d14003cc5c0f161b7ce8f3164993634`.

**Deployment mode**

GitHub Actions Cloud readiness, with disposable PostgreSQL fixtures.

Related: #11293 fixed the earlier gap between cancellation and retry-row insertion. This change waits for the complete execution instead. Duplicate searches covered the workspace test, retention fixture and runner shutdown reports.

## What Changed

- Replace both workspace-test polling helpers with the existing heartbeat completion barrier. Keep final status, retry and coordination assertions.
- Bound workspace-preparation client shutdown to one second. Apply the existing embedded-PostgreSQL fixture budget to its teardown.
- Use AWS only for the first exact-source attempt. Route later verification, chaos and cloud-migrator planning/build attempts to GitHub-hosted runners. The publisher keeps its existing routing. The final branch also incorporates upstream #18821's CODEOWNER routing fix.
- Add runner-expression coverage for numeric, string, missing and later attempt values. Document retry routing.

## Verification

- The original workflow's failed assertions and interrupted jobs remain linked above. A local two-suite run also passed all 157 assertions and failed only the original 30-second teardown hook.
- All 341 workflow contracts pass. After the master synchronization, all 347 routing and CI-planner controls pass. Actionlint passes for the three changed workflows.
- A matched delayed-adapter comparison uses disposable PostgreSQL 17 databases, the same 11-second adapter delay, and the same 90-second experimental outer budget. The old helper fails with `running`; the completion barrier passes. Production test deadlines stay unchanged.
- Earlier experiments hit a 30-second experimental outer limit and the macOS host's shared-memory limit during native database startup. The completed comparison uses task-owned databases on an existing test server. Those earlier attempts are not counted as passes.
- All 31 workspace-busy cases pass with normal test deadlines. The 126-case workspace fixture also passes with its 90-second teardown budget.
- Final-head cleanup measurements: main client close 7 ms, owned database cleanup 20,938 ms, and workspace-home removal 826 ms. A measured fixture held 124,494 PostgreSQL files (208 MiB) after repeated table resets. The measured database cleanup alone consumes most of the old 30-second hook budget. The separate baseline reproduced a timeout at that hook; the fixture now uses its established 90-second cost class.
- Repository typecheck and build pass on synchronized head `2aceb3abd144a569efddabed3b7e0550ea23ceb2`.
- All 157 focused cases pass on the synchronized branch, including complete PostgreSQL 18 fixture shutdown. [Full hosted CI](https://github.com/paperclipai/paperclip/actions/runs/41697804471) passes on the final head, including the required verification and E2E aggregates, all server/chat/workspace groups, Runner lanes and all eight browser shards. The separate GitHub quality-review workflow also passes. All 60 reported checks are terminal: 54 pass, three are informational neutral results, and three are expected skips. There are no failures or pending checks. The complete local `pnpm test:run` command was not run; targeted fixtures already encountered this host's shared-memory limit. Hosted CI supplies the full Vitest and browser matrix.
- Greptile and Paperclip Review both give synchronized head `2aceb3abd144a569efddabed3b7e0550ea23ceb2` 5/5. There are no open review threads.
- Module-boundary and whitespace checks pass.

## Risks

- Hosted reruns can have different cache hit rates and queue times. They execute the same source and verification commands.
- The first attempt retains the existing AWS Spot fleet policy. AWS evidence establishes several interruption-triggered failures; later verification attempts use an independent pool. This does not change the fleet's external capacity policy.
- The completion barrier waits for owned background execution. A hung execution still fails the test's outer deadline.
- The larger teardown budget remains finite. PostgreSQL's bounded stop protocol and cleanup errors still fail the hook.
- No production execution, database schema or provider contract changes.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, repository inspection, code edits and test execution. The runtime does not expose the exact serving model ID or context-window size.

## Checklist

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


Co-Authored-By: Paperclip <noreply@paperclip.ing>
canary/v2026.1103.0-canary.12
2026-11-03 13:08:28 -06:00
DottaandPaperclip 83b5f7d486 fix(ci): keep CODEOWNER PRs on AWS as master advances (#18821)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Its PR checks use reserved AWS capacity for authorized maintainers.
> - The routing gate waited for GitHub capacity and compared an old test
merge with a newer base branch.
> - Normal merges into master then sent trusted PR jobs to the GitHub
queue.
> - This change restores AWS admission and validates the immutable merge
selected for the run.
> - Current CODEOWNER and rerun identity checks still control PR
execution.

## Linked Issues or Issue Description

Related: #18376 moved the gate to GitHub-hosted capacity. #16042
introduced reserved AWS admission.

**What happened?**

In PR #18811, GitHub created test merge
`608d862ff1650db108a3832a2c67f8795924f0e7` at 17:17 UTC. Master advanced
at 17:25. The gate started at 17:30 and rejected the older merge parent
because it did not contain the newer master tip. The authorized PR then
selected GitHub-hosted runners. The gate itself also needed a
GitHub-hosted slot.

**Expected behavior**

Authorized maintainers use reserved AWS admission and PR workers. Normal
base advancement does not change runner placement. Current owner checks,
PR head checks, run-attempt checks, and base-history validation remain
required.

**Steps to reproduce**

1. Start a PR run whose author and sender are current CI CODEOWNERS.
2. Advance master while its routing job waits.
3. Run the gate against the original event merge and the current base
tip.
4. Before this change, the gate selects `ubuntu-latest`. After this
change, it selects the public PR AWS fleet.

**Paperclip version or commit**

Reproduced on master `367f29478d14003cc5c0f161b7ce8f3164993634`.

**Deployment mode**

GitHub Actions with RunsOn Fleet on AWS.

## What Changed

- Restore the reserved routing fleet for the synchronized maintainer
admission roster.
- Check the immutable event merge and its PR head. Accept base snapshots
between the event base and the current base tip.
- Allow GitHub to regenerate its live merge ref while the original run
waits.
- Retain stacked-base validation and all current CODEOWNER, repository,
author, sender, and rerun-attempt checks.
- Add advancement, regenerated-merge, unrelated-history, and
admission-placement regressions, including a stacked parent that
descends from the event base but diverges from the live base.
- Align the developing and CI profiling guides with reserved AWS
admission and current CODEOWNER checks before PR execution.

## Verification

- `node --test .github/scripts/tests/ci-routing.test.mjs
scripts/__tests__/e2e-shard.test.mjs`: 103 passed.
- `node --test .github/scripts/tests/ci-profile.test.mjs
.github/scripts/tests/ci-plan.test.mjs`: 31 passed.
- `actionlint .github/workflows/ci-routing.yml`: passed.
- `git diff --check`: passed.
- Mutation check: removing the snapshot-to-live-base comparison in a
temporary workflow copy makes the new side-branch regression fail,
proving it protects that ancestry guard.
- [Greptile:
5/5](https://github.com/paperclipai/paperclip/pull/18821#issuecomment-6612170970)
with a successful check on `abd6a8ce347228219d8e5127f34f57151788830e`;
both review threads are resolved.
- A regression reproduction on the original workflow selected GitHub
instead of AWS. The patched workflow passes that scenario.
- A read-only execution of the patched routing step against the actual
event for run
[41682867664](https://github.com/paperclipai/paperclip/actions/runs/41682867664),
using live GitHub metadata, selected
`runs-on/fleet=paperclip-public-pr-x64/env=public-ci`. This did not
allocate a runner or rerun CI.
- Full repository typecheck, Vitest, and build were not run in this
worktree, which has no installed dependencies. The change is limited to
workflow routing, its tests, and documentation. Broader CI checks remain
queued.

## Risks

- A stale admission roster can allocate the five-minute metadata gate.
The gate still checks current CODEOWNERS before it authorizes any PR
code on AWS.
- The routing runner group must authorize
`ci-routing.yml@refs/heads/master`. The fleet already exists; live
placement still needs confirmation after merge.
- Rewritten base history, changed PR heads, and unavailable identity or
ancestry evidence continue to deny AWS PR execution.
- Existing runs retain their previously resolved workflow. New runs use
this change after it reaches master.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository inspection, code
execution, and live GitHub API tools. The runtime does not expose the
exact serving model variant or context-window size.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
native-ingestion-benchmark-2026-11-03-b0
2026-11-03 12:47:16 -06:00
DottaandPaperclip ccc63ea458 fix(server): prove completed history before idle sleep (#18646)
Reviewed and verified at 592bce69c6.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-11-03 12:37:25 -06:00
DottaandPaperclip 367f29478d fix(recovery): preserve the bound remote writer before continuation (#18803)
## Thinking Path

> - Paperclip manages agent work and preserves task files.
> - A remote workspace records the exact run that owns its unsaved
files.
> - A later setup failure can become the latest task run without owning
those files.
> - Retry only inspected that latest run, so the original writer had no
file-recovery owner.
> - A released reusable source also failed the legacy retention rule.
> - This change registers the bound writer, saves its files, and then
admits the saved user request once.
> - Existing stop, ownership, approval and execution-evidence checks
remain effective.

## Linked Issues or Issue Description

**What happened?**

A native run prepared a remote workspace and failed before finalization.
Its sandbox stopped with a dedicated stop receipt. The workspace stayed
dirty and retained that run as its writer. A later setup failure became
the latest task run. Retry inspected the newer run and left the original
file obligation unregistered. New attempts failed at the writer fence.

Registration alone was insufficient. After file recovery, continuation
still required the Retry source to be one of the older recovery sources.
A newer pre-provider failure did not satisfy that check. Workspace
selection could also choose a fresh directory instead of the recovered
files.

**Expected behavior**

Find the writer through the task's current workspace binding. Register
only its exact prepared source. Preserve the released lease and its stop
receipts. Save the files before continuation. Independently prove that a
newer setup failure never dispatched work. Select the verified saved
workspace and admit the Retry once.

**Steps to reproduce**

1. Fail a native run with a prepared remote workspace and a released
reusable sandbox.
2. Keep matching dedicated stop receipts and the workspace's dirty
writer authority.
3. Record a newer pre-provider setup failure for the same task.
4. Request Retry. Before the fix, no file-maintenance record owns the
actual writer.

**Paperclip version or commit**

Reproduced before production edits on master `f7ad1dc7ca`. The branch
was rebased on master `adb49a44346849e751548b2fb640693dbfad4b6f`.

**Deployment mode**

Authenticated hosted execution with Paperclip Runner and remote
workspace storage.

Companion: #18798 (now merged upstream) fixes a pre-existing inbox test
synchronization race. Its test-only commit
`110aa88489cad926a451c972ad731ede469347c1` is cherry-picked here with
attribution so this PR can run the complete CI matrix.

Related: #17800 registers missing preservation for the selected source
run. #18678 holds unfinished remote exports. #18718 repairs a prior
warm-controller projection failure. #17916 addresses a separate legacy
warm-copyback policy case.

## What Changed

- Discover the failed writer through the company-scoped task/workspace
binding during Retry and authenticated message admission.
- Reuse the exact prepared-source validator for released remote
workspaces. Registration changes neither the lease policy nor its stop
receipts. Physical recovery retains its full allocation checks.
- Check a newer failed preparation separately before allowing an older,
repaired source to release continuation. Provider events, leases,
results and execution ownership remain disqualifying evidence.
- Select the bound saved workspace through the continuation's verified
repair receipt.
- Add PostgreSQL safety cases and an HTTP Retry workflow with real file
capture and saved-copy reads. Document the execution contract.
- Reuse #18798's completion barrier for the existing inbox cancellation
test; preserve its eight-slot limit, rejection and coalescing
assertions.

## Verification

- RED before production edits: the exact stopped writer has no
`workspaceRestoreRecovery` after Retry. Six unsafe-source controls pass
on the baseline.
- GREEN: the expanded Retry/message/preview selection passes 17 cases.
All 627 tests in the five affected recovery suites pass on recovery
commit `b572304f772d236b8a8bdf095e13e651d808f41c`, including Stop,
ownership, saved messages and duplicate Retry.
- The HTTP workflow registers maintenance, keeps execution held before
save, reads back the saved file bytes, and admits exactly one
continuation with `resume_preserved_workspace`. A registration-only
repair first failed after save because it did not reconcile the newer
Retry source. The incident-shaped pair passes with an independent active
stranded-task action present.
- Greptile identified the path where the newer setup failure already
owns a reconciliation hold. Its new regression failed before the
correction (saved files, zero queued continuations); the
contradictory-dispatch control passed. All four HTTP variants pass after
the correction, covering both hold shapes and both dispatch-evidence
controls.
- Repository typecheck and build passed. Server typecheck and build also
passed after the recovery changes. Module-boundary and whitespace checks
pass.
- The full local Vitest command failed in the GitHub fast-response
acknowledgement test (`delivery_unknown`: one response write instead of
zero). That isolated case passes on this branch and unchanged master;
the original full-run cause remains unestablished. A sibling run passed
199 test cases but timed out in workspace-suite database cleanup.
Unchanged master also passed all 126 workspace cases and timed out in
the same cleanup hook; all five acknowledgement outcomes passed in that
baseline comparison. These local failures remain recorded rather than
being reported as suite passes.
- The first hosted matrix failed the pre-existing inbox cancellation
assertion (expected nine computes, saw eight). The same assertion failed
in [master
CI](https://github.com/paperclipai/paperclip/actions/runs/41664339150/job/126015526840)
and reproduced locally before applying #18798's existing repair. All six
inbox HTTP/database cases pass after that repair. Final head
`7f2fafcbace74cb1ad8ea68c8375a69b42ed30d9` adds only that attributed
test change to the verified recovery code.
- [Final hosted
CI](https://github.com/paperclipai/paperclip/actions/runs/41677310377)
passes on head `7f2fafcbace74cb1ad8ea68c8375a69b42ed30d9`, including `ci
/ verify`, `ci / e2e`, typecheck, build, every server/chat/workspace
group, Runner checks and all eight browser shards. There are 54
successful check results, two informational neutral results and three
skipped results, with no pending or failing results.
- Greptile and the Paperclip reviewer both report 5/5 for that final
head; no review threads remain open. The Paperclip review check is
informational (`neutral`) and records its local-testing limitations.
- The file workflow uses real PostgreSQL, subprocess file capture,
encrypted saved objects and production recovery code. Provider
resume/stop calls are simulated at the environment driver boundary. This
does not prove a live Daytona or Codex recovery. No deployment or
original-task retry is claimed.

## Risks

- File-maintenance discovery must not authorize another executor.
Existing writer, coordinator and physical allocation checks still own
that decision.
- A false bootstrap summary cannot prove that a newer run never
executed. The new path also requires its immutable native-adapter claim,
unresolved preparation state, absent
coordinator/process/session/lease/result evidence, and no dispatch
events.
- Additional database reads run only during explicit task admission or
Retry strategy selection. They are company/task scoped and add no
polling loop. The saved-writer query returns one matching snapshot and
omits prompt bodies.
- No schema, credential, provider profile or deployment setting changes.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository inspection, code
execution and test tools. The runtime does not expose the exact serving
model ID or context-window size.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 11:25:24 -06:00
DottaandPaperclip 1c9ca5829f perf(ci): overlap Runner workloads and cache debug workspace builds (#18794)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Required CI checks protect its server and Runner contracts.
> - Recent full runs waited on general server files and the final Runner
lane.
> - That Runner lane ran package tests before native server tests.
> - PR jobs also rebuilt unchanged debug workspace binaries.
> - This pull request overlaps those workloads and reuses exact trusted
build inputs.
> - The benefit is a shorter critical path with the same test coverage
and AWS authorization.

## Linked Issues or Issue Description

Refs #18544, #18773, #18774, #18798, #18799.

**What existing behavior does this improve?**

The wall time of required PR tests and the precision of affected-test
selection.

**Current behavior**

General server and native files use two workers. The second Runner
Vitest lane runs its package shard and then the native server group.
Each Runner job compiles debug workspace units again. A Vite raw import
also retains its consumer for unrelated source edits.

**Proposed behavior**

Use three isolated workers for general server files and four for native
files. Reuse the two Runner slots for a complete package suite and a
complete native server suite. Restore all debug binaries only on an
exact input key. Record raw imports as explicit file inputs.

## What Changed

- Separate the Runner package and native server workloads without adding
CI jobs. Preserve the old command arguments for master-pinned workflows,
local commands, and other shard counts.
- Increase file workers while retaining fork isolation, serial cases,
and the shared-database guard.
- Add an exact debug workspace cache. Derive binary targets from Cargo.
Include source, fixtures, generated inputs, manifests, compiler
identity, flags, Cargo configuration, and checkout path in its key.
Discard incomplete restores. Only verified master pushes can save it.
- Keep older PR checkouts working when they lack the new cache helper.
- Track Vite `?raw` imports as repository file inputs. Keep unknown
loaders, untracked files, and opaque effects conservative.
- Add cache and coverage guards and document the behavior. AWS runner
authorization is unchanged.
- Keep paired test benchmarking available when install hooks correctly
disable materialized dependency reuse. The old benchmark asserted a
cache hit in that safe fallback.
- Wait for active dispatch work to finish before asserting idle reads.
Analyze the two 16,400-row database fixtures before their queries so
they have current planner statistics. Suppress only PostgreSQL notices
inside the cleanup transaction to avoid logging every cascaded table;
warnings/errors remain visible. Row counts, real queries, assertions,
database reset, and timeouts remain unchanged.

## Verification

- 196 affected CI tests passed, including routing, coverage, selection
failure fallbacks, shared-database serialization, and existing/new cache
guards. Final worker/cache adjustments also passed their focused checks.
- `pnpm build` and `pnpm -r typecheck` passed locally. Full test
coverage is being verified by the Linux CI profile; `pnpm test:run` was
not repeated as a full serial local run. The paired CI benchmark passed.
Both paired benchmarks passed, and full CI passed again after
integrating newer master commits. The external Paperclip Review app is
the only pending status.
- Actual debug artifacts remained fresh after simulating checkout
timestamps: Cargo completed in 0.82 seconds; the content key was
unchanged. This is local reuse evidence, not a measured GitHub cache
transfer.
- Same real UI inventory for a retention-test-only change: 861 files;
retained selection fell from 413 to 401. All 12 omitted files are
raw-input consumers; edits to their inputs remain covered by regression
tests.
- The first local worker comparison is invalid. Development dependency
links disappeared during its second run. The two-worker baseline also
had two macOS path assertions in company-skills. Neither result is
counted as a successful speedup. Dependencies were restored. A local
repeat was interrupted before producing results. The completed Linux
comparison below supplies the valid paired evidence.
- [Paired Linux
benchmark](https://github.com/paperclipai/paperclip/actions/runs/41671244016):
baseline and candidate ran the same 113 general-server files and 1,777
cases on one host. Wall time fell from 381.29s to 346.89s (9.0%). Both
exited successfully and file/case inventories matched exactly. The
fixture command took 31.77s versus 28.65s. This is one sequential pair,
excluding setup, queueing and the full CI critical path.
- [Repeat paired Linux
benchmark](https://github.com/paperclipai/paperclip/actions/runs/41675496862):
the same 113 files and 1,974 cases took 485.06s in the baseline versus
368.54s in the candidate (24.0%). File and case inventories again
matched exactly. The baseline includes newer master changes than the
first pair, so these are two independent comparisons, not repeated
timings of an identical tree.
- Initial full AWS profile: package suite passed 4,891 tests (289 files,
one skipped); native suite passed 172 tests (9 files, 12 pre-existing
platform/feature skips). Native execution took 239.06 seconds; the older
sample ran 165 native tests in 391.29 seconds. The package lane now
takes 11m01 including setup and debug compilation. This is an indicative
cross-run comparison, not a paired full-CI speedup claim.
- The initial run failed two UI fixtures, one serialized inbox
settlement assertion, and one dispatch idle assertion. The two UI
failures reproduced locally. The fixes preserve authorization,
admission, and zero-idle-read assertions. All four affected files passed
locally: 48/48 tests. Token gates passed. All four also passed in the
next AWS profile. That profile passed every other workload but timed out
the 16,400-row keyed attention fixture at its existing 180s limit; this
is accounted for by the explicit fixture-statistics change. Local
focused comparisons pass both large cases before and after; their
roughly 33s test execution is unchanged locally, so this is not claimed
as an additional speed gain. A full local file run passed both large
cases (7.57s / 18.05s) but hit the unchanged 30s teardown deadline on
its first test while emitting thousands of cascade notices. Cleanup now
suppresses those notices within its transaction; the complete file then
passed 34/34 in 107.72s. The latest-head AWS run passed the previously
failing shard: the keyed bulk case took 40.25s (previously exceeded
180s), the keyless case took 8.02s, and the complete attention file took
89.61s. The 16,400-row fixtures and original timeouts remain intact.
- [Full CI profile before master
integration](https://github.com/paperclipai/paperclip/actions/runs/41675497699)
passed on commit `5858956d4ac2c99c911ad2447b63e1f8fc84e31e`, including
all tests, build, typecheck and final verification. Greptile reviewed
that exact commit at 5/5; the only actionable review thread is resolved.
The separate Paperclip Review app is still queued.
- [Final merged-head
CI](https://github.com/paperclipai/paperclip/actions/runs/41678956312)
passed on `499aa206a6e03bf8f3249692ea731367a000591d`: 48 successful jobs
and one intentionally skipped file-selection job. Backup checkpoint
qualification and legacy security status passed. Greptile reviewed this
exact commit at 5/5. The PR is conflict-free; only the separate
Paperclip Review app remains queued.
- Integrated current master after #18798 and #18799 merged the inbox and
UI fixture fixes separately. Preserved master's inbox settlement fix and
verified its entire file (6/6 tests). Those three files are no longer
part of this PR's diff. Removed the temporary `profile-ci` label after
both successful benchmark pairs; the normal planner still schedules
every job because the CI configuration changes are global inputs.
- Second AWS profile: native tests took 229.97s with the same 172 passes
/ 12 skips; package tests took 427.44s with 4,891 passes / 10 skips. The
complete package job took 10m44, including setup and 79s debug
compilation.
- The PR uses the trusted workflow from master. Candidate worker/layout
code runs now; workflow-level debug restores begin after merge and the
first successful master cache writer.

## Risks

- Extra workers can expose load-sensitive tests. The final full CI
profile passed, but one run is not a long-term flake-rate measurement.
- The cache is an optimization only. Misses rebuild, and test commands
still execute. A new Cargo workspace member requires a cache-contract
review.
- Compiler alternatives remain experiments. Required TypeScript checks
are unchanged.

Typechecker experiment: three sequential checks per tool on identical
built projects, with TypeScript 7.0.2, Bun 1.4.4, and tsrs
0.20.1-ts7.1.0-beta. Median seconds (TypeScript / Bun / tsrs): shared
0.429 / 0.083 / 0.157; UI 2.554 / 0.390 / 0.740; server 9.949 / 1.194 /
1.782. TypeScript and Bun passed all three projects. All tools caught
the same four deliberate assignment, property, nullability, and
generic-constraint errors. tsrs reported one additional server TS2345
diagnostic in the run-dispatch adapter, so it did not pass the project
comparison. These local compiler times exclude generation and build
steps; they are not full CI savings.

## Model Used

OpenAI GPT-6 Codex. Tool use and code execution assisted this change.
The exact deployment identifier and context window are not exposed in
this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 11:17:53 -06:00
DottaandPaperclip 8c2502503d fix(github): preserve repository access during PR webhooks (#18805)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Agents need current repository access before they can start work.
> - Managed GitHub access checks run while webhook delivery can update
the same grant.
> - Pull request events update webhook health but do not change
repository permissions.
> - The refresh guard treated those health updates as access changes and
could exhaust its retry.
> - This change keeps repository preparation available during normal
pull request activity.

## Linked Issues or Issue Description

**What happened?**

A pull request webhook can make a managed GitHub repository refresh fail
with `github_access_changed`. Repository discovery then omits the failed
connection. Workspace admission can report that repository access is no
longer available, even after the checkout succeeds.

**Expected behavior**

Pull request activity must preserve valid repository access.
Installation changes and newer access snapshots must still stop a stale
refresh.

**Steps to reproduce**

1. Start a managed GitHub access refresh.
2. Process a pull request edit while the provider request is in flight.
3. Repeat on the bounded retry. The old guard rejects both checks
because `lastWebhookAt` changes.

## What Changed

- Use `accessRevision` and `lastAccessRefreshAt` as the existing access
fences. Stop using webhook delivery time as an access fence.
- Allow the existing commit merge to retain the latest webhook timestamp
and health after a pull request event.
- Exercise real webhook processing and PostgreSQL during refresh. Cover
a pull request event, an access conflict followed by that event, and
installation suspension.
- Document the distinction between access changes and delivery health.

## Verification

- Red: both pull request race cases failed on the old code with
`github_access_changed`.
- Green: all seven focused refresh cases pass. These include the
existing concurrent access-change checks and the new suspension case.
- Command: `pnpm exec vitest run
server/src/__tests__/tool-access-service.test.ts -t 'binds a managed
GitHub identity'`.
- Full affected group: 526 tests pass across the tool-access,
repository, and webhook suites.
- `pnpm -r typecheck` and `pnpm build`: pass.
- Current-head CI: 54 checks pass. The duplicate Paperclip review is
neutral. Two Storybook checks are skipped. Greptile and the completed
Paperclip review both rate this commit 5/5 with no findings. There are
no unresolved review threads.
- The duplicate local `pnpm test:run` did not complete. Its first four
chat shards passed. It was stopped after CI completed on this commit;
the full CI test matrix passed. The complete affected local group passed
separately.
- Retained earlier failure: the first affected-group attempt passed 525
tests and had one socket hang-up in the profile attention feed test.
That test passed alone without changes. The unchanged base tool-access
suite also passed all 498 tests, and the complete fixed affected group
then passed. The cause of the socket closure remains unproven.
- Environment: source checkout on macOS arm64, Node 26.4.0, pnpm 9.15.4,
disposable PostgreSQL. Provider responses and Cloud transport use
controlled fixtures. The webhook handler, refresh logic, transactions,
and saved grant reads are real.
- Live acceptance remains unverified. The available CLI token cannot
list GitHub App installations. The affected staging deployment still
serves the old code. This PR does not deploy or recover the original
task.

## Risks

- Removing an access fence could admit stale permission data. The access
revision and refresh timestamp checks remain. The real suspension
webhook regression confirms that stale provider data cannot restore
removed access.
- This change adds no retry, timer, schema change, or credential
forwarding.

## Model Used

OpenAI Codex, based on GPT-6. The exact runtime model ID and context
window are not exposed in this session. Used reasoning, code editing,
and terminal 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 11:15:49 -06:00