Commit Graph
452 Commits
Author SHA1 Message Date
DottaandPaperclip fa064d247d merge: reconcile measured sandbox qualification with Claude platforms
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 17:01:24 -05:00
Dotta e23dd02651 Merge remote-tracking branch 'origin/codex/codex-runner-default' into codex/claude-runner-protocol
* origin/codex/codex-runner-default:
  docs: record final environment metadata review closure
  fix(environments): preserve measured proof atomically on metadata saves
  test(agents): distinguish measured and unknown sandbox defaults
  test(e2e): qualify managed Codex images in existing fixtures
  fix(agents): preserve legacy policy and require measured sandbox defaults
  fix(environments): bind platform facts to the effective immutable image
  fix(runner): close review gaps in setup and platform qualification
2026-10-12 16:48:56 -05:00
DottaandPaperclip 2a19f3721d fix(runner): close review gaps in setup and platform qualification
Read inert exact-source target data before selecting hosted native jobs. Bound registration teardown and retain unconfirmed recovery state. Record company-scoped sandbox platform facts only for immutable OCI images after confirmed cleanup; preserve existing connection and runner behavior.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 16:14:34 -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 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
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
DottaandPaperclip 7d9b93aee2 docs: establish paired feature and agent access maps (#16025)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Contributors use the feature map to understand and verify product
capabilities.
> - Agents need APIs and usable entry points within their permission
boundaries.
> - Product recipes and agent access need separate records with shared
capability IDs.
> - This pull request establishes that contract and gives maintenance
ownership of both maps.
> - The benefit is repeatable access review without changing product
behavior or permissions.

## Linked Issues or Issue Description

Refs: #15983, which added the portable verification skills and updated
the feature map. This is its contract-only follow-up. Related: #15288
added the original feature map.

**Issue type**

Missing documentation and maintenance contract.

**Where is the issue?**

The product map, its missing agent access companions, and the
verification skills.

**What's wrong?**

There is no shared convention for API presence, agent entry points,
permission boundaries, access gaps, or evidence. An API declaration does
not prove that a permitted native or remote agent can use it.

**Suggested fix**

Keep the product and agent access maps as siblings under `maps/`. Reuse
capability IDs. Record representative source inventory separately from
operation review and live proof. Keep both maps aligned through the
maintenance skill.

## What Changed

- Move the existing product map to `maps/feature-map/`. Preserve its
recipes and Poteto format. Update relative links and add companion
links.
- Add `maps/agent-access-map/` with 35 area files and 171 matching
capability IDs. Seed representative API operations, agent paths,
policies and known gaps from the reviewed source inventory. Mark entries
as `baseline`, `source-only`, and live verification `not-run`.
- Define the shared contract in `maps/README.md`. Track API presence,
agent availability, policy, evidence and review depth separately.
Require operation enumeration before marking a broad capability
operation-reviewed.
- Extend `maintain-verification-skill` to reconcile both maps and
inspect actual route policy and production tool admission. Update
creation, verification, notices and contributor references to use the
shared contract.
- Add `scripts/check-maps.mjs` and ten regression tests. Check matching
areas/IDs, indexes, references, contract fields, API method/path
examples, justified exceptions and evidence-state consistency.
- Keep the coverage inventory unchanged. Preserve skill invocation
policies and both MIT license blocks. No product API, permission, UI,
runtime helper, migration, dependency or workflow changes.

## Verification

- Passed: `node scripts/check-maps.mjs` reports 35 paired areas and 171
matching capability IDs. All local map references resolve.
- Passed: `node --test scripts/check-maps.test.mjs` runs ten tests.
Fixtures cover missing companions, renamed/orphan/duplicate IDs, broken
references, missing operation fields, empty exceptions, missing API
examples, empty policy fields, and verified evidence without passed
verification.
- Passed: metadata validation for all three verification skills. Skill
and contributor links resolve. Invocation policies and both license
blocks are unchanged.
- Compared relocated recipes with current master. Contents differ only
in relative links and companion links. `coverage.json` is byte-for-byte
unchanged. Master's updated read-only interaction test reference is
retained.
- Passed: `git diff --check`. The branch starts from current master and
contains only this contract change.
- Repository-wide typecheck, tests and build were not run locally for
this documentation and Node structural-check scope. Live agent access
was not exercised. Those gaps are explicit in the baseline entries; the
structural check does not certify behavior. CI and automated review are
pending.

## Risks

- The companions are representative source baselines. They are not
complete operation inventories or passed agent journeys.
- The checker cannot establish actual permissions, native tool
admission, operation completeness or truthful live evidence. Maintenance
must inspect source and verify with the actual permitted agent context.
- External links to the old product-map directory may need updates.
Repository references have been updated.
- Existing access gaps and board/user restrictions remain recorded. This
contract does not change authority or implement missing paths.

## Model Used

OpenAI GPT-6 through Codex assisted with this change. The session does
not expose the exact serving model ID, configured context-window size,
or reasoning setting. Capabilities used: source analysis, file edits,
shell commands, and structural regression tests.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-12 14:52:28 -05:00
DottaandPaperclip 0953983526 Verify installed Claude SDK admission in native clean consumers
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 14:34:15 -05:00
DottaandPaperclip 33077bed73 Pin provider-pack producer Node to the image version
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 14:08:03 -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 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
DottaandPaperclip a5b16834c8 fix: pin ARM qualification helper to workflow source
Keep frozen product and reusable workflow tooling identities separate. Preserve clean-consumer and package integrity checks.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 13:26:56 -05:00
8227485372 perf(ci): cache the Runner's release crate artifacts keyed by their inputs (#15932)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Its trusted PR workflow builds the Runner's Rust binary in five
jobs: Typecheck + Release Registry, Build, Canary Dry Run, and the
`rust` and `vitest 2/2` lanes of Verify Paperclip Runner. The `Build`
job is the slowest check in almost every green run (461s, 489s and 515s
in runs 38180574768, 38182717052 and 38183149920 on 2026-10-11).
> - Each of those jobs restores master's `release-runner-v2` dependency
cache. That cache holds only third-party crates. `rust-cache` removes
workspace crates when it saves, and a fresh checkout invalidates cargo's
mtime fingerprints for workspace crates in any case. So cargo compiles
`paperclip-runner-core` in release mode from scratch on every run. The
job logs show one `Compiling` line and 28s to 49s for it.
> - 18 of the 239 commits on `master` in the seven days before this
change touched the crate's inputs. The other 221 compiled the same
sources to the same binary again.
> - This pull request adds a second cache that holds only that crate's
release artifacts (about 70 MB). The key is made from the compiler, the
checkout root and a hash of every input the build reads. The master Rust
lane writes it. A pull request whose merge tree has byte-identical
inputs restores it and marks the artifacts newer than the checkout, so
cargo keeps them. Every other pull request misses and compiles as
before.
> - The benefit is about 28s to 49s less on `Build`, which is the run's
critical path, and the same on Typecheck, Canary Dry Run and two Runner
lanes, for about nine of ten pull requests.

## 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 time the trusted PR workflow spends to compile the Runner's own
crate on every run.

**Subsystem affected**

CI / build

**Current behavior**

Every PR job that builds the Runner binary compiles
`paperclip-runner-core` in release mode from scratch. The compile takes
28s to 49s on the `runs-on` fleet (one `Compiling paperclip-runner-core`
line, then `Finished release profile` in runs 38169436532 to 38183149920
on 2026-10-11). The shared Rust cache cannot help: it holds only
third-party crates, and a fresh checkout gives every source a new mtime,
which makes cargo discard the crate's fingerprints.

**Proposed behavior**

The master Rust lane in `release-verify.yml` saves the crate's release
artifacts under a key made from the compiler, the checkout root and a
hash of every input the release build reads. The PR jobs that build the
binary restore that entry read-only. On a hit, a script sets the mtime
of every restored artifact to now, so cargo sees every source as older
than the build and keeps the artifacts. `pnpm build` then prints
`Finished release profile` in under a second for that crate. On a miss,
cargo compiles the crate exactly as before.

**Reason and benefit**

About 28s to 49s off `Build`, the slowest check in almost every green
run, and the same off the Typecheck, Canary Dry Run and two Verify
Paperclip Runner lanes. The saving applies to every pull request whose
merge tree does not change the crate's inputs, which was 221 of 239
`master` commits in the week before this change.

**Breaking changes**

None. The checks are unchanged. A miss or an eviction costs the compile
time it costs today and nothing else.

**Additional context**

This is a workflow change. `pr.yml` calls `pr-trusted.yml@master`, so
this pull request's own run uses the current reader steps, and the
writer runs only on a master push. The saving becomes visible on the
first PR runs after the merge and after the next master push has written
the entry.

## What Changed

- `scripts/runner-release-crate-cache.mjs` (new): the one place that
defines the cached path list (`CACHED_PATHS`), the hashed inputs
(`HASHED_INPUTS`) and the files a restore must contain (`REQUIRED`). The
`scope` subcommand prints the platform, the `rustc --version --verbose`
release, commit and host, and a hash of the checkout root. The
`mark-current` subcommand sets the mtime of every restored artifact to
now and prints a `paperclip.runner-release-crate.v1` JSON line with the
file count and bytes. If the restored set lacks any of the seven files
cargo's freshness check reads or checks for (the fingerprint hash and
dep-info of each unit, the lib's rlib and rmeta, the binary under
`deps`), it removes the restored files, prints `action: discarded`
naming the missing file, and exits 0, so cargo compiles as on a miss.
- `.github/workflows/pr-trusted.yml`: after the `release-runner-v2`
restore in `typecheck_release_registry`, `build` and `canary_dry_run`,
and in the `rust` and `vitest 2/2` lanes of `verify_paperclip_runner`
(new matrix key `release_crate: true`), three steps: compute the key
scope, `actions/cache/restore` the crate under
`runner-release-crate-v1-<scope>-<hashFiles(...)>` with no
`restore-keys`, and run `mark-current` when `cache-hit == 'true'`. The
`hashFiles` list covers the toolchain pin, the manifests and lockfile,
the crate sources, the protocol tree and the three fixture trees the
test modules include (two under the Runner package and
`tests/runner-e2e/fixtures` at the repository root).
- `.github/workflows/release-verify.yml`: in the Rust lane of an
own-repository master push, after the debug warm-up: compute the key
scope, look the key up (`lookup-only: true`), and on a miss run `cargo
clean --release -p paperclip-runner-core` and `pnpm build:binary` (the
exact command the readers run, so the entry carries no test-profile
variants), then `actions/cache/save` the same path list under the same
key.
- `scripts/__tests__/runner-release-crate-cache.test.mjs` (new, 10
tests, in `test:release-registry`): the path list and hashed inputs are
the expected ones and exist; every `include_str!` and `include_bytes!`
in the crate sources resolves under a hashed input, whether its argument
is a literal or a `concat!` of literals, `env!("CARGO_MANIFEST_DIR")`
and macro metavariables, and any other form fails the test by name (this
guard found the `test/fixtures` tree during development and the
`tests/runner-e2e/fixtures` tree in review); the parser itself is tested
on a synthetic crate for each supported form and six rejected ones;
every PR reader restores after the dependency cache and marks before its
build command with the script's exact path list and key and no
`restore-keys`; the Verify lanes are gated by `release_crate`; the
writer runs after the checks with the lookup, the clean and build, and
the save under the readers' exact path list and key; the scope changes
with the compiler and the checkout root and survives a missing `rustc`;
`mark-current` makes every artifact newer than the checkout, leaves a
dependency's artifacts alone, discards a restore missing any one of the
seven required files (one case each, checking the named reason), and
still marks a restore missing any one of the seven files cargo does not
need.

## Review fixes

Greptile's two findings on the first commit were both valid and are
fixed in the second commit:

- **Incomplete caches looked usable.** `REQUIRED` checked four files,
but cargo's freshness check needs seven. Measured with cargo 1.97.1 on
the real crate (restore, touch the artifacts, delete one file, build):
without the lib's fingerprint hash, dep-info, rlib or rmeta it
recompiles the crate (24s to 27s); without the bin's fingerprint hash,
dep-info or the binary under `deps` it relinks (1.5s); without either
`.json` fingerprint record, either rustc `.d` file, the uplifted binary
or its `.d` it reports the crate fresh in 0.15s and recreates the
uplifted binary from `deps`, and the staging step then copies it.
`REQUIRED` now lists exactly the seven files, the glob for the binary
under `deps` excludes the `.d` beside it, and the test removes each of
the seven in turn (discarded, naming it) and each of the other seven in
turn (still marked).
- **Composed include paths escaped the guard.** The regex matched only
`include_str!("literal")` and skipped `include_str!(concat!(...))`,
which the crate uses four times (30 of 34 includes were checked). The
guard now parses every include argument and resolves the `concat!`
forms; one of them, in `provider_events.rs`, reads
`tests/runner-e2e/fixtures/native-completion/terminal-tool-carrier.json`
at the repository root, outside every hashed input. It is test-only, so
today's release binary does not read it, but the key is meant to cover
every include whatever its build, so `tests/runner-e2e/fixtures/**` (9
files, 80 KB) joins `HASHED_INPUTS` and the six key embeddings in the
two workflows. With the first commit's input list, the new guard fails
on exactly that include.
- `doc/RELEASE-AUTOMATION-SETUP.md`: a "Runner release crate cache"
section under the Runner verification dependency cache, with the key,
the writer, the readers and the trust boundary.
- `package.json`: the new test file in `test:release-registry`.

## Verification

