Commit Graph
2086 Commits
Author SHA1 Message Date
DottaandPaperclip 8771606fd8 test(e2e): cover browser task editing and reopening (#18782)
## Thinking Path

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

## Linked Issues or Issue Description

Refs: #18655.

**What existing behavior does this improve?**

Product E2E coverage of ordinary task editing and completion.

**Current behavior**

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

**Proposed behavior**

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

## What Changed

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

## Verification

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

## Risks

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

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

## Model Used

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

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 17:47:13 -06:00
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
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
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
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 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
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
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
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 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
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 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 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
Nicky LeachandClaude Opus 5.5 2e37c73c05 Tell the agent about the launcher's memory limit (#18801)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The native Runner supervises the harness and tool processes for each
run
> - A launcher can put a memory limit on those processes
> - When a process goes above the limit, the kernel kills it, and the
agent sees only exit code 137
> - Any SIGKILL gives that code, so the agent cannot know the cause
> - The agent can then run the same command again and fail again, and
the people who read the run see no reason
> - Only the launcher knows if a limit applies, so Runner must not
assume one
> - The kernel gives the limit and the kill count in cgroup files
> - This pull request adds an optional `--task-memory-cgroup` flag that
names the task's cgroup
> - The benefit is that the agent knows the limit before it starts, the
agent can confirm a kill, and the run shows each kill

## Linked Issues or Issue Description

Related: #18730. That pull request lets the kernel kill large tool
processes under Runner. This pull request tells the agent when that
occurs because of a memory limit.

**What happened?**

A tool process on a VM with a 6 GiB task limit went above the limit. The
kernel killed it, and Runner and the agent continued. The agent received
exit code 137 and no other data. It ran the same type check in different
forms, and the kernel killed it five times.

**Expected behavior**

The agent knows about the limit. It can confirm that the limit killed
its process. It can then reduce the work or report a blocker. The run
shows the kills.

**Steps to reproduce**

Start Runner under a memory limit. Start a tool process that goes above
the limit. The agent and the run show only exit code 137.

**Deployment mode**

Native Runner on Linux, when the launcher gives the flag.

## What Changed

- Add the optional `--task-memory-cgroup <path>` flag to
`paperclip-runnerd`. Runner has no default value.
- Runner reads the limit from `memory.max` on the parent of that cgroup.
Runner reads the kill count from `oom_kill` in `memory.events` on that
cgroup.
- When a limit exists, add a note to the Codex and ACPX instructions at
launch. The note gives the limit, the meaning of exit code 137, the
paths of the files to read, and what to do after a kill.
- Record a `provider.notice.recorded` warning when `oom_kill` increases.
Runner watches `memory.events` for the kernel's change signal, so an
idle Runner does not read it. Runner takes the starting count before it
executes commands. It does not report kills from before it started.
- Without the flag, or when the parent has no limit, the instructions do
not change. Without the flag, Runner does not read the files and records
no notice.
- Add unit and integration tests.

## Verification

Tests ran in `rust:1-bookworm` on Linux arm64:

- `cargo fmt --all`
- `cargo clippy -p paperclip-runner-core --bin paperclip-runnerd --lib`
has no warnings in the new module.
- `cargo test -p paperclip-runner-core --lib -- task_memory
restores_settled_session_for_explicit_run_attachment_only`: The task
memory tests pass. The second filter covers the note in the ACPX session
configuration.
- `cargo test -p paperclip-runner-core --test task_memory`: this test
uses a separate process. It checks that no note and no monitor exist
before the flag value is set. It then checks the note in the Codex
`thread/start` and `thread/resume` requests.

The tests use files in a temporary directory that have the format of the
cgroup files. In a separate test on a real kernel, a kill above a limit
on a parent cgroup increased `oom_kill` in the cgroup of the killed
process. It did not increase `oom_kill` in the other child.

On a real kernel, an unprivileged process that watched `memory.events`
through a read-only mount received the change signal at the kill.

I did not run the complete suite locally. CI runs it.

I did not run this with a real launcher or on a real VM. The notice
payload has the same fields as other Runner notices, but no test sends
it through the control plane.

## Risks

- The note adds text to each provider's instructions when the launcher
gives the flag. If the text makes the instructions go above the provider
limit, Runner does not add the note.
- The task must be able to read the cgroup files. If it cannot, the note
is absent or the agent cannot read the paths, and the run continues as
it does now.
- A kill while the run is not ready gives a notice later. A kill after
Runner records 64 notices gives no notice. The agent can still read the
file.
- This pull request does not send a message into an active turn, because
most ACPX profiles do not support this. The agent must read the file to
confirm a kill.

## Model Used

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`) in 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: targeted tests only, as
listed above
- [x] I have added or updated tests where applicable
- [ ] I have updated relevant documentation to reflect my changes: not
applicable, because no document lists the `paperclip-runnerd` flags
- [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 09:02:19 -08:00
DottaandPaperclip 5f1b59ba2c fix(db): preserve binary data in checkpoint backups (#18650)
Reviewed and verified at 69792ccbcf.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-11-03 10:47:19 -06:00
DottaandPaperclip 1423d745e8 feat(server): expose owned sleep blocker diagnostics (#18632)
Reviewed and verified at 26e5a22b3c.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-11-03 10:45:10 -06:00
DottaandPaperclip f7ad1dc7ca fix(workspaces): make attached project repositories writable (#18770)
## Thinking Path

> - Paperclip lets people assign work to AI agents and inspect the
result.
> - A task can use repositories from more than one project.
> - Remote workspace preparation treated every additional project as a
read-only source.
> - This rejected valid Git repositories before the provider started,
even when no source was read-only.
> - The task page then hid the preparation reason behind a generic
failed-attempt notice.
> - This pull request prepares authorized repositories as writable task
copies and shows redacted workspace validation errors.
> - People can work across both projects and see the reason if folder
preparation fails.

## Linked Issues or Issue Description

**What happened?**

A remote task with a selected Git-backed project and another mentioned
Git-backed project failed before provider execution. It reported
`remote_workspace_readonly_sources_unsupported`. Neither project had a
read-only setting. The task notice showed only that the attempt failed.

**Expected behavior**

Each attached repository should be writable inside the task workspace.
This should preserve source access checks and saved work. A workspace
validation failure should explain its cause on the task page.

**Steps to reproduce**

1. Configure remote workspace storage and a native sandbox environment.
2. Select a Git-backed project for a task.
3. Mention another authorized Git-backed project in the task.
4. Start the task. Preparation rejects the additional source before it
starts the provider.

**Paperclip version or commit**

Reproduced against `dc9f387edc`, before this change.

**Deployment mode**

Managed remote workspace storage. Local regression checks use disposable
PostgreSQL and independent filesystem processes.

Related merged work: Refs #18403 and #18504. Those changes established
remote storage and metadata-only references. They still reject
configured cross-project repositories. No duplicate writable-repository
repair was found. Refs #18774 for the test-only retention-fixture repair
reused to unblock CI.

## What Changed

- Add every configured Git repository of an authorized referenced
project to `.paperclip-repositories/` as an editable task copy.
- Retain saved paths, refs, pins and files. Recheck retained project
access and source trust. Keep explicit read-only, local-source import
and remote feature gates.
- Keep committed snapshot repository coverage separate from later
requests. Honor configured default refs, resolve remote branch names,
and publish secondary checkouts only after checkout succeeds. Share
configured-ref resolution between root and attached-repository scripts.
- Include redacted workspace validation messages in the task
interruption projection and existing failure notice. Preserve Retry
permissions and render messages as plain text. Explain retained-folder
access and trust denials while preserving the dedicated low-trust
project-access path.
- Add source, authorization, redaction, UI and save/restore regressions.
Update the implementation contract and workspace maps.
- Reuse the reviewed test-only repair from #18774. Batch the
shared-workspace history fixture, verify its complete privacy-source
map, and drain owned test bodies before cleanup. Production retention
behavior is unchanged.

## Verification

- Red before repair: four source-resolution cases failed with the
reported read-only error. The task-error projection test also failed
because it omitted the cause.
- Green: 49 source and transport tests, 16 task projection and redaction
tests, and 10 UI tests. The assembled preparation check passes. All 48
remote workspace lifecycle tests pass. The full preparation suite passed
all 126 assertions but its cleanup hook exceeded 30 seconds. The
unchanged base reproduces the same cleanup timeout with all 126
assertions passing. The focused preparation check finishes cleanly.
- The real PostgreSQL/Git lifecycle checks cover both first-turn and
later project attachments. They verify the configured release branch,
saved edits, replacement allocations, unchanged source folders,
committed archive contents, and released writers.
- Review regressions were red before repair: the default ref became
null, later attachment failed during restore, non-default remote branch
checkout failed, and invalid secondary refs left partial checkouts. They
now pass.
- The local browser shows the workspace validation explanation and Retry
action. The fixture uses a saved failed run; this proves the error
display, not a successful live sandbox run.
- `pnpm -r typecheck` and `pnpm build` pass. Map, module-boundary and
token checks pass.
- A local two-turn acceptance probe repeated at `17ed67eaa7` used real
Codex CLI 0.165.0, `gpt-6-astra`, and `xhigh` effort. Codex wrote files
in both task repositories. The services saved them, removed the first
allocation and its Git sources, restored another allocation, and Codex
read the saved files and wrote a follow-up in both folders. This uses
the real source resolver and persistence services with a local
command-runner stand-in for sandbox transport. It does not qualify
Daytona, managed subscription forwarding, or the original task. The
first probe waited for stdin; closing the test harness pipe fixed that
setup error before provider work.
- `pnpm test:run` passed the first chat shard, then failed one chat
response concurrency assertion in the second shard. That case passes in
isolation on this head. The same isolated chat case passes on the
unchanged base. Both corresponding unchanged-base chat shards also pass
(351 and 324 tests). The cause of the earlier wider-suite chat failure
is not established. The preparation cleanup timeout and chat failure
remain part of this record.
- CI at `17ed67eaa7`: all other verification jobs passed. Browser shard
6 initially reported a request callback from the preceding test after
teardown; its single unchanged-code retry passed. Server shard 10 timed
out the 2,100-row retention confirmation fixture at 15 seconds on both
attempts. That exact case passes locally, but the repeated CI failure
required a repair. Its setup repeatedly rebuilt a shared-workspace
privacy map. The logs and failures remain part of this record.
- Reused the test-only repair from #18774 without changing its source
file. At `ab774e9418`, all 54 retention tests and all 25 ordinary
production privacy tests pass locally (79 total). Full CI
[41650626903](https://github.com/paperclipai/paperclip/actions/runs/41650626903)
passed on that commit, and its exact-head Greptile review was 5/5. The
fixture preserves its 2,100 rows, query-bound assertions, privacy-source
map, and completed-history test deadlines.
- The additional review identified retained-source access/trust denials
that still bypassed the workspace explanation. Real-resolver regressions
reproduced both plain-403 failures before the correction. Both now yield
a safe workspace validation explanation, while the dedicated low-trust
denial and saved workspace remain unchanged. The optional duplicate
ref-resolution block is also consolidated.
- Final review corrections: 65 source, transport and error-projection
tests passed, followed by all 48 lifecycle cases against an owned
disposable PostgreSQL database (113 total). The initial combined command
skipped the explicitly gated lifecycle suite; the subsequent dedicated
invocation enabled and passed all 48. Full workspace typecheck and build
pass. The first standalone lifecycle launcher lacked the repository
TypeScript loader; rerunning with that loader resolved its import setup
error. At previous head `7be02ea09c4ac76df560e8b517050cdf398f33f0`, full
CI [41654422329, attempt
2](https://github.com/paperclipai/paperclip/actions/runs/41654422329/attempts/2)
passed, Greptile is 5/5 on that exact head, and all review threads are
resolved. The first attempt timed out the unchanged voice-session
report/history case at 15 seconds; 3,027 cases in its shard passed. That
exact case passed locally, then its single unchanged-code shard retry
passed. The wider-suite timeout cause remains unproven. The completed
Paperclip advisory check is neutral; its earlier findings are corrected.
No staging deployment or original-task recovery is claimed.

- Master advanced while checks ran, producing conflicts in two adjacent
test additions. Merged `27d35314be` into this branch as
`d5c8d552939c86056abf8fe10833881b2ade41a4`, preserving both
workspace-error tests and the audited Stop/receipt tests. Kept workspace
maps to minimal corrections under the updated map rule. All 82 source,
transport and interruption cases, all 48 real-database save/restore
lifecycle cases, full typecheck, build, and map checks pass locally on
the combined code. Current-head [CI
41660719214](https://github.com/paperclipai/paperclip/actions/runs/41660719214)
and Greptile are pending; the earlier verified head has not been merged.

## Risks

- Additional remote project repositories are now editable task copies.
Shared source folders and upstream push permissions do not change.
- Snapshot coverage uses existing workspace metadata. Older workspaces
capture their previous inventory at reservation; successful save
publication advances coverage and snapshot identity in one transaction.
No SQL migration is needed.
- A retained copy can contain files from a project that is no longer
mentioned. Preparation rechecks that project's current access and trust
policy before reuse.
- Explicit read-only descriptors still fail. Source-less projects remain
metadata-only. Local folders still require import. No migration or
provider-pack update is needed.
- Error display reads only workspace validation errors. The existing
run-secret registry and username censor run before display truncation. A
redaction failure omits the explanation.
- The retained fixture repair pauses its row trigger only within the
isolated seed transaction, rebuilds the exact source map, and restores
the trigger before commit. A rollback regression and 25 ordinary privacy
cases pass. Its paged-case test budget honors the existing bounded inner
waits; production deadlines are unchanged.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, tool use and code execution.
The exact serving model ID and context-window size are not exposed by
this session. The test-only fixture changes are reused from #18774,
whose model record identifies OpenAI GPT-6.1 Sol. The optional live
acceptance probe uses `gpt-6-astra` with `xhigh` effort through Codex
CLI 0.165.0.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 15:33:44 +00:00
DottaandPaperclip b8f8b904fa feat(routines): preserve automation ancestry through webhooks (#18772)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Routines let external services start agent tasks through a normal
webhook.
> - A repair routine must recognize failures from work that it created.
> - The receiver added in #18714 put Sentry payload parsing and repair
policy in core routine intake.
> - This pull request replaces that receiver with verified creation
provenance on normal routine calls.
> - Sentry can filter the resulting tags before it sends a normal
routine webhook.
> - The benefit is reusable provenance and loop prevention without a
provider-specific route.

## Linked Issues or Issue Description

**Problem or motivation**

A Sentry repair alert can start another repair when its own work fails.
The dedicated receiver in #18714 solved this through a Sentry-specific
payload contract. Ordinary routine webhooks also need a trusted way to
retain the automation that created their tasks.

**Proposed solution**

Use the existing public `/fire` route and its current authentication. An
optional source run credential proves a live Paperclip caller. Store the
caller separately from webhook JSON. Follow recorded task creation links
and export bounded ancestry tags on run-failure events. Let Sentry alert
filters exclude repair-origin work.

**Alternatives considered**

Keep the dedicated Sentry receiver, accept sender-supplied origin IDs,
or filter only the immediate routine. The first couples core intake to
one provider. The second accepts unverified claims. The third misses
child tasks, standalone follow-ups, and calls through another routine.

**Roadmap alignment**

This improves the shipped Scheduled Routines and Activity log
capabilities. It does not add a new scheduler or incident system.
Related work: #18714 is the implementation replaced here; #9727 adds
source-task navigation and UI attribution; #10206 proposes exception-led
routine dispatch. This PR uses existing task creation records and does
not implement either of those broader proposals.

## What Changed

- Remove the Sentry-only route, parser, authentication exception, and
payload-specific tests and documentation from #18714.
- Add optional `X-Paperclip-Source-Run-Token` support to normal routine
webhooks. Validate the standard run JWT, company, instance, agent, live
run, and cancellation state. Authenticated routine API calls also retain
their validated caller.
- Add nullable `routine_runs.created_by_run_id`. Keep the reference
after source-run deletion so missing history becomes unknown. Separate
callers during coalescing, including legacy open tasks.
- Follow company-scoped routine receipts, creator runs, task parents,
and legacy creation activity. Mark missing, foreign, cyclic, private, or
excessive ancestry for safe alert filtering.
- Add event-local `automation_origin_status`, `automation_routine_ids`,
and `task_shareable` tags through the existing Sentry SDK hook. Cap
ancestry at 32 reads and share a 750 ms deadline across metadata and
ancestry lookup. Keep the SDK one-second wait and eight-lookup
concurrency bound. Redact the source credential from HTTP logs and
Sentry request headers.
- Preserve unexpected database failures as server errors without
rejection bookkeeping in an aborted transaction. Document 409, 415, and
422 responses for the normal webhook.
- Keep `routineScopedRunId` as generic opt-in
`X-Paperclip-Idempotency-Scope: routine` support. Default trigger
receipts and timestamped HMAC replay protection retain their current
scope.
- Add [the Sentry repair
tutorial](https://github.com/paperclipai/paperclip/blob/codex/routine-creation-provenance/docs/guides/board-operator/sentry-run-failure-repairs.md),
API documentation, OpenAPI headers, and capability-map updates.

## Verification

At commit `0f4a0051528edd1367a921a76a8050c3fd087872`:

- `pnpm -r typecheck` and `pnpm build` passed for the full workspace.
Migration numbering and safety checks passed.
- The focused service, route, Sentry, redaction, and OpenAPI suites
passed 228 tests in five files. The database Sentry resolver and OpenAPI
invocation passed 33 tests in two files. These are separate invocations
with some overlap.
- The full routine HTTP suite passed all 15 tests. The authenticated API
case failed before the caller-wiring fix and passed after it. The
fixture loads the real route-owned services. No test deadline was
raised.
- The real `@sentry/node@10.71.0` suite passed all 12 tests. It uses an
in-memory transport and sends no events to Sentry. It covers terminal
failures, saved-message interruptions, event isolation, and source-token
redaction.
- Regression tests also cover forged, expired, foreign, and cancelled
caller credentials; payload spoofing; inherited routine ancestry; legacy
coalescing; deleted history; private work; cycles; query and time
limits; database error causes; and routine-wide receipts. The new error
and budget cases failed before their fixes.
- `node scripts/check-maps.mjs` and `git diff --check` passed. The map
check covers 35 areas and 192 capabilities.
- The full local Vitest command was stopped for a required master merge
after four chat shards passed 1,278 tests. No full local pass is
claimed. An earlier combined HTTP run hit an existing malformed-ID
timeout whose original cause is not established; the final full HTTP
suite passes. An extra local run of the unchanged heartbeat scheduling
file passed 12 of 13 tests and failed a readiness-status assertion. Its
logs also reported a missing fixture user membership. The same 13-test
file now passes in current-head CI.
- All GitHub Actions checks pass on this commit. Three jobs initially
lost their self-hosted runners. One server shard initially failed while
deleting a company in an unchanged heartbeat test because of a
`company_skills` foreign key. One retry passed all affected jobs without
code changes. That server shard passed all 499 tests, and all eight
browser shards passed.
- Fresh Greptile review is 5/5 on this commit. All six review threads
are resolved. The separate Paperclip Review check remains queued. Its
bot reports that work is paused. A maintainer must resume the assigned
review work to complete that check. No full green check state is claimed
while this external gate is pending.
- Live Sentry configuration and deployment are outside this PR's
acceptance evidence. The tutorial describes setup, a controlled failure,
and a repair-origin exclusion check for operators to run after
deployment.

## Risks

- Normal app webhooks retain the sender's full JSON in company-readable
routine history and the execution task. Alert filters and task
visibility are not content scrubbers. Operators must scrub sensitive
event data in Sentry before forwarding it and keep connection tests in
setup mode.
- Existing users of `/sentry-run-failures` must move to `/fire` and add
the documented Sentry filters before enabling repair intake. The old
route is removed. Stored tasks and receipts remain.
- The additive migration uses `ADD COLUMN IF NOT EXISTS`. It does not
rewrite historical tasks or infer missing provenance.
- Unknown or unshareable ancestry is excluded by the tutorial's alert
rule. Missing history, an enrichment timeout, or more than five routine
IDs can therefore suppress a repair webhook while retaining the Sentry
error.
- An external webhook without a verified source credential starts a new
receiving-routine origin. Its JSON cannot establish upstream Paperclip
ancestry. Sentry must not store a Paperclip run credential.
- Routine-wide receipt reuse needs a stable sender key. A static Sentry
custom header is not a per-event key. The tutorial keeps delivery
deduplication separate from loop prevention.

> 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

OpenAI Codex, GPT-6 model family, with reasoning, repository access,
code editing, and test execution. The session does not expose the exact
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>
2026-11-03 09:33:17 -06:00
DottaandPaperclip b40e6edaea perf(inbox): combine startup reads and reduce refetch pressure (#18790)
## Thinking Path

> - Paperclip helps people manage AI agents and their work.
> - Mine shows the work and failures that need the viewing user's
attention.
> - A reload waits for account access, task rows, parent captions and
failure state.
> - Those reads form several browser request stages before useful rows
appear.
> - Live invalidations can also restart the same task-list request more
than once.
> - This change supplies startup data together and removes redundant
refresh work.
> - It preserves current authority, failure presentation, archive state
and row stability.

## Linked Issues or Issue Description

Refs #18645. Related: #18642. This change implements the read-receipt
behavior independently on current master. It does not use that PR's
branch or patch.

**What happened?**

Mine starts its primary request after the session, company and
board-access reads. It then requests parent captions and rich failure
context. Four observed loads spent another 0.45–0.70 seconds after the
primary rows arrived. One slower load waited 1.71 seconds for parent
context. The same run observed two HTTP 429 responses. These timings
identify request waits; they do not attribute the delay to SQL or Node
CPU.

Read receipts also invalidate general task collections and dashboards.
Overlapping parent and child invalidations can start two requests and
abort the first.

**Expected behavior**

The initial page should use one account-safe startup projection and one
complete Mine response. A read receipt should refresh the affected
viewer's inbox state. Each invalidation batch should refresh a covered
query once. Required captions and current failure state must be ready
before first reveal. Existing rows should remain usable during a
transient refresh error.

**Steps to reproduce**

1. Open a populated Mine inbox with parent tasks outside the visible
index and a current failed run.
2. Reload with browser HTTP cache retained and application query state
empty.
3. Inspect the session, company, board access, task-index and context
request sequence.
4. Open or mark a task read and inspect the unrelated collection
refetches.

**Paperclip version or commit**

Baseline: `44a9a043b80c18314d8dd89556f444dae696d3d6`.

**Deployment mode**

Authenticated board UI. Local proof uses the real APIs and database with
a synthetic session resolver. No hosted candidate deployment is claimed.

## What Changed

- Add optional, same-user `boardBootstrap` data to the existing session
response. Seed account-keyed company and board-access reads from it.
Keep ordinary endpoint reads for fallback, retry and live
reconciliation. A returning account cannot reuse revoked cached access.
Cache collection and later real refetches remain effective.
- Add the board-only Mine snapshot endpoint. Reuse the canonical issue
selector, private-task filtering, run-read decisions, secret redaction
and interruption/recovery presentation. Assemble the fixed 100-task
page, parent captions and bounded 200-run failure window on the server.
Start independent reads together. Optional source errors keep successful
task rows usable.
- Keep array-shaped source caches for archive and undo. Key Mine source,
context and reveal state by account. Validate the response's user stamp
before committing it. Remove array-only cross-tab Mine broadcasts, which
cannot carry its paired failure context. The server retains
authenticated in-flight coalescing.
- Scope read-receipt invalidation to the affected viewer's inbox, badges
and chat unread state. Keep company and task audit views fresh. Preserve
genuine task mutations, run updates and subscription-gap reconciliation.
- Remove covered scalar-prefix invalidations within a batch. Preserve
predicates, exact matches and different query/refetch constraints. A
later event can still refresh again.
- Retire Mine in-flight reuse after local company activity or run
lifecycle changes. A post-archive read starts fresh. Old work keeps its
admission slot until it settles, even if every HTTP client aborts. The
eight-compute limit stays in place.
- Add API, component and browser regressions. Extend the production
timing harness with matching API/UI artifact receipts, retained-cache
reloads, request status counts, layout checks and optional controlled
network latency. Update API and verification documentation.

## Verification

- Full `pnpm -r typecheck` passes.
- Full `pnpm build` passes.
- Token gates, map checks and diff whitespace checks pass.
- 72 Inbox component tests pass. Startup-specific checks pass, including
account switching, revoked cached access, malformed/absent bootstrap
data and cache collection. The canonical authenticated bootstrap route
also passes against real PostgreSQL.
- Six real PostgreSQL Mine route cases pass, including full equivalence
with legacy authorized projections, privacy, fixed bounds, membership
revocation, coalescing, archive freshness and aborted-client admission.
Four focused legacy coordinator controls pass. Six assembly tests pass.
- Refetch and invalidation regressions pass. Meaningful pre-fix tests
observed two request starts and one abort for overlapping invalidations;
the fix produces one start and no abort. The archive race test observed
the post-archive GET joining the old body before the fix.
- All 11 existing startup browser checks pass, including Tasks/Mine code
loading, desktop/mobile bounds, non-streamlined Coordinator, task
navigation, failed preloading and Back position. The nine focused
startup, fallback, partial-source error, rate-limit recovery,
archive/undo and geometry cases also pass. The 429 cases use the
inherited retry window. Archive and keyboard undo are corroborated
through the real API and reload. Authentication and failure fault
injection in these cases are explicitly simulated.
- Matched production before/after timings are complete. Full aggregate
Vitest coverage is left to CI. At 15:15 UTC, the final-head runner gate
and `review` workflow pass. The 45 build/test/policy jobs are queued;
the Build job has no `ubuntu-latest` runner assigned and has executed no
steps, so remote aggregate results remain unverified. Paperclip Review
and Superagent Security Scan are in progress. Superseded runs for this
PR were cancelled. No current-head check has reported a failure; the CI
checklist remains open. The remaining step is runner allocation and
completion of those external checks; persistent queueing needs the
repository CI owner's attention.
- Greptile completed on final head
`ecde295565309275c4f2fae903caf6892f1eaafd` at 5/5. The map-scope finding
is fixed and resolved; there are no open review threads.

### Matched browser timings

Five alternating baseline/candidate pairs per network scenario, measured
serially
in headful Chrome 156.0.8078.80 on Apple silicon with Node 24.22.2 and a
1440×900
viewport. Both production UIs use their matching real API revision
against one
fixed disposable PostgreSQL fixture: 34 tasks, 31 visible task rows, one
paused
agent and one terminal failure. No active agent/provider work. HTTP
cache is
retained; each initial sample reloads the document and clears
application query
state. Warm navigation and Back retain query state. CPU and throughput
are native.
The 100ms scenario uses CDP minimum request-to-response-header latency
without
request interception. It is a controlled request-dependency experiment,
not a
staging reproduction.

Values are median milliseconds with min–max in parentheses. Paired
saving is
baseline minus candidate for each alternating pair; its median differs
from the
difference of arm medians. Positive saving favors the candidate.

| Network / journey | Baseline | Candidate | Median paired saving |
| --- | ---: | ---: | ---: |
| Native / initial rows | 187.6 (128.9–208.5) | 191.7 (130.3–210.9) |
−23.3 |
| Native / Recent → Mine | 100.17 (99.90–101.87) | 100.07 (99.88–101.37)
| −0.16 |
| Native / task → Back | 70.45 (56.63–72.02) | 66.64 (56.52–67.71) |
+4.94 |
| 100ms / initial rows | 930.3 (914.1–1015.7) | 799.0 (697.1–826.0) |
+211.8 |
| 100ms / Recent → Mine | 100.09 (97.73–100.16) | 100.18 (96.65–100.98)
| −0.02 |
| 100ms / task → Back | 74.97 (62.77–75.82) | 75.47 (61.51–76.19) |
+0.35 |

The controlled-latency initial pairs all improve, saving 108.3–217.0ms.
Native
initial pairs have mixed signs (−67.5 to +78.1ms), so they establish no
reliable
native page-speed improvement. Warm and Back timings are effectively
unchanged.
Median primary-request start is 110.4→135.2ms natively and 497.8→480.9ms
with
100ms latency; these runs do not establish a large startup-only timing
gain.

API requests through the two-second post-reveal window fall from median
32
(32–37) to 27 (25–28) natively, and 37 (33–38) to 30 (25–32) with 100ms
latency.
All 20 samples preserve the same ordered 31 task links, first-row DOM
identity
and geometry. All recorded API responses are HTTP 200; zero unfinished
requests,
429s or JavaScript errors. Lower 429 incidence on staging remains
unproven.

Measured source: baseline `44a9a043b80c18314d8dd89556f444dae696d3d6`,
candidate
`4c12b2ef0ecf0754676e99bd6b9399e84408646c`. Final head
`ecde295565309275c4f2fae903caf6892f1eaafd` changes only two feature-map
files
and existing browser test expectations since that measured candidate;
production executable inputs are identical to the measured candidate.
Asset hashes, API
health revision receipts and every raw sample were retained. These
results do
not claim hosted-page improvement or deployment of this PR.

## Risks

- Startup enrichment adds work to the session read. Failure of this
optional projection falls back to the ordinary company and access
endpoints. It does not invalidate a valid session or replace per-request
server authorization.
- Account changes, stale cache entries and same-company navigation can
expose old presentation data. Account keys, principal stamps, reveal
scopes and canonical bootstrap replacement have dedicated negative
cases. Permission failures hide cached task rows; transient network, 429
and 5xx failures preserve them.
- Current failure state can differ from the historical failed run. The
snapshot uses the existing canonical rich projection and verified
missing-ID coverage. Partial context failures retain an explicit retry
path.
- Mutation retirement follows events delivered to the current server
process. This adds no cross-replica invalidation protocol or distributed
read-after-write guarantee.
- New UI assets require the matching API. Older UI/session consumers
retain their existing response and endpoint behavior. There is no
database migration, provider call or deployment change.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, code execution, browser
automation and delegated source review. The exact runtime model ID and
context-window size are not exposed by 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
- [ ] 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 09:28:57 -06:00
DottaandPaperclip 27d35314be feat: connect experimental Cursor Cloud to native Runner (#18069)
## Thinking Path

> - Paperclip helps people manage AI agents at work.
> - Native Runner owns execution and task authority.
> - Operators need an explicit native Cursor Cloud choice.
> - Tools/files need company, task, session and run scope.
> - This connects setup and lifecycle to Runner Core.
> - Legacy agents retain their selected runtime/history.

## Linked Issues or Issue Description

Depends on #18068. Managed-only API placement and API-owned Stop
containment are in #18310. Refs #5664, #11803, #17299, and #17471. No
duplicate native integration was found.

## What Changed

- Add explicit native setup, Test, saved settings and dispatch with
no-prompt account/catalog/callback checks.
- Scope MCP to company, agent, task, session and run through
authenticated HTTPS. Register verified artifacts/work products; preserve
unpriced accounting, completion and budgets.
- Connect Question/Plan continuation, cold handoff and admitted fresh
sessions; recheck waiting admission under the task lock.
- Present audited Stop intent while preserving physical containment
proof; bound typed Stop metadata. Preserve title edits through
preference/navigation readiness.
- Select one live/history representation per run to prevent duplicate
settled cards. Integrate upstream task/read-state/retry/schema contracts
and repair two test fixtures.

## Verification

Core #18068 merged normally as `3910352c923a`. Product head
`5a60f541c982` has passing repository CI/build/browser checks, including
required `ci / verify` and `ci / e2e`, plus a fresh exact-head Greptile
5/5 review with zero unresolved threads. Two non-required external app
checks remain pending: Paperclip Review is queued waiting for its
assigned agent; Superagent Security Scan is reviewing the diff. These
are not claimed completed. The normal merge endpoint enforces the
repository rules; no policy or status override is used. The dependent
Top PR is addressing its fresh deferred-Stop review finding.

Product `5a60f541c982` passed 270 UI and 20 selected
Cloud/native/disk-full tests, UI/server typechecks, tokens and maps.
Top’s clean normal merge passed tokens/maps/diff checks; its matrix is
pending. Earlier receipts retain Core’s 47 Rust, 4 compiled-native
retirement and 19 settlement controls, normal Runner build, and runtime
9af build/recursive typecheck. Reproduced loading/shutdown CI fixture
failures passed 7/7 browser and 121/121 unit tests, including a
broken-unsubscribe negative control. No timeout/oracle was relaxed.

The actual-parent restack adopts 75 mainline paths, including
schema/permission and generic runtime changes, beyond the prior two
fixture repairs and client projection fix. The full difference from
hosted `9af1a5de42a2` is therefore larger than four files; backend
source also differs. The merged config-read checks match Core391, and
the Cursor placement/runtime/relay/selection modules match frozen9af;
selected affected regressions pass. Paid proof keeps its actual 9af
source/image identity. The separately tested live/history card fix is
not claimed deployed on that host.

On 9af, original saved settings survived reopening and fresh no-prompt
Test authenticated catalog/model/binding; no new Save is claimed. Fresh
Linux admission passed. With `claude-sonnet-4-5`, Artifact downloaded
exact 27 bytes and matched 121 canonical identities in complete
S3-served history. First Stop’s strict quiet FAILED on a 10.373-second
gap despite independent cancellation/no late artifact. One targeted
unchanged-sampler retry retained all 24 samples: 35.076 seconds quiet /
maximum gap 4.584 seconds, 67 ordered canonical identities, and separate
CANCELLED/artifact-absent status after the original 180-second deadline.
The pre-reload duplicate settled card is disclosed. Exact-ID cleanup for
the three new provider agents is awaiting authorization, so full grades
and the fresh Artifact’s post-cleanup download remain blocked. A
historical deleted-provider artifact was newly downloaded on 9af;
original F226 Question/Plan/reconnect and cleanup proof keep their
identities.

Retained failures: stale fixture 132/133; E8 48/135 with 87 reset-hook
failures; Mac 17 SQL failures/skips; earlier
title/startup/custody/Stop/deletion failures with unknown causes/bounded
recoveries; `dea023` Stop’s warning/50 duplicate identities; pre-repair
ACK and original Core CI failures. WSP’s 251-assertion, 7/8-suite
attempt failed physical PostgreSQL teardown; the unchanged wrapper rerun
passed 128. Retry alone establishes no cause or full local-suite pass.
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 checked through behavior; unpriced provider dollar
cost stays UNKNOWN.

Callbacks need authenticated reachable HTTPS. Scope, revocation,
budgets, completion and cleanup remain authoritative. Operator Stop
intent is separate from physical containment. Saved legacy agents retain
their selected runtime. Prior shared title-save/deletion failures remain
disclosed.

## Model Used

OpenAI Codex, GPT-6 family, assisted with reasoning, repository tools,
code execution, browser verification, and delegated code review. The
serving model’s exact API ID and context window are not exposed. No
value is inferred from the model catalog.

## 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; pending gates
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 09:01:39 -06:00
DottaandPaperclip 3910352c92 feat(runner): add experimental Cursor Cloud provider (#18068)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Runner gives each provider durable execution, tool, and completion
contracts.
> - Cursor Cloud currently has a direct adapter outside that native
contract.
> - Cloud work needs exact ownership across HTTP, SSE, Stop, and
recovery.
> - This PR adds a qualified Cloud API driver and verified proxy to
Runner.
> - Product setup and hosted placement remain separate dependent
changes.

## Linked Issues or Issue Description

Refs #5664, #11803, #17299, and #17471. Product integration is #18069.
Managed-only API placement is #18310. The related direct-adapter
model/transcript work is not replaced. No duplicate native Cursor Cloud
integration was found.

## What Changed

- Add native `cursor_cloud` / `cursor_cloud_api` with explicit
model/repository validation, bounded HTTP/SSE, durable per-turn
checkpoints and a qualified Node proxy launched through the Rust facade.
- Commit terminal custody before publication/release; coalesce
consumers, preserve ambiguous create/Stop/recovery fences, reject late
tools, and distinguish definitely-unsent oversized requests from
ambiguous responses.
- Support scoped cold attachment, current authority, reconnect and
verified artifact staging. Keep unsupported warm/external and Windows
paths closed.
- Preserve early source cursors and verify immutable legacy prefixes
before suffix replay, including supported compaction/masking. Optional
proof publication failure falls back to complete cold verification.
- Preserve canonical records at the durable native producer with
JSON-safe privacy and the existing 8MiB bound; drain admitted successor
flushes. Reconcile upstream semantic/pack/schema/lifecycle changes and
retain S3 and legacy CLI checks.

## Verification

Core head `37c442ec8dd4`: all 66 check contexts are terminal
successful/neutral/skipped, including required `ci / verify` and `ci /
e2e`; fresh exact-head Greptile is 5/5 with zero unresolved threads.
Dependent Product `81e828d20a77` and Top `dd2959689627` include the
later client handoff correction and are running their own fresh gates.

Core: 47/47 Rust, 4/4 compiled-native retirement, 19/19 settlement and
normal Runner build passed. Runtime Top `9af1a5de42a2`: build and
recursive typecheck passed. Earlier source-scoped evidence includes 398
focused tests, five relay tests and token/type gates. The 251-assertion,
7/8-suite run failed physical PostgreSQL teardown; the unchanged stable
WSP suite passed 128 through the documented wrapper.

The downstream fixture corrections passed 7/7 browser and 121/121
startup tests without timeout/oracle relaxation; a broken-unsubscribe
control fails. Hosted runtime `9af1a5de42a2` has a passing normal build
and recursive typecheck. Latest dependent heads additionally contain a
pure client fix for stale live/history overlap, with 48 focused tests,
UI typecheck and token gates passing. Its new client is not deployed in
the frozen hosted image; server/native/API/provider source is unchanged
by these four downstream files.

Fresh hosted Linux admission on `9af1a5de42a2` retired its owned
runner/proxy/socket with zero prompts. The saved configuration survived
reopening, and Test authenticated the bound key/catalog/selected model
without a prompt. Artifact with `claude-sonnet-4-5` completed,
downloaded the exact 27 bytes, matched 121 canonical identities against
complete logs and independently reached FINISHED. The active Stop retry
was clicked within its immutable boundary, produced one canonical
interrupted turn and acknowledged run-only cancellation, retained a
valid 35.076s quiet window (maximum gap 4.584s), and independently
remained CANCELLED/artifact-absent beyond the original 180-second
command deadline. All provider-observer credential callbacks, DB clients
and loaders closed; accounting is settled and explicitly unpriced. The
first fresh Stop retains its failed 10.373s sampling gap and every
sample.

On the upgraded9af server, the historical F226 artifact was reopened and
downloaded with its exact original hash after its provider agent had
been deleted/GET404 earlier today. Historical F226 full Artifact/Stop
cleanup, Question, corrected Plan and reconnect retain their original
identities. Deleting the three newly created disposable provider agents
and the new-case post-delete grade remain blocked on explicit user
approval after automatic review rejected expanding the two original
approved IDs. No new-case cleanup or complete fresh matrix pass is
claimed; this is separate from the directly observed task/Stop behavior
and retained exact cleanup proof.

Retained failures: stale fixture 132/133; E8 48/135 with 87 reset-hook
failures; Mac 17 SQL failures/skips; earlier startup-title, title-save,
custody/Stop and deletion failures with unknown causes and bounded
recoveries. `dea023` Artifact passed 17 checks but Stop failed its false
warning and 50 duplicate log identities. Pre-repair ACK failure and
original Core CI failure remain recorded; retry alone establishes no
cause or full local-suite pass. Provider dollar cost stays UNKNOWN.

## Risks

Experimental: Cursor independently acknowledges neither the effective
model nor instruction digest before the first prompt. The caller’s
explicit values are submitted and behavior checked live. Unpriced
provider dollar cost stays UNKNOWN.

Only qualified per-turn controllers are admitted. Uncertain work and
incomplete cleanup stay fenced. Existing damaged/duplicate archives
remain failed evidence. Prefix proof adds bounded object-store I/O; the
8MiB producer bound does not guarantee future providers’ output fits.

## Model Used

OpenAI Codex, GPT-6 family, assisted with reasoning, repository tools,
code execution, browser verification, and delegated code review. The
serving model’s exact API ID and context window are not exposed. No
value is inferred from the model catalog.

## 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; pending gates
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 08:38:57 -06:00
DottaandPaperclip 86b365f992 feat(auth): add independent agent configuration read permission (#18766)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents need to inspect configuration and instructions when they set
up teammates.
> - Configuration reads currently depend on configure or suggestion
grants.
> - Legacy standard agents did not receive those newer grants. Some
self-read routes also lose the target agent identity.
> - This pull request adds an independent read grant and applies only
that grant to eligible existing agents.
> - The benefit is that agents can inspect and verify configuration
without new write authority.

## Linked Issues or Issue Description

Refs #15283. Related earlier proposals: #8064 and #12050. Those
proposals target older authorization code; this change uses the current
configure/suggest model and adds a read-only rollout for existing
agents. Related bounded peer metadata proposal: #14160.

**What existing behavior does this improve?**

Agent configuration, configured skill, instruction, and revision reads
through the existing APIs and native instruction tools.

**Current behavior**

An agent needs `agents:configure` or `agents:suggest-changes` to inspect
a peer. Older standard agents can hire a teammate but cannot verify the
saved instructions. Configuration and revision routes also reject
self-reads because they authorize a company resource instead of the
target agent.

**Proposed behavior**

Add `agents:read` for redacted configuration and instruction reads. Keep
`agents:configure` as read/write and preserve suggestion-grant
compatibility. Standard new agents receive the read grant. A one-time
migration grants only reads to eligible existing standard agents.
Individual routes authorize the target agent and grant scope.

**Reason and benefit**

Read access can be granted without configuration changes, rollback, or
environment probes. Legacy agents can verify teammates without receiving
new write permissions.

## What Changed

- Add `agents:read` to the shared permission contract and standard-agent
defaults.
- Accept read, configure, or suggestion grants for configuration reads,
with target scope and the responsible-user ceiling.
- Fix target-aware self and scoped configuration, skill, and revision
reads.
- Add an audited data migration. Skip low-trust, built-in, inactive,
pending, terminated, setup, and scoped configuration/suggestion cases.
Preserve existing read scopes and later revocation.
- Update the specifications, default-permission documentation, and agent
access map.

## Verification

- Focused acceptance: 276 tests passed across seven suites on commit
`a37314df88`: authorization, real-key HTTP access, route authorization,
canonical instructions, default grants, migration replay, and lifecycle
configuration.
- The HTTP journey uses a real hashed agent API key and a disposable
PostgreSQL database. It verifies redaction, scoped reads, denied edits
and rollback, cross-company isolation, and both agent and
responsible-user revocation.
- New read-grant regressions failed before the authorization change. A
realistic paused/pausing/resuming migration fixture also failed before
the lifecycle filter correction and passed afterward. Setup-held paused
agents stay excluded.
- `pnpm -r typecheck` and `pnpm build` passed on the rebased branch; the
final lifecycle migration correction also passed the database package
typecheck and build. The latest commit changes only a test assertion.
- `pnpm --filter @paperclipai/db check:migrations`, `node
scripts/check-maps.mjs`, and `git diff --check` passed.
- Full current-head [CI
run](https://github.com/paperclipai/paperclip/actions/runs/41641531426)
passed: typecheck, build, all general and serialized test lanes, Runner
checks, all eight browser shards, canary dry run, policy, and aggregate
gates. The local full-suite attempt was stopped after exposing the
outdated cross-company 403 assertion; interrupted local full-suite
attempts are not counted as passes.

## Risks

The migration deliberately expands redacted configuration and
instruction visibility for eligible existing standard agents in the same
company. An absent older write grant does not deny the new read
capability. It adds no write grant. Scoped configure/suggestion grants
and all existing read-grant scopes stay intact. Administrators can
remove the independent read grant after the one-time rollout; ordinary
updates and startup do not restore it. Configure and suggestion grants
still imply reads within their own scopes. Cross-company
configuration/history reads now return 404 through the common
target-aware helper. No schema change or credential-value disclosure is
added.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository tools, and code
execution. The host does not expose a more specific runtime 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 07:26:27 -06:00
DottaandPaperclip 07721e33f7 fix(runner): distinguish new skill authoring from source imports (#18717)
## Thinking Path

> - Paperclip lets people manage agents and shared company skills.
> - Agents can author new skills or add existing skills from approved
sources.
> - Source import and catalog install retain the source used by update,
audit and reset flows.
> - The create_skill tool described saving complete Markdown, without
explaining this choice.
> - An agent can therefore read an existing source and create an
unrelated managed copy.
> - This PR explains the distinction in the existing tool contract and
tests its actual model-facing projections.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The model-facing description of create_skill and the choice between
authoring and source import.

**Current behavior**

The tool says it saves a complete single-file skill. It does not
distinguish new authored content from adding an existing skill. The
Company Skills reference already directs external sources to import and
catalog skills to install.

**Proposed behavior**

The tool explains that it saves newly authored content. It directs
existing-source work to the existing import or install APIs through
search_api and call_api. It retains the existing assignment and retry
guidance.

**Reason and benefit**

A managed copy does not retain the original source locator. Clear
guidance helps an agent choose the source-aware operation without adding
a tool or changing permissions. This is a guidance repair. It does not
claim that the import backend failed.

## What Changed

- Clarify the canonical documentation, live descriptor and scenario
descriptor for create_skill.
- Test the actual discovered contract and provider projection, including
API claim and company-boundary negatives.
- Refresh generated descriptions and the seeded catalog digest and
manifest entry.

## Verification

- A focused regression fails on the parent because the actual discovered
contract lacks the authoring versus import distinction.
- Three focused pure suites pass: 48 tests. They include existing skill
creation, retry, schema and authorization controls.
- The source-owned serializers and protocol fixture validator
regenerated the inventories without an app or native build.
- Both generated inventories change only create_skill description.
Arguments, required claims, modes, idempotency and all other tools
remain byte-equivalent.
- Current head `bca441726a6cba80e6e7da0321db59e27183c5b8` passes
required `ci / verify` and `ci / e2e`; the full main CI run has 49
successful jobs. Windows, Docker, security and review checks also pass
or are intentionally skipped. Greptile and the final Paperclip review
are 5/5, with zero unresolved threads. This PR remains deliberately
unmerged for the separately reviewed evaluation candidate.

- Original head CI preserved: one unrelated UI clipboard-confirmation
assertion failed after the clipboard call passed; 47 other executions
succeeded and the final verify check failed as its dependent. The two UI
owners are unchanged from the base. Exact React scheduler/timer timing
is unknown. No unchanged-head retry was used before the necessary master
conflict sync.
- The conflict sync preserves incoming task-assignment guidance and
regenerates metadata. It is not a fix for the UI test failure. The
synchronized current head passes CI and review. No same-head recovery
was consumed.

## Risks

The description changes model context and the catalog digest. It may
affect tool choice, but actual same-request and same-model acceptance is
still pending. The change does not alter authorization, API behavior or
grading. Local checks did not run the app, database, native runner, full
graph typecheck or build. Intermediate test-fixture claim and placement
mistakes were corrected; the final focused run passes. Do not treat
these pure controls as a feature pass.

## Model Used

OpenAI GPT-6.1 Sol (`gpt-6.1-sol`) through Codex, with reasoning, source
inspection and tool execution. The host does not report a 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; narrow
create_skill/import and skill-import-guidance searches found no
duplicate.
- [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 06:27:50 -06:00
DottaandPaperclip b22ef22d56 fix(runner): show provider failure details on tasks and runs (#18622)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native Runner starts providers and records their run outcomes.
> - Provider failures can contain useful details below a generic
protocol error.
> - Those details were lost at the sidecar, command receipt, and run
projection boundaries.
> - A blocked task therefore showed a generic failure even when the
provider gave a clear reason.
> - This pull request carries a bounded, sanitized provider failure
through those boundaries.
> - Operators can read the actual cause on the task and run pages.
Existing recovery policy still controls retries.

## Linked Issues or Issue Description

Refs #17543 for the related closed JSON-RPC classification work. Refs
#17272 for provider recovery in the legacy OpenCode adapter. This change
adds display evidence for native Runner providers and keeps recovery
policy separate.

**What happened?**

A native Claude session returned an HTTP 429 message below an ACP
`Internal error`. The task and run showed only a generic startup
failure. Operators had to inspect the provider response to learn that
the account was rate limited.

**Expected behavior**

The task and run show the sanitized provider message, provider name,
startup or turn phase, and available HTTP status. Unknown categories
keep the message. Display evidence does not grant a retry or replace a
recovery decision.

**Steps to reproduce**

1. Start a native Claude run whose account is rate limited during model
selection, or return a structured provider failure from a Codex or
OpenCode turn.
2. Let the run fail and open its task page.
3. Before this change, the page shows a generic failure. With this
change, the page shows the provider explanation. The run page shows the
same explanation after reload.

**Paperclip version or commit**

Source checkout based on `f8a888a072`.

## What Changed

- Add one JSON Schema for `ProviderFailure` and generate matching Runner
and shared TypeScript readers. Keep the Runner package independent of
the app workspace at runtime.
- Normalize known ACPX, Codex, and OpenCode error fields. Preserve
unknown messages. Redact known credential values and credential patterns
before applying the 4,000-character limit. Do not copy response bodies,
headers, stacks, or prompts.
- Carry startup failures through the sidecar, Rust Runner, durable
command receipts, and private transport error details. Keep provider
text out of generic transport error strings and Sentry failure messages.
- Keep turn failures in authenticated run events. Recover the same
evidence from persisted events during server finalization. Store the
cause in the company-scoped run result.
- Show one shared notice on failed tasks and run details. Keep existing
retry permissions, scheduled retry state, and resolved-task behavior.
- Reapply registered run-secret and current-user redaction to task
display data. Batch secret lookups for only the visible provider causes.
- Document the contract and update the capability maps. Add regressions
for nested Claude 429 errors, OpenAI quota and rate limits, OpenCode
errors, redaction, durable receipt replay, restart finalization, and UI
rendering.

## Verification

- `pnpm -r typecheck` passed. `pnpm build` passed. All GitHub Actions
build, test, package, and validation jobs passed on `d49c79ab4a`. The
security checks passed. Greptile gave this head 5/5 with no open review
threads. Paperclip Review remains pending. Its review worker exhausted
six workspace recovery attempts with export_interrupted. A fresh review
request was accepted and is waiting for that saved workspace. The
ordinary Retry action rejected reuse of the stopped request. This is an
external review-worker blocker; no check was overridden. The duplicate
local `pnpm test:run` was stopped after the CI test shards passed.
- Focused server settlement, provider evidence, and restart tests: 93
passed. Runner admission and normalization tests: 103 passed. Focused
Runner wire and terminal tests: 29 passed. The real Rust/sidecar command
receipt replay test passed.
- The new projection test failed before implementation because the task
had no provider cause. The startup producer regression also failed
before extraction moved ahead of the generic error-code replacement.
Both pass with this change.
- Live check: a rebuilt local sidecar received an actual Claude HTTP 429
during startup. The new cause kept the full provider message. A
synthetic task in an isolated local app then reached the same provider
error through the native Runner path. The dedicated run result, task
API, task banner, and run detail all showed the same message after
reload. The later reporting fix keeps run.error generic; a regression
proves the display cause still survives restart and stays out of Sentry
capture.
- The first isolated app attempt failed earlier in
`admission_credential`. An ordinary manual Retry reached the provider
and produced the 429 above. This PR does not claim to fix that separate
admission failure. An initial parallel local database test setup also
failed; the serial restart suite passed.
- Security review found that copying display text into run.error could
forward it to Sentry. A regression failed with that behavior. The fix
keeps the cause in its display field. All 38 settlement/restart tests
and the server typecheck pass.
- All 29 protocol contract tests pass, including bounds and extra-field
rejection. An earlier full Rust core run passed. The later full run hit
one 5-second receipt-limit timing assertion; its isolated rerun passed.
The full Linux and Windows Rust CI jobs passed on this head.
- Review regressions pass for explicit Codex driver identity, native
Rust startup and resume receipt propagation, oversized OpenCode/ACPX
errors, task read-time registered-secret redaction, and the standalone
source closure. The focused Runner run passed 103 tests;
projection/restart tests passed 16.
- Read-time redaction edge cases passed: a missing decryption key omits
only the unsafe optional explanation; expanded redacted messages remain
visible within the Unicode character limit. All 14 task projection tests
and server typecheck pass.
- The package Build check found an old Pi assertion against
Error.message. The probe now checks the exact closed classification in
the authenticated receipt and requires the new provider cause. Both
polling transport cases pass. A local credential-free Pi probe stopped
at admission_installation because this checkout lacks the Pi
distribution; the installed Linux package probe passed in CI on this
head.
- UI coverage includes startup and turn text, unknown categories,
literal rendering of provider text, read-only retry behavior, and
resolved-task suppression. Token gates and map checks passed.
- The live check covers Claude startup on macOS. Codex, OpenCode,
terminal failures, and restart paths have deterministic regression
coverage. The change is not deployed.

## Risks

- Provider messages are untrusted text. The boundary selects specific
fields, redacts credential patterns and known environment secret values,
bounds the message, and renders plain text. New provider-specific secret
formats may need added redaction patterns.
- Optional fields preserve compatibility with older producers and stored
runs. Old runs without the field keep their current fallback text. No
database migration is required.
- The cause stays in company-scoped run data. This change does not send
provider text to first-party Telemetry or add it to OpenTelemetry span
attributes.
- Normalized categories and retryability are display evidence. Existing
recovery classifiers, retry limits, ownership checks, and approval gates
keep their authority.

## Model Used

- OpenAI GPT-6 through Codex, with reasoning, repository tools, code
execution, and browser verification. The host does not expose the exact
serving 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 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 06:13:26 -06:00
DottaandPaperclip 89e3c23062 fix(daytona): stream files when export copies cannot fit (#18712)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A native task must save its files before finalization can release
its writer.
> - Remote workspace capture can produce an archive that fits on the
sandbox disk.
> - Daytona then makes another copy before it downloads that archive.
> - The second copy can run out of space and leave completed work behind
a save hold.
> - This pull request uses the existing bounded stream when that copy
cannot fit.
> - The benefit is a saved archive without a second archive-sized
sandbox allocation.

## Linked Issues or Issue Description

Related: #18644 reduced capture scratch space. #18172 added the
disk-free directory export. #18705 preserves provider checkpoints on a
full disk and reports capture failures. This change handles the later
single-file download copy. It does not depend on #18705 and does not
replace its checkpoint fix.

**What happened?**

A native provider turn returned a successful result and captured its
workspace archive. The Daytona outbound file guard then failed with exit
43 and `No space left on device` while it copied the archive to a
reserved download file. Finalization and file-preservation retries could
not pass this boundary. The workspace kept its writer hold.

**Expected behavior**

Download an existing archive through a confined sandbox process when its
second copy cannot fit. Keep the source and all finalization holds until
the normal save commits.

**Steps to reproduce**

1. Capture a workspace archive in a Daytona sandbox.
2. Leave less free space than the archive needs for another copy.
3. Finalize the task. The outbound single-file guard fails before
download.

**Paperclip version or commit**

Reproduced against `6d72243d8a`. The incident used the native sandbox
runtime. This branch is based on current master.

**Deployment mode**

Cloud with native Paperclip Runner execution and remote workspace
storage.

## What Changed

- Remove partial outbound file copies after a failed snapshot command.
Only copy capacity failures select the stream path. Permission,
confinement, and transport errors still fail.
- Use the existing acknowledged terminal stream to read each file as the
sandbox user. Recheck canonical confinement and regular-file status. Use
a no-follow open. Keep the sandbox disk limit, host free-space reserve,
shared transfer deadline, and bounded teardown.
- Keep host files in private temporary files until the complete batch
has transfer receipts. Never pass a mutable live source to the
privileged download API.
- Add real-file and real-terminal regressions, a disposable Daytona
acceptance case, and workspace verification notes.

## Verification

- Red before repair: a real 5 MiB copy under a controlled file-size
limit failed with exit 43. The same test passes after repair and
compares every source and destination byte.
- The ten capacity cases pass. They cover producer failure after output,
source replacement, stream size limits, permission errors, timeout
teardown, cleanup, and a successful retry. A two-file case completes the
first stream and fails the second. Both old host files stay intact, both
source files survive, and retry saves both files. An isolated
early-publication mutation makes that test fail.
- The initial complete Daytona package suite passes: 537 tests passed, 8
skipped. After the test-only review follow-up, all 60 focused transfer,
stream, and diagnostic tests pass. The package typecheck also passes.
- Real Daytona acceptance passes: an owned Ubuntu 24.04 sandbox stops
and resumes, a copy to `/dev/full` reports ENOSPC, production export
saves the exact archive hash, and a second save after reconnect passes.
The test deletes only its disposable sandbox. This is provider-boundary
acceptance, not a UI journey.
- Assembled finalization acceptance passes with real Postgres and a live
Linux x86_64 Daytona sandbox. The production control-plane port accepts
one fixture result. The production workspace finalizer encounters the
failed copy, saves the files, and commits the task as `done`. A new
database client reads the committed result and exact file hash. The
workspace has no writer or running save, and there is still one run and
one result. No model turn runs. The target resolver is bound to the live
provider fixture. Object storage uses the production encryption and
local-disk service with an S3 admission label; this does not prove an S3
network transfer or a UI journey.
- The live tests used the fixed transfer source with SHA-256
`857b3d8c9ad75170707d95277c17c051ce4582fa79a9c38331d51d2800b36af5`. That
source is identical in this PR head.
- `pnpm -r typecheck` passes locally. The duplicate full local test run
passed all six chat shards before it was stopped during the server lane
in favor of completed current-head CI; a duplicate local build was
skipped. Full current-head CI passes, including typecheck, build,
general and serialized tests, Rust/Runner checks, all eight E2E shards,
and the `ci / e2e` and `ci / verify` aggregate gates.
- Greptile is 5/5 on the current commit with no unresolved review
threads.
- `node scripts/check-maps.mjs` and `git diff --check` pass. Map checks
prove structure only.

## Risks

- The fallback requires the Bash and GNU `dd`/`base64` tools already
required by the existing stream path.
- Terminal streaming is slower than the normal bulk download. It runs
only after a copy capacity failure.
- The workspace capture itself still needs sandbox space. This change
does not fix a full disk before an archive exists.
- The test injects a failed copy sink. It does not exhaust a shared host
disk. No original task was retried, and no deployment or merge is part
of this PR.
- No schema, API permission, writer ownership, or retry-budget change.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository inspection, code
editing, shell tools, browser diagnostics, and automated tests. The
exact serving variant and context-window size are not exposed in this
session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [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 06:11:41 -06:00
DottaandPaperclip 1bc3ed6dd5 fix(opencode): keep native tool calls pending until Runner settlement (#18711)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native OpenCode sends semantic tool calls through the durable Runner
protocol.
> - The Runner retains a call until its result is known.
> - OpenCode and its HTTP bridge each stopped waiting after 30 seconds.
> - A slow response could leave a call pending after the provider had
moved on.
> - A later run then failed the pending-call guard during attachment.
> - This pull request keeps native calls under their Runner owner until
settlement or cancellation.
> - The benefit is one delivered result and a safe next run.

## Linked Issues or Issue Description

Related: #18343 applies the durable wait policy to native ACPX calls.
This change applies that policy to native OpenCode.

**What happened?**

An assigned tool call took more than 30 seconds. OpenCode reported an
MCP timeout while the Runner still retained the request. The source turn
could finish, but suspension failed. A later `run.attach` failed because
provider tool calls were still pending.

**Expected behavior**

The native proxy must wait for the authoritative result under the Runner
owner. Stop, bridge close, and lease expiry must still end the wait.
Unknown effects must still block attachment.

**Steps to reproduce**

1. Start a native OpenCode run with an assigned semantic tool.
2. Hold the controller's result for 31 seconds.
3. Observe the provider's MCP result and attach a later run to the
session.

**Paperclip version or commit**

Reproduced against `6d72243d8af0362d73fbcb5331fd42a384800d4e`. The
branch is based on `36df8933f0`.

## What Changed

- The native OpenCode proxy enables a driver capability for durable tool
requests.
- That capability removes the bridge's separate request timer and sets
OpenCode's fixed MCP client ceiling to one hour, consistent with the
native ACPX sidecar.
- Ordinary driver calls keep their 30-second deadline. Assigned external
MCP connections keep their existing budget.
- Cancellation, result replay, tool authorization, and the pending-call
attachment guard keep their existing contracts.
- The native regression holds a result for 31 seconds, then completes a
second run with the same Runner process and no pending provider calls.
- The deterministic provider fixture honors the emitted MCP budget and
retains the initial completion contract across ordinary follow-ups.
- Execution semantics and the session verification maps describe the
wait and its safety checks.

## Verification

- Red before production edits: the bridge aborted a durable request at
the ordinary 30-second boundary. The driver emitted `30000` instead of
the expected `3600000` client budget.
- The final combined bridge, driver, proxy, and native protocol run
passed: 95 tests in five files (71.17 seconds).
- The native delayed-result probe passed with the current Rust Runner
and current bundled proxy. It completed the later run with the same
Runner PID and an empty pending-call ledger.
- Actual provider check passed on macOS arm64 with
`opencode-ai@1.18.34`, the changed source, and a local deterministic
OpenAI-compatible model. One assigned write waited 31 seconds and saved
one file. Both turns completed. The second turn retained the saved tool
receipt. The write ran once across five model requests.
- Bridge safety cases cover result replay without another action,
ordinary timeout behavior, MCP cancellation, and teardown of an
unanswered durable request.
- Both runner TypeScript projects passed typecheck. The first attempt
lacked the eval-kernel build; it passed after building that declared
dependency.
- Map alignment, module boundaries, and `git diff --check` passed.
- The first native follow-up probe failed because its synthetic provider
forgot the initial completion-contract revision. This caused a repair
turn and another discovery call. The fixture now retains the contract,
and the same probe passed. The failure is retained here.
- Runtime acceptance used the implementation at `583d316ad4`. The later
clarification commit changes comments, documentation, and a test name
only. It does not change runtime behavior.
- Full current-head CI passes, including typecheck, build, general and
serialized tests, Rust/Runner checks, all eight E2E shards, and the `ci
/ e2e` and `ci / verify` aggregate gates. Greptile is 5/5 on this
commit, with no unresolved review threads. No deployment or retry of the
affected historical task was performed. The actual provider check used a
local model; it does not qualify a deployed Linux sandbox or a live
model service.

## Risks

- A native call can wait longer while its Runner owner remains valid.
Stop, teardown, and lease expiry still end the wait. The MCP client has
a separate fixed one-hour ceiling. Runner ownership can renew beyond
that ceiling. If the client deadline expires first, the operation
outcome remains unknown and the existing hold must still block replay
and run attachment.
- This prevents new premature timeouts. It does not classify or replay
an old request whose effect remains unknown.
- No schema, migration, credential, or permission change is required.

## Model Used

- OpenAI Codex, GPT-6 family. The harness does not expose the exact
model ID or context window. Capabilities used: code generation,
tool-assisted diagnosis, local execution, and test analysis.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-03 06:11:24 -06:00
DottaandPaperclip dd02595390 fix(db): retain hosted census construction callers (#18725)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators use a bounded database census to find expensive read
paths.
> - The hosted server combines application code and capture wrappers in
generated files.
> - Generic HTTP owners suppressed caller capture. Generated paths also
failed the caller allowlist.
> - This PR retains trusted query construction locations without source
maps.
> - Operators can distinguish application query sites while exact
owners, privacy limits and capture bounds stay intact.

## Linked Issues or Issue Description

Refs #17522, #17564.

**What happened?**

A real map-free bundle with two application query sites produces one
callerless census group. This affects unowned queries and queries under
the generic `http.request` owner.

**Expected behavior**

The census should distinguish trusted query construction sites. It must
keep unknown calls unknown and preserve specific semantic owners without
stack capture.

**Steps to reproduce**

Run the committed bundle test and pinned comparison helper. The helper
bundles the actual capture module and real Drizzle, with a fake
postgres-js client. It does not assign a fake path to an isolated mock
caller.

**Paperclip version or commit**

Control: `09162fe893b464dd2ccdafd14bd3ef1611202a86`. The branch is
rebased onto `1e3ab690b072bbf6a1c1f1d0454ea49315b2074c`; neither
intervening commit changes this owner.

## What Changed

- Keep the exact generic HTTP owner and add bounded caller capture only
during the active census window.
- Exclude capture wrapper frames with a local V8 stack boundary. Keep
one static generated construction anchor for trusted root builders.
- Keep unscoped bundled driver and relational paths unknown. Their first
frame can belong to the ORM.
- Cover exact application lines, alternate parameters, prepared
builders, transactions, nested savepoints, privacy and inactive windows
in a real bundled scaffold.
- Add a portable pinned comparison helper and retained measurements.
Existing group, page, byte, expiry and output-rate caps remain in force.

## Verification

- PASS: 54 focused unit, real-bundle, existing runtime bundler and
real-PostgreSQL tests.
- PASS: database TypeScript check.
- PASS at final head `b270b92821`: full local `pnpm -r typecheck` and
`pnpm build`.
- The first local `pnpm test:run` was stopped after two chat partitions
passed (668 cases), because source changed during the run. This is
partial evidence only. The fresh final-head full local command passed
all six chat partitions (1,829 cases). Its general phase reported one
failure in the unchanged workspace port-conflict test. The exact case
then passed alone (1 pass, 178 skipped), and the unchanged full file
passed 179/179. The original failure did not reproduce; its cause is
unknown. The general phase was stopped after its remaining serial queue
was measured at roughly three hours. Cancellation did not print the
original assertion diff. The full command is failed/incomplete, not
passed. Remaining general files, workspace-project phases and 164
serialized files were not reached. Fresh final-head CI passes all
partitions.
- Final-head code CI passes. Greptile is 5/5 with its thread resolved.
Paperclip Review is delayed by an external review workspace recovery
failure; it has not completed on this head.
- Five serial alternating rounds per mode, 1,000 queries per sample, 40
retained samples, Node v24.22.2. Generic and unowned attribution rises
from 0/1,000 to 1,000/1,000 across two exact application anchors.
Specific owners stay stack-free.
- HTTP median wall cost is 10.528 → 15.169 µs/query; process CPU is
15.461 → 17.662 µs/query, with noisy overlapping ranges. This is census
overhead, not a product speedup.
- Review failure retained: the existing runtime bundler test first
failed because the public parser accepted arbitrary generated frames.
The fix restores default rejection and permits generated frames only
from the trusted builder boundary. The unchanged existing test now
passes.
- Full ranges, raw samples, proof boundaries and reproduction commands:
https://github.com/paperclipai/paperclip/blob/b270b92821ae47c456a1b61d78c1593c5348ede7/doc/performance/2026-11-03-db-capture-hosted-callers.md

## Risks

- Active generic HTTP census now captures stacks. More groups can reach
the existing 2,048-group cap sooner.
- Anchors identify construction sites in one exact generated build. They
do not identify later async consumers, TypeScript lines or CPU owners.
- Unscoped driver calls can remain unknown. This is intentional.
- The no-network local fixture excludes deployed Sentry/OTel stack
formatting and traffic effects. It does not establish exact hosted
overhead.
- Wrappers remain installed after expiry. Disabled, future and expired
windows do not capture stacks. No schema, authorization, provider, or
query behavior changes.

## Model Used

OpenAI GPT-6.1 Sol (`gpt-6.1-sol`) with reasoning, code execution and
repository tools. The host does not expose the context-window size.
Independent review used the same model family.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [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 06:04:42 -06:00
Nicky LeachandClaude Opus 5.5 32f93cf9dd fix(runner): set Runner OOM protection at launch (#18730)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The native Runner supervises the harness and tool processes for each
run
> - A launcher can start Runner with a negative `oom_score_adj`, and
Linux children inherit that value
> - Then a large tool process has the same protection as Runner
> - If that protection is -1000, the kernel cannot kill the process, and
a VM with no other candidate panics
> - The correct protection for Runner is different in each environment,
so one static value is not correct
> - This pull request adds an optional `--oom-score-adj` launch flag.
With the flag, Runner also sets supervised processes to 0 when Runner
has a negative score
> - The benefit is that the launcher controls Runner protection, and the
kernel can kill the large process and let Runner report the failure

## Linked Issues or Issue Description

Related: #14509 and #16844. These pull requests change scores for the
server and for sandbox tool commands. This pull request changes the
native Runner.

**What happened?**

A native Runner on a VM inherited `oom_score_adj` -1000 from the VM init
process. A typecheck under Runner used most of the VM memory. The kernel
could not select any process and panicked with "System is deadlocked on
memory". The VM restarted, and the run failed with a transport error.

**Expected behavior**

The kernel can kill the large tool process. Runner continues and reports
the result.

**Steps to reproduce**

Start Runner with a negative score. Start a supervised process. Read
`/proc/<pid>/oom_score_adj` for the supervised process. The value is the
same as the Runner value. After this change, the value is 0 when the
launcher gives the flag.

**Deployment mode**

Native Runner on Linux, when the launcher gives Runner a negative score.

## What Changed

- Add the optional `--oom-score-adj <-1000..=1000>` flag to
`paperclip-runnerd`. Runner has no default value.
- Stop Runner startup when the value is not valid. When the kernel
refuses a valid value, or the platform is not Linux, write a diagnostic
and continue, because Runner is then only less protected than the
launcher asked for.
- When the launcher gives the flag and the Runner score is negative, set
each supervised process and verified stdio MCP server to 0 after spawn.
- Without the flag, Runner does not change any score. Children inherit
the Runner score, as they do now.
- Add unit and integration tests.

## Verification

Tests ran in `rust:1-bookworm` (rustc 1.99.1) on Linux arm64 with
`CAP_SYS_RESOURCE`:

- `cargo fmt --check -p paperclip-runner-core`
- `cargo test -p paperclip-runner-core --no-fail-fast`, before the flag
gate commit: all targets pass except `codex_provider` and `stdio_mcp`.
- `provider_receives_the_isolated_codex_auth_home` and its subprocess
helper fail in this container. They fail in the same way on clean
`master`.
-
`receipt_limit_polls_an_authoritative_terminal_with_unacknowledged_events`
and `cancellation_waiting_behind_output_is_handled_before_new_dispatch`
failed once in the full run. They pass when they run alone, on this
branch and on clean `master`. I think that they are sensitive to load,
but I did not prove this. CI is the check for these tests.
- `--test oom_score` and `--test oom_score_without_flag` pass with and
without `CAP_SYS_RESOURCE`. It covers the real Runner binary, flags that
are not valid, a Runner that cannot apply a negative score, and a
supervised child of a Runner with a negative score. The tests for
children of a Runner with a negative score skip themselves without
`CAP_SYS_RESOURCE`. The unit test covers the same reset logic without
that capability.

I also ran the debug Runner binary through the agent computer `enter`
command in a container, as UID 1000, with a floor of -500:

- Runner stayed alive at -500.
- The `oom_score` integration test passed in the same setup. Its
supervised child had the score 0.

After the flag gate commit, I ran the library `oom_score` tests, `--test
oom_score`, `--test oom_score_without_flag`, and `--test
process_supervisor` again. CI runs the complete suite.

I did not run this on a real VM.

## Risks

- A fork in the short time after exec and before the write keeps the
Runner score. The kernel can still kill that process unless the score is
-1000.
- The write to the child can fail. Runner ignores this failure, because
the child has no more protection than Runner.
- A launcher without the necessary privilege gets a Runner at the
inherited score. Runner writes a diagnostic to standard error, and the
run continues. A launcher that discards standard error does not see the
diagnostic.
- A launcher that does not give the flag has no change, also when Runner
starts with a negative score. On Linux, each spawn has one more atomic
read and no system call. A `oom_score_without_flag` test binary checks
this.
- A new Runner under a launcher that does not give the flag and gives
Runner -1000 keeps its children at -1000. Update that launcher to give
the flag.

## Model Used

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`) in 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
- [ ] 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-02 22:02:45 -08:00
DottaandPaperclip 09162fe893 refactor(runner): clarify native task assignment guidance (#18704)
## Thinking Path

> - Paperclip lets people assign work to AI agents.
> - Native task creation can start the assigned agent as soon as the
task is ready.
> - The default assignee is the agent that creates the task.
> - An agent can omit the requested teammate, create work for itself,
and reassign it too late.
> - The server already supports setting the requested agent and project
during creation.
> - This pull request makes those fields and the early-start risk clear
in both tool catalogs and the delegated-task example.
> - The benefit is clearer guidance for the existing delegation
contract.

## Linked Issues or Issue Description

Related: #13281.

**What happened?**

An observed delegation attempt called `create_task` with only a title
and retry key. The task used the documented current-agent default and
started work before a later reassignment. The server honored its
contract. The original failed QA result remains failed.

**Expected behavior**

When a user names an existing teammate and project, the agent should put
their exact IDs in `assigneeActorId` and `projectId` during task
creation. The requested worker can then start with the correct task
ownership and project.

**Steps to reproduce**

1. Ask an agent to create work for a named existing teammate in a named
project.
2. Create a ready task with only its title and retry key.
3. Observe that the creating agent is assigned and can start before a
later reassignment.

This is a guidance mitigation for agent tool misuse. It does not change
the default or enforce model compliance. A fresh deployed qualification
is still required.

## What Changed

- Explain initial teammate and project selection in the canonical
`create_task` description and its two field descriptions.
- Add a representative delegated-task call with an explicit agent,
project, and saved plan.
- Synchronize the same three descriptions in the sibling semantic-action
catalog. Keep its schema constraints intact.
- Keep the global execution prompt byte-for-byte equal to the base.
Revision `paperclip-execution.v8` already directs the agent to create
delegated work with the teammate ID.
- Refresh the generated catalogs, seeded catalog identity, and protocol
manifest.
- Make the collaboration contract test use the declared scenario
example, as the general contract test already does. Retain separate
live-example validation and strict scenario input checks.

## Verification

- All 79 existing native controls passed across `chat-project-tools`,
`paperclip-runner-tool-authority`, `task-creation-backlog`, and
`tool-receipt-recovery`. These cover alternate-agent readback,
current-agent defaults, backlog holds, company boundaries, and dispatch
recovery after commit.
- All 108 protocol-action, semantic-catalog, catalog-doc,
runtime-context, and collaboration checks passed. All 11 contract
fingerprint checks passed.
- The original CI collaboration-test failure reproduced locally before
correction: the test sent live-only fields to the older scenario schema.
After selecting the declared scenario example, all 13 collaboration
cases passed. Malformed titles, undeclared project fields, and arbitrary
unknown fields still reject. No schema or live example was changed for
this correction.
- Generator checks passed for semantic contracts, the semantic-action
catalog, operation groups, and protocol coverage.
- `pnpm -r typecheck`, `pnpm build`, `node scripts/check-maps.mjs`, and
`git diff --check` passed.
- A structural comparison confirms that types, required fields, lengths,
modes, permissions, outputs, and defaults are unchanged on both catalog
surfaces.
- No new text-matching tests were added. These local checks passed again
after the global prompt was restored. Fresh exact-head Greptile and
Paperclip reviews are both 5/5 with no actionable findings.
[Current-head
CI](https://github.com/paperclipai/paperclip/actions/runs/41565349812)
completed successfully on `2e5f93e122191e17b316a7780a74ff404fc468f3`
(attempt 2), with all 49 workflow jobs successful.
- Current-head CI first failed only the unchanged native-process
retention case at its original five-second limit. The shard passed 2,130
other tests and skipped eight. One unchanged local Node 24 control
passed. The original test and retention dependency blobs match the base.
The exact timeout cause remains unknown. No source, assertion, or time
limit changed. One authorized recheck of only that runner shard and its
normal final gate passed. The recheck passed 2,131 tests and skipped
eight. The other 47 successful job results were carried forward with
their original start and finish times.
- The prior head `42207172dd4bc376dab880dbccb2c698acccf6c4` passed all
49 CI jobs and fresh 5/5 reviews. Its first CI attempt lost one server
runner to a reported shutdown. One authorized retry of only that lost
shard and its normal gate passed with unchanged assertions and time
limits. The original failure and recovery remain recorded.
- A normal merge of that prior head was rejected by the base policy
because the global prompt required a code-owner review. This revision
drops that optional global change. No policy override or bypass was
used.
- The new catalogs have semantic catalog digest
`sha256:5af904b7fc088509b98dde41611c042ff3c22c881e252190817978de0d1b733a`.
The authorized `create_task` operation digest is
`sha256:f5dad10c7969680d91514603966705ac09ceaa3f2cff21fd6c199e2de0d7a63d`.
- A new run can report admitted prompt revision v8 and its digest. The
ordinary run-profile tool fingerprint does not hash these descriptions.
Source/Core and matching provider-pack identity prove deployed code, not
model delivery or compliance. A fresh campaign with new agents is still
required.
- No model run or deployed acceptance was performed for this source
change. The original failed qualification remains failed. This
tool-guidance change is a mitigation hypothesis.

## Risks

The longer tool descriptions change the catalog identity. A model can
still omit a requested field. The global prompt remains v8. Existing
assignment, authorization, wake, replay, and session behavior stays
intact. A new campaign is required because a stored provider session can
retain old tool declarations. A native-process retention test has a
recurring five-second timeout on unchanged code. One unchanged local
Node 24 control passed. This does not prove CI reliability or explain
the timeout. No timeout or assertion was changed.

## Model Used

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

## 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-02 20:46:07 -06:00
Nicky LeachandPaperclip 6d72243d8a fix(runtime): recover task creation after a lost acquire reply (#18692)
## Thinking Path

> - Paperclip manages agent execution and the resources that execution
owns.
> - A retained environment task can need a replacement before the next
turn.
> - Core records the creation attempt before it asks the provider to
acquire it.
> - A lost reply can leave that journal without the resource ID needed
for cleanup.
> - This change lets an opted-in provider recover the identity from that
recorded attempt.
> - Core still requires the existing exact-task termination proof before
it releases ownership.

## Linked Issues or Issue Description

Refs #18405 and #18658. This is the separate lost-reply follow-up from
their recovery audit.

**Draft status:** Both prior review findings are fixed in
`a66520c8d4e06e0eec6ccb5446190ace0ca3003f`. Fresh standard Greptile is
5/5 with no unresolved threads. Current-head CI is green (53 successful
checks/statuses including Snyk, three expected skips). Apex completed
5/5 on this exact head at 2026-11-03T00:54:59Z (request 6592407130),
with no unresolved threads. The review cycle is complete; the PR remains
draft for human merge review.

**What happened?**

If a replacement task's acquire reply is never received, the creation
journal can reach manual reconciliation without a resource ID. Startup
cleanup cannot complete, and ordinary Stop cannot release the run owner.

**Expected behavior**

When the selected provider supports creation inspection, Core should
retain enough binding information to inspect the recorded attempt and
continue verified cleanup. An inspected identity must not count as
termination proof.

**Steps to reproduce**

1. Use a retained task whose provider supports creation inspection.
2. Lose the replacement acquire reply before Core records its resource
ID.
3. Restart cleanup and inspect the existing creation journal.
4. Confirm that the exact task is stopped and its termination receipt is
saved before cancellation releases the owner.

**Paperclip version or commit**

Current head `a66520c8d4e06e0eec6ccb5446190ace0ca3003f`, based on master
`3a7bb4dac62dbd7a34a55deb665c55df310bdf64`. Tested with isolated
PostgreSQL fixtures.

**Deployment mode**

Local source tests with controlled provider responses. No deployment or
live model request.

## What Changed

- Save the selected inspection capability and prior opaque binding in
the existing creation journal. Acquire through that same worker so a
worker replacement cannot change the recorded contract.
- Make failed opted-in creations due through the existing cleanup
notification and saved retry deadline.
- Add optional SDK observation field `creationLease`. Use it only to
recover the requested creation's identity and binding, then follow the
existing Stop, status, completion and receipt checks.
- Keep old entries and providers without the recorded opt-in under their
existing reconciliation policy.
- Add actual PostgreSQL recovery, cancellation and worker-replacement
regressions. Document the provider contract.

## Verification

- Current correction: 53 distinct targeted cases pass (20 real-worker
IPC, 12 task-recovery PostgreSQL and 21 generic-sandbox cases), with
server typechecking. The IPC selection was rerun after the final
late-observation ordering refinement; the other tested sources were
unchanged.
- An assembled live-child IPC and PostgreSQL case proves cleanup and
cancellation release the owner despite the original worker never
replying and remaining alive. Accepted host work, unrelated acquisitions
and late observations remain accounted for. Removing the recovery
handoff and restoring the old drain snapshot produce distinct assertion
failures.
- The unsupported-host control skips all 12 PostgreSQL cases without
setup; removing the guard fails the controlled setup. These tests do not
constitute live VM/provider qualification.
- Initial head `5db6882` passed an exact reference-tree comparison
against master plus the reviewed follow-up patch, 97 targeted cases and
server typechecking. Those checks predate the final inspection
correction.
- Previous head `59f87fa` passed 32 targeted cases (11 task-recovery and
21 generic-sandbox cases) and server typechecking. Both `pending` and
`needs_reconciliation` observations without `creationLease` preserve
ownership and get a future retry deadline. Restoring the previous guard
makes both new cases fail.
- The reproduced failure was a wrong generic cleanup shape that could
resolve its journal while leaving the owner held—not a demonstrated
false ownership release. The correction reuses the existing
missing-identity guard before the generic decoder and leaves generic
sandbox behavior unchanged.
- Earlier affected selection: 72 cases passed on the original follow-up
base. This overlaps the final selection and is not an additional count
of unique final-head tests.
- Two isolated negative controls restore the missing task inspection and
redirected-worker behaviors. Both fail at the expected recovery and
dispatch assertions without changing production source files.
- The PostgreSQL cases verify one cleanup lease, both late-result
orders, recovery after the old journal deadline, ordinary legacy
behavior, retained ownership on missing or mismatched identity, and
cancellation acknowledgement after exact termination proof.
- The tests use actual database and run-control services with controlled
worker callbacks. They do not prove live IPC or production recovery
timing.
- A compatible provider is needed for the new recovery path. No normal
successful-startup database or network call is added. No ordinary TTFT
improvement is claimed; the repair removes an indefinite ownership hold
for supported lost replies. No full local CI suite was run; GitHub runs
the full regression gates.

## Risks

The provider must recover the identity for the recorded creation
operation and preserve its selected binding. `creationLease` does not
authorize a new turn or prove that a process stopped. Older journal
entries do not infer a new identity after an upgrade. Unavailable
original machine bindings or credentials retain the existing uncertainty
behavior; this change does not repair machine or credential loss. No
schema migration, deployment or ROM qualification is included.

## Model Used

OpenAI Codex, GPT-6 family. Exact model ID and context window are not
exposed in this session. Tool-assisted implementation, targeted tests
and independent subagent review.

## 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-02 17:45:07 -08:00
DottaandPaperclip 16643a6e03 fix(runner): recover pending Stop cleanup after controller restart (#18682)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The server owns execution, Stop delivery, and usage settlement.
> - A Stop can remain pending when the controller restarts.
> - Recovery must reach the existing session without starting more paid
work.
> - Startup and budget fences currently block that cleanup, and
interrupted runs leave the restart selection.
> - This pull request gives exact audited Stop cleanup a shared
authority check and records its real acknowledgement.
> - The benefit is safe cleanup while usage debt remains honest.

## Linked Issues or Issue Description

**What happened?**

A pending native run-only Stop can retain an unfinished execution owner
after controller loss. Its startup cancellation fence prevents recovery
admission. If teardown marks the run interrupted and clears its task
lock, restart selection also omits it. A recovered session can receive
cancellation without the durable acknowledgement that the normal Stop
request would save.

**Expected behavior**

Recover only the existing remote session for the audited pending Stop.
Keep fresh provider work blocked, including during budget pause. Keep
incomplete usage pending until a real complete receipt arrives.

**Steps to reproduce**

1. Start a native run with a held reservation and an authenticated
remote checkpoint.
2. Persist a run-only Stop and its startup and execution cancellation
fences before acknowledgement.
3. Lose the controller. Leave the coordinator unfinished, mark the run
interrupted, and clear its task lock.
4. Attempt restart recovery and observe the missing claim or
cancellation admission rejection.

Related work: #14733 binds caller Stop requests. #18658 handles plugin
startup cleanup obligations. #18035 proposes broader accounting changes.
This change repairs existing-session Stop recovery and preserves current
accounting guards.

## What Changed

- Add one shared check for the exact run, company, issue, pending Stop,
intent audit, live recovery lease, generation, and remote reattachment
disposition.
- Include eligible interrupted Stop owners in restart selection. Keep
generic interruptions and another task execution owner fenced.
- Reuse only the original held reservation for cleanup. Keep budget
pause effective for fresh, bootstrap, dead-runner, and unproven recovery
work.
- Apply the same cleanup authority at heartbeat runtime selection and
native entry. Skip attachment staging and warm-owner retirement only for
that exact cleanup. Revalidate at session publication and fail closed if
the proof changed.
- Send the existing Stop through the audited cancellation producer
before turn submission. Close the coordinator only after
acknowledgement, including system-owned run-only Stops.
- Give verified cleanup a transport purpose that retains pending
semantic inputs without replay, dispatch, or a semantic reply. Add an
explicit Core retain-only intake mode; preserve authenticated identity,
schema, digest, sequence and replay checks. Ordinary execution still
requires its dispatcher.
- Add PostgreSQL, native protocol, and startup-fence regressions. Update
the execution contract and recovery maps.

## Verification

- The combined Stop regression fails on unchanged production at the
cancellation reservation guard. Interrupted restart selection also fails
before the repair while its running sibling passes.
- An assembled `heartbeatService.recoverNativeRunsAfterRestart`
regression exposes the earlier runtime-selection fence with the rest of
the repair present. The fixed path reaches selector/claim, runtime
selection, original held-budget reuse during pause, dispatch, real
native protocol, audited cancellation, and heartbeat settlement. It
starts no provider turn, acquires no new sandbox, and does not reclaim
the task lock.
- Fail-if-called attachment staging and turn boundaries remain
untouched. A retained warm-owner regression prevents retirement.
Invalidation at session publication fails closed. Actual backend/driver
constructor tests verify cleanup-purpose forwarding, absent default
options, and denial under foreign ownership.
- Missing usage retains accounting debt and the hold. A later complete
receipt records one 25-cent charge and settles that same hold.
Concurrent duplicate cleanup is denied. Both user-owned and system-owned
Stops pass.
- Real transport tests: 9 pass on Node 24.22.1. They preserve ordinary
saved-tool and completion recovery, prove the authority-denial race
before cancellation, and qualify cleanup with pending
ordinary/completion inputs and either handler ordering.
- Real authenticated Core intake: 11 pass. Callback-present and
callback-absent retain-only cases persist and acknowledge unanswered
input without dispatch, recovery or result. Default missing-dispatcher
rejection and digest-integrity/suspension guards remain effective.
Omitting the callback alone first failed by disconnecting; the explicit
retain-only mode fixes that defect.
- Repository `pnpm -r typecheck`, the server/runner-vendor build, map
checks, and module boundary checks passed locally before integrating the
latest retained-startup change. That integration passes 11 native
startup/cleanup compatibility cases and 3 assembled recovery/accounting
cases. The final broad affected-suite attempt was stopped without a
verdict; it is incomplete. Full repository tests and build are delegated
to current-head GitHub CI.
- Review exposed an additional real-process close failure with
unanswered input. The corrected path requires a fresh exact
provider-stop receipt confirming exit, followed by durable event drain
and exact runner suspension. All 11 focused transport cases pass,
including missing/false exit-proof negatives and ordinary recovery
siblings. Runner TypeScript and map validation pass after this
correction.
- The assembled test controls provider, workspace-transfer and GitHub
bootstrap I/O. It does not qualify hosted credentials, physical provider
termination, workspace preservation, or historical receipt recovery.
Local socket-permission setup failures are separate from semantic
regression results. No hosted replay has run on this repair.

- CI exposed an ordinary factory-call compatibility regression and an
obsolete authority-read test fixture. The repair preserves
single-argument ordinary calls and updates the race fixture without
changing its Stop assertions. The complete native executor suite now
passes all 1,049 tests; the ordinary factory boundary and all five
Stop-accounting cases pass. A separate local environment-default case
twice exceeded its five-second wait while other work ran; its prior-head
CI case passed. The same unchanged case passed an isolated diagnostic
without changing its assertion or timeout; the precise contention source
remains unproven.

- The final fixture edit initially used the wrong overloaded promise
type. CI rejected it; the annotation-only correction passes the exact
`pnpm run typecheck:build-gaps` entry point locally, including server
service tests and remaining workspaces. No runtime assertion changed.

- Final exact head `007fd4d04a2d6338c6dc073e97ab470a277fc3d0`: required
`ci / verify` and `ci / e2e`, build, typecheck and test groups pass.
Greptile is 5/5 with both findings resolved. Three expected checks are
skipped; the optional native check is neutral, while its final
current-head comment gives 5/5 from source inspection without a checkout
or test execution. The first server-shard attempt lost its self-hosted
runner connection; one scoped rerun passed. Its original annotation and
unavailable-log evidence are retained.

## Risks

- This changes execution authority. Cleanup requires an audited, exactly
bound pending Stop, an unfinished coordinator, a live remote
reattachment claim, and the original held reservation. It never
authorizes a replacement provider turn.
- Missing checkpoint, original held reservation, termination, or usage
evidence can keep cleanup or accounting pending. The repair does not
manufacture acknowledgement, replies, receipts, or zero cost.
- Retain-only intake is an internal trusted constructor option. It
changes no agent tool permission. Ordinary callback requirements and
semantic dispatch remain unchanged.
- No schema, public API, provider profile, or deployment setting changes
are required.

## Model Used

OpenAI Codex, `gpt-6.1-sol`, Extra High reasoning, with source
inspection, code execution, and regression testing. The 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 described the issue in-PR following the bug report template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal
ticket ID
- [x] I have run affected 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 risks
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open findings
- [x] I will address all reviewer comments before merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-02 18:22:40 -06:00
sentry[bot] 18abf7c905 fix(db): tolerate unparseable connection URLs during teardown (#18684)
closeRegisteredClients now uses hostPortKeyOrNull and returns early for URLs that new URL() rejects, matching createDb, which never registers clients under such URLs.

Fixes PAPERCLIP-BACKEND-5C
Fixes #18685
2026-11-02 15:33:36 -08:00
Nicky Leach 3a7bb4dac6 perf(runner): activate shared retained startup context (#18405) 2026-11-02 15:12:57 -08:00
DottaandPaperclip 58308a3e63 perf(apps): scope sidebar review counts to one connection (#18688)
## Thinking Path

> - Paperclip helps people manage AI agents and their governed app
access.
> - The Apps sidebar displays a Review badge for one connection.
> - It polls the full company attention service every 30 seconds to get
two counts.
> - That service also loads every profile, rule and active catalog
entry.
> - This pull request adds an authorized connection read for only those
counts.
> - The same badge now avoids unrelated profile evaluation and database
result decoding.

## Linked Issues or Issue Description

**Current behavior**

The contextual sidebar polls company-wide attention. It needs only one
connection's signed pending action count and whether any catalog entries
are quarantined.

**Proposed behavior**

Both sidebar variants poll a connection-scoped count endpoint with
existing connection read authority. The full attention API stays
unchanged.

**Reason and benefit**

The retained 200-profile fixture performs seven service queries and
returns 80,115 rows for this badge. The new reader performs one query
and returns one row. This reduces local service CPU and avoids
unnecessary decoded profile/rule data. Related merged evaluator fixes
are already in the baseline: Refs #18673, Refs #18681. A search found no
existing PR for this scoped count reader. ROADMAP.md contains no
duplicate planned work.

## What Changed

- Add the narrow shared DTO, typed client, guarded REST/OpenAPI route
and catalog-owned reader.
- Count independent aggregates with both company boundaries and exact
signed-argument semantics. Keep archived counts at zero.
- Update both contextual sidebar variants. Keep the exact badge, 30
second interval and existing attention-prefix invalidation.
- Add PostgreSQL differential tests, authenticated HTTP tests, sidebar
tests, a repeatable benchmark and access documentation.

## Verification

- Baseline: `3f8ebfff8aa9febd0733c6f65b07485dcadfd0e2`, including both
merged evaluator fixes. Service treatment: `0827f7d0b3`; route/fixture
refinement: `230c42ee15`; final local existence gate: `390a218254`.
- Passed: focused PostgreSQL differential test; assembled HTTP test with
board session and agent JWT; 14 sidebar component tests; token gates;
map checks; diff check.
- Fixture: 200 profiles, 76,000 rules, 1,900 active tools, 695 removed
references, 12 agents, a sibling connection and 2,000 unrelated
historical invocations. Complete normalized two-count results are equal
on both paths.
- Quiet local measurement: seven alternating warm pairs after two warmup
pairs, Node 24.22.2, macOS, real disposable PostgreSQL. Legacy/new
service CPU median: 213.891/1.680 ms; ranges:
209.329–228.552/0.515–6.137 ms. Wall median: 156.867/1.248 ms; ranges:
151.739–162.289/1.115–1.386 ms. Queries: 7/1. Returned rows: 80,115/1.
- Run `cd server && node --expose-gc --import
./node_modules/tsx/dist/loader.mjs
scripts/benchmark-connection-review-counts.ts`. See
`doc/connection-review-counts.md` for scope and failure history.
Byte-volume accounting runs outside timing; no HTTP payload reduction is
claimed.
- Initial raw scalar SQL failed with ambiguous columns. Typed subqueries
fixed it and passed real PostgreSQL equality. Initial fixture agents
lacked permissions and correctly received 403. The final fixture grants
explicit authority and tests revocation. An initial benchmark included
byte serialization in timing; the reported run excludes it.
- Passed required local `pnpm -r typecheck` and `pnpm build`. The
initial sandbox typecheck could not open the tsx IPC socket; the
escalated rerun passed. Full monolithic `pnpm test:run` was not run
locally: repeating a 15–25 minute suite serially across both performance
PRs adds no distinct evidence. Broad suite acceptance uses final-head
CI, which runs the same general/serialized test partitions and the
native Runner lane. Final-head CI is in progress.
- Passed built-static Chromium journey on an owned local-trusted server
and disposable PostgreSQL: Review 3 appears, only scoped counts are
requested by this sidebar, resolving requests/clearing quarantines then
reloading removes the badge, and archived counts are zero. The first ad
hoc launcher omitted required decision-service options and failed at
startup; using the normal no-op wake callback fixed that harness.
- Greptile identified an invalid resolved-request fixture label.
Corrected `denied` to the declared `rejected`, then reran the real
PostgreSQL differential test. The count SQL and non-pending exclusion
are unchanged.
- The first final-head CI failed the static cross-tenant
existence-oracle guard because the new explicit resource company
assertion had no nearby `hasCompanyAccess` gate. Existing management
admission already rejects foreign resources with 404. The route now also
follows the standard local two-step gate. The unchanged guard plus
authenticated HTTP negative cases passed (3 tests). No allowlist or test
was weakened.
- These local service measurements do not prove hosted performance. No
deployment is included.

## Risks

Low risk. New read admission reuses existing current-authority checks.
The old attention API is unchanged. Counts still require database work
over relevant pending/catalog rows. No cache, schema migration or
telemetry is added. Roll back this PR if the badge differs or scoped
reads deny previously permitted users.

## Model Used

OpenAI `gpt-6.1-sol` (Sol 6.1), with reasoning, tool use and code
execution. The host does not expose the configured context-window size.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-02 17:12:22 -06:00
DottaandPaperclip 554a770c6f perf(apps): scope connection profile configuration reads (#18687)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Apps Permissions page reads saved access profiles for one
connection.
> - The page fetched every company profile, rule, and binding.
> - It used only the app profile and eligible task-granted profiles for
that connection.
> - This pull request adds a saved configuration read owned by the
profile service.
> - The route keeps current connection and per-profile management
checks.
> - The benefit is fewer database rows, less Node work, and a smaller
response for the same displayed configuration.

## Linked Issues or Issue Description

**Subsystem affected**

Server profile configuration reads and the Apps Permissions and Review
data caller.

**Problem or motivation**

One connection page loaded and serialized unrelated company profiles. In
the local fixture, this produced a 32.35 MB response with 76,001 rule
rows.

**Proposed solution**

Select the displayed connection's app profile and exact eligible
connection-intent profiles before loading their entries and bindings.
Return an explicit configuration DTO with complete selected arrays. Keep
summary and pending-review work on the existing full API.

**Alternatives considered**

Filtering the full response in the browser retains the database and Node
cost. Truncating the full API would break other clients. Filtering
entries to the connection would lose selectors and bindings needed by
saves.

**Roadmap alignment**

This improves the existing governed Apps capability. It does not add a
new access policy. Related merged work: #18673 and #18681. Related
harness cleanup: #18683. Public PR searches found no duplicate
connection configuration projection.

## What Changed

- Add `ToolProfileConfiguration` and `GET
/api/tool-connections/{connectionId}/profile-configuration`.
- Select `app:<connectionId>` at any status and exact active, correctly
bound `connection_intent` profiles. Run the existing per-profile
visibility filter before configuration queries.
- Preserve complete ordered entries and bindings. Keep all save
behavior, empty-binding install fallback, and the existing full profile
API.
- Switch AppDetail and AgentConnectionAccess to the configuration DTO.
Use a distinct cache key below the existing profiles prefix so mutation
and reconnect invalidations still refresh it.
- Add real PostgreSQL differential and authenticated HTTP tests, UI
caller coverage, a reproducible benchmark, and access-map documentation.

## Verification

Baseline: `3f8ebfff8aa9febd0733c6f65b07485dcadfd0e2`, after both #18673
and #18681. Final head: `b40c1ed7c0833d403e20ee8a5ff9d09f1bad6d22`.
Current implementation and reproduction details are in [the performance
note](https://github.com/paperclipai/paperclip/blob/b40c1ed7c0833d403e20ee8a5ff9d09f1bad6d22/doc/performance/2026-11-02-connection-profile-configuration.md).

- AppDetail: 79 tests passed. Existing access/action saves, install
fallback, grant removal, errors, and refresh cases use the new caller.
- Real PostgreSQL differential: passed. Full ordered selected
configuration equals the old full-list selection. Cases cover extra
bindings, sibling selectors, wrong source/key/status/binding, null and
non-string agent metadata, fresh mutations, empty app bindings,
visibility filtering, and company scope.
- Production HTTP authentication: API key and run JWT cases passed.
Hidden personal accounts omit spanning profiles. Private connection
reads fail. Restored ownership restores visibility. Revoked management
grants deny fresh calls. Final UID canonicalization and foreign-company
404 assertions passed on the current head.
- Map structural check, token gate, and `git diff --check`: passed.
- Required local `pnpm -r typecheck` and `pnpm build`: passed on the
current head. `pnpm test:run` was started once. Its first chat shard
passed (337 tests, 151.87 seconds); the second shard was intentionally
interrupted after the complete final-head CI suite passed. This partial
local attempt is not a full local test pass. The same stable-runner
general and serialized partitions and Runner lanes passed in [final-head
CI](https://github.com/paperclipai/paperclip/actions/runs/41538805231),
including aggregate `ci / verify`. No local assertion failures were
observed before interruption.
- Final-head focused acceptance: four files, 85 tests passed. Full token
gates, maps, and module boundaries passed.
- Actual browser acceptance passed on the current checkout with isolated
real PostgreSQL and a local trusted board. An action saved as Off
persisted after reload. Both app agent bindings stayed intact. Removing
an agent-only task grant persisted after reload. Requests used only the
narrow profile endpoint. No browser exceptions occurred. No responses
were mocked. No provider calls or hosted changes were made. The helper
stopped the owned runtime and database after the check.
- Final exact-head checks: 54 success, one clean neutral Paperclip
Review, three skipped, zero pending or failures. Legacy Snyk status:
success. No inline review comments.
- Greptile reviewed exact head
`b40c1ed7c0833d403e20ee8a5ff9d09f1bad6d22`: 5/5, no findings, completed
successful check.

Five alternating unprofiled pairs ran with real PostgreSQL on
macOS/arm64, Node v24.22.2, and forced GC. The fixture has 210 profiles
(200 heavy profiles plus ten eligibility cases), 76,001 rules, 1,900
active tools, 695 removed tools, and 12 agents. The selected
configuration has two profiles and 1,391 rules. Each pair asserts
complete ordered selected DTO equality.

| Measurement | Full list | Configuration read |
| --- | --- | --- |
| Service wall ms, median [min, max] | 200.927 [193.319, 213.390] |
6.414 [5.508, 6.973] |
| Node CPU ms, median [min, max] | 262.751 [246.727, 271.573] | 27.525
[22.576, 29.308] |
| Select queries / returned rows | 7 / 78,139 | 3 / 1,397 |
| Actual response JSON bytes | 32,353,534 | 592,233 |
| Serialization wall ms, median [min, max] | 90.714 [88.715, 99.304] |
1.644 [1.637, 1.745] |
| Serialization CPU ms, median [min, max] | 90.644 [88.569, 96.778] |
1.654 [1.637, 1.807] |

Reproduce: `node --expose-gc --import
./server/node_modules/tsx/dist/loader.mjs
server/scripts/benchmark-connection-profile-configuration.ts 5`.

The benchmark measures the service and actual response JSON separately.
It excludes HTTP and authorization work through an identity visibility
callback. Node CPU does not include PostgreSQL CPU. No hosted or
foreground-under-load improvement is claimed.

Preserved failures: offline install lacked a locked tarball; frozen
online install passed. Sandbox localhost and tsx IPC restrictions
blocked initial DB/typecheck attempts; permitted retries use the same
checks. The first HTTP suite needed the unbuilt plugin SDK; its owning
build helper fixed setup. Shared typecheck caught a missing barrel
export, now fixed. A broad typecheck inherited the host's unpinned
nested pnpm executable; the owned stalled process was stopped and
restarted with pinned pnpm 9. The complete current-head CI suite and
aggregate verify passed. The separate Paperclip agent review published
5/5 with no findings at this head and completed with a clean neutral
conclusion. An added foreign-company assertion expected 403; the
existing guard correctly returned 404. Only the test expectation
changed. The first real browser attempt saved and reloaded the action
correctly, but grant deletion returned 422: the stress fixture had a
company binding, which makes that profile the company default. The UI
showed the existing deletion error. The second browser seed used the
production agent-only intent shape and passed removal and reload. This
does not change the stress fixture or product deletion gates. Initial
external browser script attempts also needed an ESM extension and the
correct dependency path. Logs, first-failure screenshots, traces, and
result files are retained in the local evidence directory.

## Risks

- SQL selection must stay aligned with connection-intent eligibility.
Differential fixtures cover the exact current predicate.
- A selected connection can still have many rules. This change preserves
all selected configuration.
- Per-profile authorization still reads current selected resources and
can add work. Connection visibility does not bypass that check.
- No migration, default-allow, effective-access, policy-review,
provider, or deployment behavior changes. The old endpoint remains
available. Rollback restores the old AppDetail caller and removes the
additive endpoint.

## Model Used

OpenAI Codex, `gpt-6.1-sol`, with code execution and repository tools.
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-02 16:55:56 -06:00
Huanyi Xie 3b24911c16 fix(hermes-gateway): preserve complete whitespace-sensitive delta frames (#15201)
## Thinking Path

> - Paperclip shows agent output in a live transcript.
> - Hermes Gateway emits structured events and raw message text to
stdout.
> - A raw chunk without a newline prefixes the next structured event.
> - The anchored transcript parser then loses later message deltas.
> - This pull request emits each delta once as a complete JSON event.
> - The transcript can read all streamed events again.

## Linked Issues or Issue Description

Fixes: #15132


Related PR #9909 removes the duplicate raw-delta log write. This PR also
fixes two separate fidelity defects that its diff leaves:
whitespace-only/leading/trailing delta text is trimmed by `nonEmpty`,
and large delta payloads are shortened by the event-log summarizer. The
final implementation preserves the exact delta in a JSON frame and the
assembled output, with whitespace, 9,000-character, and forged-prefix
regressions. The overlapping raw-log removal is required so the safe
frame is the only transcript copy.

## What Changed

- Remove the redundant raw stdout write; each delta is logged once as a
JSON event.
- Preserve whitespace and existing secret redaction; keep complete
message deltas beyond the diagnostic truncation limit.
- Exercise the persisted stdout through the real transcript parser, with
consecutive chunks, embedded newlines, a forged event prefix, and a
9,000-character delta.

## Verification

- The new regression finds only the first delta on the original code.
- All 87 Hermes adapter tests pass after the fix.
- Hermes adapter typecheck and build pass.
- git diff --check passes.
- All remote full-repository CI gates pass for the final head.

## Risks

Low risk. Message-delta logs contain one canonical redacted delta
payload. Other events keep their existing diagnostic limits. The
regression proves there are no duplicate stdout entries or fabricated
events.

## Model Used

OpenAI Codex, based on GPT-6, assisted with source review,
implementation and local test execution. The exact deployment model ID
and context-window size are not exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass (targeted local checks and
full remote CI pass)
- [x] I have added or updated tests where applicable (Hermes regression
/ real Compose interpolation checks)
- [x] I have updated relevant documentation to reflect my changes
(Hermes has no configuration changes; Compose documentation is updated)
- [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-02 15:07:44 -06:00
Devin FoleyandPaperclip 49b3804830 fix(connections): load permissions and show AI account identity (#18653)
## Thinking Path

> - Paperclip manages AI agents and their access to saved connections.
> - The Permissions page loads account details and permitted agent
choices.
> - Company-wide AI accounts repeat retained installation checks for
every candidate agent.
> - This makes target checks grow with the square of the roster size.
> - The browser waits without a deadline, and connection names do not
identify the provider login.
> - This pull request batches direct AI checks and bounds loading with
recovery controls.
> - Account details load separately and show a provider email or an
explicit profile-permission limit.

## Linked Issues or Issue Description

**What happened?**

Opening a saved AI account can leave Permissions on “Loading AI
account…” for minutes. The account itself can be connected. The existing
capability batch excludes AI connections, so a company-wide install
repeats checks for every installed agent for every candidate. Saved
names and Paperclip ownership also do not establish which provider login
a credential uses.

**Expected behavior**

Direct AI accounts reuse retained-target checks within one descriptive
request. The page shows loaded account controls or a bounded failure
with Retry. Permissions shows the provider email and organization when
the selected credential permits profile access. An unavailable profile
must not block maintenance or disconnect a working inference credential.
Writes still authorize current permissions.

**Steps to reproduce**

1. Save an AI account in a company with several agents.
2. Install the account company-wide.
3. Open its Permissions page.
4. Delay the account-list or grants response to exercise the loading
failure.

The maintained benchmark reproduces database work with synthetic local
data. Refs #17945, which added batching for non-AI connections. Searches
found no open PR for this AI-account loading fix.

## What Changed

- Include direct AI accounts in the existing board capability batch.
Referenced pool members, delegated actors, complex selectors, and
mutation authorization keep their existing evaluator.
- Add opt-in per-caller GET deadlines to the API client. A timed-out
caller detaches the stuck coalesced request so Retry starts a fresh
fetch. Other callers retain their own cancellation and results.
- Apply a 15-second deadline to AI account lists and grants reads. The
permissions view retries transient failures once and reports other
failures immediately.
- Share the parent Permissions grants query with AI account details.
Keep loaded controls visible after a background error and offer Retry.
- Add a grant-scoped identity GET and OpenAPI contract. The service
checks current membership and credential audience before it reads the
selected credential. It projects only the provider email and
organization; provider IDs, tokens, and raw errors stay private.
- Show account details independently of maintenance controls. Anthropic
profile denial has an explicit message and preserves connected status.
Transient profile failures have a separate Retry action. Observations
are cached for five minutes under the company, connection, grant, and
grant revision.
- Extend real PostgreSQL and authenticated HTTP coverage,
client/component regressions, map guidance, and the existing benchmark's
AI mode.
- Address the Apex file-size finding by moving gallery identity lookup
into the existing app display helper module. `AppDetail.tsx` is 984
lines; lookup behavior is unchanged.

## Verification

Local source checkout on macOS, Node 25.6.1, pnpm 9.15.4, and disposable
PostgreSQL 18.1. Provider calls use fixtures. No hosted deployment or
provider qualification is claimed.

- Passed `pnpm -r typecheck` and `pnpm build`.
- Passed all 87 tests in `connection-management.integration.test.ts` and
all 33 tests in `connection-install-capabilities.test.ts`.
- Passed 196 AI connection, identity-provider, and OpenAPI tests:
`ai-connections.test.ts`, `ai-account-identity.test.ts`, and
`openapi-routes.test.ts`.
- Passed 107 focused UI/client tests: `client.test.ts`,
`ManagedAiConnectionDetails.test.tsx`,
`AiConnectionAccountDetails.test.tsx`, and `AppDetail.test.tsx`.
- After the Apex extraction, passed UI typecheck/build and 106 component
tests across `AppDetail.test.tsx`, `AppNotConnected.test.tsx`,
`ManagedAiConnectionDetails.test.tsx`, and
`AiConnectionAccountDetails.test.tsx`.
- The authenticated HTTP tests compare complete grants responses across
human roles, for tool and AI connections. They verify a real install
mutation succeeds before revocation and fails afterward.
- A company-wide personal AI fixture checks 20 targets instead of 400.
Pool-member permission loss still denies the complete pool description.
- UI states covered: shared cached grants, stuck account/grants fetches,
one automatic retry, terminal failure, manual recovery, authorization
failure, background failure with retained controls, and unmount
cancellation. Client tests also cover stalled response bodies and
independent coalesced callers.
- Identity coverage includes actual bearer-key/JWT reads, private-user
and cross-company denial before provider access, revoked credentials,
provider 401/403/429/500, and grant-change isolation. A real local HTTP
fixture stalls the response body, verifies the shared 15-second
deadline, and keeps unrelated HTTP requests responsive. The test
shortens the native deadline to 100 ms. Profile permission denial leaves
the saved account connected.
- Passed `pnpm check:token-gates`, `node scripts/check-maps.mjs`, `node
scripts/check-module-boundaries.mjs`, and `git diff --check`.
- The full local `pnpm test:run` started with the loading-only change.
All six chat shards passed. It recorded one failure in the unchanged
`heartbeat-retry-scheduling.test.ts` quota-rejection case. That exact
case passed a focused rerun on the final head. The initial failure cause
remains unconfirmed and its local log is retained; this is not a full
local-suite pass. The redundant local run was stopped with SIGINT after
final-head CI completed successfully.
- [Final-head
CI](https://github.com/paperclipai/paperclip/actions/runs/41515556725)
passed on `ead8d276d4a2023614fff54b3e9a858f28c3178b`: typecheck, build,
all 12 server shards, all 3 chat shards, workspace suites, all 9
serialized suites, all 8 end-to-end shards, Runner checks, and the
aggregate verify gate. Security checks passed. Optional Storybook checks
were skipped by workflow policy. GitHub reports the branch mergeable
without conflicts.
- Apex reviewed head `ead8d276d4a2023614fff54b3e9a858f28c3178b` at 5/5,
confirmed the extraction resolves its earlier file-size finding, and has
no unresolved review threads.

The existing benchmark ran for the loading-only change now recorded in
commit `5f725b36fa52002545241a5a8b7a10602f0cc0ce`, with `--purpose=ai
--baseline-ref=026fd9e3452`. The identity follow-up leaves that batch
source unchanged. Two warmups and five alternating samples exercise each
of eight fixture shapes. Complete positional capabilities, reasons, and
operations match the baseline in every sample.

| AI fixture | SQL calls before → after | Target reads before → after |
| --- | ---: | ---: |
| 1 agent, company install | 9 → 9 | 1 → 1 |
| 32 agents, company install | 5,248 → 164 | 1,024 → 32 |
| 100 agents, company install | 50,400 → 504 | 10,000 → 100 |
| 100 agents, partial install | 13,175 → 503 | 2,575 → 100 |

These counts measure the real service over local PostgreSQL. They do not
predict hosted page latency. To reproduce the standalone benchmark, use
a checkout of commit `5f725b36fa52002545241a5a8b7a10602f0cc0ce` and run
the command below. Its isolation check intentionally rejects the later
identity follow-up's additional runtime changes.

```sh
node --import ./server/node_modules/tsx/dist/esm/index.mjs scripts/benchmark-connection-install-capabilities.ts --purpose=ai --baseline-ref=026fd9e3452
```

Run the final-head regression suites from the repository root:

```sh
cd server
pnpm exec vitest run src/__tests__/connection-management.integration.test.ts src/__tests__/connection-install-capabilities.test.ts src/__tests__/ai-connections.test.ts src/services/ai-account-identity.test.ts src/__tests__/openapi-routes.test.ts
cd ../ui
pnpm exec vitest run src/api/client.test.ts src/components/ai-connections/ManagedAiConnectionDetails.test.tsx src/components/ai-connections/AiConnectionAccountDetails.test.tsx src/pages/apps/AppDetail.test.tsx
```

## Risks

Descriptive permissions reuse retained facts within one request. A
concurrent change can leave a displayed choice stale; the next request
and every write still authorize fresh. Pools with referenced members
retain their existing cost.

The account and grants clients now bound each caller's wait. A browser
abort does not cancel an authorization read already admitted by the
server. The independent profile request uses a fixed Anthropic origin, a
bounded body, no redirects, and a 15-second deadline. Inference-only
credentials can return no email; that is reported without guessing from
a Paperclip owner or changing connection health. Profiles are fetched
only for the caller's authorized selected grant. No ambient CLI
identity, persistent identity cache, background polling, or
billing-identity change is added. Other API reads keep their existing
behavior. No migration is required. Staging has not been redeployed.

## Model Used

OpenAI `gpt-6.1-sol` through the Codex harness in T3 Code, with xhigh
reasoning, code editing, execution, and test tools. 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 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-02 12:29:03 -08:00
DottaandPaperclip 33fc7fcddc fix(db): clean up owned PostgreSQL test servers (#18627)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Database, server, and CLI tests use real PostgreSQL fixtures.
> - Each fixture must own its server until PostgreSQL has stopped.
> - The old teardown returned after five seconds even when its server
remained alive.
> - A startup failure could then mark the host unsupported and skip
database coverage.
> - This pull request waits for owned shutdown and makes supported-host
startup failures fail collection.
> - A shared-server command reduces process creation while keeping each
fixture's database isolated.

## Linked Issues or Issue Description

Refs: #7756 for related development-process ownership. That
development-server issue remains outside this test-fixture fix.
Related PR: #16799 collects macOS startup diagnostics. This PR changes
test shutdown and failure behavior.

**What happened?**

Repeated test runs can exhaust macOS PostgreSQL semaphore resources. The
fixture abandoned slow shutdown and swallowed stop errors. The support
probe converted ordinary startup failures into skipped integration
coverage.

**Expected behavior**

Tests must stop their PostgreSQL servers, retain files if stop fails,
and report resource exhaustion as a failure. Fixtures on a shared server
must not close another database's clients.

**Steps to reproduce**

Start a fixture whose PostgreSQL stop exceeds five seconds, or make a
supported-host startup fail with semaphore exhaustion. The former helper
returned from teardown or reported unsupported coverage. The new
regression tests exercise both paths without exhausting host resources.

**Paperclip version or commit**

Source checkout based on current master.

**Deployment mode**

Local source development and CI tests.

## What Changed

- Add explicit ownership for test PostgreSQL children. Wait for fast
shutdown, then request immediate PostgreSQL shutdown if needed. Keep
cleanup idempotent and fail when exit cannot be confirmed.
- Publish reusable templates only after clean shutdown. Do not publish a
crashed or immediately stopped cluster.
- Stop owned children on POSIX worker signals and process exit. Preserve
test directories when shutdown fails. Remove conflicting dependency exit
hooks.
- Retry port collisions only. Fail collection for resource exhaustion,
unavailable configured servers, and other startup failures on supported
platforms. Preserve skips for explicitly unsupported platforms.
- Move administrative-server support into the common fixture used by
database, server, and CLI tests. Close clients at database scope and
migrate cloned templates before use.
- Add a directly invoked shared-server runner to own one test server and
run suites against separate disposable databases. Preserve child exit
status and support an existing disposable server.
- Unify cancellation: stop and wait for the test process group before
PostgreSQL teardown. Add nested-process coverage, real worker-exit
coverage, shutdown regressions, shared-database isolation checks, and
setup documentation. Replace ignored heartbeat completion polls with the
existing execution drain before asserting persisted outcomes.
- Finish owned cluster-directory cleanup after signal-driven
cancellation. Preserve prepared templates across shared-fixture tests.
Remove stopped directories asynchronously, using native removal on POSIX
with a Node fallback.
- Align database and CLI fixture hook budgets with the existing
30-second server budget. This lets the ten-second shutdown protocol
finish and report errors. The idle-sleep suite uses the existing
90-second fixture budget for its unusually large final directory
cleanup.

## Verification

- The semaphore-exhaustion regression failed against the old helper
because it resolved to `supported: false` instead of rejecting.
- All 37 focused tests pass on the final runtime code (`e4de00ab40`).
They include real worker exit, SIGTERM, nested-process SIGINT/SIGTERM
cancellation with PostgreSQL available during child cleanup, failed
template shutdown, unsupported platforms, stale SQL templates, and
directory removal. A child exiting with status 7 makes the shared-server
runner return status 7 after cleanup.
- Full local `pnpm -r typecheck` and `pnpm build` pass on the final
runtime code (`e4de00ab40`). Subsequent changes only adjust server-test
lifecycle waits, the idle-sleep cleanup budget, and documentation; the
server typecheck also passes with those final tests.
- A fresh shared-server run on `90b65fd8ad` passed all 68 database files
and 297 tests. Earlier preparation runs had two ten-second teardown
timeouts and a stale-template failure; the final helper migrates cloned
templates and permits the full shutdown budget. These earlier failures
remain accounted for here.
- The local full repository attempt stopped on a Discord receiver-lease
test after 338 passing chat tests. That exact case passed in isolation.
This full-suite attempt is not a pass.
- On `90b65fd8ad`, [server shard
6](https://github.com/paperclipai/paperclip/actions/runs/41496495253/job/125452949369)
and its one unchanged retry reported two 15-second cached-input
heartbeat timeouts and a 30-second idle-sleep teardown timeout. All ten
cached-input cases then passed locally with their original deadlines. A
local parallel run of the full files reported eight behavioral/hook
failures and two suite teardown timeouts (281 tests passed).
- Serial idle-sleep runs passed all 225 test assertions but overran the
30-second final hook. Tracing measured client closure in 4 ms,
PostgreSQL exit after 5 seconds, and synchronous directory removal
taking another 35 seconds. Asynchronous Node removal and native removal
also exceeded that hook under the full workload. Cleanup now remains
asynchronous and awaited, and only this heavy suite's final hook uses
the existing 90-second fixture budget. No PostgreSQL shutdown deadline
or test assertion was weakened. The serial run then passed the complete
idle-sleep file, including all 225 tests and teardown. Its heartbeat
file passed 63/64 cases, exposing an ignored three-second completion
poll. After replacing that poll with an execution drain, another timer
case exposed the same pattern (63/64 passed). All remaining ignored
completion polls now await execution drain without changing test
deadlines. The complete heartbeat file then passed all 64 tests. The
intentional asserted lock-contention poll is unchanged. The final server
typecheck passes, and a process audit found no PostgreSQL server left by
the completed local runs.
- On final head `2cf316d3f571e4516890cba743ecffeb9703cce3`, the
previously failing server shard 6, database lane, full typecheck, and
build passed. The [first CI
attempt](https://github.com/paperclipai/paperclip/actions/runs/41506358136)
then lost self-hosted runners in the canary and serialized-server lanes,
with additional jobs cancelled. The server-2 lane had one 30-second
timeout in the unchanged workspace-recovery HTTP Retry test (2,282 other
tests passed). That exact test passed locally with its original deadline
(28.78 seconds including imports). One unchanged retry of only failed
and cancelled jobs completed successfully on the exact final commit
(workflow attempt 2). The workspace-recovery timeout case and every
interrupted lane passed; successful first-attempt jobs were retained.
The additional Paperclip Review check remains queued, waiting for its
assigned agent; it has no unresolved GitHub review threads. Greptile
completed review on this exact final head at 5/5, with zero unresolved
review threads. All four original review findings and their threads are
resolved; additional Windows shutdown, cancellation-directory, and
template-reuse findings are fixed.
- `pnpm check:module-boundaries` and `git diff --check` passed. Post-run
process checks found no PostgreSQL servers left by the completed
verification runs.

## Risks

- Immediate PostgreSQL shutdown can require crash recovery. This
fallback applies to disposable test clusters and runs only after fast
shutdown times out.
- No process can handle `SIGKILL` or machine power loss. Host resource
cleanup remains a separate backstop.
- Directory cleanup on cancellation applies to the shared-server
runner's own cluster. Directly terminated test workers stop their
PostgreSQL process but retain disposable files for inspection. The
process-exit guard cannot await file removal.
- The two-second `pg_ctl -W` deadline bounds signal dispatch. It is
separate from the five-second and thirty-second server-exit waits. A
control-command failure fails cleanup and retains its directory; it does
not report successful cleanup or force-delete live files.
- Shared fixtures require a disposable PostgreSQL server and a role that
can create databases. The directly invoked shared command is opt-in; the
existing `pnpm test` command remains available.
- These changes do not stop unrelated development servers or delete
host-wide IPC resources. Clean shutdown uses the owned `pg_ctl` binary
on every supported platform. Its real control path was tested on macOS.
A Windows host was not available for live verification; POSIX signal
tests skip on Windows.

## Model Used

- OpenAI Codex, based on GPT-6. The runtime does not expose the exact
serving variant or context-window size. The agent used reasoning, shell
execution, code editing, and GitHub tools.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-02 14:24:48 -06:00
Chris KaubischandClaude Opus 5.5 b0f5451fdf fix(claude-local): skip the 2.1.291 copy hint in the setup-token prompt parser (#15550)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - AI connections let a user connect a Claude subscription for all
agents of an organization
> - In a Docker deployment the server runs `claude setup-token` in a PTY
and parses its output for the sign-in URL
(`server/src/services/local-ai-browser-login.ts`,
`packages/adapters/claude-local/src/server/setup-token-parse.ts`)
> - The Docker image installs `@anthropic-ai/claude-code@latest`, and
`claude` 2.1.291 adds a copy hint line between the URL and the code
prompt
> - The parser requires the prompt on the first non-blank line after the
URL, so it returns null and the UI waits without an error
> - This pull request lets the parser skip that exact hint line
> - The benefit is that Claude subscription sign-in works again with the
current CLI

## Linked Issues or Issue Description

No issue exists. I searched open and closed issues and pull requests for
"Preparing browser sign-in" and the setup-token parser.

**What happened?**
In the "Claude - Subscription" connection dialog, the UI shows
"Preparing browser sign-in…" and "Start sign-in again". It shows no
sign-in link and no error. "Start sign-in again" starts the same wait
again. After 300 s the run fails with "Sign-in ended before the account
was connected".

**Expected behavior**
The dialog shows the sign-in link and the code field a few seconds after
the start.

**Steps to reproduce**
1. Run the Docker image with `claude` 2.1.291 (`@latest` at the time of
writing).
2. Open the AI connection dialog and select "Claude - Subscription".
3. The dialog stays on "Preparing browser sign-in…".

**Paperclip version or commit**
`master` at 03cf6a6ec (includes #14970). `claude --version`: 2.1.291.

**Deployment mode**
Authenticated, private exposure, Docker.

**Relevant logs or output**
The server logs nothing for this flow. A manual run in the container
shows the output that the parser reads (URL values removed):

```
docker exec -it -u node <container> sh -c 'CLAUDE_CONFIG_DIR=$(mktemp -d) CLAUDE_CODE_OAUTH_TOKEN= ANTHROPIC_API_KEY= ANTHROPIC_AUTH_TOKEN= BROWSER=true claude setup-token'

 Browser didn't open? Use the url below to sign in (c to copy)

https://claude.com/cai/oauth/authorize?code=true&client_id=…&response_type=code&redirect_uri=ht
tps%3A%2F%2Fplatform.claude.com%2Foauth%2Fcode%2Fcallback&scope=user%3Ainference&code_challenge=…
…&code_challenge_method=S256&state=…

 Hold Shift while selecting to use your terminal's native copy


 Paste code here if prompted >
```

`findBrowserCodePrompt` reads the first non-blank, non-URL line after
the URL. Here that line is the copy hint, so the parser returns null.
The runner never calls `onPrompt`, and `check()` returns
`sign_in_required` with no `authorizationUrl` and no error.

## What Changed

- `setup-token-parse.ts`: add `COPY_HINT_LINE_RE`, which matches only
the exact copy hint (CHA-spaced words and a curly apostrophe are
allowed). `findBrowserCodePrompt` skips a line that matches it. The
prompt line rules do not change.
- `setup-token-parse.test.ts`: add a test with the 2.1.291 layout
(hard-wrapped URL, CHA-spaced hint, blank lines, prompt).

## Verification

- `bun test
packages/adapters/claude-local/src/server/setup-token-parse.test.ts
packages/adapters/claude-local/src/server/setup-token-characterization.test.ts`:
71 pass, 1 skip, 0 fail.
- The 2.1.291 output above returns `null` before the change and the URL
plus prompt after the change.
- The test "returns null when a code-like value sits on the line after
the URL" still passes. Other prose between the URL and the prompt still
fails closed.

## Risks

- Low risk. The change skips one fixed line and binds no value from it.
- A later CLI version can change the hint text again. The flow then
hangs the same way until the 300 s timeout. An earlier error (for
example, when no prompt appears after 60 s) and a `log` option for
`runSetupTokenLogin` in `local-ai-browser-login.ts` would make this
visible. This pull request does not include them.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with tool use
(code search, shell, test runs). A human ran the container commands and
reviewed 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
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-11-02 13:00:20 -06:00
DottaandPaperclip a92f616cd0 fix(db): attribute bounded captures to execution owners (#18634)
## Thinking Path

> - Paperclip manages agent work through shared server and database
services.
> - High query volume can spend main-thread time on SQL construction and
result decoding.
> - The existing bounded response capture counts rows and bytes, but it
cannot name the operation that executed a query.
> - Bundled callers can be missing, and measuring every parsed JSON
value adds work to the diagnostic.
> - This pull request carries a semantic execution owner into each
measured query and bounds response sizing.
> - Operators can rank query volume by owner before choosing a
performance fix.

## Linked Issues or Issue Description

Refs #17504, #17522, #17537, #17564.

**What existing behavior does this improve?**

The opt-in `PAPERCLIP_DB_RESPONSE_CAPTURE_WINDOW` diagnostic.

**Subsystem affected**

Database diagnostics and existing server execution boundaries.

**Current behavior**

The capture groups by table, operation, and builder caller. It cannot
distinguish execution owners that reuse a prepared query. Bundled
deployments need source maps for caller locations. The capture visits
every returned value and serializes parsed JSON again to estimate its
size.

**Proposed behavior**

Version 2 captures use fixed semantic owners for HTTP requests, delivery
queues, execution phases, and budget work. They count completed calls,
returned rows, errors, and completion wall time. They sample successful
results 1, 17, 33, and so on, with fixed cell and string/byte limits.
Complex values remain explicitly unmeasured. Semantic ownership works
without source maps.

**Reason and benefit**

A short census can identify which operations issue the most queries or
return the most rows. It preserves each query's execution owner through
pooled connections, concurrent operations, and prepared-query reuse. It
reduces diagnostic work without claiming per-query CPU attribution.

**Breaking changes**

Opt-in capture output moves from `entry.v1`/`summary.v1` to
`entry.v2`/`summary.v2`. Sampled primitive byte lower bounds replace
all-result byte estimates. Consumers must use the new fields and must
not extrapolate them into a total. There is no database migration or
application API change.

## What Changed

- Added `withDatabaseOperation(owner, action)` to the DB package. The
server owns semantic labels. Queries retain execution ownership in their
completion closure.
- Added fixed owners at existing HTTP, delivery, budget, and execution
timing boundaries. Delivery work replaces the context of the producer
that woke it.
- Added complete call/row/error counters and completion wall durations
within the existing bounded window. Durations include waits and are not
CPU measurements.
- Sampled and bounded primitive-value sizing. Complex and oversized
values remain unmeasured. Known owners skip repeated stack extraction.
- Preserved the deadline, group limit, paced output, query outcomes, and
existing coverage exclusions. Updated the operator documentation.
- Added real PostgreSQL owner-isolation and prepared-query tests, server
scope tests, and plain bundled-runtime proof.
- Moved projection-test and benchmark full-response sizing into an
explicit test helper. Existing payload-size thresholds remain unchanged;
these synthetic checks do not use the production sampler.

## Verification

- Final-commit DB capture suites: 43 tests passed in 13.86 seconds
against the actual PostgreSQL driver, including concurrent owners,
prepared reuse, transaction failure, a dedicated client, sampling, and a
query crossing the deadline. DB typecheck passed.
- Server attribution and existing delivery/execution/budget suites: 53
tests passed. Final concurrent HTTP GET/JSON POST scope refinement: 5
tests passed.
- Runtime bundle suite: 8 existing tests passed. The new no-source-map
owner test passed after its fixture was corrected to use a driver
thenable. An ordinary native Promise can bypass an overridden `.then`;
the real PostgreSQL tests separately cover driver integration.
- Observer-only experiment: three alternating pairs per case on Node
24.22.2, macOS arm64, 3,000 calls per run after warm-up. For an
already-materialized synthetic JSON result, median capture CPU fell from
82.37 to 1.87 microseconds/call. For a 100-row primitive result, it fell
from 8.10 to 4.40 microseconds/call. Every result retained identity and
all calls were counted. The new diagnostic intentionally measures less
byte data; these are observer costs, not database latency or a hosted
application speedup. Disabled-case timings were below one
microsecond/call and too small for a performance conclusion.
- Final lifecycle regression: 28 unit tests and DB typecheck passed.
Closing the last capture disables and replaces owner storage;
overlapping captures retain it.
- Final-commit full recursive typecheck passed in 38.38 seconds and full
build passed in 40.81 seconds. The build stamp matches
`83ed4ba9cebd68d48929c46f15ab32d5c0b9687b`. Focused server attribution
tests also passed 5/5 before the test-helper-only migration. Full test
acceptance will use the repository's parallel CI gates; the unsharded
local command takes hours.
- Verification failures retained: initial sandbox runs could not open
local IPC/PostgreSQL listeners; permitted reruns exercised the real
services. One old test expected JSON serialization and was updated to
assert explicit unmeasured complex values. The first bundle fixture used
the wrong promise boundary, as described above. A retained-callback
regression also demonstrated that disabling and reusing the same
AsyncLocalStorage instance could resurrect an old owner; replacing the
instance after the last capture closes fixes that case.
- The first CI run found older projection tests consuming the previous
full-response fields. Those consumers now use the full-response test
helper, preserving their byte thresholds. The two complete affected
route suites plus helper tests passed 115/115 in 73.08 seconds. A
temporary full-row query negative control then failed the unchanged
2,048-byte limit at 5,000,812 bytes and was restored; five other service
suites passed 34/34 and the affected chat cases passed 3/3. All three
previously failing CI shards passed on the corrected head. A local
skill-list benchmark smoke check completed 255 captured queries with no
in-flight queries or overflow; this was integration verification under
concurrent test load, not a timing benchmark.
- Two final-head runner jobs lost communication with GitHub before
reporting a test result. One targeted retry of those jobs and their
dependent aggregate passed. All 53 final-head checks passed, with 3
intentional skips; previously passing suites were preserved.
- Hosted capture remains pending until the reviewed revision is deployed
and a bounded window is explicitly configured.

Reproduce the focused checks with Node 24 and the repository pnpm
version:

```sh
pnpm exec vitest run packages/db/src/response-capture.test.ts packages/db/src/response-capture-postgres.test.ts
pnpm --dir server exec vitest run --config vitest.config.ts src/__tests__/database-operation-attribution.test.ts scripts/bundle-runtime.test.mjs
pnpm -r typecheck
pnpm build
```

## Risks

- Owner context is active while a valid future capture window is armed,
up to 30 minutes before accounting starts. No-window and expired calls
take the direct path. Diagnostics still add bounded work while enabled.
- A nearest nested owner is reported. Broad `http.request` and
unattributed groups remain visible; this does not identify every service
callsite or SQL shape.
- The existing census excludes tagged/raw explicit-execute/streaming
paths and internal protocol commands. Its counts are not PostgreSQL
transaction totals.
- Completion wall time includes queue/database/network/scheduling waits.
Concurrent durations overlap. Sampled bytes exclude complex values,
large strings, and values beyond the sizing limits.
- Activation is startup-only. Operators must choose the window after
artifact preparation, allow rollout/warm-up time, and remove the expired
setting afterward.

## Model Used

OpenAI Codex, GPT-6 family, with code execution, repository inspection,
and parallel agents. The exact deployment model ID and context-window
size are not exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-02 12:12:21 -06:00
DottaandPaperclip 40b8075916 feat(ui): show scheduled task waits as Monitoring (#18624)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task indicators tell an operator whether work is active, waiting, or
complete.
> - An agent can yield until a saved monitor checks CI or another
external result.
> - The task currently keeps the In Progress icon between those runs.
> - Changing the stored status would affect monitor admission and
recovery.
> - This pull request adds a read-only Monitoring display state with a
blue clock.
> - Operators can see and filter scheduled waits while execution keeps
its existing rules.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Task status indicators, task collection filters, board columns, and
search facets.

**Current behavior**

A task that waits for a scheduled monitor shows the same In Progress
icon as active work.

**Proposed behavior**

Show Monitoring with a static blue clock when an agent-owned task has a
saved ordinary monitor and no active run, execution lock, review
participant, or active recovery action. Return to the normal task
indicator when the check starts. Preserve review, blocked, terminal, and
quota-recovery states.

**Reason and benefit**

Operators can distinguish scheduled waits from current agent work
without reading the task history.

**Breaking changes**

None to stored task statuses or execution rules. Task reads gain
optional displayStatus. Collections and search gain a separate read
filter. Existing status filters keep their lifecycle meaning.

Related historical work: #4396 covered a monitoring label exemption.
This change uses the saved monitor and a read projection.

## What Changed

- Add one SQL projection for task detail, compact lists, summaries,
relations, search results, and display-status counts.
- Show the blue clock in shared task icons and chips, task detail and
breadcrumbs, properties, references, list rows, recent navigation, and
agent task cards.
- Add Monitoring to task filtering, grouping, sorting, board columns,
and search facets. Prevent a drag from creating this derived state.
- Keep stored lifecycle statuses, monitor scheduling, checkout,
recovery, permissions, and agent status filters unchanged.
- Add database and UI regression coverage, full-page Storybook examples,
and product and access-map documentation.

## Verification

- Passed `pnpm -r typecheck`.
- Passed `pnpm build`.
- Passed `pnpm check:token-gates` and the map/catalog structural checks.
- Ran the new database suite on a disposable PostgreSQL 17 database. It
covers read surfaces, filter pagination/counts, queued/running checks,
rescheduling, completion, retained monitors, review, and recovery holds.
- Ran existing search, list, board, sidebar, task status, and filter
tests. Corrected board queries use displayStatus and eight columns.
- The full local `pnpm test:run` stopped in a chat integration shard: 12
failures (four timeouts and eight FIFO publication assertions). That run
spanned a rebase. A focused rerun on the current source passed all 21
selected cases without assertion or timeout changes. The original cause
is not established; this does not claim the full local suite passed.
Final-head required CI passed (ci / verify and ci / e2e, run
41488267209).
- Passed 82 tests for task API summaries, recovery precedence, search,
and optimistic updates; 54 tests for status cards, database reads, and
board moves; and 27 tests for blocker relations and recent-task cache
updates. One intermediate database setup hit its 20-second hook timeout;
the serial rerun passed.
- Greptile is 5/5 on d003ea672d. All
review threads are resolved. The separate optional Paperclip Review
ended early and returned neutral; GitHub rejected a direct rerun request
with HTTP 404. It is not counted as a completed review.
- Review fixes cover escalated recovery, is:open, optimistic status
changes, blocker-summary projection, and recent-task dedupe.
- Reviewed 15 screenshots from production pages and components with
isolated Storybook API fixtures: task detail/properties/breadcrumb,
list, groups, status filter, board, search/results/filter counts,
reference pills, sibling navigation, agent cards, picker, dashboard,
inbox, profile, watched status-card tasks, light theme, and mobile. This
does not claim deployed staging verification.

## Risks

- Read projections must stay consistent across full, compact, summary,
and search responses. Tests cover those paths.
- Monitoring is not a writable lifecycle status. The board does not
accept drops into its Monitoring lane or onto a monitored card. This
prevents a drag from implying a scheduled check. Move work into In
Progress through that lane instead.
- The read projection adds run/recovery existence checks. Existing
heartbeat_runs_company_issue_created_idx and
issue_recovery_actions_company_source_status_idx indexes cover the
company/task lookup. It adds no worker, timer, recurring query,
migration, or provider call. No production load benchmark is claimed.
- Older clients can ignore displayStatus. Existing lifecycle status
filters keep their behavior.

## Model Used

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

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-02 11:35:30 -06:00
DottaandPaperclip b9e94b85a6 feat(observability): trace Daytona lease acquisition and resume (#18626)
## Thinking Path

> - Paperclip manages work performed by AI agents.
> - The server prepares a remote execution environment before the agent
can respond.
> - OpenTelemetry traces show the time spent in each preparation phase.
> - A retained Daytona sandbox can take minutes to resume from an
archive.
> - The provider worker previously exposed no child timings for this
operation.
> - This pull request adds bounded lease phase spans under the existing
worker trace.
> - Operators can separate provider startup from lookup, teardown waits,
and workspace verification.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Daytona environment acquisition and resume tracing. A long worker
invocation currently hides the operation that consumed its time. Related
tracing foundation: #10999. No duplicate open PR was found.

**Subsystem affected**

The Daytona sandbox provider and the server's existing plugin span
validation boundary.

**Proposed behavior**

Show lookup, start or recovery, teardown waits, workspace verification,
and fresh acquisition setup as timed child spans. Group verification
steps beneath one parent. Record the bounded provider state before
start, including archived.

**Reason and benefit**

An operator can identify which step delayed the first agent response.
SDK startup includes its internal readiness wait; this change does not
claim to measure provider queue time separately.

**Breaking changes**

The opt-in trace schema gains fixed span names and one closed state
attribute. Execution, authorization, resource ownership, deadlines, and
retry policies keep their existing contracts.

## What Changed

- Add lease phase spans through the existing invocation tracer.
- Add the fixed span names and state values to host validation. Define
the state key in the shared startup attribute contract.
- Preserve the exact work result or error if tracing fails. Never repeat
work through the tracing wrapper.
- Test worker receipts for archived and warm resume, recovery,
concurrency, failure, disabled tracing, and cleanup.
- Document the waterfall and its attribution limits.

## Verification

- Daytona plugin suite: 525 passed, 7 skipped. The expanded tracing
suite separately passes all 12 cases.
- Host span validation and instrumentation tests: 46 passed, 3 skipped.
- Standalone Daytona TypeScript check passes.
- Module boundary and whitespace checks pass.
- Full recursive typecheck and full build pass.
- The full local Vitest attempt at
b007a8e64f stopped in chat shard 1: 337
passed, 2 failed, 1478 skipped. One retry-notice case exceeded 15
seconds. The next case then reported that fake timers were no longer
installed. A focused rerun of all four retry-notice cases passed. This
is not a full local suite pass.
- Final head 8e1f81523b passes CI and
Greptile 5/5, with no unresolved review threads. The CI chat shards pass
despite the preserved local timeout attempt.
- The expanded lease tracing suite passes 15 tests; host/shared tracing
suites pass 66 tests. Disabling the wait flag makes the new teardown
test fail, and restoring it passes. Standalone plugin typecheck passes.
- The tests drive the real plugin worker RPC and invocation tracer with
a controlled provider fixture. They validate receipts and host parent
IDs. They do not establish deployment or Honeycomb ingestion. Inspect a
new deployed run to verify the exported waterfall.

## Risks

- A new provider phase must be added to the closed host allowlist or it
appears as other.
- SDK-internal start and readiness work remains one measured operation.
- Older workers without withSpan run normally without these new detail
spans.
- Receipt budgets and clock validation can omit detail from very large
or invalid receipts.

## Model Used

OpenAI Codex, GPT-6, with repository tools and code execution. The exact
runtime model ID and context window were not exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-02 11:32:03 -06:00
AlexFrontegg 8424a51018 fix(redaction): scan long letter/digit runs in linear time (#16999)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip removes secrets from run logs, tool results and
diagnostics with `redactSensitiveText` before it stores or shows them.
> - The JSON field-name patterns in that function start with an
unanchored `[A-Za-z0-9_-]*`. Each start position inside a long run of
letters and digits scans to the end of the run, so the cost grows with
the square of the run length.
> - Base64 of text is one long run of letters and digits. A 40 KB base64
blob of source code in a shell command takes about 5 s to redact, and a
large Gmail RAW tool result does not finish.
> - The redaction runs on the main event loop, so one such chunk blocks
every request and every run on the instance.
> - This pull request makes these scans start only at the start of a
run. The scans become linear, and the redacted output does not change.
> - The benefit is that large base64 payloads no longer freeze the
server, and every secret that was redacted before is still redacted.

## Linked Issues or Issue Description

Fixes #15393. The tool content guard runs `redactSensitiveText` on tool
results. A Gmail RAW body is base64url of a MIME message, which is one
long letter/digit run. `validateToolContent` on a Gmail-shaped result
with a 68 KB `raw` field takes 59 s on master and 19 ms with this
change; a 5.4 MB `raw` field takes 0.65 s.

Thanks @ts-beat for the clear report in #15393.

Refs #12309 (false positives in the same patterns; this pull request
does not change which text matches).

I searched issues and pull requests for "redaction",
"redactSensitiveText", "quadratic", "backtracking", "hangs the server"
and "100% CPU". #15393 reports the hang. No open pull request fixes it.

**Subsystem affected:** secret redaction (`server/src/redaction.ts`,
`packages/adapter-utils/src/command-redaction.ts`), used by run logs,
tool content guards and run diagnostics.

**Problem:** `compactRunLogChunk` redacts each run log chunk before it
truncates the chunk. A shell command such as `printf '%s' '<base64>' |
base64 -d > file.ts` with base64 of source text makes one long
letter/digit run. Base64 of ASCII text contains `+` or `/` only where
every third byte is `>`, `?`, `~` or DEL, so random base64 does not show
the problem. One `redactSensitiveText` call on such a tool-call line
takes (local measurement):

| Blob | Before | After |
| --- | --- | --- |
| 4 KB | 66 ms | 1.1 ms |
| 8 KB | 272 ms | 0.5 ms |
| 16 KB | 1.1 s | 1.0 ms |
| 40 KB | 5.4 s | 2.5 ms |
| 1 MB | not measured | 36 ms |

The `\b`-anchored command patterns (`KEY=value`, JWT, `Authorization`)
and the CLI option pattern have the same quadratic cost on
dash-separated runs, for example base64url data.

**Proposed solution:** start the unanchored field-name scans only where
no name character precedes them (`(?<![A-Za-z0-9_-])`). A prefixed name
such as `myApiKey`, `x_auth_token` or `GITHUB_TOKEN` still matches,
because the scan from the start of the run absorbs the prefix.

**Alternatives considered:**

- Truncate or cap the input before redaction. Rejected: a cut can split
a secret and leave part of it unredacted, and the scan stays quadratic
below the cap.
- Run the scan in a worker thread with a deadline, as #15393 suggests.
That limits the damage, but the CPU cost stays, and a timeout must then
drop or block the content. It is still useful as an additional defense.

## What Changed

- `server/src/redaction.ts`: `JSON_SECRET_FIELD_TEXT_RE` and
`ESCAPED_JSON_SECRET_FIELD_TEXT_RE` start a field name only where no
name character precedes it. The lookbehind comes after the optional
opening quote, so quoted keys behave as before. The `Authorization` word
scan uses the same start instead of `\b`.
- `packages/adapter-utils/src/command-redaction.ts`: the `KEY=value`
pattern uses the same start. The JWT pattern starts only at the first
word character of a run. A CLI option name can no longer span `--`, so
each `--` starts its own short scan.
- Redaction still runs before truncation. `compactRunLogChunk` does not
change.

Why the output does not change: when an old pattern could match from the
middle of a run, it could also match from the start of that run, because
the `[A-Za-z0-9_-]*` prefix absorbs the extra characters. The engine
tries the start of the run first. So the old patterns never matched from
the middle of a run, and the new start condition only skips positions
that cannot produce a match. The old JWT pattern also always matched
from the first word character of a run. A CLI option name always ends at
the end of its run, so the match from the last `--` in the run ends at
the same place.

## Screenshots

No UI change.

## Verification

- New tests:
- prefixed secret field names in plain, single-quoted and escaped JSON
are still redacted, also when the name is glued to the end of a long
run;
- a 48 KB base64-of-source tool call through `compactRunLogChunk` stays
intact, the secret in the same command is redacted, and the call
finishes within 1 s;
- `redactSensitiveText` and `redactCommandText` handle 48-120 KB
letter/digit, dash-separated, `--`-separated and base64url runs within 1
s, and redact the secret that follows each run;
- `x--token`, `--x--token`, `a-b--api-key=`, `prefix-MY_API_KEY=` and
`--<jwt>` are still redacted, and `my-token is here` is not changed.
- Without the change, the timing tests fail: 9.3 s, 9.8 s, 32 s and 60 s
against the 1 s budget. The correctness tests pass with and without the
change.
- Mutation check: I reverted each of the six pattern changes one at a
time. Each revert makes at least one test fail.
- Differential fuzzing: 1.1 million random inputs built from secret
names, quotes, escaped quotes, dashes, dots, colons and JWT parts gave
the same output from the old and new `redactSensitiveText`,
`redactCommandText` and `redactDiagnosticText`.
- `cd server && npx vitest run <files>`: all 33 server test files that
use the redaction helpers, with embedded Postgres, 1730 passed.
- `npx vitest run --project @paperclipai/adapter-utils
packages/adapter-utils/src/command-redaction.test.ts`: 25 passed.
- `npx vitest run --project @paperclipai/adapter-utils`, complete suite:
80 of 82 files passed in the parallel run. The 2 other files
(`github-launcher-environment`, `sandbox-managed-runtime`) had 5 s
timeouts in git-based tests and passed on rerun.

## Risks

- Lookbehind assertions need ES2018. `redactCommandText` also runs in
the UI bundle. Node and current browsers support lookbehind, and the
server (`server/src/log-redaction.ts`) and the UI
(`ui/src/components/task-chat/task-chat-attachments.ts`) already use it.
- A change to a redaction pattern could leak a secret. The reasoning
above, the unchanged existing tests and the differential fuzzing show
the same output as before.
- Not changed here, found while checking: `redactDiagnosticText`
backtracks exponentially on an escaped JSON secret value made of many
escaped backslashes with no closing quote, and `redactSensitiveText`
redacts a value only up to its first quote of either kind
(`{"apiKey":"abc\"tail"}` keeps `tail"`). Both behave the same before
and after this pull request.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`) by Anthropic, in Claude Code, with
tool use (shell, code search, and test runs) and extended thinking. A
human directed the design choices.

## 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
- [ ] 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-02 10:49:08 -06:00
Nicky LeachandPaperclip f8a888a072 feat(runner): define bounded startup lifetime contracts (#18490)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Native sessions and plugin workers have separate startup lifetimes.
> - A caller timeout does not prove that a worker or an accepted host
write has stopped.
> - Shared startup code needs an exact deadline and an actual settlement
contract.
> - This PR adds those contracts and tests them before any production
caller uses them.
> - The dependent integration in #18405 will use the contracts with
verified cleanup.
> - This split lets reviewers assess the lifetime rules separately from
runtime activation.

## Linked Issues or Issue Description

Refs #18405. This PR extracts the foundation from that change. The
integration will depend on this PR.

**Problem or motivation**

A bounded startup must distinguish the caller's wait from actual worker
completion. A late worker reply or an accepted host request can outlive
the original RPC promise. A shorter timeout must not discard that
responsibility or authorize a replacement run.

**Proposed solution**

Add one Core execution context, optional Runner startup deadlines, and
worker settlement and retirement APIs. Test the contracts with real
child-process IPC. Keep production context creation, transport
projection, capability negotiation, durable cleanup, and Stop/recovery
integration in #18405.

**Alternatives considered**

One combined PR mixed the lifetime contract with its activation and
cleanup policy. Activating a shorter deadline before the complete
transport and cleanup path is unsafe. Splitting only the SDK
declarations would not provide a useful behavioral review boundary.

**Roadmap alignment**

This supports the existing plugin, sandbox execution, and bounded
recovery capabilities. It adds no new product workflow, endpoint,
migration, provider or runtime dependency.

## What Changed

- Add `NativeExecutionContext`. It retains the selected input, binds one
transport target, rejects work after cancellation or expiry, and tracks
actual worker settlement. Expiry alone does not complete pending work.
- Add worker startup groups. A caller timeout retains the group's
responsibility. Retirement revokes obsolete invocation scopes, waits for
accepted host handlers, and retires only the verified group's
bookkeeping. Each cleanup wait is bounded; an unfinished handler remains
pending for a later retry.
- Add an optional Runner startup signal and absolute deadline for
session open and retained attachment. Clean up abort listeners after
synchronous failures. Existing callers keep their default timeout path.
- Expose the remaining confirmed controller-lease budget without a new
query or renewal timer.
- Add optional SDK context and preparation-retirement fields. No
production bridge sends or advertises them in this slice.
- Keep the required lease mock update with the new `remainingMs()`
contract so this foundation typechecks independently.

Production activation files, including the native executor and
environment task bridge, match the current master base. No production
caller constructs `NativeExecutionContext` or passes the new Runner
startup option. This PR adds no durable cleanup producer and claims **no
standalone TTFT improvement**. The dependent integration must enable the
context, transport and verified cleanup together.

## Verification

Initial split verification on
`8ebc8a2f9c6c02e90f920356138b3742a4067b28`, based on
`9a3f395737e7a92faf99971459979061d423531a`:

- From `server`: `../node_modules/.bin/vitest run
src/services/native-runtime/native-execution-context.test.ts
src/services/native-runtime/native-execution-context-worker.test.ts
src/services/native-runtime/native-execution-lease.test.ts` — **17
passed**. Includes real child IPC, caller timeout with late reply,
worker death, accepted host-handler draining, obsolete cleanup
invocation rejection, unrelated work preservation, context scope,
expiry, and confirmed lease budget.
- From `packages/paperclip-runner`: `../../node_modules/.bin/vitest run
src/native-session-runtime.test.ts` — **188 passed**. Includes existing
callers without the new option and new
synchronous-failure/expired-deadline cases for fresh and retained
sessions.
- Direct TypeScript checks for `packages/plugins/sdk/tsconfig.json` and
`packages/paperclip-runner/tsconfig.json` — passed. Built only their
shared/eval TypeScript prerequisites.
- From `server`: `../node_modules/.bin/tsc --noEmit` — passed.
- Checked the partition against the reviewed combined candidate. The
twelve selected files match exactly. The thirteenth file carries only
the required test-mock update. The production executor, bridge and
target wiring remain equal to the base.
- `git diff --check`, added-line secret/private-data scan, and
added-code-comment scan — passed. No generated files or run artifacts
are committed.
- The first old-base split passed its focused tests but failed server
typechecking because the unchanged executor test mock lacked
`remainingMs()`. The original failure is retained. Moving that one-line
mock update into this foundation resolved the split dependency; the
final current-master checks above pass.
- Full local CI suites, Rust builds, deployments and provider calls were
not run, as requested. GitHub CI will provide full regression coverage.
Current-head GitHub CI, standard review and Apex are complete; exact
acceptance is recorded below.

Review follow-up `d7635f4d280dab757a3666703737bcda955c9361` fixes three
reproduced lifetime edge cases. A retiring startup group rejects new
calls and shares one retirement promise; failed bounded retirement
remains retryable. Transport identity uses a fixed field tuple rather
than object-property order. Finishing a context rejects pending lookup
waits without aborting an already-started backend or pretending the
worker completed. All three new regressions failed before the fix; the
updated foundation selection passes **20 tests**, including actual IPC
and failed-retirement retry. Server typechecking and added-line checks
pass. These replace the earlier 17-case selection, rather than adding 20
unique cases to it. The original failures and passing reruns are
retained.

Apex follow-up `cb8860bab5ab9d6357a352e11f276be0d0cdb26d` keeps removed
worker handles reachable while they own startup calls, accepted host
handlers or retirement work. Drain/retirement includes active and
retained handles; completion callbacks remove settled handles. Stop and
replacement remove only the exact handle they observed. This adds no
database/network operation, polling loop or generic shutdown wait. The
SDK, Runner, context and transport bridge are unchanged.

Seven additional IPC regressions failed on the prior manager and pass
after the fix, covering removal/replacement, Stop-all, host-handler
settlement, bounded retirement failure/retry, pruning, and replacement
ownership. The updated foundation selection passes **27 tests**, with
server typechecking passing; these supersede the earlier 20-case
selection. A separate real-PostgreSQL/child-IPC characterization uses
the integration's actual SDK host, storage registry and run-control path
with the exact corrected manager bytes. Before the fix, cleanup released
run ownership while an accepted host write was blocked, then the late
write restored obsolete metadata. After the fix, ownership and cleanup
remain pending until the write completes; cleanup then removes the stale
metadata before releasing ownership. The original failure, corrected
result and source snapshots are preserved. Final head
`cb8860bab5ab9d6357a352e11f276be0d0cdb26d` passes GitHub CI (53
successful checks, 2 expected skips, Snyk success), standard Greptile
**5/5** and fresh Apex **5/5**. All review threads are resolved and the
PR is conflict-free.

## Risks

The shared Runner helper now observes synchronous operation failures
through its asynchronous cleanup path. The affected runtime file's 188
tests pass, but full regression coverage still depends on CI.

The new lifetime APIs require honest settlement signals. A future caller
must not treat a deadline, worker crash or Stop intent as verified
retirement. Production activation remains dependent on the complete
integration and its cleanup tests in #18405. This foundation introduces
no shorter production startup budget or new cleanup policy by itself.

The context's transport projection is process-local. It must not be
persisted or used as a general authorization cache. No production caller
supplies it in this PR. There is no database migration, new dependency,
or deployment requirement for merging this foundation.

## Model Used

OpenAI Codex, based on GPT-6, assisted with repository inspection, the
split, tests and command execution. The exact serving model ID and
context-window size were not exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` /
`Closes: #` / `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 issue
identifier
- [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 — this
description documents the inactive contracts; operational activation
documentation remains with #18405
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green on `cb8860bab5`
- [x] Current-head standard Greptile and Apex are 5/5 with no unresolved
findings
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-02 08:22:42 -08:00
DottaandPaperclip baa4a2e64e fix(runner): restore Codex auth before source recovery (#18612)
Restore disposable Codex authentication and safe provider configuration before cold source recovery. Preserve source receipts and conversation state until reconciliation, and rebuild assigned tools from the admitted launch binding.

Validate remote launch destinations, clear stale authentication when no selected credential exists, and atomically publish SSH file transfers without following retained links. Red/green recovery regressions, a real two-turn Codex/MCP probe, and all current PR checks pass.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-11-02 10:14:24 -06:00
DottaandPaperclip 42c64d4ec4 fix(runner): preserve deliberate OpenCode approval waits (#18610)
## Thinking Path

> - Paperclip coordinates agent work and human approval.
> - A saved Plan must wait for the user before work continues.
> - The native runner interrupts the provider after it saves that wait.
> - OpenCode can report the interruption with an idle event and no final
answer.
> - Empty-answer recovery treated that intentional pause as an
incomplete answer.
> - This change stops answer recovery for an interrupted turn.
> - The runner still requires a real provider terminal before it settles
the wait.

## Linked Issues or Issue Description

**What happened?**

OpenCode could send a corrective model request after Paperclip
interrupted a turn for a saved approval request. The native runner then
failed with `governed_wait_provider_terminal_unproven` because it did
not receive the required terminal event within the existing drain
window.

**Expected behavior**

An interrupted turn must stop requesting answers. A provider idle or
cancelled event must remain available to the native wait settlement. An
HTTP abort acknowledgement alone must not count as a terminal.

**Steps to reproduce**

1. Start an OpenCode turn that has no final answer.
2. Interrupt that turn after saving a durable approval request.
3. Have the provider return `session.idle` or an idle `session.status`.
4. Observe that the old driver sends a second prompt instead of emitting
the terminal event.

The controlled regression fails before the fix for both idle event
forms: it observes two prompts instead of one. The affected driver blob
is identical in the deployed source and the current base. Related:
#18364 added the empty-answer recovery path.

## What Changed

- Record interruption intent for the active OpenCode turn before
asynchronous settlement.
- Cancel its answer-recovery timer and prevent corrective requests for
that turn.
- Preserve provider terminal checks and avoid a delayed abort of a later
turn.
- Cover both idle event forms, an existing correction, durable question
handoff, in-flight tools, missing terminal evidence, and a later
authorized turn.
- Document the interruption contract.

## Verification

- The new regression failed before the fix for the expected extra
prompt. A second red test confirmed the sibling durable-question handoff
also sent that unwanted prompt.
- Interrupted idle events now produce explicit cancellation terminals.
Tests reject a failed terminal and require normal completion on the
later authorized turn.
- All 305 targeted tests passed across the OpenCode driver, normal
empty-answer recovery, interruption, backend publication, and native
session runtime suites.
- Runner TypeScript check passed: `tsc -p
packages/paperclip-runner/tsconfig.json --noEmit`.
- `git diff --check` passed.
- Greptile reviewed the current commit at 5/5 with no findings. Both
earlier Paperclip review findings are fixed and their threads are
resolved.
- The additional Paperclip advisory reviewer could not assess the final
commit because its earlier workspace save did not finish. Its check is
neutral; this is not a passing assessment. No reviewer configuration or
workspace safety gate was changed.
- Repository CI passed on run 41470761437, attempt 2. Attempt 1 retained
a 15-second timeout in an unchanged voice-report test; only the failed
jobs were retried once, on the same commit. The identical test also
passed in an isolated baseline check against disposable PostgreSQL with
its original timeout.
- Live QA acceptance is pending. No eval prompt, model, allowance,
timeout, or assertion changed.

## Risks

The change affects OpenCode interruption and answer recovery. A silent
provider must remain unproven; the negative control checks that case.
Ordinary answer recovery remains enabled on authorized turns. No schema,
permission, or provider configuration changes are required.

## Model Used

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

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-02 10:07:32 -06:00
Nicky LeachandPaperclip aab87ce707 Prove workspace delivery before inspecting cleanup candidates (#18493)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Completed tasks leave workspaces that need safe cleanup.
> - Providers without ancestry inspection currently need a recorded
merged PR before cleanup can start.
> - A squash merge changes commit identity, so direct ancestry cannot
prove that delivery.
> - This change proves delivery from the finalized HEAD and captured
destination before storage inspection.
> - Fresh storage checks and a second delivery check still protect
removal.
> - This reuses the existing worker and receipts. It adds no table or
timer.

## Linked Issues or Issue Description

Refs #18410. Related: #13400 addresses workspace identity during
inspection; this change adds destination-aware delivery discovery.

## What Changed

- Resolve direct ancestry or discover an associated merged GitHub PR
without a recorded work product. Require exact HEAD, destination
repository and branch, and a reachable integration commit.
- Use the latest settled finalization receipt as a candidate. Reject
missing, failed, pending, conflicting, or changed evidence.
- Inspect storage only after positive remote proof for providers that
disable ancestry inspection. Recheck HEAD, branch, source identity and
delivery before removal intent.
- Add an optional provider capability for a specific PR recovery hint
and an optional prepared destination receipt. Older providers keep their
existing protocol.
- Wake cleanup when finalization writes settle or gain a workspace
binding. Keep remote-only retries for concrete unresolved delivery. Park
missing or unsupported candidate identity.
- Reuse the source project's explicit GitHub consent for delivery checks
through the existing authorization service. Revalidate the bound
repository and consent before removal; missing or revoked consent
retains storage without switching identities. Public sources remain
anonymous.
- Update the contracts, documentation, access map, and targeted
regressions.

## Verification

- Initial-head GitHub CI passed at
`5acd4c818be6f1764f8319f42891f9956c357c26`, including full build and
typecheck. Standard Greptile is 5/5 with no open threads. The first Apex
review scored 3/5 and found startup latency from remote proof lookups
and removal retry identity changes across provider capability changes.
Both corrections are pushed in `9d1a38df3d`, with the qualified
destination-ref length fix. Red regressions reproduced each issue.
Latest targeted checks passed: cleanup/worker 26, provider integration
41, SDK contract 8. Head `9d1a38df3d` passed standard and Apex reviews
at 5/5 with no open threads, plus all CI including full build/typecheck.
Canary Dry Run initially failed on an npm 404 fetching
`rollup-4.67.3.tgz`; the exact tarball subsequently returned HTTP 200,
and one targeted retry passed without source changes. Aggregate
verification also passed. After #18403 merged, master introduced
conflicts in shared workspace cleanup and metadata handling. Resolved
them in `7700832536`, preserving remote snapshot cleanup and Git
delivery-proof cleanup. Targeted merge verification: 93 tests passed in
the first run (59 opt-in skips), then all 16 remote lifecycle tests
passed with explicit database opt-in; map checks passed. The isolated
database is stopped. The merge head received standard review 5/5. Its CI
found two failures in unchanged test cleanup paths: native runtime
temporary-directory ENOTEMPTY and agent-chat cancellation lock timeout.
Those failures remain recorded and require successful final-head
verification. The subsequent project-consent change has targeted
coverage: authorization36, resolver33 and cleanup19 tests passed,
including revoked consent, anonymous public access and source-binding
races. The positive consent test failed before the fix. Current head
`c772679e05` has green GitHub CI, including full build/typecheck and
both previously failing test jobs, plus standard Greptile 5/5 with no
open threads. Neither cleanup failure required changes to its test path.
Apex also completed at 5/5 on this exact head (check `124651548532`,
summary updated 2026-10-31 20:45 UTC), with no open review threads. The
PR remains draft. Earlier failures remain recorded above.

- Final focused run on the rebased branch: **182 tests passed across six
files**, covering the resolver, delivery cleanup, provider bridge,
finalization notifications, existing workspace service and SDK
contracts. The disposable test database was stopped afterward.
- Targeted resolver tests: 33 passed. These cover ancestry, discovered
squash PRs, wrong HEAD/base/repository, removed integration commits,
bounded responses, cancellation and credential isolation.
- Targeted cleanup integration tests: 17 passed. Provider bridge
integration: 36 passed. SDK contract tests: 7 passed. Finalization
notification tests: 9 passed.
- A negative control that removed the final delivery check failed the
race regression as expected. The check was restored.
- Read-only live GitHub probe: the new resolver discovered PR #18410
from HEAD `3f5ade4cd42b707040e9a82fdaed343e3d736a09`, without PR hints.
It proved integration commit `6484f96c28bfb11a793ce4be6cc5ac15643a5a02`
reachable from observed master
`5bc029bf6c1db6647e96acea323f3723fd1d3eba`. GitHub also advertised the
exact original HEAD at `refs/pull/18410/head`.
- `node scripts/check-maps.mjs` passed. Structural checks do not prove
deployment behavior.
- Local tests are targeted by user request. Full build, typecheck and
test suite run in GitHub CI. A standalone server typecheck could not
complete because local Runner build declarations were absent; no
changed-file diagnostics were reported.
- Initial local execution hit a stalled pnpm wrapper, sandbox socket
denial and stale generated SDK declarations. Direct installed binaries,
the authorized isolated test database and rebuilt SDK resolved those
setup failures. No deployment was performed.

## Risks

- Destructive cleanup still requires fresh storage safety and lifecycle
checks. Remote proof alone never authorizes removal. A persisted removal
intent preserves existing recovery semantics.
- Legacy workspaces without an unambiguous destination or completion
HEAD remain retained. This does not guess the original default branch.
- Private repositories use the explicit standing GitHub consent on the
bound source project, with current membership, grant, repository access
and trust checks. No separate company token is required when that
consent is valid. Missing or revoked consent and unavailable or
rate-limited access retain storage; the worker does not borrow a task or
agent identity.
- A known undelivered commit keeps the existing five-minute retry
because external GitHub changes have no guaranteed ingress to this lane.
This can keep cleanup scheduled. Missing evidence parks instead.
- Providers need the new recovery capability to clean a squash merge
whose original HEAD is reachable only through its PR ref. Providers
without it remain conservative.
- The resolver currently supports GitHub.com. Existing local and
other-provider ancestry paths stay in place.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository tools and
parallel code agents. The exact runtime model ID and context-window size
were not exposed to this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-02 07:59:17 -08:00
06bc8db75c fix(runner): install verified API artifacts in execution workspaces (#18494)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Native agents use authorized API tools to read saved results.
> - Copying a large response as model-generated text can lose bytes.
> - The execution workspace needs an exact copy of the saved artifact.
> - This change copies the authorized bytes without shell API
credentials.
> - The runtime verifies size and SHA-256 before it reports success.
> - A live evaluation checks capture origin, installation and delivered
bytes.

## Linked Issues or Issue Description

**What happened?**

A native agent lost one byte while copying a 24576-byte API text page.
Existing capture and paging controls passed. The runtime had no verified
saved-artifact-to-workspace copy tool. A separate historical screening
refusal does not establish capture corruption.

**Expected behavior**

An authorized native run can install the complete saved artifact. The
stored and installed bytes must match in size and SHA-256. A conflicting
file must remain unchanged. A lost publication receipt must not cause an
unsafe repeated write.

**Steps to reproduce**

Run
`server/src/services/native-runtime/artifact-transfer.integration.test.ts`
on baseline `c46b8f35add9255fc71be294d27f5a7865bcb47f`. Its capability
assertion fails because `download_artifact` is absent. Apply this PR and
run the same regression with the filesystem and API controls. It
verifies 388131 synthetic bytes through the authority and installation
boundary.

Refs #18429. Related #17393, #17086 and #17285 cover paging, transfer
foundations and remote output reads. This PR adds the inbound
installation contract.

## What Changed

- Add `download_artifact` for supported standard-mode native runs, with
a 10 MiB limit.
- Reuse current company, task, run, asset-content and responsible-user
access checks. Check authority again before publication.
- Bind installation to the current execution lease. Stage through
confined Linux descriptors and publish with an atomic create-only link.
Keep API credentials out of execution commands.
- Verify bytes at storage and execution boundaries. Reject unsafe paths,
symlinks, different existing files, truncation and corruption. Reconcile
an unknown publication through verification only; an identical explicit
retry remains safe.
- Add API documentation, access-map entries and content-free audit
records.
- Preserve only the pinned Daytona SDK numeric HTTP status when failed
creation cleanup is unconfirmed. Keep provider exception messages and
headers private.
- Keep only the finished runner binary and portable provider pack in
their builder layers. Remove Rust targets/downloads and the Node install
workspace/store before export. Report tree sizes.
- Add one explicit Codex/Daytona Product E2E cell. Its independent
oracle checks the server capture audit, redacted tool receipts, stored
source, delivered attachment and browser link after reload. Uploaded
substitutes and one-byte loss fail.

## Verification

- Merge update at `6766b46553cab5b9787ade87a002eccaf50e68fc`: merge
current master and preserve both explicit suites in the catalog. Focused
catalog, image and oracle checks: 122 pass. Filesystem and authority
integration: 20 pass. Maps, module boundaries and whitespace checks
pass. New-head CI and Greptile are running. Full local build and
typecheck remain unavailable because cargo is absent.

- Behavioral red/green: the absent-tool regression failed before
implementation and passed afterward. The initial missing-package attempt
is recorded as setup failure.
- Linux filesystem and real PostgreSQL/authority integration: 19
filesystem tests and 1 authority test pass. Existing API authority,
capture and paging controls: 70 pass, 2 skipped. Provider transport in
these integration controls is simulated.
- Qualification calibration: 118 focused tests pass. The real
persisted-event redaction case and a byte-identical uploaded substitute
each failed before their oracle repair. The unchanged controls then
passed. Harness typecheck passes.
- Full recursive typecheck and build pass locally on the integrated
production source at `086f7a2901`. Later commits change only
qualification tests and documentation.
- Structural map/module checks and whitespace checks pass.
- Full local `pnpm test:run` was attempted by the implementation worker
and stopped under its 8 GiB memory limit, with no full-suite verdict.
Current-head CI must supply the full test result.
- The complete local Product E2E support suite has 13 failures. The
unchanged pre-qualification baseline reproduces all 13: host PostgreSQL
semaphore capacity, stale timeout/profile/workflow inventory
expectations, and expired fixed-date allowance fixtures. This is not a
full local suite pass.
- Earlier CI at `086f7a2901` failed a browser test during cleanup
cancellation with a database lock timeout. It remains recorded. All
browser shards passed on both later fully checked heads.
- Two qualification campaigns were canceled during setup before provider
execution while correcting the oracle. They provide no live product
verdict.
- Previous-head CI at `28fa001457f59df1c300c016dc569049e72d0061`: 53
checks pass, 3 intentional selection skips; no failed or pending checks.
- Real image/export regression: campaign 41272351780 compiled the Linux
runner and passed root/unprivileged CLI and transport checks, then
failed BuildKit export with `no space left on device` (exit 102,
filesystem 99.99%). No provider cell ran. Removing the Rust build trees
alone was insufficient: campaign 41273604815 also failed during
provider-pack export with exit 102. Commit `5bbb549c54` also
consolidates dependency installation and pack construction so the
disposable workspace/store can be removed in the same layer. All 122
image/oracle/catalog checks pass. Image and remote-pack publication then
passed in campaign 41275196741 after cleanup changes plus a scoped
60-to-120 GiB expansion of only its delete-on-termination builder disk.
The VM terminated and the expanded volume was deleted. This does not
establish that cleanup alone makes the default 60 GiB builder
sufficient.
- The Daytona admission probe then failed before agent creation, with
unconfirmed provider cleanup. The queued continuity case was canceled
because it shares this prerequisite. No artifact-transfer or continuity
success is claimed.
- Status-diagnostic regression: the original generic wrapper failed the
HTTP-status assertion. After the repair, the full plugin suite passes
501 tests, with 7 skips; its standalone TypeScript check passes.
Controls reject invalid status values, avoid accessors, preserve the
original create status and exclude credential canaries.
- Previous-head Greptile: 5/5; its summary names
`28fa001457f59df1c300c016dc569049e72d0061`. All review threads are
resolved.
- Live artifact transfer and three-turn warm continuity: NOT VERIFIED.
The diagnostic case in
https://github.com/paperclipai/paperclip/actions/runs/41277598289
confirmed Daytona HTTP 401 before agent creation. The configured
`DAYTONA_API_KEY` in the existing `runner-e2e-paid` environment needs
verification or refresh. The queued continuity control was not run after
its shared admission prerequisite failed. No further provider attempt is
planned until the credential changes. Unconfirmed provider cleanup
remains recorded.

## Risks

- Source-provenance changes can repeat the provider dependency install.
The combined build layer trades install-cache granularity for lower
retained disk use.
- Linux descriptor semantics and Node are required on the execution
host. Unsupported providers and restricted runtime profiles fail closed.
- Storage reads buffer at most 10 MiB. A provider outage can leave a
reserved staging file. Partial bytes never become the destination.
- A lost publication receipt can leave a complete file with an unknown
result. Recovery verifies the existing file without installation.
Conflicts are never overwritten.
- Artifact contents remain untrusted. The copy does not bypass screening
or authorize downstream execution.
- Live qualification uses the governed Codex API profile. It does not
establish every model, subscription profile, platform or historical
failure cause.

## Model Used

OpenAI GPT-6 in Codex assisted with diagnosis, code, tests and
qualification. The desktop runtime does not expose a more specific model
deployment ID or context-window size. The model used reasoning, source
inspection, tools and code execution. The native implementation agent is
configured with OpenAI `gpt-6.1-sol`.

## Checklist

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

---------

Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-11-02 08:27:09 -06:00
TonioandClaude Opus 5.5 8ea240218d feat(ui): bring the Refine-v2 layout to the agent page (#18378)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The agent page is where an operator reads and changes one agent.
> - The page grew one tab at a time. Ten tabs sit in one flat list, the
header holds many buttons, and the audit links leave the agent.
> - A design pass, called Refine-v2, made a calmer layout for this page.
It ran in a prototype on fixture data only.
> - This pull request moves the Refine-v2 layout and behavior into the
live agent page. Where the prototype and the live page differ, the live
behavior stays. For example, the floating save bar stays on screen.
> - The benefit is a shorter navigation, edits in place, and audit data
that stays inside the agent.

## Linked Issues or Issue Description

Refs #13716
Refs #9166

Both pull requests mount plugin tabs or slots on the agent page. They
touch the same files, so one of the three needs a rebase after the
others merge.

**Subsystem affected**

The agent detail page in `ui/` (`ui/src/pages/AgentDetail.tsx`, its
sidebar, header actions, and the shared agent config form).

**Problem or motivation**

The agent page is hard to scan. The navigation shows ten items in one
list. The header shows Run now, Pause, Clear error, and Assign task as
separate buttons. The name, title, and manager are form fields deep in
the Harness tab. Activity, Runs, Costs, and Budgets open company-wide
pages, so the operator loses the agent context. Each tab uses a
different card and table style.

**Proposed solution**

Use one layout system for every agent tab. Group the navigation into
Agent and Audit. Edit the name and title in the header. Move the work
actions into the actions menu. Show audit views inside the agent. Keep
the persistent save bar and add a status dot to it.

**Alternatives considered**

We considered a flag that lets operators opt in to the new layout. We
did not use a flag, because two layouts on one page double the test
surface. We also considered moving the save button to the end of the
scrolling content, as the prototype did. We kept the floating save bar,
because it is always visible.

**Roadmap alignment**

`ROADMAP.md` has no item for the agent page. This change is a UI
refinement of an existing page. It adds one optional field to an
existing API request and no database schema change.

## What Changed

- **Navigation.** The agent sidebar shows two groups. Agent has
Overview, Identity, Harness / Runtime, Secrets & variables, Permissions
/ Trust, and Advanced. Audit has Activity, Runs, Costs, and Budgets.
Skills, Tools, Channels, and API Keys leave the navigation. Their URLs
still open. The Instructions tab is now Identity, at `/identity`. The
old `/instructions` URL redirects.
- **Header.** The agent character shows at a larger size. The name and
title edit in place. Enter or blur saves. Escape cancels. An error
restores the old value. A rename updates the URL. While a tab has
unsaved changes, the header refuses the edit and asks the operator to
save or discard first. The third line shows the title and a link to the
manager. A crown button to the right of the name replaces the "Set as my
primary" link and the separate primary indicator. It is outlined and
dimmed until the agent is your primary, then fills with a short pop.
Replacing an existing primary asks for confirmation first, and the pop
plays after the dialog closes. Reduced motion skips the pop. Run now,
Pause or Resume, Clear error, and Agent history move into the actions
menu. Assign task is the primary button. For an agent that still needs
setup, Finish setup is the primary button and Assign task moves into the
menu.
- **Agent history.** A new dialog in the actions menu replaces the
Revisions entry. Each row shows a plain-language summary, the person or
agent who made the change, the date, and Restore. Restore asks for
confirmation. A failed restore shows its reason inside the confirmation.
- **Overview.** Four metric tiles show activity this week, runs this
week, costs this month, and budget used. Each tile links to its audit
view. When the list behind a count is capped, the tile shows a lower
bound, for example 200+. The activity tile follows live updates.
Profile, Skills, and Recent tasks panels follow. The Profile panel lists
direct reports under their label in at most two rows, then a "+N more"
button that expands the list. The Latest run card is removed.
- **Identity.** The file preview shows the raw text. A pencil button
switches to a plain text editor. Copy and Delete are icon buttons. A
chip next to the file name says "Managed by you" or "Managed
externally". Per-file history and unsaved edits stay.
- **External instructions.** "Advanced settings" opens Root path and
Entry file fields and a Load profile button. Load profile asks the
server to switch only if the root directory and the entry file already
exist. If either is missing, the server refuses and the panel shows the
reason. The entry file can have any name inside the root.
- **Instructions bundle API.** `PATCH /agents/:id/instructions-bundle`
accepts an optional `requireExistingEntry` flag (`packages/shared`
validator, `server` service, `ui` client). With the flag, the server
checks the root directory and entry file before it creates or copies
anything. Without the flag, the behavior does not change.
- **Harness / Runtime.** The config form gets a `panels` layout: Harness
and Execution side by side, then the runtime test, then environment
variables. The identity and run-policy sections are hidden from this
tab.
- **Secrets & variables and Advanced.** These tabs use the same filled
panels and table headers.
- **Permissions / Trust.** The trust presets show as selectable cards.
An Allowed connectors panel shows up to three connector logos and links
to Apps, filtered to installed connectors. Capabilities sit in their own
panel. Changes on this tab save at once, with no save bar.
- **Audit inside the agent.** Activity, Runs, Costs, and Budgets render
inside the agent page. Runs is locked to the current agent. A failed
budget incident action shows its reason under the incident.
- **Save bar.** The floating save bar stays. It shows an amber dot for
unsaved changes and a green dot with "Changes saved" after a save.
Discard stays.
- **Leave guard.** If the form has unsaved changes and the operator
clicks a link, a dialog asks to keep editing or discard the changes.
Browser back and the header actions keep the native confirm.
- **Apps.** `/apps?source=installed` opens the Apps page on the
Installed filter.
- **Tokens.** New `--agent-detail-*` tokens in `ui/src/index.css` hold
every spacing and fill value. Each fill is a mix of foreground into
background, so one value works in light and dark themes.
- **Tests and maps.** Tests cover the navigation model, sidebar, header
menu, Overview layout, Identity editor, and helpers. The
agent-configuration recipe in `maps/feature-map/agents.md` describes the
new page.

## Verification

- `cd ui && npx vitest run`: 849 test files and 10,081 tests pass.
- `cd server && npx vitest run
src/__tests__/agent-instructions-routes.test.ts
src/__tests__/agent-instructions-service.test.ts`: 40 tests pass.
- `npx playwright test --config tests/e2e/playwright.config.ts
tests/e2e/onboarding.spec.ts
tests/e2e/chat-adapters-ui-providers.spec.ts`: 26 tests pass.
- `cd ui && pnpm typecheck` passes. This includes the Storybook catalog
check. The `server` and `packages/shared` typechecks pass.
- `cd ui && pnpm build` passes.
- `pnpm run check:token-gates` reports all gates clean.
- `node scripts/check-maps.mjs` and `node scripts/storybook-catalog.mjs
--check` pass.
- Manual check on a local instance, in light and dark themes:
1. Open an agent. Confirm the sidebar shows the Agent and Audit groups.
2. Click the agent name in the header. Type a new name and press Enter.
Confirm the URL changes. Rename it back.
3. Open the actions menu. Open Agent history. Confirm the rows and the
Restore confirmation.
4. Open Identity. Click the pencil. Change a line. Confirm the save bar
shows the amber dot. Click a sidebar link. Confirm the discard dialog.
Click Keep editing, then save. Confirm the green dot.
5. Open Harness / Runtime, Secrets & variables, and Permissions / Trust.
Confirm the panels and the trust cards.
6. Open Runs under Audit. Confirm the list shows only this agent and the
page stays inside the agent.

## Risks

- This changes a high-traffic page for every operator at once. There is
no flag to switch back.
- Run policy and Reports to are no longer editable on the agent page.
The header shows the manager as a link only.
- Skills, Tools, Channels, and API Keys are reachable by URL only.
Operators who use them from the sidebar must use the URL.
- The pull request conflicts with #13716 and #9166 in `AgentDetail.tsx`
and the agent sidebar.
- Storybook stories for the agent page and `DESIGN.md` are not updated
in this pull request.
- The new `requireExistingEntry` flag is opt-in. Existing API callers
keep the old behavior, which creates a missing external directory and
entry file.
- I ran two e2e spec files locally, not the full `pnpm test:e2e` or
`pnpm test:release-smoke`. CI runs the full e2e suite. I did not run the
repo-wide `pnpm -r typecheck`, because the local toolchain has no Rust.

> 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
reasoning and tool use (shell, file edits, and a browser for visual
checks). A person directed every design decision.

## 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-01 22:27:09 -08:00