mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-25 02:26:12 +02:00
1a79522a4e634aaa577e02e0c5bf03f35f9dc435
5589
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
1a79522a4e |
perf: narrow run-to-issue authorization reads (#17405)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Run pages show links to issues associated with a run. > - The issue-link route checks the run's company and issue visibility before it reads those links. > - That check needs four binding fields, but the route reads the full run. > - Full runs can include large prompt, output, and result fields. > - This pull request uses the existing narrow authorization read. > - The route transfers less database data and keeps its permission checks and response contract. ## Linked Issues or Issue Description **Problem or motivation** `GET /api/heartbeat-runs/:runId/issues` calls `heartbeat.getRun` for a permission check. Its consumers only need the run ID, company ID, scope kind, and issue ID. The full read also fetches context, output excerpts, result data, and runtime metadata. Repeated link reads pay for that unused data. **Proposed solution** Use `getRunReadAccess`, the existing four-field read from #17348. Keep authentication, company access, company-scope authorization, run issue visibility, and linked-issue filtering in their current owners. Keep the existing empty response for missing and inaccessible runs. **Alternatives considered** A new query contract is unnecessary because the existing read provides all required fields. A permission cache could become stale. Changing the full run reader could affect consumers that need run details. **Roadmap alignment** This is a focused improvement to an existing route. It adds no feature, route, provider, cache, or background work. Searches for run-issue links, narrow run authorization, and `getRunReadAccess` found no open duplicate. Related work: #17348. ## What Changed - Change the issue-link route to use the existing narrow run read. - Reject full run reads in the route tests. Cover readable company and issue scopes, denied issue access, missing bindings, anonymous requests, and the missing/cross-company empty response. - Add a developer note about the read contract. ## Verification - `pnpm --filter @paperclipai/server exec vitest run src/__tests__/activity-routes.test.ts src/services/heartbeat/run-state.test.ts src/__tests__/agent-live-run-routes.test.ts`: 126 tests passed. - `pnpm build`: passed. - `pnpm -r typecheck`: passed. - The route tests use the real run visibility helper with only the four binding fields. The full reader throws if a route calls it. The existing run-state suite checks the exact four-column selection and covers database wiring with disposable Postgres. - `git diff --check`: passed. Reviewed the committed diff for secrets and private identifiers before push. - Apex review: 5/5 on exact head `888a4ce9ea88bb2b284eb5e28fafc01b6f48b386`, completed successfully, with zero review threads. - Full current-head CI passed: 54 successful checks and two expected skips. [Workflow attempt 2](https://github.com/paperclipai/paperclip/actions/runs/40051399577/attempts/2). [Initial serialized shard failure](https://github.com/paperclipai/paperclip/actions/runs/40051399577/job/120744710081): the first company-history case in unchanged `agent-live-run-routes.test.ts` hit its 15-second timeout; the shard had 391 passing tests and one failure. That file, its agent route, the heartbeat state reader, and the service export are unchanged from base. The five company-history cases pass on unchanged base `9fb28d06ffebbb615f413765078aeeb7c4291311` (9.12s suite) and this head (8.29s suite). They also passed in the 126-test focused run. The timeout was not reproduced locally; its cause is not established. Only the failed shard was retried on the same head. It passed in attempt 2. This establishes recovery; the initial timeout remains in this record and its cause is unproven. - The prior local full-suite failures and baseline triage remain documented in #17384. No new local full-suite pass is claimed here. This change uses focused local tests and the full current-head CI gate. - No hosted user journey, staging deployment, production deployment, or billed savings is claimed for this follow-up. ## Risks - Run visibility depends on the canonical `scopeKind` and `issueId`. The existing helper returns those same fields. It does not use prompt or result data for access decisions. - Linked issues still pass the existing SQL visibility filter. Missing and cross-company runs still return `200 []`. Anonymous requests still fail before the run read. - The database query count stays the same. The change reduces data selected for this authorization read. - No schema or API response changes. ## Model Used OpenAI Codex, GPT-6 family. The exact deployment model ID and context window are not exposed in this session. Used reasoning, repository tools, 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> |
||
|
|
70de7d49e9 |
Migrate standalone nested clones onto the shared project clone (#17407)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Each task runs in a git worktree. Extra project repositories sit in
the task under `.paperclip-repositories/`.
> - #17382 made new nested checkouts git worktrees of one shared clone
per project repository. Existing standalone nested clones stayed as full
clones.
> - Long-lived tasks keep those clones for a long time. Many tasks hold
the same large repository, so most of the disk is duplicate `.git` data.
> - #17369 added a locked and verified move of isolated workspaces onto
the shared clone, with a reaper sweep behind a setting.
> - This pull request adds the same kind of locked and verified
conversion for standalone nested clones in open tasks, and runs it from
the same reaper path behind a new setting.
> - The benefit is that the duplicate clones go away without losing any
state or local history, and the disk stops filling.
## Linked Issues or Issue Description
Refs #17382 #17369. Bug report described in-PR (bug report template).
**What happened?**
On one instance, 44 open task worktrees in one project each hold a
standalone nested clone of the same large repository. Together they use
about 25 GB. About 16 GB of that is duplicate `.git` data. Another
project has 33 smaller clones. The clones are removed only when their
tasks expire, and many tasks stay open for a long time. The disk keeps
filling.
**Expected behavior**
Existing standalone nested clones in open tasks become worktrees of the
project's shared clone, with the same state and local history, and the
standalone clones are deleted.
**Steps to reproduce**
1. Prepare a task with a nested project repository before #17382, or
without a shared clone, so that `.paperclip-repositories/<name>` is a
standalone clone.
2. Create the project's shared clone, as #17382 does for new tasks.
3. Observe that the old checkout stays a full clone until the task
expires.
**Paperclip version or commit:** `master` at
canary/v2026.1024.0-canary.5
|
||
|
|
aa0037a137 | Support Codex warm plugin task reuse and recovery (#17390) canary/v2026.1024.0-canary.4 | ||
|
|
9fb28d06ff |
docs(release): stable notes for the 2026.1023.0-beta.0 soak (v2026.1026.0, since v2026.1023.0) (#17388)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The release process promotes a soaked beta to stable. The stable GitHub Release body comes from `releases/beta/v<beta-version>.md` on `master` > - The `draft_stable_notes` job pushed a commit-log skeleton to this branch when `2026.1023.0-beta.0` published (workflow run 40013393296) > - The skeleton is grouped commit subjects. It is not in release-notes voice and it does not tell a self-hoster what to do before an upgrade > - Stable `v2026.1023.0` was published from `2026.1012.1-beta.0` while this draft was open, so the previous stable is now `v2026.1023.0`, not `v2026.1012.0`. The notes must describe only what changed after it > - This pull request replaces the skeleton with curated notes. Each claim traces to a pull request in `v2026.1023.0..d69631b0` > - The benefit is that the earliest stable promotion (v2026.1026.0) finds finished notes on `master` on time, and readers of the `v2026.1023.0` changelog are not told about the same features twice ## Linked Issues or Issue Description **Issue type** Missing content **Where is the issue?** `releases/beta/v2026.1023.0-beta.0.md` is the stable-notes file for the `2026.1023.0-beta.0` soak. It exists only on this machine-owned branch and only as the auto-generated skeleton. **What's wrong?** The stable promotion reads this file from `master` and publishes it as the GitHub Release body. Until this branch merges, the stable preflight has no notes to resolve. The skeleton lists 715 raw commit subjects with nested PR summaries, 200 of which already shipped in `v2026.1023.0`. It does not call out the changes that need operator attention before the upgrade. **Suggested fix** Merge the curated notes so the notes invariant holds for the v2026.1026.0 promotion. Correct the `> Released:` date in a follow-up if the promotion date slips. ## What Changed - Replaced the skeleton in `releases/beta/v2026.1023.0-beta.0.md` with curated stable notes for v2026.1026.0 in the same layout as `releases/v2026.1023.0.md`: overview, Breaking Changes, Highlights, Fixes, Improvements, Upgrade Guide, Contributors - The notes are delta-framed against `releases/v2026.1023.0.md`. Features that stable already describes (private tasks, model gateways, Slack self-setup, the two-screen GitHub review bot, the Costs ledger, MCP assistant edits, the Operator preset change, OpenAI Dot, the primary agent, decision models, agent identity keys) are not repeated. Bullets that mixed shipped and new work were split so only the new work remains - Breaking Changes lists the removal of the Pipelines experiment (#16834), Sentry run-failure reporting off by default (#16300), the bounded run history list (#16286), and the `--yes` flag on `instructions-file:delete` (#11504) - Highlights cover the Paperclip Runner as the default for new agents, GitHub Copilot as a native harness, project repositories as skill sources, prompt injection prevention, OTLP observability, the unified Google Workspace connection, self-cleaning workspaces with restore points, bulk roster edits, Grok Bot and Muse, Speko, and the Slack, GitHub bot, Apps catalog, and Audit commentary follow-ups - Upgrade Guide enumerates migrations `0334` through `0374` with one-phrase effects, including the constraint rewrites in `0340` and `0361` and the index in `0360`, the new environment variables with defaults, the new default-off experimental settings, the `commentary:view` grant and its startup re-seeding, the Docker image entrypoint change, and the API changes - Release and CI internals, smoke specs, canary tooling, Paperclip Cloud work, dependency bumps, and test-only changes are left out ## Verification - Beta publish is complete: npm dist-tag `beta` is `2026.1023.0-beta.0`, tag `beta/v2026.1023.0-beta.0` points at `d69631b01b9c658f80bf8bed389863dce82643e1`, and the `publish_beta`, `smoke_beta`, and `draft_stable_notes` jobs in run 40013393296 all succeeded - Stable `v2026.1023.0` (tag at `1b4c803816e82b0265c8422e1a804327a7ed8c54`, the `2026.1012.1-beta.0` source commit) is an ancestor of `d69631b0`; `git rev-list --count v2026.1023.0..d69631b01b9c658f80bf8bed389863dce82643e1` returns 515, which matches the overview and Contributors section - Contributors are the 26 unique GitHub logins of the commit authors in the range, read from the GitHub compare API with `[bot]` accounts excluded. The 21 external handles are thanked by name - `./scripts/release.sh stable --date 2026-10-26 --print-version` returns `2026.1026.0` - Every `#NNNN` link in the file was checked: none is a commit subject in `v2026.1012.0..v2026.1023.0`, and each is either a squash-merge subject in `v2026.1023.0..d69631b0` or a pull request whose `merge_commit_sha` lies in that range (#14180, #16356, #16401, #16403, #16976) - Migration numbers, setting keys, env var names, defaults, and harness pins were checked in the code at the source commit and at `v2026.1023.0`, not in commit subjects - The file contains no internal ticket ids or instance-local links - The compare view for this branch shows one changed file and no source changes ## Risks - Low risk: a single markdown file, no source changes. If the promotion date slips past 2026-10-26, the `> Released:` line and H1 need a one-line update before the stable dispatch. The beta-keyed filename makes that re-date harmless - The notes assume the next stable is promoted from this beta. If a later beta is promoted instead, its own notes file supersedes this one ## Model Used - Claude (Anthropic), model ID `claude-fable-5-1` (Claude Fable 5.1), extended thinking enabled, tool use via Claude Code ## 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: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: Bender (Fable) <bender-fable@users.noreply.github.com>canary/v2026.1024.0-canary.3 |
||
|
|
0c3b5ce273 |
perf: filter sandbox provider lookups in Postgres (#17395)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Sandbox execution selects a provider from installed plugin manifests. > - Driver-key lookup reads every plugin row before it finds a matching declaration. > - Repeated lookups transfer manifests for unrelated plugins. > - The registry owns this database read and can filter candidates before returning rows. > - This pull request adds that filter and keeps the existing provider selection checks. > - The returned data now grows with matching providers instead of the full catalog. ## Linked Issues or Issue Description **Problem or motivation** `resolvePluginSandboxProviderDriverByKey` calls the unfiltered registry catalog on each lookup. The caller only needs plugins with a sandbox-provider declaration for one driver key. Unrelated plugin manifests still cross the database connection and are decoded in the server. **Proposed solution** Add an optional sandbox-provider driver-key filter to `pluginRegistryService().list`. Match the key and kind on the same JSON declaration in Postgres. Pass the key from the existing resolver. Keep full matching rows, install order, status checks and worker checks. **Alternatives considered** A catalog cache would need an invalidation contract for plugin updates and independent writers. This change keeps fresh database reads. Returning only one SQL row could skip a later eligible provider when an earlier worker is unavailable. The filter therefore keeps all matching candidates. **Roadmap alignment** This is a small performance fix to existing plugin execution. It adds no product feature or provider. Searches for sandbox/driver lookup and plugin catalog/filter work found no duplicate PR or issue. ## What Changed - Add an optional JSONB containment filter to the existing registry list read. - Use it in sandbox-provider lookup by driver key. The unfiltered catalog call keeps its existing behavior. - Add real-Postgres regression coverage and a short developer note. ## Verification - Four focused provider-selection/recovery/seam suites passed: 27 tests. - `pnpm build`: passed. - `pnpm -r typecheck`: passed. - The new Postgres tests seed large unrelated manifests and verify the actual resolver SQL filters rows in the database. They cover key/kind matching within one declaration, unfiltered reads, install order, disabled and unavailable workers, live status/declaration changes, unknown keys and bound unusual input. - The filter keeps the existing SQL call count. It does not add a network call, cache, timer, or background worker. - `git diff --check`: passed. Reviewed the staged diff for secrets and private account data before push. - Full PR CI passed on `6f97c984a931416b6347e9885d840557c54d8aab`: 54 successful checks and two expected skips. [Workflow evidence](https://github.com/paperclipai/paperclip/actions/runs/40041294925). - Apex review: 5/5 on that exact head, completed cleanly, with no comments or unresolved threads. Merge state is clean. - The prior local full run on parent work is documented in #17384. It did not pass because of test setup, host Git-default assumptions and transient failures. Those results were preserved and triaged against unchanged code. This change uses focused local checks and full current-head CI; no new local full-suite pass is claimed. - No hosted user journey, live provider throughput, production deployment or billed savings is claimed. ## Risks - JSONB containment checks the stored manifest before returning rows. It still scans candidate metadata in the database; no index or SQL-call reduction is claimed. - Both key and kind must match the same declaration. The regression fixture includes a plugin with those values on different declarations. - Selection still uses the existing install-order and live readiness/worker checks. Matching disabled records remain visible to callers that do not require a running worker. - No schema, API response, provider protocol, file-copy behavior or scheduling changes. ## Model Used OpenAI Codex, GPT-6 family. The exact deployment model ID and context window are not exposed in this session. Used reasoning, repository tools 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> |
||
|
|
8549ecaf9a |
Re-home isolated repository workspaces onto the shared project clone (#17369)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Each isolated task workspace is a Git worktree on the server disk,
and the terminal workspace reaper and git cache expiration keep that
disk usage bounded
> - For some days, workspace preparation gave each isolated
`git_worktree` workspace its own full clone under
`isolated-repository-sources/`, with the worktree inside it. #16963
removed that creation path, but the existing clones stay, and restoring
an expired workspace still recreates its private clone
> - On one instance, 30 such clones use about 23 GB: 12.1 GB of `.git`
data, 5.2 GB of unused default-branch checkouts and 5.75 GB of real
worktrees. 28 of them back open workspaces, and the disk is nearly full
> - This pull request moves each such open workspace onto the project's
shared managed clone with its exact state, deletes the private clone,
and makes restore use the shared clone
> - The benefit is that disk use drops to one shared clone per project
repository, with no lost work and no change for workspaces that cannot
move safely
## Linked Issues or Issue Description
Refs #16963. Bug report described in-PR (bug report template).
**What happened?**
On one instance, 30 per-issue clones under
`isolated-repository-sources/<companyId>/<issueId>/checkout` use about
23 GB. 28 of them back open workspaces. No process moves these
workspaces to the shared project clone that new workspaces use, and git
cache expiration does not remove a clone while its workspace is open.
Reopening an expired workspace whose recipe has `sourceKind:
"isolated_remote"` clones the full repository again. Separately, a
restore point with a non-default file mode cannot be restored: the
reference read back from jsonb has reordered `modes` keys, so
`loadGitRestorePoint` fails with `restore_point_manifest_mismatch`.
**Expected behavior**
An open workspace with a private clone moves to the shared project clone
with the same branch, HEAD, index, worktree, untracked and ignored
files, and file modes. The private clone is then deleted. Reopening an
expired workspace uses the shared clone when one exists. A restore point
with non-default file modes restores.
**Steps to reproduce**
1. Create an isolated `git_worktree` workspace with
`materializeIsolatedRemoteWorkspaceSource` and record
`isolatedRemoteSource` metadata, as preparation did before #16963.
2. Keep its issue open. The clone stays, because no sweep moves or
removes it.
3. Expire the workspace with a restore point that has a file with mode
`0600`, then reopen it. The private clone is created again, and the
restore fails with `restore_point_manifest_mismatch`.
**Paperclip version or commit:** `master` at
|
||
|
|
724fa6144d |
fix(opencode-local): preserve the agent during sandbox tool OOMs (#17398)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The Legacy OpenCode adapter runs an agent and its shell tools in the same sandbox. > - A large build can exceed the sandbox memory limit and trigger a kernel OOM kill. > - The kernel can kill OpenCode before its tool, which loses the active turn. > - OpenCode provides a shell environment hook that can change only the tool subprocess. > - This pull request uses that hook to prefer Bash tool processes as OOM victims. > - The benefit is that OpenCode can report a failed tool and continue the task. ## Linked Issues or Issue Description **What happened?** An OpenCode sandbox run ended with exit 137 after a TypeScript process had also been killed. Its cgroup limit was 4 GiB. It had completed useful tool work and had not reached its execution deadline. The original sandbox was deleted, so its historical OOM counters are unavailable. Memory pressure is the leading explanation, not a proven historical kernel diagnosis. **Expected behavior** When a shell tool exhausts sandbox memory, the kernel should prefer that tool over OpenCode where possible. The adapter must preserve configured shell startup, model, permissions and resource limits. **Steps to reproduce** Use a disposable 4 GiB Linux sandbox with OpenCode. Retain a 1,200 MiB buffer in the agent and run a Bash tool with fourteen 250 MiB workers. Compare the same workload before and after installing the generated hook. Record the cgroup memory events, agent exit and final response. Refs: #16844. That PR covers the same protection for Claude. This PR adds the OpenCode-owned hook. Refs: #17254 for the separate shutdown recovery path. This PR does not change retry policy. ## What Changed - Generate a dependency-free OpenCode `shell.env` plugin and a Bash startup file in the isolated runtime config. - Stage those assets only for sandbox execution. Resolve the startup file relative to the copied plugin. - Set child `oom_score_adj` to `1000` on Linux. Keep the parent environment unchanged and honor existing `BASH_ENV` values, including an empty value. - Keep command execution available if the optional plugin cannot be installed or the process priority cannot be changed. - Materialize linked plugin directories inside the isolated config before writing. Keep the operator directory and its plugins unchanged. - Cover relocation, shell output and exit status, operator settings, local execution, remote staging and failed installation. Update the adapter documentation and verification recipe. ## Verification - Red before the fix: the new runtime regression failed because the sandbox shell plugin was absent. The existing 15 runtime config tests passed. The same regression passes with the fix. - Apex first found a host-write risk through linked plugins directories (4/5). A new regression reproduced the host directory modification, then passed after the installer materialized the copied link. The finding is resolved. Apex reviewed current head `df82a8f51725a197c0ea3ebeb942488509f32b34` at 5/5 with no actionable issues remaining. - The full OpenCode adapter suite passes: 87 source tests. The adapter typecheck and map checks pass. - Real sandbox acceptance used the plugin generated from `df82a8f51725a197c0ea3ebeb942488509f32b34` and OpenCode 1.18.34 in a disposable 4 GiB Daytona sandbox. A controlled local model endpoint issued an actual Bash tool call; no real model credentials were used. The tool reported priority `1000`; the parent reported `0`. - Controlled OOM comparison: without the guard, OpenCode was killed by SIGKILL after 8.95 seconds and the cgroup recorded one OOM kill. With the guard, five workers were killed, OpenCode exited `0` after 21.87 seconds, and its final response was `fixture-complete`. The cgroup counter increased by five. Both attempts used the same allocation and workload. The fixture started with a linked operator plugins directory. The installer preserved that directory, staged a real plugins directory, and passed both the process-priority probe and OOM comparison. The fixture allocation was deleted afterward. - The first live probe was not accepted because the harness could not parse the provider response framing. After correcting the decoder, the unchanged plugin passed the probe and the controlled OOM comparison. Both initial disposable allocations were deleted. After the review fix, a third allocation repeated the probe and OOM comparison with a linked plugins directory. That allocation was also deleted. The initial comparison had the same agent-killed/agent-survived result (8.42 seconds without the guard; 19.42 seconds with it). - Repository `pnpm -r typecheck` and `pnpm build` pass. The full CI matrix passes on `df82a8f51725a197c0ea3ebeb942488509f32b34`, including tests, browser shards, Runner checks, build, policy and Apex. [CI run](https://github.com/paperclipai/paperclip/actions/runs/40043947375). - The first full local test attempt had 15 embedded PostgreSQL startup failures. Direct `initdb --version` and `postgres --version` probes showed missing bundled library symlinks and SIGABRT. Running the bundled symlink hydration script restored both executables. All 15 affected cases pass after that setup repair. No product code changed for it. - Two existing workspace tests assumed that new Git repositories have no `main` branch. They failed under the host default branch setting of `main`, then passed with a per-command `init.defaultBranch=master` setting. The global setting was preserved. The full local test command was restarted with hydrated libraries and that controlled fixture default. It had no reported assertion failures when it was stopped after the complete CI matrix passed. It did not complete locally; full-suite verification comes from CI. The first failed invocation was also stopped. - The original tenant task has not been retried or recovered by this PR. Its board API credential is expired. The original hosted workflow and the separate shutdown recovery remain outside this acceptance result. ## Risks - This is a preference, not a memory reservation. It cannot guarantee that OpenCode survives every OOM condition, including memory growth inside OpenCode itself. - The guard applies to non-interactive Bash tools. Other shells ignore `BASH_ENV`. Existing operator startup settings take precedence. - Older OpenCode releases without the shell environment hook will not apply the preference. No model, permission, deadline, memory quota or retry limit changes. - No schema change or migration. ## Model Used OpenAI GPT-6 through Codex, with repository tools and code execution. The exact deployment variant and context window are not exposed by this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes: #` / `Refs: #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.1024.0-canary.2 |
||
|
|
a978ecadef | Honor negated attachment requests in task completion (#17399) | ||
|
|
72a5fecb58 |
docs(release): canonicalize stable notes for v2026.1023.0 (#17400)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The release process promotes a soaked beta to stable. The stable run publishes the curated notes from `releases/beta/v<beta-version>.md` on `master` as the GitHub Release body > - After the publish, the notes must move to `releases/v<stable-version>.md` on `master`. That path is the canonical location that the release announcements and the next release notes link to > - Stable `v2026.1023.0` was published on 2026-10-23 from beta `2026.1012.1-beta.0`. The `canonicalize_stable_notes` job in run 40030701194 was skipped, so the workflow did not push this branch > - This pull request does by hand what that job does: it renames the beta notes file to the stable path. It changes no content > - The benefit is that `releases/v2026.1023.0.md` exists on `master` and is byte-identical to the published GitHub Release body ## Linked Issues or Issue Description Refs #17373 Refs #16053 **Issue type** Missing content **Where is the issue?** `releases/` on `master`. The stable notes for `v2026.1023.0` are only at the beta path `releases/beta/v2026.1012.1-beta.0.md`. The canonical path `releases/v2026.1023.0.md` does not exist. **What's wrong?** The GitHub Release https://github.com/paperclipai/paperclip/releases/tag/v2026.1023.0 is published, but the `canonicalize_stable_notes` job of the release run was skipped. The release announcements link to `releases/v2026.1023.0.md` on `master`, and that link returns 404 until this rename is merged. **Suggested fix** Rename the beta notes file to the stable path with no content change. This is the same step the workflow job performs. ## What Changed - `releases/beta/v2026.1012.1-beta.0.md` is renamed to `releases/v2026.1023.0.md`. Git reports a 100% rename - No line of the file changes. The blob is `170e36dc971bc8f3a59fb574fc8cd19d366fc2d4`, the same blob the release run pinned at `master` commit `e0d90339` when it created the GitHub Release - This pull request is a pure rename. It does not change the release workflow ## Verification - `git diff --stat -M100% master...release-notes/v2026.1023.0-canonicalize` shows one renamed file and no insertions or deletions - `git rev-parse release-notes/v2026.1023.0-canonicalize:releases/v2026.1023.0.md` prints `170e36dc971bc8f3a59fb574fc8cd19d366fc2d4` - `git rev-parse e0d90339bfbde08ba804c1e527b781ac139075c7:releases/beta/v2026.1012.1-beta.0.md` prints the same blob - `gh api repos/paperclipai/paperclip/releases/tags/v2026.1023.0 --jq .body | diff - releases/v2026.1023.0.md` reports only the one trailing newline that `jq` adds to its output. The release body and the file have the same bytes - After merge, https://github.com/paperclipai/paperclip/blob/master/releases/v2026.1023.0.md returns 200 and the old beta path returns 404 ## Risks - Low risk: one markdown file moves and no content changes - The release workflow's `canonicalize_stable_notes` job was skipped in run 40030701194 although `publish_stable` succeeded and the notes mode was `master_beta`. The job's `if` has no status function, so GitHub applies an implicit `success()` that evaluates to false when an upstream job in the chain was skipped (`release_runner_binaries` was skipped in this run). A one-line `!cancelled() &&` guard, as `publish_stable` already uses, fixes it. That fix is not in this pull request so this stays a pure rename ## Model Used - Claude (Anthropic), model ID `claude-fable-5-1` (Claude Fable 5.1), extended thinking enabled, tool use via Claude Code ## 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: Paperclip <noreply@paperclip.ing> |
||
|
|
100477e887 | Avoid live workspace inspection in task context reads (#17396) canary/v2026.1024.0-canary.1 | ||
|
|
cc127f5516 |
fix(runner): drop unusable musl builds and duplicate Copilot from provider packs (#17387)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The runner release builds a Linux "provider pack". The pack is a self-contained runtime with Node, Claude Code, Codex, and OpenCode. The Daytona runner image and SSH hosts use it. > - The release artifacts for this pack are large: 1,182 MB for linux-x64 and 543 MB for linux-arm64. > - Most of that size is necessary. But about 20–30% of it is code that the pack can never run. > - pnpm 9 installs all libc variants of optional platform packages. Thus the pack includes the musl (Alpine) builds of Claude Code and OpenCode. The pack runs only its copied glibc Node, so these builds are never used. > - On linux-x64, the npm Copilot package is also a byte-identical copy of the verified Copilot provider asset (162 MB). > - This pull request removes these unused bytes from the pack at build time. > - The benefit is smaller release artifacts and Docker images, and faster uploads and downloads. ## Linked Issues or Issue Description **What happened?** The runner release workflow uploads `runner-provider-packs-linux-x64` (1,182 MB) and `runner-provider-packs-linux-arm64` (543 MB). The darwin daemon artifacts are about 10 MB. The darwin artifacts are a different artifact type, so the comparison is not direct. But inspection of the extracted Linux packs found real waste: - `@anthropic-ai/claude-agent-sdk-linux-*-musl` (about 225 MB uncompressed) - `opencode-linux-*-musl` and `opencode-linux-x64-baseline-musl` (about 180 MB each) - on linux-x64, `node_modules/.pnpm/@github+copilot-linux-x64@1.0.88/.../copilot`, which has the same SHA-256 (`0059754c…`) as `provider-assets/copilot/linux-x64/copilot` **Expected behavior** The pack contains only code that its copied Node and its launch shims can run. **Paperclip version or commit** `master` at `e0d9033`. The inspected release run built commit `837add0`. **Steps to reproduce** 1. Install `@agentclientprotocol/claude-agent-acp@0.73.0` and `opencode-ai@1.18.34` with pnpm 9.15.4 on a glibc Linux host. 2. List `node_modules/.pnpm`. 3. See the `*-musl` entries. Their `package.json` declares `"libc": ["musl"]`. ## What Changed - `provider-pack-runtime-tree.mjs`: add `providerPackLibc()`. It reports the libc of the Node that the pack copies (`glibc` or `musl` on Linux, none on other platforms). - `provider-pack-runtime-tree.mjs`: add `removeForeignLibcPackagesFromProviderPack()`. It removes each pnpm virtual-store entry whose own `package.json` declares a `libc` list without the pack libc. Packages without a `libc` field stay (for example `@openai/codex-linux-*`, which holds Codex's static musl binary). - `provider-pack-runtime-tree.mjs`: add `removePnpmStoreEntries()`. It removes store entries and every symlink that resolves into them, so no dangling link stays in the pack. It refuses names outside `node_modules/.pnpm`. - `provider-pack-executable-shims.mjs`: when `provider-assets/copilot/<platform>/copilot` has the same bytes as the npm Copilot executable, the `copilot-<platform>` command now points at the asset and the npm copy is removed. If the bytes are different, the old behavior stays. - `build-provider-pack.mjs`: run the libc prune immediately after `pnpm deploy`. Log the removed entries. - No file bound by `provider-pack.json` changes. Thus the manifest digest and the qualification and digest pins do not change. ## Verification - `node --test packages/paperclip-runner/scripts/provider-pack-runtime-tree.test.mjs packages/paperclip-runner/scripts/provider-pack-executable-shims.test.mjs packages/paperclip-runner/scripts/candidate-provider-pack.test.mjs packages/paperclip-runner/scripts/release-provider-pack.test.mjs`: 40/40 pass. - The new Copilot test fails with the old `provider-pack-executable-shims.mjs` and passes with the new file. - A real pnpm 9.15.4 tree with the pack's provider dependencies, on linux-x64 (glibc): - The prune removed `@anthropic-ai+claude-agent-sdk-linux-x64-musl`, `opencode-linux-x64-musl`, and `opencode-linux-x64-baseline-musl`. The Copilot dedupe removed the npm Copilot entry. - `node_modules` changed from 1.9 GB to 1.1 GB. - `find node_modules -xtype l` found no dangling links. - The claude-agent-acp SDK still resolves `@anthropic-ai/claude-agent-sdk-linux-x64/claude`. - `opencode --version` prints `1.18.34`. - The rewritten `copilot-linux-x64` prints `GitHub Copilot CLI 1.0.88.` - Earlier measurement: we pruned the same paths from the extracted pack artifacts and compressed them again with `tar -czf`. linux-x64 went from 1131 MB to 876 MB. linux-arm64 went from 519 MB to 360 MB. - Not run locally: a full `build-provider-pack.mjs` build. This sandbox has 10 GB of disk, which is not sufficient for a full workspace install and a 3 GB pack. The `release-verify.yml` provider-pack step and the Daytona image `RUN claude/codex/acpx --version` checks run the full build. - `test/acpx-codex-package-contract.test.mjs` has 1 failure in this sandbox, and the failure is the same on `master`. The cause is that this checkout has no `node_modules`. ## Risks - Low risk. All pack builds run on glibc hosts (Debian bookworm images and Ubuntu CI). On a musl build host, the prune removes the glibc packages, and the existing Claude executable resolution fails at build time. Before this change, that build gave a pack with a Claude binary that could not run. - The `copilot-linux-x64` command now runs the binary from `provider-assets/copilot/linux-x64/`. The bytes are identical. Runtime admission already uses this asset, through the descriptor-pinned lease. - The pack content changes but the manifest digest does not. The remote verifier checks only the bound artifacts, `dist`, and provider asset trees. None of these change. - Not in this pull request: Pi's bundled Node is byte-identical to the pack Node (121 MB, about 43 MB compressed). A fix for it changes the qualified Pi closure digest. Also, the `release.yml` publish jobs download the full packs, but they read only `provider-pack.json`. These are possible follow-ups. ## Model Used - Claude Opus 5.5 (Anthropic), model ID `claude-opus-5-5`, used through Claude Code with tool use (shell, file edit) and code execution in a sandbox. ## 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 (no doc change is necessary: build behavior and commands do not change) - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [ ] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>canary/v2026.1024.0-canary.0 |
||
|
|
5a7e740d82 |
fix: validate managed AI accounts before verification allocation (#17384)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - A saved agent must pass execution configuration checks before it can run. > - Managed AI account selection belongs to the existing company-scoped account service. > - Verification allocated its execution environment before it checked that account. > - A missing default or revoked account could create a sandbox on each retry. > - This pull request checks account selection before allocation and keeps the final check. > - Invalid account settings now fail before they cause avoidable provider work. ## Linked Issues or Issue Description **What happened?** Saved-agent lifecycle verification allocates a test execution target before `withManagedAiProbe` selects the managed AI account. An unavailable account can cause sandbox creation and cleanup on every retry. **Expected behavior** Check account compatibility, responsible-user access, default selection, grant health and agent eligibility before target allocation. Check current account access again before the real probe. **Steps to reproduce** 1. Configure a saved OpenCode agent with an OpenRouter personal-default binding. 2. Leave that user's OpenRouter default empty, or revoke the selected grant. 3. Let lifecycle verification use a sandbox execution environment. 4. Before this change, verification can allocate the target and then reject the account. After this change, account selection rejects the configuration before allocation. **Paperclip version or commit** Source baseline `b5bf86ea09042a864b60910f80c8fdf7e64e9ef0`. **Deployment mode** Server with Postgres and a remote execution environment. Tests use disposable local Postgres and a controlled target allocation boundary. Searched public PRs and issues for verification, connection preflight, probes and lifecycle work. No duplicate fix found. Related public issue #16128 concerns credential probing and reauthorization. This change does not alter reauthorization or credential refresh. ## What Changed - Call the existing managed account selector with the resolved adapter and model before allocating a verification execution target. - Keep router resolution, secret handling, final account selection and target release with their existing owners. - Add six Postgres tests and document the saved-agent ordering contract. ## Verification - Focused verification suites: 30 tests passed across two files. - `pnpm build`: passed. - `pnpm -r typecheck`: passed. - Full PR CI passed on `ddc0879d46b60310c533e6d3c4b8acdd178d979c`: 54 successful checks and two expected skips. [Workflow evidence](https://github.com/paperclipai/paperclip/actions/runs/40033261909). This includes typecheck, tests, build and browser checks. - Apex review: 5/5 on that exact head, clean check, no comments or unresolved threads. Merge state is clean. - Additional local `pnpm test:run` is still running. The complete local suite is not yet a pass claim. - The first focused run passed the missing-default case but failed five setup hooks because fixtures reused a unique company prefix. Each fixture now has a distinct prefix. The complete focused rerun passed. - Tests use real account selection, memberships, grants, personal defaults, encrypted fixture credentials and managed runtime preparation. Only execution-target allocation and the provider probe are controlled boundaries. No live provider request is required. - Tests prove that missing defaults, revoked grants and incompatible models do not allocate a target. A valid account still reaches the managed probe. Revocation during allocation still fails the final account check and releases the target. Probe failure also releases it. - `git diff --check`: passed. Reviewed the diff for secrets and private account data before push. - No hosted login or production deployment is claimed for this follow-up. ## Risks - Adds one read-only account selection to each managed saved-agent verification attempt. The selector can read company accounts and grants. It makes no provider request and creates no execution target. - Final selection stays fresh. A successful preflight does not reserve an account or cache authorization. - Missing configuration still follows the existing durable lifecycle retry schedule. This change prevents needless allocation; it does not remove those database retries. - No schema, scheduler, provider-account choice, capacity or public API changes. ## Model Used OpenAI Codex, GPT-6 family. The exact deployment model ID and context window are not exposed in this session. Used reasoning, repository tools 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> |
||
|
|
ec2e0d6407 |
Share one clone across nested project repository checkouts (#17382)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - A project can attach extra repositories, and every task worktree
gets a managed checkout of each one under
`.paperclip-repositories/<name>`
> - Each of these nested checkouts is a full clone. The clone uses
`--dissociate`, so no objects are shared between tasks
> - On one instance, 44 task worktrees held nested checkouts of the same
repository: 24 GB of 27 GB of worktrees. Each new task added about 0.5
GB, and the disk filled twice in one day
> - Main task worktrees already avoid this, because they are git
worktrees of one managed project clone
> - This pull request makes nested checkouts git worktrees of one shared
clone per project and repository, and adapts expiration, restore points,
restore and cleanup to them without weaker safety checks
> - The benefit is that repository objects are stored once, and a new
task adds only its working files
## Linked Issues or Issue Description
No public issue exists. This is the in-PR bug report.
**What happened?**
Every task in a project with extra repositories got a full, independent
clone of each extra repository under `.paperclip-repositories/`. On one
instance this used 24 GB of 27 GB of task worktrees (16 GB of it `.git`
data) and filled the disk twice in one day.
**Expected behavior**
Nested checkouts of the same repository share one object store, like
main task worktrees share the managed project clone. Disk use per new
task is only its working files.
**Steps to reproduce**
1. Create a project with a primary repository and a second repository
that has a repository URL and no local folder.
2. Run several tasks in isolated git worktrees in that project.
3. Look at `<task worktree>/.paperclip-repositories/<name>/.git`: each
one is a full directory with its own copy of every object.
**Paperclip version or commit:** `master` at
canary/v2026.1023.0-canary.18
|
||
|
|
bf223596ab |
fix(runner): bootstrap fresh sessions in replacement sandboxes (#17320)
Reuse the untouched-session proof and atomic directory claim when a sandbox replacement carries a provider session that has never run. Retain verified restore for saved backups and all prior or ambiguous session activity. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
e0d90339bf |
fix(runner): make task-capable plugin environments Runner-only (#17380)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - An environment plugin can run Paperclip Runner tasks through a `driver: "plugin"` environment. > - Core lets only the `paperclip_runner` adapter use a plugin environment. > - A Claude agent cannot get onto the Runner there. Runner selection needs a reported platform for Claude, and a plugin target reports none. > - The adapter Test route also checks the driver against the requested harness before it resolves the runner. > - This pull request makes task-capable plugin environments Runner-only and checks the Test driver after runner selection. > - The benefit is that Claude and Codex agents can use a Runner task environment, and the UI cannot produce a legacy agent there. ## Linked Issues or Issue Description Refs #17029. Refs #16978. **What happened?** A Claude Code agent with a plugin environment that runs Runner tasks fails the connection test. The error is: `Environment driver "plugin" is not allowed here. Allowed drivers: local, ssh, sandbox`. The Runner selector in Advanced shows Paperclip Runner as unavailable, so the user cannot fix the agent in the form. **Expected behavior** A Claude or Codex agent on that environment uses Paperclip Runner. The connection test passes the driver check. Legacy is not offered, because a legacy adapter cannot run on a plugin environment. **Steps to reproduce** 1. Install an environment plugin whose driver declares `supportsTasks: true`. 2. Create a plugin environment for that driver. 3. Open an agent with the Claude Code harness and select that environment. 4. Click Test. The test fails with the driver error above. **Agent adapter(s) involved** Claude Code, Codex (through Paperclip Runner). **Additional context** Two gates caused the failure: - `agentRunnerAvailability` requires a platform for `claude_local`. A plugin target is `{ driver: "plugin" }`, so Claude resolves to Legacy. - The Test route ran `assertAdapterTestEnvironmentForCompany` with the URL adapter type before `resolveNewAgentRunnerForCompany`. The plugin driver is allowed only for `paperclip_runner`, so the check failed before selection could change the adapter. ## What Changed - Runner target resolution marks a plugin environment as `runnerTasks` when its plugin manifest declares `supportsTasks` for the driver key and has the `environment.drivers.register` capability. The check reads the manifest only. Run admission still verifies a ready plugin and a live worker. - `agentRunnerAvailability` returns Paperclip Runner as the only runner for a `runnerTasks` target. It skips the platform check, because the plugin provisions the host and launches its own pinned Runner. Harnesses that the Runner does not support keep the Legacy result. - The adapter Test route runs the company-binding check first, as before. It runs the driver check after runner selection, with the resolved adapter type. The driver check still runs before secret resolution, leases, and the adapter probe. - A saved adapter that cannot run on the requested environment no longer keeps its saved execution in the Test route. It re-resolves its runner. - `assertAdapterTestEnvironmentBinding` is a new helper that holds the binding half of the guard. `assertAdapterTestEnvironmentForCompany` keeps its behavior and calls it. - The agent form switches a Legacy runner choice to Paperclip Runner when the selected environment offers no Legacy runner. The test and the save then send the Runner choice. - `docs/specs/agent-config-ui.md` records the Runner-only rule and the Run test order. ## Verification - `pnpm --filter @paperclipai/server exec tsc --noEmit` passes. - `pnpm --filter @paperclipai/ui exec tsc -b` passes. - Server suites pass: `agent-runner-selection.test.ts`, `agent-test-environment-routes.test.ts`, `agent-permissions-routes.test.ts`, `environment-selection-route-guards.test.ts`, `agent-harness-verification.test.ts`, `plugin-runner-task.integration.test.ts`, `adapter-routes.test.ts`, `adapter-auth-signal-routes.test.ts`, `agent-adapter-validation-routes.test.ts`, `agent-hire-ai-connections.test.ts`. - UI suites pass: `AgentConfigForm.render.test.tsx` and `NewAgent.test.tsx`. - New tests cover these cases: - A task-capable plugin target offers only Paperclip Runner for Codex and Claude. - Claude saves as `paperclip_runner` with ACPX on that target. - A driver without `supportsTasks`, a different driver key, a missing capability, an unknown plugin, or a malformed config keeps the existing plugin target. - The Test route checks the driver against the resolved adapter. - The Test route still rejects a plugin environment for a legacy adapter, before any secret resolution. - A saved adapter re-resolves only when it cannot run on the requested environment. - The form switches a saved or new Legacy choice to Paperclip Runner on a Runner-only environment. - I ran the new route and form tests against the old code. They fail without the change. - The full server `typecheck` script did not complete locally. Its Runner vendor step needs `cargo`, which this machine does not have. CI runs the full script. - I did not run a live plugin environment end to end. ## Risks - Behavior change: Codex on a task-capable plugin environment now offers only Paperclip Runner. Before, the selector also showed Legacy, but Core rejected a legacy save on that environment. No working setup loses an option. - The `runnerTasks` decision reads the plugin manifest. If the plugin is down, the agent still selects the Runner, and the run fails at admission with the existing plugin error. - The adapter auth-signal route still checks the driver with the requested harness. This PR does not change that route. - An explicit Legacy save through the API on a Runner-only environment still fails with the existing driver error. The UI no longer offers that choice. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`) wrote the change and the tests in Claude Code, with shell, file editing, and local test runs. - Claude Fable 5.1 (`claude-fable-5-1`) did the earlier diagnosis in the same session. - The session does not expose the context window size. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Paperclip <noreply@paperclip.ing> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b5bf86ea09 |
Clear process identity when settled native runs stop (#17374)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Each heartbeat run stores the process identity of its runner in
`heartbeat_runs.process_pid`, `process_group_id` and
`process_started_at`
> - Only cancellations (`release_run_resources`), restart recovery and
heartbeat recovery cleared these fields
> - Failed native runs take the `schedule_retry` path, so they kept a
PID after the run finished, for weeks
> - Liveness checks for old runs then depend on PID-reuse defenses, and
code that reads a set PID as "running" is misled
> - This pull request releases the process identity of settled native
runs when the process is confirmed gone, and backfills old sandbox rows
> - The benefit is that a finished run no longer looks like it owns a
live process, while live, warm and retrying processes keep their
identity
## Linked Issues or Issue Description
Refs #17358. Bug report described in-PR (bug report template).
**What happened?**
On one instance, 16 of 23 old native workspace sync folders belonged to
failed or cancelled runs that finished more than a week earlier and
still had `process_pid` set. The PID is written when the run's process
starts (for remote targets it is the sandbox PID). The status arbiter
clears it only through `release_run_resources`, which only cancellations
emit. Failed native runs take `schedule_retry`, and other terminal paths
(finalization reconciler, restart recovery terminal failures, watchdog
timeouts) do not clear it either.
**Expected behavior**
When a run is terminal and its process is confirmed gone (or no longer
owned), the process identity is cleared. A process that may still be
alive keeps its identity.
**Steps to reproduce**
1. Run a native task whose provider turn fails, so the arbiter decides
`run_failed_partial_evidence_preserved` with a `schedule_retry` effect.
2. Wait for the run to finish and the runner to exit.
3. Read the run row: `status = 'failed'` and `process_pid` /
`process_group_id` / `process_started_at` are still set.
**Paperclip version or commit:** `master` at
canary/v2026.1023.0-canary.17
|
||
|
|
4660b22f6b |
perf: narrow run bindings during event ingestion (#17378)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Agent runs append native events and retry-exhaustion receipts. > - These writes lock the run row and verify its company and agent bindings. > - The binding check fetched the full run, including prompts, configuration, and output. > - This pull request reads only the two fields that the check uses. > - The benefit is less database transfer while the same row lock protects sequence allocation and replay checks. ## Linked Issues or Issue Description Related: #17355 and #17348 reduce other run-read payloads. GitHub searches for event-read and narrow-run PRs found no duplicate of this event-ingestion change. **What existing behavior does this improve?** The run binding read during native event and retry-exhaustion ingestion. **Current behavior** The transaction uses a full-row `SELECT ... FOR UPDATE` to check only `companyId` and `agentId`. Large run context, runner configuration, and output cross the database connection for each such check. **Proposed behavior** Select only the company and agent IDs. Keep the same run ID predicate, row lock, transaction, sequence allocator, replay cursor, and binding checks. **Reason and benefit** The check does not use the other fields. Narrowing the query removes that repeated transfer without a cache or an authorization change. **Breaking changes** None. No API response, schema, event, or scheduling changes. ## What Changed - Narrow the existing run-row read inside `appendHeartbeatRunEvent`. The event service continues to own its transaction and binding validation. - Exercise native and retry-exhaustion writes and replays with large stored payloads. Record the executed SQL to prove the lock reads only the two binding fields. - Extend missing-run and company/agent mismatch coverage to both transaction paths. Keep the existing concurrent-writer and rollback tests. - Document the read boundary. ## Verification - Focused PostgreSQL event append and sequencing suites: 9 tests passed. They include concurrent writers, replay conflicts, rollback, cursor checks, large payloads, and company/agent/missing-run denials. - The first new retry-exhaustion fixture failed its duplicate assertion because it omitted the exhaustion fields from the event payload. The existing producer contract stores those fields in the payload. The fixture now does the same; the rerun passed. No production retry or replay logic changed. - `pnpm build`: passed. - `pnpm -r typecheck`: passed. - Greptile Apex: 5/5 on `76a6d3ee813ae47919b1ec8e8b88e01e58d5655d`, clean check and no review threads. The [full PR workflow](https://github.com/paperclipai/paperclip/actions/runs/40028758665) passed on that exact head: 54 checks passed and two conditional checks were skipped. The branch is conflict-free. - The full local all-in-one suite is not repeated for this follow-up. Full exact-head CI will supply the broader suite coverage. - No hosted deployment or bill reduction is claimed for this follow-up. ## Risks - Low scope: only the selected columns change. The `FOR UPDATE` lock, transaction boundaries, sequence reservation, duplicate checks, and failure handling remain in place. - Native replay and legacy event writers must still serialize on the same row. Existing real PostgreSQL concurrency tests cover that boundary. - No migration, provider change, new timer, or operator configuration is required. ## Model Used OpenAI Codex (GPT-6 family), with code execution and repository tools. The runtime does not expose the exact model ID or context window. ## 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 (focused suites above) - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
ff835a3fc6 |
docs(release): set the v2026.1023.0 header and contributor count before publish (#17373)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The release process promotes a soaked beta to stable. The stable workflow reads `releases/beta/v<beta-version>.md` from `master` at dispatch time and publishes that revision as the GitHub Release body > - The curated notes for `2026.1012.1-beta.0` (#16053) were written on 2026-10-13. They guessed the stable version `v2026.1015.0` and the date `2026-10-15`. That version did not ship > - The promotion is now set for 2026-10-23. `./scripts/release.sh stable --print-version --date 2026-10-23` on `master` returns `2026.1023.0` > - Nothing merged after the stable dispatch reaches the release page, so the header must be correct on `master` before the dispatch > - This pull request sets the header, the date, the intro version, and the contributor count. It changes no other text > - The benefit is that the public release page for v2026.1023.0 shows the correct version, date, and credits ## Linked Issues or Issue Description Refs #16053 **Issue type** Incorrect content **Where is the issue?** `releases/beta/v2026.1012.1-beta.0.md` on `master`: the H1, the `> Released:` line, the first sentence of the overview, and the first sentence of the Contributors section. **What's wrong?** The file names `v2026.1015.0` and `2026-10-15`. The stable that promotes this beta is `v2026.1023.0` on `2026-10-23`. The Contributors line says 6 contributors. `git shortlog -sn v2026.1012.0..1b4c803816e82b0265c8422e1a804327a7ed8c54` lists 8 authors. With `github-actions[bot]` excluded, as in `releases/v2026.1012.0.md`, the count is 7 (Dotta, Devin Foley, dependabot[bot], Nicky Leach, Michael Nguyen, Kilian, lorenzozane). **Suggested fix** Replace the four stale values. Keep every other line unchanged so the file still matches the audited content. ## What Changed - `# Paperclip v2026.1015.0` becomes `# Paperclip v2026.1023.0` - `> Released: 2026-10-15` becomes `> Released: 2026-10-23` - The overview sentence now reads `Paperclip v2026.1023.0 carries 200 commits, promoted from 2026.1012.1-beta.0` - The Contributors sentence now reads `This release has 200 commits from 7 contributors` - No other line changes. The credited handles `@lorenzozanee` and `@lusoris` stay as they were ## Verification - `git diff master..release-notes/v2026.1023.0-prepublish --stat` shows one file, 4 insertions, 4 deletions - `grep -n '1015\|2026-10-15' releases/beta/v2026.1012.1-beta.0.md` returns nothing on this branch - `./scripts/release.sh stable --print-version --date 2026-10-23` on `master` prints `2026.1023.0` - `git rev-list --count v2026.1012.0..1b4c803816e82b0265c8422e1a804327a7ed8c54` prints 200. The GitHub compare API for the same range also reports 200 - `git shortlog -sn v2026.1012.0..1b4c803816e82b0265c8422e1a804327a7ed8c54` lists 8 authors. 7 remain after `github-actions[bot]` is removed, which is the rule `releases/v2026.1012.0.md` used - All 129 `#NNNN` links in the file resolve to pull requests inside the range. The 71 in-range pull requests that the file does not cite are CI, tests, lockfile refreshes, heartbeat refactors, dependency bumps, release housekeeping, or Paperclip Cloud work (#15405, #15566, #15691, #15722) - Migrations `0306` to `0333` are the 28 `.sql` files added in the range. The three environment variables in the Upgrade Guide and their defaults match `server/src/services/authorization.ts` and `server/src/index.ts` at the source commit ## Risks - Low risk: a single markdown file, four lines. If the UTC day changes before the dispatch, the dispatch keeps `stable_date=2026-10-23` so the header and the tag agree ## Model Used - Claude (Anthropic), model ID `claude-fable-5-1` (Claude Fable 5.1), extended thinking enabled, tool use via Claude Code ## 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: Paperclip <noreply@paperclip.ing> |
||
|
|
a3e8fa568a |
Reduce repeated work during plugin Runner admission (#17377)
## Thinking Path > - Paperclip manages agents and their execution authority. > - A plugin can host Runner tasks on a persistent execution host. > - Admission checks must observe current lifecycle state and credentials. > - The current runtime reader also resolves files, skills, and connections for each check. > - Some providers also repeat the same project authentication check during submit. > - This pull request adds a compact fresh reader and an explicit submit-time validation contract. > - Providers can remove repeated work while Core keeps lease and scope checks. ## Linked Issues or Issue Description **What existing behavior does this improve?** Plugin Runner startup before task submission. **Current behavior** One measured startup made 30 full runtime reads during bootstrap. These reads took 5.759 seconds. Each guard can resolve instructions, skills, and connection assignments even when it only needs current identity and lifecycle state. Providers that already enforce project authentication in submit must also perform a separate Core preflight. **Proposed behavior** Use a lifecycle-owned runtime identity reader for guards. Keep the full runtime reader for synchronization inputs. A trusted provider can declare `projectAuthCheckAtSubmit: true` in its prepared result. Core pins the exact configuration, provider, and project set on the lease. The provider must reject authentication conflicts before credential installation or task start. Providers that omit the flag retain the existing preflight. **Reason and benefit** Estimated savings with a paired consumer: 4–5 seconds from compact runtime checks, plus roughly 0.5–0.9 seconds from removing the duplicate authentication operation. These estimates overlap and are not a measured deployed result. Refs #17367 for the separate run-log HEAD optimization; this PR does not duplicate it. **Breaking changes** No migration. The SDK adds a host method and an optional prepared-result field. Consumers that use them require this Core host version. Older providers retain their existing flow. ## What Changed - Add `agents.getRuntimeIdentity` across SDK, RPC, host service, and test fixtures. - Reuse the existing company, plugin ownership, readiness, and capability checks. Read lifecycle state and protected instance secrets fresh on every call. - Skip instruction, organization, skill, and connection hydration in the compact reader. - Allow explicit submit-time authentication enforcement, with prepared scope pinned and checked again at dispatch. - Emit fixed preparation, project-auth, submit, and connection timing phases. ## Verification - 72 targeted SDK capability, plugin task-owner, and environment-task mediator tests passed. - 33 real embedded-Postgres lifecycle and secret tests passed, including pause, content revision, company isolation, plugin disablement, and secret rotation. - SDK and server TypeScript checks passed. Local Runner/eval TypeScript outputs were built to satisfy server type resolution. - Local Postgres required missing library aliases in ignored dependencies; no product source was changed for that repair. - Full repository suites are delegated to GitHub CI. No live rollout or speedup claim. ## Risks The submit-time flag delegates enforcement to a trusted provider. It is not proof that prepare already checked authentication. The provider must check before VM credential installation or task admission. Core still checks exact prepared scope, active lease, run identity, credential type, and dispatch fencing. Existing providers keep the separate check. Roll back a paired consumer before rolling back this host. ## Model Used OpenAI GPT-6 (Codex), with code execution and repository tools. Exact deployment build and context-window size are not exposed. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal ticket id - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation 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> |
||
|
|
3c4050e3ad |
fix(connections): keep Cloud refresh reauthorization diagnostics (#17362)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Managed connections (Gmail, Google Workspace, GitHub, Asana, Slack manager) get and refresh their OAuth tokens through the Paperclip Cloud connector broker. > - When a provider refuses a refresh grant (the user revoked it, it expired, it was already rotated, or it no longer matches the profile), the broker sent `502 PROVIDER_OPERATION_FAILED`. That is the same answer it sends for a provider outage. > - Paperclip then kept the grant `active`. Each health check, catalog refresh, or catalog read failed again with a 502, and the server reported each failure as a crash. The user did not get a reconnect prompt. > - The broker now sends `409 PROVIDER_REAUTHORIZATION_REQUIRED` for a dead grant. The connector already maps any 409 to `REAUTHORIZATION_REQUIRED`. Thus the grant becomes `needs_reauthorization` and the caller gets a 422 reconnect response. > - This pull request adds the new reason to the diagnostic allowlist, pins the producer contract, and tests the reconnect path from end to end. > - The benefit is that a dead managed grant asks the user to reconnect. It is not reported as a server crash. Real provider outages keep their 502 response. ## Linked Issues or Issue Description **What happened?** A managed Paperclip Cloud connection refresh failed with `Paperclip Cloud connector rejected the request (operation=refresh, status=502, reason=PROVIDER_OPERATION_FAILED)`. The connection health-check and catalog routes wrap that error as an HTTP 502. The error handler reports every status of 500 or more, so the failure went to error monitoring. The grant stayed `active`, so the same failure can occur again on each use. **Expected behavior** If the provider refuses the refresh grant, Paperclip marks the grant `needs_reauthorization` and shows the reconnect path. Only a real provider or Cloud outage gives a 502. **Steps to reproduce** 1. Connect a managed Gmail connection through Paperclip Cloud. 2. Revoke the app access in the Google account, or let the refresh grant expire. 3. Wait until the access token needs a refresh. Then run a connection health check. 4. Before the broker change, the health check returns 502 and the server reports it. The grant stays `active`. **Additional context** The broker-side classification ships separately in Paperclip Cloud. This pull request is safe before or after that change, because the connector already maps any broker 409 to `REAUTHORIZATION_REQUIRED`. Without this pull request, the 409 still works, but diagnostics show `reason=UNKNOWN_BROKER_ERROR`. ## What Changed - `server/src/services/paperclip-cloud-connector.ts`: Add `PROVIDER_REAUTHORIZATION_REQUIRED` to the allowlist of broker reasons that errors can keep. - `server/src/services/fixtures/cloud-connector-refresh-reauthorization.json`: Add the producer HTTP contract (status 409 and the exact JSON error body). - `server/src/services/paperclip-cloud-connector.test.ts`: Test that a refresh with this contract gives `REAUTHORIZATION_REQUIRED`, status 409, the retained reason, and no refresh-token text. - `server/src/__tests__/tool-access-service.test.ts`: Test that `refreshOAuthGrantCredentials` changes a reauthorization refresh failure into a 422 `oauth_reauthorization_required` response and sets the grant to `needs_reauthorization`. - `doc/connections/GMAIL.md`: Add a troubleshooting row that tells the two refresh reasons apart. ## Verification - `cd server && npx vitest run src/services/paperclip-cloud-connector.test.ts`: 48 passed. Without the allowlist change, the new test fails (`reason=UNKNOWN_BROKER_ERROR`). - `cd server && npx vitest run src/__tests__/tool-access-service.test.ts -t "managed grant for reconnection|orders managed reconnect"`: 2 passed, against the embedded test database. - I did not run the full `pnpm -r typecheck`, `pnpm test:run`, and `pnpm build` locally. The sandbox has a 4 GB memory limit, and the server `tsc` process was stopped because it used too much memory. CI runs these checks. ## Risks - Low risk. The status mapping does not change: a 409 already meant `REAUTHORIZATION_REQUIRED`. This change only keeps one more fixed reason code in the error message. - The reason allowlist accepts fixed protocol codes only. The broker message, request data, and tokens stay out of errors and logs. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, in Claude Code agent mode with tool use (shell, file edit, code search, and a sub-agent for the code-path trace). ## 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 Opus 5.5 <noreply@anthropic.com> |
||
|
|
eadc560712 |
fix(evals): retain usage during bounded gateway shutdown (#17371)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Product E2E tests use a local gateway to record model usage and reserve costs. > - Test cleanup can disconnect a background request before its provider sends final usage. > - Immediate abort then leaves the request cost unknown and its full reservation held. > - This pull request lets an admitted response finish within a short drain period. > - The benefit is better accounting without a new request or a retry. ## Linked Issues or Issue Description Refs #17365 and #17354. **What happened?** Two hosted cases passed, but cleanup canceled background requests before the gateway received their usage. Both requests kept their full holds. A local HTTP regression reproduces this loss with OpenAI and Anthropic responses. **Expected behavior** Retain final usage when an already-dispatched response finishes within a bounded cleanup period. Keep the client outcome canceled. Preserve the hold when final usage is still unknown. **Steps to reproduce** 1. Send a request through the eval gateway. 2. Disconnect the client before the provider returns final usage. 3. Close the gateway and let the provider response arrive. 4. Before this fix, the gateway aborts immediately and loses the final receipt. **Paperclip version or commit** Reproduced against master `b6e690044a821faab52d5466db36c4330c754a2d`. ## What Changed - Drain an already-dispatched response for at most five seconds after a disconnect. Keep the original provider timeout and campaign deadline. - Stop forwarding response data to a disconnected client. Stop admission as soon as shutdown begins. - Retain `clientDisconnected` on the private receipt and keep the outcome canceled, even when final usage arrives. - Make repeated shutdown calls share completion. - Add HTTP and SSE regression tests. Document the drain and its accounting limits. ## Verification - Red: five regression cases failed against the previous gateway implementation. Four further cases reproduced late cancellation during review. - `pnpm test:e2e:runner:unit`: 4,072 Vitest tests and 137 Node contract tests passed. Two existing Vitest tests were skipped. - `pnpm exec tsc --noEmit -p tests/runner-e2e/tsconfig.json`: passed. - `git diff --check`: passed. - Regressions cover both providers, an interrupted SSE stream, grace expiry, a shorter provider deadline, no dispatch during interrupted identity resolution, no new admission, repeated shutdown, client-only disconnects, and disconnects during each final receipt/ledger write. - Full repository typecheck, tests, and build passed in [CI run 40026846417](https://github.com/paperclipai/paperclip/actions/runs/40026846417) on `40ef13bd4d5df6b2ca0a9666fb485adf2a565c28`. Greptile and Paperclip reviews are both 5/5. No paid model test was run for this change. Live qualification remains open. ## Risks - Cleanup can take up to five more seconds. The admitted request can continue generating during that period, inside its existing reservation and deadlines. - An abort does not prove that the provider stopped billing. Missing usage keeps the unknown hold. - This change does not establish invoice settlement or rewrite historical receipts. It changes only the eval gateway. ## Model Used OpenAI Codex, GPT-6 family, with reasoning, code execution, and tool use. The host does not expose an exact model build identifier or context window size for 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> |
||
|
|
2e38649341 | perf(run-logs): skip durable log restore for a proven-new run (#17367) | ||
|
|
a967d479bd |
fix(e2e): retain verified onboarding artifact evidence (#17370)
Retain the bounded verified onboarding download and independent oracle in private normalized results before cleanup. Preserve the receipt when later readback or trace checks fail without marking the journey as passed. Refs #17262. Refs #17253. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
75fcb0b3fc |
fix(sentry): keep typed ACP provider access failures local (#17366)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The server sends unexpected run failures to Sentry through
`reportRunFailure`
> - A Claude ACP run can fail because the provider rejects the stored
credential ("Invalid API key"). The ACP agent then sends a typed
`access` terminal failure.
> - The Claude adapter already classifies this failure as
`claude_auth_required`. The run is blocked, and the responsible user
gets a reconnect request.
> - Sentry also records each of these runs as "ACP agent reported a
terminal access failure.". This duplicates a signal that the user
already gets, and it hides real defects.
> - This pull request adds one narrow exclusion for this proven provider
auth state, next to the existing quota and Codex capacity exclusions.
> - The benefit is less Sentry noise, and all other access-shaped
failures stay visible.
## Linked Issues or Issue Description
Refs #14886 (related: it routes ACP access failures in recovery, not in
Sentry reporting).
**What happened?**
Staging Sentry recorded a run failure "ACP agent reported a terminal
access failure." for 8 events. All events had
`agent_adapter=claude_local`, `error_code=claude_auth_required`, and a
typed provider access failure ("Invalid API key · Fix external API
key"). The failures stopped when the provider accepted the credential
again.
**Expected behavior**
A provider auth failure that the adapter types and classifies is a user
action state. The product already shows it to the user. It must not go
to Sentry in `unexpected` mode.
**Steps to reproduce**
1. Configure a `claude_local` agent with an ACP runtime and a credential
that the provider rejects.
2. Start a run.
3. The ACP agent ends the turn with `category: "access"`. The run fails
with `claude_auth_required`.
4. See a new Sentry run-failure event.
**Agent adapter(s) involved**
Claude Code (`claude_local`, ACP lane).
## What Changed
- `server/src/services/run-failure-report.ts`: add
`isAcpProviderAccessFailure`. It skips a report only when all of these
are true: the reporting mode is `unexpected`, the run status is
`failed`, the error code is `claude_auth_required` or
`acpx_auth_required`, `resultJson.terminalSessionFailure.category` is
`access`, the phase is not `setup`, and the run has no signal.
- `server/src/services/__tests__/run-failure-report.test.ts`: add
regression tests for the staging shape, eight near-miss shapes that must
still report, and the setup and `all` mode paths.
## Verification
- Red: without the predicate, the two new "keeps ... local" tests fail
(`2 failed | 266 passed`).
- Green: `cd server && pnpm exec vitest run
src/services/__tests__/run-failure-report.test.ts
src/services/__tests__/run-failure-diagnostics.test.ts` gives `383
passed`.
- `pnpm exec tsc --noEmit -p server` shows no errors in the changed
files.
## Risks
- Low risk. The change only removes Sentry events. It does not change
run status, recovery, or user notices.
- A real defect that the ACP agent wrongly types as `access` with a
Claude auth code is not reported in `unexpected` mode. Set
`PAPERCLIP_SENTRY_RUN_FAILURES=all` to see these events again.
- Codex and other ACP adapters do not map `access` to an auth code, so
their access failures still report.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), run through Claude Code
with tool use (shell, file edit, Sentry and GitHub connectors).
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Co-authored-by: Devin Foley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
canary/v2026.1023.0-canary.16
|
||
|
|
f73ace9f1a |
Clear stale native recovery state so closed work expires (#17359)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Native runs keep a finalization row, and tasks can keep recovery
actions while work is stuck
> - Git cache expiration and native harness backup expiration read these
rows to decide if closed work is still live
> - Commit paths never cleared
`native_run_finalizations.recovery_state`, so committed rows kept
`resuming_session` forever
> - Recovery actions on done or cancelled tasks also stayed active when
the close did not go through the issue routes
> - So workspaces and backups of tasks that closed weeks ago stayed live
and used disk space
> - This pull request clears the stale state at commit, backfills old
rows, resolves stale recovery actions on closed tasks, and lets the two
expiration jobs ignore settled state on closed trees
> - The benefit is that closed work expires on schedule, while live
leases, unsettled phases, unverified owners, and in-flight system
recovery still keep it
## Linked Issues or Issue Description
Refs #17248
**What happened?**
On one instance, Git cache expiration and native harness backup
expiration kept the workspaces and backups of tasks that were done for
more than 7 days. Three kinds of stale state caused this:
1. 51 `native_run_finalizations` rows had `phase = 'committed'` and
`recovery_state = 'resuming_session'`. The commit writes (status
decision commit, attention replay, result persistence, and the finalizer
claim) never cleared `recovery_state`. No reader uses `recovery_state`
on a row that has a result, except the expiration predicates.
2. Rows with `phase = 'terminal_failure'` and `recovery_state =
'blocked'` (or NULL) are permanent states. Nothing clears them, and
continuations are refused for closed tasks.
3. `issue_recovery_actions` with status `active` or `escalated` stayed
open after the source task became done or cancelled. Only the issue
routes cancel them at close. The periodic sweep
`reconcileActiveRecoveryActions` skipped every action unless
`wakePolicy.type` was a bounded owner policy or the owner was the board.
Agent-owned `resume_native_run` actions (which use `wakePolicy.kind`)
and system-owned actions were never resolved.
**Expected behavior**
When a task tree is closed for the configured time, its Git cache and
native backup expire. Only live state keeps them: a held finalization
lease, an unsettled finalization phase, an unverified native workspace
owner, or system recovery that is scheduled, running, or awaiting
evidence.
**Steps to reproduce**
1. Run a native task. Let the runner restart so that restart recovery
sets `recovery_state = 'resuming_session'` on the finalization row.
2. Let the resumed run commit its result and close the task as done.
3. Wait past the expiration window (or run the sweep with a later
clock).
4. See that the finalization row is still `committed` +
`resuming_session`, and that Git cache expiration and native backup
expiration skip the closed workspace.
**Paperclip version or commit**
`master` at
|
||
|
|
dfe7df0015 |
fix(native): expire settled native workspace sync folders (#17358)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Native runs sync their workspace with a remote sandbox through a
per-run folder under `<instance>/native-workspace-sync/<run>/`
> - The folder holds a descriptor, a manifest, and workspace and git
seed archives, and a seed can be more than a gigabyte
> - Only a few finalization commit paths delete the folder, so many runs
leave it on disk forever
> - Instance disks fill up with folders that no code will read again
> - This pull request adds a bounded, deadline-driven retention worker
that removes a folder only when no reader can still need it
> - The benefit is that instance disks stop growing with stale sync
folders, and recovery and export paths keep their files
## Linked Issues or Issue Description
**What happened?**
`cleanupNativeWorkspaceSync(runId)` is the only code that deletes
`native-workspace-sync/<run>/`. It runs only when finalization commits
through specific paths. Runs that end in `terminal_failure`, retained
recovery, reconstruction, superseded runs, commits through other paths,
and server crashes leave the folder forever. On one instance, 22 folders
older than 7 days held 6.8 GB. Single seed archives were up to 1.4 GB.
**Expected behavior**
A run's sync folder is removed after a retention window, once the run
and its finalization are settled and no recovery or export path can
still read it. The shared `generations` seed cache stays.
**Steps to reproduce**
1. Run a native agent with a remote sandbox and let the run end in
`terminal_failure` (or crash the server during finalization).
2. Wait more than 7 days.
3. Look in `<instance>/native-workspace-sync/`. The run's folder and its
seed archives are still there.
**Paperclip version or commit**
`master` at
|
||
|
|
188bb14ba0 |
fix: evict stale workspace seed generations instead of freezing (#17357)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Native runs in remote sandboxes upload the workspace as seed
archives, and a per-company cache keeps recent seeds so a later run with
the same workspace state can reuse them
> - The cache only ever added seeds; at its limits it stopped accepting
new ones and never removed old ones
> - On one instance it had filled to its 8 GiB cap with week-old seeds
and froze, so it held disk without helping new runs
> - This pull request expires seeds unused for 7 days and evicts the
least recently used seeds when a new one needs room
> - The benefit is a cache that stays useful within the same limits and
gives back space held by stale seeds
## Linked Issues or Issue Description
Bug report described in-PR (bug report template).
**What happened?**
On one instance, the company's seed cache
(`native-workspace-sync/generations/<company>/`) sat at its 8 GiB cap:
209 generations across 169 workspaces. 107 generations (8.2 GB) had not
been touched for 7 days or more. Because publishing stops at the limits,
new workspace states were no longer cached, while seeds for old states
and deleted workspaces stayed forever.
**Expected behavior**
Stale seeds are removed, and a new seed can replace the least recently
used ones when the cache is full.
**Steps to reproduce**
1. Run enough native sandbox runs across workspace states to reach a
seed cache limit.
2. New seeds are no longer published, and nothing is ever removed.
**Paperclip version or commit:** `master` at
|
||
|
|
b6e690044a |
feat(evals): allow reviewed bounded provisional accounting (#17365)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Product E2E campaigns include model, setup, storage and observability costs. > - Some billing windows close after the tests and reports finish. > - Requiring settled receipts for retained storage can block a good campaign for weeks. > - A reservation alone does not prove that a pending charge has an upper bound. > - This change permits provisional completion only with reviewed evidence for the exact cost configuration. > - Reports preserve unknown amounts and holds until later reconciliation. ## Linked Issues or Issue Description Refs: #17360, #17354 **What existing behavior does this improve?** The Product E2E scheduled accounting gate and its retained cost reports. **Current behavior** Every pending receipt fails finalization. A completed test can wait through the retained-storage billing window. The journal has dollar reservations but no explicit qualification of their bounds. **Proposed behavior** Permit provisional completion when all pending charges have reviewed full-lifecycle bounds, no requests remain in flight, no receipts conflict, and known costs plus held amounts fit the campaign limit. Keep all existing holds and receipt-finality rules. Missing evidence still fails. **Reason and benefit** An operator can distinguish delayed but bounded billing from unbounded or missing costs. The campaign can finish without claiming that its invoice is settled. **Breaking changes** The journal accepts optional bound metadata and an invalidation record. New reports add an optional bounded-pending count. Current readers retain old journals and reports without granting them bounds. Older code must not read new bound-bearing journals. The existing daily/weekly enable gates and budget amounts stay unchanged. **Additional context** This implements the operator-approved provisional-cost policy for the existing eval harness. It does not change product budgets or the agent-eval roadmap. A targeted GitHub search found no duplicate open PR. ## What Changed - Accounting owns a narrow evidence contract: a reviewed audit hash, exact configuration hash and full-lifecycle scope. - Gateway reservations retain that evidence before dispatch. Changed limits or rates fail before dispatch. Unexpected routing, model identity or excessive usage invalidates a bound durably. - Operating allowances can carry separately reviewed bounds for each cost component, including its full storage lifetime. No existing allowance becomes qualified by default. - Reports and Braintrust experiment metadata distinguish provisional accounting, complete receipts, blocked accounting and unavailable evidence. Public output omits private audit hashes. - Scheduled finalization allows qualified provisional costs and explicitly rejects spending overruns, including overruns with complete receipts. - Add regressions for legacy data, restarts, late reconciliation, conflicts, malformed evidence, routing uncertainty and exceeded limits. Document the evidence and migration boundaries. ## Verification - Full offline eval-support suite: 4,062 Vitest tests passed, two existing tests skipped; 137 Node tests passed. - `pnpm test:e2e:runner:typecheck`: passed. - `git diff --check`: passed. - Read the retained hosted qualification journal with the new policy. All six legacy pending charges remain unqualified. Known cost `$0.421245200`, held `$19.327680000`, and committed `$19.748925200` remain unchanged. The journal's SHA-256 is unchanged. The original failed workflow remains failed. - No paid models, network provider calls, receipt imports or reservation releases were used for this verification. - Rebased onto master `4d90634f74`; 142 targeted tests and typecheck passed. - Review reproduced a lost OpenAI input-total limit check when cache details were missing (1 failed, 47 passed). Fix `5fef9ba7c149951a51eae468614a4b89f0b3d6db` preserves unpriced provider usage bounds. Then 178 focused tests, full offline support and typecheck passed. - Current-head hosted CI passed: https://github.com/paperclipai/paperclip/actions/runs/40020746585 (attempt 2). Typecheck, build, all general/Runner/serialized tests and browser gates are green. - CI attempt 1 hit a PostgreSQL deadlock in unchanged chat-test cleanup; its skipped flag restoration caused a second assertion failure. One failed-jobs-only retry passed. Original failure evidence is preserved in attempt 1. No paid eval campaign was rerun. - Greptile reviewed exact head `5fef9ba7c149951a51eae468614a4b89f0b3d6db` at 5/5; its one finding is fixed and resolved. The optional Paperclip review is paused and supplied no assessment. ## Risks - Evidence hashes bind an audit to a configuration; they do not prove the audit is true. Operators must review real provider limits, all price tiers, full resource lifetimes and enforceable infrastructure limits before configuring bounds. - Provisional completion is not invoice settlement. Later billing can reveal overruns, which must be reconciled and inspected. The ledger cannot stop external billing. - Existing pilot journals, configured allowances and price cards have no new bound approval. This PR alone does not qualify or enable recurring paid work. - The gateway cannot qualify a canceled Braintrust request without route confirmation. It preserves that unknown hold. - No new dependency, secret, repository variable, workflow, schedule or cloud resource changes. ## Model Used OpenAI Codex, GPT-6 family. The exact deployment ID and context-window size are not exposed in this session. Used reasoning, repository tools and code execution. No paid evaluation model calls were made for this change. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
9377f6dab7 |
chore(lockfile): refresh pnpm-lock.yaml (#17363)
Auto-generated lockfile refresh after dependencies changed on master. This PR only updates pnpm-lock.yaml. Co-authored-by: lockfile-bot <lockfile-bot@users.noreply.github.com> |
||
|
|
4d90634f74 |
feat(connections): add 1Password service-account connector (#17249)
## Thinking Path > - Paperclip lets people manage AI agents and govern their access to outside services. > - Apps connections keep provider credentials in the instance vault and mediate every action through grants, policies, and audit. > - Agents currently have no Apps connection for reading or managing 1Password items with an operator-supplied service-account token. > - 1Password supports this through its official SDK; its documentation MCP server does not expose vault operations. > - This pull request adds a one-field service-account connection and registers eight reviewed SDK actions through the existing native-provider catalog. > - Operators can select vault permissions in 1Password and agent/action access in Paperclip without installing a CLI or handing durable credentials to agents. ## Linked Issues or Issue Description Related: #16144 adds 1Password as Paperclip's secret-storage backend. This PR adds the separate Apps/tool-access flow with a per-connection credential; it does not change secret providers or use a server-wide ambient token. **Subsystem affected** Apps catalog, native tool-provider registry, and shared connection setup. **Problem or motivation** People need to paste their own `OP_SERVICE_ACCOUNT_TOKEN`, find official service-account setup instructions, and authorize agents to use the selected vaults through ordinary Paperclip governance. **Proposed solution** Add `1password/service-account`, with a password field labeled **Paste your 1Password Service Account token** and a link to [1Password's creation instructions](https://www.1password.dev/service-accounts/get-started#create-a-service-account). Use the pinned official `@1password/sdk` 0.5.0 for vault/item listing, item and secret-reference reads, and item create/update/archive/delete. **Alternatives considered** A remote MCP card would not provide vault access. A local CLI or a third-party MCP package adds installation and credential surfaces that the official SDK avoids. ## What Changed - Added the source and generated catalog definition, permission review, official light/dark artwork, documentation, and capability-map references. - Added a provider-owned SDK bridge with strict schemas, account-domain validation, contract hashes, fixed errors, bounded output, and disposable workers with cancellation/deadlines. - Registered the provider through master's native-provider catalog; existing setup, health, discovery, gateway, policy, and audit owners remain in place. - Labeled the single password field **Paste your 1Password Service Account token**. The shared formatter preserves explicit credential prompts; ordinary key labels retain their existing presentation. - Added credential-ownership, gateway read/write, policy/revocation, worker, setup UI, and interactive Storybook coverage. ## Verification - Final review follow-up: added two deterministic worker-entry tests for nested exact-token redaction and oversized-output rejection. Removing each guard caused exactly its regression test to fail. The focused provider suite passed **15 tests**, and the official server typecheck passed after rebuilding stale workspace SDK artifacts. Current head `569cb7f9bd57db791fb13a6034ce69bf50878cb1` passed the full [CI run](https://github.com/paperclipai/paperclip/actions/runs/40016166260), including typecheck, build, unit/integration tests, and all eight browser shards. The required `ci / verify` and `ci / e2e` gates passed. [Greptile](https://github.com/paperclipai/paperclip/pull/17249#issuecomment-6353731805) and [Paperclip Review](https://github.com/paperclipai/paperclip/pull/17249#issuecomment-6372165895) each rated this exact head **5/5**, with no open findings. Paperclip Review completed with a neutral check because its scoped reviewer could not inspect CI; the full CI result was verified separately. Other neutral checks and intentionally skipped Storybook jobs contain no actionable findings. - CI recovery on the same commit: the first attempt lost six jobs. Five logged simultaneous runner shutdowns at 18:49 UTC on 2026-10-23; the sixth stopped logging before a test result and its root cause was not established. No assertion failure appeared in the retrieved logs. All six jobs and the aggregate gates passed one failed-job retry. [CI run](https://github.com/paperclipai/paperclip/actions/runs/40005754936). - Copy follow-up: the catalog, provider, and setup UI suites passed **275 tests**. The governed gateway lifecycle passed **3 tests** (one live-only test skipped), including create, update, archive, and delete. UI token gates and map checks passed. The branch was rebased onto `9c442cf48e` to resolve additive catalog conflicts while retaining the Vercel and Supabase entries. The existing live write proof below remains applicable; this follow-up changes presentation only. - Five affected suites, including the existing Daytona consumer, passed **290 tests** (one opt-in live test skipped in fixture mode). Full `pnpm -r typecheck` and `pnpm build` passed. The local broad-suite attempt could not complete because PostgreSQL semaphore exhaustion prevented a competing integration suite from starting; the complete affected-file retry passed after disposable-process cleanup. The prior commit `f208a8aff6dc28859cc3f343497e18d85f124579` passed the complete CI matrix. All CI build, typecheck, test, and browser gates also passed on the earlier commit `9e2f3d3140368a08da43b004b2e8d3a0f4c0fa4d`. The separate Paperclip Review reported a worker-result regression-test gap on that head; the follow-up above addresses it. Its review thread is resolved, and both fresh current-head reviews are clean as recorded above. - Token gates, module boundaries, brand validation, map checks, and Storybook catalog checks passed. - Direct SDK create/archive/delete and rejected-call coverage passed (13 provider tests); the Storybook Permissions/Test play completed successfully in Chromium. Server and UI typechecks passed after those fixes. Greptile passed prior commit `9e2f3d3140368a08da43b004b2e8d3a0f4c0fa4d` at **5/5**. Both reviewers also passed the worker-test follow-up at **5/5** as recorded above. - Live 1Password proof on 2026-10-22: connect, eight-action discovery, vault listing through an actual run-scoped gateway, refresh, reconnect preserving identity, another read, removal, and denial through the retained session passed. Credential absence was asserted in responses/audit; an exact-token scan of inspected logs and changed files passed. - Real browser: Connectors → 1Password → token entry → personal access limited to one disposable agent → completion → Permissions → Test `onepassword_list_vaults` succeeded. The disposable connection was removed afterward. - Compiled-worker smoke initially exposed inherited Node launch flags. Workers now use explicit empty `execArgv`; the compiled worker then authenticated successfully and listed one vault. - Live write proof on **2026-10-23**: the replacement Keychain token passed all eight actions through the actual run-scoped gateway for both personal and shared credentials. The test created, listed, read, updated, resolved a secret from, archived, and deleted two synthetic items. Both were absent from subsequent item lists. Refresh/reconnect, identity preservation, removal, retained-session denial, and credential absence passed. The live suite passed **3 tests** (one fixture-only test skipped) in 116.65 seconds. The expanded fixture suite passed **3 tests** (one live-only test skipped); the 13 provider tests and server typecheck passed. An exact-token scan passed over 64 source/log files. - Failure history: the original token denied creation on 2026-10-22 and created no item. The first 2026-10-23 attempt stopped before provider calls because host PostgreSQL semaphore limits prevented test-database startup. A temporary ten-connection test profile allowed the complete retry to pass without changing shared services or host limits. A new delete-result assertion also exposed an outdated fixture response (`success` instead of `deleted`); the fixture was corrected and the complete fixture suite passed. Official contract sources: [service accounts](https://www.1password.dev/service-accounts/get-started), [SDKs](https://www.1password.dev/sdks), [item management](https://www.1password.dev/sdks/manage-items), [official JavaScript SDK](https://github.com/1Password/onepassword-sdk-js). ## Risks - Authorized item/secret reads intentionally return secret values to the agent and ordinary tool-result history. Select trusted agents and use restricted service-account vaults; the connection token itself is redacted. - New connections retain existing Paperclip action defaults, including Allowed writes/destructive actions for selected agents. 1Password still enforces the token's permissions. - Service-account access cannot be changed in place. Create a replacement account and reconnect to change vault permissions; read-only keys can connect but cannot write. - SDK authentication happens per action in a disposable worker. Calls have a 30-second maximum; cancellation stops local work, while a mutation already accepted by 1Password can still finish. Writes are never automatically retried. - Additive dependency and catalog change; no database migration. Disable/remove the connection to withdraw access, or remove the provider from catalog visibility for rollback. ## Model Used OpenAI Codex, based on GPT-6, with code editing, shell/browser tools, and live provider verification. A more specific runtime model ID and context-window limit were not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
17f1e386e8 |
feat(evals): compare retained Anthropic billing exports (#17360)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Product E2E evaluations retain request costs and failed attempts in a journal. > - Provider exports can arrive later and contain costs at a wider time scope. > - Comparing those exports by hand makes repeated accounting work hard to audit. > - This change adds a read-only command for the observed Anthropic export format. > - It binds the comparison to the retained manifest, journal and exact export bytes. > - Operators can see the unmatched amount without assigning it to canceled requests or releasing their holds. ## Linked Issues or Issue Description Refs: #17009, #17354 **Problem or motivation** A hosted campaign retained eight complete model receipts and two canceled requests. A later provider export covered the campaign, but comparing its tokens, rounded costs and the journal required a one-off script. Repeated observations must not be counted twice or treated as request-level billing proof. **Proposed solution** Add a private comparison command for one standard Claude Sonnet 5 campaign and one UTC date. Validate the selected workspace, key label, model and pricing dimensions. Compare provider totals with one authoritative observation per request. Retain source hashes and signed differences. Keep provisional exports, partial receipts and missing model identities explicit. **Alternatives considered** Manual comparisons remain possible but are hard to repeat. Importing a day total as a request cost would invent attribution. This command leaves journal and budget mutations to their existing owners. **Roadmap alignment** This is a focused operational tool for the existing Product E2E accounting subsystem. It does not change the product budgeting or agent-eval roadmap features. A targeted search found no duplicate billing-comparison PR or issue. ## What Changed - Add the provider-specific CSV parser, scope checks, token pricing comparison and private CLI in the Product E2E accounting owner. - Report complete and partial receipt coverage, source hashes, rounded provider charges, separate non-model charges, and signed unmatched amounts. - Reject ambiguous scopes, unsupported price dimensions, duplicate rows, contradictory token observations, changed journals and incomplete input. - Write a new mode-0600 output without replacing existing files or changing the journal. - Document the supported export shape and the limits of comparison evidence. ## Verification - After rebasing onto master `e48f1867b3b45f74edca4645d640846bcafeee4c`, 104 focused and adjacent tests passed across comparison, ledger, provider pricing, operating receipts and gateway behavior. - `pnpm test:e2e:runner:typecheck`: passed after rebase. - `git diff --check origin/master...HEAD`: passed. - Ran the actual CLI on retained hosted-campaign CSV exports and journal. It reproduced the prior manual calculation: provider-token estimate `$0.428027200`, with `$0.006782000` unmatched to retained request cost receipts. No provider or network call was made. - The first retained-data run exposed an empty provider key-status field and canceled receipts without a returned model identity. Those cases are now covered by the fixtures; the rerun passed. No request identity or final charge was invented. - Regression tests first reproduced loss of token fields on unknown-cost receipts (2 failed, 13 passed). The repair retains each known field independently of cost and rejects conflicting equal-authority token values. - Full PR CI passed on `f74ef1c31391ee62b686fcf300601e9cf8e73a7e`, including typecheck, tests, build and browser suites. Greptile and Paperclip each reviewed that exact head at 5/5 with no remaining actionable findings. ## Risks - The initial supported schema is standard global Claude Sonnet 5 with context buckets at or below 200k, one campaign and one UTC date. Other provider shapes fail explicitly. - Matching workspace/key labels does not prove exclusive key use or a complete provider export. The output remains a comparison and cannot release a reservation. - Provider costs are rounded and can lag. The token estimate uses retained prices and is kept separate from provider charges and non-model costs. - Config and exports are private inputs. Output includes hashes instead of raw key labels. This file is not a public report projection. - No runtime dependency, schedule, credential, paid execution, journal format or admission rule changes. ## Model Used OpenAI Codex, GPT-6 family. The exact deployment ID and context-window size are not exposed in this session. Used reasoning, repository tools and code execution. No paid evaluation model calls were made for this change. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.1023.0-canary.15 |
||
|
|
e48f1867b3 |
fix(evals): correlate canceled gateway requests with their ledger (#17354)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Product E2E evaluations use a budget ledger and a local model gateway. > - The gateway writes each request reservation before it calls a provider. > - A client can cancel before the gateway returns response headers. > - Project-only logging context cannot then bind a late gateway log to its reservation. > - This change sends the reserved request identity in Braintrust log metadata before dispatch. > - Operators can use that identity to inspect late records without guessing from time or session order. ## Linked Issues or Issue Description Refs: #17009 **What happened?** A hosted evaluation passed its product checks but retained two canceled requests with no response headers. Later gateway logs had partial usage. Their time and session matched, but they had no ledger request identity. **Expected behavior** A gateway log must carry the ledger request ID even when the client receives no headers. A log match must not imply final billing. **Steps to reproduce** Run the gateway regression with a transport that receives a request and then cancels before response headers. Before this change, both requests have the same project-only parent header. ## What Changed - Add a small Braintrust context encoder owned by the eval gateway integration. It uses the existing SpanComponentsV4 project-log format and an allowlist of request, campaign, execution, attempt and phase metadata. - Save the exact context in the private request receipt before dispatch. Send it as the gateway parent header. - Test both provider routes, repeated identical requests, cancellation holds and direct-provider isolation. - Document the lookup field and the limits of historical and partial evidence. ## Verification - The new cancellation regressions failed before the change: 2 failed, 37 passed. - Gateway, operations and tracing checks: 82 passed. - `pnpm test:e2e:runner:typecheck`: passed after rebuilding stale generated plugin-SDK types from current master. The initial stale-type failure remains recorded locally. - Official Braintrust SDK 3.38.0 decoded the actual generated context. Its `startSpan` implementation emitted the reserved identity in the initial LLM log. The check captured logs in memory and forbade all network calls. - `git diff --check`: passed. - Full repository CI passed on `529446a9295f2dc6fa6499a2f976480d74626097`, including typecheck, tests, build and both required gates. Greptile is 5/5 on that head; its sole finding is fixed and resolved. - The optional Paperclip review turn ended without a final response. Its check remains queued and provides no review assessment. No passing review is inferred from it. - Live hosted acceptance of this header change remains unqualified; no paid test was dispatched for this fix. ## Risks - The encoder follows Braintrust's versioned span format. A future vendor change can require an update. It adds no package dependency. - A late log can have partial usage. Existing reservations remain held until valid cost evidence resolves them. - Historical project-only requests cannot gain the new identity retroactively. - Only synthetic eval identifiers enter the new metadata. Credentials, prompts and responses are excluded. Direct provider routes keep their current headers. ## Model Used OpenAI Codex, GPT-6 family. The exact deployment ID and context-window size are not exposed in this session. Used reasoning, repository tools and code execution. No paid evaluation model calls were made for this change. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
ef9e17304c |
perf: narrow runtime progress database reads (#17355)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Active runs publish workspace and provider progress. > - Each update must check the current run status before it publishes. > - This check fetched the full run, including prompts and runner configuration. > - This pull request reads only the four fields that progress publication uses. > - The benefit is less database transfer while each update still checks durable state. ## Linked Issues or Issue Description Related: #17348 narrows authorization reads. This PR applies a separate projection to runtime progress. GitHub searches for runtime progress and run-status read PRs found no duplicate. **What existing behavior does this improve?** The database read before a runtime progress update. **Current behavior** Each update calls `getRun`. This reads large run context and runner configuration even though progress publication only uses the run ID, company ID, agent ID, and status. **Proposed behavior** Use a fresh four-column read for each update. Keep the existing active-status check, event fields, error propagation, and late-progress suppression. **Reason and benefit** Transfer progress can emit many updates for one run. A small status check avoids repeatedly transferring payloads that it does not use. **Breaking changes** None. No API response, schema, scheduling, or cancellation policy changes. ## What Changed - Add `getRunStatus` to the run-state owner. The full reader remains available to consumers that need execution data. - Wire the narrow reader into the lifecycle progress check. Remove the unused context field from that check's input contract. - Cover the exact selected fields, missing runs, database errors, large stored payloads, terminal transitions, and the public heartbeat service wiring. Document the read boundary. ## Verification - Run-state and lifecycle suites: 44 tests passed before the additional public-service regression. - Lifecycle, public-service wiring, and native cancellation suites: 36 tests passed. - `pnpm -r typecheck`: passed. - `pnpm build`: passed. - The [full PR workflow](https://github.com/paperclipai/paperclip/actions/runs/40013092548) passed on `95bf496f689049812646b5ccddcdf2cca21efad0`. All 54 reported checks passed; two conditional checks were skipped. This includes test shards, browser checks, Runner verification, production bundle boot, and clean npm installation. - `GIT_CONFIG_COUNT=1 GIT_CONFIG_KEY_0=init.defaultBranch GIT_CONFIG_VALUE_0=master pnpm test:run`: an additional local all-in-one run is still in progress; no full-local-pass claim. The process-scoped Git default matches existing master-only fixtures and does not change user configuration. - The initial description check failed because the dedup-search checkbox was missing. Searches are complete and the template checkbox is now included. The rerun passed on the same source commit. - Greptile Apex: 5/5 on `95bf496f689049812646b5ccddcdf2cca21efad0`, no findings or review threads. - No hosted deployment or bill reduction is claimed. CI and Apex are complete; this PR is merge-ready. ## Risks - Low scope: the query selects fewer columns and preserves the existing status read on every progress callback. It adds no worker, cache, timer, or idle query. - A run can still finish after the status read and before publication, as it could before this change. This PR does not change that concurrency boundary. - No migration or operator configuration is required. ## Model Used OpenAI Codex (GPT-6 family). The runtime does not expose the exact model ID or context window. ## 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 (focused suites above; full suite is still running) - [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> |
||
|
|
837add0caa |
fix(recovery): deliver review-path repair instructions to agent prompts (#17353)
## Thinking Path > - Paperclip keeps agent tasks moving through owned execution and durable waiting paths. > - A consumed review path gets one repair wake before board escalation. > - That wake records an instruction to save a new monitor or another real review path. > - The canonical wake builder omitted that instruction from the agent prompt. > - This pull request delivers it through the existing disposition-repair section. > - The repair limit and execution authority stay unchanged. ## Linked Issues or Issue Description **What happened?** An agent opened a pull request and promised to watch CI and external review. It ended without scheduling a monitor. Its bounded review repair also ended without a maintained path, so the task became blocked for a board decision. The saved repair context contained the instruction, but the canonical wake payload and rendered prompt did not. **Expected behavior** The repair prompt tells the agent to save a one-shot monitor before promising another check. Both fresh and resumed sessions receive that instruction and the one-attempt limit. **Steps to reproduce** 1. Create a review-path recovery with `decideIssueReviewPathRecovery` for a task whose review path was consumed. 2. Pass its recorded context through `buildPaperclipWakePayload` and wake serialization. 3. Render a fresh or resumed prompt. Before this fix, it has the wake reason but no repair instruction. **Paperclip version or commit** Base: `9c442cf48e6`. Current head: `f38d65891c8d9f2b38eb76a8578e870debc2b1e6`. **Deployment mode** Observed on managed sandbox execution with the legacy Claude ACP adapter. The corrected canonical payload serves the shared adapter prompt path. Refs #15764 introduced the bounded exhausted-repair disposition. This change preserves that limit. Open monitor-retry work such as #17171 addresses an already scheduled check; this defect occurs before a check exists. Searches found no duplicate for this missing prompt field. ## What Changed - Build the review-path repair disposition in its owning recovery module, using the server instruction and fixed one-attempt bound. - Project it into the existing canonical `dispositionRepair` field. No new adapter field or execution protocol is added. - Ignore arbitrary replacement instructions and attempt-limit values. A pending repair keeps its instruction across a connection-pool retry that changes the wake reason. Ordinary wakes without the typed repair marker ignore raw instruction text. Existing legacy disposition repairs retain their values. - Add a regression across the real recovery decision, wake builder, serializer and fresh/resumed prompt selectors. Update execution guidance and the affected recovery maps. ## Verification - Verified red before the source fix: two regression cases failed because the instruction was absent; the sibling repair case passed. The same cases pass after the fix. - Passed 194 focused and safety tests across wake preparation, review recovery, adapter prompt handling, and database-backed liveness escalation. The connection-pool retry case also failed before its correction and passes after it. - Replayed the recorded incident wake through the fixed boundary. The old canonical prompt lacked its saved instruction. The corrected serialized prompt includes it with a 1/1 bound. This replay did not execute a provider turn or change the source task. Private evidence stays outside the repository. - Passed `pnpm -r typecheck` after the pool-retry correction. Passed `pnpm build` on final head `f38d65891c8d9f2b38eb76a8578e870debc2b1e6`. Map checks and `git diff --check` pass. - [Full CI](https://github.com/paperclipai/paperclip/actions/runs/40005890857) passed on that head: 54 checks passed and two optional checks were skipped. This includes the full test shards, typecheck, build, and browser suites. The local `pnpm test:run` attempt was stopped after the CI test shards passed. No complete local full-suite pass is claimed. - [Apex review](https://github.com/paperclipai/paperclip/pull/17353#issuecomment-6372160694) is 5/5 on the final head, with no open findings. The first review found the pool-retry case. Its regression and correction are included. The branch has no merge conflicts. - Test setup first required the plugin SDK build, then an empty credential-history database boundary. Those setup errors are not the claimed red. The temporary recorded-wake probe was removed before final validation. - Hosted follow-up acceptance is not run. The source task remains blocked and needs a retry after rollout. This PR does not claim that prompt delivery itself schedules a monitor or that every model will follow the instruction. ## Risks - This fixes instruction delivery. The agent must still save a valid monitor or another waiting path. The existing bounded escalation remains if it does not. - The wake stays a normal-model repair. It does not become status-only recovery and can continue useful work. Existing ownership, approval, budget, pause, and monitor admission gates remain in place. - No schema, credential, telemetry, API, model, or retry-budget change. ## Model Used OpenAI Codex, based on GPT-6, assisted with diagnosis, code editing, shell execution and verification. The exact serving model ID and context-window limit 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 (194 focused and safety cases; full coverage 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>canary/v2026.1023.0-canary.14 |
||
|
|
73526559b5 |
fix(ui): reload once when a lazy chunk or its CSS fails to load (#17349)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board UI loads each page as a lazy chunk. Vite adds a preload
step that also loads the CSS of the chunk.
> - Staging error monitoring reported `Unable to preload CSS for
…/assets/issue-detail-CIbQo9zW.css`. The same file name comes from the
current build, so the CSS did not change. One request for the file
failed for a short time.
> - Vite rejects only the first import that asks for a failed file.
Later imports skip that file. On a task page, the first import is the
task-page prefetch, which ignores errors. The page then shows without
its CSS. On other pages that share the CSS, such as Agents, the lazy
route rejects and the route error boundary shows the error.
> - Neither case recovers without a manual reload. This is a routine
retry that the app can do safely, so the app must do it.
> - This pull request reloads the page one time when a chunk or its CSS
fails to load. A loop guard limits this to one reload in five minutes.
> - The benefit is that a stale tab after a deploy, or one failed asset
request, recovers without an error page, without missing styles, and
without error-monitoring noise. An asset that stays broken still shows
the error and still sends a report.
## Linked Issues or Issue Description
**What happened?**
A board page showed "This page hit an error" with `Unable to preload CSS
for <origin>/assets/issue-detail-<hash>.css`. On a task page, the same
failure gave no error message, but the page showed without its
stylesheet.
**Steps to reproduce**
1. Build the UI and start the server.
2. Make the first request for `/assets/issue-detail-*.css` fail. The new
Playwright spec does this with `page.route(...).abort("failed")`.
3. Open `/<prefix>/agents`. The route error boundary shows "Unable to
preload CSS".
4. Open `/<prefix>/issues/<id>`. The task shows, but the browser does
not request the task CSS again. The page has no task-page CSS.
**Expected behavior**
The app recovers from a chunk or CSS load failure without user action.
If the failure continues, the app shows the error one time, with no
reload loop.
**Environment**
Hosted staging browser. The local build of current `master` reproduces
it.
I searched open and closed PRs for `preload`, `preloadError`, and `stale
chunk`. I found no related PR.
## What Changed
- `ui/src/lib/chunk-load-recovery.ts` (new): `isChunkLoadError` matches
the Vite CSS preload error and the dynamic import errors from Chromium,
Firefox, and WebKit. `reloadForChunkLoadError` reloads one time. A
`sessionStorage` marker blocks a second reload for five minutes. After
the first reload, later chunk errors in the same document are part of
that reload, so they return "reloading" and do not show an error.
Without `sessionStorage`, the function does not reload, because it
cannot guard against a loop. `startChunkLoadRecovery` listens for
`vite:preloadError`. It does not cancel the event, so the import still
rejects as before.
- `ui/src/main.tsx`: Starts the listener before the first render. The
listener covers every import, including the task-page prefetch that
ignores errors.
- `RouteErrorBoundary` and `AppErrorBoundary`: While a recovery reload
runs, they show a loading state. They do not show the error page and do
not report to Sentry. Other errors work as before.
- Tests: Vitest unit tests for the helper and both boundaries. A new
Playwright spec, `tests/e2e/chunk-load-recovery.spec.ts`, runs against
the real server and the built UI.
## Verification
- `pnpm --filter @paperclipai/ui exec vitest run
src/lib/chunk-load-recovery.test.ts
src/components/RouteErrorBoundary.test.tsx
src/components/AppErrorBoundary.test.tsx`: 21 tests pass.
- Full UI Vitest suite: 793 files, 9,075 tests pass.
- `pnpm --filter @paperclipai/ui typecheck` passes. `node
scripts/check-token-gates.mjs` is clean. `node scripts/check-maps.mjs`
passes.
- `npx playwright test --config tests/e2e/playwright.config.ts
chunk-load-recovery.spec.ts`: 3 tests pass.
- Task page with one failed CSS request: one reload, the CSS loads, and
the task shows.
- Agents page with one failed CSS request: one reload, the CSS loads,
and the page shows with no error page.
- CSS that always fails: one reload, then the error page shows. There is
no second reload.
- Red proof: I ran the same spec with the `ui/src` changes removed
(current `master` UI code). Both recovery tests fail. On Agents, the
route error boundary shows `Unable to preload CSS for
http://127.0.0.1:3199/assets/issue-detail-CIbQo9zW.css`. On the task
page, the CSS is never requested again.
- A failure I found and fixed during the work: my first version changed
only the error boundaries. The task-page e2e case failed with that
version, because the prefetch received the error and the boundary never
saw it. The `vite:preloadError` listener fixes that case. All results
above are from the final commit.
- The spec blocks service workers, because Playwright page routes cannot
see requests that a service worker sends. With a worker, a failed
network request for an asset that is not cached also gives
`Response.error()`. Thus the preload error is the same.
## Risks
- A page can reload while a user is on it. This occurs only after a
chunk or CSS load failure, and at most one time in five minutes per tab
session. Without this change, that page already shows an error or shows
without styles. Composer drafts use browser storage, so a reload keeps
them.
- A failure that recovers no longer sends a Sentry event. A failure that
continues after the reload still sends one, so a broken deploy stays
visible.
- The error patterns match browser and Vite message prefixes. If a
browser changes its message, that browser goes back to the current
behavior: the error page and a report.
- No API, schema, or server changes.
## Model Used
- Claude Opus 5.5 (`claude-opus-5-5`) by Anthropic, run as a Paperclip
agent through Claude Code with tool use (shell, file edits, Playwright,
and Vitest).
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
|
||
|
|
9c442cf48e |
feat(connections): deploy and configure Supabase and Vercel (#17217)
Add governed Supabase and Vercel deployment/configuration connectors with vaulted credentials, agent access policies, approvals, audit, and bounded provider operations. Deliver reviewed REST actions to native agents and guard Vercel preview intent on empty projects. Preserve secret-safe request identity and durable retry behavior. Verified by current-head CI, fresh Greptile 5/5 review, and real-agent deployment/configuration walkthroughs on disposable provider resources. Test resources and temporary provider tokens were removed after verification. Co-Authored-By: Paperclip <noreply@paperclip.ing>canary/v2026.1023.0-canary.13 |
||
|
|
59d1b7b43d |
fix(evals): collect usage when GitHub skips jobs without a runner (#17352)
## Thinking Path > - Paperclip manages AI agents and their operating budgets. > - Product E2E runs retain accounting for each campaign. > - The usage collector reads completed GitHub jobs without rerunning paid work. > - GitHub returns a null runner ID for jobs that it skips before assigning a runner. > - The collector rejected those jobs and could not retain the completed campaign inventory. > - This change accepts that skipped-job representation while preserving started-job checks. ## Linked Issues or Issue Description Refs #17009. **What happened?** The usage collector rejected completed qualification run 39971835384. GitHub returned skipped jobs with null runner IDs. The collector accepted only zero for jobs without a runner. **Expected behavior** Retain completed run usage when skipped jobs have a null or zero runner ID. Do not count VM time for those jobs. Continue to reject missing runner identity for started jobs. **Steps to reproduce** Read the completed first-attempt Product E2E run with operating-receipts.ts github. Before this fix, a skipped job with runner_id set to null throws Invalid workflow job identity. **Paperclip version or commit** |
||
|
|
f406b32d08 |
perf: narrow run authorization reads (#17348)
## Thinking Path > - Paperclip lets people manage and observe AI agents at work. > - Live events and run history must enforce current task permissions. > - Some permission checks load the complete run record for each event or request. > - Run context, runner configuration, results, and output can make those records large. > - This pull request reads only the persisted fields each access check needs. > - The checks still read current permissions and fail closed. ## Linked Issues or Issue Description Related: #17251, #9155, and #16229. Those changes cover other run projections, recovery polls, and task history. This change covers live-event delivery, workspace-operation run checks, and event-list access. **What happened?** Live-event delivery loaded every run column for each subscribed viewer, although the permission helper used only the company, run scope, and task binding. Workspace-operation checks did the same. The run-event endpoint also loaded complete run records before returning separate event rows. Large contexts and runner configurations therefore increased database transfer for ordinary access checks. **Expected behavior** Read only the stored access binding. Keep company checks, permission freshness, deleted-task tombstones, and response bodies unchanged. **Steps to reproduce** 1. Create an issue-bound run with large context, runner configuration, and output fields. 2. Subscribe an authorized viewer to company live events and publish run updates. 3. Read the run events and a linked workspace operation. 4. Inspect the run SELECT projections. The previous queries load fields that the access checks do not use. **Paperclip version or commit** The defect is present on the current master base, `62c78e56e53`. **Deployment mode** Authenticated server with PostgreSQL. The defect is not adapter-specific. ## What Changed - Select only company, scope, and issue bindings for live-event and workspace-operation run authorization. - Add a four-field run-access read for event-list requests. Reuse those columns in the existing log-access projection. - Preserve per-delivery and per-request permission checks, company predicates, and event responses. - Add real PostgreSQL and WebSocket coverage with large run fixtures. Cover grant revocation, grant restoration, deleted tasks, missing and foreign-company runs, and failed reads. - Document the bounded access reads in the existing polling-load guide. ## Verification - Six focused suites pass: 40 tests. They cover the changed paths, run-state reads, run logs, disconnect retries, and native event replay. - The new WebSocket test uses the production delivery path and a real database. It verifies that the selected SQL omits large run fields while authorization changes take effect on the same open socket. - The event-route test preserves the returned event content and denies the next request after grant revocation. - Local recursive typecheck, full build, and module-boundary checks pass. [Exact-head CI](https://github.com/paperclipai/paperclip/actions/runs/39997370119) completed successfully, covering the full grouped unit suites, typecheck, build/runtime boot, and E2E shards. The separate serial local `pnpm test:run` is still running and is not claimed as completed. - A later CI shard failed an unchanged email test expecting exactly one task after permission grants. The unchanged-base test passes at normal speed; adding only a 2.2-second pause before granting access reproduces the extra tasks because previously denied deliveries become due for retry. No email code or assertions are changed in this PR. The original failure is retained. The single shard rerun passed: 92 suites and 1,489 tests passed, with one suite and two tests skipped by the existing gates. - CI initially found a route-test double missing the new access helper. The follow-up fixes that fixture and adds event reads to malformed-ID and missing/cross-company checks. All 122 tests across the four changed-path suites pass; a final rerun of the expanded route suite passes all 92 tests. - The first CI run also timed out in the signoff changes-requested handoff while waiting for a reviewer run after a cancelled execution blocker. The focused test passes locally on this source. The same focused test passes three times on unchanged master. Controller-lease cancellation messages appear on both versions. The original CI timeout is preserved as an intermittent, unreproduced failure; its exact cause remains unknown. The signoff shard passes in the final exact-head CI run. - Initial fresh-checkout tests could not start because workspace SDK output and native PostgreSQL library links were missing. Dependency builds and the package's native-link setup corrected those prerequisites. The subsequent focused run passed. Focused command: ```sh pnpm --filter @paperclipai/server exec vitest run src/__tests__/live-events-run-read.test.ts src/__tests__/heartbeat-run-privacy-routes.test.ts src/services/heartbeat/run-state.test.ts src/__tests__/native-observer-replay.test.ts src/__tests__/heartbeat-run-log.test.ts src/__tests__/heartbeat-run-privacy-disconnect-retry.test.ts ``` ## Risks - A missing access field could cause an incorrect allow or denial. Tests exercise current grants, revocation, company scope, and deleted-task bindings through the real consumers. - No successful permission decision is cached. Every delivery and request still checks current database state. - No schema, migration, polling interval, API response, or deployment configuration changes are included. - This reduces bytes for the affected reads. It does not establish a whole-account savings percentage. Hosted transfer measurements remain separate validation after staging deployment. ## Model Used OpenAI Codex, a GPT-6 based coding agent, used repository tools, code execution, and database-backed tests. The session does not expose the exact backend model identifier or context-window size; those values are not guessed. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.1023.0-canary.12 |
||
|
|
62c78e56e5 |
fix: describe unexpected git expiration failures in the result (#17341)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Git cache expiration keeps a workspace and reports
`inspection_failed` when an unexpected error interrupts its checks
> - It dropped that error, so the log never said what went wrong
> - This pull request adds a sanitized description of the error to the
expiration result, which the workspace reaper already logs
> - The benefit is that unexpected expiration failures can be diagnosed
without exposing commands or credentials
## Linked Issues or Issue Description
Refs #17248. Bug report described in-PR (bug report template).
**What happened?**
On one instance, a closed workspace was kept with `inspection_failed`.
The log entry carried only that reason, so the cause could not be
determined from the logs.
**Expected behavior**
An unexpected failure is logged with enough detail to diagnose it,
without leaking the command line or credentials.
**Steps to reproduce**
1. Break a closed issue's worktree so a git command fails, for example
by pointing its `.git` file at a missing directory.
2. Run expiration: the log says `inspection_failed` and nothing else.
**Paperclip version or commit:** `master` at
|
||
|
|
0001a4514e |
fix: keep stale pending restore points from blocking retries (#17344)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Git cache expiration saves restore points and tracks them in
`execution_workspace_restore_points`
> - A unique index allows one row per workspace, generation and checkout
unless the row is deleted
> - A pending row left by an interrupted attempt therefore blocked every
retry for an hour, and superseded rows blocked retries until retention
deleted their objects
> - This pull request narrows the index to live rows and supersedes
stale pending rows before inserting
> - The benefit is that an interrupted expiration no longer stalls a
workspace's cleanup
## Linked Issues or Issue Description
Refs #17257. Bug report described in-PR (bug report template).
**What happened?**
On one instance, a closed workspace with a nested checkout failed
expiration with `inspection_failed` on every sweep. An earlier attempt
had left a `pending` row for the nested checkout at the workspace's next
generation. Each retry stored the main worktree's restore point, then
failed to insert the nested row because the unique index on
`(execution_workspace_id, generation, checkout_name) WHERE status <>
'deleted'` still held the stale row. Running the expiration by hand
reproduced the failing insert.
**Expected behavior**
A retry after an interrupted attempt replaces the stale row and expires
the workspace.
**Steps to reproduce**
1. Leave a `pending` restore point row for a workspace's next generation
and a nested checkout, as an interrupted attempt does.
2. Run expiration with restore points: it fails with `inspection_failed`
until retention supersedes the row after the one-hour pending grace
period.
**Paperclip version or commit:** `master` at
|
||
|
|
23bd238f05 |
fix(costs): stop OAuth refreshes from duplicating unverified subscriptions (#16512)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Costs page shows one card for each AI subscription account. Each
card comes from an `ai_subscriptions` row.
> - Paperclip cannot verify an xAI/Grok account with the provider. So
each Grok row used a key made from a hash of the raw credential.
> - The Grok CLI refreshes its OAuth tokens during runs. Paperclip
writes the refreshed `auth.json` back to the vault and keeps the
secret's `aiSessionEpoch`.
> - The next run sees a new credential hash.
`subscriptionService.register()` finds no match and adds a new
subscription row. The old row stays on the Costs page. One Grok
connection becomes many duplicate cards.
> - Claude and OpenAI rows usually avoid this problem, because a
provider profile check merges them. Grok rows never get verified, so
nothing merges them.
> - This pull request keys unverified rows on the credential session
(secret id + `aiSessionEpoch`), not on the token bytes.
> - The benefit is one subscription card per Grok connection through
token refreshes. A real reconnect still gets its own card.
## Linked Issues or Issue Description
No public issue exists. Bug description:
**What happened?**
On a hosted staging instance, the Costs → Subscriptions list showed many
duplicate Grok subscriptions for one Grok connection. Each Grok OAuth
token refresh added one more card.
**Expected behavior**
One Grok connection shows one subscription card. A token refresh does
not change it.
**Steps to reproduce**
1. Connect an xAI/Grok subscription account.
2. Run an agent until the Grok CLI refreshes its OAuth token. Paperclip
writes the refreshed credential back to the vault.
3. Run the agent again.
4. Open Costs → Subscriptions. A second Grok card is there. Each refresh
adds another card.
**Agent adapter(s) involved**
Not adapter-specific (core bug). The trigger is the Grok OAuth refresh
write-back in `server/src/services/ai-connection-runtime.ts`.
**Paperclip version or commit**
`master` at
|
||
|
|
93d26eb84e |
feat(ui): link routine-created tasks back to their source routine (#16471)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Routines create tasks on a schedule. Each task records its source in `originKind = "routine_execution"` and `originId = <routine id>`. > - The issue Properties card shows Relationships such as Parent. It does not show the routine that created the task. > - An operator who reads a routine-created task cannot go to the routine from the task. They must find the routine by hand. > - This pull request adds a "Routine" row under Relationships, below Parent. The row links to the source routine. > - The benefit is a direct path from a routine-created task to the routine that made it. ## Linked Issues or Issue Description Refs #9727. That PR adds a general provenance model and an "Origin" chip. It also links routine executions to the routine. It is much larger (DB migration, server, plugin SDK). It has merge conflicts and no updates since August. This PR is a small UI-only change with no schema or API change. If #9727 merges later, the two rows can be merged into one. **Is your feature request related to a problem?** A task that a routine creates has no visible link back to that routine in the Properties card. **Describe the solution you'd like** Show a "Routine" row under Relationships for routine-created tasks. The row shows the routine title and links to `/routines/:id`. **Describe alternatives you've considered** A generic "Origin" row for all origin kinds (the #9727 approach). That needs new server data for most kinds. A routine link needs only data that the issue already has. ## What Changed - Add `ui/src/components/issue-properties/routine-origin-row.tsx` (`RoutineOriginRow`). It reads the routine with the same `queryKeys.routines.detail` query as the routine page, so the cache is shared. - Show the row in `IssueProperties.tsx` after Parent, only when `originKind === "routine_execution"` and `originId` is set (+5 lines). The new component is in its own file because `IssueProperties.tsx` already has more than 3,000 lines. - States: - Routine found: an outline chip with the repeat icon and the routine title. The chip links to the routine page. - Routine still loading, routine deleted (404), or any other lookup error: no row. The card never shows an empty Routine field. - Task not created by a routine: no row. No routine request is sent. - Add 4 tests in `IssueProperties.test.tsx`: routine found, routine loading, routine deleted, and task not created by a routine. ## Verification - `pnpm --filter @paperclipai/ui exec vitest run src/components/IssueProperties.test.tsx`: 94/94 pass. - `vitest run src/pages/IssueDetail.test.tsx src/components/TaskSidePanel.test.tsx` (other surfaces that render the panel): 195/195 pass. - `node scripts/check-token-gates.mjs`: all gates clean. - `tsc --noEmit` in `ui`: no errors in the changed files. Other errors came from workspace packages that were not built locally (`@paperclipai/plugin-sdk/ui`, generated storybook catalog JSON). This change does not touch them. - Manual check: open a task that a routine created. Find the "Routine" row under Parent. Click it. The routine page opens. - Not run locally: full `pnpm -r typecheck`, `pnpm test:run`, `pnpm build`, and a browser check. CI runs these. ## Risks - Low risk. The change is UI-only and additive. It has no schema, API, or contract change. - Each routine-created task view sends one extra `GET /routines/:id` request. The routine page uses the same cache key, so the response is often cached already. - The row appears only after the routine request finishes. On a cold cache, it shows up a moment after the rest of the card. - If the routine is deleted or the user cannot read it, the task shows no Routine row. The task still records `originKind`/`originId`. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - Anthropic Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with tool use (shell, file edit, test execution). Standard context window. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Devin Foley <139239+devinfoley@users.noreply.github.com> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>canary/v2026.1023.0-canary.11 |
||
|
|
a708a13336 |
Separate plugin workspace completion from maintenance inspection (#17329)
## Thinking Path > - Paperclip manages AI agents and their work. > - Storage plugins own remote task workspaces. > - Run completion currently requests the same inspection used for storage cleanup. > - That inspection can fetch remote refs and scan files after the agent has finished. > - Completion needs retained workspace identity and durable writes, not permission to delete files. > - This change adds an optional completion operation and keeps maintenance inspection separate. ## Linked Issues or Issue Description **What happened?** A successful plugin-backed run can fail during completion because the storage provider performs a slow remote-recoverability inspection. Dirty or unpublished output does not need a deletion-safety proof to remain valid task output. **Expected behavior** Completion verifies the assigned workspace and confirms durable writes through the provider. Cleanup still requires the existing full inspection. **Steps to reproduce** Run a task with plugin-owned storage whose inspection fetches remote refs. Let the agent finish, then observe completion waiting for the maintenance inspection. Related context: #16428 added finalization observability. The duplicate search found no existing PR for this provider contract. ## What Changed - Add optional `workspaceProvider.finalizationVersion: 1` and strict `finalize` / `finalized` schemas. - Pass original run and agent context through live completion and recovered completion. - Give the new operation a 30-second deadline and keep ownership checks and lifecycle serialization. - Retain the inspection fallback for providers without the capability. Keep maintenance inspection and removal unchanged. ## Verification - SDK build passed. - Targeted SDK and completion unit tests: 7 passed. - Added integration coverage for capability selection, legacy fallback, maintenance separation, and failure propagation. - Local integration setup timed out starting PostgreSQL; the integration suite passed in GitHub CI. - Full build/typecheck and broader tests passed in GitHub CI (55 successful checks, 2 skipped). Local checks remained targeted as requested. - Diff reviewed for secrets and operational identifiers. ## Risks The capability is opt-in. Providers must not advertise it until their completion implementation is available. An unavailable provider or failed durability barrier remains retryable. No schema or stored-binding migration is required. Test-file size exception: the provider integration suite grows from 973 to 1,002 lines. The 29-line case follows the same prepared binding through legacy completion, opted-in completion, and maintenance inspection using the existing single-connection database fixture. Keeping it there avoids a second embedded-Postgres lifecycle for one transition test. The completion helper unit tests are in a separate focused file. ## Model Used OpenAI GPT-6 Codex, with reasoning and code/test 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>canary/v2026.1023.0-canary.10 |
||
|
|
0fb152ee94 |
fix: save stale remote-tracking commits in nested restore points (#17289)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Git cache expiration removes closed issues' worktrees once their
remote, or a restore point, can reproduce them
> - A nested `.paperclip-repositories` checkout's remote-tracking refs
can point at commits the remote has since dropped, such as a branch
deleted after a squash merge
> - Strict expiration rightly keeps such a checkout, because it may hold
the only copy; but restore point mode also kept it forever, because
there was nothing else to save
> - This pull request saves those commits in the checkout's restore
point, so the workspace can be reclaimed without losing them
> - The benefit is that long-lived multi-repository workspaces are
reclaimed in restore point mode
## Linked Issues or Issue Description
Refs #17257. Bug report described in-PR (bug report template).
**What happened?**
With restore points enabled, four closed workspaces on one instance were
kept with `unpushed_managed_checkout`, after a dry run had counted them
as candidates. Each nested `paperclip` checkout was clean, on `main` at
a commit that is on GitHub `main`. But it held about 1,000
remote-tracking refs, and between 28 and 41 of them pointed at branches
the remote has since deleted or force-pushed (147 to 203 commits it no
longer has).
The relaxed check passed, so the checkout needed no restore point beyond
those commits. Capture saved only `HEAD` history, so it found nothing to
save and stored no restore point. The final check before deletion then
ran the strict check, which keeps stale remote-tracking commits, and
rolled back. The dry run never reaches that final check, so it
over-reported. Together these workspaces held about 2.4 GB.
**Expected behavior**
In restore point mode, commits that exist only in remote-tracking refs
are saved, and the workspace expires.
**Steps to reproduce**
1. In a nested checkout, fetch a remote branch, so
`refs/remotes/origin/<branch>` exists.
2. Delete that branch on the remote and garbage-collect it.
3. With restore points enabled, close the issue and run expiration: the
workspace is kept with `unpushed_managed_checkout`.
**Paperclip version or commit:** `master` at
canary/v2026.1023.0-canary.9
|
||
|
|
6877760904 |
feat: save ignored files in git restore points (#17290)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Git cache expiration removes closed issues' worktrees after saving a
restore point of anything the remote cannot reproduce
> - Restore points left ignored files out, so any worktree with an
ignored file that was not a known regenerable directory was kept
> - On one instance that was the most common reason a closed worktree
was still kept, mostly build caches and agent evidence files
> - This pull request saves those ignored files in the restore point too
> - The benefit is that more closed worktrees are reclaimed, and nothing
an agent left behind is lost
## Linked Issues or Issue Description
Refs #17248, #17257. Bug report described in-PR (bug report template).
**What happened?**
With restore points enabled, one instance kept 15 of 43 inspected closed
worktrees with `unique_ignored_files`. Among 19 such worktrees on disk:
- 9 held `ui/tsconfig.tsbuildinfo`;
- others held build output outside the regenerable directory names
(`server/ui-dist/`, `test-results/`);
- 2 held materialized skill copies under `.claude/skills/`;
- about 5 held small files agents kept out of git: patches, evidence
archives, recovery receipts.
**Expected behavior**
Ignored files that are not regenerable are saved with the restore point
and restored on reopen, so the worktree can be removed.
**Steps to reproduce**
1. In a closed issue's worktree, leave an ignored file outside
`node_modules`, `dist` and the other regenerable directories (for
example `ui/tsconfig.tsbuildinfo`).
2. With restore points enabled, run expiration: the worktree is kept
with `unique_ignored_files`.
**Paperclip version or commit:** `master` at
|
||
|
|
ed3f067dbd |
fix(evals): select operating allowance by cadence (#17326)
## Thinking Path > - Paperclip manages AI agents and their operating budgets. > - Product E2E runs exercise real agents through a bounded gateway. > - The daily and weekly selections share one repository configuration variable. > - Their different request limits need different operating allowances. > - The existing parser accepts only one allowance, which cannot represent both reviewed limits. > - This change selects the allowance for the actual cadence and retains all existing validation. ## Linked Issues or Issue Description Refs #17009. **What happened?** The daily selection needs an $11 operating reservation; the weekly selection needs $21. A shared $21 allowance cannot fit the $20 daily cap. A shared $11 allowance does not cover the reviewed weekly resource bound. A configuration containing both allowances was rejected before paid admission. **Expected behavior** Each cadence uses its own reviewed allowance. Missing or malformed selected allowances stop admission before paid work. **Steps to reproduce** Set the operating allowance JSON to an object with daily and weekly allowances. Prepare either campaign through prepareScheduledCampaign. Before this change, both calls reject the outer object. **Paperclip version or commit** |
||
|
|
3f1ad6b25b |
fix(chat): resume task Retry after retained-source containment (#17317)
## Thinking Path > - Paperclip manages agent work through task and execution ownership. > - A task Retry must retain the user's request while the system verifies an old executor. > - Connected conversations also retain their external principal and accepted source batch. > - Chat Retry admission required containment before it could save a wait for that recovery. > - A saved chat receipt could also remain deferred after containment became available. > - This pull request lets the exact recovery-bound request wait and resume through its chat owner. > - Normal admission still checks containment, current access and policy before one successor starts. ## Linked Issues or Issue Description **What happened?** An ordinary task Retry on a connected conversation failed with `failed_chat_retry_not_authorized` while its retained remote executor still needed verification. The new recovery generation existed, but the chat action failed before it could save a dispatch wait. This occurred after the repairs in #17267 were confirmed live. The local regression found a second gap. A processed chat action with a deferred wake was not reconsidered after containment. Re-entering normal admission also tried to insert its existing durable receipt again. A historical hold for the same failed run could also hide the current Retry's workspace strategy, so the request still waited after the old executor was contained. Preparation could then read that sibling instead of the recovery record that owned the fresh successor, losing the fresh-workspace decision. **Expected behavior** Save the exact authorized Retry while its matching recovery generation verifies the executor. Once containment is recorded, reconsider the same chat receipt through normal admission. Start one successor with the original source batch. Keep publication, principal, pause and budget checks. **Steps to reproduce** 1. Admit a GitHub or Telegram request and record a failed native run with a retained remote lease. 2. Leave the executor's termination unconfirmed and exhaust its prior recovery generation. 3. Use ordinary task Retry to create a new recovery generation. 4. Process the connected Retry. The old admission code fails instead of retaining a wait. 5. With a deferred receipt, record exact containment and reconcile again. The old dispatch path leaves it stranded. **Paperclip version or commit** Reproduced from `412914cfa78d5abefcc34d3eb8d4bb86e461caac`. The branch is rebased onto current master. **Deployment mode** Hosted tenant for the reported failure. Isolated local PostgreSQL and the assembled heartbeat/chat services for the regression. Refs #17267 and #16965. This change does not add a Cloud dependency or change the absence protocol. ## What Changed - Permit a saved admission wait only for the exact current task Retry and active source recovery generation. - Keep promotion and execution dependent on independent containment proof. - Bind a Retry to active automatic recovery without replacing its generation, attempt count or deadline. - Share the current exact Retry strategy across historical holds for the same run. Retain each hold's stop and effect checks. - Select the canonical recovery record that proves the committed fresh successor during workspace preparation. - Let task receipt reconciliation return the same deferred action to its chat owner. - Recheck current board and external authority, then update the existing wake atomically with its successor. - Preserve the exact chat source batch. Keep generic board-comment filtering unchanged. - Add GitHub and Telegram cases for waiting, containment, one successor, stale generations, source edits, endpoint pause and supersession. - Update recovery documentation and capability maps. ## Verification - Red: real PostgreSQL tests reproduced the admission rejection for GitHub and Telegram. Six active-recovery cases then failed on the generation-ID assumption. Two same-run historical-hold cases remained deferred after containment on the old strategy lookup. A preparation regression then returned no readiness when the historical hold was stored first. - Green: 73 selected chat integration tests and 394 continuation, task Retry, workspace recovery, recovery-state and workspace-preparation tests passed. The chat tests use the real chat admission and heartbeat queue. Unrelated capacity stays occupied so no real provider starts. They assert no executor before containment, one successor afterward, unchanged source leases while waiting, preserved recovery budgets, and denial after authority changes. A separate source hold still blocks work. Both sibling insertion orders pass through the committed-readiness lookup and real workspace preparation: the new directory has independent Git storage and files, and unpublished old work remains intact. - `pnpm -r typecheck`, `node scripts/check-maps.mjs` and `git diff --check` passed after the review fixes. The source build passed on `f6660efa48e37a80cd9857217a537606de85b5c8`. - The local host reached its PostgreSQL semaphore limit. Successful runs used a test-only loader to reduce disposable cluster connection and worker limits. No repository database helper or application setting changed. - The full local `pnpm test:run` began before the final review edits, failed in its first server phase and was stopped after those failures. Later phases did not run. This is not a full local pass. The transient Retry-notice case passes after fixture isolation; the four Discord close-owned publication cases also pass in isolation. Other broad-run failure causes are not established. - Exact-head CI and security checks pass on `f6660efa48e37a80cd9857217a537606de85b5c8`; Greptile is 5/5 with zero unresolved review threads. Optional Paperclip Review completed neutral with a 5/5 static assessment of all 11 changed files and no findings; it did not run tests or hosted acceptance. All 57 current-head checks are terminal: 54 success, two skipped and the optional neutral review, with no failures or pending checks. - The real hosted workflow confirmed that the deployed absence repair recorded containment. A later ordinary UI Retry was accepted and saved a durable request, but it remains behind the historical source hold. The useful existing review remains unchanged. This follow-up is not deployed; a live successful successor and final task disposition remain unverified. No missing files are claimed recovered. ## Risks - The existing task reconciliation sweep can restage a deferred chat action. This reuses its receipt and generation; it does not grant new execution or publication authority. - Dispatch must keep the board Retry identity separate from the connected request's principal. The regression checks these bindings and preserves current chat authorization at each phase. - Historical holds reuse a strategy only from the locked same-run recovery that names the current Retry intent. A different run, malformed recovery record or independent source hold remains fenced. - Unknown executors without a matching active recovery still fail admission. Promotion and execution require containment even when admission saved a wait. - No schema or API shape changes. No credential changes, manual deployment or service restart. ## Model Used OpenAI GPT-6 through Codex, with repository tools, code execution and browser verification. The exact served model 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> |
||
|
|
3bd709df8b |
Operationalize Product E2E accounting, tracing, and scheduling (#17009)
## Thinking Path > - Paperclip manages agents and the work they perform. > - Product E2E tests exercise real agents, tools, setup and cleanup. > - Repeated runs need retained costs and traces, including failed attempts. > - A passing retry must not hide earlier spending or feedback. > - Recurring work also needs admission limits, private reports and failure detection. > - This change supplies those controls while keeping paid activation behind existing gates. ## Linked Issues or Issue Description Refs #16996 and #17158. This is the consolidated landing of the reviewed eval operations stack: #17019, #17024, #17025, #17027, #17030, #17033, #17035, #17038, #17043, #17044, #17048, #17050, #17051, #17092, #17096, #17100, #17107, #17109, #17112, #17115, #17117, #17121, #17123, #17127, #17130, #17133, #17134, #17137, #17140, #17143, #17154. The repository permits squash merges only. A local rehearsal showed that squashing each slice into master causes repeated conflicts in dependent slices. This PR preserves their commits and lands their combined implementation in one reviewed integration. Superseded slices will close after this merge. ## What Changed - Retain every attempt's cost receipt, including setup, failures, interruptions and retries. Separate reported amounts, estimates, missing costs and reserved capacity in reports and dashboards. - Add bounded request admission, exact model pricecards, gateway error accounting, cache policy and reconciliation evidence. Refuse unsupported routes and requests that cannot fit the allowance. - Integrate Braintrust gateway traces and OTLP correlation, with secret filtering and experiment imports. Record phase timing, including setup, feedback capture and cleanup. - Collect complaint and suggestion evidence after each attempt, including recovery after failures. Retain explicit review dispositions independently from the product grade. - Add gated daily and weekly selections, measured forecasts, setup/readiness checks, wall-time limits and retained monitor notifications. Include declared non-model overhead in admission while reporting the existing subscription separately. - Keep recurring history private, bound uploads and retention, and use isolated archive environments. Keep public publication an explicit operation. Report and publication jobs install only the committed lockfile, with scripts disabled and isolated stores; stale pins fail closed. - Integrate current master, preserve its Product E2E journey billing and privacy checks, and test that shared setup receipts do not double-count estimated agent usage. Preserve upstream fixture repairs. ## Verification - Complete integration baseline `0649a5ce7544ab20825c2183d2f421157db49f24` includes master `a3e1d915a7110a4e115b46396a3f79e6dfb4d05e` and every original stack head as ancestors. - On that integration baseline, `pnpm test:e2e:runner:unit`: 4,012 Vitest tests passed, two skipped, across 193 files; all 137 Node source-contract tests passed. - Focused billing and Product E2E journey contract checks: 24 tests passed. They cover estimates, unknown receipts, single-owner setup costs, browser-only execution and private publication guards. - Monitor and workflow wiring: all 96 Node tests passed. `node scripts/check-token-gates.mjs`: all gates clean. - `pnpm test:e2e:runner:typecheck` passes after the standard Runner TypeScript build refreshed local declarations. The first attempt rejected the new master `runner_prepared_launch` union member because this checkout retained an older generated declaration; the source union already contained that member. - Current head `492b0708a1c531a023d9ac10f0f3dc6383cc1754` removes dependency re-resolution from archive-authorized reporting jobs after the security scan flagged it. All 15 workflow-security tests pass after updating the old two-install assertion. - Hosted [CI run 39965460180](https://github.com/paperclipai/paperclip/actions/runs/39965460180) passes on the current repaired head, including typecheck, build, general tests, Runner checks, browser shards and canary dry run. Greptile is 5/5 on this exact head with no unresolved threads. The security scan passes after the fixed lockfile-resolution thread was resolved. - Paperclip Review returned a terminal neutral check but an incomplete 0-file assessment on the repaired head: its platform blocked both diff responses as prompt-injection content. This is not a successful code review. No bypass or fabricated pass was attempted; current-head Greptile, security and hosted CI are independent gates. - No paid test, reservation release or recurring-run activation accompanies this landing. Live route qualification and invoice reconciliation remain separate evidence. <details> <summary>Retained earlier validation failures</summary> Earlier broad local runs had two 15-second test timeouts (CLI poll cadence and Retry validation), one embedded-database setup timeout, and later PostgreSQL semaphore exhaustion. The exact timed-out tests passed separately without larger timeouts; the broad-run causes were not established. Those local failures remain distinct from subsequent hosted CI results. The independent #17158 fix records its Muse fixture collision, its UUID correction and hosted verification. Local Muse execution was also blocked by macOS embedded-PostgreSQL startup. No historical failure is treated as a successful paid qualification. </details> ## Risks - Admission is a local control, not a provider invoice cap. Missing provider receipts or resource charges retain unknown amounts and holds; estimates do not become billed dollars. - The bounded path admits only documented local suites and provider routes. Ordinary legacy or separate Runner protocol campaigns have different controls. Merging this code does not qualify additional routes. - Recurrence still needs the existing enable gates and current qualification/allowance evidence. Archive permissions and resources must match the documented private-environment contract. - Legacy reports may lack complete attempt or phase evidence. Reports preserve that limitation and do not infer zero usage from missing data. ## Model Used OpenAI GPT-6 through Codex, with reasoning, repository tools and code execution. The exact deployment variant and context-window size are not exposed by this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass (targeted checks; full-suite host limitation disclosed above) - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.1023.0-canary.8 |
||
|
|
4173059081 |
fix(inbox): keep advisory GitHub review waits quiet (#17318)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Mine shows a person's participation and requests that need their response. > - Automatic GitHub reviews should remain quiet until the assigned work needs human input. > - Review bots used completion requests to keep tasks in review while CI or a PR was still open. > - Those requests correctly entered Mine, despite the automatic-work filter from #16356. > - This pull request gives published reviews an existing provider-event wait in the in-review state and withdraws superseded completion requests. > - Real input requests and separate approval gates keep their current behavior. ## Linked Issues or Issue Description Refs #16356 Refs #13314 An advisory score below 5/5, pending or failed CI, or an unmerged PR does not by itself require a person's approval in Paperclip. The bot can track those conditions without adding a completion card to Mine. ## What Changed - Explain the quiet review lifecycle in the GitHub review instructions. Use a yielded provider-event wait, or an existing saved monitor, after publication. Report done when the configured completion conditions are met. - Keep a confirmed, published review in `in_review` during that wait. Reuse existing authorization and monitor delivery. Add no schema, timer, or polling loop. - Recognize the committed external wait in review attention and recovery. Shared execution receives a verified publication flag; GitHub receipt lookup stays with the GitHub module. Other chat waits keep their current status. - Withdraw an older system-created completion request only when the same bot has published both reviews and its new report has no attention request. Keep explicit cards, separate approval gates, and requests without the prior review evidence. Finish validation stays read-only; withdrawal commits atomically with the new status after checking current governance, the admitted result, and both low-risk contracts. - Update the product rule, capability maps, and regression coverage. ## Verification - Final commit `7c502118eec36028d1b4537bb8797ef893940bf0`: 54 GitHub checks passed, two intentionally skipped, none pending or failed. Greptile is 5/5, the security scan passed, and all review threads are resolved. - Passed: `pnpm -r typecheck` and `pnpm build`. Rechecked server typecheck and build after the final authorization guard. - Passed: 195 tests across `external-chat-wait.integration.test.ts`, `status-arbiter.test.ts`, `review-instructions.test.ts`, and `issue-liveness.test.ts`. The database suite used an isolated PostgreSQL 18 container through `PAPERCLIP_TEST_DATABASE_URL`. - Acceptance coverage: 3/5 stays in review outside Mine; 5/5 with pending completion conditions stays in review; 5/5 with satisfied conditions finishes quietly; a real repository-access request enters Mine until resolved. Also covers obsolete-card withdrawal, transaction rollback, rejected reports, separate approval gates, multiple reviewed heads, saved monitors, and revoked endpoints, repositories, or membership. - Passed: `node scripts/check-maps.mjs`, `node scripts/check-module-boundaries.mjs`, and `git diff --check`. - The initial acceptance run exposed a missing authenticated user in the test fixture. Added the user required by the existing addressee authorization check. All 154 tests in that database suite then passed, including the final rerun. - Review found premature withdrawal during validation, stale repository access, and a missed receipt when a run reviewed multiple heads. The revision fixes all three and includes acceptance regressions. The saved-monitor commit guard now accepts the verified published-review state. - `pnpm test:run` was attempted but could not complete on this Mac. Embedded PostgreSQL failed to allocate semaphores (`semget: No space left on device`); a direct initialization probe confirmed the host resource limit. The isolated database run proves the affected suite, not the full repository suite. - These tests exercise persisted review receipts, finalization, and Mine queries. They do not prove a live model followed the new instructions or submit a live GitHub review. ## Risks - The bot must follow the new lifecycle instructions. A real explicit attention request still enters Mine. - Existing obsolete cards are withdrawn when the same bot publishes a later review and reports no human dependency. This change does not run a bulk cleanup of historical inbox entries. - Review attention now recognizes an external response path. It requires an applied current status decision, a successful bound run, and current conversation, repository, and principal authorization. - No migration or UI change. The full local test suite is unverified because of the host database limit; CI passes on the final commit. ## Model Used OpenAI Codex, GPT-6, with reasoning, repository inspection, code editing, and test execution. The exact deployment model ID and context-window size are not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass (195 focused tests; full-suite limitation above) - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
a3e1d915a7 |
fix(evals): preserve unknown Codex provider cost (#17158)
## Thinking Path > - Paperclip manages AI agents and their work. > - Direct Runner evals report usage and costs for each attempt. > - Codex App Server supplies token counts without a provider USD receipt. > - The legacy Runner ledger fills an absent cost with zero. > - The eval result exposed that ledger value as provider-reported cost. > - This fix keeps provider cost unknown and preserves the estimate and budget stop. ## Linked Issues or Issue Description Refs #17009 and #17154. This is a separate direct Runner accounting fix based on master. ## What Changed - Codex direct eval usage now reports null provider USD cost with unavailable provenance. Token counts and the pinned price estimate remain available. - Preserve the original ledger amount as optional `legacyLedgerCostNanodollars`. Keep the original snapshot receipts unchanged. - Apply the existing budget ceiling to the retained ledger amount as well as the token estimate. Correcting a label does not relax admission or settlement checks. - Use the unique operator UUID for Muse test company prefixes, eliminating collisions from the previous 24-bit random suffix. - Document the cost distinction in the Runner/Evals integration contract. Other provider behavior and historical artifacts remain unchanged. ## Verification - Reproduced both zero and nonzero legacy amounts being labelled as provider USD cost before the fix. - The current eval-session contract and entrypoint tests pass (39 tests). The new regression uses the bounded usage consumer and actual campaign cost projection. It checks unknown provider cost, the separate estimate, unchanged raw evidence and the legacy ledger budget stop. - All 15 Runner report, campaign and metrics Node tests pass. Four adjacent CLI entrypoint/runtime tests also pass. - Runner TypeScript validation passes with its standard dependency-build prerequisite; `git diff --check` passes. Both Runner TypeScript configurations also passed during the original accounting verification. - Hosted CI run [39960367674](https://github.com/paperclipai/paperclip/actions/runs/39960367674) passes on `7d84f138044bac58cdced6baa2d0ec3d63767bb8`, including typecheck, build, server tests and browser checks. Greptile and Paperclip Review both report 5/5 on that commit with no actionable findings. - The previous CI failure was a PostgreSQL unique-index collision on `companies_issue_prefix_idx` during Muse fixture setup. Local Muse verification could not reach test execution because embedded PostgreSQL timed out during macOS startup (initially reporting a missing zstd library). The corrected fixture passed in hosted Linux CI. - Verification used no model calls. Full repository checks were performed by hosted CI; local verification was scoped to the changed contracts and fixture. ## Risks Consumers that assumed Codex always had a numeric provider cost must handle null. The contract already permits null for other profiles, and the report consumer retains missing-cost coverage. The legacy amount remains available as unverified evidence and still triggers the existing budget stop. Neither the estimate nor that ledger amount proves an invoice charge. This change does not rewrite old artifacts or qualify other provider routes. ## Model Used OpenAI GPT-6 through Codex, with repository tools and code execution. The exact deployment variant and context-window size are not exposed by this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [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> |