- `node --test scripts/__tests__/runner-release-crate-cache.test.mjs`:
10 tests pass, three consecutive runs.
- The second commit, with the real toolchain on the real crate (`rustc
1.97.1`, cold build 2m20s, the crate's artifacts 74 MB): every source
touched to a fresh-checkout mtime and the archived artifacts restored,
cargo compiles (`Compiling paperclip-runner-core`, 24.8s and 25.1s, the
two controls); restored and marked by `mark-current`, `Finished release
profile in 0.16s` with no `Compiling`, and the same on a second build.
Then, for each of the fourteen files cargo wrote for the two units, one
at a time: restored, marked and that file deleted, cargo recompiles
without any of the seven `REQUIRED` files and reports fresh without any
of the other seven; restored with that file deleted and run through
`mark-current`, the seven required ones print `action: discarded` naming
the file and the others print `marked`. A restore holding only the seven
required files builds fresh in 0.15s, cargo recreates the uplifted
binary, and `stage-runner-binary.mjs` copies it into `dist/bin`.
- `node --test '.github/scripts/tests/*.test.mjs'`: 378 of 381 pass. The
3 failures are in `docker-canary-promotion.test.mjs` and fail the same
way on an unmodified `master` checkout in this sandbox (the stand-in
docker writes no `calls.jsonl`).
- `node --test scripts/__tests__/release-verify-workflow.test.mjs
scripts/__tests__/e2e-shard.test.mjs
scripts/__tests__/run-vitest-stable-shard.test.mjs`: 99 of 101 pass; the
2 failures spawn `pnpm`, which is not on this sandbox's PATH, and fail
the same way on `master`.
- Both workflows parse as YAML. No job has a duplicate step name or step
id.
- The mechanism, with the real toolchain (`rustc 1.97.1`, the package's
pin) on a copy of the crate: a cold `cargo build --release -p
paperclip-runner-core --bin paperclip-runnerd` (2m23s here). Then the
artifact set was archived with `tar`, removed, every source re-copied so
each had a new mtime, and the set restored:
- restored, not marked: `Compiling paperclip-runner-core`, 24.6s (the
control; this is what CI does today);
- restored and marked by `mark-current`: `Finished release profile in
0.15s`, no `Compiling`; a second build is the same;
  - one Rust source edited after a marked restore: `Compiling`, 24.0s;
- one protocol schema edited after a marked restore: `Compiling`, 23.9s
(the protocol files enter cargo's dep-info as absolute paths and are
hashed into the key);
- the rlib removed from a restored set: `mark-current` prints `action:
discarded, reason: incomplete: the lib rlib is missing` and removes the
rest, and cargo compiles.
- Not run here: the workflow steps themselves. `pr.yml` pins
`pr-trusted.yml@master`, so this pull request's own `Build` job runs the
current reader steps, and the writer runs only on a master push. The
first PR runs after the merge and after the next master push show the
`Restore the Runner release crate` step's result and the
`paperclip.runner-release-crate.v1` line in their `Build` logs.

## Risks

- Correctness: a reader can only restore artifacts that master built
from inputs byte-identical to its own merge tree, because every input
the build reads is in the key, and the key has no prefix match. A pull
request cannot write. The trust boundary is unchanged: master writes, a
pull request restores master's entry.
- An input the build reads that is outside the hashed patterns would let
a stale artifact pass. The release dep-info lists the crate sources and
the protocol schemas and fixtures; all are hashed. The guard test
resolves every `include_str!` and `include_bytes!` in the crate, literal
or `concat!`, and fails if one lands outside the hashed inputs or uses a
form it cannot resolve, so a new include form cannot slip past it by not
matching a regex.
- GitHub-hosted runners check out under `/home/runner/work`, the
`runs-on` fleets under `/home/runner/_work`. Cargo records the included
protocol files as absolute paths, so an entry from one root is useless
under the other. The checkout root is in the key, so the other root
misses instead of restoring something cargo then discards.
- A cache that does not keep mtimes on restore changes nothing: the
artifacts are then already newer than the checkout, and `mark-current`
sets them to now in any case.
- Cost: one new entry of about 70 MB per distinct input set, written
only by the master Rust lane on a miss, plus one `cargo clean` and
`build:binary` (about 40s to 50s) on that lane when the inputs changed.
The readers pay one restore of about 70 MB (two to three seconds at the
fleet's observed restore speed) and a sub-second `mark-current`.
- An incomplete entry (a writer that saved a partial set) is discarded
by `mark-current` and costs one compile, the same as a miss.

## Model Used

- Claude (Anthropic), model id `claude-fable-5-1` (Claude Fable 5.1),
extended thinking, with tool use (shell, file editing, GitHub CLI, a
local Rust toolchain for the experiment) 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:31 -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 39fec166b9 Merge the exact Pi v23 install-probe digest
Co-Authored-By: Paperclip <noreply@paperclip.ing>

* codex/claude-runner-default:
  fix(runner): pin the Pi v23 digest in its install probe
2026-10-12 12:55:37 -05:00
DottaandPaperclip 33c9216614 fix(runner): pin the Pi v23 digest in its install probe
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 12:55:28 -05:00
DottaandPaperclip a1ac0a7324 Merge the current Pi clean-install assertion
Co-Authored-By: Paperclip <noreply@paperclip.ing>

* codex/claude-runner-default:
  fix(runner): verify the current Pi profile in clean installs
2026-10-12 12:55:06 -05:00
DottaandPaperclip ca52d2998c fix(runner): verify the current Pi profile in clean installs
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 12:54:57 -05: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
Devin Foley 4f399cec9f fix(docker): skip the full-tree ownership walk on routine boots (#15992)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The Docker image keeps all instance state under `PAPERCLIP_HOME`,
and deployments mount a volume there
> - Before it drops privileges, the entrypoint makes sure that the
`node` user owns the home (#10530)
> - The ownership probe is a first-mismatch `find` over the whole tree.
When ownership is correct, `-quit` never fires, so every boot stats
every file on the volume
> - On homes with many agent worktrees and `node_modules` trees, this
walk takes minutes before Node starts. That is close to common 300s
healthcheck windows
> - This pull request keeps a cheap shallow probe on every boot. It runs
the full-tree probe only once per volume and node UID/GID, and records
the result in a marker file
> - The benefit is that routine boots no longer scale with the file
count, and the original root-owned-volume fix stays in place

## Linked Issues or Issue Description

Refs #10431 (the open follow-up about the remaining cost of the
ownership walk). Related: #10530 (the change that added the full-tree
probe).

**What happened?**
On a hosted multi-instance deployment, we measured the time from
container start to the first app log line. Instances with many files on
the volume (agent worktrees with `node_modules`) took about 190-235s.
Small instances took 5-22s. One 30 GB volume with few files took 24s, so
the cost follows the file count, not the volume size. Almost all of this
time is the `find "$home_dir" \( ! -user node -o ! -group node \) -print
-quit` walk in `scripts/docker-entrypoint.sh`. When ownership is already
correct, `-quit` never fires, so the walk stats every inode on every
boot.

**Expected behavior**
When ownership is already correct, the entrypoint pre-start phase is
short and does not depend on the number of files in `PAPERCLIP_HOME`. A
fresh root-owned volume, a UID/GID remap, and an upgrade from an older
image are still repaired before the privilege drop.

**Steps to reproduce**
1. Run the image with a volume on `/paperclip` that holds a few hundred
thousand files, all owned by `node:node`.
2. Restart the container.
3. Compare the container start time with the first server log line. The
gap grows with the file count, even though no `chown` runs.

**Paperclip version or commit**
`master` at a9830414c7

**Deployment mode**
Docker production image (any platform that mounts a volume on
`PAPERCLIP_HOME`)

## What Changed

- `scripts/docker-entrypoint.sh`: the ownership probe is now tiered.
- Every boot runs a shallow probe (`find -maxdepth 3`) over the home and
its first three levels. A fresh root-owned mount and a UID/GID remap
both change the owner of the home directory, so this probe catches them.
- The full-tree probe runs only when
`PAPERCLIP_HOME/.paperclip-ownership-verified` is missing or records a
different node UID/GID (format `v1 <uid>:<gid>`). This catches deep
mismatches, including the first boot after an upgrade from an older
image.
- Any mismatch repairs the whole tree with `chown -R node:node`, as
before. After a clean probe or a repair, the entrypoint writes the
marker as the `node` user (through `gosu`), so the marker never trips
the shallow probe. A failed marker write is ignored: the only cost is a
full probe on the next boot. If `find` exits non-zero without a mismatch
(for example, an I/O error), the walk is incomplete, so the entrypoint
does not write the marker and the next boot probes again.
- `scripts/docker-entrypoint.sh`: timestamped log lines at entrypoint
start, around the full probe and the repair (with durations), and right
before the `exec` of the server. You can now measure the pre-start phase
from the container log.
- `server/src/__tests__/docker-entrypoint.test.ts`: the `find` stub now
tells the shallow probe from the full probe. New tests cover each path
(see Verification).
- `doc/DOCKER.md`: documents the marker, how to force a full check, and
the new log lines.

## Verification

- `cd server && npx vitest run src/__tests__/docker-entrypoint.test.ts
src/__tests__/container-init-reaping.test.ts
src/__tests__/docker-build-stamp.test.ts`: 40 passed. New cases:
- correct ownership + current marker → shallow probe only, no full walk,
no `chown`
- current marker + deep-only mismatch → full walk skipped (this is the
documented trade-off)
- no marker (upgrade) → one full walk, no `chown`, marker written as `v1
1000:1000`
- no marker + deep mismatch → full walk finds it, `chown -R`, marker
written
- root-owned top level with a current marker → `chown -R` without a full
walk
- full walk fails without a mismatch → no `chown`, no marker, retry
logged
- marker for another UID/GID → full walk runs again, marker rewritten as
`v1 1001:1001`
- timestamped `starting`, `ownership repair finished in Ns`, and
`starting app as node` lines, in order, before the command runs
- The new tests fail against the old script. The 11 existing tests pass
unchanged.
- `cd server && npx tsc --noEmit`: passes.
- `shellcheck -s sh scripts/docker-entrypoint.sh`: clean. `dash -n`:
syntax OK.
- Manual run with the real `find` (BSD find, script run under `dash`)
against a 200k-file tree. Only `id`/`usermod`/`groupmod`/`chown`/`gosu`
were stubbed, and `node` was mapped to the local user and group:
- boot 1, no marker, one deep group mismatch → full probe, `chown -R`,
marker written
  - boot 2, marker present → no full probe, no `chown`
- boot 3, no marker, clean tree → full probe, no `chown`, marker written
  - boot 4, marker present, clean → no full probe, no `chown`
  - boot 5, top-level group mismatch with marker → `chown -R`
- I did not build the image or measure boot time in a container. Docker
was not available on the machine I used. On a warm local disk the full
walk is sub-second, so the local timings say nothing about cold network
block storage.

## Risks

- Once the marker is current, a mismatch deeper than three levels is not
detected automatically. Examples: files written as root by `docker exec`
or a restore run as root, after the first verified boot. Before this
change, those files were repaired on the next boot. The workaround is in
`doc/DOCKER.md`: delete the marker and restart. A remap, a fresh volume,
and a root-owned top level are still detected on every boot.
- On the first boot after an upgrade, each existing volume pays one full
walk (the same cost as today). Later boots skip it.
- The marker is a new dotfile in `PAPERCLIP_HOME`. Backups and copies
that include the home carry it along. This is safe, because the marker
records the UID/GID that it verified, and a different UID/GID forces a
new full probe.
- The non-root path (restricted PodSecurity, OpenShift) does not change,
apart from two new stdout log lines.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`, 1M context) via Claude Code, with
tool use (shell, file editing). Default reasoning mode.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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
2026-10-12 10:22:43 -07:00
DottaandPaperclip 598e190cb3 test: bind installed native payloads and await saved state
Isolate actual Codex resolver fixtures from global bridge lookup and preserve host qualification blockers with literal evidence.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 11:45:22 -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 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
2223b7de9e perf(ci): stop building the standalone packages inside the public-install verifier (#15908)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Every pull request runs the `Build` check. Its last step is the
public npm install verifier, `scripts/verify-grok-npm-install.mjs`,
which stages the eighteen public packages reachable from the server and
the CLI, packs them, and installs them in a clean consumer.
> - Before it stages anything, the verifier runs
`scripts/build-standalone-public-packages.mjs`. That script installs and
compiles the seven sandbox-provider plugins that live outside the pnpm
workspace: one network `pnpm install --ignore-workspace` and one `tsc`
per plugin. The line was copied from the build block of `release.sh`
when the verifier was written in #14024.
> - release.sh needs that build because it publishes those plugins. The
verifier does not for anything it stages. Its package closure follows
`workspace:` specs from the server and the CLI, and a `workspace:` spec
can only name a workspace package, so none of the eighteen packages it
packs is a standalone package and no tarball reads that build's output.
> - One optional path at the end of the verifier does read it:
qualification-package retention, which `docker-runner-check.yml` enables
with `PAPERCLIP_RUNNER_QUALIFICATION_PACKAGES_DIR` after running only
`pnpm build`, packs the Daytona plugin from its `dist`. The PR `Build`
job never sets that variable.
> - The timing line from #15868 and #15888 puts the verifier's staging
phase at 75.8s on the latest run, of which the staged sources, the
bundled installs and the packs account for 46.5s. The rest, about 29s,
is the work before staging, and the standalone build is almost all of
it. `Build` at 433-530s is the slowest check in most PR runs once the
server shards drop, so that time sits on the run's critical path.
> - This pull request runs the standalone build only when retention is
requested, keeps the `ui-dist` copy the server package needs, and times
both on the timing line so what is left before staging is visible.
> - The benefit is a `Build` job about 21-28s shorter, with no change to
what the verifier installs or checks, no loss of coverage for the
standalone build because Canary Dry Run still runs it through
`release.sh`, and no change to the retention path that reads it.

## Linked Issues or Issue Description

Replaces #15892, which GitHub closed automatically when the branch of
#15888 (its base at the time) was deleted on merge. Same two commits,
rebased onto `master` on top of #15888 (1ac48aff), #15868 (026bdcd0) and
#15861 (85f4205d), which are all merged. #15892 reached Greptile 5/5
with every check green on the same content; the review history is there.

**What existing behavior does this improve?**

The `Build` PR check. Its public npm install verifier builds the seven
standalone sandbox-provider plugins before it stages the eighteen
packages it installs, and none of those packages uses that build.

**Subsystem affected**

CI and release scripts (`scripts/verify-grok-npm-install.mjs`,
`scripts/build-standalone-public-packages.mjs`).

**Current behavior**

The verifier runs `node scripts/build-standalone-public-packages.mjs`
and then `bash scripts/prepare-server-ui-dist.sh` before it stages any
package, on every run. The first script removes each sandbox-provider
plugin's `node_modules`, runs `pnpm install --ignore-workspace` against
the registry, links the in-repo plugin SDK, and runs the plugin's `tsc`
build, four plugins at a time. On the last `Build` run before this
change
([38146108750](https://github.com/paperclipai/paperclip/actions/runs/38146108750))
`stage_packages_ms` was 75.8s and the staged sources, bundled installs
and packs inside it took 0.9s, 15.7s and 29.9s, which leaves about 29s
before staging. The verifier's package closure, computed in the same way
as the script computes it, contains eighteen workspace packages and no
sandbox-provider plugin.

**Proposed behavior**

Run the standalone build only when
`PAPERCLIP_RUNNER_QUALIFICATION_PACKAGES_DIR` is set, because that is
the one path that reads it, and report it as `stage_standalone_build_ms`
when it runs. Always run the `ui-dist` copy before staging. With
`PAPERCLIP_RELEASE_REUSE_UI_DIST=1`, which the verifier already sets,
that copies the `ui/dist` that `pnpm build` produced into
`server/ui-dist`, which the server package publishes. Report that copy
as `stage_ui_dist_ms` on the timing line. Leave `release.sh` unchanged,
so the release flow and Canary Dry Run still build the standalone
packages where they are published.

**Reason and benefit**

The verifier proves that the eighteen public packages install cleanly
from the public registry. The standalone build adds seven network
installs and compiles whose output is never packed. Removing it from the
PR path shortens the staging phase by about 21-28s. Measured on this
PR's first commit
([38150787810](https://github.com/paperclipai/paperclip/actions/runs/38150787810)):
`stage_packages_ms` 54.2s against 75.8s, with `stage_ui_dist_ms` 21ms,
and the time before staging, `stage_packages_ms` minus the four staging
keys, down from about 29s to under 0.1s.

**Breaking changes**

None. The same eighteen packages are staged, packed, checked and
installed from the same sources. The retention path builds exactly what
it built before. `scripts/build-standalone-public-packages.mjs` exports
two existing helpers for the new guard test and otherwise does not
change.

## What Changed

- `scripts/verify-grok-npm-install.mjs`: the
`build-standalone-public-packages.mjs` call before staging runs only
when `PAPERCLIP_RUNNER_QUALIFICATION_PACKAGES_DIR` is set, in the same
position as before, and reports `stage_standalone_build_ms`. The flag is
read once into `retainQualificationPackages`, and the retention call at
the end is gated by the same variable, so the build and the retention
that reads it cannot diverge. The `prepare-server-ui-dist.sh` call
stays, is timed, and reports `stage_ui_dist_ms`. The comment explains
why release.sh and the retention path need the standalone build and the
staged packages do not. The timing-line comment lists both new keys.
- `scripts/build-standalone-public-packages.mjs`:
`parseWorkspaceEntries` and `isWorkspacePackage` are exported. No
behavior change.
- `scripts/__tests__/grok-public-install-sandbox.test.mjs`: one new
guard test. It asserts the verifier references the standalone build
exactly once, inside the retention gate, timed under its own key, and
that no second reading of the retention variable exists; that
`retain-runner-qualification-packages.mjs` still packs the Daytona
plugin from its `dist`, and that the Daytona plugin is a standalone
package, so the gate cannot pass vacuously; recomputes the verifier's
package closure from `release-package-map.mjs list` and the manifests
through `workspace:` specs; checks that the public package map still
carries packages outside the pnpm workspace; checks that every package
in the closure is inside the workspace globs of `pnpm-workspace.yaml`;
checks that the server package still publishes `ui-dist`; and checks the
order: closure, gated standalone build, `ui-dist` copy, then staging,
with the reuse flag set and the timing keys written.

### Review fix, second commit

Greptile's P1 on the first commit was valid: `docker-runner-check.yml`
runs `pnpm build` and then the verifier with
`PAPERCLIP_RUNNER_QUALIFICATION_PACKAGES_DIR` set, and
`retainRunnerQualificationPackages` copies
`packages/plugins/sandbox-providers/daytona/dist`. The Daytona plugin is
excluded from the pnpm workspace, so `pnpm build` never produces that
directory and the first commit would have failed that job with `ENOENT`
after every install check had passed. The second commit restores the
build on exactly that path: the verifier builds the standalone packages
when, and only when, retention is requested, and the guard test pins the
gate to the reason for it.

## Verification

- `node --test scripts/__tests__/grok-public-install-sandbox.test.mjs
scripts/__tests__/pack-staged-packages.test.mjs
scripts/__tests__/stage-bundled-packages.test.mjs
scripts/__tests__/prefetch-child.test.mjs
scripts/__tests__/retain-runner-qualification-packages.test.mjs`: 37
tests pass.
- The updated guard test fails against the first commit's verifier (no
retention gate around the build) and passes with the second commit. The
first commit's test failed against #15888's verifier the same way.
- `node --check` passes on the verifier.
- The verifier itself needs Docker and a network, which this workspace
does not have. The first commit's `Build` job
([38150787810](https://github.com/paperclipai/paperclip/actions/runs/38150787810))
ran the branch's verifier and measured the saving: `stage_packages_ms`
54.2s (was 75.8s), `stage_ui_dist_ms` 21ms, `stage_sources_ms` 0.8s,
`stage_bundled_ms` 21.0s, `stage_pack_ms` 32.4s, so under 0.1s remains
before staging where about 29s was. The second commit does not change
the PR path: the gate is false on every PR run, so that job's timing
line should show the same shape with no `stage_standalone_build_ms` key.
- The retention path was not run here: it needs Docker and the
`docker-runner-check` workflow's inputs. The code on that path is the
same `run(process.execPath, [...build-standalone-public-packages.mjs])`
call the verifier made before this PR, in the same position.
- `scripts/__tests__/release-dry-run-notes.test.mjs` exercises the
standalone build inside `release.sh` and is unchanged; 11 of its 12
tests spawn `pnpm`, which is not installed in this workspace, and fail
the same way on #15888's head. They pass in CI, where `pnpm` is
installed.
- The first commit's run failed only on `General tests (server
(11/12))`: all 74 files and 1257 tests passed, then Vitest caught a
teardown `EACCES` removing the per-worker `PAPERCLIP_HOME` under
`native-session-resumption.test.ts`, the same error as the first attempt
of #15888's run 38146108750. Neither PR touches server code; the failure
is noted on the tracking issue.

## Risks

- If a future change makes a package the verifier installs depend on a
standalone package, the verifier would stage an unbuilt `dist`. That
cannot happen through a `workspace:` spec, which is the only kind the
closure follows, and the guard test fails if the closure ever contains a
package outside the workspace globs.
- If a future path in the verifier reads a standalone build without
setting the retention variable, it would see no `dist`. The guard test
pins the single reference to the gate; a new reader has to extend the
gate and the test together.
- The sandbox-provider plugins' builds are no longer exercised on every
PR by the `Build` job. They are still built by `release.sh` in Canary
Dry Run on every PR, by the verifier whenever retention is requested,
and the `build-standalone-concurrency` and `release-dry-run-notes` tests
still cover the script.
- Low risk otherwise. The bundled installs, the packs, the consumer
install, and every probe are unchanged.

> 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

- Claude Fable 5.1 (Anthropic), model ID `claude-fable-5-1`, run as a
Paperclip agent through the Claude Code local adapter with tool use
(shell, file edits, GitHub CLI). Reasoning mode: extended thinking.

## 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 (CI is running on the second
commit)
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(the P1 on the first commit is fixed in the second; re-review 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 12:14:32 -07:00
1ac48aff8d perf(ci): pack the verifier's staged packages concurrently (#15888)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Every pull request runs the `Build` check. Its last step is the
public npm install verifier, `scripts/verify-grok-npm-install.mjs`,
which stages the eighteen public packages, packs them, and installs them
in a clean consumer.
> - The verifier packs the eighteen packages one after another with `npm
pack`. Each pack is a Node start-up plus gzip of that package's files.
The timing line added in #15868 shows this loop at 35.6-37.9s on four
`Build` runs, the largest part of the staging phase now that the bundled
installs overlap.
> - `Build` at 433-530s is the slowest check in most PR runs once the
server shards drop, so its staging phase sets the wall-clock time of the
run.
> - Each pack reads only its own `package-<index>` directory and writes
one tarball. The runner has four cores and the packs are CPU-bound.
There is no reason to run them one after the other.
> - This pull request runs the packs as concurrent children through the
prefetch helper, one per core, bundled packages first, each into its own
directory, and puts the results back into dependency order before every
tarball is checked.
> - The benefit is a shorter `Build` job, and a timing line that reports
the packs' serial cost next to their wall time, so the saving is
measured on this PR's own run instead of estimated.

## Linked Issues or Issue Description

Refs #15868, #15861. This branch is stacked on the branch of #15868,
which is stacked on #15861, because the same execution branch holds both
open PRs. The diff here is this PR's one commit. After #15868 merges,
this PR is retargeted to `master` and rebased.

**What existing behavior does this improve?**

The `Build` PR check. Its public npm install verifier packs the eighteen
public packages in one serial loop.

**Subsystem affected**

CI and release scripts (`scripts/verify-grok-npm-install.mjs`,
`scripts/prefetch-child.mjs`).

**Current behavior**

After the bundled installs are joined, the verifier runs `npm pack
--pack-destination <root>` for each of the eighteen packages in turn,
finds the new tarball by listing the shared root, and checks it with
`tar -tzf`. `stage_pack_ms` was 35.8s, 37.1s, 35.6s and 37.9s on the
four runs of #15868. The parent is idle apart from the one pack it waits
on, and three of the four cores are idle.

**Proposed behavior**

Start the packs as children through `startPrefetch`, at most one per
core (`os.availableParallelism()`), bundled packages first because they
make the largest tarballs. Each child packs into its own `pack-<index>`
directory, so the tarball a package produced is read from there instead
of guessed from a shared directory. Register each child for cleanup as
it starts. Put the results back into dependency order, then check every
tarball with `tar -tzf` as before, then copy them into the assets
directory as before. Report `stage_pack_check_ms`, `pack_concurrency`,
`pack_serial_ms` and `pack_longest_ms` on the timing line, and keep
`stage_pack_ms` as the whole step.

**Reason and benefit**

With four slots the step takes about the longest single pack or a
quarter of the serial sum, whichever is larger. The expected saving is
20-25s of the 36-38s, measured on this PR's own `Build` job by
`pack_serial_ms` against `stage_pack_ms`.

**Measured on this PR's first run
([38146108750](https://github.com/paperclipai/paperclip/actions/runs/38146108750),
`Build` 433s, verifier 214s):** `stage_pack_ms` 29.9s against 35.6-37.9s
before, so the step shed 6-8s, not the 20-25s estimated.
`pack_serial_ms` is 44.4s with `pack_concurrency` 4, and
`pack_longest_ms` is 28.3s: one pack (the server with its 513 MB bundled
closure, gzip level 9 inside npm) runs for almost the whole step, so the
step is bounded by that pack and not by the slot count. The seventeen
other packs fit inside it. The serial sum is 7-9s higher than the old
serial step, which is the four processes contending for the runner's
cores. `stage_packages_ms` 75.8s against 80.4-89.6s on #15868's runs;
`image_wait_ms` and `grok_wait_ms` stay 0.

**Breaking changes**

None. The same packages are packed with the same `npm pack` arguments,
the tarballs are checked the same way, and the consumer receives them in
the same order.

## What Changed

- `scripts/pack-staged-packages.mjs` (new): `packStagedPackages`
launches the packs in the order given with at most `concurrency`
children at once, through the prefetch helper, and resolves with each
entry's tarball path and wall time in the same order. Every child is
passed to `register` as it starts. The first failure rejects with the
tail of that child's log and nothing further is launched. A child that
leaves anything other than one tarball in its directory is a failure.
`packConcurrency` is one per core. `packTimings` gives the timing-line
keys.
- `scripts/verify-grok-npm-install.mjs`: each entry gets its `index` and
its own `pack-<index>` destination. The pack loop is replaced by one
`packStagedPackages` call, launched bundled-first and registered into
the `prefetches` list as each child starts, so an earlier failure stops
any pack still running before the scratch root is removed. The results
are sorted back into dependency order, every tarball is checked in that
order, and the copy into the assets directory is unchanged. Only the
lifecycle sentinel is still packed in-process.
- Timing line: `stage_pack_check_ms`, `pack_concurrency`,
`pack_serial_ms` and `pack_longest_ms` are added. `stage_pack_ms` still
covers the whole step, so the before and after numbers compare directly.
- `scripts/__tests__/pack-staged-packages.test.mjs` (new): five tests
with a local stand-in for npm and no network. The default concurrency
and the pack arguments; four packs with two slots run the two slow packs
together, start a third only once a slot is free, receive the exact `npm
pack` argument list, register every child, and come back in entry order
with their tarballs and timing keys although the fast packs finish
first; a failed pack rejects with its log tail, launches nothing more,
and leaves the running pack to be stopped; a pack that produces two
tarballs or none is a failure; and no entries resolve at once.
- `scripts/__tests__/grok-public-install-sandbox.test.mjs`: the guard
test on the verifier source now checks the import, the bundled-first
launch with cleanup registration, the reorder before the checks, the
checks before the copy, that only the sentinel is packed in-process,
that each pack has its own directory, and the new timing keys.
- `package.json`: the new test file is in `test:release-registry`.

## Verification

- `node --test scripts/__tests__/pack-staged-packages.test.mjs
scripts/__tests__/grok-public-install-sandbox.test.mjs
scripts/__tests__/stage-bundled-packages.test.mjs
scripts/__tests__/prefetch-child.test.mjs`: 35 tests pass (30 on
#15868's head plus the five new ones). The new file passes three
consecutive runs.
- `node --check` passes on both scripts.
- With the real npm 11.12.1 on Node 25.9: two small packages packed
through `packStagedPackages` with two slots produce one tarball each in
their own `pack-<index>` directory, with the expected `package/` listing
from `tar -tzf`.
- The verifier itself needs Docker and a network, which this workspace
does not have. This PR's own `Build` job runs the branch's verifier; its
timing line carries `stage_pack_ms`, `pack_serial_ms`, `pack_longest_ms`
and `pack_concurrency`, which is the measurement. Before, on #15868's
four runs: `stage_pack_ms` 35.8s / 37.1s / 35.6s / 37.9s.
- `docker-runner-check.yml` runs the same script after `pnpm build` and
needs no change.

## Risks

- Four `npm pack` processes run at once and share the host npm
configuration and cache. `npm pack --ignore-scripts` reads the package
directory and writes one tarball; it does not write to the cache beyond
its per-process log file. If a shared-state race appears, the failing
pack's log tail is in the error and the fix is to lower the concurrency.
- Memory and CPU: four Node processes instead of one on the four-core
`Build` runner, for about thirty seconds. The largest pack (the server
with its bundled dependencies) is the same process as before.
- The pack output moves from the CI log into per-pack log files in the
scratch root. A failure reports the last 2000 characters of the failing
pack's log; a successful pack's output is discarded with the scratch
root.
- The tarball checks (`tar -tzf`) now run after all packs instead of
after each pack. A bundled Codex payload is still rejected before any
tarball reaches the consumer; the only change is that the error arrives
a few seconds later.
- Low risk otherwise. The bundled installs, the consumer install, and
every probe are unchanged.

> 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

- Claude Fable 5.1 (Anthropic), model ID `claude-fable-5-1`, run as a
Paperclip agent through the Claude Code local adapter with tool use
(shell, file edits, GitHub CLI). Reasoning mode: extended thinking.

## 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 (run 38146108750: `server
(11/12)` failed on its first attempt on a teardown `EACCES` while
removing the per-worker `PAPERCLIP_HOME` after all 74 files and 1257
tests had passed, in a file this PR does not touch; the re-run of that
job passed and every check is green)
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

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

Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-11 12:02:19 -07:00
026bdcd046 perf(ci): run the verifier's bundled-dependency installs concurrently (#15868)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Every pull request runs the `Build` check. Its last step is the
public npm install verifier, `scripts/verify-grok-npm-install.mjs`,
which stages the eighteen public packages and installs them in a clean
consumer.
> - The verifier stages the packages in one serial loop. Three packages
bundle dependencies (`@paperclipai/server`,
`@paperclipai/adapter-utils`, `@paperclipai/db`). Each one runs a
host-side `npm install` of its bundled closure in-process, one after the
other, and nothing in the loop overlaps anything else. The staging phase
took 102-116s on recent `Build` jobs.
> - `Build` at 433-530s is the slowest check in most PR runs once the
server shards drop, so its staging phase sets the wall-clock time of the
run.
> - The three installs write only their own directories and read nothing
from each other. There is no reason to run them one after the other.
> - This pull request starts the three installs together as child
processes and copies the other fifteen packages while they run. Packing
stays serial and in order.
> - The benefit is a shorter `Build` job, and a timing line that reports
each install, so the saving is measured on this PR's own run instead of
estimated.

## Linked Issues or Issue Description

Refs #15861. This branch is stacked on the branch of #15861 because the
same execution branch holds that open PR. The diff here is this PR's
three commits. After #15861 merges, this PR is retargeted to `master`
and rebased.

**What existing behavior does this improve?**

The `Build` PR check. Its public npm install verifier stages the
eighteen public packages in one serial loop.

**Subsystem affected**

CI and release scripts (`scripts/verify-grok-npm-install.mjs`,
`scripts/prepare-bundled-package.mjs`, `scripts/prefetch-child.mjs`,
`scripts/prefetch-supervisor.mjs`).

**Current behavior**

The staging loop calls `prepareBundledPackage` in-process for the three
packages that bundle dependencies. Each call runs a host-side `npm
install` of that package's bundled closure, applies the repo patches,
and rewrites the manifest, before the loop moves to the next package.
The timing line reports the whole staging phase as one number, 102-116s
on recent runs. The three installs are the only network work in that
phase.

**Proposed behavior**

Stage the three bundled packages' sources first, start their installs at
the same time as children of the script's own CLI entry (the same path
`release.sh` uses), copy the other fifteen packages while the installs
run, join the installs, and then pack every package in dependency order
as before. Report `stage_sources_ms`, `stage_bundled_ms`,
`stage_pack_ms`, `bundled_wait_ms` and one `stage_bundled_<package>_ms`
per install on the timing line.

**Reason and benefit**

The staging phase shortens by the two shorter installs. Measured on this
PR's first run (below): the three installs sum to 25.9s and take 16.4s
of wall time together, so the overlap removes about 9.5s from the phase,
and the timing line now shows that packing (35.8s) and the
standalone-package build before the loop (about 27s) are the larger
remaining costs. The original estimate of 30-50s was too high because it
assumed the installs were about half of the phase; they are a quarter.

**Breaking changes**

None. The packed tarballs are produced by the same script, in the same
order, and are checked the same way.

## What Changed

- `scripts/stage-bundled-packages.mjs` (new): `startBundledInstalls`
starts one child per bundled package through the prefetch helper from
#15849, with the child's output in a log file. `bundledInstallTimings`
reads each child's wall time for the timing line. `bundledPackageSlug`
maps a package name to its log and timing key.
- `scripts/verify-grok-npm-install.mjs`: the staging loop is split. The
bundled packages' sources are staged first and their installs start
together. The other packages are copied while the installs run. The
install handles are pushed into the `prefetches` list at once, so an
earlier failure stops any install still running before the scratch root
is removed. The join comes before the first `npm pack`. The pack loop
walks every package in `needed` order and checks each tarball as before.
`prepareBundledPackage` is no longer imported.
- Timing line: `stage_sources_ms`, `stage_bundled_ms`, `stage_pack_ms`,
`bundled_wait_ms` and `stage_bundled_server_ms`,
`stage_bundled_adapter_utils_ms`, `stage_bundled_db_ms` are added.
`stage_packages_ms` and the download keys are unchanged, so the before
and after numbers compare directly.
- `scripts/__tests__/stage-bundled-packages.test.mjs` (new): five tests
with local Node children and no network. The slug mapping; the children
run the CLI entry `release.sh` runs and never an in-process call; two
installs run at the same time, each with its own source and target, and
their logs and timing keys are correct (each child exits only after the
other has started, so serial children would fail); a failed install
rejects with its own log tail while the other is still running and is
then stopped; and stopping the installs after one fails also stops the
stand-in npm process the other wrapper is blocked on.
- `scripts/__tests__/grok-public-install-sandbox.test.mjs`: one guard
test on the verifier source. The bundled sources are staged before the
installs start, the installs are registered for cleanup before the other
packages are copied, the join comes before the pack loop, the pack loop
is in order, there is still exactly one package pack loop, and the
timing keys are reported.
- `package.json`: the new test file is in `test:release-registry`.

### Review fix (second commit): stop a prefetch's whole process tree

Greptile pointed out that `stopPrefetches` signalled only the Node
wrapper of each install, while the wrapper is blocked in
`execFileSync('npm', ['install', ...])`. After one install failed, the
other wrapper died and its npm process kept writing into the scratch
root the verifier was about to remove.

- `scripts/prefetch-child.mjs`: on POSIX every prefetch child is started
`detached`, so it leads its own process group. `stopPrefetches` signals
the group (`process.kill(-pid, ...)`) and waits until no member of the
group exists, with `SIGKILL` after the grace period and a second grace
period before it gives up so cleanup cannot hang. On Windows the child
is signalled directly as before.
- `scripts/__tests__/prefetch-child.test.mjs`: two tests. A stopped
prefetch's grandchild is gone after `stopPrefetches` returns, and a
`SIGINT` to a separate parent process reaches the grandchild while the
parent still exits with `SIGINT`. Both are skipped on Windows.
- `scripts/__tests__/stage-bundled-packages.test.mjs`: one test, the
cancellation case Greptile asked for. The stand-in
`prepare-bundled-package` blocks in `execFileSync` on a stand-in npm
that announces its pid; after the other install fails, `stopPrefetches`
must leave that pid gone.

All three new tests fail against the previous helper (`grandchild <pid>
outlived stopPrefetches`, `outlived the parent's SIGINT`, `stand-in npm
<pid> outlived stopPrefetches`) and pass with the fix.

### Review fix (third commit): the parent runs no signal handler

Greptile pointed out that the second commit forwarded the terminal's
signals to the detached groups through a JavaScript handler in the
parent. The verifier spends most of its time blocked in `execFileSync`
(the standalone builds, every `npm pack`), where no handler can run, and
installing the handler also replaced the parent's immediate default
termination. So a Ctrl-C during a synchronous phase did nothing until
that command returned, and the detached installs and downloads kept
running meanwhile. Confirmed with the new test against the second
commit's helper: the parent survived the `SIGINT`, exited with code 1
once the synchronous command had died, and its handler never ran.

- `scripts/prefetch-supervisor.mjs` (new): every prefetch command now
runs under this small Node script as the leader of its own process
group. The supervisor holds a pipe from the parent on stdin (which the
parent writes to only to request a stop where it cannot signal the
group; see the fourth commit). The kernel closes it when the parent
exits for any reason, including a signal delivered while the parent is
blocked, or `SIGKILL`, and the supervisor then stops its group
(`SIGTERM`, `SIGKILL` after the grace period) and says so in the log. It
passes group-wide signals on to the command and stays alive until the
command exits, so its own exit reports the command's exit code or
signal. Run by hand with a terminal or `/dev/null` on stdin it watches
nothing.
- `scripts/prefetch-child.mjs`: `startPrefetch` spawns the supervisor
with the command, with stdin as the pipe. The forwarding handlers are
gone; the parent installs no signal handler, so its default termination
stays immediate. `stopPrefetches` is unchanged.
- `scripts/__tests__/prefetch-child.test.mjs`: the first test asserts
the parent has no `SIGINT`/`SIGTERM`/`SIGHUP` listener after a prefetch
starts. The "signal to the parent" test now runs for `SIGINT` and
`SIGKILL` and checks the supervisor's log line. One new test gives a
separate parent its own process group, blocks it in `execFileSync` on a
20s command, and sends `SIGINT` to that group the way a terminal sends
Ctrl-C: the parent must die of `SIGINT` within two seconds and the
prefetch's grandchild must be gone. Against the previous helper the
parent exits with code 1 instead, the grandchild outlives `SIGKILL`, and
the handler-count assertion fails.

### Review fix (fourth commit): a deliberate stop on Windows goes
through the pipe

Greptile pointed out that on Windows, where `startPrefetch` now returns
the supervisor, `stopPrefetches` called `child.kill('SIGTERM')` on it.
Windows has no process groups and `kill` there ends a process outright,
so the supervisor died without running its handler and its command kept
running after cleanup returned.

- `scripts/prefetch-child.mjs`: without process groups, the graceful
stop no longer signals the supervisor. `stopPrefetches` writes a stop
request to the pipe the supervisor already watches for the parent's exit
(`stdin.end('stop\n')`). Only the `SIGKILL` escalation targets the
supervisor itself, after it has had its grace period. POSIX keeps the
group signal, so `stopPrefetches` and the existing stop and failure
tests are unchanged there.
- `scripts/prefetch-supervisor.mjs`: data on stdin is a stop request and
EOF is the parent's exit; both end the group the same way (`SIGTERM`,
`SIGKILL` after the grace period) and the log line names which one it
was.
- `scripts/__tests__/prefetch-child.test.mjs`: one new test writes the
stop request to a running prefetch's pipe instead of calling
`stopPrefetches`. The supervisor must exit with `SIGTERM`, the command's
grandchild must be gone, and the log must say `stop requested` and not
`parent exited`. The previous supervisor fails it on the log line, since
it treated the request as the parent's exit. The Windows kill itself
cannot be reproduced on the Linux runners, so the Windows branch of
`signalTree` is verified by that supervisor test plus reading its two
lines. The stand-in that ignores `SIGTERM` is now skipped on Windows,
where `kill` cannot be ignored.

## Verification

- `node --test scripts/__tests__/stage-bundled-packages.test.mjs
scripts/__tests__/grok-public-install-sandbox.test.mjs
scripts/__tests__/prefetch-child.test.mjs`: 30 tests pass (was 19 before
this PR, 24 after the first commit, 27 after the second, 29 after the
third). The three files pass three consecutive runs.
- `node --check` passes on all four scripts.
- The verifier itself needs Docker and a network, which this workspace
does not have. This PR's own `Build` job runs the branch's verifier.
Measured on all four commits' runs
([38132868803](https://github.com/paperclipai/paperclip/actions/runs/38132868803):
`Build` 428s, `pnpm build` 112s, verifier step 214s;
[38136912842](https://github.com/paperclipai/paperclip/actions/runs/38136912842):
`Build` 447s;
[38139871529](https://github.com/paperclipai/paperclip/actions/runs/38139871529):
`Build` 432s;
[38142762538](https://github.com/paperclipai/paperclip/actions/runs/38142762538):
`Build` 457s, `pnpm build` 123s, verifier step 231s):

| Key | Run 1 | Run 2 | Run 3 | Run 4 | Before |
| --- | --- | --- | --- | --- | --- |
| `stage_packages_ms` | 80.4s | 84.9s | 81.5s | 89.6s | 102-116s on
master runs 38112764439, 38111191543, 38110932348, 38110914319,
38110897745, 38127611057; 84.5s on #15849's run on a runner as fast as
these |
| `stage_sources_ms` | 1.4s | 0.7s | 0.9s | 0.9s | (inside the number
above) |
| `stage_bundled_server_ms` / `adapter_utils` / `db` | 16.3s / 5.0s /
4.6s | 14.9s / 3.6s / 3.3s | 16.4s / 5.2s / 4.7s | 17.1s / 4.4s / 3.8s |
ran one after another, 21.8-25.9s together |
| `stage_bundled_ms` (wall, all three) | 16.4s | 15.0s | 16.5s | 17.2s |
21.8-25.9s |
| `bundled_wait_ms` | 15.3s | 14.7s | 16.1s | 16.7s | — |
| `stage_pack_ms` | 35.8s | 37.1s | 35.6s | 37.9s | (inside the number
above) |
| `consumer_install_ms` | 94.4s | 97.8s | 95.7s | 99.8s | 97-134s |
| `image_prefetch_ms` / `image_wait_ms` | 33.7s / 0ms | 31.8s / 0ms |
34.7s / 0ms | 33.7s / 0ms | unchanged |
| `grok_prefetch_ms` / `grok_wait_ms` | 2.1s / 0ms | 2.1s / 0ms | 2.0s /
0ms | 2.1s / 0ms | unchanged |

The overlap removed about 7-10s from the staging phase on each of the
four runs (21.8-25.9s of installs in 15.0-17.2s of wall time). The third
and fourth commits changed no timing: both waits stay at 0ms and the
staging phase sits inside the band of the first two runs. The rest of
the phase is the standalone-package build and the server UI dist prep
before the loop (about 27-32s) and the eighteen serial `npm pack` plus
`tar -tzf` checks (36-37s).

- `docker-runner-check.yml` runs the same script after `pnpm build` and
needs no change.

## Risks

- Three `npm install` processes share the host npm cache at the same
time. npm's cache (`cacache`) is designed for concurrent writers, and
the three installs have disjoint target directories. If a shared-cache
race appears, the failing install's log tail is in the error and the fix
is to serialize again. The first CI run showed none.
- The install output moves from the CI log into per-install log files in
the scratch root. A failure reports the last 2000 characters of the
failing install's log; a successful install's output is discarded with
the scratch root.
- Memory: three concurrent installs on the `Build` runner instead of
one. The closures are small (the server's eight bundled packages are the
largest), so this stays well under the runner's memory.
- Process groups: a detached child no longer receives the terminal's
signals on its own. The supervisor stops its group when the parent is
gone, which it learns from the kernel closing the stdin pipe, so this
holds for a Ctrl-C while the parent is blocked in synchronous work and
for `SIGKILL` alike. The parent keeps Node's default signal handling. If
the supervisor itself is killed with `SIGKILL` before its command, the
command is orphaned and finishes on its own (each install ends within
seconds; the image pull within a minute), the same as any orphaned
grandchild before this PR.
- One extra Node process per prefetch (five per verifier run). Each
costs about 40ms to start, inside the time the parent spends staging
anyway.
- Low risk otherwise. Packing, the tarball checks, the consumer install,
and every probe are unchanged.

> 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

- Claude Fable 5.1 (Anthropic), model ID `claude-fable-5-1`, run as a
Paperclip agent through the Claude Code local adapter with tool use
(shell, file edits, GitHub CLI). Reasoning mode: extended thinking.

## 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 on all four commits
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(on the fourth commit Greptile reviewed 8 files and added no comments;
all three earlier threads are resolved)
- [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:44 -07:00
42ecc96556 perf(ci): prefetch the consumer image and Grok binary during package staging (#15849)
## 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 #15826 leveled the server shards and #15831 split
`workspaces-b`, the `Build` lane is the slowest check: 634s in run
38107578322 (2026-10-11 03:11Z), ahead of the next server shard at 574s.
It was 533s in run 38107430861 and 543s to 568s in earlier runs that
night.
> - `Build` is about 100s of setup, 148s to 195s of `pnpm build`, and
278s to 320s of `scripts/verify-grok-npm-install.mjs`. The verifier
stages and packs the public packages first (84s to 100s of CPU work),
and only then pulls the pinned `node:24-trixie` consumer image (8
layers, 440 MB compressed) and downloads the Grok binary (65 MB gzip,
166 MB verified). Both network waits sit after the staging work on the
critical path.
> - Neither download depends on the packed packages. The image is pinned
by digest and the Grok binary is checked against its pinned sha256 by
the provisioning script, so starting them early changes no verified
property.
> - This pull request starts both downloads as background children
before staging and joins them where they are needed: the image before
the first sandbox, the binary after the negative probe. It also prints
one timing line per phase so the next slow part is visible in the CI
log.
> - The benefit is that the whole pull and the whole Grok download hide
behind the staging phase. The verifier should shed the full pull time on
a fresh runner, and the next cycle can read the phase split instead of
inferring it from log gaps.

## Linked Issues or Issue Description

**Describe the enhancement**

The `Build` PR check is now the slowest check in the run. Its last step,
the public npm install verifier, pulls the consumer Docker image and
downloads the Grok binary only after it has finished staging and packing
the eighteen public packages. Both downloads could run while the
packages are staged.

**Why is this needed**

The slowest check decides the wall-clock time of every PR run. In the
most recent fully green run (38107578322), `Build` took 634s: `pnpm
build` 195s and the verifier 320s. The verifier's log shows the staging
phase ends at +100s and the Grok result arrives at +267s. The image pull
and the Grok download are inside that second window, in series with the
consumer install and the probes.

**Proposed solution**

Start `docker pull --platform linux/amd64 <pinned image>` and
`provision-grok.mjs` as background children at the top of the verifier,
before the staging loop. Await the image right before the first sandbox
call. Provision Grok into a prefetch directory and rename it into
`native/grok` only after the "npm must not provision Grok" assertion and
the negative probe, so that assertion keeps its meaning and only the
positive probe sees the binary. Print a
`paperclip.public-npm-install.timing.v1` line with wall time per phase
and the time the main flow waited on each prefetch.

**Alternatives considered**

- Run the verifier as its own job in parallel with `Build`. It needs the
full `pnpm build` output, so that job would be as long as `Build` is
now.
- Warm the consumer npm cache from an Actions cache. That changes the
"clean public npm install" property the verifier reports. Not done here;
the new timing line will show whether the consumer install is worth that
discussion.
- Pack the eighteen packages concurrently. The three bundled packages
run their own npm installs in series inside `prepareBundledPackage`,
which is synchronous. A larger restructure; left for a later cycle with
timing data.
- Overlap the Pi runtime setup with the Grok probes. Both use the same
consumer directory from separate containers, and the probes after
provisioning are short. Not worth the concurrency on shared state.

## What Changed

- `scripts/prefetch-child.mjs` (new): `startPrefetch({ name, cmd, args,
cwd, env, logPath })` spawns a background child with no inherited
terminal and its stdout and stderr in a log file, and returns `{ name,
child, done, durationMs }`. `done` resolves on exit 0 and otherwise
rejects with the tail of the log; the rejection is observed inside the
helper, so an unawaited failure never becomes an unhandled rejection.
`durationMs` ends at the child's last write to its log (the exit event
is observed late when the caller is blocked in synchronous work; both
downloads print their final status line as they complete).
`stopPrefetches(prefetches, { graceMs })` sends `SIGTERM` to every child
still running, escalates to `SIGKILL` after the grace period, and
resolves only once all of them have exited.
- `scripts/verify-grok-npm-install.mjs`: the image pull and the Grok
provisioning start through the helper inside the `try` block, before the
staging loop. The main flow awaits the image before the first `npm
install` sandbox and awaits Grok after the negative probe, then renames
the staged binary into `native/grok` before the positive probe. The
`finally` block awaits `stopPrefetches` and only then removes the
scratch root, so cleanup never races a child's writes. The script prints
one timing JSON line after the Pi probe, with the per-phase wall times,
the two `*_wait_ms` values, and the corrected `*_prefetch_ms` values.
- `scripts/__tests__/prefetch-child.test.mjs` (new): seven tests run
small local Node children, with no Docker or network: a successful
prefetch resolves and keeps its output; the duration ends at the
download even when the parent is blocked for 1.5s; a nonzero exit
rejects with the log tail; a command that cannot start rejects instead
of hanging the join or the stop; stopping a running child waits for its
exit; a child that ignores `SIGTERM` is killed after the grace period;
an unawaited failure does not produce an unhandled rejection in a fresh
process.
- `scripts/__tests__/grok-public-install-sandbox.test.mjs`: the source
test asserts that exactly the pinned image is pulled for `linux/amd64`,
that Grok is provisioned into the prefetch directory and never straight
into `native/grok`, that both prefetches start before staging, that the
image is joined before the first sandbox, that the rename happens after
the npm assertion and the negative probe and before the positive probe,
that the verifier imports the helper, and that `stopPrefetches` is
awaited before the single `rmSync(root, ...)`.
- `package.json`: `test:release-registry` runs the new test file.

## Verification

- `node --test scripts/__tests__/prefetch-child.test.mjs
scripts/__tests__/grok-public-install-sandbox.test.mjs
scripts/__tests__/cursor-public-install-sandbox.test.mjs
scripts/__tests__/retain-runner-qualification-packages.test.mjs`: 22
pass (7 new in `prefetch-child.test.mjs`, 12 in the sandbox file). The
new file passed on three consecutive runs.
- `node --check scripts/verify-grok-npm-install.mjs` passes.
- `node packages/paperclip-runner/scripts/provision-grok.mjs
<scratch>/prefetch/grok` on this host: `Verified Grok 1.0.13
(linux-x64)` in 1.0s, `native/grok` absent before the move, 166079904
bytes in place after it.
- Docker is not installed on the host that produced this PR, so the
verifier was not run end to end locally. This PR's own `Build` job runs
`node scripts/verify-grok-npm-install.mjs` from the branch checkout, so
it exercises the change in full.

**Measured on this PR's first CI run**
([38108964834](https://github.com/paperclipai/paperclip/actions/runs/38108964834),
created 03:37Z), against two `master` runs created in the same twelve
minutes:

| | 38108878166 (`master`, 03:35Z) | 38108995800 (`master`, 03:37Z) |
38108964834 (this PR, 03:37Z) |
| --- | --- | --- | --- |
| Verifier step | 307s | 248s | 219s |
| `Build` job | 555s | 484s | 459s |
| Slowest check in the run | `Build` 555s | server (8/12) 535s | server
(12/12) 547s |

The PR run's timing line: `stage_packages_ms 84464`, `image_wait_ms 1`,
`consumer_install_ms 97305`, `lifecycle_rebuild_ms 3545`,
`codex_probe_ms 1211`, `grok_wait_ms 0`, `grok_probes_ms 626`,
`pi_setup_ms 22194`, `pi_probe_ms 7753`. Both waits are at most 1ms, so
the staging phase hid both downloads in full. The window from the end of
staging to the Grok result went from 161s to 167s on the two runs that
motivated this PR to 102s here, so the hidden downloads were about 60s
on a fresh `runs-on` spot instance. Against the two same-window `master`
runs, the verifier step is 29s to 88s shorter and `Build` is 25s to 96s
shorter; the spread is runner variance in `pnpm build` and the consumer
install.

The `image_prefetch_ms` and `grok_prefetch_ms` values on that first run
(84550 and 186696) were the parent's late observation of each exit, not
download times; that is the first Greptile finding, fixed by the second
commit, which measures from the download's last output instead.

## Risks

- Low risk. The verifier proves the same properties in the same order.
The only ordering change is that the downloads start earlier; every
assertion, probe, and sandbox argument is unchanged.
- The Grok binary is written under the scratch root in a `prefetch`
directory during staging. That directory is never mounted into a
container, and the `native/grok` assertion still runs against an absent
path.
- A prefetch that fails early now fails the step at its join point,
where the old code would have failed at the same download a few seconds
later. Same outcome, same message source.
- `*_prefetch_ms` is a last-output time. A child that printed early and
then kept working silently would be under-reported; neither `docker
pull` nor `provision-grok.mjs` behaves that way (both print their final
status as they finish), and a silent child falls back to the observed
exit time.
- Cleanup now waits up to 5s for a child that ignores `SIGTERM` before
killing it, so a failing run can end up to 5s later than before. Both
children exit promptly on `SIGTERM` in practice.
- The timing line is one more stdout line. Nothing in the repository
parses the verifier's stdout; the two workflows that run it write the
output to the job log or to a file.

## 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 21:10:13 -07:00
8dd381f5dd perf(ci): run the db project as its own workspaces-c lane (#15831)
## 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.
> - The `General tests (workspaces-b)` lane runs every non-server Vitest
project except `ui` and `cli` in one job, one project after the other.
After #15826 leveled the server shards, this lane became the slowest
check: 593s in run 38084171761, 523s in 38083084276 and 499s in
38083020834 (all 2026-10-10).
> - The `@paperclipai/db` project alone is 168s to 250s of that lane's
436s to 513s of vitest time. Its `client.test.ts` suite is 117s to 189s
and `backup-lib.test.ts` is 70s to 107s. The other seventeen projects
together are only 263s to 276s.
> - Vitest's native `--shard` does not level this lane. The db project
is already file-parallel, and a hash-ordered slice can put both slow
suites on one runner.
> - This pull request moves the db project into its own `workspaces-c`
lane in both `pr-trusted.yml` and `release-verify.yml`. The runner
script gains a `general-workspaces-c` group, and `general-workspaces-b`
drops the db project.
> - The benefit is that both lanes should finish in about 230s to 340s
instead of 499s to 593s, and `workspaces-b` leaves the critical path.
The cost is one more job per run, about 60s of setup.

## Linked Issues or Issue Description

**Describe the enhancement**

The `General tests (workspaces-b)` PR check is the slowest check in the
run. It runs seventeen Vitest projects in sequence on one runner. The
`@paperclipai/db` project is one third to one half of its vitest time.

**Why is this needed**

The slowest check decides the wall-clock time of every PR run. In the
last three fully green runs on the current server-shard manifest,
`workspaces-b` took 499s, 523s and 593s (runs 38083020834, 38083084276
and 38084171761, 2026-10-10). The next slowest lanes, `Build` and the
server shards, sit at about 480s to 570s. A split of `workspaces-b`
removes 170s to 250s from this lane.

**Proposed solution**

Run the `@paperclipai/db` project as its own lane,
`general-workspaces-c`. Keep the other seventeen projects in
`general-workspaces-b`. Add the lane to both the PR and the release
verification matrix. Extend the guard tests so the three workspace lanes
still cover the root Vitest project list exactly once, so the new group
rejects shard flags, and so both workflows carry the lane unsharded.

**Alternatives considered**

- Vitest native `--shard` on `workspaces-b`, as `workspaces-a` does. The
db project is already file-parallel and its wall time is set by two long
suites, so a hash-ordered slice does not give a predictable split.
- Move `adapter-utils` (115s to 117s) into the new lane as well. That
makes the db lane 285s to 366s of tests and the rest 147s to 160s, which
is less level than db alone (168s to 250s) against the rest (263s to
276s).
- Three lanes. Not needed now: after this split both lanes sit under the
server shards and `Build`, so a third runner would not shorten the run.

## What Changed

- `scripts/run-vitest-stable.mjs`: new `general-workspaces-c` group that
runs `@paperclipai/db` alone. `general-workspaces-b` now excludes it.
Local `--mode all` runs all four general groups, so every project still
runs once. The dry-run output reports the new group's projects.
- `.github/workflows/pr-trusted.yml`: a `workspaces-c` entry in the
general tests matrix, with a comment that records the 2026-10-10
measurements.
- `.github/workflows/release-verify.yml`: the same `workspaces-c` entry,
to keep parity with the PR workflow.
- `scripts/__tests__/run-vitest-stable-shard.test.mjs`: the coverage
test includes the new group; shard flags are rejected for both
`workspaces-b` and `workspaces-c`; a new test asserts that
`workspaces-c` runs only the db project, that `workspaces-b` no longer
carries it, and that both workflows list both lanes without shard
arguments. The workflow check parses each `- group:` matrix entry with
comment lines removed, so a comment between keys cannot hide a shard
setting, and it first confirms the parser sees the shard keys on the
`workspaces-a` entries. A small parser test covers the
comment-between-keys case (Greptile P2).
- `scripts/__tests__/release-verify-workflow.test.mjs`: the release
workflow must carry `general-workspaces-c`.

## Verification

- `GITHUB_WORKFLOW= node --test
scripts/__tests__/run-vitest-stable-shard.test.mjs`: 32 pass (was 29).
- `node --test scripts/__tests__/release-verify-workflow.test.mjs`: the
workflow parity test passes. Two unrelated tests in that file (`a
publisher lockfile lets a fresh smoke install retain validation after
... drift`) fail on my machine on pristine `master` too, because `pnpm`
is not on its PATH. They pass in CI.
- `node scripts/run-vitest-stable.mjs --mode general --group
general-workspaces-c --dry-run` reports `["@paperclipai/db"]`; `--group
general-workspaces-b` reports the other seventeen projects.
- Both workflow files parse as YAML; the general tests matrix has 19
entries in `pr-trusted.yml` and 17 in `release-verify.yml`.
- This PR's own run exercises the script change but not the workflow
change: `pr.yml` calls `pr-trusted.yml@master`, so the matrix comes from
`master` and the `workspaces-c` job appears only on PR runs created
after the merge. Because the script comes from this branch, the
`workspaces-b` job in this PR's run (38090713637) ran the seventeen
remaining projects and finished in 340s, inside the expected 325s to
340s band. No job in that run ran `@paperclipai/db`.
- To cover the gap, I ran the new lane locally with the same command CI
uses, `pnpm test:run:general -- --group general-workspaces-c`: 49 test
files, 180 tests, all passed, 111s of vitest time (c324d0080, Node 25,
PGlite).

Expected result on the first PR run after merge, from the three measured
runs:

| Check | Before | After (expected) |
| --- | --- | --- |
| `General tests (workspaces-b)` | 499-593s | ~325-340s (263-276s of
vitest time plus ~60s of setup) |
| `General tests (workspaces-c)` | (new) | ~230-310s (168-250s of vitest
time plus ~60s of setup) |
| Slowest check in the run | `workspaces-b` | `Build` (497-570s) or a
server shard (~480-520s) |

## Risks

- Low risk. The group split only changes which job runs the db project.
The guard test asserts that the three workspace lanes cover the root
Vitest project list exactly once, so a project cannot be dropped or run
twice.
- One more job per PR and release verification run, about 60s of setup
time on the runner fleet.
- The lane names are matrix labels only. The required checks are `ci /
verify` and `ci / e2e`, which aggregate the matrix, so no branch ruleset
needs an update.

## 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
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

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

---------

Co-authored-by: Bender (Fable) <noreply@anthropic.com>
Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
2026-10-10 20:25:44 -07:00
Devin FoleyandPaperclip 07f3536e37 fix(connections): discover Sentry OAuth through its MCP resource (#15837)
Correct Sentry OAuth discovery and cover new and saved client registration.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-10 17:10:23 -07:00
Devin FoleyandPaperclip 4f680bc85c fix(release): retry verified canary tarball 404s (#15768)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Canary releases test the published npm packages through a clean
onboarding install.
> - A package can be announced in registry metadata while a fresh
install gets a tarball 404.
> - The existing ETARGET retry does not cover that distinct error.
> - This pull request retries only a verified public canary tarball
acquisition failure.
> - Permanent errors still fail, and onboarding runs once after
acquisition succeeds.

## Linked Issues or Issue Description

Refs: #15743

**What happened?**

The [Release
run](https://github.com/paperclipai/paperclip/actions/runs/37998601578)
published canary `2026.1009.0-canary.17`. Its registry verification and
publisher install passed. The separate fresh onboarding install then
failed with a final npm E404 for that exact `@paperclipai/server`
tarball. The tarball was available on a later read. This is a separate
failure from ETARGET; it does not prove the cause of the registry
visibility difference.

**Expected behavior**

Allow a bounded acquisition retry when the effective registry is public
npm and fresh primary metadata announces the exact released canary
tarball. Keep other E404, authentication, configuration, and onboarding
errors fatal.

**Steps to reproduce**

1. Run the offline helper fixture with a first-attempt E404 for an
allowlisted package at the requested canary version.
2. Return canonical effective npm registry configuration and matching
primary metadata.
3. Make the second install succeed. Confirm two installs and one
onboarding invocation.
4. Keep the tarball unavailable. Confirm three installs, a final
failure, and no onboarding invocation.

**Paperclip version or commit**

The observed run used commit `ce024457f3d747657501fbcfe8eac71f6772150f`.
This fix builds on the merged acquisition helper and current master.

**Deployment mode**

Other: the published npm canary onboarding check in release CI.

## What Changed

- Add a canary-only classifier for one unambiguous final E404 at the
canonical tarball URL of a CI-published package from the existing
release manifest.
- Query effective npm registry configuration in the failed install's
prefix, environment, and working directory. Keep bounded configuration
output in memory without logging it. Respect scoped registry precedence.
- Require fresh primary metadata to match the package, version, tarball
URL, and valid SHA512 integrity syntax. Reject redirects, conflicting
errors, truncated logs, other artifacts, and unknown or custom effective
registries.
- Share one five-second configuration and metadata deadline within the
existing two-minute acquisition budget. Keep three install attempts,
refreshed retry metadata, process cleanup, and one onboarding
invocation.
- Bound metadata header and body waits even if transport abort
propagation stalls. Cancel the reader and any late response body without
waiting for cancellation acknowledgements.
- Preserve the existing ETARGET classifier and its character-based
stderr tail. Add offline subprocess, actual npm configuration, body
cancellation, budget, identity, and failure tests. Document the scope
and limits.

## Verification

- `node --test scripts/__tests__/release-verify-workflow.test.mjs
scripts/cloud-source-verification.test.mjs
scripts/standard-image-contract.test.mjs`: 73 passed, no skips, under
Node 22 on the PR head.
- `pnpm test:release-registry`: 153 passed, no skips, on the PR head.
This includes the existing real Playwright cleanup and startup timeout
tests.
- [Hosted
CI](https://github.com/paperclipai/paperclip/actions/runs/38011028296)
passed on this head. The rollup has 53 successful checks and two
expected Storybook skips. This includes typecheck, build, the full
general and serialized test suites, E2E, and Canary Dry Run.
- The new tests use fixture-owned npm configuration and local HTTP
servers. No published canary tarballs were downloaded or installed for
this change. No release or deployment was triggered.
- `pnpm test:run` was not run locally. The full split test suites passed
in hosted CI. Independent source checks on this head passed 46 committed
tests, 22 boundary checks, and six negative controls. Greptile scored
this head 5/5 with no unresolved threads.
- The source diff and PR text were checked for secrets, private
identifiers, and unintended artifacts.

## Risks

- The new retry is conservative. A configuration capture over 4 KiB,
metadata over 64 KiB, or a check that exceeds five seconds denies retry.
A persistent tarball failure still fails after three installs.
- Matching primary metadata proves the announced artifact identity and
integrity syntax. It does not independently verify tarball bytes. The
subsequent npm install retains its integrity check.
- Registry checks use npm's resolved global and applicable scope values.
They do not add credentials or change npm configuration. Existing
ETARGET behavior, onboarding assertions, and overall startup deadlines
remain unchanged.

## Model Used

OpenAI GPT-6 (Codex). The exact model variant and context window are not
exposed in this session. Used reasoning, code execution, offline
fixtures, and GitHub API tools.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-10 16:49:15 -07:00
9ba5569030 ci: refresh the general-server shard durations (#15826)
## 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.
> - The general-server vitest lane runs with one worker per job, so it
can only go faster across runners. Twelve shards share its suites, and a
recorded duration manifest decides which suite goes to which shard.
> - That manifest was recorded on 2026-09-15 from 646 suites. The lane
now has 871 suites and about 5031s of serial vitest time. 230 suites
have no record and get the median weight, so the shards drifted apart.
In run 38076972444 (2026-10-10) they ran 313s to 554s of tests, and
`General tests (server (2/12))` at 618s was the slowest check of the
whole run.
> - An unbalanced lane wastes the runner time it already spends. The
total work does not change, but one shard holds the run.
> - This pull request records the current duration of every suite from
two recent PR runs and keeps the same twelve shards.
> - The benefit is that the twelve shards level to about 415s of tests
each. The slowest server shard should drop from about 618s to about
480s, and this lane leaves the critical path.

## Linked Issues or Issue Description

**Describe the enhancement**

The general-server PR test lane is out of balance. Its shard manifest,
`scripts/general-server-shard-durations.json`, is three and a half weeks
old and misses 230 of the 871 current suites. The heaviest shard runs
554s of tests while the lightest runs 313s.

**Why is this needed**

The slowest check decides the wall-clock time of every PR run. In the
last fully green runs the slowest check was a general-server shard
(618s, 562s, 585s, 646s in runs 38076972444, 38076844891, 38076844368
and 38073555316). A balanced lane removes about 135s from that shard at
no extra runner cost.

**Proposed solution**

Re-record the per-suite durations from two recent PR runs (38076844891
and 38076972444) with the method that the manifest's `$comment`
documents: diff consecutive per-suite completion timestamps in each
shard log, and measure the first suite from the Vitest `RUN` banner.
Average the two runs. Keep the chat and native-runner entries from the
old manifest, because those suites run in their own lanes and do not
appear in the shard logs. Drop the two suites that no longer exist.

**Alternatives considered**

More shards would also shorten the lane (fourteen shards level at about
355s, sixteen at about 311s). They are not needed now: `Build`
(489-592s) and `General tests (workspaces-b)` (513-618s) already sit
above the balanced twelve-shard level, so extra runners would not
shorten the run. Those two checks are the next targets.

## What Changed

- `scripts/general-server-shard-durations.json`: 873 recorded suites
(was 646). 870 come from the two 2026-10-10 runs, 3 carry over (chat and
native-runner suites), 2 obsolete entries are gone. The `$comment` names
the source runs, the fleet, and the carry-over rule.
- `.github/workflows/pr-trusted.yml`: a comment on the general tests
matrix records the 2026-10-10 measurements and why the shard count stays
at twelve. No workflow behavior changes.

## Verification

- `node --test scripts/__tests__/run-vitest-stable-shard.test.mjs`: 29
pass. This covers the manifest coverage check, the duration-balance
check (spread must stay under the heaviest suite), and the complete
non-overlapping partition.
- `node --test .github/scripts/tests/pr-dependency-cache.test.mjs
.github/scripts/tests/pr-runner-rust-cache.test.mjs
.github/scripts/tests/lockfile-refresh-workflows.test.mjs`: 17 pass.
- Measurement check: the per-shard sum of the recorded durations matches
Vitest's own `Duration` line within 1s on all 24 shard logs.
- Simulation with the real planner (`scripts/run-vitest-stable.mjs
--dry-run`, `GITHUB_WORKFLOW=PR`) on the current suite list: with the
old manifest the twelve shards carry 321s to 510s of recorded tests;
with this manifest they carry 413s to 415s.
- Expected result on the first PR run after merge: every `General tests
(server (n/12))` job finishes in about 480s (415s of tests plus about
65s of setup) instead of 374s to 618s.

## Risks

- Low risk. The file only sets weights for the shard planner. A wrong
weight moves a suite to a different shard; it cannot drop or duplicate a
suite, because the guard tests assert the partition is complete and
non-overlapping.
- The durations come from the `runs-on` public PR fleet. GitHub-hosted
runners are faster in absolute terms, but the relative weights, which
are all the planner uses, are the same.
- The manifest will go stale again as suites grow. The guard test fails
when coverage drops under 50%, and the matrix comment records how to
refresh it.

## 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, built the
manifest, 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 13:52:05 -07:00
Devin FoleyandPaperclip 40701adecf fix(release): preserve pinned master identity for canary publishing (#15770)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A master push publishes a canary from its exact source commit.
> - The publisher now checks out that commit by SHA. Git leaves HEAD
detached.
> - The release script requires the local branch name to be master.
> - This pull request verifies the trusted push and source before
attaching local master at the same commit.
> - The canary can pass the existing branch guard without following a
newer master tip.

## Linked Issues or Issue Description

Related: #15712 pinned the publisher checkout and bound the smoke
lockfile to its source. I searched open PRs and issues for canary,
detached HEAD, and checkout fixes. No overlapping fix was found. #15768
addresses a later tarball visibility error and does not change this
branch guard.

**What happened?**

The canary publisher checks out the exact push SHA. The checkout is
detached. `release.sh canary --skip-verify` then fails because the local
branch is not master. Publication does not start. The [failed master
run](https://github.com/paperclipai/paperclip/actions/runs/38008956836)
shows this failure.

**Expected behavior**

The trusted master publisher must keep the exact verified push commit
and pass the existing local master guard. It must not follow a newer
branch tip.

**Steps to reproduce**

1. Check out a master push commit by its full SHA.
2. Confirm that HEAD is detached.
3. Run the real `require_on_master_branch` helper from
`scripts/release-lib.sh`.
4. Observe the detached-HEAD rejection. The tests reproduce this without
calling a publisher.

**Paperclip version or commit**

`baa94bbea58919bcc6cf247a708f04e6e1002679`.

**Deployment mode**

The GitHub Actions master canary publishing lane.

## What Changed

- Add one step immediately after the pinned publisher checkout.
- Require the canonical repository, a push event, and the master ref
before changing any Git ref.
- Validate the full source SHA and require the checked-out HEAD to match
it.
- Attach local master to that exact SHA and recheck HEAD. Do not fetch
or select a floating branch.
- Preserve release scripts, local branch guards, package authentication,
other publishing lanes, and source-named lockfile artifacts.
- Add disposable real-Git tests for a pinned source behind local and
remote master, rejected trust and SHA inputs, unchanged refs on
rejection, and the original branch guard. Document the branch handoff.

## Verification

- `node --test scripts/__tests__/release-verify-workflow.test.mjs
scripts/cloud-source-verification.test.mjs
scripts/standard-image-contract.test.mjs scripts/release-lib.test.mjs`
passed all 82 tests on Node 25.6.1. Twelve new cases include eleven
real-Git fixtures and one workflow invariant check. The tests run the
actual workflow shell and the unchanged release guard.
- `actionlint -shellcheck= .github/workflows/release.yml` passed.
ShellCheck passed for the new shell. Full local actionlint reports four
pre-existing advisories in other steps; their text matches the base
exactly. This change adds no advisory.
- Workspace `pnpm -r typecheck` and `pnpm build` passed on Node 25.6.1.
The local tests cover the changed release flow; the full application
test suite runs in hosted CI.
- [Hosted full
CI](https://github.com/paperclipai/paperclip/actions/runs/38011530698)
passed all 47 jobs at commit `ffff774a2629663fa71e2fad5143da0696afc5e6`.
This includes policy, typecheck, release registry, build, runner tests,
application tests, browser tests, and the canary dry run. Hosted policy
ran all 12 new guard tests and passed all 67 source-policy tests.
- Greptile scored the same commit 5/5 with no actionable issues. There
are zero review threads. Separate source review approved all three
changed files and 32 dependencies. It passed 82 tests and caught four
broken variants.
- The supply chain scanner returned neutral with zero annotations. It
cannot verify this inline workflow change and reports limited coverage
for existing action references. Manual review confirmed that all action
references and pins are unchanged. The security, Socket, and Snyk checks
passed.
- No live canary install, registry publication, Docker image operation,
workflow rerun, or deployment was performed. Tests use disposable Git
repositories and local fixtures.

## Risks

Low risk. The new step changes only local Git state in the trusted
canary publisher. A wrong event or source fails before ref changes. The
original release and authentication guards remain in place. The exact
checkout and artifact source stay unchanged. Revert the new step to
restore the previous behavior. No runtime or database change is
required.

## Model Used

OpenAI GPT-6 through Codex. Used repository inspection, code execution,
Git and Bash fixtures, and independent agent review. The runtime does
not expose a more specific model snapshot or context-window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [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-10 13:33:34 -07:00
Devin FoleyandPaperclip e9dae72c0c ci: use a Docker Hub mirror for image builds (#15756)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Image workflows build the exact source selected for a release.
> - BuildKit fetches the Dockerfile frontend and base images from Docker
Hub.
> - Docker Hub token failures can stop these builds before application
code runs.
> - BuildKit supports a public image cache with canonical Docker Hub
fallback.
> - This pull request configures that cache in the two affected image
builders.
> - The build inputs and publication gates stay the same.

## Linked Issues or Issue Description

**What happened?**

[Docker run
37995539183](https://github.com/paperclipai/paperclip/actions/runs/37995539183)
failed before application builds started. The AMD64 job received HTTP
504 from the Docker Hub token endpoint while it fetched
`docker/dockerfile:1.20`. The ARM64 job fetched the frontend, then
received the same error while it resolved `node:24-trixie-slim`.
[Release preview run
37992058052](https://github.com/paperclipai/paperclip/actions/runs/37992058052)
also failed while it fetched the frontend.

**Expected behavior**

Use the supported public cache when it has the requested Docker Hub
image. Retain canonical Docker Hub fallback when the cache has no usable
copy. Preserve build and publish failures.

**Steps to reproduce**

Run either affected image builder during a Docker Hub token failure. The
unconfigured BuildKit container contacts Docker Hub for the frontend and
base image. It cannot use a host Docker daemon mirror for those build
requests.

**Paperclip version or commit**

The observed Docker run used `00d944f96c8c4aba8332ce224df2be73a601228a`.
The preview used `b6b3cb3761dabd1bd668054e7a99a245d53707c4`.

**Deployment mode**

GitHub Actions image builds. No application runtime change.

The public PR and issue search found no matching open registry-mirror
change. #15743 addresses a separate npm canary dependency publication
failure.

## What Changed

- Add the official `docker.io` to `mirror.gcr.io` BuildKit configuration
to the public Docker build matrix and the release preview image builder.
- Keep all existing action references, image sources, targets, build
arguments, platforms, credentials, cache settings, outputs, permissions,
triggers, and publication gates.
- Extend the existing dependency-free workflow tests to check mirror
placement, verified HTTPS, both native platforms, preview isolation, and
publication access.
- Document cache eviction, tag freshness, canonical fallback, and
builder bootstrap limits.

## Verification

- `node --test scripts/__tests__/release-verify-workflow.test.mjs
scripts/cloud-source-verification.test.mjs
scripts/standard-image-contract.test.mjs`: 43 passed on the final
source. No dependency install required.
- Six offline controls removed each builder's mirror, changed the
registry or cache host, enabled insecure transport, or added preview
registry login. Each control failed the new check as expected.
- `actionlint -shellcheck='' .github/workflows/docker.yml
.github/workflows/release.yml`: workflow validation passed. With
optional ShellCheck enabled, the ten diagnostics match the unchanged
base exactly. This PR changes no shell scripts.
- A real TOML parser accepted the inline configuration. Removing the new
block from each workflow produced byte-identical base files.
- Read-only registry metadata checks confirmed matching raw bytes and
SHA256 values for the current Node24 index and both native manifests.
The Dockerfile frontend index and both native manifests also match
Docker Hub and Google. No image config blobs or layers were fetched.
- Full application tests, typecheck, and build were not repeated locally
because application source is unchanged. [Hosted
CI](https://github.com/paperclipai/paperclip/actions/runs/37998739440)
passed the full required checks on `25fd57f`, including all test shards,
build, typecheck, policy, E2E, and canary dry run.
- The supply-chain advisory returned neutral because it cannot verify
these infrastructure edits. It reported zero annotations. Independent
review confirmed all action references, Dockerfiles, package files, and
lockfiles are unchanged. The other 53 checks passed; two Storybook
checks skipped as expected.
- Independent review approved all four changed files and the workflow
graph. Greptile scored the same source 5/5 with no unresolved comments.
- No Docker build, workflow rerun, image publish, or deployment was
started for verification. A normal image workflow after merge must prove
full image acquisition.

## Risks

Google is a cache, not an outage guarantee. It can evict images. Mutable
tags can lag upstream changes or deletions. Matching manifests now does
not prove future tag freshness or blob availability. A cache miss can
still fail when Docker Hub is unavailable. The initial
`moby/buildkit:buildx-stable-1` bootstrap uses the host Docker daemon
before this configuration takes effect and remains dependent on Docker
Hub.

The registry mirror applies to image pull and resolution only. GHCR
publication and cache writes keep their existing routes. See [Docker's
supported
configuration](https://docs.docker.com/build/ci/github-actions/configure-builder/#registry-mirror)
and [Google's cache
limits](https://docs.cloud.google.com/artifact-registry/docs/pull-cached-dockerhub-images).

## Model Used

OpenAI GPT-6 through Codex, with reasoning and code execution tools. The
exact service variant and context window are not exposed in this
session.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-09 17:15:50 -07:00
Devin FoleyandPaperclip 935fb63ed5 fix(release): retry incomplete canary dependency publications (#15743)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Release checks must test the exact package that a new user installs.
> - The canary onboarding smoke uses a fresh npm dependency graph.
> - An upstream package can reference a dependency before that version
is available.
> - The smoke then exits before Paperclip onboarding starts.
> - This pull request retries only that npm acquisition failure within
the existing startup limit.
> - Onboarding still runs once, and all other errors remain visible.

## Linked Issues or Issue Description

**What happened?**

[Release run
37986221440](https://github.com/paperclipai/paperclip/actions/runs/37986221440)
published `2026.1009.0-canary.13`, then failed the onboarding smoke. The
retained server log contains npm `ETARGET` for
`@aws-sdk/credential-provider-env@^3.972.73`. The upstream parent
package was published before this dependency. The missing version became
available during the smoke startup. Paperclip onboarding never ran.

**Expected behavior**

Retry a short dependency publication window before onboarding starts.
Stop on a permanent install failure. Never repeat onboarding to hide a
startup failure.

**Steps to reproduce**

1. Start the exact-version canary smoke with an empty npm cache.
2. Make the first npm install return `ETARGET` because a dependency
version is not available.
3. Make the next install succeed after registry metadata changes.
4. Confirm that onboarding starts once. Repeat with permanent `ETARGET`
and an unrelated failure; onboarding must not start.

**Paperclip version or commit**

The observed release source was
`cb291bf1298ab484a2792c05eb977f3440f1f351`.

**Deployment mode**

Linux release CI. The change affects the canary smoke harness.

Related: #15712 fixes a separate lockfile mismatch in the smoke's
repository dependency install. This change covers the fresh
published-package npm install.

## What Changed

- Separate npm package acquisition from the onboarding command.
- Retry only the final npm `ETARGET` error, up to three attempts with
online metadata and clean temporary prefixes.
- Share a two-minute acquisition budget across attempts and backoff.
Keep the existing five-minute Playwright startup limit.
- Run the installed exact-version CLI once. Preserve authentication,
network, lifecycle, and onboarding failures.
- Stop owned process groups on cancellation and remove owned install
prefixes. Keep server logs and instance data for diagnostics.
- Keep dependency-free helper subprocess tests in the required policy
lane. Run real Playwright lifecycle tests in the required
release-registry lane after its dependencies are installed.
- Document the bounded behavior in the release runbook.

## Verification

- `node --test scripts/__tests__/release-verify-workflow.test.mjs`: 24
passed in a clean source snapshot with no `node_modules`. These cover
transient and permanent missing versions, other npm errors, nested error
codes, onboarding failure, deadline expiry, and cancellation.
- `pnpm test:release-registry`: 153 passed, including real Playwright
teardown with a descendant that ignores SIGTERM and startup-timeout
cleanup of a detached npm installer.
- `pnpm -r typecheck`: passed.
- `pnpm build`: passed.
- `pnpm test:run`: deliberately stopped after complete hosted CI passed;
this is not a local full-suite pass. The run's 15 warm-runtime real-DB
cases failed before assertions because the local macOS PostgreSQL
package lacked library symlinks after installation with
`--ignore-scripts`. After running the package's local hydration step,
all 15 affected cases passed in a targeted rerun. The remaining
aggregate was stopped with exact owned-process cleanup.
- [Hosted
CI](https://github.com/paperclipai/paperclip/actions/runs/37994486323)
is complete and green at `1c80e6f`: all tests, build, typecheck, policy,
release registry, E2E, and canary dry run passed. Greptile scored the
same head 5/5 with no unresolved comments.
- Independent review covered all seven changed files and the exact
policy and release-registry commands. It also used real npm 11.9 against
a loopback registry: a required version was absent, then became
available. The retry refreshed metadata and started the installed
exact-version CLI once. The one-attempt negative control failed.
- The new subprocess tests use local npm and CLI fixtures. They do not
contact a registry or start a real Paperclip instance.
- The patch preserves the exact-version clean-install check. It adds no
dependency pins, credentials, or workflow reruns.

## Risks

The install phase now has a two-minute budget inside the existing
five-minute startup limit. An unusually slow valid install can still
fail the smoke. Permanent missing versions remain failures after the
bounded retry. The lifecycle tests cover the Linux release runner
behavior; Playwright does not support graceful SIGTERM on Windows. No
application, database, or deployment behavior changes.

## Model Used

OpenAI GPT-6 through Codex, with reasoning and code execution tools. The
exact service variant and context window are not exposed in this
session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` /
`Closes: #` 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-09 17:15:02 -07:00
Devin FoleyandPaperclip 23b9aaa8e6 fix(release): reuse publisher lockfile for canary smoke (#15712)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Every master push publishes an exact canary and tests its onboarding
flow.
> - The publisher resolves dependencies before building, while the smoke
job checks out the committed lockfile again.
> - Patch or manifest changes can land before the separate lockfile
refresh, so the smoke fails before testing the published app.
> - This pull request passes the publisher's resolved lockfile to the
same-source smoke job.
> - The smoke keeps frozen dependency validation and tests the exact
published canary version.

## Linked Issues or Issue Description

Refs #10393, #13689. Related work: #13690 documents lockfile drift;
#12115 addresses refresh-PR check triggering. This change addresses the
canary smoke handoff. #15678 only refreshed the opencode importer.
#15164 incidentally corrected the earlier patch hash on master.

**What happened?**

Canary publication succeeded, but the fresh onboarding smoke install
failed with `ERR_PNPM_LOCKFILE_CONFIG_MISMATCH` on sources `0b80ea17ef`
and `ab8dcab809`. The committed `acpx@0.13.1` hash no longer matched its
patch. A later run on `6080daf262` failed with
`ERR_PNPM_OUTDATED_LOCKFILE` after an `opencode-ai` manifest addition.
The current master lockfile has since caught up; the workflow still
permits this race on future changes.

**Expected behavior**

The exact published canary reaches the onboarding smoke with test
dependencies resolved for its source. A mismatched or missing lockfile
must fail validation.

**Steps to reproduce**

Check out either affected source and run `pnpm install
--frozen-lockfile` with pnpm 9.15.4. The smoke fails before a browser
starts, although the publisher already installed that source with a
resolved lockfile.

## What Changed

- Pin both canary publisher and onboarding smoke checkouts to the
triggering SHA.
- Save the publisher's resolved lockfile before restoring tracked files
or rewriting release versions.
- Restore that same-run, source-named artifact before the unchanged
frozen smoke install.
- Add offline real-pnpm regressions for patch-hash and manifest drift,
plus wrong-source rejection and workflow binding checks.
- Document artifact retention and job reruns. No manifest or committed
lockfile changes.

## Verification

- Focused release workflow, exact-source verification and image-contract
tests: 45 passed.
- Negative control: both new real-pnpm fixture cases fail against the
original workflow with their respective original lockfile errors; both
pass with the handoff.
- `pnpm install --frozen-lockfile` passed on the current source.
- `pnpm -r typecheck` and `pnpm build` passed after reclaiming
disposable build-cache space.
- The browser onboarding smoke passed against exact published
`2026.1009.0-canary.7`: 1 test passed using a fresh temporary instance.
- Full local `pnpm test:run` was attempted twice. The first hit disk
exhaustion; the second reported one chat integration failure and then a
Vitest worker `ENOTCONN` crash before completing. This is not a
full-suite pass. The named chat case passed in isolation without
changes. All full sharded PR CI checks passed on the final head.
- Independent source review and a clean-checkout run also passed all 45
focused tests, with separate real-pnpm negative controls.
- The supply-chain scan is neutral because managed intelligence could
not scan the pinned official `download-artifact` action. It reported no
defect; independent review inspected the action source, same-run scope
and v4/v4 compatibility.
- Reviewed the diff for secrets, personal data and generated artifacts;
none found.

## Risks

- The smoke now depends on a same-run GitHub artifact retained for 14
days. A missing artifact intentionally fails instead of resolving a
different dependency set.
- A publisher rerun replaces its source-named artifact; a smoke-only
rerun reuses the retained artifact.
- Frozen validation and the exact published version remain required. The
source app and publication logic do not change.

## Model Used

OpenAI GPT-6 through Codex, with tool execution and parallel independent
review. The exact serving variant and context-window size are not
exposed in this session.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-09 17:14:32 -07:00
DottaandPaperclip 0726a00e80 feat(apps): qualify Bitly, Context7, Hugging Face and Resend MCP (#15686)
## Thinking Path

> - Paperclip lets people manage AI agents and govern their tools.
> - Apps already supports provider-hosted MCP servers.
> - Useful providers still need real setup and read proof before Browse
exposure.
> - Browser testing found a Bitly token-format failure and an OAuth
callback that replaced an explicit empty agent selection.
> - This PR qualifies four provider methods and fixes those shared OAuth
paths.
> - Providers that still need login or approval remain withheld from new
Browse listings.

## Linked Issues or Issue Description

Refs: #15621, the merged hosted MCP qualification batch. Searches found
no duplicate Bitly or Context7 implementation PR.

**What existing behavior does this improve?**

Apps setup, provider MCP discovery, token exchange, and saved agent
access through an OAuth callback.

**Subsystem affected**

Shared Apps definitions, the server tool-access service, and the UI
setup/recovery route.

**Current behavior**

Bitly's callback can fail with `oauth_access_token_missing` when its
token response is not JSON. Curated MCP callbacks can treat an
explicitly saved empty install set as access for all agents. Context7 is
hidden and Hugging Face lacks a curated anonymous method. Saved drafts
for hidden providers cannot finish setup through their catalog wizard.

**Proposed behavior**

Request JSON token responses. Record explicit remote MCP install choices
in the install transaction and preserve them at callback. Keep managed
subject/default access when no choice was saved. Expose the qualified
Bitly and Context7 methods, add anonymous Hugging Face Hub access, and
record Resend qualification. Resume hidden providers only through an
exact matching company-visible saved draft.

**Reason and benefit**

People can connect useful hosted tools through the existing Apps flow.
An explicit zero-agent selection survives authorization. Public
documentation lookups need no OAuth app, account, or key.

**Breaking changes**

Explicitly saved empty MCP install choices now remain empty. Existing
managed defaults without a saved choice retain their behavior. No schema
migration or API shape change.

## What Changed

- Qualify Bitly OAuth, Context7 public MCP, Hugging Face public MCP, and
Resend OAuth with real browser setup, useful UI reads, and reload
checks.
- Negotiate JSON OAuth token responses and preserve explicitly saved MCP
agent reach with a transactional config marker.
- Add durable provider overrides, permission reviews, dated text-only
evidence, and official branding provenance. Remove all seven evidence
images and their report references.
- Restore Finish setup for hidden-provider drafts with
company/application/provider checks; keep fresh setup withheld. Preserve
success after the exact resumed draft becomes active, and restore valid
completed URLs to that exact saved Permissions page; reject initially
active, disabled, or archived resume targets. Align the Context7 route
assertion with its new qualified availability.
- Retain hidden ClickUp, Klaviyo, and monday.com research with exact
login/approval gates. monday.com requires provider callback
allowlisting.

## Verification

- Connector service suite: 420 passed, including
default/empty/agent/company reach and managed OAuth lifecycle coverage.
- Shared catalog: 36 passed. Override tests: 3 passed. Brand checks: 101
identities passed.
- Focused UI setup/recovery, Browse, and OAuth-helper suites: 306
passed. Actual hidden-draft resume/reload passed; a public Context7
draft seeded in the isolated app completed through the active-state
refetch and persisted with zero agents after reload. The completed URL
refresh and View connection open the exact saved Permissions page. Fresh
hidden setup, normal initially active resume, disabled/archived status,
and provider mismatch are rejected; a draft at the completion stage
stays in setup.
- Recursive typecheck and build passed again after rebase. The full
local Vitest run began before rebase and was cancelled as superseded
while still in the general-server phase; it is not a completed pass.
Exact-head CI supplies broad coverage. The focused connector service
suite passed against the final code. UI typecheck, build, and token
gates passed again after the draft-recovery fix.
- Targeted mock MCP Playwright journey: 1 passed (34.9s). It now
verifies View connection reaches the exact saved Permissions route, both
discovered actions, and reload persistence. This replaces the stale
Browse expectation found by CI.
- All CI gates passed on final commit
`32bb4c6422411eee632486950e45e2735102d1b3`. The inherited-pause test
timed out in the cleanup run and passed on one retry without code
changes; the entire file also passed locally, 36/36. Its exact timing
cause remains unresolved. A signoff timeout on the preceding
production-code commit also passed on its single retry. Greptile
reviewed the final commit at 5/5 with no open findings.
- Actual UI reads: Bitly current-user read; Resend List Domains
including a fresh setup after the final callback refinement; Context7
React docs; Hugging Face public docs search. No provider writes or real
agent runtimes ran.
- [Text-only qualification
report](https://github.com/paperclipai/paperclip/blob/32bb4c6422411eee632486950e45e2735102d1b3/doc/connections/easy-mcp-batch-2-2026-10-09.md)
include evidence limits and exact blockers. Account/profile/domain
results are omitted.

## Risks

- Bitly `mcp.all` and Resend `full_access` remain broad provider grants.
Qualification kept writes Off and selected no agents. Read proof does
not establish write behavior or refresh rotation.
- The explicit-choice marker applies to new install saves; older
unmarked flows keep their existing defaults. Its JSONB merge avoids
overwriting concurrent config fields.
- Public provider catalogs and rate limits can change. Unqualified
providers remain withheld from new Browse entries; retained connections
remain available for recovery.

## Model Used

OpenAI Codex, based on GPT-6, plus OpenAI `gpt-6-luna` subagents for
provider research, browser testing, and review. The exact primary
serving variant and context-window size are not exposed by this runtime.
Used reasoning, code execution, and browser tools.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-09 11:52:33 -05:00
Nicky LeachandPaperclip 2dd9811e8b Own agent hiring and termination in a lifecycle module (#15631)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Agent creation, approval, pause, and termination change whether an
agent can work.
> - These changes currently use separate service and database paths.
> - Some agents need setup after their record exists and before their
harness can run.
> - This PR adds one module that owns those state changes and their
transactions.
> - The module records progress and retries incomplete work after a
server restart.

## Linked Issues or Issue Description

**Problem or motivation**

A hire can become active before its execution environment is ready.
Direct agent state writes also bypass one common lifecycle boundary.
Termination does not have a durable step for resource cleanup.

**Proposed solution**

Use agent lifecycle commands for hiring, approval, pause, resume, and
termination. Keep proposed hires pending until approval. Prepare each
approved hire, test its saved harness configuration, and then permit
work. Wait for required plugin work before completing a step.

**Alternatives considered**

A general database commit hook would expose transaction details to
callers. A timer alone would delay each new hire. This module owns each
command transaction and starts its worker after commit. A periodic scan
recovers work after a process stops.

**Roadmap alignment**

This change adds the lifecycle boundary for the existing agent and
plugin systems. It does not add a user interface or a provider
implementation. It replaces the lifecycle delivery approach in #15391.
Related proposals include #11475 and #13099; this change does not add
their external executor protocols.

## What Changed

- Add durable approval, preparation, verification, pause, resume, stop,
and cleanup states.
- Keep agent creation, lifecycle state, configuration writes, and agent
record deletion behind `modules/agent-lifecycle`. Keep validation,
credential rules, reads, onboarding, invitations, general approvals, and
company data deletion in their services.
- Commit a hire before the remaining onboarding or invitation work.
Serialize requests with the existing dedicated database connection
helper. Reuse the hire after a failed attempt.
- Keep accepted work in the run queue during setup. Require execution
and cleanup evidence before completing pause or termination.
- Retain the original credential owner when approving migrated hires.
- Start lifecycle work after each command commits. Use database leases
and revision checks for retries and late results.
- Add a generic required plugin method and capability. Store the
required plugin IDs for each agent. Track host and plugin completion
separately.
- Keep harness verification and credential rules in services. Supply
their results to lifecycle through explicit ports. Reject failed logins
and incomplete probes.
- Remove reverse imports from lifecycle adapters into services. Share
record queries and validation below both layers.
- Use one hire decision path for agent IDs and approval IDs. Resolve the
approval and agent change in one transaction. Expose policy
reconciliation on the command object.
- Save configuration and verification invalidation through one module
operation. Ordinary commands own their transaction. Restrict the shared
transaction integration to its listed callers. Reject identity, company,
accounting, timestamp, and unknown fields.
- Require completed termination before deleting agent records. The
company deletion service calls the module through a separate deletion
interface.
- Save an approved secret before applying its agent binding. A failed
binding leaves the secret approved. A retry reuses it.
- Remove old permission and metadata export files. Move hire
configuration rules out of lifecycle persistence.
- Report failed worker steps and scans with operation context and known
error codes. Omit provider messages, stacks, and configuration values.
Document ordinary application use and extension rules.
- Recover expired native owners only after execution stops. Retry
cleanup of failed retained leases. Wait for setup in the live smoke
script.
- Keep manual, budget, and company pause reasons separate. Guard
execution status writes with the ready state.
- Add a migration, boundary checks, regression tests, and a lifecycle
guide. Keep the current UI.
- Limit periodic policy checks to 100 eligible agents per minute.
Continue from the last agent ID. Exclude pending hires and agents in
termination or a final state. Keep explicit company and budget updates
immediate.

## Verification

- Latest head `ccc3c86b0` aligns the shared status update schema with
the PATCH route. All 15 shared tests, 104 server route/OpenAPI tests, 2
CLI tests, and shared/server/CLI TypeScript checks pass. The Greptile
comment is fixed. CI is running; no new Apex review was requested for
this small fix.

- Rebase `75708ac94` rebases onto master at `2eb5187e4`. The backup test
uses the shared 90-second timeout from master; the older timeout commit
was removed. The affected backup test passes.
- Agent budget edits now pass the target agent ID through enforcement.
All 50 affected tests, server TypeScript, and 11 boundary tests pass.
Both configuration update paths have regression coverage. CI is running
on the latest head. No new Apex review was requested for these small
fixes.

- Rebase `9b568666a` preserves the run-control extraction and
delivery-work startup changes from master. All 559 affected tests,
server TypeScript, and 11 boundary tests pass. CI and Apex review are
running on the final head.
- The policy scan fix passes 56 lifecycle and diagnostic tests, 24
policy and caller tests, and 11 boundary tests. A regression test checks
the page limit, interval, excluded states, and cursor wraparound.

- Rebase `fd4698750` passes all 611 tests in eight affected lifecycle,
route, and heartbeat suites. Server TypeScript and all 11 module
boundary tests pass. The company deletion rollback and concurrent hire
tests are included.

- Follow-up `00aac7646` fixes the failed decision deletion test and the
public update schema. All 126 affected server tests, 8 shared tests, 2
CLI tests, and 11 boundary tests pass. Shared, server, CLI, and UI
TypeScript checks pass.

- Server TypeScript passes: `pnpm --filter @paperclipai/server exec tsc
--noEmit`.
- All 11 boundary tests pass: `node --test
scripts/check-module-boundaries.test.mjs`. The repository boundary scan
also passes.
- All 68 lifecycle, failure diagnostic, and harness verification tests
pass. Tests reject forbidden fields through the root command, combined
transition command, and restricted transaction integration. They also
check invalid fields returned by configuration preparation.
- Failure tests cover host work, plugin work, claims, renewals,
deferrals, and scans. Log tests check known error codes and exclusion of
private error contents.
- All 355 tests in 10 existing caller suites pass. These tests cover
secret approval, pairing, pending hires, credentials, company import,
built-in agents, managed agents, instructions, and skills.
- The branch is rebased onto `origin/master` at `2eb5187e4`. The queue
guards and cancellation lock fix use the extracted heartbeat queue.
Agent cancellation uses the extracted run-control file. The source diff
scan found no secrets or private URLs.
- Earlier checks cover approval retries, rollback, late verification
results, policy holds, deletion rollback, concurrent company deletion,
and queue admission during setup.
- Before the rebase, CI passed on `00aac7646` and Greptile Apex rated it
5/5 with no open comments. CI and a new Apex review are pending for the
rebased commit. The branch has no merge conflicts.
- The full local build and native Dot suite were not run. The host has
no Cargo. No live provider test was run.

## Risks

- Background policy recovery takes more than one minute when more than
100 agents are eligible. Explicit company and budget updates still
reconcile immediately.

- The restricted configuration integration keeps an agent reference and
its dependent records in one transaction. It cannot change a budget or
invoke a lifecycle transition. A periodic scan starts pending setup
after that transaction commits.
- Secret approval and binding use separate transactions. A failed
binding can leave an approved secret with no agent binding.
- New approved hires remain unavailable until setup and verification
complete. Existing clients see `paused` during these steps. This PR does
not change the UI.
- Migration `0320_daily_onslaught.sql` maps existing statuses and pause
reasons. It does not create external resources or claim that an existing
harness passed verification.
- Use a coordinated server update for this migration. Older server
versions can write legacy status without the new lifecycle checks.
- A failed onboarding or invitation request can leave a committed hire.
A retry reuses that hire and completes the remaining records.
- Deletion requires completed termination. Company deletion can require
another request while cleanup is pending.
- Required plugins must make repeated calls safe and reject older
revisions. A disabled or removed required plugin blocks completion until
it is restored.
- A new hire can run a second harness test after the existing UI test.
Some adapters can charge for this test.
- No live provider test was run. Provider implementation and resource
backfill remain separate work.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, code tools, and local test
execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have described the issue in this PR with the feature request
field labels
- [x] I have not referenced internal or instance-local issues or links
- [x] My branch name describes the change
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation
- [x] I have considered and documented risks
- [x] Targeted local tests pass
- [x] All Paperclip CI gates are green on the new commit
- [x] Greptile Apex is 5/5 with no open comments on the new commit
- [x] I will address review comments before requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-09 09:05:12 -07:00
DottaandPaperclip 57e977be72 feat: integrate Pi 1.0 into the experimental Runner (#14921)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - The experimental Runner owns provider processes and durable
sessions.
> - Pi needs working task execution and human controls.
> - The five-PR stack must preserve changes already on master.
> - Each layer now carries the complete integrated source for a safe
sequential fallback.
> - This PR belongs to native GitHub stack #15602, ending at #14956.

## Linked Issues or Issue Description

Refs #14436, #14631, #14743 and #14956.

Ship Pi 1.0 through the experimental Paperclip Runner. The five PRs are
#14921, #14922, #14923, #14924 and #14956. The user authorized the
complete merge after checks pass. Existing `pi_local` execution is
unchanged. Accounting and wider provider/platform qualification remain
deferred.

## What Changed

- Recover missing final replies after workspace finalization changes
owners, using accepted-turn evidence without rerunning work or granting
external-chat publication.
- Preserve the admitted Pi instruction root across warm runs, while
retaining changed-root rejection.
- Give Pi a bounded 15-second default shutdown grace so stop, drain
acknowledgement and durable suspension can complete. Explicit deadlines
and other providers retain their existing behavior.
- Integrate the Pi 1.0 runtime and master contracts.
- Use Pi profile 22. Preserve explicit caller-selected models and exact
native thinking levels. Keep Pi's wrapper, helper, extension and
question/control behavior unchanged from the qualified profile-19
runtime.
- Preserve master's Dot lifecycle and consent fields, configured task
environment, status guards and current Codex/Claude dependency versions.
Cursor stays qualified. Copilot stays pending; profile 17 binds the
changed shared protocol validation sources.
- Exclude general AWS IAM credentials from Pi static/custom provider
bindings and selected task projections; preserve the provider-scoped
Bedrock bearer key. Profile 21 is retained as historical provenance.
Rust and cloud install probes use the current declaration.
- Patch bundled brace-expansion 5.0.9 to the exact official 5.0.12
payload. Pin the patch and complete runtime closures. Include the patch
in normal installed setup tooling. Keep the upstream Pi shrinkwrap as
provenance and permit only this exact security correction.
- Include current attestation files in the Docker build context. Keep
the repository lockfile unchanged from master. CI and private image
builds resolve manifest changes before their frozen installation.

## Verification

- Full local `pnpm -r typecheck` passes, including Runner Rust, server
and UI. Focused integration checks pass: 194 Runner
admission/environment tests, 63 profile/credential tests with one
expected skip, 152 Dot/UI configuration tests, and Pi transcript/notice
tests.
- Full local `pnpm build` passes on the final source.
- Fresh final-source checks pass: all 698 Rust workspace tests (32
binaries), 156 credential/profile/controller tests with one expected
skip, Runner TypeScript typecheck, and 20 package/setup/sandbox tests.
- The profile-21 Pi materializer passes on the native host with the
official pinned Node 24.21.0 and its npm. It verifies all 150 locked
packages, the patched dependency and the exact closure. Setup/package
bundle tests and UI token gates pass.
- The old hashes were reproduced for all three supported targets before
calculating the patched graph. New closure hashes are darwin-arm64
`282022db10150c6632b3444df421342e7d534bdf5d5fb1097a2e79d0625a2bcf`,
darwin-x64
`64e251e19009f755c0b04f73ce2138246faab71a961b0f13d75ebfcc34bef12e`, and
linux-x64
`713b1fdff42fb56a1518bdc084f181d70bee8ebadc3e4b1d76321ed9108c8410`.
Independent native platform execution is separate from graph identity
reproduction.
- Historical cloud qualification remains unchanged: all seven core cases
pass on shipping source `10dc43c9ec65d88c2f782d62afb296d09494f215`,
harness `1a4408a48cfb5a1f094a311141c257c92cd7a893`, image
`sha256:5b3a775b383591bda1b0c1889e509acc70ce7f37c53f09733c81d59037f02280`,
and accepted Sonnet 4.6/low fixture. All 215 canonical files and all
seven cleanup checks pass independent verification. These are profile-19
results and are not relabeled as fresh profile-22 runs.
- Current Pi digest:
`sha256:e92078bee3c23bec4100aa589013a44613d054cd686826534025d8019e9f39a9`.
[The readiness
plan](https://github.com/paperclipai/paperclip/blob/codex/pi-production-readiness/doc/plans/2026-10-02-pi-production-readiness.md)
preserves campaign and failed-attempt provenance.
- Merge only after every PR's current-head CI and fresh review pass.
Linux CI covers the full suites, build and browser tests. The local
embedded Postgres API-authority suite cannot start on this macOS/Node 26
host, so Linux CI must confirm that suite.

### Fresh profile-22 core qualification — 2026-10-08

All seven accepted core cases pass canonically on Pi profile 22, with
`openrouter/anthropic/claude-sonnet-4.6` and native-confirmed low
thinking. This model is a fixture; production accepts the caller's
explicit Pi provider/model.

Runtime/install source: `3241a992f2a7703e59e97ed0fd3e5d6405de4401`.
Frozen accepted harness: `1a4408a48cfb5a1f094a311141c257c92cd7a893`.
Immutable cloud image:
`ghcr.io/paperclipai/paperclip-daytona-runner@sha256:506f22db7edd78f37c0c40bec1cc084af1850455026dbf467194bfbb8fcef141`.
Pi digest:
`sha256:e92078bee3c23bec4100aa589013a44613d054cd686826534025d8019e9f39a9`.

[Hosted Linux image and clean-install
verification](https://github.com/paperclipai/paperclip/actions/runs/37868328023)
passes, including all 20 source-bound archives, normal CLI/Pi setup,
companion import and the production pack reader. This exact installation
source includes the latest master integration and the corrected Pi warm
instruction-root fence. Full local typecheck/build and current-head
hosted CI verify the final stack. All 13 focused real-root regressions
pass. The full local executor suite passed 662 tests; 15 database tests
could not start the Mac embedded PostgreSQL service. Hosted Linux CI
passes the full required verification and E2E checks. These fresh
results keep their own source identity; profile-19 results remain
historical.

| Core path | Canonical campaign | Retained archive SHA-256 |
| --- | --- | --- |
| File edit, validation, download and Done |
`pi-core22-replyfix-0-1791511228` | 23 files;
`a473e8603a3dd4737863291f8d3d1e392391f0b16d433c3e0e0e9d8baf7a97b0` |
| Pending question and controller restart |
`pi-core22-replyfix-1-1791511376` | 33 files;
`6b829c4eb74e1f32a89c692a4ae7130dbfc1c6d3cf13915effe2103d9e242c8e` |
| Three-turn session/process/workspace continuity |
`pi-core22-replyfix-2-1791511587` | 23 files;
`7a87021f8f9a3fdd3c58bb4467f8d82c635e3ea4795d6e75f144d9aa14818df8` |
| Four typed questions and browser reconnects |
`pi-core22-replyfix-3-1791511881` | 42 files;
`9e31755252be1f4f9cb0626c984c142d4d1ae5f5bee3a7af08444db8d12c280a` |
| Plan approval and completion | `pi-core22-replyfix-4-1791512031` | 22
files;
`a0383ce1aab38e7b5a25ce0e9dd3bebea5c037ebd96ae6b29dae19015da2ae2c` |
| Same-turn steering and permission denial |
`pi-core22-replyfix-5-1791512261` | 39 files;
`c929b8c7070f0b66aedc17e65ca46e6beab1e363926ac9f7e2a75fb250f05949` |
| Stop during pending permission | `pi-core22-replyfix-6-1791512390` |
33 files;
`7f0a58ae0f4d5bfc76149435f4e322537089c5bd16e7ffe9b5ad71f10a621a07` |

All 215 canonical files (28714587 bytes) are independently
hash-verified. All seven cleanup grades pass, with no owned runtime
process or temporary root after each case. Automatic retries are zero.
The owned cloud host stopped normally after retention. The prior
profile-22 warm attempt remains failed and separately retained: archive
SHA-256
`1e54eba5ec72b50cee1534b23d1d1d4f21a090006b8a64501ba70db972abfde5`. Its
original canonical classification is preserved. Diagnosis reproduced a
product bug comparing an agent-files root against an unset
checkpoint-only field. The fix stores the admitted physical root
separately from the adopted per-run collection capability. The real-root
regression fails before the fix and passes afterward, including
rejection of a changed physical root. Fixture, grader, model and all
seven accepted case IDs are unchanged; this fresh campaign tests
final-reply publication after file registration first. The intermediate
restart attempt also remains failed and retained: archive SHA-256
`5dcaefdf1d17cf4cd54fd4cf810f45e736667392339b8ce7caf08bb4e225277f`. Its
original canonical classification is preserved. Pi resumed, wrote the
verified answer and completed its task; exact runner suspension was
proven, but idle stop consumed about 5.2s and left under 3s for the
drain acknowledgement. The Pi-only default shutdown grace is now 15s,
preserving a full 5s drain round trip and a finite suspension reserve.
Explicit caller deadlines, other provider defaults, literal drain
receipts and exact suspension identity checks remain unchanged. The
timing regression fails before this correction and passes afterward; all
18 focused settlement tests and Runner typecheck pass. The final-source
file attempt is also preserved as failed (`candidate_failure`), archive
SHA-256
`db6767b6773ea618997927ac77bdb005a5ac81492c7b9c0ffbc900449f829bc9`.
Native edit, validation, exact downloadable artifact and Done/succeeded
all passed, and the exact final reply was durably recorded. A workspace
recovery owner completed before the live heartbeat reached presentation,
leaving that reply absent from task chat. Recovery now materializes only
a completed final reply from the accepted turn of an ordinary internal
Done task, preserving issue/run/contract binding, suppression,
external-chat authorization and same-run deduplication. The database
regression covers the generated file-preparation receipt, suppression,
unapproved external continuation and replay. Server typecheck and all 49
response-selection tests pass; hosted Linux verifies the database
regression because embedded PostgreSQL cannot start on this Mac.

The delayed-final-answer database regression passes on [the final
root-source Linux server
shard](https://github.com/paperclipai/paperclip/actions/runs/37868262553/job/113628594152),
alongside 1,108 passing tests. The first root Runner shard had one
unchanged durable-resume test exceed its 5-second timeout; the identical
top-source shard and the isolated exact test passed. One rerun of that
failed job and its required aggregate passed without source or test
changes. The original failed job log and the single-rerun receipt remain
retained.

### October 9 merge verification

Current merge head: `5a8fe63512a7166aaef5cf50065a25008aa8b44b`. All
current-head checks pass, including `ci / verify` and `ci / e2e`;
exact-head Greptile review is 5/5 with no unresolved threads. Current
master conflicts are resolved. The user authorized the maintainer
override of the code-owner review gate after these checks. The seven
retained live core cases remain bound to source
`3241a992f2a7703e59e97ed0fd3e5d6405de4401` and its recorded cloud image.

## Risks

- The security correction changes the dependency closure and profile
identity. Old sessions must reopen on the new profile. Exact identities
and credential bindings fail closed.
- The runner remains experimental and requires explicit selection.
Legacy Pi Local is unchanged. Caller model IDs pass through; the E2E
model is a fixture.
- Accounting and the broad platform/provider matrix remain deferred.
This merge does not publish a release or deploy a service.

## Model Used

OpenAI GPT-6 through Codex assisted with reasoning, repository
inspection, editing and tool use. The exact serving ID and context
window are not exposed in this session. Final live qualification uses Pi
1.0.0 with `openrouter/anthropic/claude-sonnet-4.6` and native-confirmed
low thinking.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-09 08:56:41 -05:00
DottaandPaperclip 6f9d0a56ba fix: resolve installed Codex and preserve npm host dependencies (#15555)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Installed agents need their own runtime dependencies.
> - Native Codex startup and browser login could depend on a global CLI.
> - npm bundles do not inherit workspace dependency overrides or
patches.
> - Codex native packages must come from npm for the consumer host.
> - This PR fixes executable resolution and the required npm dependency
layout.
> - Usable installed CLI versions can run without a numeric version
gate.

## Linked Issues or Issue Description

Refs: #15422. This is a prerequisite for the later Codex-default change.

**What happened?**

Packaged native Codex startup and browser login could require a global
Codex CLI. The server's vendored runner lacked its own declared Codex
bridge. A bundled wrapper could retain producer-host binaries or select
the legacy adapter's separate platform version.

**Expected behavior**

Prefer the installed Codex executable. Accept usable older or newer
versions. Use the selected host's PATH when the dependency is absent.
Keep the patched JavaScript graph. Let npm install official native
packages for the consumer platform. Grant sandbox reads only to the
selected vendor resources.

**Steps to reproduce**

1. Prepare a clean npm installation without a global Codex command.
2. Start native Codex or its browser login.
3. Inspect the selected executable, installed host package, and sandbox
resource paths.

**Paperclip version or commit**

Frozen master base: `1881894973a2b25838d8abed9bd8aeebc3af4441`.

**Deployment mode**

Source and packaged self-hosted installation.

## What Changed

- Share installed Codex command resolution across native startup, direct
evals, and browser login. Accept usable version differences. Preserve
explicit commands, recorded sessions, remote host boundaries, and the
Linux ARM64 legacy login path. Return safe errors for missing
executables and failed login terminals.
- Declare the server's Codex bridge. Retain the patched JavaScript
dependency graph in npm packages. Strip Codex native payloads. Declare
official optional host packages on the published server manifest so
native and legacy versions remain separate. Preserve consumer esbuild
platform dependencies.
- Adjust resource lookup for npm's separate platform packages. Retain
package identity, path containment, resolver ownership, and narrow
sandbox reads. Keep exact version and digest checks for explicit ACPX
artifact qualification.
- Extend existing packaging, installed-consumer, login, selection,
recovery, and integrity tests. Document the retained behavior.

The diff is now 23 files, 1,709 additions, and 53 deletions. The prior
diff had 35 files and about 3,700 changed lines. Removed work is
preserved on `codex/runner-packaging-full-snapshot` at
`6f3060beaa842ffcee21af058370d3cab5f571e9`.

Removed from this PR: release workflows and assembly, provider-pack
changes, Docker materialization, Git installer changes, extra login
HOME/working-directory isolation, and unrelated CI fixture repairs. This
PR does not change agent defaults, stored runner choices, UI, schema, or
provider qualification.

## Verification

- Final candidate: `dde37d7ed0121c10b60b8801eda80f8dc17909ad`. [All 47
ordinary CI jobs
passed](https://github.com/paperclipai/paperclip/actions/runs/37842288835)
on attempt 1, including typecheck, tests, build, E2E, runner checks, and
the installed-consumer canary. [Fresh Greptile
review](https://github.com/paperclipai/paperclip/pull/15555#issuecomment-6059581546)
is 5/5 on this head; no unresolved threads remain. Human approval is
still outstanding.
- Reused focused controls passed with Node 24: 71 initial narrowed
checks, then 56 affected Codex/selection/eval checks after the relative
PATH correction. Runner TypeScript no-emit, syntax, and diff checks
passed. The existing fixture reproduces the original relative PATH
failure and verifies working-directory selection, empty entries,
ordering, and absolute launch. One CLI entrypoint test was initially
blocked by sandbox IPC and passed with its existing local socket
allowed. Login HOME, config-directory assignments, and working directory
match master.
- [The actual clean Linux npm
consumer](https://github.com/paperclipai/paperclip/actions/runs/37842288835/job/113535523044)
passed on the final candidate's CI integration. All 17 Paperclip
tarballs omit native Codex payloads. Official npm host packages retain
their own integrity and `inBundle=false`. Native Codex selects 0.160.0;
the legacy closure retains 0.156.1. Package admission, command leases,
narrow native sandbox resources, consumer hooks, preserved lock, and
offline lifecycle controls passed. Provider calls were zero. No new
workflow is added.
- Actual official Codex 0.156.1 passed on the final committed source on
macOS ARM64: installed dependency preference, absolute and relative PATH
fallback, narrow resource lookup, and app-server initialize/initialized.
Executed module hashes match the candidate. One scripts-disabled
install, three version probes, and one handshake completed in 26
seconds; owned files and process group were removed. No login,
account/model request, or provider task ran.
- CI checked out `dfda8e708e87306d22ed735d78bdfcbe770e99b7` on base
`65b558180533039a891ee0cd1ccab9988aa79adc`. Its 13 upstream paths do not
overlap this PR's 23 paths or alter packaging/Codex inputs. The consumer
report records producer `7b8e94c08657b5ddc265946459762340f125726c`,
after the existing canary staged a generated-lock-only commit (one file,
three insertions). These identities are kept separate; raw
generated-lock bytes were not retained.
- No local Docker or Rust build, paid provider turn, merge, or
deployment. Later PRs must prove live onboarding and production cloud
packaging before changing defaults.

## Risks

- npm must install optional host dependencies. Missing dependencies
still return errors. Paperclip tarballs do not pre-bundle Codex
executables.
- The repository requires CI-owned lockfile updates. PR CI resolves the
changed manifest and stages its own producer lockfile. A raw source
Docker build with `--frozen-lockfile` must wait for the existing master
lockfile bot to merge its refresh, or use a disposable resolved
checkout. No Docker build or deployment is qualified by this PR.
- Ordinary Codex startup accepts version differences. Actual protocol or
login failures remain errors. Explicit ACPX artifact checks retain their
release pins.
- The published server delegates platform installation outside its
bundled JavaScript graph. Focused negative controls reject unsafe
package metadata and paths. The hosted consumer test passed on this
candidate’s CI integration.
- Existing agents retain their stored runner choices. There is no data
migration or automatic upgrade. Release pipeline and platform
qualification work remain separate prerequisites for later defaults.

## Model Used

OpenAI Codex, GPT-6, with reasoning, tool use, code execution, and
parallel agents. The exact serving snapshot and context window are not
exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-09 08:19:51 -05:00
DottaandPaperclip 2b688a8fe3 feat(connections): add verified MCP providers and setup fixes (#15621)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Apps give agents governed access to external tools.
> - Several provider MCP servers lacked a supported catalog entry or
failed during setup.
> - Real browser tests identified specific registration, session, and
form defects.
> - This pull request adds seven catalog entries and fixes the shared
paths used by eleven verified providers.
> - Users can connect these providers through the existing Apps flow and
control each action.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The Apps catalog and remote MCP setup, discovery, and action tester.

**Subsystem affected**

Shared app definitions, the server tool services, and the Apps UI.

**Current behavior**

Seven providers lack a catalog entry. Airtable can select an advertised
client-metadata flow that fails. Calendly rejects the registration name.
Tavily needs an initialized session before a call. Firecrawl exposes
invalid defaults for optional nested form objects. HTTPS setup can
display a callback that differs from the server callback.

**Proposed behavior**

Add Calendly, Exa, Firecrawl, GSC Wizard, Parallel Search, Tavily, and
Windsor.ai. Preserve Airtable, Linear, Make, and PostHog. Use reviewed
provider options through the shared MCP and OAuth paths. Display the
callback supplied by the server. Omit untouched optional object inputs.

**Reason and benefit**

Eleven providers passed bounded reads through the real Paperclip browser
action tester. This PR includes that verified set and its required
shared fixes. Unfinished providers remain outside this change. AgentMail
keeps its existing integration.

**Breaking changes**

No database migration. Existing OAuth credentials and action policies
keep their ownership and access rules. Airtable's reviewed method
re-registers a retained client-metadata binding through DCR. Tavily's
reviewed method initializes sessions before dispatch. The existing
customer, managed, and Vercel OAuth gateway paths now refresh on
upstream 401 and return `oauth_refreshed_retry_required` (409),
requiring an explicit caller retry. They do not replay the rejected call
automatically; the next invocation initializes a fresh credential-scoped
session when required.

Related catalog work: #15545. This branch preserves current master
catalog entries and does not add another Google Workspace integration.
Open and closed provider PRs and public MCP issues were searched. No
duplicate for this verified set was found. The change extends the
shipped Connected Apps roadmap item.

## What Changed

- Add seven catalog entries with official provider branding and reviewed
permissions.
- Update Airtable, Linear, and Make metadata and show Make in Apps.
- Add authoritative provider source overrides that use the existing
generation and review checks.
- Add the reviewed Airtable DCR option and compatible Calendly
registration names.
- Initialize Tavily sessions before discovery and calls, including
retained connections. Keep credential-scoped session caching and single
call dispatch.
- Use the server's callback URL in the OAuth setup UI.
- Omit untouched optional object defaults; keep supplied empty strings,
false, zero, and object values intact in the action tester, with strict
required-child validation.
- Require an explicit retry after OAuth refresh instead of replaying a
tools/call with missing session headers.
- Record all eleven successful reads, write policies, auth-method
limits, and qualification caveats. Omit credentials and private account
data.
- Find chat connector cards through catalog search in browser tests, so
pagination does not hide Slack or other later entries.

## Verification

- Real browser qualification: eleven bounded reads passed. Catalog
refresh and reload proof are recorded in
`doc/connections/verified-mcp-qualification-2026-10-08.md`.
- Provider writes and actual agent-adapter sessions were not run.
Alternative documented auth methods and expiry refresh remain
unqualified.
- `pnpm -r typecheck`: passed.
- `pnpm build`: passed.
- Focused shared/catalog tests: 41 passed. Source override tests: 3
passed. Catalog regeneration fixture suite: 9 passed. Latest form
regression suites: 31 passed. Gateway suite: 38 passed. Other focused UI
suites passed before these last fixes.
- UI token gates and both token sync checks: passed.
- The catalog regeneration fixture includes the authoritative provider
overrides; its full suite passes. The focused OAuth socket case passed
on isolated rerun.
- Complete local UI suite: 7,866 passed. CLI suite: 511 passed, 6
skipped. Complete shared suite: 892 passed.
- The unchanged AgentMail/ClickUp discovery fallback suite passes all 45
cases. An OpenAI login test passed on isolated rerun.
- Local broad database coverage is limited by macOS PostgreSQL
shared-memory exhaustion (`shmget: No space left on device`, not disk
space). The broad run was interrupted after diagnosis; no host settings
or other running services were changed.
- CI found that the Slack browser test assumed its catalog card was on
the first page. The test now uses catalog search; the Slack case passes
locally. Adjacent GitHub and iMessage cases could not start locally
because embedded PostgreSQL initialization failed before browser
assertions.
- Final commit
[`febe4520e`](https://github.com/paperclipai/paperclip/commit/febe4520e13d4b3a4121eafc3d22540c2b11379d):
all 54 checks passed, with none pending or failed. [CI
run](https://github.com/paperclipai/paperclip/actions/runs/37847182065)
passed typecheck, build, all general and serialized test suites, all
eight browser shards, runner checks, canary dry run, and the aggregate
verification gate.
- Greptile reviewed that exact final commit at 2026-10-08 21:33 UTC and
returned 5/5 with no outstanding findings or unresolved threads.
- PR UI preview against the existing local test server: Calendly
catalog/setup observed; Firecrawl read passed after expanding More
options with nested optional inputs untouched. This retest proves
frontend behavior, not the revised OAuth backend against a live
provider.

<details>
<summary>Browser evidence</summary>

![Calendly
catalog](https://raw.githubusercontent.com/paperclipai/paperclip/codex/verified-mcp-connectors/doc/connections/verified-mcp-qualification-2026-10-08/calendly-catalog.jpg)

![Calendly
setup](https://raw.githubusercontent.com/paperclipai/paperclip/codex/verified-mcp-connectors/doc/connections/verified-mcp-qualification-2026-10-08/calendly-setup.jpg)

![Firecrawl
read](https://raw.githubusercontent.com/paperclipai/paperclip/codex/verified-mcp-connectors/doc/connections/verified-mcp-qualification-2026-10-08/firecrawl-read.jpg)

</details>

## Risks

- Provider registration and consent behavior can change. Airtable's DCR
option is explicit and keeps the existing issuer, resource, redirect,
and PKCE checks.
- Tavily uses initialized sessions without automatic call retries. After
a successful OAuth refresh following 401, callers now receive a
retry-required result. A later explicit retry can still require
reauthorization if the provider rejects the refreshed token. Provider
writes are not live-qualified.
- Optional object cleanup affects the shared action tester. Regression
tests cover absent objects, required children, defaults, and populated
values.
- GSC Wizard's underlying Google data scopes and Google flow completion
were not independently verified. Its account reported paid/trial
metadata of unknown origin. No purchase was performed.
- No database migration, new AgentMail integration, or change to
existing action grants is included.

## Model Used

OpenAI GPT-6 through Codex assisted with implementation, research, tool
use, and code execution. The root backend model ID and context window
are not exposed in this session. The cheaper subagents used OpenAI
`gpt-6-luna`. No model context size is inferred.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-09 08:10:46 -05:00
Devin FoleyandPaperclip 4265cb3a2b fix(ci): cache native server integration builds in release verification (#15619)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Release verification must run the same server integration coverage
as PR verification.
> - Three server suites use real Rust Runner binaries.
> - PR checks already run them with the shared Rust dependency cache,
but release checks cold-build them inside ordinary server test shards.
> - A cold Dot Runner build can consume its entire setup deadline before
any test runs.
> - This pull request gives release verification a required cached
native integration lane and preserves every test.

## Linked Issues or Issue Description

**What happened?**

On master commit `1881894973a2b25838d8abed9bd8aeebc3af4441`, [Cloud
readiness run
37837810707](https://github.com/paperclipai/paperclip/actions/runs/37837810707)
failed in server shard 7. Dot Runner's `cargo build --release` exceeded
its 300-second child-process deadline. The other 1,640 tests in that
shard passed. The failed setup then tried to remove an undefined
temporary path and emitted a second error. Cargo output was captured as
an opaque buffer, which obscured build progress.

**What did you expect to happen?**

Build fixture binaries before tests in a lane with the existing Rust
dependency cache. Run all native integration tests and make their result
required for release readiness. A setup failure should keep its original
diagnostic.

**Steps to reproduce**

Run release verification on a clean runner. The existing
`general-server-without-chat` group retains the three Cargo-backed
suites outside the PR workflow. The Dot suite can time out during a cold
release build. Run the new workflow and partition tests against the
previous source to reproduce five routing and coverage failures
deterministically.

**Version / commit**

Observed at `1881894973a2b25838d8abed9bd8aeebc3af4441`; this change is
based on `ed6abbf158b`.

**Deployment mode**

GitHub Actions Release and Cloud readiness verification. No application
runtime or deployment action changes.

Related work: #15581 also edits Dot integration tests for onboarding
behavior. It does not repair release test routing. Searches found no
open PR for this failure.

## What Changed

- Add an explicit server test group that excludes the dedicated chat and
native suites. Preserve the existing PR and local test groups.
- Run all three native server suites in one required matrix lane with
the existing trusted Rust dependency cache.
- Build debug and release fixture binaries in a visible step with a
10-minute limit before tests start. Keep Cargo freshness checks and the
existing test deadlines.
- Stream Cargo diagnostics and clean up safely when Dot setup stops
before creating its temporary directory.
- Test complete, non-overlapping partitions for Release, Cloud
readiness, other callers, and existing PR/local groups. Check cache
restrictions, build order, and the source verification dependency.

## Verification

- Passed 44 workflow and partition tests: `node --test
scripts/__tests__/run-vitest-stable-shard.test.mjs
scripts/__tests__/release-verify-workflow.test.mjs`.
- The same tests fail in five relevant cases against the previous
workflow and selector. They pass after this correction.
- An independent review repeated all 44 tests successfully.
- Passed `actionlint .github/workflows/release-verify.yml`, `git diff
--check`, and a secret scan.
- Passed all 381 workflow policy tests: `node --test
'.github/scripts/tests/*.test.mjs'`.
- Passed full `pnpm build` in a clean worktree.
- Built debug and release fixture binaries, then passed all 32 tests in
the three real native integration suites: `pnpm test:run:general --
--group general-server-native-runner` (49.5 seconds, no skips).
- Passed full `pnpm -r typecheck`.
- Injected a synthetic Cargo setup failure. The suite reports that
failure without the secondary undefined-path cleanup error.
- Exact-head CI completed: 53 successful checks, including all server
shards, both native Runner lanes, build, typecheck, browser tests, and
the canary dry run. Two optional Storybook checks were skipped.
- Started the duplicate local `pnpm test:run` aggregate and stopped it
after exact-head CI passed. No completed local aggregate result is
claimed. All test processes owned by that run exited.
- Greptile scored the final head
`ffe5ab5a28af2dfea9db1c1952c652f070bf52f8` at 5/5 with no review
threads.
- `Superagent Supply Chain Scan` is neutral, not passed: it only
supports exact dependency pin replacements and cannot verify structural
edits to `.github/workflows/release-verify.yml`. It reported no
annotations. Independent source review, Greptile, actionlint, and the
workflow policy tests cover this structural change.
- GitHub still requires a code-owner review for the workflow files.
There are no merge conflicts.

## Risks

- Adds one CI matrix job, which increases concurrent runner demand. The
existing cache writer and trust restrictions stay in place.
- Missing dependencies still compile from the lockfile. A build that
exceeds its step limit fails visibly; failed tests still block source
verification.
- Workflow execution uses the new lane after merge. PR tests validate
the workflow contract and run the existing native test lane.
- The supply chain scanner cannot analyze this workflow structure. Its
neutral result is an explicit coverage limit; it is not counted as a
successful scan.
- No runtime, schema, deployment, publication, credential, or
test-timeout changes.

## Model Used

OpenAI Codex, based on GPT-6, with repository inspection, code
execution, and independent agent review. The exact deployed model ID and
context-window size are not exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-08 14:33:10 -07:00
Devin Foley 53105d5830 feat(apps): add Gauge connection (#15596)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents reach external services through the Apps catalog. Each
catalog entry is a reviewed `AppDefinition` that connects a provider's
hosted MCP server to Paperclip's shared vault, grants, policies,
gateway, and audit trail.
> - Gauge (`withgauge.com`) measures how AI answer engines mention and
cite a brand. Its hosted MCP server also gives SEO, analytics and ads
reports, and runs a content pipeline that can publish to a connected
CMS.
> - Gauge is not in the catalog. Operators must use the generic "Connect
your own MCP server" flow. That flow has no branding, no organization
guidance, and no warning about live CMS publishing.
> - The connector playbook supports this provider with existing
definition fields. The server supports dynamic client registration with
a reviewed `mcp` scope, and it accepts an organization API key as a
bearer header.
> - This pull request adds the Gauge definition, its artwork, the
research and permission-review ledger rows, documentation, and
deterministic tests.
> - The benefit is a branded, governed Gauge connection with browser
sign-in, an API-key option, and a clear publish warning.

## Linked Issues or Issue Description

**Problem or motivation**

Marketing and growth teams use Gauge to track their brand in AI answers
and to run content workflows. They want their Paperclip agents to read
visibility, keyword and traffic data, and to prepare content. Gauge is
not in the Apps catalog. Operators must paste the MCP URL into the
generic remote-MCP flow, which gives no branding, no method guidance,
and no warning that content tools can publish live.

**Proposed solution**

Add a catalog-only Gauge connection built from the connector playbook.
Browser sign-in uses Gauge's dynamic client registration and requests
only the reviewed `mcp` scope. The user selects one Gauge organization
on the consent screen. An optional method sends a customer-created
organization API key as an `Authorization: Bearer` header. Both methods
warn the operator to set publish actions to Ask first. Every discovered
tool stays governed by the normal per-action policies.

**Alternatives considered**

A plugin was not needed because no custom UI, tables, workers, or
webhooks are involved. The identity scopes that Gauge also advertises
(`openid`, `profile`, `email`, `organizations`) are not requested,
because Gauge selects the organization on its consent screen. A
Gauge-specific `classifyRisk` rule was not added, because the tool names
cannot be seen without an account. The generic rule already classifies
`publish`, `create` and `update` tools as writes.

**Roadmap alignment**

This extends the existing self-serve remote-MCP connection catalog and
does not overlap planned core work.

## What Changed

- Added the `gauge` provider to `scripts/ingest-app-definitions.mjs`
(category `analytics`, API-key placement, guidance, warnings,
description) and regenerated
`packages/shared/src/app-definitions/gauge.json` and the generated
registry.
- Added the Gauge row to the self-serve MCP research ledger with
`dcr_or_api_key` auth and risk tier S3.
- Added permission reviews for `gauge/mcp-oauth` (explicit scope `mcp`,
from Gauge's live authorization-server metadata) and `gauge/mcp-api-key`
(provider key), with evidence links.
- Added Gauge's mark (`ui/public/brands/apps/gauge.png`, the avatar of
Gauge's official GitHub organization) and the brand manifest entry.
- Added gallery copy for the Gauge card.
- Added `doc/connections/GAUGE.md` (endpoints, scopes, administrator
setup, capabilities and policy, manifest, brand provenance, validation
hook) and linked it from the connections README and the permission
audit.
- Tests: Gauge definition shape, store visibility and artwork, URL
recognition, reviewed scope, bearer-header placement, the connect form's
sign-in default and API-key gating, and the pinned catalog counts.

## Verification

- `pnpm exec vitest run packages/shared/src/app-definitions.test.ts
packages/shared/src/app-definitions-url.test.ts
ui/src/pages/apps/AppsConnect.test.tsx ui/src/pages/apps/Browse.test.tsx
ui/src/lib/app-brand-assets.test.ts
ui/src/pages/apps/AppLogo.brand-assets.test.tsx
server/src/__tests__/tool-access-service.test.ts` — 738 passed.
- `node scripts/check-app-brand-assets.mjs` and `node --test
scripts/app-brand-validation.test.mjs` — passed.
- `pnpm --filter @paperclipai/shared typecheck`, `pnpm --filter
@paperclipai/server typecheck`, `pnpm --filter @paperclipai/ui
typecheck` — clean.
- `node scripts/ingest-app-definitions.mjs --definitions-only` produces
no Gauge drift.
- Manual: on a local instance, open Apps → Browse and confirm the Gauge
card and icon. Open `/apps/connect?source=gauge` and confirm that
sign-in is the default and that the API-key method is under Advanced.
- Live metadata probe on 2026-10-08: an unauthenticated `initialize` on
`https://app.withgauge.com/mcp` returns 401 with `resource_metadata`.
Both `.well-known` documents return the recorded endpoints and scopes.
Dynamic client registration succeeds. The authorize endpoint accepts
`scope=mcp` and rejects an unknown scope with HTTP 400.

## Risks

- Low risk to existing providers: the change is additive catalog data
plus tests. The generated registry only gains one import.
- Gauge content tools can publish to a connected CMS, including live,
and a bulk keyword update replaces each prompt's keyword list. Both
methods warn the operator, and the API-key helper text tells operators
to set publish actions to Ask first.
- Gauge API keys are not scoped and reach the whole organization.
Paperclip cannot narrow an issued key.
- The permission-review ledger records live proof for both methods as
not run. The lifecycle checklist in `doc/connections/GAUGE.md` still
needs a documented pass with a Gauge organization.

> 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

- Claude Opus 5.5 (`claude-opus-5-5`, 1M context) in Claude Code, with
tool use (shell, file editing, web research).

## 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
2026-10-08 12:37:06 -07:00
DottaandPaperclip 2f0c485dec fix(skills): ship the completion helper with the installed skill (#15554)
## Thinking Path

> - Paperclip manages work for AI agents.
> - Legacy agents use the Paperclip skill to save task status and
comments.
> - The skill names a script relative to the task workspace.
> - That script exists only in the Paperclip source repository.
> - Agents in other workspaces can hit a missing command or search for
it.
> - This PR ships the helper inside the skill and uses the installed
skill path.
> - The repository command remains available through a forwarding
wrapper.

## Linked Issues or Issue Description

Fixes #9527. Refs #15548 for the preceding runtime checkout guidance.
Related: #6052 addresses LF line endings for the repository helper; this
change addresses helper delivery and path resolution.

## What Changed

- Bundle the existing issue update helper with the Paperclip skill.
Preserve its HTTP checks, echoed-status check and two-attempt limit.
- Resolve the command from the installed skill directory. Use a verified
PATCH when that path is unavailable, without searching the filesystem.
- Keep the repository command as a wrapper that works from any
directory.
- Test shell execution and exact status/comment payloads through both
provider skill-home layouts, including paths with spaces.
- Add helper sources and existing verification tests to stock-harness
admission. Record an absent historical helper explicitly. Add the
missing declaration for the admission fingerprint export.

## Verification

- Complete directly affected source suites: 30 tests pass. They cover
skill delivery, preserved multiline comments and links, authentication
headers, empty responses, mismatched status, transient retries and
definitive rejections.
- Product E2E typecheck passes. Support suites: 1,835 Vitest tests pass,
one is skipped; 128 Node tests pass.
- Full local build and workspace typecheck pass.
- Full local repository tests are not claimed as passed. Embedded
PostgreSQL was unavailable in this worktree during the preceding task;
Linux CI will run the repository gates.
- The authorized matched Codex/Claude comparison is pending. It uses the
existing assigned-skill case and original oracle, one initial attempt
per profile and variant.
- CI and a fresh Greptile review are pending. Keep this PR in draft
until readiness gates complete.

## Risks

- Correct path resolution depends on the harness supplying the installed
skill path. The instructions use verified PATCH when that path is
unavailable.
- The helper still requires Bash, curl and jq. Its existing retry and
response-verification behavior is unchanged.
- Tests use the shared skill-directory symlink mechanism and an HTTP
fixture. Real provider completion behavior still requires the bounded
live comparison.
- This fix does not redesign native completion, legacy recovery or
ambiguous transport handling.

## Model Used

OpenAI Codex, GPT-6 family. The exact model build and context window are
not exposed in this session. Used code editing, shell tools and test
execution.

## Checklist


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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-08 07:21:30 -05:00