Commit Graph
1710 Commits
Author SHA1 Message Date
DottaandPaperclip 559644635d feat(claude): default qualified macOS and Linux targets to Runner
Enable Intel Mac, Linux x64, and Linux ARM64 after passing package consumers and the bounded API task/continuity pairs. Keep unsupported targets, overrides, and explicit Legacy choices intact.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 16:01:22 -05:00
Dotta 02ad4267d4 Merge branch 'codex/claude-runner-default' into codex/claude-runner-platforms
* codex/claude-runner-default:
  fix(adapter-utils): preserve SSH command positional arguments
  feat(runner): qualify native Codex platforms and cache hosted builds
  docs: establish paired feature and agent access maps (#16025)
  fix: retain matching-host runner development overrides
  fix(acpx): record turn activity at the moment a stop fires (#15962)
  refactor(chat): extract channel status, receipts, drafts and authority workflows (#15995)
  fix: preserve saved setup and match sandbox runner targets
  feat: add portable verification skills and refresh the feature map (#15983)
2026-10-12 15:29:24 -05:00
Dotta 14aed41d53 Merge branch 'codex/claude-runner-protocol' into codex/claude-runner-default
* codex/claude-runner-protocol:
  fix(adapter-utils): preserve SSH command positional arguments
  feat(runner): qualify native Codex platforms and cache hosted builds
  docs: establish paired feature and agent access maps (#16025)
  fix: retain matching-host runner development overrides
  fix(acpx): record turn activity at the moment a stop fires (#15962)
  refactor(chat): extract channel status, receipts, drafts and authority workflows (#15995)
  fix: preserve saved setup and match sandbox runner targets
  feat: add portable verification skills and refresh the feature map (#15983)
2026-10-12 15:29:12 -05:00
DottaandPaperclip b833c609cf fix(adapter-utils): preserve SSH command positional arguments
Keep shell argv intact and apply environment assignments after exec. Validate environment keys before constructing the remote command.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 15:29:01 -05:00
Dotta 7eb4f8bdba Merge remote-tracking branch 'origin/codex/codex-runner-default' into codex/claude-runner-protocol
* origin/codex/codex-runner-default:
  feat(runner): qualify native Codex platforms and cache hosted builds
  docs: establish paired feature and agent access maps (#16025)
  fix: retain matching-host runner development overrides
  fix(acpx): record turn activity at the moment a stop fires (#15962)
  refactor(chat): extract channel status, receipts, drafts and authority workflows (#15995)
  fix: preserve saved setup and match sandbox runner targets
  feat: add portable verification skills and refresh the feature map (#15983)
2026-10-12 15:20:10 -05:00
Dotta d3916151f6 Merge the standalone runtime prerequisite into Codex defaults
Co-Authored-By: Paperclip <noreply@paperclip.ing>

* codex/runner-native-platform-prerequisites:
  feat(runner): qualify native Codex platforms and cache hosted builds
  docs: establish paired feature and agent access maps (#16025)
  fix(acpx): record turn activity at the moment a stop fires (#15962)
  refactor(chat): extract channel status, receipts, drafts and authority workflows (#15995)
  feat: add portable verification skills and refresh the feature map (#15983)

# Conflicts:
#	doc/plans/2026-10-06-default-agent-runner.md
#	packages/paperclip-runner/README.md
#	server/src/__tests__/dev-runner-native-binary.test.ts
2026-10-12 15:16:51 -05:00
DottaandPaperclip ddcd746eb6 feat(runner): qualify native Codex platforms and cache hosted builds
Extract the tested runtime and installed-package prerequisite without changing agent creation defaults or experimental gates.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 15:15:01 -05:00
4b766b8729 fix(acpx): record turn activity at the moment a stop fires (#15962)
Record stop-time ACP turn diagnostics (last-event age, oldest pending tool age, pending tool kinds) so wall-clock timeout events show whether the turn was stalled. Diagnostics only.

Co-Authored-By: Paperclip <Paperclip-Paperclip@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:33:59 -07:00
DottaandPaperclip e79254b2fa fix: preserve saved setup and match sandbox runner targets
Share actual-target artifact selection across remote Codex setup and execution. Keep unchanged saved setup choices outside new-selection rules while retaining runtime and authentication validation. Update the served platform guide.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 14:12:14 -05:00
Dotta adf258ede2 Merge latest Codex qualification and master fixes
Co-Authored-By: Paperclip <noreply@paperclip.ing>

* codex/claude-runner-default: (25 commits)
  fix: pin ARM qualification helper to workflow source
  perf(ci): cache the Runner's release crate artifacts keyed by their inputs (#15932)
  perf(build): reuse a current Runner build in the server's prepare:runner-vendor step (#15926)
  fix(test): make the vitest home teardown remove read-only runtime-context bundles (#15931)
  fix(test): stop the Sentry timeout assertion matching a random event id (#15929)
  test: qualify Linux ARM with stock sandbox prerequisites
  docs(release): align 2026.1006.0-beta.0 stable notes header with v2026.1012.0 (#16000)
  fix: use Hermes adapter API URL env in prompt (#9425)
  fix(docker): skip the full-tree ownership walk on routine boots (#15992)
  refactor(chat): isolate Telegram, Slack, GitHub and Discord workflows (#15976)
  Preserve repository dependencies when importing GitHub skills (#15731)
  refactor(heartbeat): extract instruction working-copy lifecycle (#15984)
  test: retire redundant suites and consolidate owner coverage (#15978)
  refactor(heartbeat): extract runtime service coordination (#15980)
  refactor(heartbeat): extract execution configuration (#15973)
  refactor(heartbeat): extract run output recording (#15969)
  refactor(chat): extract failed-run retry coordination (#15845)
  refactor(chat): extract publication replacement decisions (#15844)
  refactor(chat): extract identity linking (#15836)
  refactor(heartbeat): extract session preparation (#15966)
  ...

# Conflicts:
#	packages/paperclip-runner/package.json
2026-10-12 13:45:03 -05:00
Dotta ed31211f0e Merge latest Codex qualification and master fixes
Co-Authored-By: Paperclip <noreply@paperclip.ing>

* codex/claude-runner-protocol: (25 commits)
  fix: pin ARM qualification helper to workflow source
  perf(ci): cache the Runner's release crate artifacts keyed by their inputs (#15932)
  perf(build): reuse a current Runner build in the server's prepare:runner-vendor step (#15926)
  fix(test): make the vitest home teardown remove read-only runtime-context bundles (#15931)
  fix(test): stop the Sentry timeout assertion matching a random event id (#15929)
  test: qualify Linux ARM with stock sandbox prerequisites
  docs(release): align 2026.1006.0-beta.0 stable notes header with v2026.1012.0 (#16000)
  fix: use Hermes adapter API URL env in prompt (#9425)
  fix(docker): skip the full-tree ownership walk on routine boots (#15992)
  refactor(chat): isolate Telegram, Slack, GitHub and Discord workflows (#15976)
  Preserve repository dependencies when importing GitHub skills (#15731)
  refactor(heartbeat): extract instruction working-copy lifecycle (#15984)
  test: retire redundant suites and consolidate owner coverage (#15978)
  refactor(heartbeat): extract runtime service coordination (#15980)
  refactor(heartbeat): extract execution configuration (#15973)
  refactor(heartbeat): extract run output recording (#15969)
  refactor(chat): extract failed-run retry coordination (#15845)
  refactor(chat): extract publication replacement decisions (#15844)
  refactor(chat): extract identity linking (#15836)
  refactor(heartbeat): extract session preparation (#15966)
  ...

# Conflicts:
#	tests/runner-e2e/README.md
2026-10-12 13:44:22 -05:00
Dotta 12f7b62115 Merge latest Codex qualification prerequisites and master fixes
Co-Authored-By: Paperclip <noreply@paperclip.ing>

* origin/codex/codex-runner-default: (25 commits)
  fix: pin ARM qualification helper to workflow source
  perf(ci): cache the Runner's release crate artifacts keyed by their inputs (#15932)
  perf(build): reuse a current Runner build in the server's prepare:runner-vendor step (#15926)
  fix(test): make the vitest home teardown remove read-only runtime-context bundles (#15931)
  fix(test): stop the Sentry timeout assertion matching a random event id (#15929)
  test: qualify Linux ARM with stock sandbox prerequisites
  docs(release): align 2026.1006.0-beta.0 stable notes header with v2026.1012.0 (#16000)
  fix: use Hermes adapter API URL env in prompt (#9425)
  fix(docker): skip the full-tree ownership walk on routine boots (#15992)
  refactor(chat): isolate Telegram, Slack, GitHub and Discord workflows (#15976)
  Preserve repository dependencies when importing GitHub skills (#15731)
  refactor(heartbeat): extract instruction working-copy lifecycle (#15984)
  test: retire redundant suites and consolidate owner coverage (#15978)
  refactor(heartbeat): extract runtime service coordination (#15980)
  refactor(heartbeat): extract execution configuration (#15973)
  refactor(heartbeat): extract run output recording (#15969)
  refactor(chat): extract failed-run retry coordination (#15845)
  refactor(chat): extract publication replacement decisions (#15844)
  refactor(chat): extract identity linking (#15836)
  refactor(heartbeat): extract session preparation (#15966)
  ...

# Conflicts:
#	tests/runner-e2e/pi-controls-evidence.test.ts
2026-10-12 13:43:39 -05:00
DottaandPaperclip 665c155472 Merge master and preserve qualified runner paths
Retain both hosted cache scopes. Use shared test-home teardown and restore owned directory permissions for the observed Mac ENOTEMPTY failure while keeping unrelated errors visible.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 13:42:01 -05:00
7df517d18f perf(build): reuse a current Runner build in the server's prepare:runner-vendor step (#15926)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Its CI runs a `Build` job on every pull request. That job runs `pnpm
build` and then verifies the public npm install. Since the server test
shards got faster, `Build` is the slowest check in most runs (419-497s
on the six fully green `master` runs created 2026-10-11 19:46Z to
20:17Z).
> - Inside `pnpm build`, the server package is the last package to
build. Its first step, `prepare:runner-vendor`, runs the full build of
`@paperclipai/paperclip-runner` so the server can vendor
`packages/paperclip-runner/dist`. In `pnpm -r build` that package was
built seconds earlier by the same command. The second build took 12-16s
on those six runs and sits on the critical path of the job.
> - The step cannot just be removed. A standalone `pnpm --filter
@paperclipai/server build` (the Dockerfile's path) must produce a
current vendored Runner on its own.
> - This pull request keeps that guarantee and skips only the build that
is provably redundant. The Runner's `build` records a digest of its
inputs, captured before its first step and confirmed after its last,
together with a manifest of every file it left under `dist`. The
server's `prepare:runner-vendor` reuses the build when the digest
matches, the stamp is for this platform, and every recorded output is
still there at its recorded size. Any other state runs the same full
Runner build as before.
> - The benefit is 12-16s off the `Build` job and off every other `pnpm
build` (the Canary Dry Run's `release.sh` build, local builds), with no
change to what a standalone server build produces. The PR's own first
run measured it: the server step reused the build in 56ms where `master`
runs rebuilt for 11-15s.

## Linked Issues or Issue Description

No GitHub issue exists for this. The description below follows the
enhancement template.

**What existing behavior does this improve?**

The server build's `prepare:runner-vendor` step, which builds
`@paperclipai/paperclip-runner` so the server can vendor its `dist`.

**Subsystem affected**

Build and CI (`server/package.json`,
`packages/paperclip-runner/package.json`, the `Build` job).

**Current behavior**

`prepare:runner-vendor` runs the Runner's full `build` every time.
Inside `pnpm -r build`, the Runner was already built by the same command
20 to 25 seconds earlier, with every check passing. On six fully green
`master` runs (2026-10-11, 19:46Z to 20:17Z) the repeated build took
11.8s to 15.9s, measured from the `server build$ pnpm run
prepare:runner-vendor` log line to the Runner's `Finished release
profile` line. The server package builds last, so that time is on the
critical path of `Build`, which is the slowest check in most pull
request runs.

**Proposed behavior**

- The Runner's `build` starts with `node scripts/record-build-inputs.mjs
--begin` and ends with `node scripts/record-build-inputs.mjs --finish`.
`--begin` removes the previous stamp and records a digest of every build
input before the first step reads anything. `--finish`, after every step
and check has passed, recomputes the digest and writes
`packages/paperclip-runner/node_modules/.cache/paperclip-runner-build-inputs.json`
only if the inputs are still the ones the build began with, together
with the path and size of every file under `dist`. A stamp therefore
exists only for a build that completed every step on inputs that did not
move while it ran.
- The server's `prepare:runner-vendor` becomes `node
scripts/prepare-runner-vendor.mjs`. It reuses the build if the stamp is
for this platform and architecture, every recorded output is still there
at its recorded size, and the recomputed digest matches. In every other
case it removes the stamp and runs `pnpm --filter
@paperclipai/paperclip-runner build`, the exact command the step ran
before.
- `PAPERCLIP_RUNNER_VENDOR_REBUILD=1` forces the rebuild.

**Reason and benefit**

12-16s off the `Build` job and off every other `pnpm build` (the Canary
Dry Run's `release.sh` build, local builds), with no change to what a
standalone `pnpm --filter @paperclipai/server build` produces.

**Breaking changes**

None. The rebuild path runs the same command as before. A fresh
checkout, a changed input, a missing or truncated output, an edit that
landed during the build, or a stamp from another platform always
rebuilds.

## What Changed

- `packages/paperclip-runner/scripts/build-inputs.mjs` (new): defines
the input set, the digest, the output manifest and the reuse decision.
Inputs are every file under `packages/paperclip-runner` except `dist`,
`dist-browser`, `runner/target`, `node_modules` and dot-directories;
every file under `packages/paperclip-eval-kernel` except `dist` and
`node_modules` (the Runner's `build:typescript` builds it first); every
file under the root `patches` directory (`check:acpx-profiles` hashes
`acpx@0.13.1.patch`, and `build-verified-provider-entrypoints.mjs`
copies the `pi-acp` and `brace-expansion` patches into
`dist/cli/pi-provision-inputs`); and the root `package.json`,
`pnpm-lock.yaml`, `pnpm-workspace.yaml` and `tsconfig.base.json`. The
toolchain pins are inside the package (`rust-toolchain.toml`,
`runner/Cargo.lock`) and the TypeScript version is in the lockfile, so
both are covered. A symlink is hashed as its target path and is not
followed. Outputs are every file under `dist`, the tree the server
copies wholesale, recorded with their sizes. Exports `beginRunnerBuild`,
`recordRunnerBuild` and `assessRunnerBuild` (returns `current` plus a
stable reason code: `no_stamp`, `schema_mismatch`, `platform_mismatch`,
`missing_output`, `outputs_changed`, `inputs_changed`,
`inputs_unchanged`).
- `packages/paperclip-runner/scripts/record-build-inputs.mjs` (new): the
two phases, `--begin` and `--finish`, added as the first and last step
of the Runner's `build` in `packages/paperclip-runner/package.json`.
Each build chain has its own pending record under
`node_modules/.cache/paperclip-runner-build-inputs.pending/`, named
after the shell pnpm runs the chain in (the parent of both phases;
`--finish` also accepts a record named after its own pid, for a shell
that execs the last command of the chain), so two Runner builds running
at once in one checkout do not consume each other's record. `--finish`
without a pending record for its chain is an error, so the chain cannot
silently lose its first step; `--finish` after an edit that landed
mid-build prints `not recorded: inputs changed while the build ran` and
leaves no stamp. Records of builds that never finished are pruned by the
next `--begin` after a day.
- `server/scripts/prepare-runner-vendor.mjs` (new): the reuse-or-rebuild
step. It resolves the pnpm that is running it through `npm_execpath`
with the same rule as the CLI's `resolvePnpmInstallInvocation` (a
JavaScript entry runs through this Node, a native executable runs
directly, otherwise `pnpm` on PATH). It prints one
`paperclip.runner-vendor.timing.v1` line with `action`, `reason`,
`check_ms` and `build_ms`, so the saving is measured on this PR's own
`Build` job.
- `server/package.json`: `prepare:runner-vendor` now runs that script.
The `build` and `typecheck` scripts are unchanged.
- `server/src/__tests__/server-package-build-script.test.ts`: pins the
new indirection and the command behind it (imported from the script, so
the Dockerfile's standalone path stays pinned), and a new test asserts
the Runner's `build` starts with `--begin`, ends with `--finish`, and
has exactly those two record steps.
- Tests: `packages/paperclip-runner/scripts/build-inputs.test.mjs`
(node:test, added to `test:typescript:prep`) and
`server/scripts/prepare-runner-vendor.test.mjs` (vitest, in the server
lane).
- `doc/DEVELOPING.md`: a "Reused Runner build in the server build"
section under Test Commands.

## Review findings on the first head, all fixed in
[2d57542](https://github.com/paperclipai/paperclip/pull/15926/commits/2d575425977f87b4612222d0a1961bc9cc5ba569)

Greptile scored the first head 2/5 with three P1 findings. All three
were valid.

| Finding | Fix |
| --- | --- |
| **Patch edits leave stale output.** The root patch files the build
reads were not in the digest. | Every file under the root `patches`
directory is an input. Tests assert the patches are in the input list
and that editing the pi-acp patch, the acpx patch, or adding a patch
changes the digest. |
| **Missing helpers pass the build.** Only four outputs were checked, so
a `dist` that lost `dist/cli/acpx-runtime-sidecar.cjs` still counted as
current. | `--finish` records the path and size of every file under
`dist`; reuse requires all of them intact and reports `outputs_changed`
naming the first missing or resized file. Schema bumped to
`paperclip.runner-build-inputs.v2` so an older stamp is rejected. Tests
cover a deleted helper, a truncated helper, and the server step
rebuilding on `outputs_changed`. |
| **New edits count as compiled.** The digest was hashed after the
build, so a source edited while `build:binary` ran was recorded as
compiled. | The build is bracketed: `--begin` captures the digest before
the first step, `--finish` compares after the last and writes nothing if
it moved. Tests cover the mid-build edit, the undisturbed build, and the
CLI in both orders. |

## Review finding on the second head, fixed in the third commit

Greptile scored the second head 4/5 with one P1, valid: two Runner
builds running at once in one checkout (server `build` beside
`typecheck`) shared one pending record, so the second `--begin`
overwrote it, the first `--finish` consumed it, and the second
`--finish` threw `no pending build inputs` after every step of its build
had passed. Reproduced on the second head's module: two
`beginRunnerBuild`, then the first `recordRunnerBuild` records and the
second throws.

| Finding | Fix |
| --- | --- |
| **Overlapping builds fail each other.** One pending file for every
build chain. | One record per chain, named after the shell pnpm runs the
chain in: `--begin` sees it as its parent, and so does `--finish`,
unless the shell execs the last command of the chain (bash can), in
which case `--finish` is that shell and finds the record under its own
pid. Pending records of builds that never finished are pruned after a
day. An edit between two overlapping starts leaves no stamp whichever
build finishes last: the build that began before the edit reports
`inputs_changed_during_build` and removes the stamp the other wrote, so
a stamp always means every build that wrote `dist` read the inputs it
names. Tests: both finishing orders with and without an edit, the
pruning, a `--finish` that cannot use another chain's record, and two
real `sh -c` chains (plus one under `bash`) held open at the same time,
both recording. On the real repo, two `pnpm --filter
@paperclipai/paperclip-runner build` chains started 8s apart with a
cargo stand-in both recorded (`files=1223 outputs=1434`) and left the
pending directory empty. |

## Measured on this PR's runs

The `Build` job ran `pnpm build` from the branch, so both builds of the
Runner in that job used the new chain.

First head (c23faab, run 38175281058), against the four fully green
`master` runs created 20:41Z to 21:37Z the same evening:

| | PR run 38175281058 | `master`, four runs |
| --- | --- | --- |
| Runner build inside `pnpm -r build` | 55.3s | 47.3-62.0s |
| Server step `prepare:runner-vendor` |
`action:reuse,reason:inputs_unchanged,check_ms:56,build_ms:0` | full
Runner rebuild, 11.0-14.8s |
| Server package build | 29.7s | 32.3-43.5s |
| `pnpm build` step | 114s | 102-135s |
| `Build` job | 411s | 409-474s |

Second head (2d57542, run 38178686999), against the two fully green
`master` runs created in the same half hour (38178951607 and
38180574768):

| | PR run 38178686999 | `master`, two runs |
| --- | --- | --- |
| Runner build inside `pnpm -r build` | 53.9s (`begin:` 22:03:04,
`recorded:` 22:03:57, digest 460e02616081 over 1231 files and 1434
outputs) | 62.2s, 64.6s |
| Server step `prepare:runner-vendor` |
`action:reuse,reason:inputs_unchanged,check_ms:109,build_ms:0` | full
Runner rebuild, 14.5s, 15.2s |
| Server package build | 27.4s | 43.5s, 44.8s |
| `pnpm build` step | 106s | 91s, 95s to the Runner's end plus the rest;
127s, 135s in all |
| `Build` job | 405s | 461s, 479s |

The rebuild is gone from the server build on both heads, and the check
costs 56-109ms where `master` rebuilds for 11-15s; the rest of the
spread between runs is runner speed on the `runs-on` fleet. The third
commit changes nothing on the reuse path: it only changes where the
pending record lives between `--begin` and `--finish`.

## Verification

- `node --test packages/paperclip-runner/scripts/build-inputs.test.mjs`:
11 tests, three consecutive runs clean. Two builds at once in one
checkout each keep their own pending record and both record, in either
finishing order; with an edit between their starts the build that began
before it leaves no stamp whichever finishes last. A build's start
prunes records older than a day and no others. A `--finish` cannot use
another chain's record. Two chains run by separate `sh -c` shells at the
same time (and one under `bash`) both record and leave the pending
directory empty. The input set is exactly the sources, the eval kernel,
the root patches and the root config, and skips outputs and caches. The
output manifest lists every file under `dist` with its size and nothing
outside it. The digest is stable across reads, does not change when
`dist`, `dist-browser`, `node_modules`, `.vite` or `runner/target`
change, and does change for a Runner source, a Rust source, an
eval-kernel source, the pi-acp patch, the acpx patch, a new patch, the
lockfile, the base tsconfig, a new file and a rename. A symlink changes
the digest but its target's files are not read. A build is current only
with a matching v2 stamp, the same platform and architecture, the four
named outputs present, and every recorded output intact (a deleted or
truncated helper is `outputs_changed`; a file the build did not produce
may appear). An input edited between `--begin` and `--finish` leaves no
stamp and removes the pending record; the next build records normally.
`--finish` without `--begin`, or after a build that left a required
output missing, throws and leaves no stamp. The CLI brackets the build
for the package it is pointed at, reports a mid-build edit, and rejects
`--finish` first, both flags, neither flag, and unknown arguments.
- `vitest run scripts/prepare-runner-vendor.test.mjs
src/__tests__/server-package-build-script.test.ts` in `server/`: 21
tests. The decision reuses a current build, rebuilds a stale one, and
rebuilds a current one only when `PAPERCLIP_RUNNER_VENDOR_REBUILD=1`.
The pnpm resolver covers a JavaScript entry, a native executable, a
Windows executable, no `npm_execpath`, and npm's `npm_execpath`. With an
injected build command: a current stamp runs nothing and prints
`action:reuse`; changed inputs run the exact command from `server/` with
the stale stamp already removed; a lost
`dist/cli/acpx-runtime-sidecar.cjs` rebuilds with
`reason:outputs_changed`; a failing build returns its status and leaves
no stamp. End to end, the CLI with a stand-in `pnpm` on PATH that
brackets its build the way the real one does rebuilds on the first call,
reuses on the second, rebuilds after a source change, rebuilds after a
lost output, and reuses again. The guard test asserts
`prepare:runner-vendor` is the new script, the rebuild command is still
`pnpm --filter @paperclipai/paperclip-runner build`, the server build
still starts with the step, and the Runner's `build` starts with
`--begin` and ends with `--finish` with exactly two record steps; it
fails against the previous build script on its first assertion.
- Real repo, this checkout, with `cargo` and `pnpm` stand-ins on PATH
(no Rust toolchain here; the stand-in writes the one file
`stage-runner-binary.mjs` copies): `pnpm --filter
@paperclipai/paperclip-runner build` ran the complete chain through the
real nested pnpm (every TypeScript step and check, the stand-in cargo,
the binary staging, the two binary-reading checks) and recorded
`files=1223 bytes=18735059 outputs=1434`; the stamp is 128 KB. `pnpm run
prepare:runner-vendor` in `server/` then printed
`action:reuse,reason:inputs_unchanged,check_ms:222,build_ms:0`.
Appending a newline to `patches/pi-acp@0.0.33.patch` made the assessment
`inputs_changed`; truncating or deleting
`dist/cli/acpx-runtime-sidecar.cjs` or
`dist/cli/opencode-app-server-proxy.cjs` made it `outputs_changed` in
1-5ms; restoring each made it current again. `--begin`, an edit to
`src/index.ts`, then `--finish` printed `not recorded: inputs changed
while the build ran` and left no stamp.
- The whole check on the real set (1223 inputs, 18 MB; 1434 outputs)
takes 165-227ms on this host and took 56ms on the CI runner; the output
manifest part is 1-5ms.
- Not run here: the real `cargo build`, and `pnpm build` end to end.
This PR's `Build` job runs `pnpm build` from the branch, so its log
carries the Runner's `begin:` and `recorded:` lines and the server
build's `paperclip.runner-vendor.timing.v1` line with the measured
result.

## Risks

- A build input outside the digest would let a stale Runner be vendored.
The digest covers the whole Runner package except its outputs, the eval
kernel the Runner builds first, the root patches, and the root files the
Runner's tsconfig and dependency graph read. The lockfile covers
dependency versions, and the pins inside the package cover the Rust
toolchain. An input missed here is a bug to fix by adding it to the set;
the fallback direction is always a rebuild, never a skip.
- The whole `patches` directory is an input, although the Runner build
reads three of its fourteen files. An edit to an unrelated patch
therefore costs one extra Runner rebuild, which is the safe direction
and avoids a hand-kept list of file names that could drift from the
scripts that read them.
- The stamp lives in `node_modules/.cache`, which pnpm does not manage
and `.gitignore` and `.dockerignore` already exclude. It is about 128 KB
with the output manifest. The Docker build has no stamp and no prior
Runner build, so it rebuilds exactly as before.
- A stamp is written only after the Runner's `build` passed every step
on inputs that did not change while it ran. The Runner's first step and
the prepare step both remove any existing stamp before a rebuild starts,
so a build that stops partway, or a build command that fails before its
first step, leaves no stamp.
- `node scripts/record-build-inputs.mjs --finish` on its own is an
error; the script is only meant to run as the two ends of the `build`
chain, and the guard test pins that.
- The pending record is keyed by the pid of the shell that runs the
chain. pnpm runs every script through a shell (`sh -c` on POSIX, `cmd`
on Windows), and both phases are children of that shell; a shell that
execs the last command of the chain is covered by matching the finishing
process's own pid too. A chain run some other way, with the two phases
under different parents, fails loudly at `--finish` rather than
recording. Two builds running at once still write the same `dist`, which
no stamp can make safe; what the record guarantees is that a stamp is
written only by a build whose own inputs did not move.
- The platform and architecture are part of the comparison, so a stamp
from another machine is never trusted; an older v1 stamp is rejected by
schema.
- The Typecheck job builds the Runner once, through this step, with no
prior `pnpm -r build`, so it gains nothing and loses nothing: there is
no stamp and it rebuilds as before.
- The step now spawns pnpm through `npm_execpath` instead of relying on
`pnpm` being on PATH, because under corepack the script shell has no
`pnpm`. Outside a pnpm script it still falls back to `pnpm` on PATH.

## Model Used

- Claude (Anthropic), model id `claude-fable-5-1` (Claude Fable 5.1),
extended thinking, with tool use (shell, file editing, GitHub CLI) in a
Paperclip agent 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
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

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

---------

Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 11:25:24 -07:00
DottaandPaperclip 0690801f13 test: qualify Linux ARM with stock sandbox prerequisites
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 13:13:19 -05:00
DottaandPaperclip 3fc2af5511 Merge Claude Auto effort and implicit harness fixes
Co-Authored-By: Paperclip <noreply@paperclip.ing>

* codex/claude-runner-default:
  fix(claude): honor Auto effort and implicit native harness settings
2026-10-12 13:13:05 -05:00
DottaandPaperclip 4dc13aa32c fix(runner): match image and native provider pack contents
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 13:13:05 -05:00
DottaandPaperclip 23d834fc0a fix(claude): honor Auto effort and implicit native harness settings
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 13:12:58 -05:00
DottaandPaperclip 4c7e0538c3 Merge the reviewed compact question envelope fix
Co-Authored-By: Paperclip <noreply@paperclip.ing>

* codex/claude-runner-default:
  fix(runner): point v7 question answers to the compact envelope
2026-10-12 12:41:48 -05:00
DottaandPaperclip 8cd1212d08 Merge reviewed v7 question-answer correction
Co-Authored-By: Paperclip <noreply@paperclip.ing>

* codex/claude-runner-protocol:
  fix(runner): point v7 question answers to the compact envelope
2026-10-12 12:40:55 -05:00
DottaandPaperclip bd966ff9cd fix(runner): point v7 question answers to the compact envelope
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 12:40:52 -05:00
2145925f71 fix: use Hermes adapter API URL env in prompt (#9425)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Hermes adapter builds the command environment and renders the
prompt that tells Hermes how to call back into Paperclip.
> - Adapter configuration can provide `PAPERCLIP_API_URL` through
`config.env`, which is often the most precise API base for the spawned
worker.
> - The current prompt rendering path does not read that adapter env
value when deciding which Paperclip API base to show.
> - This can leave Hermes with prompt instructions that point at a less
appropriate process-level or public URL, even though the adapter env
contains the intended API base.
> - This pull request makes prompt rendering honor the adapter env value
before falling back to broader process defaults.
> - The benefit is a more consistent Hermes prompt without changing
command execution behavior.

## Linked Issues or Issue Description

Adapter: Hermes

Agent or provider

Hermes local adapter.

Why this adapter is useful

The Hermes adapter lets Paperclip run Hermes-backed agents while
preserving Paperclip run context and callback instructions.

How the agent is invoked

Paperclip spawns the configured Hermes command and passes a rendered
prompt plus adapter-provided environment variables.

What happened?

When `PAPERCLIP_API_URL` is supplied through adapter env, the Hermes
subprocess receives it, but the prompt can still render its API base
from a process-level fallback instead of that configured adapter env
value.

Expected behavior

If an explicit `paperclipApiUrl` option is not configured, the prompt
should prefer `config.env.PAPERCLIP_API_URL` when present, then fall
back to the existing defaults.

Related context: supersedes NousResearch/hermes-paperclip-adapter#118
for Paperclip's bundled Hermes adapter.

## What Changed

- Added a small env-value resolver for raw string values and `{ value:
string }` wrappers.
- Builds a prompt-specific config that uses
`config.env.PAPERCLIP_API_URL` when `paperclipApiUrl` is not explicitly
set.
- Added a focused command-resolution test that verifies the rendered
prompt API base uses the adapter env URL instead of the process
fallback.

## Verification

- `pnpm --filter @paperclipai/hermes-paperclip-adapter
test:command-resolution`
- `pnpm --filter @paperclipai/hermes-paperclip-adapter typecheck`
- `pnpm --filter @paperclipai/hermes-paperclip-adapter build`

## Risks

Low risk. This only affects prompt rendering for Hermes adapter runs
where `paperclipApiUrl` is unset and `config.env.PAPERCLIP_API_URL` is
present. Explicit `paperclipApiUrl` values keep precedence, and existing
fallback behavior remains in place when the env value is absent.

## Model Used

OpenAI Codex, GPT-5.5, tool-enabled coding session with local shell,
Git, and GitHub CLI validation.

## 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: zocomputer <help@zocomputer.com>
Co-authored-by: commitperclip[bot] <284812584+commitperclip[bot]@users.noreply.github.com>
2026-10-12 10:39:23 -07:00
DottaandPaperclip d0da37c221 feat(runner): assemble target-owned Claude Linux provider packs
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 12:37:57 -05:00
DottaandPaperclip 18626f677f Merge reviewed Claude behavior into platform qualification
Co-Authored-By: Paperclip <noreply@paperclip.ing>

* codex/claude-runner-default:
  docs: link the approved protocol review boundary
  fix(runner): retain versioned profiles in Docker context
  docs: record Claude review fixes and protocol prerequisite
  fix(claude): preserve permissions and connection claims at creation
  feat(runner): admit Claude effort before native prompts and resume
  fix: preserve TOML table semantics during Codex startup trust
2026-10-12 12:30:54 -05:00
DottaandPaperclip 12f30702d7 Merge branch 'codex/claude-runner-protocol' into codex/claude-runner-default
* codex/claude-runner-protocol:
  feat(runner): admit Claude effort before native prompts and resume
  fix: preserve TOML table semantics during Codex startup trust

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 12:24:59 -05:00
DottaandPaperclip 6263a47185 fix(claude): preserve permissions and connection claims at creation
Retain native import settings, explicitly select Legacy in qualification, pin updated CLI fixtures, and version shared source attestations.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 12:24:17 -05:00
DottaandPaperclip 2e8d5e906e feat(runner): admit Claude effort before native prompts and resume
Version the shared Pi and pending Copilot source attestations without changing their executable closures or platform qualification.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 12:24:15 -05:00
DottaandPaperclip a200495cc7 fix: preserve TOML table semantics during Codex startup trust
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 12:14:38 -05:00
DottaandPaperclip c860863295 feat(runner): select verified Claude Linux ARM64 artifacts
Automatic defaults remain gated until installed execution qualification passes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 12:08:07 -05:00
DottaandPaperclip 966b3d5bc2 Merge latest Codex platform qualification into Claude default
Preserve Codex GNU libc eligibility and Linux ARM64 behavior; keep Claude automatic selection limited to Apple Silicon.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 11:48:10 -05:00
DottaandPaperclip 5a60820588 fix(runner): acknowledge Claude cold session resume
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 11:42:37 -05:00
Dotta a9830414c7 Preserve repository dependencies when importing GitHub skills (#15731)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Its Skills library imports reusable instructions from GitHub.
> - Some skills depend on shared files and sibling skills outside their
folders.
> - Directory-only imports lose those dependencies.
> - This pull request keeps repository files together and lets users
select which skills appear in the library.
> - Agents receive the original files and paths through the existing
skill delivery system.

## Linked Issues or Issue Description

Refs #14713. Related: #13656.

**Current behavior**

GitHub imports copy independent skill directories. Shared scripts,
policies, helper skills, and credit files can be omitted. One
shared-file error can block every skill.

**Proposed behavior**

New imports keep repository files together at the scanned commit. Show
all skills with their folders and select every skill by default.
Collapse individual package files. Omit unsupported or oversized files
with a warning. Import available content without repair choices or an
extra confirmation. Keep warnings on the saved source.

**Reason and benefit**

Skills can read their shared repository files after Paperclip delivers
them to an agent. Original instructions stay unchanged. Users can
inspect included files and understand omissions before and after import.

## What Changed

- Store immutable company-scoped repository snapshots and associate them
with skill versions. Regenerate the migration after merging master.
- Preserve original bytes, binary encoding, and executable modes.
Shared-file changes version each usable selected skill. Pinned versions
retain their original files.
- Deliver a discovery wrapper with the original tree under
`.paperclip-repository/`. Preserve invocation policy when present. Fork
and create-from-existing retain repository files.
- Keep repository files together for new UI imports. Show every skill,
select all by default, and collapse package files. Remove the
packaging-mode menu. Keep the mobile import footer visible.
- Warn about symlinks, submodules, oversized files, missing references,
and invalid optional manifests. A malformed skill remains selected with
a Needs setup warning. Usable skills still publish, and its original
file remains repository support content. Source warnings persist through
refresh. Unsafe execution and secret-disclosure findings omit the
affected files before snapshot publication.
- Expose saved repository files in the installed skill viewer, including
binary encoding and executable flags. Reads use the immutable
company-scoped snapshot; relative instruction links resolve from their
original repository directory.
- Audit delivered inventories and derive trust from all repository
files. Accept evaluation prose while retaining dynamic-execution
findings. Read blobs in one bounded Git batch and check budgets before
reading support files. Imports never execute setup commands.
- Add scanner, database, delivery, UI, and browser regressions. Update
Storybooks, screenshots, and documentation.

## Verification

- 69 focused scanner, Git snapshot, delivery, and PostgreSQL lifecycle
tests passed. They cover partial imports, warning persistence, missing
references, binary files, executable modes, copies, pinned versions,
cancellation, company isolation, and batched Git reads.
- 110 import, skill-detail, selection-tree, saved-file link, and
Markdown renderer tests passed. A file-switch regression failed before
the resolver fix and passed afterward. Browser walkthroughs cover the
all-selected open tree, repository preview, non-blocking warnings,
missing references, setup problems, warning persistence, and mobile
layout.
- Full workspace typecheck and build passed. Runner E2E TypeScript
checks and design-token gates passed. The full serial local suite was
attempted and stopped after over an hour. The existing 40,000-file Git
streaming stress test failed after 303 seconds on macOS; the broad local
suite is not counted as passed. All 55 applicable PR checks passed on
the final commit, including the full sharded CI suite. Two optional
Storybook workflows were skipped; the five local Storybook walkthroughs
passed.
- On October 12, live campaigns imported the private Poteto repository
through the production GitHub Sources UI, selected architect and bro,
and invoked them with native Codex on local and Daytona. The source
connection was disabled before invocation. Independent filesystem
inspections matched every supported Git blob, size, and executable mode.
Both tasks completed and cleanup passed. Campaigns:
`local-2026-10-12T15-07-39-504Z` (both environments),
`local-2026-10-12T16-08-39-682Z` (local), and
`local-2026-10-12T16-03-33-938Z` (Daytona). Each of the two selected
skills received all 234 supported repository files. Later changes only
affect the installed-file viewer and documentation. This qualifies skill
delivery and instruction reads, not every Poteto workflow or adapter.
- A manual source-checkout test drive imported a synthetic private
GitHub repository with a symlink, an oversized file, a missing
reference, and malformed skill metadata. Available skills published with
their shared checklist. The walkthrough found duplicate warning text
after import; the final UI fix removes that duplicate. The file viewer
exposes saved repository dependencies and resolves authored relative
links without changing the instructions. A follow-up live refresh
omitted unsafe helper files with warnings, preserved every safe file
byte, and survived a server restart.

Screenshots below use the production UI with Storybook fixtures. They do
not replace the live GitHub and runtime checks.

<details>
<summary>All skills visible and selected by default</summary>

![Default repository
import](https://github.com/paperclipai/paperclip/blob/codex/skill-repository-packages/docs/pr-screenshots/pr-15731/repository-import.png?raw=true)

</details>

<details>
<summary>Import stays available with warnings</summary>

![Import
warnings](https://github.com/paperclipai/paperclip/blob/codex/skill-repository-packages/docs/pr-screenshots/pr-15731/repository-warning-details.png?raw=true)

</details>

<details>
<summary>Repository file inspector</summary>

![Repository
files](https://github.com/paperclipai/paperclip/blob/codex/skill-repository-packages/docs/pr-screenshots/pr-15731/repository-files.png?raw=true)

</details>

<details>
<summary>Mobile import</summary>

![Mobile
import](https://github.com/paperclipai/paperclip/blob/codex/skill-repository-packages/docs/pr-screenshots/pr-15731/repository-mobile.png?raw=true)

</details>

## Risks

- Each selected skill carries a repository copy in the runtime. The
database stores each snapshot once. Runtime transfer and storage
increase with the number of assigned skills.
- Runtime budgets remain: 9,990 repository entries, 1 MiB per file, and
61 MiB total. File omissions are recorded as warnings. Source-access or
download failures retain installed versions.
- Import success does not prove that every skill can run. Environment
dependencies, credentials, delegation tools, and schedules still need
configuration. Imported setup scripts are not executed.
- Live runtime coverage is native Codex on local and Daytona. Other
adapters use the shared delivery path but were not exercised in these
campaigns.

The roadmap marks Skills Manager as shipped. This extends the existing
importer. Related pull requests were checked.

## Model Used

OpenAI Codex, based on GPT-6, with repository inspection, code
execution, automated tests, and browser tools. The exact deployment
identifier 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 (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-10-12 11:30:20 -05:00
DottaandPaperclip 123a188374 fix: separate Codex login and ARM host qualification
Keep daemon ABI admission separate from CLI login and preserve restricted Docker installation checks before native host startup.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 11:20:36 -05:00
DottaandPaperclip 695506ad39 perf: cache native builds and verify Linux runtime targets
Preserve actual target selection, record the GNU libc baseline, and retain primary installed-startup failures for ARM qualification.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 10:53:24 -05:00
DottaandPaperclip 5f0b898e9e merge: reconcile latest Codex runner target qualification
Preserve the user-approved Codex Linux ARM64 default and keep Claude automatic selection limited to Apple Silicon.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 10:50:42 -05:00
DottaandPaperclip 6fd56c445d feat: default Claude to the native runner on Apple Silicon
Carry Claude effort through versioned native admission and recovery. Preserve existing connections and saved runner choices, and price Opus 5.5 API receipts.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 10:48:21 -05:00
DottaandPaperclip 1e0cfcb4fc test: retire redundant suites and consolidate owner coverage (#15978)
Retire duplicate ACPX and Codex proof after migrating unique assertions to their behavioral owners. Consolidate repeated fixtures and exact case matrices while preserving lifecycle, protocol, company, credential, approval, and ordering contracts. Remove the unused runtime-controls compatibility path and document test ownership and preservation evidence.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 10:44:02 -05:00
DottaandPaperclip dcdf09a0f3 feat: include Linux ARM64 in native Codex runner targets
Ship a source-bound ARM64 daemon alongside Linux x64 and both Mac targets.
Use native hosted builds and the existing clean npm/native startup verifier.
Read target requirements from the immutable source to preserve historical
three-target promotions. Keep Codex payloads as official npm dependencies.

Resolve ARM64 creation and login through the shared platform contract. Share
the existing legacy CLI permission calculation with import previews without
changing execution behavior. Record targeted checks and live Mac evidence.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 10:13:34 -05:00
DottaandPaperclip 058af8f1b6 fix: preserve release artifacts during dev builds and display imported policy
Keep source-built daemons separate from source-bound release targets and select
fresh compatible source output only inside the development supervisor. Preserve
release manifests, running executable inodes, and explicit operator overrides.

Show the existing translated Codex permission policy in import controls while
preserving runner-only override payloads. Record real Apple Silicon task and
paused import proof alongside exact qualification sources.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 09:32:28 -05:00
DottaandPaperclip d522822a5d fix: preserve imported runner choices and qualify Mac install fixtures
Forward explicit import runner overrides, preserve native harness identity without experimental Codex gating, refresh existing host daemon outputs atomically, and retain strict public install probes with target paths and the executing Node SDK.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 08:55:54 -05:00
DottaandPaperclip f4abdc2cfc Merge master b189f586d into Codex runner default candidate
Preserve master transport, lifecycle, and release verification controls alongside macOS qualification.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 07:55:09 -05:00
DottaandPaperclip 7b8d29a856 feat: qualify macOS Codex runner defaults and release artifacts
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 07:48:52 -05:00
DottaandPaperclip db2f00947b fix(runner): include Claude ACP in the server package (#15912)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The server ships a copy of Paperclip Runner for native agent
execution.
> - The Claude ACP bridge is a runner dependency, but it was absent from
the server package.
> - The runner resolves this bridge dynamically, so the import-based
packaging check missed it.
> - A Claude session then failed before startup and showed a generic
connection error.
> - This pull request ships the qualified bridge and reports a missing
runtime with repair steps.

## Linked Issues or Issue Description

**What happened?**

A server install with the vendored runner could not load
`@agentclientprotocol/claude-agent-acp/package.json`. The runtime test
suggested checking the model or provider connection. The task reported
an interrupted runner session.

**Expected behavior**

The server package includes its qualified Claude ACP runtime. A missing
bundled runtime reports an installation error and explains how to repair
it.

**Steps to reproduce**

1. Use a server install with the vendored runner and no Claude ACP
bridge.
2. Select the Paperclip Runner ACP harness and the Claude provider.
3. Run the runtime test or open a session.
4. Observe a missing-module failure before Claude starts.

**Paperclip version or commit**

Reproduced on `e733bddc45`. This branch is based on current master.

**Deployment mode**

Self-hosted server built from source.

Related: #15224 upgrades the Claude bridge. This PR fixes its omission
from the server package and keeps the current qualified version. A
future bridge upgrade must update the server pin too.

## What Changed

- Add Claude ACP `0.73.0` to the server dependencies and bundled
dependencies.
- Pin the qualified Claude SDK and bridge dependencies in the npm bundle
producer graph. Publish its native packages as consumer optional
dependencies instead of shipping the producer executable.
- Bind source-built vendored runners to the shared pnpm dependency store
while retaining the server manifest as the provider authority. Keep
published npm and self-rooted deployment roots unchanged.
- Include dynamically resolved Claude and Codex bridges in the vendor
dependency check.
- Preserve the missing-package category through the sidecar, Rust
transport, and native session executor. Stop automatic retries for this
installation failure.
- Show **Runtime missing** in the runtime test and task thread. Explain
the reinstall or rebuild, server restart, and runtime test steps.
- Add regression tests and document the repair procedure.

## Verification

- Red/green comparison with the production sidecar: the same
missing-package setup returns a raw require-stack error on the original
code and an actionable, non-retryable missing-runtime error on this
branch.
- The rebuilt server's vendored Claude installation probe passes using
the source-server authority selected by the launcher, without starting a
provider or making a model request.
- Focused server verification: 830 tests passed. Runner installation and
sidecar error tests: 83 passed, with 6 platform-specific skips.
Packaging tests: 28 passed. Rust transport tests: 7 passed.
- Source/deployment authority tests: 3 passed. Red/green packaging tests
include a Linux-produced Claude SDK bundle, actual npm pack, and closed
executable admission on Linux x64 and macOS ARM64/x64.
- Full UI suite: 8,113 tests passed.
- `pnpm -r typecheck` and `pnpm build` passed on the rebased branch.
- Full current-head CI passed, including all general and serialized test
lanes, Runner Rust/Vitest verification, typecheck, build, and all eight
e2e shards. Greptile reviewed `d6a2741834ff922956560f2f689382827bbaf9e0`
at 5/5 with no unresolved threads.
- Local full-suite limitation: an earlier parallel attempt hit Postgres
startup failures and timeouts; the affected server tests and a database
fixture passed in isolation. The subsequent serial `pnpm test:run` was
stopped while still running after complete CI passed. Focused local
tests, full UI tests, typecheck, and build completed as listed above.
- Token gates and `git diff --check` passed.

## Risks

- Bundling the Claude bridge increases the server package size. The
producer graph retains the qualified SDK version and existing isolation
patch. Native platform declarations move to the published server
manifest, while the runtime keeps its exact version, declared-slot,
no-follow, and executable digest checks.
- The new failure category is permanent. Operators must repair the
server installation before they retry the task.
- Existing failed runs retain their recorded error. The clearer message
applies to new runtime tests and runs.
- No database schema or migration changes.

## Model Used

OpenAI GPT-6 through Codex. The system reports the model family; the
exact deployment ID and context-window size are not exposed in this
session. Used reasoning, browser inspection, tool use, and code
execution.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-12 06:51:22 -05:00
1b4c803816 fix(costs): preserve usage attribution and explain unavailable run costs (#15948)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The cost ledger records usage and controls agent budgets.
> - Some runs lose the selected model or API billing category before
their usage reaches the ledger.
> - The dashboard reports missing costs but does not identify the
affected runs or the available actions.
> - This pull request preserves usage attribution and adds a compact
view of cost gaps grouped by agent.
> - Operators can inspect runs, open agent settings, and record saved
costs without starting new agent work.

## Linked Issues or Issue Description

Refs #15634, #11938, #15879.

This follows the merged subscription reporting in #15634. The older
proposal in #11938 addresses part of ACP model attribution. This change
also rejects conflicting models across a turn, fixes credential-backed
billing capture, and explains cost gaps in the existing dashboard. The
Pi receipt recovery work in #15879 covers a separate adapter recovery
path. This PR preserves unresolved receipts and does not recover
provider usage or reprice past entries.

**What happened?**

ACP results use the requested model even when the runtime selects a
model. Native and OpenCode API runs can report unknown billing despite a
matching API credential. The dashboard shows notices without links to
the affected runs. These gaps make API and subscription totals harder to
interpret.

**Expected behavior**

Record the model selected by the runtime. Identify API billing only from
a matching credential. Keep costs unresolved when reliable usage or
pricing is absent. Show the affected agents and runs. Offer an
accounting retry only when saved evidence is ready.

**Steps to reproduce**

1. Run an ACP agent with its model left unset, or use a matching API
credential with a native or OpenCode agent.
2. Inspect the recorded model and billing category for the run.
3. Open Costs Overview for a period with unpriced usage or terminal runs
awaiting receipts.
4. Inspect the notice and try to find the affected run or the relevant
agent settings.

**Paperclip version or commit**

Current master at `657e71584a0`. The patch is rebased on that commit.

**Deployment mode**

Self-hosted server and local development. The cost detail tests use a
disposable PostgreSQL database.

## What Changed

- Preserve the runtime-selected ACP model in checkpoints and final
results. Conflicting models across one turn remain unpriced.
- Share credential-backed API billing checks across native and OpenCode
results. Keep unknown billing when the provider and credential do not
match.
- Add company-scoped cost detail and accounting retry endpoints. Board
viewers can read details but cannot retry. Agents and foreign companies
are denied.
- Limit each detail category to 25 entries. Use one consistent database
snapshot and sparse indexes matching the filters and sort order. Add the
unknown-billing count to the existing summary query.
- Replace the notice with an expandable count line. Group entries
alphabetically by agent. Show the saved blob icon, link each name to
configuration, and put run and retry links beside the date.
- Preserve estimate and unavailable-price labels on Providers and
Billers; keep retry confirmation after the resolved notice disappears.
- Fetch details only while expanded. Retain the last loaded details
after a refresh error. Retry saved accounting only; do not rerun the
agent.
- Add capture, database, authorization, UI, and Storybook coverage.
Update the implementation contract and cost reporting guide.

## Verification

- `pnpm -r typecheck` passed after rebase.
- `pnpm build` and `pnpm build-storybook` passed after rebase.
- `pnpm check:token-gates` and `git diff --check` passed.
- 1,086 focused tests passed across 17 files. These cover ACP, native
and OpenCode capture, PostgreSQL reports, route authorization, OpenAPI,
Costs UI, and existing accounting regressions.
- Desktop and mobile browser checks passed during UI review. They cover
lazy expansion, alphabetical groups, loaded blob icons, configuration
and run links, keyboard retry, and layout overflow.
- `pnpm test:run` was attempted with command-scoped
`init.defaultBranch=master` for Git fixtures. It was stopped after a
native session recovery failure reproduced on unchanged base
`657e71584a0` (`archives a damaged prior epoch before completing guarded
same-task replacement with real runnerd`). An MCP upload case failed in
the broad run but passed in isolation on both the base and PR branch.
These local limitations do not affect the 1,086 passing focused tests.
- Current-head CI is green, including all unit test lanes, serialized
server suites, runner checks, typecheck, build, policy, and eight
browser shards. Apex re-review is 5/5 on `56de60effdb`; all four
original findings are addressed and every review thread is resolved.
- PostgreSQL applies migration `0333` in the integration fixtures.
EXPLAIN ANALYZE on the production detail statements uses the new sparse
indexes behind 5,000 healthy records in each table.
- Ten provider/biller card cases cover missing and estimated prices; the
retry test covers row removal and notice unmount. A live Storybook
browser check verifies the confirmation and updated count.
- Run `pnpm storybook`. Open **Costs / Subscriptions / Cost Data Quality
Details** to inspect the production page with representative fixtures.

## Risks

- Model and billing metadata change for future runs. Estimates still
require complete usage and a supported price. No past cost amount is
rewritten.
- A missing final receipt remains unresolved for budget checks. The
display changes after fifteen minutes; that time limit never authorizes
settlement.
- Accounting retry is a write action. It retains the existing company
checks, receipt fences, idempotency, and audit logging.
- Detail reads are bounded and run only after expansion. They poll every
30 seconds while open. No provider probe or new summary query is added.
- Migration `0333` adds two partial indexes; it changes no stored cost
amounts or table columns. Index creation scans the existing tables and
may briefly block writes during deployment. No lockfile or workflow
change is included.

## Model Used

OpenAI Codex, based on GPT-6, with repository inspection, code editing,
browser checks, and test execution. The exact serving model ID,
reasoning setting, and context window were not exposed to this session.

## Checklist

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

---------

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-11 22:48:29 -07:00
4611d6e95a fix(restore): retry sandbox copy-back before holding the task for repair (#15920)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Legacy sandbox runs copy the agent's workspace files back to the
host when the run settles.
> - When this copy-back fails one time, the server keeps the sandbox and
puts a board-owned repair hold on the task.
> - The hold blocks new messages. Only an operator API runbook can clear
it. The board shows that runbook text, so users do not know what to do.
> - Many copy-back failures are transient: a dropped transfer, a
provider error, or a merge lock that another restore holds.
> - This pull request retries those failures automatically while the run
still has its merge baseline and before any host file changes.
> - The benefit is that most failures repair themselves, and the
remaining holds explain the situation in plain language.

## Linked Issues or Issue Description

Refs #14590 (related: no-replay recovery holds)

**What happened?**
A legacy sandbox run finished its work, but one workspace copy-back
attempt failed. The task then showed "Recovery needed" with
operator-only steps ("verify its exact stop receipt … record
workspaceRepairEvidence"). Messages from the user stayed saved and were
not delivered.

**What did you expect?**
Paperclip retries a transient copy-back failure automatically. If a
repair is still necessary, the task says clearly what happened and who
must act.

**Steps to reproduce**
1. Run an agent in a legacy sandbox environment.
2. Make the workspace copy-back fail one time, for example with a
dropped transfer or a held merge lock.
3. Open the task. It is blocked behind a repair hold, and new messages
wait.

**Additional context**
Separately, distinct live events can render toasts with the same title
and body (for example, two runs of the same agent that both succeed).
The toast list showed both, and the desktop app forwards each one as a
notification.

## What Changed

- `packages/adapter-utils/src/sandbox-managed-runtime.ts`: retry the
workspace copy-back up to three times in total, with a 2 s and then a 5
s backoff. A retry occurs only when the failure is `restore_failed` or
`restore_lock_timeout` and no host workspace file was written. The merge
now marks the first host write in `beforeApply`, which runs under the
merge lock before any write.
- Unsafe archives, permission failures, and failures after the merge
started writing are never retried. Each retry uses a new temp directory
and a new imported Git ref.
- New optional `workspaceRestoreRetryDelaysMs` input, so tests can
shorten the backoff. Nested repository restores receive the same value.
- `ui/src/components/ExecutionBlockerNotice.tsx`: for a workspace repair
hold, the notice now says what happened in plain language. The stored
operator runbook text moves under "Technical details".
- `ui/src/context/ToastContext.tsx`: also dedupe toasts on their visible
tone, title, and body inside the existing 3.5 s window.
- `doc/workspace-restore-locks.md`: document the automatic retry.

## Verification

- `pnpm --filter @paperclipai/adapter-utils exec vitest run
src/sandbox-managed-runtime.test.ts src/workspace-restore` (218 tests
pass). New tests cover:
  - a retry after a dropped transfer,
  - a retry after a merge-lock timeout,
  - a stop after three attempts,
- no retry for an unsafe archive, a permission failure, or a failure
after the merge started writing.
- The existing step-diagnostic test now uses failures that persist
across retries. It still expects one diagnostic per failed step.
- `pnpm --filter @paperclipai/ui exec vitest run
src/context/ToastContext.test.tsx
src/components/ExecutionBlockerNotice.test.tsx
src/context/LiveUpdatesProvider` (120 tests pass).
- `tsc --noEmit` for adapter-utils passes. `node
scripts/check-token-gates.mjs` is clean.
- Not fully run: the full adapter-utils suite has two failures in
`github-launcher*.test.ts`. They read the managed GitHub credentials of
the local sandbox environment and do not touch restore code. UI `tsc -b`
reports five errors in `PluginOrganizationSwitcher.tsx`, which is not
changed here, because the plugin SDK is not built in a fresh install.

## Risks

- A copy-back that fails before the merge can take about 7 s longer
before it reports a failure. Lock timeouts can take up to about 97 s in
total (three 30 s lock waits plus backoff). Settlement already waits for
the copy-back.
- The retry logic depends on `beforeApply` running before the first host
write in `mergeDirectoryWithBaseline`. That is true today, and a comment
marks it.
- A Git bundle import before the merge can leave unreferenced Git
objects after a retry. They are harmless, and the existing ref cleanup
still runs on each attempt.
- Content-based toast dedupe can hide a second toast with identical text
inside 3.5 s. That is the intended behavior.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`, Anthropic) in the Claude Agent
SDK, with tool use (shell, file edit, code search) and a subagent for
code exploration.

## Checklist

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

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

---------

Co-authored-by: Paperclip Agent <agent@paperclip.ing>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:44:11 -07:00
657e71584a fix(daytona): record provider state when lease creation hits its deadline (#15946)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents can run in Daytona sandboxes. The Daytona plugin acquires a
lease before each run.
> - Some acquisitions time out. The report then says
`environmentAcquisitionPhase=create` and that the whole 300 s budget was
used.
> - The Daytona SDK create timeout is equal to the acquisition budget.
The outer timer always fires first. Thus the report never shows the
state that Daytona left the sandbox in.
> - Without that state, we cannot choose a fix. A stuck snapshot pull, a
slow start, and a missing allocation need different fixes.
> - This pull request adds one bounded, read-only provider lookup at the
create deadline. It records a fixed-enum provider state in the existing
acquisition diagnostic.
> - The benefit is that the next timeout identifies where the provider
stopped. Retry, budget, and cleanup behavior do not change.

## Linked Issues or Issue Description

Refs #15736 (bounded Daytona acquisition failure observations). This
pull request extends those observations.

**What happened?**

A Daytona lease acquisition timed out with `Daytona lease acquisition
timed out; allocation cleanup is pending`. The run-failure report gave
`environmentAcquisitionPhase=create`,
`environmentAcquisitionElapsedMs=300004`, and
`environmentAcquisitionBudgetMs=300000`. The report did not show the
Daytona sandbox state. The SDK create timeout is equal to the outer
budget, so the outer timer always fires first. The SDK never reports the
stuck state.

**Expected behavior**

When creation is still pending at the deadline, the report should show
where the provider left the allocation. Examples are `pulling_snapshot`,
`starting`, `error`, or `absent`.

**Steps to reproduce**

1. Configure a Daytona environment.
2. Start a run when Daytona does not finish create and start within the
acquisition budget.
3. Inspect the setup-failure report. Only the phase, the elapsed time,
and the budget are present.

**Paperclip version or commit**

`master` at ba04968.

## What Changed

- `packages/plugins/sdk`: Add an optional `providerState` field to
`PluginEnvironmentAcquisitionDiagnostic`. The values are a fixed enum:
`absent`, `creating`, `pending_build`, `building_snapshot`,
`pulling_snapshot`, `starting`, `started`, `error`, `build_failed`,
`other`, `unavailable`. The reader omits an unknown value and keeps the
other fields. Old envelopes stay valid.
- `packages/plugins/sandbox-providers/daytona`: When the acquisition
deadline fires while `client.create` is still pending, make one
`client.get` of the provisional `paperclip-create-<attempt>` name. The
lookup has a 5 s bound, inside the host's 30 s RPC margin. Report the
state only when the name and all ownership labels match. Otherwise
report `unavailable`. A not-found error reports `absent`.
- The lookup does not delete. It does not set `observedProviderLeaseId`.
It does not change retries, budgets, or the cleanup envelope.
- After the deadline, only the timer settles the call. A late create
result cannot replace the timeout error before the lookup completes. A
test proves this guard.
- `server`: Project the value to
`run_execution.environmentAcquisitionProviderState`. It uses the same
private, run-scoped receipt as the other acquisition fields.
- `doc/observability.md`: Document the new field and its limits.

## Verification

- `cd packages/plugins/sandbox-providers/daytona && npx vitest run
--config vitest.config.ts`: 392 passed, 6 skipped.
- `npx vitest run
packages/plugins/sdk/tests/environment-creation-cleanup.test.ts
server/src/services/__tests__/run-failure-diagnostics.test.ts
server/src/__tests__/environment-acquisition-diagnostics.test.ts
server/src/__tests__/run-failure-sentry-real-sdk.test.ts
server/src/__tests__/environment-runtime.test.ts`: 302 passed, 11
skipped.
- `tsc --noEmit` passes for `packages/plugins/sdk` and
`packages/plugins/sandbox-providers/daytona`.
- New tests cover state mapping, `other` for unlisted states, `absent`,
`unavailable` for lookup failure and label mismatch, a hung lookup
bounded to 5 s, no deletion, an unchanged cleanup envelope, and a late
create that settles during the lookup.
- A mutation check removed the late-settlement guard. The late-create
test then failed.
- The local server `tsc` was killed by the sandbox memory limit. CI must
confirm the server typecheck. The server change is one line.

## Risks

- Low risk. The timeout rejection can come up to 5 s later, and only for
a create-phase deadline. The host RPC margin is 30 s.
- The lookup is one extra read-only Daytona API call for each
create-phase timeout.
- This is a diagnostic. It does not fix the upstream cause. The next
timeout report must show the state before a fix is chosen.
- This is a Sentry run-failure report change. It is not a Telemetry
change. The new value is a fixed enum. It does not contain provider IDs,
causes, URLs, or paths.

> 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`), used through Claude
Code with tool use (shell, file edit, test runs). Context window and
reasoning configuration are set by the runtime.

## Checklist

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

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

Co-authored-by: Devin Foley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-11 21:22:39 -07:00
Devin FoleyandPaperclip 0ba88d2195 fix(opencode-local): preserve deadlines in Daytona process sessions (#15905)
## Thinking Path

> - Paperclip manages work done by AI agents.
> - The OpenCode adapter can run its CLI in a provider sandbox.
> - A long CLI run must remain observable until it exits or the host
stops it.
> - The legacy main command does not request a provider process session.
A launch without an active run step uses one synchronous command
request.
> - That request can lose its response before the resolved execution
budget ends.
> - This PR requests the existing asynchronous session path for the main
Daytona OpenCode command. It also carries the resolved command deadline
through the host and provider contracts. The host still applies the
effective capability gate.
> - The change removes that synchronous request from eligible sandbox
runs.

## Linked Issues or Issue Description

Related reporting fix: #15789. A search of open and closed GitHub issues
and PRs found no duplicate transport fix.

**What happened?**

A hosted Linux legacy OpenCode run retained a gateway-timeout response
about one hour after CLI startup. It had a four-hour resolved budget and
recent agent output. The earlier reporting fix correctly kept the
configured policy separate from the timeout message. The original
response owner and specific timer were not retained, so this PR does not
claim to prove that provider cause.

**Expected behavior**

A Daytona target that supports persistent process sessions should
dispatch the long agent command asynchronously. Paperclip should observe
its output and exit through the existing session path. A single command
HTTP request should not need to stay open for the full agent run.

**Steps to reproduce**

1. Use legacy OpenCode with a Daytona sandbox target and a four-hour
execution budget.
2. Launch outside an active measured step. The current main CLI call
does not request a persistent process session.
3. In the SDK fixture, make the synchronous command request return a
provider timeout before the budget ends. Keep the asynchronous session
healthy for more than one hour.
4. The regression tests require session dispatch, incremental output,
one command dispatch, a real exit result, and session cleanup.
5. Set a shorter command deadline and stall dispatch, a status read or a
log snapshot. The command must return a timeout at its own deadline.
Healthy output must not extend that deadline.

**Paperclip version or commit**

Observed source: `e733bddc45e00efb1819fffa8677f7b0d07802c5`. PR base:
`4b59e2a2ecc67e98b336b8eb4c09959681672d54`.

**Deployment mode**

Hosted Linux server, legacy OpenCode adapter, Daytona sandbox. The host
gate retains the old path when a provider cannot keep process sessions.

## What Changed

- Add an optional sandbox session choice to the shared process helper.
Leave the choice absent for existing callers.
- Request that session only at the main Daytona OpenCode CLI invocation.
Keep probes, preparation and other providers on their existing paths.
- Carry a separate optional command deadline through the shared runner,
host service and plugin SDK. Existing session callers can omit it. Zero
leaves the command deadline disabled.
- Bound dispatch, live output and recovery observations with one command
timer. Clear it on settlement. Fence late stream callbacks. Remove
deadline listeners after each read and clear interrupted polling waits.
Preserve a known exit code if only the final logs time out. A timeout
result does not claim that the process stopped.
- Add adapter and process-helper regression coverage. Test the existing
Daytona session for a virtual run beyond one hour, without command
replay, with release cleanup.
- Document transport selection and the limits of the timeout evidence.

## Verification

- `pnpm -r typecheck` passed on Node 25.6.1 and pnpm 9.15.4 with the
command deadline changes.
- `pnpm build` passed with the updated contracts. The separately
installed Daytona package also passes its own typecheck and build
against the pinned SDK 0.203.0 and TypeScript 5.9.3.
- Five focused process, adapter, host and provider suites passed all 349
tests. They cover a run beyond one hour, five stalled observation
phases, healthy-stream deadlines, zero/absent budgets, late callbacks,
session release, SSH and capability fallback.
- The real environment-runtime forwarding tests passed: 19 selected
tests, including the separate command budget and forced session flag in
the worker RPC.
- An earlier full local `pnpm test:run` began before a clean build and
overlapped the correction. That mixed-version run was stopped and is not
passing evidence. After the build, the two previously uncollected suites
pass (1,659 tests). Two unchanged workspace fixtures fail with this
machine's Git default branch `main`; both pass with a test-process-only
`master` override. One unchanged native recovery fixture fails on both
this branch and an independently installed and built checkout of base
`4b59e2a2ecc67e98b336b8eb4c09959681672d54`, with the same
terminal-result mismatch. It is not introduced by this change. The full
local suite is not claimed green. These limitations do not replace fresh
hosted CI.
- Final head `8e96638cdef8ca6e91f7a42a52c83a075180c137` passes all
hosted checks: 54 passed and 2 expected Storybook skips. Greptile
reviewed this exact head at 18:12 UTC and scored it 5/5. Both actionable
findings are fixed and resolved. GitHub reports no conflicts and a clean
merge state. Earlier-head results are not used as final-head proof.
- The standalone provider test command passes 373 tests, with 14
expected live-probe skips.
- SDK tests use a controlled virtual clock. No live provider experiment,
task replay, manual deployment, or public PR merge was performed.

## Risks

The main legacy OpenCode command uses Daytona's existing session route
when the effective capability gate permits it. A quiet log stream can
use the existing status and snapshot fallback. Those observations add
provider reads. The new optional command budget is separate from each
read's timeout and returns a timeout without the host RPC's extra
30-second allowance. Existing session callers that omit it keep their
previous lifetime.

Callback bridge control commands retain their explicit session bypass.
The capability gate retains the previous path for unsupported targets.
Other providers, local and SSH execution keep their existing path. Retry
policy, Stop acknowledgement and lease ownership rules stay the same.
The SDK exposes no cancellation handle for an abandoned stream; existing
Stop and lease teardown still own command termination. This fix does not
establish the original gateway owner or prove recovery in a live
environment.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, repository tools and code
execution. The exact backend identifier 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 (changed paths; the
independently reproduced local baseline failure is documented above)
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-11 12:08:45 -07:00
85f4205de3 perf(test): reuse a migrated embedded Postgres template across suites (#15861)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Pull requests to this repository run a large CI matrix. The slowest
check sets the time a contributor waits for a green run.
> - After #15853, the twelve `server (n/12)` shards are the slowest
check in every fully green PR run created since its merge (518s to 548s;
`Build` is 482s to 504s). Each shard is about 60s of setup and 454s to
490s of Vitest, with one worker, so its wall time is the sum of its
suites.
> - From two shard logs, 22 to 24 of the 71 to 74 suites in a shard
start an embedded Postgres cluster. Each one runs `initdb`, starts the
cluster, and applies all 331 migrations before its first test. The
smallest such suite takes 6s with one or two trivial tests. On top of
that, the `getEmbeddedPostgresTestSupport` probe starts and stops a
second throwaway cluster at import time in 19 of those suites, which is
why their per-file import overhead is 3.5s to 4.1s against 0.9s to 1.0s
for suites without Postgres.
> - A cluster data directory that was shut down cleanly can be copied
and started again as an independent cluster, and the migrated schema is
the same for every suite.
> - This pull request builds one migrated cluster per host, publishes
its data directory as a template under the OS temp directory, and starts
every later suite from a copy of it. The probe reuses the template
instead of starting a cluster.
> - The benefit is about 7s to 8s per Postgres suite on the CI runners
(measured 3.2s to 0.35s per start and 0.75s to 0.03s per probe locally),
about 120s to 170s per server shard, and a faster local test loop for
every suite that uses the fixture.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The `server (n/12)` PR checks are the slowest check in every fully green
PR run since 2026-10-11 05:27Z. Their Vitest step runs suites one at a
time, and about a third of each shard's time is embedded Postgres setup
that produces the same migrated cluster every time.

**Subsystem affected**

Test infrastructure: the embedded Postgres test fixture in
`@paperclipai/db` (`packages/db/src/test-embedded-postgres.ts`), used by
349 server suites, the db package suites, and the CLI suites.

**Current behavior**

`startEmbeddedPostgresTestDatabase` runs `initdb`, starts the cluster,
creates the database, and applies all 331 migrations for every suite.
`getEmbeddedPostgresTestSupport` starts and stops a second throwaway
cluster once per test file, because the module-level cache does not
survive Vitest's per-file isolation. Measured locally: 3.2s per suite
start plus 0.4s cleanup, and 0.75s per probe. On the CI runners the
smallest Postgres suite takes 6s and the probe adds 2.5s to 3s of import
time per file.

**Proposed behavior**

The first caller on a host builds one migrated cluster, stops it, and
renames its data directory to
`$TMPDIR/paperclip-embedded-postgres-templates-<uid>/<key>`. Every later
caller copies the template (about 90 MB) and starts Postgres on the
copy, then still runs `ensurePostgresDatabase` and
`applyPendingMigrations`, which are no-ops on a current template. The
probe treats a published template as proof that the host can run the
cluster. The key hashes the embedded-postgres version, the initdb flags,
the user, and the name and content of every migration file plus the
journal, so a changed migration builds a new template.
`PAPERCLIP_TEST_POSTGRES_TEMPLATE=0` bypasses the cache. Any template
failure falls back to the fresh path.

A template is a Postgres data directory, and starting it executes its
configuration, so the cache is private to the current OS user: the root
name carries the uid, the root is created with mode 0700, and both the
root and each template are used only when `lstat` shows a real directory
(not a symlink) owned by the current user with no group or world write
bit. A root that fails those checks (for example one pre-created by
another user in a shared `/tmp`) is left alone and the process uses
fresh clusters.

**Reason and benefit**

The slowest check decides the wall-clock time of every PR run. Measured
locally, a template start takes 0.3s to 0.4s instead of 3.2s, cleanup
0.15s instead of 0.43s, and the probe 0.03s instead of 0.75s. Four
server suites that passed in both modes dropped by 3.4s to 6.0s each;
the db `backup-lib` suite dropped from 42.7s to 20.9s. The CI runners
are about twice as slow, so the expected saving per server shard is 120s
to 170s.

**Breaking changes**

None. The returned database is fully migrated in both paths, each suite
still gets its own cluster and data directory, and a failure on the
template path falls back to the existing fresh path.

## What Changed

- `packages/db/src/test-embedded-postgres.ts`: adds the template cache.
`computeEmbeddedPostgresTemplateKey` hashes the inputs that determine a
migrated cluster. `buildEmbeddedPostgresTemplate` starts a fresh cluster
through the existing bounded retry, creates the database, applies
migrations, waits for the real `stop()` (30s bound), and publishes the
data directory with an atomic rename; a concurrent builder that loses
the rename discards its copy.
`startEmbeddedPostgresFromTemplateWithRetry` copies the template into a
fresh temp directory, sets mode 0700, and starts the cluster with the
same per-attempt port and directory rotation as the fresh path.
`startEmbeddedPostgresTestDatabase` tries the template first and falls
back to the fresh path with one warning per process.
`probeEmbeddedPostgresSupport` returns supported when a template exists
or can be built, and keeps its throwaway-cluster path for the case where
the template could not be published. Templates for other keys are pruned
after seven days. The connection strings and initdb flags move into
shared constants; no other behavior changes.
- `packages/db/src/test-embedded-postgres.test.ts`: two unit tests for
the key (stable for identical inputs; changes with migration content, a
new file, flags, version, or user; hashes this package's real migrations
directory) and two real-cluster tests (the published template is
complete and private, a suite starts from it in `template` mode with
migrations up to date, a table written to one copy is not visible in the
next copy, and `PAPERCLIP_TEST_POSTGRES_TEMPLATE=0` runs the `fresh`
path). The real-cluster tests skip on hosts without embedded Postgres,
like every other fixture suite.
- `doc/DEVELOPING.md`: a short section under Test Commands describing
the template, its key, and the bypass variable.

**Review fixes (second commit), one per finding:**

- Private cache root (Greptile P1, Superagent P2). The root is
`$TMPDIR/paperclip-embedded-postgres-templates-<uid>` (user name on
Windows), created with mode 0700. `prepareTemplateRootDir` and
`isUsableTemplate` both reject, via `lstat`, anything that is a symlink,
not a directory, owned by another uid, or writable by group or others; a
rejected root logs one warning and the process uses fresh clusters. A
root created by the first commit with a wider mode is narrowed to 0700
if this user owns it.
- Template failures never skip suites (Greptile P1).
`getEmbeddedPostgresTemplateDir` no longer rejects. A build or root
failure resolves `null` with one warning, and
`probeEmbeddedPostgresSupport` then runs the throwaway-cluster probe, so
only a cluster that cannot start reports `supported: false`. A migration
that cannot apply fails the suite on the fresh path instead of being
hidden by `describe.skip`.
- Idle-based pruning (Greptile P2). Each reuse refreshes the template's
timestamp with `utimes`, so the seven-day prune measures idle time. A
template that disappears after a process resolved its path is not
retried five times: `startEmbeddedPostgresFromTemplateWithRetry` checks
the template before each copy and throws at once, the suite falls back
to a fresh cluster, and the fallback forgets the path so the next caller
in that process rebuilds the template.
- The template-path test enables the cache for itself and restores the
original `PAPERCLIP_TEST_POSTGRES_TEMPLATE` value afterwards (Greptile
P1), so the suite passes under `PAPERCLIP_TEST_POSTGRES_TEMPLATE=0` as
well.
- Eight new tests: `isUsableTemplate` accepts a private cluster
directory and rejects a missing `PG_VERSION`, a leftover
`postmaster.pid`, a symlink, and (POSIX) group- or world-writable modes;
a world-writable pre-created root and a symlinked root both fall back to
fresh clusters, publish nothing, log one warning, and still report
`supported: true` from the probe; a ten-day-old template is refreshed on
reuse; a pruned template is rebuilt on the next start; and a template
build that fails after the cluster started falls through to the fresh
probe. The tests that need an empty cache point `os.tmpdir()` at a
scratch directory (`TMPDIR`, `TEMP` and `TMP` together, so the redirect
holds on Windows as well) and restore it.

**Review fixes (third commit, e8f8e9c8), one per finding:**

- Fallback test on Windows (Greptile P1). `useScratchTmpdir` set only
`TMPDIR`, which Windows `os.tmpdir()` ignores, so the one scratch-root
test that runs on every platform would assert on paths outside the
scratch root there. The helper now sets and restores `TMPDIR`, `TEMP`
and `TMP` together, and that test asserts `os.tmpdir() === scratch`
before it checks any path under it.
- Timestamp test runs on its own (Greptile P2). The test assumed an
earlier test had published a template; under
`PAPERCLIP_TEST_POSTGRES_TEMPLATE=0` the probe does not, so a
name-filtered run threw `ENOENT` from `utimes`. It now starts and cleans
up one suite first, which builds the template when nothing has, and
asserts `isUsableTemplate` before ageing it.

## Verification

- `pnpm --filter @paperclipai/db exec tsc --noEmit`: clean (both
commits).
- `pnpm --filter @paperclipai/db exec vitest run
src/test-embedded-postgres.test.ts`: 15 pass (12 new across both
commits) with the template root absent (first run builds it), with it
present, and with `PAPERCLIP_TEST_POSTGRES_TEMPLATE=0`.
- After the review fixes: `client.test.ts` and `backup-lib.test.ts` (29
tests) pass on the template path, and
`agent-action-audit-routes.test.ts` and `status-cards.test.ts` (36
tests) pass through the server route harness. The second commit's CI run
([38121021162](https://github.com/paperclipai/paperclip/actions/runs/38121021162))
passed every check: `server (n/12)` jobs 277s to 425s, Vitest `Duration`
219s to 365s, no fallback warning in any shard.
- Third commit: reproduced the Greptile P2 on the old file first
(`PAPERCLIP_TEST_POSTGRES_TEMPLATE=0 vitest run
src/test-embedded-postgres.test.ts -t "refreshes the template"` on an
empty cache root fails with `ENOENT … utime`), then with the fix the
same command passes, and the full file passes 15/15 on a cold cache, a
warm cache, and with the bypass set. `tsc --noEmit` clean.

**Measured on this PR's first CI run
([38117892083](https://github.com/paperclipai/paperclip/actions/runs/38117892083)),
which ran the branch's fixture in every server shard:**

| | Before (runs 38114982393, 38115279131, 38115459196) | This PR's run
|
| --- | --- | --- |
| Vitest `Duration` per `server (n/12)` shard | 454s to 490s | 224s to
337s |
| `server (n/12)` job | 518s to 548s | 286s to 401s |
| Slowest check in the run | a server shard | `Build`, 453s |

No shard logged a fixture fallback warning, so every Postgres suite in
the run started from the template.
- `pnpm --filter @paperclipai/db exec vitest run
src/status-card-migrations.test.ts
src/nested-skill-folders-migration.test.ts
src/chat-github-migration.test.ts
src/question-response-delivery-migration.test.ts src/client.test.ts`: 5
files, 24 tests pass on the template path. These suites drop indexes,
re-run migration files, and create extra databases on the cluster, so
they cover the semantics the template must preserve.
- Four server suites run with `PAPERCLIP_TEST_POSTGRES_TEMPLATE=0` and
then with the template, all passing in both modes:

| Suite | Fresh path | Template path |
| --- | --- | --- |
| `server/src/__tests__/agent-action-audit-routes.test.ts` (11 tests) |
17.9s | 14.6s |
| `server/src/__tests__/status-cards.test.ts` (25 tests) | 22.2s | 18.6s
|
| `server/src/__tests__/plugin-managed-routines.test.ts` (3 tests) |
18.2s | 14.3s |
| `server/src/__tests__/issue-thread-interactions-service.test.ts` (106
tests) | 19.0s | 12.9s |
| `packages/db/src/backup-lib.test.ts` (9 tests) | 42.7s | 20.9s |

- Per-operation timings on the same host: fresh `initdb` + start +
create database + 331 migrations 3.2s, cleanup 0.43s; template copy
0.20s to 0.23s, start from copy 0.03s to 0.04s, `ensurePostgresDatabase`
0.013s, `applyPendingMigrations` fast path 0.03s to 0.04s, stop 0.03s.
Probe with the template on disk 0.03s (was 0.75s). Template size 87 MB,
3215 files.
- This PR's own server shards run the branch's fixture, so their Vitest
`Duration` lines show the saving directly. Expected on this PR's run:
`server (n/12)` about 330s to 430s (was 440s to 548s on runs
38114982393, 38115279131 and 38115459196), and the slowest check moves
to `Build` (482s to 504s).

**Measured on the three fully green `master` PR runs created after
#15853 merged** (slowest server shard logs):

| Run | Server shards | Vitest `Duration` (slowest shard) | Postgres
suites in it | Their reported duration | Their import overhead |
| --- | --- | --- | --- | --- | --- |
| 38115459196 | 476s to 548s | 483.8s (tests 72%, import 24%) | 22 of 71
| 197s | 83s |
| 38114982393 | 409s to 518s | 452.8s (tests 65%, import 30%) | 24 of 74
| 264s | 98s |

## Risks

- Low risk to correctness. The template is a cleanly stopped cluster
produced by the same `initdb` flags and the same migration runner as the
fresh path, and each suite still runs `ensurePostgresDatabase` and
`applyPendingMigrations` on its copy. Each suite gets its own copy, its
own port, and its own data directory, so isolation between suites is
unchanged.
- A stale template is prevented by the key. If a migration file, the
journal, the embedded-postgres version, the initdb flags, or the user
changes, the key changes and a new template is built. Even if a stale
template were used, `applyPendingMigrations` on the copy would bring it
up to date.
- Concurrent first use (for example the file-parallel db project) can
build the same template twice. The rename is atomic, so one copy wins
and the other is discarded; a template that exists is always complete.
The loser pays one extra fresh start, which is what every suite paid
before.
- The template root lives under the OS temp directory and holds about 90
MB per key. Keys that have not been used for seven days are pruned when
a new template is built; each reuse refreshes the timestamp.
- A template is executable configuration, so the cache is only used when
the root and the template are private directories of the current user
(ownership, no symlink, no group or world write bit). On a host where
another user pre-created the path, the process logs one warning and uses
fresh clusters. Windows has no uid, so there the check is
directory-only; its temp directory is already per user.
- Any failure on the template path (root checks, build, copy, start,
publish) falls back to the fresh path and logs one warning per process,
so a broken cache can make a suite slower but never fail it or skip it.
The guard tests assert the mode, so a silent fallback on CI would fail
`test-embedded-postgres.test.ts` rather than hide.
- Hosts that cannot run embedded Postgres still get `supported: false`
with the real Postgres output, now from the throwaway probe: a failed
template build is one warning, then the probe decides. On such a host
the probe costs one extra failed start attempt cycle per file compared
with the first commit; every suite on that host skips anyway.

## Model Used

- Claude (Anthropic), model ID `claude-fable-5-1`, running as a
Paperclip agent through the Claude local adapter with tool use (shell,
file edits, GitHub CLI). The agent collected the CI data, made the
change, ran the tests, and wrote this description.

## 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 (53 checks green on both run
38117892083 and run 38121021162; the third commit re-runs them)
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(1/5 on the first commit, 4/5 on the second; every finding so far is
fixed and answered, and Superagent confirmed its fix; re-review of the
third commit pending)
- [x] I will address all Greptile and reviewer comments before
requesting merge

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

---------

Co-authored-by: Bender (Fable) <bender-fable@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
2026-10-11 11:19:13 -07:00
de9ab8e7e0 perf(runner): compile the TypeScript surface once per build (#15853)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Pull requests to this repository run a large CI matrix. The slowest
check sets the time a contributor waits for a green run.
> - After #15849 hid the verifier's downloads behind package staging,
the `Build` lane is still the slowest check in five of the eight fully
green PR runs created after that merge (535s to 581s; the top server
shard is 529s to 560s in the other three). `Build` is about 100s of
setup, 160s to 176s of `pnpm build`, and 265s to 312s of the public
install verifier.
> - Inside `pnpm build`, every package after `@paperclipai/shared` waits
on the Runner: its `build` script takes 76s to 80s, and for 65s of that
it is the only job running. The chain runs `build:typescript` three
times: once directly, then again inside `check:semantic-contracts` and
`check:runner-workflow-traceability`, which rebuild first so they work
standalone. The server's `prepare:runner-vendor` then runs the whole
Runner build a second time, so the job pays for four redundant
TypeScript passes.
> - Each pass is two `tsc` projects, the eval-kernel rebuild, and the
schema checks, 7s to 8s on the CI runner. The dist they rebuild is the
dist the first pass just wrote; nothing between the passes changes it.
> - This pull request calls the two check scripts directly after the
chain's own `build:typescript`, the way `generate-replay-goldens.mjs
--check` already does in the same line. The standalone `check:*` scripts
keep their rebuild.
> - The benefit is about 30s off the `Build` job's critical path, about
15s off the Typecheck job and every Docker or release build that runs
the server build, with no change to what is checked.

## Linked Issues or Issue Description

**Describe the enhancement**

The `Build` PR check is the slowest check in most green PR runs. Its
`pnpm build` step compiles the Runner's TypeScript surface four times:
three passes inside one Runner `build`, and a second full Runner `build`
from the server's `prepare:runner-vendor`. Only the first pass produces
a dist that the later steps read; the other three rewrite the same
output.

**Why is this needed**

The slowest check decides the wall-clock time of every PR run. In the
eight fully green PR runs created after 2026-10-11 04:10Z, `Build`
topped five of them at 535s to 581s. The Runner build sits alone on the
`pnpm build` critical path for about 65s, and the server build repeats
it. From the job logs of four of those runs, the second and third
`build:typescript` passes cost 7s to 8s each, in both Runner builds, so
about 30s per job.

**Proposed solution**

In the Runner's `build` script, replace `pnpm run
check:semantic-contracts && pnpm run check:runner-workflow-traceability`
with `node scripts/generate-semantic-contracts.mjs --check && node
scripts/check-runner-workflow-traceability.mjs`. Both scripts read
`dist/`, which `build:typescript` wrote earlier in the same chain, and
the steps between them are `--check` runs that write nothing to `dist/`.
Add a guard test so the chain compiles TypeScript exactly once and both
checks still run after it.

**Alternatives considered**

- Skip the server's `prepare:runner-vendor` rebuild when `pnpm -r build`
already built the Runner. That rebuild is what makes `pnpm --filter
@paperclipai/server build` correct standalone (the Dockerfile uses it),
and the server's build-script test pins the command. A freshness check
would be a larger semantic change; this PR shrinks the rebuild instead
of removing it, and the next cycle can measure what remains.
- Make `tsc` incremental across the passes. It would still re-run the
schema checks and the eval-kernel build each time, and would add
build-info files to the package.
- Change `check:semantic-contracts` and
`check:runner-workflow-traceability` themselves to stop rebuilding. They
are documented standalone commands (`pnpm --dir
packages/paperclip-runner check:semantic-contracts` in the capability
docs) and must work from a stale dist; unchanged here.

## What Changed

- `packages/paperclip-runner/package.json`: the `build` script runs
`node scripts/generate-semantic-contracts.mjs --check` and `node
scripts/check-runner-workflow-traceability.mjs` directly, after its own
`build:typescript`, instead of the `check:semantic-contracts` and
`check:runner-workflow-traceability` wrappers that rebuild first. Every
other step, its order, and the standalone `check:*` scripts are
unchanged.
- `server/src/__tests__/server-package-build-script.test.ts`: a new test
in the suite that already pins the server's Runner vendoring reads the
Runner's `build` script, resolves every `pnpm run` step, and asserts
that exactly one step compiles TypeScript (directly or through a
wrapper), that both check scripts are in the chain, and that both come
after `build:typescript`. It fails against the previous script with
`expected [ 'build:typescript', …(2) ] to deeply equal [
'build:typescript' ]`.

## Verification

- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/server-package-build-script.test.ts`: 9 pass (1 new). With
the `package.json` change stashed, the new test fails as described above
and the other 8 pass.
- In `packages/paperclip-runner`, the new chain without the cargo step
(`check:protocol-manifest`, `build:typescript`,
`check:capability-contract`, `check:capability-inventory`,
`check:protocol-coverage`, `generate-semantic-contracts.mjs --check`,
`check-runner-workflow-traceability.mjs`, `generate-replay-goldens.mjs
--check`, `generate-semantic-action-catalog.mjs --check`) exits 0:
`Runner stress traceability passed: 44 findings, 12 workflows, 1
explicit exclusion.` and `Replay golden snapshots and parity summaries
are current.` Cargo is not installed on the host that produced this PR,
so `build:binary` was not run locally; it is unchanged and runs in this
PR's `Build` job.
- `pnpm run build:typescript` alone takes 5.8s to 5.9s on this host, so
each removed pass is in the range the CI logs show.
- This PR's own `Build` job runs `pnpm build` from the branch checkout,
so its log shows the new chain in both Runner builds. Expected on that
job: `pnpm build` about 130s to 146s (was 160s to 176s on the four
measured runs), `Build` about 505s to 551s (was 535s to 581s).

**Measured on the four post-#15849 `master` runs that motivated this
PR** (`Build` job logs):

| Run | `pnpm build` | Runner build in `pnpm -r build` | Extra
`build:typescript` passes in it | Runner rebuild in server build | Extra
passes in it |
| --- | --- | --- | --- | --- | --- |
| 38112764439 | 169s | 76s | 04:51:52 and 04:52:00 (about 14s) |
04:53:12 to 04:53:41 | 04:53:26 and 04:53:34 (about 15s) |
| 38111191543 | 176s | 79s | 04:22:40 and 04:22:48 (about 14s) |
04:24:04 to 04:24:34 | 04:24:18 and 04:24:26 (about 15s) |
| 38110914319 | 160s | 75s | 04:19:07 and 04:19:14 (about 14s) |
04:20:25 to 04:20:53 | 04:20:39 and 04:20:46 (about 14s) |
| 38110897745 | 172s | 80s | 04:15:16 and 04:15:25 (about 15s) |
04:16:41 to 04:17:11 | 04:16:55 and 04:17:03 (about 15s) |

## Risks

- Low risk. The same two checks run against the same dist, in the same
position in the chain. The only difference is that the dist is not
rewritten twice between them.
- If a future step between `build:typescript` and the two checks starts
writing to `dist/`, the checks would read that output instead of a fresh
rebuild. The steps there today (`check:capability-contract`,
`check:capability-inventory`, `check:protocol-coverage`) are `--check`
runs that only compare generated spec files.
- The standalone `check:semantic-contracts` and
`check:runner-workflow-traceability` scripts still rebuild first, so
documented usage is unchanged.
- The guard test lives in the server's suite because that suite already
pins the Runner vendoring contract and runs in the server shards on
every PR.

## Model Used

- Claude (Anthropic), model ID `claude-fable-5-1`, running as a
Paperclip agent through the Claude local adapter with tool use (shell,
file edits, GitHub CLI). The agent collected the CI data, made the
change, ran the tests, and wrote this description.

## 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)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

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

Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-10 22:27:38 -07:00
Devin FoleyandPaperclip b36960b758 fix(server): permit verified question-delivery history to sleep (#15851)
Treat completed question-response deliveries as history only after their exact same-company handoff finishes. Preserve missing evidence, retries, accounting debt, and idle-sleep fences.

Add a concurrent all-state question history index with interrupted-build repair. Keep migrations serialized without waiting snapshots that deadlock concurrent index builds.

Validated by 118 focused sleep tests, six migration/snapshot tests, full repository typecheck, all CI gates, and Apex 5/5.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-10 21:47:55 -07:00