mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-13 23:04:38 +02:00
e1863d4fe72d80eeab49fc54873e5d3cd8b4ffd0
5065
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e1863d4fe7 |
Fence shared workspace readers and preserve projectless chat admission
Retain root source and reader provenance, recheck publication access, keep plain agent transfers independent of repository discovery, and resolve organizational projects without filesystem sources. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
c205ab39f0 |
Merge origin/master and rebase feature migration sequence
Preserve upstream usage attribution and sandbox restore retry semantics alongside task workspace persistence. Regenerate task and physical-root migrations after upstream 0333 with replay-safe guards. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
b189f586d2 |
chore(lockfile): refresh pnpm-lock.yaml (#15960)
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> |
||
|
|
db2f00947b |
fix(runner): include Claude ACP in the server package (#15912)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The server ships a copy of Paperclip Runner for native agent execution. > - The Claude ACP bridge is a runner dependency, but it was absent from the server package. > - The runner resolves this bridge dynamically, so the import-based packaging check missed it. > - A Claude session then failed before startup and showed a generic connection error. > - This pull request ships the qualified bridge and reports a missing runtime with repair steps. ## Linked Issues or Issue Description **What happened?** A server install with the vendored runner could not load `@agentclientprotocol/claude-agent-acp/package.json`. The runtime test suggested checking the model or provider connection. The task reported an interrupted runner session. **Expected behavior** The server package includes its qualified Claude ACP runtime. A missing bundled runtime reports an installation error and explains how to repair it. **Steps to reproduce** 1. Use a server install with the vendored runner and no Claude ACP bridge. 2. Select the Paperclip Runner ACP harness and the Claude provider. 3. Run the runtime test or open a session. 4. Observe a missing-module failure before Claude starts. **Paperclip version or commit** Reproduced on `e733bddc45`. This branch is based on current master. **Deployment mode** Self-hosted server built from source. Related: #15224 upgrades the Claude bridge. This PR fixes its omission from the server package and keeps the current qualified version. A future bridge upgrade must update the server pin too. ## What Changed - Add Claude ACP `0.73.0` to the server dependencies and bundled dependencies. - Pin the qualified Claude SDK and bridge dependencies in the npm bundle producer graph. Publish its native packages as consumer optional dependencies instead of shipping the producer executable. - Bind source-built vendored runners to the shared pnpm dependency store while retaining the server manifest as the provider authority. Keep published npm and self-rooted deployment roots unchanged. - Include dynamically resolved Claude and Codex bridges in the vendor dependency check. - Preserve the missing-package category through the sidecar, Rust transport, and native session executor. Stop automatic retries for this installation failure. - Show **Runtime missing** in the runtime test and task thread. Explain the reinstall or rebuild, server restart, and runtime test steps. - Add regression tests and document the repair procedure. ## Verification - Red/green comparison with the production sidecar: the same missing-package setup returns a raw require-stack error on the original code and an actionable, non-retryable missing-runtime error on this branch. - The rebuilt server's vendored Claude installation probe passes using the source-server authority selected by the launcher, without starting a provider or making a model request. - Focused server verification: 830 tests passed. Runner installation and sidecar error tests: 83 passed, with 6 platform-specific skips. Packaging tests: 28 passed. Rust transport tests: 7 passed. - Source/deployment authority tests: 3 passed. Red/green packaging tests include a Linux-produced Claude SDK bundle, actual npm pack, and closed executable admission on Linux x64 and macOS ARM64/x64. - Full UI suite: 8,113 tests passed. - `pnpm -r typecheck` and `pnpm build` passed on the rebased branch. - Full current-head CI passed, including all general and serialized test lanes, Runner Rust/Vitest verification, typecheck, build, and all eight e2e shards. Greptile reviewed `d6a2741834ff922956560f2f689382827bbaf9e0` at 5/5 with no unresolved threads. - Local full-suite limitation: an earlier parallel attempt hit Postgres startup failures and timeouts; the affected server tests and a database fixture passed in isolation. The subsequent serial `pnpm test:run` was stopped while still running after complete CI passed. Focused local tests, full UI tests, typecheck, and build completed as listed above. - Token gates and `git diff --check` passed. ## Risks - Bundling the Claude bridge increases the server package size. The producer graph retains the qualified SDK version and existing isolation patch. Native platform declarations move to the published server manifest, while the runtime keeps its exact version, declared-slot, no-follow, and executable digest checks. - The new failure category is permanent. Operators must repair the server installation before they retry the task. - Existing failed runs retain their recorded error. The clearer message applies to new runtime tests and runs. - No database schema or migration changes. ## Model Used OpenAI GPT-6 through Codex. The system reports the model family; the exact deployment ID and context-window size are not exposed in this session. Used reasoning, browser inspection, tool use, and code execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.1012.0-canary.5 |
||
|
|
1b4c803816 |
fix(costs): preserve usage attribution and explain unavailable run costs (#15948)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The cost ledger records usage and controls agent budgets. > - Some runs lose the selected model or API billing category before their usage reaches the ledger. > - The dashboard reports missing costs but does not identify the affected runs or the available actions. > - This pull request preserves usage attribution and adds a compact view of cost gaps grouped by agent. > - Operators can inspect runs, open agent settings, and record saved costs without starting new agent work. ## Linked Issues or Issue Description Refs #15634, #11938, #15879. This follows the merged subscription reporting in #15634. The older proposal in #11938 addresses part of ACP model attribution. This change also rejects conflicting models across a turn, fixes credential-backed billing capture, and explains cost gaps in the existing dashboard. The Pi receipt recovery work in #15879 covers a separate adapter recovery path. This PR preserves unresolved receipts and does not recover provider usage or reprice past entries. **What happened?** ACP results use the requested model even when the runtime selects a model. Native and OpenCode API runs can report unknown billing despite a matching API credential. The dashboard shows notices without links to the affected runs. These gaps make API and subscription totals harder to interpret. **Expected behavior** Record the model selected by the runtime. Identify API billing only from a matching credential. Keep costs unresolved when reliable usage or pricing is absent. Show the affected agents and runs. Offer an accounting retry only when saved evidence is ready. **Steps to reproduce** 1. Run an ACP agent with its model left unset, or use a matching API credential with a native or OpenCode agent. 2. Inspect the recorded model and billing category for the run. 3. Open Costs Overview for a period with unpriced usage or terminal runs awaiting receipts. 4. Inspect the notice and try to find the affected run or the relevant agent settings. **Paperclip version or commit** Current master at `657e71584a0`. The patch is rebased on that commit. **Deployment mode** Self-hosted server and local development. The cost detail tests use a disposable PostgreSQL database. ## What Changed - Preserve the runtime-selected ACP model in checkpoints and final results. Conflicting models across one turn remain unpriced. - Share credential-backed API billing checks across native and OpenCode results. Keep unknown billing when the provider and credential do not match. - Add company-scoped cost detail and accounting retry endpoints. Board viewers can read details but cannot retry. Agents and foreign companies are denied. - Limit each detail category to 25 entries. Use one consistent database snapshot and sparse indexes matching the filters and sort order. Add the unknown-billing count to the existing summary query. - Replace the notice with an expandable count line. Group entries alphabetically by agent. Show the saved blob icon, link each name to configuration, and put run and retry links beside the date. - Preserve estimate and unavailable-price labels on Providers and Billers; keep retry confirmation after the resolved notice disappears. - Fetch details only while expanded. Retain the last loaded details after a refresh error. Retry saved accounting only; do not rerun the agent. - Add capture, database, authorization, UI, and Storybook coverage. Update the implementation contract and cost reporting guide. ## Verification - `pnpm -r typecheck` passed after rebase. - `pnpm build` and `pnpm build-storybook` passed after rebase. - `pnpm check:token-gates` and `git diff --check` passed. - 1,086 focused tests passed across 17 files. These cover ACP, native and OpenCode capture, PostgreSQL reports, route authorization, OpenAPI, Costs UI, and existing accounting regressions. - Desktop and mobile browser checks passed during UI review. They cover lazy expansion, alphabetical groups, loaded blob icons, configuration and run links, keyboard retry, and layout overflow. - `pnpm test:run` was attempted with command-scoped `init.defaultBranch=master` for Git fixtures. It was stopped after a native session recovery failure reproduced on unchanged base `657e71584a0` (`archives a damaged prior epoch before completing guarded same-task replacement with real runnerd`). An MCP upload case failed in the broad run but passed in isolation on both the base and PR branch. These local limitations do not affect the 1,086 passing focused tests. - Current-head CI is green, including all unit test lanes, serialized server suites, runner checks, typecheck, build, policy, and eight browser shards. Apex re-review is 5/5 on `56de60effdb`; all four original findings are addressed and every review thread is resolved. - PostgreSQL applies migration `0333` in the integration fixtures. EXPLAIN ANALYZE on the production detail statements uses the new sparse indexes behind 5,000 healthy records in each table. - Ten provider/biller card cases cover missing and estimated prices; the retry test covers row removal and notice unmount. A live Storybook browser check verifies the confirmation and updated count. - Run `pnpm storybook`. Open **Costs / Subscriptions / Cost Data Quality Details** to inspect the production page with representative fixtures. ## Risks - Model and billing metadata change for future runs. Estimates still require complete usage and a supported price. No past cost amount is rewritten. - A missing final receipt remains unresolved for budget checks. The display changes after fifteen minutes; that time limit never authorizes settlement. - Accounting retry is a write action. It retains the existing company checks, receipt fences, idempotency, and audit logging. - Detail reads are bounded and run only after expansion. They poll every 30 seconds while open. No provider probe or new summary query is added. - Migration `0333` adds two partial indexes; it changes no stored cost amounts or table columns. Index creation scans the existing tables and may briefly block writes during deployment. No lockfile or workflow change is included. ## Model Used OpenAI Codex, based on GPT-6, with repository inspection, code editing, browser checks, and test execution. The exact serving model ID, reasoning setting, and context window were not exposed to this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Codex <noreply@openai.com> Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.1012.0-canary.4 nightly/v2026.1012.0-nightly.0 beta/v2026.1012.1-beta.0 |
||
|
|
4611d6e95a |
fix(restore): retry sandbox copy-back before holding the task for repair (#15920)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Legacy sandbox runs copy the agent's workspace files back to the host when the run settles. > - When this copy-back fails one time, the server keeps the sandbox and puts a board-owned repair hold on the task. > - The hold blocks new messages. Only an operator API runbook can clear it. The board shows that runbook text, so users do not know what to do. > - Many copy-back failures are transient: a dropped transfer, a provider error, or a merge lock that another restore holds. > - This pull request retries those failures automatically while the run still has its merge baseline and before any host file changes. > - The benefit is that most failures repair themselves, and the remaining holds explain the situation in plain language. ## Linked Issues or Issue Description Refs #14590 (related: no-replay recovery holds) **What happened?** A legacy sandbox run finished its work, but one workspace copy-back attempt failed. The task then showed "Recovery needed" with operator-only steps ("verify its exact stop receipt … record workspaceRepairEvidence"). Messages from the user stayed saved and were not delivered. **What did you expect?** Paperclip retries a transient copy-back failure automatically. If a repair is still necessary, the task says clearly what happened and who must act. **Steps to reproduce** 1. Run an agent in a legacy sandbox environment. 2. Make the workspace copy-back fail one time, for example with a dropped transfer or a held merge lock. 3. Open the task. It is blocked behind a repair hold, and new messages wait. **Additional context** Separately, distinct live events can render toasts with the same title and body (for example, two runs of the same agent that both succeed). The toast list showed both, and the desktop app forwards each one as a notification. ## What Changed - `packages/adapter-utils/src/sandbox-managed-runtime.ts`: retry the workspace copy-back up to three times in total, with a 2 s and then a 5 s backoff. A retry occurs only when the failure is `restore_failed` or `restore_lock_timeout` and no host workspace file was written. The merge now marks the first host write in `beforeApply`, which runs under the merge lock before any write. - Unsafe archives, permission failures, and failures after the merge started writing are never retried. Each retry uses a new temp directory and a new imported Git ref. - New optional `workspaceRestoreRetryDelaysMs` input, so tests can shorten the backoff. Nested repository restores receive the same value. - `ui/src/components/ExecutionBlockerNotice.tsx`: for a workspace repair hold, the notice now says what happened in plain language. The stored operator runbook text moves under "Technical details". - `ui/src/context/ToastContext.tsx`: also dedupe toasts on their visible tone, title, and body inside the existing 3.5 s window. - `doc/workspace-restore-locks.md`: document the automatic retry. ## Verification - `pnpm --filter @paperclipai/adapter-utils exec vitest run src/sandbox-managed-runtime.test.ts src/workspace-restore` (218 tests pass). New tests cover: - a retry after a dropped transfer, - a retry after a merge-lock timeout, - a stop after three attempts, - no retry for an unsafe archive, a permission failure, or a failure after the merge started writing. - The existing step-diagnostic test now uses failures that persist across retries. It still expects one diagnostic per failed step. - `pnpm --filter @paperclipai/ui exec vitest run src/context/ToastContext.test.tsx src/components/ExecutionBlockerNotice.test.tsx src/context/LiveUpdatesProvider` (120 tests pass). - `tsc --noEmit` for adapter-utils passes. `node scripts/check-token-gates.mjs` is clean. - Not fully run: the full adapter-utils suite has two failures in `github-launcher*.test.ts`. They read the managed GitHub credentials of the local sandbox environment and do not touch restore code. UI `tsc -b` reports five errors in `PluginOrganizationSwitcher.tsx`, which is not changed here, because the plugin SDK is not built in a fresh install. ## Risks - A copy-back that fails before the merge can take about 7 s longer before it reports a failure. Lock timeouts can take up to about 97 s in total (three 30 s lock waits plus backoff). Settlement already waits for the copy-back. - The retry logic depends on `beforeApply` running before the first host write in `mergeDirectoryWithBaseline`. That is true today, and a comment marks it. - A Git bundle import before the merge can leave unreferenced Git objects after a retry. They are harmless, and the existing ref cleanup still runs on each attempt. - Content-based toast dedupe can hide a second toast with identical text inside 3.5 s. That is the intended behavior. ## Model Used - Claude Opus 5.5 (`claude-opus-5-5`, Anthropic) in the Claude Agent SDK, with tool use (shell, file edit, code search) and a subagent for code exploration. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Paperclip Agent <agent@paperclip.ing> Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>canary/v2026.1012.0-canary.3 |
||
|
|
657e71584a |
fix(daytona): record provider state when lease creation hits its deadline (#15946)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents can run in Daytona sandboxes. The Daytona plugin acquires a
lease before each run.
> - Some acquisitions time out. The report then says
`environmentAcquisitionPhase=create` and that the whole 300 s budget was
used.
> - The Daytona SDK create timeout is equal to the acquisition budget.
The outer timer always fires first. Thus the report never shows the
state that Daytona left the sandbox in.
> - Without that state, we cannot choose a fix. A stuck snapshot pull, a
slow start, and a missing allocation need different fixes.
> - This pull request adds one bounded, read-only provider lookup at the
create deadline. It records a fixed-enum provider state in the existing
acquisition diagnostic.
> - The benefit is that the next timeout identifies where the provider
stopped. Retry, budget, and cleanup behavior do not change.
## Linked Issues or Issue Description
Refs #15736 (bounded Daytona acquisition failure observations). This
pull request extends those observations.
**What happened?**
A Daytona lease acquisition timed out with `Daytona lease acquisition
timed out; allocation cleanup is pending`. The run-failure report gave
`environmentAcquisitionPhase=create`,
`environmentAcquisitionElapsedMs=300004`, and
`environmentAcquisitionBudgetMs=300000`. The report did not show the
Daytona sandbox state. The SDK create timeout is equal to the outer
budget, so the outer timer always fires first. The SDK never reports the
stuck state.
**Expected behavior**
When creation is still pending at the deadline, the report should show
where the provider left the allocation. Examples are `pulling_snapshot`,
`starting`, `error`, or `absent`.
**Steps to reproduce**
1. Configure a Daytona environment.
2. Start a run when Daytona does not finish create and start within the
acquisition budget.
3. Inspect the setup-failure report. Only the phase, the elapsed time,
and the budget are present.
**Paperclip version or commit**
`master` at
canary/v2026.1012.0-canary.2
|
||
|
|
ba04968b10 |
fix(server): report failing workspace jobs as 422 instead of 500 (#15941)
A non-zero workspace job exit is the operator job's own result. Map it to 422 workspace_job_failed with the exit code instead of a 500 that the error handler reports to Sentry as a server crash. A job without a command is also 422. Other workspace command failures are unchanged. Co-Authored-By: Amy (Opus) <claude@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>canary/v2026.1012.0-canary.1 |
||
|
|
f331d386dc |
fix(ui): attribute stackless unhandled rejections with event and lifecycle facts (#15940)
Diagnostics only for Sentry PAPERCLIP-FRONTEND-G: record trusted/synthetic unhandledrejection origin, promise kind, and coarse page lifecycle facts without reading rejected values. Co-Authored-By: Amy (Opus) <claude@users.noreply.github.com> Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
bfe7c6357d |
fix(ui): stack confirmation prompt and hide raw custom target keys (#15895)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents ask the board for decisions with `request_confirmation`
interaction cards
> - The compact task-chat card puts the prompt and the target pill in
one `justify-between` flex row
> - An agent can send a `custom` target with only a machine `key` (for
example `github:org/repo:pull:16`) and no `label`
> - The card shows that raw key as a mono pill that does not shrink, so
the card splits about 50/50 and the prompt is squeezed into the left
half
> - This pull request stacks the prompt above the pill and hides custom
targets that have no human label
> - The benefit is a readable, full-width confirmation prompt without
meaningless identifier pills
## Linked Issues or Issue Description
**What happened?**
A pending confirmation card split its body into two columns. The prompt
used the left half. A pill with the raw text
`github:paperclipai/paperclip-desktop:pull:16` used the right half. The
line breaks in the prompt were also lost.
**Steps to reproduce**
1. Create a `request_confirmation` interaction with a multi-line
`prompt` and `target: { "type": "custom", "key":
"github:org/repo:pull:16" }` (no `label`).
2. Open the task chat for the issue.
3. See that the prompt and the key pill share one row, split about
50/50.
**Expected behavior**
The prompt uses the full card width and keeps its line breaks. A custom
target key is an identifier for idempotency and stale detection, so it
does not show as display text. A target pill shows only when the target
has a human label.
## What Changed
- `TaskChatCompactInteractionCard`: stack the prompt full width with the
target pill below it. This applies to the confirmation and the
checkbox-confirmation cards.
- `TaskChatCompactInteractionCard`: keep line breaks in the prompt
(`whitespace-pre-line`, `break-words`).
- `TaskChatCompactInteractionCard` and `IssueThreadInteractionCard`:
show no target chip for a `custom` target without a `label`. Labeled
custom targets (for example connection authorization) and
`issue_document` targets do not change. The stale-target chevron shows
only when both chips show.
- Add regression tests for the stacked layout, the hidden unlabeled key,
and the labeled link pill. The tests cover the compact confirmation and
checkbox-confirmation cards, and the thread card pending chip and
stale-target expiry (with labeled, unlabeled, and mixed custom targets,
and the chevron rule).
- `CONTRIBUTING.md`: add a "Keep Screenshots Out of the Repo" section.
Contributors attach review screenshots to the PR on GitHub. They do not
commit them to the repository.
## Verification
- `cd ui && npx vitest run
src/components/task-chat/TaskChatInteractionCard.test.tsx
src/components/IssueThreadInteractionCard.test.tsx`: 2 files, 100 tests
pass. If you revert the `isDisplayableRequestConfirmationTarget` guard,
3 of the new thread-card tests fail.
- `node scripts/check-token-gates.mjs`: all gates clean.
- `pnpm --filter @paperclipai/ui typecheck`: no errors in changed files.
The only errors come from `@paperclipai/plugin-sdk/ui`, which had no
built `dist` in the local workspace. These errors are not related to
this change.
- Manual: create a confirmation as in the steps above. The prompt is
full width and no key pill shows. Add `label: "PR #16"` and an `href`. A
`PR #16` link pill shows below the prompt.
### Screenshots
These are Storybook renders of the compact task-chat card at 720 px. The
three cards are: a confirmation with an unlabeled custom target (the
reported case), a confirmation with a labeled custom target, and a
checkbox confirmation with an unlabeled custom target.
The images load from an earlier commit of this PR. A later commit
deletes them, so they are not part of the merged change.
| Before (light) | After (light) | After (dark) |
| --- | --- | --- |
|

| 
| 
|
## Risks
- Low risk. This is a UI-only change. No schema or API change.
- Agents that send a custom target without a label lose the visible
pill. The key was never readable display text. To show a pill, agents
set `label`.
## Merge Note
The first commit was authored as Paperclip. When you squash-merge, keep
this trailer in the squash body:
Co-Authored-By: Paperclip <Paperclip-Paperclip@users.noreply.github.com>
## Model Used
- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, via Claude Code agent
harness with tool use (shell, file edit). 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: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
canary/v2026.1011.0-canary.12
|
||
|
|
d49cbf4c4f |
Permit idle sleep with consumed retries and passive worktrees (#15907)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Idle sleep must wait for accepted work and durable cleanup.
> - The retry dispatcher keeps its due date after it promotes a retry.
> - Finished retries and passive local directories can therefore look
like pending work.
> - This pull request requires completion proof and permits known
passive directory records.
> - It retains the records and keeps real work and unknown state awake.
## Linked Issues or Issue Description
Related: #15897 and #15851. The open runtime-service work in #13413
retains its separate service and ingress guarantees. This patch does not
change those checks.
**What happened?**
Idle sleep treats a consumed retry date as pending work. It also blocks
on every retained execution workspace, including a local directory
without runtime activity.
**Steps to reproduce**
1. Promote a scheduled retry, then finish its run without accounting
debt. Retain its scheduled retry date.
2. Retain a local primary directory or Git worktree without runtime
configuration, commands, or cleanup due.
3. Request an owned idle safety report. The report returns `present` for
this history.
**Paperclip version or commit**
Reproduced on master at
|
||
|
|
2223b7de9e |
perf(ci): stop building the standalone packages inside the public-install verifier (#15908)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Every pull request runs the `Build` check. Its last step is the public npm install verifier, `scripts/verify-grok-npm-install.mjs`, which stages the eighteen public packages reachable from the server and the CLI, packs them, and installs them in a clean consumer. > - Before it stages anything, the verifier runs `scripts/build-standalone-public-packages.mjs`. That script installs and compiles the seven sandbox-provider plugins that live outside the pnpm workspace: one network `pnpm install --ignore-workspace` and one `tsc` per plugin. The line was copied from the build block of `release.sh` when the verifier was written in #14024. > - release.sh needs that build because it publishes those plugins. The verifier does not for anything it stages. Its package closure follows `workspace:` specs from the server and the CLI, and a `workspace:` spec can only name a workspace package, so none of the eighteen packages it packs is a standalone package and no tarball reads that build's output. > - One optional path at the end of the verifier does read it: qualification-package retention, which `docker-runner-check.yml` enables with `PAPERCLIP_RUNNER_QUALIFICATION_PACKAGES_DIR` after running only `pnpm build`, packs the Daytona plugin from its `dist`. The PR `Build` job never sets that variable. > - The timing line from #15868 and #15888 puts the verifier's staging phase at 75.8s on the latest run, of which the staged sources, the bundled installs and the packs account for 46.5s. The rest, about 29s, is the work before staging, and the standalone build is almost all of it. `Build` at 433-530s is the slowest check in most PR runs once the server shards drop, so that time sits on the run's critical path. > - This pull request runs the standalone build only when retention is requested, keeps the `ui-dist` copy the server package needs, and times both on the timing line so what is left before staging is visible. > - The benefit is a `Build` job about 21-28s shorter, with no change to what the verifier installs or checks, no loss of coverage for the standalone build because Canary Dry Run still runs it through `release.sh`, and no change to the retention path that reads it. ## Linked Issues or Issue Description Replaces #15892, which GitHub closed automatically when the branch of #15888 (its base at the time) was deleted on merge. Same two commits, rebased onto `master` on top of #15888 ( |
||
|
|
0ba88d2195 |
fix(opencode-local): preserve deadlines in Daytona process sessions (#15905)
## Thinking Path > - Paperclip manages work done by AI agents. > - The OpenCode adapter can run its CLI in a provider sandbox. > - A long CLI run must remain observable until it exits or the host stops it. > - The legacy main command does not request a provider process session. A launch without an active run step uses one synchronous command request. > - That request can lose its response before the resolved execution budget ends. > - This PR requests the existing asynchronous session path for the main Daytona OpenCode command. It also carries the resolved command deadline through the host and provider contracts. The host still applies the effective capability gate. > - The change removes that synchronous request from eligible sandbox runs. ## Linked Issues or Issue Description Related reporting fix: #15789. A search of open and closed GitHub issues and PRs found no duplicate transport fix. **What happened?** A hosted Linux legacy OpenCode run retained a gateway-timeout response about one hour after CLI startup. It had a four-hour resolved budget and recent agent output. The earlier reporting fix correctly kept the configured policy separate from the timeout message. The original response owner and specific timer were not retained, so this PR does not claim to prove that provider cause. **Expected behavior** A Daytona target that supports persistent process sessions should dispatch the long agent command asynchronously. Paperclip should observe its output and exit through the existing session path. A single command HTTP request should not need to stay open for the full agent run. **Steps to reproduce** 1. Use legacy OpenCode with a Daytona sandbox target and a four-hour execution budget. 2. Launch outside an active measured step. The current main CLI call does not request a persistent process session. 3. In the SDK fixture, make the synchronous command request return a provider timeout before the budget ends. Keep the asynchronous session healthy for more than one hour. 4. The regression tests require session dispatch, incremental output, one command dispatch, a real exit result, and session cleanup. 5. Set a shorter command deadline and stall dispatch, a status read or a log snapshot. The command must return a timeout at its own deadline. Healthy output must not extend that deadline. **Paperclip version or commit** Observed source: `e733bddc45e00efb1819fffa8677f7b0d07802c5`. PR base: `4b59e2a2ecc67e98b336b8eb4c09959681672d54`. **Deployment mode** Hosted Linux server, legacy OpenCode adapter, Daytona sandbox. The host gate retains the old path when a provider cannot keep process sessions. ## What Changed - Add an optional sandbox session choice to the shared process helper. Leave the choice absent for existing callers. - Request that session only at the main Daytona OpenCode CLI invocation. Keep probes, preparation and other providers on their existing paths. - Carry a separate optional command deadline through the shared runner, host service and plugin SDK. Existing session callers can omit it. Zero leaves the command deadline disabled. - Bound dispatch, live output and recovery observations with one command timer. Clear it on settlement. Fence late stream callbacks. Remove deadline listeners after each read and clear interrupted polling waits. Preserve a known exit code if only the final logs time out. A timeout result does not claim that the process stopped. - Add adapter and process-helper regression coverage. Test the existing Daytona session for a virtual run beyond one hour, without command replay, with release cleanup. - Document transport selection and the limits of the timeout evidence. ## Verification - `pnpm -r typecheck` passed on Node 25.6.1 and pnpm 9.15.4 with the command deadline changes. - `pnpm build` passed with the updated contracts. The separately installed Daytona package also passes its own typecheck and build against the pinned SDK 0.203.0 and TypeScript 5.9.3. - Five focused process, adapter, host and provider suites passed all 349 tests. They cover a run beyond one hour, five stalled observation phases, healthy-stream deadlines, zero/absent budgets, late callbacks, session release, SSH and capability fallback. - The real environment-runtime forwarding tests passed: 19 selected tests, including the separate command budget and forced session flag in the worker RPC. - An earlier full local `pnpm test:run` began before a clean build and overlapped the correction. That mixed-version run was stopped and is not passing evidence. After the build, the two previously uncollected suites pass (1,659 tests). Two unchanged workspace fixtures fail with this machine's Git default branch `main`; both pass with a test-process-only `master` override. One unchanged native recovery fixture fails on both this branch and an independently installed and built checkout of base `4b59e2a2ecc67e98b336b8eb4c09959681672d54`, with the same terminal-result mismatch. It is not introduced by this change. The full local suite is not claimed green. These limitations do not replace fresh hosted CI. - Final head `8e96638cdef8ca6e91f7a42a52c83a075180c137` passes all hosted checks: 54 passed and 2 expected Storybook skips. Greptile reviewed this exact head at 18:12 UTC and scored it 5/5. Both actionable findings are fixed and resolved. GitHub reports no conflicts and a clean merge state. Earlier-head results are not used as final-head proof. - The standalone provider test command passes 373 tests, with 14 expected live-probe skips. - SDK tests use a controlled virtual clock. No live provider experiment, task replay, manual deployment, or public PR merge was performed. ## Risks The main legacy OpenCode command uses Daytona's existing session route when the effective capability gate permits it. A quiet log stream can use the existing status and snapshot fallback. Those observations add provider reads. The new optional command budget is separate from each read's timeout and returns a timeout without the host RPC's extra 30-second allowance. Existing session callers that omit it keep their previous lifetime. Callback bridge control commands retain their explicit session bypass. The capability gate retains the previous path for unsupported targets. Other providers, local and SSH execution keep their existing path. Retry policy, Stop acknowledgement and lease ownership rules stay the same. The SDK exposes no cancellation handle for an abandoned stream; existing Stop and lease teardown still own command termination. This fix does not establish the original gateway owner or prove recovery in a live environment. ## Model Used OpenAI Codex, based on GPT-6, with reasoning, repository tools and code execution. The exact backend identifier and context window size are not exposed by this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass (changed paths; the independently reproduced local baseline failure is documented above) - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
1ac48aff8d |
perf(ci): pack the verifier's staged packages concurrently (#15888)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Every pull request runs the `Build` check. Its last step is the public npm install verifier, `scripts/verify-grok-npm-install.mjs`, which stages the eighteen public packages, packs them, and installs them in a clean consumer. > - The verifier packs the eighteen packages one after another with `npm pack`. Each pack is a Node start-up plus gzip of that package's files. The timing line added in #15868 shows this loop at 35.6-37.9s on four `Build` runs, the largest part of the staging phase now that the bundled installs overlap. > - `Build` at 433-530s is the slowest check in most PR runs once the server shards drop, so its staging phase sets the wall-clock time of the run. > - Each pack reads only its own `package-<index>` directory and writes one tarball. The runner has four cores and the packs are CPU-bound. There is no reason to run them one after the other. > - This pull request runs the packs as concurrent children through the prefetch helper, one per core, bundled packages first, each into its own directory, and puts the results back into dependency order before every tarball is checked. > - The benefit is a shorter `Build` job, and a timing line that reports the packs' serial cost next to their wall time, so the saving is measured on this PR's own run instead of estimated. ## Linked Issues or Issue Description Refs #15868, #15861. This branch is stacked on the branch of #15868, which is stacked on #15861, because the same execution branch holds both open PRs. The diff here is this PR's one commit. After #15868 merges, this PR is retargeted to `master` and rebased. **What existing behavior does this improve?** The `Build` PR check. Its public npm install verifier packs the eighteen public packages in one serial loop. **Subsystem affected** CI and release scripts (`scripts/verify-grok-npm-install.mjs`, `scripts/prefetch-child.mjs`). **Current behavior** After the bundled installs are joined, the verifier runs `npm pack --pack-destination <root>` for each of the eighteen packages in turn, finds the new tarball by listing the shared root, and checks it with `tar -tzf`. `stage_pack_ms` was 35.8s, 37.1s, 35.6s and 37.9s on the four runs of #15868. The parent is idle apart from the one pack it waits on, and three of the four cores are idle. **Proposed behavior** Start the packs as children through `startPrefetch`, at most one per core (`os.availableParallelism()`), bundled packages first because they make the largest tarballs. Each child packs into its own `pack-<index>` directory, so the tarball a package produced is read from there instead of guessed from a shared directory. Register each child for cleanup as it starts. Put the results back into dependency order, then check every tarball with `tar -tzf` as before, then copy them into the assets directory as before. Report `stage_pack_check_ms`, `pack_concurrency`, `pack_serial_ms` and `pack_longest_ms` on the timing line, and keep `stage_pack_ms` as the whole step. **Reason and benefit** With four slots the step takes about the longest single pack or a quarter of the serial sum, whichever is larger. The expected saving is 20-25s of the 36-38s, measured on this PR's own `Build` job by `pack_serial_ms` against `stage_pack_ms`. **Measured on this PR's first run ([38146108750](https://github.com/paperclipai/paperclip/actions/runs/38146108750), `Build` 433s, verifier 214s):** `stage_pack_ms` 29.9s against 35.6-37.9s before, so the step shed 6-8s, not the 20-25s estimated. `pack_serial_ms` is 44.4s with `pack_concurrency` 4, and `pack_longest_ms` is 28.3s: one pack (the server with its 513 MB bundled closure, gzip level 9 inside npm) runs for almost the whole step, so the step is bounded by that pack and not by the slot count. The seventeen other packs fit inside it. The serial sum is 7-9s higher than the old serial step, which is the four processes contending for the runner's cores. `stage_packages_ms` 75.8s against 80.4-89.6s on #15868's runs; `image_wait_ms` and `grok_wait_ms` stay 0. **Breaking changes** None. The same packages are packed with the same `npm pack` arguments, the tarballs are checked the same way, and the consumer receives them in the same order. ## What Changed - `scripts/pack-staged-packages.mjs` (new): `packStagedPackages` launches the packs in the order given with at most `concurrency` children at once, through the prefetch helper, and resolves with each entry's tarball path and wall time in the same order. Every child is passed to `register` as it starts. The first failure rejects with the tail of that child's log and nothing further is launched. A child that leaves anything other than one tarball in its directory is a failure. `packConcurrency` is one per core. `packTimings` gives the timing-line keys. - `scripts/verify-grok-npm-install.mjs`: each entry gets its `index` and its own `pack-<index>` destination. The pack loop is replaced by one `packStagedPackages` call, launched bundled-first and registered into the `prefetches` list as each child starts, so an earlier failure stops any pack still running before the scratch root is removed. The results are sorted back into dependency order, every tarball is checked in that order, and the copy into the assets directory is unchanged. Only the lifecycle sentinel is still packed in-process. - Timing line: `stage_pack_check_ms`, `pack_concurrency`, `pack_serial_ms` and `pack_longest_ms` are added. `stage_pack_ms` still covers the whole step, so the before and after numbers compare directly. - `scripts/__tests__/pack-staged-packages.test.mjs` (new): five tests with a local stand-in for npm and no network. The default concurrency and the pack arguments; four packs with two slots run the two slow packs together, start a third only once a slot is free, receive the exact `npm pack` argument list, register every child, and come back in entry order with their tarballs and timing keys although the fast packs finish first; a failed pack rejects with its log tail, launches nothing more, and leaves the running pack to be stopped; a pack that produces two tarballs or none is a failure; and no entries resolve at once. - `scripts/__tests__/grok-public-install-sandbox.test.mjs`: the guard test on the verifier source now checks the import, the bundled-first launch with cleanup registration, the reorder before the checks, the checks before the copy, that only the sentinel is packed in-process, that each pack has its own directory, and the new timing keys. - `package.json`: the new test file is in `test:release-registry`. ## Verification - `node --test scripts/__tests__/pack-staged-packages.test.mjs scripts/__tests__/grok-public-install-sandbox.test.mjs scripts/__tests__/stage-bundled-packages.test.mjs scripts/__tests__/prefetch-child.test.mjs`: 35 tests pass (30 on #15868's head plus the five new ones). The new file passes three consecutive runs. - `node --check` passes on both scripts. - With the real npm 11.12.1 on Node 25.9: two small packages packed through `packStagedPackages` with two slots produce one tarball each in their own `pack-<index>` directory, with the expected `package/` listing from `tar -tzf`. - The verifier itself needs Docker and a network, which this workspace does not have. This PR's own `Build` job runs the branch's verifier; its timing line carries `stage_pack_ms`, `pack_serial_ms`, `pack_longest_ms` and `pack_concurrency`, which is the measurement. Before, on #15868's four runs: `stage_pack_ms` 35.8s / 37.1s / 35.6s / 37.9s. - `docker-runner-check.yml` runs the same script after `pnpm build` and needs no change. ## Risks - Four `npm pack` processes run at once and share the host npm configuration and cache. `npm pack --ignore-scripts` reads the package directory and writes one tarball; it does not write to the cache beyond its per-process log file. If a shared-state race appears, the failing pack's log tail is in the error and the fix is to lower the concurrency. - Memory and CPU: four Node processes instead of one on the four-core `Build` runner, for about thirty seconds. The largest pack (the server with its bundled dependencies) is the same process as before. - The pack output moves from the CI log into per-pack log files in the scratch root. A failure reports the last 2000 characters of the failing pack's log; a successful pack's output is discarded with the scratch root. - The tarball checks (`tar -tzf`) now run after all packs instead of after each pack. A bundled Codex payload is still rejected before any tarball reaches the consumer; the only change is that the error arrives a few seconds later. - Low risk otherwise. The bundled installs, the consumer install, and every probe are unchanged. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - Claude Fable 5.1 (Anthropic), model ID `claude-fable-5-1`, run as a Paperclip agent through the Claude Code local adapter with tool use (shell, file edits, GitHub CLI). Reasoning mode: extended thinking. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green (run 38146108750: `server (11/12)` failed on its first attempt on a teardown `EACCES` while removing the per-worker `PAPERCLIP_HOME` after all 74 files and 1257 tests had passed, in a file this PR does not touch; the re-run of that job passed and every check is green) - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>canary/v2026.1011.0-canary.11 |
||
|
|
3d3b8d4371 |
fix(external-objects): distinguish hidden GitHub repos from genuinely-missing objects (#15906)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Issues reference external objects such as GitHub pull requests. The instance resolves their status and shows it in the UI. > - GitHub returns 404 both when a pull request or issue does not exist and when the repository exists but the configured credentials cannot see it. GitHub hides private repos behind a 404 on purpose. > - The resolver treated every 404 as a terminal not-found verdict. It froze references to inaccessible repos forever, and marked them "Not found (Stale)". > - This pull request probes the repository metadata to tell the two cases apart and keep frozen objects re-checkable. > - The benefit is accurate statuses instead of permanent "Not found" labels for repos the instance cannot see. ## Linked Issues or Issue Description No public GitHub issue exists for this report. **What happened?** - The issue properties panel and markdown decoration showed many GitHub pull request and issue references as "Not found (Stale)" even though the objects exist. - GitHub returns 404 for objects the configured credentials cannot access, not only for objects that are truly gone. - The resolver treated every 404 as a terminal not-found verdict, so those references were frozen and showed a misleading "stale" liveness. - Detected-but-unresolved GitHub objects were labeled "Not yet resolved" in inline markdown, which did not match the pill label "Not yet refreshed". **Expected behavior** - A reference to a repo the credentials cannot see should surface as authorization-required, not as a permanent "Not found". - A truly missing object should still be "Not found". - A terminal object (merged, closed, or confirmed not-found) should not be labeled "Stale", because it is never refreshed again. - Inline markdown statuses should match the pill statuses. **Steps to reproduce** 1. Configure GitHub credentials with no access to a private repo. 2. Add a comment or issue description that links to a pull request in that repo. 3. Wait for the external object resolver to run. 4. Observe the reference labeled "Not found (Stale)". **Paperclip version or commit** - `master` at `e733bdd` (before this change). **Deployment mode** - Self-hosted server. ## What Changed - Added a repository visibility probe to the GitHub external object provider. - A 404 for an object now probes the repository metadata with the same credentials: - Repository visible: the object genuinely is gone. Keep the not-found snapshot. - Repository hidden: return an authorization-required result instead of a terminal not-found verdict. - Probe failed (network, rate limit, 5xx): return an unreachable result and retry later. - A rate-limited repository probe (403 with `x-ratelimit-remaining: 0`) is treated as uncertain, not as missing access, so it retries instead of showing a misleading "Requires auth". - Re-check previously frozen GitHub not-found objects on their normal schedule so they self-heal once credentials or repo visibility improve. A frozen object is only re-checked until a probe confirms the repo is visible; once confirmed deleted, it is terminal and no longer polled. - Cleared any terminal verdict when a refresh fails, so a failed refresh does not freeze an object. - Kept terminal objects from displaying the "stale" liveness label. - Matched the inline markdown status label to the pill status label, so detected-but-unresolved GitHub objects say "Not yet refreshed" instead of "Not yet resolved". - Skipped the liveness suffix in markdown links and status icons when it duplicates the status label (e.g. no more "Requires auth (Requires auth)"). - Cached repository visibility probes per company+repo + credential fingerprint, so many objects in the same repo share one probe per refresh cycle instead of each issuing its own — and a verdict obtained under one token is never reused by a later refresh running under a different credential. ## Verification - Added provider tests for visible, hidden, failed, and rate-limited repository probes, per-company/per-repo probe caching, and probe-verified not-found snapshots. - Added service tests for terminal-object liveness, for re-checking frozen GitHub not-found objects, and for ensuring probe-verified not-found objects are never re-queued. - Added UI tests asserting unresolved GitHub objects render as "Not yet refreshed" (not "Not yet resolved") and that failed-first-refresh labels do not repeat the liveness text. - Ran `npx vitest run src/__tests__/external-objects-service.test.ts` in `server/`: 34 passed (incl. provider + service suites). - Ran the external-object markdown/pill/icon suites in `ui/`: 97 passed. - `ci / Typecheck + Release Registry` and all other CI gates pass on this head. Local `tsc` could not run to completion in the execution sandbox (the sandbox memory cap killed it), so CI typecheck is the authoritative gate. ## Risks - Low risk. The change is confined to the GitHub external object provider and the external object service. - A repo that genuinely does not exist and a private repo both return 404 for the probe. Both now surface authorization-required or a retry instead of an immediate not-found freeze, which stays re-checkable and self-heals. - Re-checking frozen not-found objects adds at most one repository probe per affected object until the probe produces a definitive verdict, after which the object is terminal and no longer polled. Probe results are cached per company+repo+credential, so a credential rotation always re-probes instead of inheriting a stale verdict. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - Provider: DeepSeek via OpenRouter, model ID `openrouter/~deepseek/deepseek-v4-flash-latest`, running inside the Paperclip opencode adapter (`opencode_local`). - The model produced and assisted with the reasoning and code changes; the execution workspace, test suite, and git/PR steps above were run by the same agent run. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details — the branch name is assigned by the managed execution workspace and the harness requires keeping it - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green on the current head (all `ci / *`, Greptile, Socket, and review checks pass) - [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: devinfoley <139239+devinfoley@users.noreply.github.com> Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
026bdcd046 |
perf(ci): run the verifier's bundled-dependency installs concurrently (#15868)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Every pull request runs the `Build` check. Its last step is the public npm install verifier, `scripts/verify-grok-npm-install.mjs`, which stages the eighteen public packages and installs them in a clean consumer. > - The verifier stages the packages in one serial loop. Three packages bundle dependencies (`@paperclipai/server`, `@paperclipai/adapter-utils`, `@paperclipai/db`). Each one runs a host-side `npm install` of its bundled closure in-process, one after the other, and nothing in the loop overlaps anything else. The staging phase took 102-116s on recent `Build` jobs. > - `Build` at 433-530s is the slowest check in most PR runs once the server shards drop, so its staging phase sets the wall-clock time of the run. > - The three installs write only their own directories and read nothing from each other. There is no reason to run them one after the other. > - This pull request starts the three installs together as child processes and copies the other fifteen packages while they run. Packing stays serial and in order. > - The benefit is a shorter `Build` job, and a timing line that reports each install, so the saving is measured on this PR's own run instead of estimated. ## Linked Issues or Issue Description Refs #15861. This branch is stacked on the branch of #15861 because the same execution branch holds that open PR. The diff here is this PR's three commits. After #15861 merges, this PR is retargeted to `master` and rebased. **What existing behavior does this improve?** The `Build` PR check. Its public npm install verifier stages the eighteen public packages in one serial loop. **Subsystem affected** CI and release scripts (`scripts/verify-grok-npm-install.mjs`, `scripts/prepare-bundled-package.mjs`, `scripts/prefetch-child.mjs`, `scripts/prefetch-supervisor.mjs`). **Current behavior** The staging loop calls `prepareBundledPackage` in-process for the three packages that bundle dependencies. Each call runs a host-side `npm install` of that package's bundled closure, applies the repo patches, and rewrites the manifest, before the loop moves to the next package. The timing line reports the whole staging phase as one number, 102-116s on recent runs. The three installs are the only network work in that phase. **Proposed behavior** Stage the three bundled packages' sources first, start their installs at the same time as children of the script's own CLI entry (the same path `release.sh` uses), copy the other fifteen packages while the installs run, join the installs, and then pack every package in dependency order as before. Report `stage_sources_ms`, `stage_bundled_ms`, `stage_pack_ms`, `bundled_wait_ms` and one `stage_bundled_<package>_ms` per install on the timing line. **Reason and benefit** The staging phase shortens by the two shorter installs. Measured on this PR's first run (below): the three installs sum to 25.9s and take 16.4s of wall time together, so the overlap removes about 9.5s from the phase, and the timing line now shows that packing (35.8s) and the standalone-package build before the loop (about 27s) are the larger remaining costs. The original estimate of 30-50s was too high because it assumed the installs were about half of the phase; they are a quarter. **Breaking changes** None. The packed tarballs are produced by the same script, in the same order, and are checked the same way. ## What Changed - `scripts/stage-bundled-packages.mjs` (new): `startBundledInstalls` starts one child per bundled package through the prefetch helper from #15849, with the child's output in a log file. `bundledInstallTimings` reads each child's wall time for the timing line. `bundledPackageSlug` maps a package name to its log and timing key. - `scripts/verify-grok-npm-install.mjs`: the staging loop is split. The bundled packages' sources are staged first and their installs start together. The other packages are copied while the installs run. The install handles are pushed into the `prefetches` list at once, so an earlier failure stops any install still running before the scratch root is removed. The join comes before the first `npm pack`. The pack loop walks every package in `needed` order and checks each tarball as before. `prepareBundledPackage` is no longer imported. - Timing line: `stage_sources_ms`, `stage_bundled_ms`, `stage_pack_ms`, `bundled_wait_ms` and `stage_bundled_server_ms`, `stage_bundled_adapter_utils_ms`, `stage_bundled_db_ms` are added. `stage_packages_ms` and the download keys are unchanged, so the before and after numbers compare directly. - `scripts/__tests__/stage-bundled-packages.test.mjs` (new): five tests with local Node children and no network. The slug mapping; the children run the CLI entry `release.sh` runs and never an in-process call; two installs run at the same time, each with its own source and target, and their logs and timing keys are correct (each child exits only after the other has started, so serial children would fail); a failed install rejects with its own log tail while the other is still running and is then stopped; and stopping the installs after one fails also stops the stand-in npm process the other wrapper is blocked on. - `scripts/__tests__/grok-public-install-sandbox.test.mjs`: one guard test on the verifier source. The bundled sources are staged before the installs start, the installs are registered for cleanup before the other packages are copied, the join comes before the pack loop, the pack loop is in order, there is still exactly one package pack loop, and the timing keys are reported. - `package.json`: the new test file is in `test:release-registry`. ### Review fix (second commit): stop a prefetch's whole process tree Greptile pointed out that `stopPrefetches` signalled only the Node wrapper of each install, while the wrapper is blocked in `execFileSync('npm', ['install', ...])`. After one install failed, the other wrapper died and its npm process kept writing into the scratch root the verifier was about to remove. - `scripts/prefetch-child.mjs`: on POSIX every prefetch child is started `detached`, so it leads its own process group. `stopPrefetches` signals the group (`process.kill(-pid, ...)`) and waits until no member of the group exists, with `SIGKILL` after the grace period and a second grace period before it gives up so cleanup cannot hang. On Windows the child is signalled directly as before. - `scripts/__tests__/prefetch-child.test.mjs`: two tests. A stopped prefetch's grandchild is gone after `stopPrefetches` returns, and a `SIGINT` to a separate parent process reaches the grandchild while the parent still exits with `SIGINT`. Both are skipped on Windows. - `scripts/__tests__/stage-bundled-packages.test.mjs`: one test, the cancellation case Greptile asked for. The stand-in `prepare-bundled-package` blocks in `execFileSync` on a stand-in npm that announces its pid; after the other install fails, `stopPrefetches` must leave that pid gone. All three new tests fail against the previous helper (`grandchild <pid> outlived stopPrefetches`, `outlived the parent's SIGINT`, `stand-in npm <pid> outlived stopPrefetches`) and pass with the fix. ### Review fix (third commit): the parent runs no signal handler Greptile pointed out that the second commit forwarded the terminal's signals to the detached groups through a JavaScript handler in the parent. The verifier spends most of its time blocked in `execFileSync` (the standalone builds, every `npm pack`), where no handler can run, and installing the handler also replaced the parent's immediate default termination. So a Ctrl-C during a synchronous phase did nothing until that command returned, and the detached installs and downloads kept running meanwhile. Confirmed with the new test against the second commit's helper: the parent survived the `SIGINT`, exited with code 1 once the synchronous command had died, and its handler never ran. - `scripts/prefetch-supervisor.mjs` (new): every prefetch command now runs under this small Node script as the leader of its own process group. The supervisor holds a pipe from the parent on stdin (which the parent writes to only to request a stop where it cannot signal the group; see the fourth commit). The kernel closes it when the parent exits for any reason, including a signal delivered while the parent is blocked, or `SIGKILL`, and the supervisor then stops its group (`SIGTERM`, `SIGKILL` after the grace period) and says so in the log. It passes group-wide signals on to the command and stays alive until the command exits, so its own exit reports the command's exit code or signal. Run by hand with a terminal or `/dev/null` on stdin it watches nothing. - `scripts/prefetch-child.mjs`: `startPrefetch` spawns the supervisor with the command, with stdin as the pipe. The forwarding handlers are gone; the parent installs no signal handler, so its default termination stays immediate. `stopPrefetches` is unchanged. - `scripts/__tests__/prefetch-child.test.mjs`: the first test asserts the parent has no `SIGINT`/`SIGTERM`/`SIGHUP` listener after a prefetch starts. The "signal to the parent" test now runs for `SIGINT` and `SIGKILL` and checks the supervisor's log line. One new test gives a separate parent its own process group, blocks it in `execFileSync` on a 20s command, and sends `SIGINT` to that group the way a terminal sends Ctrl-C: the parent must die of `SIGINT` within two seconds and the prefetch's grandchild must be gone. Against the previous helper the parent exits with code 1 instead, the grandchild outlives `SIGKILL`, and the handler-count assertion fails. ### Review fix (fourth commit): a deliberate stop on Windows goes through the pipe Greptile pointed out that on Windows, where `startPrefetch` now returns the supervisor, `stopPrefetches` called `child.kill('SIGTERM')` on it. Windows has no process groups and `kill` there ends a process outright, so the supervisor died without running its handler and its command kept running after cleanup returned. - `scripts/prefetch-child.mjs`: without process groups, the graceful stop no longer signals the supervisor. `stopPrefetches` writes a stop request to the pipe the supervisor already watches for the parent's exit (`stdin.end('stop\n')`). Only the `SIGKILL` escalation targets the supervisor itself, after it has had its grace period. POSIX keeps the group signal, so `stopPrefetches` and the existing stop and failure tests are unchanged there. - `scripts/prefetch-supervisor.mjs`: data on stdin is a stop request and EOF is the parent's exit; both end the group the same way (`SIGTERM`, `SIGKILL` after the grace period) and the log line names which one it was. - `scripts/__tests__/prefetch-child.test.mjs`: one new test writes the stop request to a running prefetch's pipe instead of calling `stopPrefetches`. The supervisor must exit with `SIGTERM`, the command's grandchild must be gone, and the log must say `stop requested` and not `parent exited`. The previous supervisor fails it on the log line, since it treated the request as the parent's exit. The Windows kill itself cannot be reproduced on the Linux runners, so the Windows branch of `signalTree` is verified by that supervisor test plus reading its two lines. The stand-in that ignores `SIGTERM` is now skipped on Windows, where `kill` cannot be ignored. ## Verification - `node --test scripts/__tests__/stage-bundled-packages.test.mjs scripts/__tests__/grok-public-install-sandbox.test.mjs scripts/__tests__/prefetch-child.test.mjs`: 30 tests pass (was 19 before this PR, 24 after the first commit, 27 after the second, 29 after the third). The three files pass three consecutive runs. - `node --check` passes on all four scripts. - The verifier itself needs Docker and a network, which this workspace does not have. This PR's own `Build` job runs the branch's verifier. Measured on all four commits' runs ([38132868803](https://github.com/paperclipai/paperclip/actions/runs/38132868803): `Build` 428s, `pnpm build` 112s, verifier step 214s; [38136912842](https://github.com/paperclipai/paperclip/actions/runs/38136912842): `Build` 447s; [38139871529](https://github.com/paperclipai/paperclip/actions/runs/38139871529): `Build` 432s; [38142762538](https://github.com/paperclipai/paperclip/actions/runs/38142762538): `Build` 457s, `pnpm build` 123s, verifier step 231s): | Key | Run 1 | Run 2 | Run 3 | Run 4 | Before | | --- | --- | --- | --- | --- | --- | | `stage_packages_ms` | 80.4s | 84.9s | 81.5s | 89.6s | 102-116s on master runs 38112764439, 38111191543, 38110932348, 38110914319, 38110897745, 38127611057; 84.5s on #15849's run on a runner as fast as these | | `stage_sources_ms` | 1.4s | 0.7s | 0.9s | 0.9s | (inside the number above) | | `stage_bundled_server_ms` / `adapter_utils` / `db` | 16.3s / 5.0s / 4.6s | 14.9s / 3.6s / 3.3s | 16.4s / 5.2s / 4.7s | 17.1s / 4.4s / 3.8s | ran one after another, 21.8-25.9s together | | `stage_bundled_ms` (wall, all three) | 16.4s | 15.0s | 16.5s | 17.2s | 21.8-25.9s | | `bundled_wait_ms` | 15.3s | 14.7s | 16.1s | 16.7s | — | | `stage_pack_ms` | 35.8s | 37.1s | 35.6s | 37.9s | (inside the number above) | | `consumer_install_ms` | 94.4s | 97.8s | 95.7s | 99.8s | 97-134s | | `image_prefetch_ms` / `image_wait_ms` | 33.7s / 0ms | 31.8s / 0ms | 34.7s / 0ms | 33.7s / 0ms | unchanged | | `grok_prefetch_ms` / `grok_wait_ms` | 2.1s / 0ms | 2.1s / 0ms | 2.0s / 0ms | 2.1s / 0ms | unchanged | The overlap removed about 7-10s from the staging phase on each of the four runs (21.8-25.9s of installs in 15.0-17.2s of wall time). The third and fourth commits changed no timing: both waits stay at 0ms and the staging phase sits inside the band of the first two runs. The rest of the phase is the standalone-package build and the server UI dist prep before the loop (about 27-32s) and the eighteen serial `npm pack` plus `tar -tzf` checks (36-37s). - `docker-runner-check.yml` runs the same script after `pnpm build` and needs no change. ## Risks - Three `npm install` processes share the host npm cache at the same time. npm's cache (`cacache`) is designed for concurrent writers, and the three installs have disjoint target directories. If a shared-cache race appears, the failing install's log tail is in the error and the fix is to serialize again. The first CI run showed none. - The install output moves from the CI log into per-install log files in the scratch root. A failure reports the last 2000 characters of the failing install's log; a successful install's output is discarded with the scratch root. - Memory: three concurrent installs on the `Build` runner instead of one. The closures are small (the server's eight bundled packages are the largest), so this stays well under the runner's memory. - Process groups: a detached child no longer receives the terminal's signals on its own. The supervisor stops its group when the parent is gone, which it learns from the kernel closing the stdin pipe, so this holds for a Ctrl-C while the parent is blocked in synchronous work and for `SIGKILL` alike. The parent keeps Node's default signal handling. If the supervisor itself is killed with `SIGKILL` before its command, the command is orphaned and finishes on its own (each install ends within seconds; the image pull within a minute), the same as any orphaned grandchild before this PR. - One extra Node process per prefetch (five per verifier run). Each costs about 40ms to start, inside the time the parent spends staging anyway. - Low risk otherwise. Packing, the tarball checks, the consumer install, and every probe are unchanged. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - Claude Fable 5.1 (Anthropic), model ID `claude-fable-5-1`, run as a Paperclip agent through the Claude Code local adapter with tool use (shell, file edits, GitHub CLI). Reasoning mode: extended thinking. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green on all four commits - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups (on the fourth commit Greptile reviewed 8 files and added no comments; all three earlier threads are resolved) - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Bender (Fable) <bender-fable@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com> Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>canary/v2026.1011.0-canary.10 |
||
|
|
85f4205de3 |
perf(test): reuse a migrated embedded Postgres template across suites (#15861)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Pull requests to this repository run a large CI matrix. The slowest
check sets the time a contributor waits for a green run.
> - After #15853, the twelve `server (n/12)` shards are the slowest
check in every fully green PR run created since its merge (518s to 548s;
`Build` is 482s to 504s). Each shard is about 60s of setup and 454s to
490s of Vitest, with one worker, so its wall time is the sum of its
suites.
> - From two shard logs, 22 to 24 of the 71 to 74 suites in a shard
start an embedded Postgres cluster. Each one runs `initdb`, starts the
cluster, and applies all 331 migrations before its first test. The
smallest such suite takes 6s with one or two trivial tests. On top of
that, the `getEmbeddedPostgresTestSupport` probe starts and stops a
second throwaway cluster at import time in 19 of those suites, which is
why their per-file import overhead is 3.5s to 4.1s against 0.9s to 1.0s
for suites without Postgres.
> - A cluster data directory that was shut down cleanly can be copied
and started again as an independent cluster, and the migrated schema is
the same for every suite.
> - This pull request builds one migrated cluster per host, publishes
its data directory as a template under the OS temp directory, and starts
every later suite from a copy of it. The probe reuses the template
instead of starting a cluster.
> - The benefit is about 7s to 8s per Postgres suite on the CI runners
(measured 3.2s to 0.35s per start and 0.75s to 0.03s per probe locally),
about 120s to 170s per server shard, and a faster local test loop for
every suite that uses the fixture.
## Linked Issues or Issue Description
**What existing behavior does this improve?**
The `server (n/12)` PR checks are the slowest check in every fully green
PR run since 2026-10-11 05:27Z. Their Vitest step runs suites one at a
time, and about a third of each shard's time is embedded Postgres setup
that produces the same migrated cluster every time.
**Subsystem affected**
Test infrastructure: the embedded Postgres test fixture in
`@paperclipai/db` (`packages/db/src/test-embedded-postgres.ts`), used by
349 server suites, the db package suites, and the CLI suites.
**Current behavior**
`startEmbeddedPostgresTestDatabase` runs `initdb`, starts the cluster,
creates the database, and applies all 331 migrations for every suite.
`getEmbeddedPostgresTestSupport` starts and stops a second throwaway
cluster once per test file, because the module-level cache does not
survive Vitest's per-file isolation. Measured locally: 3.2s per suite
start plus 0.4s cleanup, and 0.75s per probe. On the CI runners the
smallest Postgres suite takes 6s and the probe adds 2.5s to 3s of import
time per file.
**Proposed behavior**
The first caller on a host builds one migrated cluster, stops it, and
renames its data directory to
`$TMPDIR/paperclip-embedded-postgres-templates-<uid>/<key>`. Every later
caller copies the template (about 90 MB) and starts Postgres on the
copy, then still runs `ensurePostgresDatabase` and
`applyPendingMigrations`, which are no-ops on a current template. The
probe treats a published template as proof that the host can run the
cluster. The key hashes the embedded-postgres version, the initdb flags,
the user, and the name and content of every migration file plus the
journal, so a changed migration builds a new template.
`PAPERCLIP_TEST_POSTGRES_TEMPLATE=0` bypasses the cache. Any template
failure falls back to the fresh path.
A template is a Postgres data directory, and starting it executes its
configuration, so the cache is private to the current OS user: the root
name carries the uid, the root is created with mode 0700, and both the
root and each template are used only when `lstat` shows a real directory
(not a symlink) owned by the current user with no group or world write
bit. A root that fails those checks (for example one pre-created by
another user in a shared `/tmp`) is left alone and the process uses
fresh clusters.
**Reason and benefit**
The slowest check decides the wall-clock time of every PR run. Measured
locally, a template start takes 0.3s to 0.4s instead of 3.2s, cleanup
0.15s instead of 0.43s, and the probe 0.03s instead of 0.75s. Four
server suites that passed in both modes dropped by 3.4s to 6.0s each;
the db `backup-lib` suite dropped from 42.7s to 20.9s. The CI runners
are about twice as slow, so the expected saving per server shard is 120s
to 170s.
**Breaking changes**
None. The returned database is fully migrated in both paths, each suite
still gets its own cluster and data directory, and a failure on the
template path falls back to the existing fresh path.
## What Changed
- `packages/db/src/test-embedded-postgres.ts`: adds the template cache.
`computeEmbeddedPostgresTemplateKey` hashes the inputs that determine a
migrated cluster. `buildEmbeddedPostgresTemplate` starts a fresh cluster
through the existing bounded retry, creates the database, applies
migrations, waits for the real `stop()` (30s bound), and publishes the
data directory with an atomic rename; a concurrent builder that loses
the rename discards its copy.
`startEmbeddedPostgresFromTemplateWithRetry` copies the template into a
fresh temp directory, sets mode 0700, and starts the cluster with the
same per-attempt port and directory rotation as the fresh path.
`startEmbeddedPostgresTestDatabase` tries the template first and falls
back to the fresh path with one warning per process.
`probeEmbeddedPostgresSupport` returns supported when a template exists
or can be built, and keeps its throwaway-cluster path for the case where
the template could not be published. Templates for other keys are pruned
after seven days. The connection strings and initdb flags move into
shared constants; no other behavior changes.
- `packages/db/src/test-embedded-postgres.test.ts`: two unit tests for
the key (stable for identical inputs; changes with migration content, a
new file, flags, version, or user; hashes this package's real migrations
directory) and two real-cluster tests (the published template is
complete and private, a suite starts from it in `template` mode with
migrations up to date, a table written to one copy is not visible in the
next copy, and `PAPERCLIP_TEST_POSTGRES_TEMPLATE=0` runs the `fresh`
path). The real-cluster tests skip on hosts without embedded Postgres,
like every other fixture suite.
- `doc/DEVELOPING.md`: a short section under Test Commands describing
the template, its key, and the bypass variable.
**Review fixes (second commit), one per finding:**
- Private cache root (Greptile P1, Superagent P2). The root is
`$TMPDIR/paperclip-embedded-postgres-templates-<uid>` (user name on
Windows), created with mode 0700. `prepareTemplateRootDir` and
`isUsableTemplate` both reject, via `lstat`, anything that is a symlink,
not a directory, owned by another uid, or writable by group or others; a
rejected root logs one warning and the process uses fresh clusters. A
root created by the first commit with a wider mode is narrowed to 0700
if this user owns it.
- Template failures never skip suites (Greptile P1).
`getEmbeddedPostgresTemplateDir` no longer rejects. A build or root
failure resolves `null` with one warning, and
`probeEmbeddedPostgresSupport` then runs the throwaway-cluster probe, so
only a cluster that cannot start reports `supported: false`. A migration
that cannot apply fails the suite on the fresh path instead of being
hidden by `describe.skip`.
- Idle-based pruning (Greptile P2). Each reuse refreshes the template's
timestamp with `utimes`, so the seven-day prune measures idle time. A
template that disappears after a process resolved its path is not
retried five times: `startEmbeddedPostgresFromTemplateWithRetry` checks
the template before each copy and throws at once, the suite falls back
to a fresh cluster, and the fallback forgets the path so the next caller
in that process rebuilds the template.
- The template-path test enables the cache for itself and restores the
original `PAPERCLIP_TEST_POSTGRES_TEMPLATE` value afterwards (Greptile
P1), so the suite passes under `PAPERCLIP_TEST_POSTGRES_TEMPLATE=0` as
well.
- Eight new tests: `isUsableTemplate` accepts a private cluster
directory and rejects a missing `PG_VERSION`, a leftover
`postmaster.pid`, a symlink, and (POSIX) group- or world-writable modes;
a world-writable pre-created root and a symlinked root both fall back to
fresh clusters, publish nothing, log one warning, and still report
`supported: true` from the probe; a ten-day-old template is refreshed on
reuse; a pruned template is rebuilt on the next start; and a template
build that fails after the cluster started falls through to the fresh
probe. The tests that need an empty cache point `os.tmpdir()` at a
scratch directory (`TMPDIR`, `TEMP` and `TMP` together, so the redirect
holds on Windows as well) and restore it.
**Review fixes (third commit,
|
||
|
|
4b59e2a2ec |
fix(server): allow idle sleep with saved AI auth and verified lease history (#15897)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Automatic idle sleep must preserve accepted work, cleanup and recovery. > - Saved AI login connections are configuration without independent workers. > - Failed ephemeral leases can remain after their resources have stopped. > - Detached subscription observations and quota refreshes can outlive a response. > - This pull request distinguishes inert configuration and verified history from unfinished work. > - The benefit is safe idle eligibility while keeping credentials and audit history. ## Linked Issues or Issue Description **What happened?** An active saved AI login blocks idle sleep after its login and agent runs finish. A failed ephemeral lease also blocks after successful cleanup and provider termination. The connection guard currently hides detached quota and subscription work that can outlive the request that started it. **Expected behavior** An idle instance can sleep with saved AI credentials and verified failed-lease history. Accepted refresh work, recovery journals, unfinished login sessions, pending cleanup and run accounting must still block. **Steps to reproduce** 1. Save an AI connection with purpose `ai`, transport `runtime_auth` and status `active`. 2. Finish login and all agent work, then acquire an owned idle drain. 3. Read the idle safety report. The saved connection alone reports background work. 4. Repeat with a failed ephemeral lease whose exact provider termination and finished run are recorded. Its history also reports background work. **Paperclip version or commit** Based on `0bbd586a2f6`, after updates from master. **Deployment mode** Single-process managed or self-hosted server using the owned idle-drain protocol. Related work: #15522, #15761 and #15851. Searched open and closed pull requests for related idle-sleep changes. The open accounting recovery work in #15879 is separate; this change does not settle or discard accounting debt. ## What Changed - Exempt active saved AI runtime-auth configuration from the connection sleep check. Other connection types and unknown states keep their existing checks. - Track detached subscription discovery and provider observations until their actual promises settle. - Track underlying quota work through an HTTP timeout, including an OAuth exchange and the save of its replacement credential. - Inspect the credential-recovery directory without reading credential bytes. Journals, probes, temporary files, unexpected entries and read failures keep sleep blocked. Empty company directories left by successful recovery permit sleep. - Exempt a failed ephemeral lease only after successful cleanup, a release timestamp, an exact company/run/lease/resource termination receipt, and a finished run without retry or accounting debt. Export and cleanup intents still block. - Use the same failed-lease proof when checking finished login-session cleanup. - Add PostgreSQL and filesystem regression tests and document the behavior. ## Verification - Four focused suites passed: 186 tests covering durable guard decisions, local recovery, detached subscription work and quota timeouts. - `pnpm build` passed. - `pnpm -r typecheck` passed. - `pnpm --filter @paperclipai/server build` and a final server TypeScript check passed after the directory-scanner cleanup. - The final directory-scan recheck passed all 18 tests. - The original-base `pnpm test:run` attempt found three local failures. It was stopped before completion to update the branch after two master changes. All three also reproduce on unchanged master `e733bddc45e`: two Git default-branch fixtures in `workspace-runtime.test.ts` and one native runner recovery fixture in `native-session-resume.test.ts`. - Both Git fixtures pass with a command-scoped `init.defaultBranch=master` setting. The native fixture fails with the same result on both branches. No global Git setting was changed. - All CI gates passed on the final head `ff63497d9bef40f5f00567bc729fb17536cfe136`: [complete CI run](https://github.com/paperclipai/paperclip/actions/runs/38156312722). The only skipped checks are the two path-gated Storybook jobs. - Greptile Apex scored the final head 5/5 with no actionable findings and no unresolved review threads. The branch has no merge conflicts with master. - After the master update, `pnpm build` and `pnpm -r typecheck` passed again. All four focused suites passed again: 186 tests. - Staging deployment and live sleep/wake verification are pending the Core merge and the verified private Cloud build. No production rollout has been started. ## Risks - The failed-lease exception is deliberately narrow. Older records without exact termination evidence or a complete run receipt continue to block. - Pending OAuth recovery blocks sleep until it is recovered. No credential, history, cleanup obligation or accounting receipt is deleted. - The existing owned drain, ingress fence, local-work generation checks, plugin drain and backup checkpoint remain required. - No schema migration or provider mutation is introduced. Roll back to the previous application release to restore the conservative guard behavior. ## Model Used OpenAI GPT-6 (Codex), with repository inspection, code editing, test execution and read-only provider analysis. No additional model version or context-window size was supplied by the runtime. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either linked existing issues or described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal ticket id or instance-derived details - [x] I have run focused tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented risks - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no unresolved comments - [x] I will address all reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.1011.0-canary.9 |
||
|
|
0bbd586a2f |
fix(server): wait for workspace restore before shutdown lease release (#15857)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Server shutdown must stop agent execution and preserve its files. > - Legacy adapters export workspace and instruction changes during settlement. > - Shutdown released their sandbox leases before this export finished. > - The provider closed command access, so the later Git export failed. > - This change stops an owned adapter through its cancellation control and waits for settlement before release. > - A bounded timeout retains the exact sandbox source behind the existing repair gate. ## Linked Issues or Issue Description **What happened?** A server restart interrupted a remote legacy run. Shutdown began releasing its sandbox lease while the adapter was still stopping its provider and restoring files. The sandbox provider closed command admission. The adapter's later Git export failed with `workspaceRestoreFailure: restore_failed`, leaving the task held for workspace repair. **Expected behavior** Shutdown keeps the exact lease available through provider stop, instruction collection, and workspace restore. Cleanup follows adapter settlement. A restore that exceeds the bounded deadline keeps its exact source for repair before later execution. **Steps to reproduce** 1. Start an owned legacy adapter with a sandbox workspace. 2. Send SIGTERM to the server while the adapter is active. 3. Delay adapter settlement until after the shutdown terminal write. 4. On the base branch, shutdown releases the lease without cancelling or joining the adapter. Its later export can encounter closed provider admission. Related: #14311 defines the provider-stop boundary for file collection. #15042 closes run admission during shutdown. This fix orders sandbox release after the adapter's existing file settlement. ## What Changed - Cancel each owned legacy adapter and join all affected runs within one shared 30-second window. - Commit a durable fence on the exact sandbox before exposing interruption. Other cleanup workers defer while export is pending. - Permit the exact aborted owner's stop-only request so shutdown does not block provider termination. - Reload current execution evidence after settlement. Release leases even when the adapter's ordinary terminal write wins the shutdown race. - Claim the current fence before recording timeout retention. An old snapshot cannot create a repair hold after successful settlement. - Recover fenced sources after controller loss, including released stop receipts. Block reuse or destruction of the physical allocation while file recovery is required, including direct resume and destroy calls with historical lease snapshots. Convert failed ordinary cleanup into stop-only recovery after its existing attempt settles. - Allow 90 seconds for shutdown in shipped Compose services. Preserve native runner restart ownership and unregistered process behavior. - Add database regressions for restore order, timeout, concurrent cancellation, another cleanup worker, controller loss, stale snapshots, stop-only settlement, and completion races. Document the shutdown order. ## Verification - The original ordering regression failed on the base implementation. - Latest recovery and environment runtime suites: 190 tests passed. The other four focused suites passed 136 tests, covering run control, shutdown, file repair, server startup, and environment runtime. - Server typecheck passed. Full `pnpm -r typecheck` and `pnpm build` passed on the latest head. - `pnpm test:run` was run locally. Its general-server group passed 18,559 tests and failed seven, with one unhandled error. The long run started before the review fixes, then loaded newer regression cases against older transformed modules. The current focused suites pass in a fresh process. The damaged-prior-epoch native-session-resume failure also reproduces on unchanged base commit |
||
|
|
5847f047c7 |
fix(connections): retire requests for already available tools (#15856)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Connection cards let users provide the access an agent needs. > - Users can complete setup in Apps while an earlier card stays pending. > - A stale card can then cancel recovery despite valid existing access. > - This change verifies existing access and expires the obsolete request. > - It records a system outcome and sends one durable continuation. > - Approval, identity, permission, and workspace recovery gates remain in place. ## Linked Issues or Issue Description **What happened?** An ordinary tool connection request stayed pending after the addressed user connected the service in Apps. A later conversation retry was cancelled with `issue_waiting_for_response`. **Expected behavior** A request expires when the same user and requesting agent already have usable access. The agent gets a durable tool refresh. The system records no new human approval. **Steps to reproduce** 1. Have an agent request a tool connection on an active task. 2. Connect the service through Apps with the addressed user's existing grant, agent install, and allowed tools. 3. Let a conversation run stop and retry. The pending card still blocks the retry on the base branch. Related: #12345 introduced the connection request flow. This fix uses its existing access checks and delivery queue. ## What Changed - Verify company, task ownership, user identity, membership, health, grants, installs, and task tool policy before expiring an ordinary request. - Preserve AI account, channel, additional permission, and upstream provider requests on their existing paths. - Save the system outcome, audit event, and continuation in one transaction. Notify an idle delivery worker when a request commits. - Recheck access and other questions or approvals before delivery, retry, final dispatch, and native projection. Refresh system observations for an authorized replacement account. - Rearm delivery when access changes before provider work starts. Verify the host's admission receipt against execution evidence before allowing retry without a repair incident. - Require the current delivery claim when marking an attempt complete. An old completion cannot overwrite a rearmed continuation. - Add embedded-Postgres regressions for permissions, policy, concurrency, restart, replacement accounts, both dispatch gates, and the delivery race. Keep genuine workspace repair holds blocking. ## Verification - Latest-head connection, wake-context, dispatch, and legacy recovery suites: 124 tests passed. The connection suite includes 44 real-Postgres cases. - Server typecheck passed. Full `pnpm -r typecheck` and `pnpm build` passed on the latest head. - `pnpm test:run` was run locally. Its general-server group passed 18,583 tests and failed 20. That long run started before the review fixes, then loaded new test cases against modules transformed before those fixes. The affected latest-head suites pass in a fresh process. - One full-run failure also reproduces on unchanged base commitcanary/v2026.1011.0-canary.8 |
||
|
|
abcd017898 |
Reject concealed managed publications in anonymous roots
Check the readable repository namespace independently of historical aliases, including existing reservations. Preserve unavailable unrelated folders and allow asynchronous chat edit verification its established polling budget. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
fa31e4ade7 |
Preserve native recovery transport and legacy workspace bindings
Fence host roots only for local and sandbox execution targets. Accept verified run-ID bindings for older native tasks and avoid unrelated historical path failures without weakening pending-acquisition guards. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
d66a2de539 |
Fence repository acquisition by durable physical root
Preserve empty-inventory admission and immutable recovery. Reserve managed or anonymous roots before acquisition and fence legacy recovery using its verified host binding. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
0e0557f123 |
Fix repository trust and settled workspace recovery guards
Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
fed4802b72 |
Merge origin/master and integrate environment selection
Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
e733bddc45 |
perf(chat): stop polling provider action and session sync queues (#15822)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Chat stores pending provider actions in durable database rows. > - Three action queues still scan every second when empty. > - These scans keep idle databases active. > - This pull request wakes those queues after a commit or at their next deadline. > - Empty queues stop querying while existing recovery rules remain in place. ## Linked Issues or Issue Description Refs #15808, #15802, #15625. Related sleep work: #15750, #15757. **What existing behavior does this improve?** Provider messages and notices, accepted GitHub webhook receipts, and Slack session status synchronization. **Current behavior** Chat maintenance scans these three queues every second. It also rechecks paused Slack sync rows every 30 seconds. **Proposed behavior** Reuse the transaction-aware action workers and existing chat_actions rows. Wake after commits. Restore unfinished work on startup. Use stored retry and stale-claim deadlines. Stop scanning when empty. Park paused Slack sync rows until an endpoint change. ## What Changed - Add three topics to the existing action worker registration. No tables, migrations, triggers, or external services. - Signal producers, claims, retry updates, terminal settlements, and operator resolution inside their owning transaction. Provider I/O remains outside transactions. - Share pending-state and deadline predicates between dispatch and scheduling. Exclude terminal history and uncertain provider sends. - Preserve GitHub receipt attempt fences and provider-send quarantine. Preserve Slack's 30-minute refresh while a session is processing. - Remove these workers from periodic app maintenance. Update the sleep inventory and add regression coverage. ## Verification - Passed: 13 focused tests across scheduler, app wiring, and real PostgreSQL integration. Covers actual commit wakes with deferred fast paths disabled, GitHub retries and startup recovery, stale-send quarantine, Slack publication-to-sync delivery, paused deadlines, and idle queues. - Passed: pnpm typecheck. - Passed: full chat integration and related coordinator suites, **1,274 tests across five files**. - Passed: pnpm build. - Greptile: **5/5**, no actionable findings, completed on `dc284cd275e5f6df624d60b27954421f2bcdbee2`. - Hosted CI: **54 checks passed**, two intentionally skipped Storybook checks, no failures or pending checks on `dc284cd275e5f6df624d60b27954421f2bcdbee2`. Provider responses are simulated. PostgreSQL, transactions, service dispatch, and scheduling are real. No live Slack or GitHub App was used. ## Risks - Writers must emit the existing process-local transaction signal. Independent database clients and direct SQL still need explicit integration or startup recovery. - Accepted GitHub receipts are covered. Finding callbacks GitHub never delivered still uses the separate provider recovery lane. - Other chat maintenance and persistent transports still run. This does not establish whole-instance sleep. - Existing claim, revision, credential, and authorization checks remain authoritative during concurrent foreground and scheduled dispatch. ## Model Used OpenAI Codex (GPT-6), with repository inspection, code execution, and automated tests. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [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.1011.0-canary.7 |
||
|
|
30e338fba6 |
refactor(heartbeat): extract environment selection (#15825)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Heartbeat execution selects an environment before it prepares a workspace and starts an agent. > - Workspace preparation now has its own module, but its selection policy still occupies the executor. > - Environment defaults, recovery bindings, chat isolation, and workspace contention must retain their order. > - This pull request moves that selection phase behind one typed function. > - The executor keeps dispatch gates and lease cleanup. Tests cover the public path and the new boundary. ## Linked Issues or Issue Description Refs #15807. This continues the heartbeat extraction series after workspace preparation. **Current behavior** The 5,599-line heartbeat service contains a 301-line environment selection phase inside run execution. **Proposed behavior** Move that phase to `heartbeat/environment-selection.ts`. Keep the same policy, statement order, errors, and public exports. **Reason and benefit** Readers can find selection policy in one module and follow the executor's phases without reading each policy branch. ## What Changed - Add `selectHeartbeatEnvironment` with typed run, task, policy, configuration, and service inputs. Return the values needed by configuration and workspace preparation. - Preserve native recovery bindings, environment precedence, managed sandbox enforcement, Kubernetes provisioning, native chat isolation, and shared workspace contention. - Reduce `heartbeat.ts` from 5,599 lines / 234,599 bytes to 5,314 lines / 220,784 bytes. The new module is 402 lines / 18,215 bytes. - Add six public-path characterization cases before moving production code and six boundary cases. Repair plugin-test teardown to drain active execution and call the database cleanup helper. - Assert the selected local environment in the browser fixture's real worker and document the new boundary. ## Verification - Before the move: 71 tests passed, including all six new public-path cases. Three plugin tests skipped when the host could not start their database probe. - After the move: all 112 focused tests passed across eight files, with no skips. These cover environment policy, local and plugin execution, contention, recovery bindings, workspace preparation, chat isolation, and low-trust directories. - Passed `pnpm -r typecheck` and `pnpm build`. - A normalized TypeScript AST comparison confirms the 301 moved lines and remaining executor statements are unchanged, apart from the new call and imports. - Browser acceptance: the task UI recovered automatically and the worker verified its selected local environment, preserved uncommitted files, and completed the task. The persistent-failure case passed in isolation: three total runs, then a blocked task that remained blocked after reload. - The first persistent-failure browser attempt missed the retry banner within five seconds, despite a persisted scheduled retry. Its unchanged isolated rerun passed. The trace is retained; the intermittent UI failure is not yet attributed to a root cause. No assertion or timeout was relaxed. - Greptile reviewed `20953dd164decb565bc8d85081aad31a23fd33e5`: 5/5, no actionable findings. - Hosted CI passed on `20953dd164decb565bc8d85081aad31a23fd33e5`: 52 successful checks and two intentionally skipped Storybook checks, with no pending or failing checks. This includes all general and serialized test groups, Runner verification, eight browser shards, typecheck, and build. - The duplicate local `pnpm test:run` was interrupted after hosted CI completed. It is not reported as a local full-suite pass. Local completed checks are the 112 focused tests, typecheck, build, and browser cases described above. ## Risks - This selection phase enforces execution restrictions. A wrong input or import could select the wrong environment or change retry behavior. Public-path tests cover refusal, redirect, contention, and dispatch; boundary tests cover recovery and isolation. - The helper can provision a managed environment row and add concurrency notes to the supplied task context. It acquires no execution lease. The executor retains its catch/finally boundaries and resource ownership. - Kubernetes selection is tested with real database state and bootstrap settings. These checks do not claim execution against a live Kubernetes cluster or hosted sandbox provider. - No schema, migration, API contract, or intended behavior change. ## Model Used OpenAI Codex (GPT-6), with repository inspection, code editing, compiler checks, database integration tests, and browser automation. The exact serving model ID and context-window size are not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.1011.0-canary.6 |
||
|
|
de9ab8e7e0 |
perf(runner): compile the TypeScript surface once per build (#15853)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Pull requests to this repository run a large CI matrix. The slowest check sets the time a contributor waits for a green run. > - After #15849 hid the verifier's downloads behind package staging, the `Build` lane is still the slowest check in five of the eight fully green PR runs created after that merge (535s to 581s; the top server shard is 529s to 560s in the other three). `Build` is about 100s of setup, 160s to 176s of `pnpm build`, and 265s to 312s of the public install verifier. > - Inside `pnpm build`, every package after `@paperclipai/shared` waits on the Runner: its `build` script takes 76s to 80s, and for 65s of that it is the only job running. The chain runs `build:typescript` three times: once directly, then again inside `check:semantic-contracts` and `check:runner-workflow-traceability`, which rebuild first so they work standalone. The server's `prepare:runner-vendor` then runs the whole Runner build a second time, so the job pays for four redundant TypeScript passes. > - Each pass is two `tsc` projects, the eval-kernel rebuild, and the schema checks, 7s to 8s on the CI runner. The dist they rebuild is the dist the first pass just wrote; nothing between the passes changes it. > - This pull request calls the two check scripts directly after the chain's own `build:typescript`, the way `generate-replay-goldens.mjs --check` already does in the same line. The standalone `check:*` scripts keep their rebuild. > - The benefit is about 30s off the `Build` job's critical path, about 15s off the Typecheck job and every Docker or release build that runs the server build, with no change to what is checked. ## Linked Issues or Issue Description **Describe the enhancement** The `Build` PR check is the slowest check in most green PR runs. Its `pnpm build` step compiles the Runner's TypeScript surface four times: three passes inside one Runner `build`, and a second full Runner `build` from the server's `prepare:runner-vendor`. Only the first pass produces a dist that the later steps read; the other three rewrite the same output. **Why is this needed** The slowest check decides the wall-clock time of every PR run. In the eight fully green PR runs created after 2026-10-11 04:10Z, `Build` topped five of them at 535s to 581s. The Runner build sits alone on the `pnpm build` critical path for about 65s, and the server build repeats it. From the job logs of four of those runs, the second and third `build:typescript` passes cost 7s to 8s each, in both Runner builds, so about 30s per job. **Proposed solution** In the Runner's `build` script, replace `pnpm run check:semantic-contracts && pnpm run check:runner-workflow-traceability` with `node scripts/generate-semantic-contracts.mjs --check && node scripts/check-runner-workflow-traceability.mjs`. Both scripts read `dist/`, which `build:typescript` wrote earlier in the same chain, and the steps between them are `--check` runs that write nothing to `dist/`. Add a guard test so the chain compiles TypeScript exactly once and both checks still run after it. **Alternatives considered** - Skip the server's `prepare:runner-vendor` rebuild when `pnpm -r build` already built the Runner. That rebuild is what makes `pnpm --filter @paperclipai/server build` correct standalone (the Dockerfile uses it), and the server's build-script test pins the command. A freshness check would be a larger semantic change; this PR shrinks the rebuild instead of removing it, and the next cycle can measure what remains. - Make `tsc` incremental across the passes. It would still re-run the schema checks and the eval-kernel build each time, and would add build-info files to the package. - Change `check:semantic-contracts` and `check:runner-workflow-traceability` themselves to stop rebuilding. They are documented standalone commands (`pnpm --dir packages/paperclip-runner check:semantic-contracts` in the capability docs) and must work from a stale dist; unchanged here. ## What Changed - `packages/paperclip-runner/package.json`: the `build` script runs `node scripts/generate-semantic-contracts.mjs --check` and `node scripts/check-runner-workflow-traceability.mjs` directly, after its own `build:typescript`, instead of the `check:semantic-contracts` and `check:runner-workflow-traceability` wrappers that rebuild first. Every other step, its order, and the standalone `check:*` scripts are unchanged. - `server/src/__tests__/server-package-build-script.test.ts`: a new test in the suite that already pins the server's Runner vendoring reads the Runner's `build` script, resolves every `pnpm run` step, and asserts that exactly one step compiles TypeScript (directly or through a wrapper), that both check scripts are in the chain, and that both come after `build:typescript`. It fails against the previous script with `expected [ 'build:typescript', …(2) ] to deeply equal [ 'build:typescript' ]`. ## Verification - `pnpm --filter @paperclipai/server exec vitest run src/__tests__/server-package-build-script.test.ts`: 9 pass (1 new). With the `package.json` change stashed, the new test fails as described above and the other 8 pass. - In `packages/paperclip-runner`, the new chain without the cargo step (`check:protocol-manifest`, `build:typescript`, `check:capability-contract`, `check:capability-inventory`, `check:protocol-coverage`, `generate-semantic-contracts.mjs --check`, `check-runner-workflow-traceability.mjs`, `generate-replay-goldens.mjs --check`, `generate-semantic-action-catalog.mjs --check`) exits 0: `Runner stress traceability passed: 44 findings, 12 workflows, 1 explicit exclusion.` and `Replay golden snapshots and parity summaries are current.` Cargo is not installed on the host that produced this PR, so `build:binary` was not run locally; it is unchanged and runs in this PR's `Build` job. - `pnpm run build:typescript` alone takes 5.8s to 5.9s on this host, so each removed pass is in the range the CI logs show. - This PR's own `Build` job runs `pnpm build` from the branch checkout, so its log shows the new chain in both Runner builds. Expected on that job: `pnpm build` about 130s to 146s (was 160s to 176s on the four measured runs), `Build` about 505s to 551s (was 535s to 581s). **Measured on the four post-#15849 `master` runs that motivated this PR** (`Build` job logs): | Run | `pnpm build` | Runner build in `pnpm -r build` | Extra `build:typescript` passes in it | Runner rebuild in server build | Extra passes in it | | --- | --- | --- | --- | --- | --- | | 38112764439 | 169s | 76s | 04:51:52 and 04:52:00 (about 14s) | 04:53:12 to 04:53:41 | 04:53:26 and 04:53:34 (about 15s) | | 38111191543 | 176s | 79s | 04:22:40 and 04:22:48 (about 14s) | 04:24:04 to 04:24:34 | 04:24:18 and 04:24:26 (about 15s) | | 38110914319 | 160s | 75s | 04:19:07 and 04:19:14 (about 14s) | 04:20:25 to 04:20:53 | 04:20:39 and 04:20:46 (about 14s) | | 38110897745 | 172s | 80s | 04:15:16 and 04:15:25 (about 15s) | 04:16:41 to 04:17:11 | 04:16:55 and 04:17:03 (about 15s) | ## Risks - Low risk. The same two checks run against the same dist, in the same position in the chain. The only difference is that the dist is not rewritten twice between them. - If a future step between `build:typescript` and the two checks starts writing to `dist/`, the checks would read that output instead of a fresh rebuild. The steps there today (`check:capability-contract`, `check:capability-inventory`, `check:protocol-coverage`) are `--check` runs that only compare generated spec files. - The standalone `check:semantic-contracts` and `check:runner-workflow-traceability` scripts still rebuild first, so documented usage is unchanged. - The guard test lives in the server's suite because that suite already pins the Runner vendoring contract and runs in the server shards on every PR. ## Model Used - Claude (Anthropic), model ID `claude-fable-5-1`, running as a Paperclip agent through the Claude local adapter with tool use (shell, file edits, GitHub CLI). The agent collected the CI data, made the change, ran the tests, and wrote this description. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>nightly/v2026.1011.0-nightly.0 canary/v2026.1011.0-canary.5 |
||
|
|
b36960b758 |
fix(server): permit verified question-delivery history to sleep (#15851)
Treat completed question-response deliveries as history only after their exact same-company handoff finishes. Preserve missing evidence, retries, accounting debt, and idle-sleep fences. Add a concurrent all-state question history index with interrupted-build repair. Keep migrations serialized without waiting snapshots that deadlock concurrent index builds. Validated by 118 focused sleep tests, six migration/snapshot tests, full repository typecheck, all CI gates, and Apex 5/5. Co-Authored-By: Paperclip <noreply@paperclip.ing>canary/v2026.1011.0-canary.4 |
||
|
|
42ecc96556 |
perf(ci): prefetch the consumer image and Grok binary during package staging (#15849)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Pull requests to this repository run a large CI matrix. The slowest check sets the time a contributor waits for a green run. > - After #15826 leveled the server shards and #15831 split `workspaces-b`, the `Build` lane is the slowest check: 634s in run 38107578322 (2026-10-11 03:11Z), ahead of the next server shard at 574s. It was 533s in run 38107430861 and 543s to 568s in earlier runs that night. > - `Build` is about 100s of setup, 148s to 195s of `pnpm build`, and 278s to 320s of `scripts/verify-grok-npm-install.mjs`. The verifier stages and packs the public packages first (84s to 100s of CPU work), and only then pulls the pinned `node:24-trixie` consumer image (8 layers, 440 MB compressed) and downloads the Grok binary (65 MB gzip, 166 MB verified). Both network waits sit after the staging work on the critical path. > - Neither download depends on the packed packages. The image is pinned by digest and the Grok binary is checked against its pinned sha256 by the provisioning script, so starting them early changes no verified property. > - This pull request starts both downloads as background children before staging and joins them where they are needed: the image before the first sandbox, the binary after the negative probe. It also prints one timing line per phase so the next slow part is visible in the CI log. > - The benefit is that the whole pull and the whole Grok download hide behind the staging phase. The verifier should shed the full pull time on a fresh runner, and the next cycle can read the phase split instead of inferring it from log gaps. ## Linked Issues or Issue Description **Describe the enhancement** The `Build` PR check is now the slowest check in the run. Its last step, the public npm install verifier, pulls the consumer Docker image and downloads the Grok binary only after it has finished staging and packing the eighteen public packages. Both downloads could run while the packages are staged. **Why is this needed** The slowest check decides the wall-clock time of every PR run. In the most recent fully green run (38107578322), `Build` took 634s: `pnpm build` 195s and the verifier 320s. The verifier's log shows the staging phase ends at +100s and the Grok result arrives at +267s. The image pull and the Grok download are inside that second window, in series with the consumer install and the probes. **Proposed solution** Start `docker pull --platform linux/amd64 <pinned image>` and `provision-grok.mjs` as background children at the top of the verifier, before the staging loop. Await the image right before the first sandbox call. Provision Grok into a prefetch directory and rename it into `native/grok` only after the "npm must not provision Grok" assertion and the negative probe, so that assertion keeps its meaning and only the positive probe sees the binary. Print a `paperclip.public-npm-install.timing.v1` line with wall time per phase and the time the main flow waited on each prefetch. **Alternatives considered** - Run the verifier as its own job in parallel with `Build`. It needs the full `pnpm build` output, so that job would be as long as `Build` is now. - Warm the consumer npm cache from an Actions cache. That changes the "clean public npm install" property the verifier reports. Not done here; the new timing line will show whether the consumer install is worth that discussion. - Pack the eighteen packages concurrently. The three bundled packages run their own npm installs in series inside `prepareBundledPackage`, which is synchronous. A larger restructure; left for a later cycle with timing data. - Overlap the Pi runtime setup with the Grok probes. Both use the same consumer directory from separate containers, and the probes after provisioning are short. Not worth the concurrency on shared state. ## What Changed - `scripts/prefetch-child.mjs` (new): `startPrefetch({ name, cmd, args, cwd, env, logPath })` spawns a background child with no inherited terminal and its stdout and stderr in a log file, and returns `{ name, child, done, durationMs }`. `done` resolves on exit 0 and otherwise rejects with the tail of the log; the rejection is observed inside the helper, so an unawaited failure never becomes an unhandled rejection. `durationMs` ends at the child's last write to its log (the exit event is observed late when the caller is blocked in synchronous work; both downloads print their final status line as they complete). `stopPrefetches(prefetches, { graceMs })` sends `SIGTERM` to every child still running, escalates to `SIGKILL` after the grace period, and resolves only once all of them have exited. - `scripts/verify-grok-npm-install.mjs`: the image pull and the Grok provisioning start through the helper inside the `try` block, before the staging loop. The main flow awaits the image before the first `npm install` sandbox and awaits Grok after the negative probe, then renames the staged binary into `native/grok` before the positive probe. The `finally` block awaits `stopPrefetches` and only then removes the scratch root, so cleanup never races a child's writes. The script prints one timing JSON line after the Pi probe, with the per-phase wall times, the two `*_wait_ms` values, and the corrected `*_prefetch_ms` values. - `scripts/__tests__/prefetch-child.test.mjs` (new): seven tests run small local Node children, with no Docker or network: a successful prefetch resolves and keeps its output; the duration ends at the download even when the parent is blocked for 1.5s; a nonzero exit rejects with the log tail; a command that cannot start rejects instead of hanging the join or the stop; stopping a running child waits for its exit; a child that ignores `SIGTERM` is killed after the grace period; an unawaited failure does not produce an unhandled rejection in a fresh process. - `scripts/__tests__/grok-public-install-sandbox.test.mjs`: the source test asserts that exactly the pinned image is pulled for `linux/amd64`, that Grok is provisioned into the prefetch directory and never straight into `native/grok`, that both prefetches start before staging, that the image is joined before the first sandbox, that the rename happens after the npm assertion and the negative probe and before the positive probe, that the verifier imports the helper, and that `stopPrefetches` is awaited before the single `rmSync(root, ...)`. - `package.json`: `test:release-registry` runs the new test file. ## Verification - `node --test scripts/__tests__/prefetch-child.test.mjs scripts/__tests__/grok-public-install-sandbox.test.mjs scripts/__tests__/cursor-public-install-sandbox.test.mjs scripts/__tests__/retain-runner-qualification-packages.test.mjs`: 22 pass (7 new in `prefetch-child.test.mjs`, 12 in the sandbox file). The new file passed on three consecutive runs. - `node --check scripts/verify-grok-npm-install.mjs` passes. - `node packages/paperclip-runner/scripts/provision-grok.mjs <scratch>/prefetch/grok` on this host: `Verified Grok 1.0.13 (linux-x64)` in 1.0s, `native/grok` absent before the move, 166079904 bytes in place after it. - Docker is not installed on the host that produced this PR, so the verifier was not run end to end locally. This PR's own `Build` job runs `node scripts/verify-grok-npm-install.mjs` from the branch checkout, so it exercises the change in full. **Measured on this PR's first CI run** ([38108964834](https://github.com/paperclipai/paperclip/actions/runs/38108964834), created 03:37Z), against two `master` runs created in the same twelve minutes: | | 38108878166 (`master`, 03:35Z) | 38108995800 (`master`, 03:37Z) | 38108964834 (this PR, 03:37Z) | | --- | --- | --- | --- | | Verifier step | 307s | 248s | 219s | | `Build` job | 555s | 484s | 459s | | Slowest check in the run | `Build` 555s | server (8/12) 535s | server (12/12) 547s | The PR run's timing line: `stage_packages_ms 84464`, `image_wait_ms 1`, `consumer_install_ms 97305`, `lifecycle_rebuild_ms 3545`, `codex_probe_ms 1211`, `grok_wait_ms 0`, `grok_probes_ms 626`, `pi_setup_ms 22194`, `pi_probe_ms 7753`. Both waits are at most 1ms, so the staging phase hid both downloads in full. The window from the end of staging to the Grok result went from 161s to 167s on the two runs that motivated this PR to 102s here, so the hidden downloads were about 60s on a fresh `runs-on` spot instance. Against the two same-window `master` runs, the verifier step is 29s to 88s shorter and `Build` is 25s to 96s shorter; the spread is runner variance in `pnpm build` and the consumer install. The `image_prefetch_ms` and `grok_prefetch_ms` values on that first run (84550 and 186696) were the parent's late observation of each exit, not download times; that is the first Greptile finding, fixed by the second commit, which measures from the download's last output instead. ## Risks - Low risk. The verifier proves the same properties in the same order. The only ordering change is that the downloads start earlier; every assertion, probe, and sandbox argument is unchanged. - The Grok binary is written under the scratch root in a `prefetch` directory during staging. That directory is never mounted into a container, and the `native/grok` assertion still runs against an absent path. - A prefetch that fails early now fails the step at its join point, where the old code would have failed at the same download a few seconds later. Same outcome, same message source. - `*_prefetch_ms` is a last-output time. A child that printed early and then kept working silently would be under-reported; neither `docker pull` nor `provision-grok.mjs` behaves that way (both print their final status as they finish), and a silent child falls back to the observed exit time. - Cleanup now waits up to 5s for a child that ignores `SIGTERM` before killing it, so a failing run can end up to 5s later than before. Both children exit promptly on `SIGTERM` in practice. - The timing line is one more stdout line. Nothing in the repository parses the verifier's stdout; the two workflows that run it write the output to the job log or to a file. ## Model Used - Claude (Anthropic), model ID `claude-fable-5-1`, running as a Paperclip agent through the Claude local adapter with tool use (shell, file edits, GitHub CLI). The agent collected the CI data, made the change, ran the tests, and wrote this description. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>canary/v2026.1011.0-canary.3 |
||
|
|
8dd381f5dd |
perf(ci): run the db project as its own workspaces-c lane (#15831)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Pull requests to this repository run a large CI matrix. The slowest
check sets the time a contributor waits for a green run.
> - The `General tests (workspaces-b)` lane runs every non-server Vitest
project except `ui` and `cli` in one job, one project after the other.
After #15826 leveled the server shards, this lane became the slowest
check: 593s in run 38084171761, 523s in 38083084276 and 499s in
38083020834 (all 2026-10-10).
> - The `@paperclipai/db` project alone is 168s to 250s of that lane's
436s to 513s of vitest time. Its `client.test.ts` suite is 117s to 189s
and `backup-lib.test.ts` is 70s to 107s. The other seventeen projects
together are only 263s to 276s.
> - Vitest's native `--shard` does not level this lane. The db project
is already file-parallel, and a hash-ordered slice can put both slow
suites on one runner.
> - This pull request moves the db project into its own `workspaces-c`
lane in both `pr-trusted.yml` and `release-verify.yml`. The runner
script gains a `general-workspaces-c` group, and `general-workspaces-b`
drops the db project.
> - The benefit is that both lanes should finish in about 230s to 340s
instead of 499s to 593s, and `workspaces-b` leaves the critical path.
The cost is one more job per run, about 60s of setup.
## Linked Issues or Issue Description
**Describe the enhancement**
The `General tests (workspaces-b)` PR check is the slowest check in the
run. It runs seventeen Vitest projects in sequence on one runner. The
`@paperclipai/db` project is one third to one half of its vitest time.
**Why is this needed**
The slowest check decides the wall-clock time of every PR run. In the
last three fully green runs on the current server-shard manifest,
`workspaces-b` took 499s, 523s and 593s (runs 38083020834, 38083084276
and 38084171761, 2026-10-10). The next slowest lanes, `Build` and the
server shards, sit at about 480s to 570s. A split of `workspaces-b`
removes 170s to 250s from this lane.
**Proposed solution**
Run the `@paperclipai/db` project as its own lane,
`general-workspaces-c`. Keep the other seventeen projects in
`general-workspaces-b`. Add the lane to both the PR and the release
verification matrix. Extend the guard tests so the three workspace lanes
still cover the root Vitest project list exactly once, so the new group
rejects shard flags, and so both workflows carry the lane unsharded.
**Alternatives considered**
- Vitest native `--shard` on `workspaces-b`, as `workspaces-a` does. The
db project is already file-parallel and its wall time is set by two long
suites, so a hash-ordered slice does not give a predictable split.
- Move `adapter-utils` (115s to 117s) into the new lane as well. That
makes the db lane 285s to 366s of tests and the rest 147s to 160s, which
is less level than db alone (168s to 250s) against the rest (263s to
276s).
- Three lanes. Not needed now: after this split both lanes sit under the
server shards and `Build`, so a third runner would not shorten the run.
## What Changed
- `scripts/run-vitest-stable.mjs`: new `general-workspaces-c` group that
runs `@paperclipai/db` alone. `general-workspaces-b` now excludes it.
Local `--mode all` runs all four general groups, so every project still
runs once. The dry-run output reports the new group's projects.
- `.github/workflows/pr-trusted.yml`: a `workspaces-c` entry in the
general tests matrix, with a comment that records the 2026-10-10
measurements.
- `.github/workflows/release-verify.yml`: the same `workspaces-c` entry,
to keep parity with the PR workflow.
- `scripts/__tests__/run-vitest-stable-shard.test.mjs`: the coverage
test includes the new group; shard flags are rejected for both
`workspaces-b` and `workspaces-c`; a new test asserts that
`workspaces-c` runs only the db project, that `workspaces-b` no longer
carries it, and that both workflows list both lanes without shard
arguments. The workflow check parses each `- group:` matrix entry with
comment lines removed, so a comment between keys cannot hide a shard
setting, and it first confirms the parser sees the shard keys on the
`workspaces-a` entries. A small parser test covers the
comment-between-keys case (Greptile P2).
- `scripts/__tests__/release-verify-workflow.test.mjs`: the release
workflow must carry `general-workspaces-c`.
## Verification
- `GITHUB_WORKFLOW= node --test
scripts/__tests__/run-vitest-stable-shard.test.mjs`: 32 pass (was 29).
- `node --test scripts/__tests__/release-verify-workflow.test.mjs`: the
workflow parity test passes. Two unrelated tests in that file (`a
publisher lockfile lets a fresh smoke install retain validation after
... drift`) fail on my machine on pristine `master` too, because `pnpm`
is not on its PATH. They pass in CI.
- `node scripts/run-vitest-stable.mjs --mode general --group
general-workspaces-c --dry-run` reports `["@paperclipai/db"]`; `--group
general-workspaces-b` reports the other seventeen projects.
- Both workflow files parse as YAML; the general tests matrix has 19
entries in `pr-trusted.yml` and 17 in `release-verify.yml`.
- This PR's own run exercises the script change but not the workflow
change: `pr.yml` calls `pr-trusted.yml@master`, so the matrix comes from
`master` and the `workspaces-c` job appears only on PR runs created
after the merge. Because the script comes from this branch, the
`workspaces-b` job in this PR's run (38090713637) ran the seventeen
remaining projects and finished in 340s, inside the expected 325s to
340s band. No job in that run ran `@paperclipai/db`.
- To cover the gap, I ran the new lane locally with the same command CI
uses, `pnpm test:run:general -- --group general-workspaces-c`: 49 test
files, 180 tests, all passed, 111s of vitest time (
canary/v2026.1011.0-canary.2
|
||
|
|
07f3536e37 |
fix(connections): discover Sentry OAuth through its MCP resource (#15837)
Correct Sentry OAuth discovery and cover new and saved client registration. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
4f680bc85c |
fix(release): retry verified canary tarball 404s (#15768)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Canary releases test the published npm packages through a clean onboarding install. > - A package can be announced in registry metadata while a fresh install gets a tarball 404. > - The existing ETARGET retry does not cover that distinct error. > - This pull request retries only a verified public canary tarball acquisition failure. > - Permanent errors still fail, and onboarding runs once after acquisition succeeds. ## Linked Issues or Issue Description Refs: #15743 **What happened?** The [Release run](https://github.com/paperclipai/paperclip/actions/runs/37998601578) published canary `2026.1009.0-canary.17`. Its registry verification and publisher install passed. The separate fresh onboarding install then failed with a final npm E404 for that exact `@paperclipai/server` tarball. The tarball was available on a later read. This is a separate failure from ETARGET; it does not prove the cause of the registry visibility difference. **Expected behavior** Allow a bounded acquisition retry when the effective registry is public npm and fresh primary metadata announces the exact released canary tarball. Keep other E404, authentication, configuration, and onboarding errors fatal. **Steps to reproduce** 1. Run the offline helper fixture with a first-attempt E404 for an allowlisted package at the requested canary version. 2. Return canonical effective npm registry configuration and matching primary metadata. 3. Make the second install succeed. Confirm two installs and one onboarding invocation. 4. Keep the tarball unavailable. Confirm three installs, a final failure, and no onboarding invocation. **Paperclip version or commit** The observed run used commit `ce024457f3d747657501fbcfe8eac71f6772150f`. This fix builds on the merged acquisition helper and current master. **Deployment mode** Other: the published npm canary onboarding check in release CI. ## What Changed - Add a canary-only classifier for one unambiguous final E404 at the canonical tarball URL of a CI-published package from the existing release manifest. - Query effective npm registry configuration in the failed install's prefix, environment, and working directory. Keep bounded configuration output in memory without logging it. Respect scoped registry precedence. - Require fresh primary metadata to match the package, version, tarball URL, and valid SHA512 integrity syntax. Reject redirects, conflicting errors, truncated logs, other artifacts, and unknown or custom effective registries. - Share one five-second configuration and metadata deadline within the existing two-minute acquisition budget. Keep three install attempts, refreshed retry metadata, process cleanup, and one onboarding invocation. - Bound metadata header and body waits even if transport abort propagation stalls. Cancel the reader and any late response body without waiting for cancellation acknowledgements. - Preserve the existing ETARGET classifier and its character-based stderr tail. Add offline subprocess, actual npm configuration, body cancellation, budget, identity, and failure tests. Document the scope and limits. ## Verification - `node --test scripts/__tests__/release-verify-workflow.test.mjs scripts/cloud-source-verification.test.mjs scripts/standard-image-contract.test.mjs`: 73 passed, no skips, under Node 22 on the PR head. - `pnpm test:release-registry`: 153 passed, no skips, on the PR head. This includes the existing real Playwright cleanup and startup timeout tests. - [Hosted CI](https://github.com/paperclipai/paperclip/actions/runs/38011028296) passed on this head. The rollup has 53 successful checks and two expected Storybook skips. This includes typecheck, build, the full general and serialized test suites, E2E, and Canary Dry Run. - The new tests use fixture-owned npm configuration and local HTTP servers. No published canary tarballs were downloaded or installed for this change. No release or deployment was triggered. - `pnpm test:run` was not run locally. The full split test suites passed in hosted CI. Independent source checks on this head passed 46 committed tests, 22 boundary checks, and six negative controls. Greptile scored this head 5/5 with no unresolved threads. - The source diff and PR text were checked for secrets, private identifiers, and unintended artifacts. ## Risks - The new retry is conservative. A configuration capture over 4 KiB, metadata over 64 KiB, or a check that exceeds five seconds denies retry. A persistent tarball failure still fails after three installs. - Matching primary metadata proves the announced artifact identity and integrity syntax. It does not independently verify tarball bytes. The subsequent npm install retains its integrity check. - Registry checks use npm's resolved global and applicable scope values. They do not add credentials or change npm configuration. Existing ETARGET behavior, onboarding assertions, and overall startup deadlines remain unchanged. ## Model Used OpenAI GPT-6 (Codex). The exact model variant and context window are not exposed in this session. Used reasoning, code execution, offline fixtures, and GitHub API tools. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.1011.0-canary.0 |
||
|
|
9ba5569030 |
ci: refresh the general-server shard durations (#15826)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Pull requests to this repository run a large CI matrix. The slowest check sets the time a contributor waits for a green run. > - The general-server vitest lane runs with one worker per job, so it can only go faster across runners. Twelve shards share its suites, and a recorded duration manifest decides which suite goes to which shard. > - That manifest was recorded on 2026-09-15 from 646 suites. The lane now has 871 suites and about 5031s of serial vitest time. 230 suites have no record and get the median weight, so the shards drifted apart. In run 38076972444 (2026-10-10) they ran 313s to 554s of tests, and `General tests (server (2/12))` at 618s was the slowest check of the whole run. > - An unbalanced lane wastes the runner time it already spends. The total work does not change, but one shard holds the run. > - This pull request records the current duration of every suite from two recent PR runs and keeps the same twelve shards. > - The benefit is that the twelve shards level to about 415s of tests each. The slowest server shard should drop from about 618s to about 480s, and this lane leaves the critical path. ## Linked Issues or Issue Description **Describe the enhancement** The general-server PR test lane is out of balance. Its shard manifest, `scripts/general-server-shard-durations.json`, is three and a half weeks old and misses 230 of the 871 current suites. The heaviest shard runs 554s of tests while the lightest runs 313s. **Why is this needed** The slowest check decides the wall-clock time of every PR run. In the last fully green runs the slowest check was a general-server shard (618s, 562s, 585s, 646s in runs 38076972444, 38076844891, 38076844368 and 38073555316). A balanced lane removes about 135s from that shard at no extra runner cost. **Proposed solution** Re-record the per-suite durations from two recent PR runs (38076844891 and 38076972444) with the method that the manifest's `$comment` documents: diff consecutive per-suite completion timestamps in each shard log, and measure the first suite from the Vitest `RUN` banner. Average the two runs. Keep the chat and native-runner entries from the old manifest, because those suites run in their own lanes and do not appear in the shard logs. Drop the two suites that no longer exist. **Alternatives considered** More shards would also shorten the lane (fourteen shards level at about 355s, sixteen at about 311s). They are not needed now: `Build` (489-592s) and `General tests (workspaces-b)` (513-618s) already sit above the balanced twelve-shard level, so extra runners would not shorten the run. Those two checks are the next targets. ## What Changed - `scripts/general-server-shard-durations.json`: 873 recorded suites (was 646). 870 come from the two 2026-10-10 runs, 3 carry over (chat and native-runner suites), 2 obsolete entries are gone. The `$comment` names the source runs, the fleet, and the carry-over rule. - `.github/workflows/pr-trusted.yml`: a comment on the general tests matrix records the 2026-10-10 measurements and why the shard count stays at twelve. No workflow behavior changes. ## Verification - `node --test scripts/__tests__/run-vitest-stable-shard.test.mjs`: 29 pass. This covers the manifest coverage check, the duration-balance check (spread must stay under the heaviest suite), and the complete non-overlapping partition. - `node --test .github/scripts/tests/pr-dependency-cache.test.mjs .github/scripts/tests/pr-runner-rust-cache.test.mjs .github/scripts/tests/lockfile-refresh-workflows.test.mjs`: 17 pass. - Measurement check: the per-shard sum of the recorded durations matches Vitest's own `Duration` line within 1s on all 24 shard logs. - Simulation with the real planner (`scripts/run-vitest-stable.mjs --dry-run`, `GITHUB_WORKFLOW=PR`) on the current suite list: with the old manifest the twelve shards carry 321s to 510s of recorded tests; with this manifest they carry 413s to 415s. - Expected result on the first PR run after merge: every `General tests (server (n/12))` job finishes in about 480s (415s of tests plus about 65s of setup) instead of 374s to 618s. ## Risks - Low risk. The file only sets weights for the shard planner. A wrong weight moves a suite to a different shard; it cannot drop or duplicate a suite, because the guard tests assert the partition is complete and non-overlapping. - The durations come from the `runs-on` public PR fleet. GitHub-hosted runners are faster in absolute terms, but the relative weights, which are all the planner uses, are the same. - The manifest will go stale again as suites grow. The guard test fails when coverage drops under 50%, and the matrix comment records how to refresh it. ## Model Used - Claude (Anthropic), model ID `claude-fable-5-1`, running as a Paperclip agent through the Claude local adapter with tool use (shell, file edits, GitHub CLI). The agent collected the CI data, built the manifest, ran the tests, and wrote this description. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com> Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>canary/v2026.1010.0-canary.1 |
||
|
|
40701adecf |
fix(release): preserve pinned master identity for canary publishing (#15770)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - A master push publishes a canary from its exact source commit. > - The publisher now checks out that commit by SHA. Git leaves HEAD detached. > - The release script requires the local branch name to be master. > - This pull request verifies the trusted push and source before attaching local master at the same commit. > - The canary can pass the existing branch guard without following a newer master tip. ## Linked Issues or Issue Description Related: #15712 pinned the publisher checkout and bound the smoke lockfile to its source. I searched open PRs and issues for canary, detached HEAD, and checkout fixes. No overlapping fix was found. #15768 addresses a later tarball visibility error and does not change this branch guard. **What happened?** The canary publisher checks out the exact push SHA. The checkout is detached. `release.sh canary --skip-verify` then fails because the local branch is not master. Publication does not start. The [failed master run](https://github.com/paperclipai/paperclip/actions/runs/38008956836) shows this failure. **Expected behavior** The trusted master publisher must keep the exact verified push commit and pass the existing local master guard. It must not follow a newer branch tip. **Steps to reproduce** 1. Check out a master push commit by its full SHA. 2. Confirm that HEAD is detached. 3. Run the real `require_on_master_branch` helper from `scripts/release-lib.sh`. 4. Observe the detached-HEAD rejection. The tests reproduce this without calling a publisher. **Paperclip version or commit** `baa94bbea58919bcc6cf247a708f04e6e1002679`. **Deployment mode** The GitHub Actions master canary publishing lane. ## What Changed - Add one step immediately after the pinned publisher checkout. - Require the canonical repository, a push event, and the master ref before changing any Git ref. - Validate the full source SHA and require the checked-out HEAD to match it. - Attach local master to that exact SHA and recheck HEAD. Do not fetch or select a floating branch. - Preserve release scripts, local branch guards, package authentication, other publishing lanes, and source-named lockfile artifacts. - Add disposable real-Git tests for a pinned source behind local and remote master, rejected trust and SHA inputs, unchanged refs on rejection, and the original branch guard. Document the branch handoff. ## Verification - `node --test scripts/__tests__/release-verify-workflow.test.mjs scripts/cloud-source-verification.test.mjs scripts/standard-image-contract.test.mjs scripts/release-lib.test.mjs` passed all 82 tests on Node 25.6.1. Twelve new cases include eleven real-Git fixtures and one workflow invariant check. The tests run the actual workflow shell and the unchanged release guard. - `actionlint -shellcheck= .github/workflows/release.yml` passed. ShellCheck passed for the new shell. Full local actionlint reports four pre-existing advisories in other steps; their text matches the base exactly. This change adds no advisory. - Workspace `pnpm -r typecheck` and `pnpm build` passed on Node 25.6.1. The local tests cover the changed release flow; the full application test suite runs in hosted CI. - [Hosted full CI](https://github.com/paperclipai/paperclip/actions/runs/38011530698) passed all 47 jobs at commit `ffff774a2629663fa71e2fad5143da0696afc5e6`. This includes policy, typecheck, release registry, build, runner tests, application tests, browser tests, and the canary dry run. Hosted policy ran all 12 new guard tests and passed all 67 source-policy tests. - Greptile scored the same commit 5/5 with no actionable issues. There are zero review threads. Separate source review approved all three changed files and 32 dependencies. It passed 82 tests and caught four broken variants. - The supply chain scanner returned neutral with zero annotations. It cannot verify this inline workflow change and reports limited coverage for existing action references. Manual review confirmed that all action references and pins are unchanged. The security, Socket, and Snyk checks passed. - No live canary install, registry publication, Docker image operation, workflow rerun, or deployment was performed. Tests use disposable Git repositories and local fixtures. ## Risks Low risk. The new step changes only local Git state in the trusted canary publisher. A wrong event or source fails before ref changes. The original release and authentication guards remain in place. The exact checkout and artifact source stay unchanged. Revert the new step to restore the previous behavior. No runtime or database change is required. ## Model Used OpenAI GPT-6 through Codex. Used repository inspection, code execution, Git and Bash fixtures, and independent agent review. The runtime does not expose a more specific model snapshot or context-window size. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.1010.0-canary.0 |
||
|
|
b1ebe3de3c |
perf(chat): wake action queues from commits and deadlines (#15808)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Chat keeps durable rows for provider actions and setup messages. > - Five action queues still scan the database every second when empty. > - Empty scans add idle database traffic and prevent cost savings from sleep. > - This pull request wakes these queues when their writes commit or their next deadline arrives. > - Existing rows retain retry and restart recovery. Empty queues stop scanning. ## Linked Issues or Issue Description Refs #15802, #15625, #15750, #15757. Related allocation work: #15153. **What existing behavior does this improve?** Idle chat reconciliation for Slack Board messages, Slack slash-command task starts, Slack setup verification messages, receipt reactions, and Slack session stops. **Current behavior** The app runs these five queue scans through chat maintenance every second, including when no work exists. **Proposed behavior** Each queue has an independent worker on the existing scheduler. Producers and foreground claim/retry/settlement writes announce work inside their database transaction. The worker wakes after the outer transaction settles. Startup restores durable work. Shared dispatch and deadline predicates select real retries and abandoned claims. Empty queues disarm. **Subsystem affected** Server chat services. No schema or migration changes. ## What Changed - Register five action workers alongside the three delivery workers from #15802. Remove their scans from periodic app maintenance. - Share action eligibility between dispatch and deadline selection. Park paused Board messages and task starts until endpoint changes. Preserve uncertain-send quarantine. - Wake setup verification after account, welcome, registration, or authenticated callback changes. Exclude prerequisites that need another event. - Persist a retry time for failed provider-confirmed task admission. Its own commit notification cannot cause an immediate retry loop. - Reuse the existing transaction signals and scheduler registration code. Provider calls remain outside database transactions. - Update the instance-sleep inventory and add scheduler and PostgreSQL regression coverage. ## Verification - Passed: 37 scheduler, coordinator, and app-wiring tests, including an idle hour with no further scans or timers. - Passed: seven focused PostgreSQL chat cases. These cover deadline eligibility, startup recovery, outer-commit wakes, actual producer signals, and signed-callback verification without maintenance polling. - Passed: `pnpm typecheck`. - Passed: full chat integration suite and related publication/coordinator tests, **1,278 tests** across six files, including **1,228 chat integration cases**. - Passed: `pnpm build`. - After the review fix, seven focused PostgreSQL integration cases and the server typecheck passed again. Deadline and dispatch queries now exclude completed action history. - Greptile: **5/5** on `35a979fce6bb9ab910fab3373ec8e537fc2d01a9`, with the prior finding resolved and no new findings. - Fixed a CI timing failure in the GitHub mention integration test: asynchronous admission now has an explicit ten-second assertion deadline instead of the default one second. The failure reproduced locally; all five mention variants and all 398 cases in the matching CI shard pass after the fix. - Hosted CI passed on `35a979fce6bb9ab910fab3373ec8e537fc2d01a9`: **54 successful checks**, two intentionally skipped Storybook checks, no failures or pending checks. Provider responses are simulated in the integration suite. PostgreSQL, transactions, service dispatch, and scheduling are real. No live Slack workspace was used. ## Risks - Every producer that changes runnable state must emit a transaction signal. This change covers the five queues and their foreground dispatch/retry paths. Direct SQL and independent DB owners still need explicit integration or startup recovery. - Missing retry or claim deadlines can strand work. Tests compare the dispatch and deadline behavior, including paused and uncertain states. - Other provider maintenance, sockets, and scheduled jobs remain. This change does not establish full instance sleep. - No new tables, migrations, or provider credentials are required. ## Model Used OpenAI Codex (GPT-6), with repository inspection, code execution, and automated tests. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [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> |
||
|
|
fed251867b |
chore(lockfile): refresh pnpm-lock.yaml (#15820)
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> |
||
|
|
a7dd5284b5 |
feat: add company fast-response acknowledgements (#15748)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Human messages start agent work through task, chat, and external-channel intake. > - Agent startup can delay the first visible response. > - A small direct model call can acknowledge the request while normal work starts. > - This response must remain separate from evidence that the agent answered or completed work. > - This pull request adds optional company-wide fast responses with durable receipts, access checks, and cost accounting. > - Users receive prompt acknowledgement without changing the agent execution path. ## Linked Issues or Issue Description **Subsystem affected** Cross-cutting: database, shared contracts, server orchestration, connections, and UI. **Problem or motivation** Users can wait for an agent to start before they receive a contextual response. Generic progress messages do not confirm what the user asked. An acknowledgement must not falsely complete work or satisfy an unanswered turn. **Proposed solution** Add a disabled-by-default experimental fast-response model using an existing shared API connection. Generate one short acknowledgement for each accepted human turn. Use the existing agent identity and external publication outboxes. Retain platform provenance and account for the inference independently. **Alternatives considered** Running the acknowledgement through the agent runtime adds startup latency. Restricting implementation to OpenRouter leaves other existing API connections unsupported. This change supports all existing API connections and uses OpenRouter for live verification. **Roadmap alignment** This extends existing agent communication and connection settings. ROADMAP.md and open pull requests were checked. No duplicate fast-response implementation was found. The maintainer supplied the feature plan directly. ## What Changed - Add Experimental fast responses to instance settings. Keep the feature off by default. Enforce the flag at intake, dispatch, and publication. - Move Decision model and Fast response setup to company Settings → Connections when the experiment is enabled. Place Connections below Environments. Separate the model sections with clear headings and a divider. Keep Decision model in General when the experiment is off. Route Costs setup links to the available settings page. - Make the acknowledgement prompt a multiline template string. Ask for a brief, positive response in the user’s language. - Add settings, connection/model selection, sponsorship, fixed-sample testing, and Costs history. - Add a Vercel AI SDK text service for existing native, routed, Bedrock, and local API connections. - Add durable per-turn requests, a bounded worker, a three-second admission deadline, and one inference attempt. - Integrate task creation, task and agent chat, steering, queued follow-ups, experimental board chat, shared chat intake, and email. - Preserve receipt provenance and exclude receipts from completion, recovery, interaction resolution, and agent wakeups. - Recheck authorization, source validity, real replies, budgets, and destination binding before publication. Preserve accounting for suppressed or uncertain outcomes. - Add provider contracts, database race tests, channel outbox tests, UI tests, Stories, and documentation. Keep decision-model execution unchanged. ## Verification - Local `pnpm -r typecheck`, `pnpm build`, and UI token gates pass after rebase. The UI typecheck and build also pass after the final navigation fixes. All 37 focused navigation tests pass. The first full local `pnpm test:run` invocation reported seven chat failures. All seven passed in isolated reruns after fixture updates and host cleanup. This invocation was stopped after the full final-head CI suite passed. - Initial local database tests hit macOS shared-memory exhaustion. A diagnostic confirmed `shmget: No space left on device` with the system limit of 32 IDs. Three abandoned, unattached PostgreSQL IPC headers were reclaimed. The isolated rerun passed all 31 fast-response and channel tests. The two additional Slack regression cases also passed in a separate rerun. No active database was stopped. - UI token gates pass. Provider contract tests cover all supported API connection types. Fast-response tests cover uniqueness, authorization, budgets, expiry, cancellation, restart, real-reply races, and the experimental flag. - The test drive verified that disabling the experiment hides Connections and redirects its direct URL. Enabling it restores the saved OpenRouter setup. After restart on the final commit, the OpenRouter settings sample returned “Sure, I’ll take care of fixing the border around the settings panel.” in 428 ms. It used 260 input and 31 output tokens and recorded $0.0000576 in reported cost. - Earlier live OpenRouter verification produced a receipt in 1.013 seconds for a new task. The real agent then supplied its substantive answer. A separate settings test took 1.34 seconds, used 246 input and 23 output tokens, and recorded $0.0000361. - Live Slack verification on final commit `24016fa525961ebc7a4acffdc5019ce123970549` passed for a fresh initial task and a follow-up in a disposable bot DM. The initial acknowledgement appeared 2.405 seconds after the Slack message and 1.413 seconds after durable acceptance; the follow-up appeared in 2.322 seconds and 1.160 seconds respectively. The recorded generation/settlement durations were 898 ms and 697 ms. The initial call used 291 input / 39 output tokens and cost $0.00006705; the follow-up used 281 / 47 and cost $0.00007035. - The Slack acknowledgements retained the assigned agent identity and `origin: fast_response`, source-turn references, and no fabricated run ID. A status snapshot after the initial receipt showed the task still `in_progress` with its real execution run. The agent then read the CSV fixture and correctly reported two ready items, two blockers, and their owners. Both replies stayed in the originating DM, and no duplicate generic working message followed the delivered contextual acknowledgements. The `new` command started a fresh conversation session without generating a fast response. - An earlier eligible Slack turn generated a response but suppressed publication. Its reported cost was retained, generic progress remained, and the real agent completed the requested result. This is recorded separately from the two delivered acknowledgements; the history did not expose a precise suppression reason. - Slack verification used real signed provider events, the existing intake/publication paths, and a user-approved temporary webhook-only HTTPS tunnel. Subscriptions were limited to direct-message events. The test bot was paused and the tunnel stopped afterward. Live channel/group-thread and GitHub journeys remain unverified; their mocked tests cover intake, destination binding, publication, and continued underlying work. - Greptile reviewed final commit `24016fa525961ebc7a4acffdc5019ce123970549` at 5/5. Both new review findings are fixed and all threads are resolved. [Final-head CI run](https://github.com/paperclipai/paperclip/actions/runs/38060015730) passed. This includes grouped server/chat/workspace tests, serialized suites, runner checks, browser journeys, typecheck, build, release registry, policy, and the canary dry run. - Manual check: enable Experimental fast responses. Open company Settings → Connections. Select a shared API connection and model. Save and use Test response. Submit a task message and confirm that its receipt does not complete the task. ## Risks - The migration adds receipt and configuration tables plus accounting references. It uses idempotent statements and follows the current migration sequence. - A slow provider, authorization change, or real-agent reply can suppress an acknowledgement. Existing progress feedback remains available. - Unknown provider billing retains a reservation until reconciliation. External deliveries with uncertain outcomes must not be blindly repeated. - Live Slack initial-task and follow-up DM journeys pass. Live Slack channel/group-thread and GitHub journeys remain unverified. The PR remains a draft and records these gaps. Final-head repository CI and automated review pass. ## Model Used OpenAI Codex, based on GPT-6, with reasoning, repository tools, code execution, and browser interaction. The runtime does not expose an exact model snapshot ID or context-window size; these are not asserted. ## 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> |
||
|
|
1100682261 |
ci: move the Grok public-install verification off Canary Dry Run (#15213)
Move the 'Verify built-in Grok from a clean public npm install' step from the Canary Dry Run lane to the end of the Build lane in pr-trusted.yml. The verifier only needs pnpm build output, which Build already produces. Expected: Canary Dry Run drops from ~484-572s to ~275-350s, and the run's critical path moves to the server (2/12) shard. |
||
|
|
50083c4e35 |
test: honor configured instance in task cwd regression
Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
5d6d6eabdf |
fix: retain task cwd overrides and protect ignored host files
Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
0e37a385e6 |
fix(pi-local): avoid embedding model catalogue in errors (#13628)
<!-- Write all pull request text in Simplified Technical English (ASD-STE100): short sentences, one instruction per sentence, simple approved vocabulary, and the active voice. --> ## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The pi-local adapter discovers and validates models before it starts a local Pi process > - An unavailable-model error included model IDs from the discovered catalogue > - Repeating those IDs made persisted and rendered failures noisy and exposed substitute choices > - This pull request keeps the requested model, reports the available count, and points to the existing lookup command > - The benefit is a concise error without changing model discovery or matching ## Linked Issues or Issue Description Fixes #13612 ## What Changed - Replace model catalogue interpolation in unavailable-model errors with the requested model, normalized model count, and `pi --list-models`. - Add a regression test with 13 discovered models and exact error-message matching. ## Verification - `pnpm exec vitest run packages/adapters/pi-local/src/server/models.test.ts` - `pnpm exec vitest run packages/adapters/pi-local/src/server/*.test.ts` - `pnpm --filter @paperclipai/adapter-pi-local typecheck` - `git diff --check` All listed checks pass locally. The test suite uses a temporary Pi executable and verifies that model IDs are not appended to the error. ## Risks Low risk. Only unavailable-model error wording changes. Model discovery, matching, valid model execution, and failure handling remain unchanged. Full repository CI was not run locally because the host uses Node 22.22.1 and the repository declares Node >=24.11.0. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used OpenAI GPT-5.6 Luna (`gpt-5.6-luna`); tool-assisted code execution. The context window size is not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [ ] 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 #13612) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [ ] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge |
||
|
|
28e134873e |
fix(opencode-local): run in the realized workspace (#11438)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Paperclip adapters start each agent in its selected workspace > - Paperclip can realize a different workspace path for local and remote runs > - The OpenCode CLI prefers its inherited `PWD` when no `--dir` option is present > - The process `cwd` option does not update the inherited `PWD` value > - This pull request passes the realized workspace to OpenCode with `--dir` > - The benefit is that OpenCode sessions and tools use the workspace that Paperclip selected ## Linked Issues or Issue Description No matching issue exists. Refs #8287. That earlier pull request describes the realized remote workspace path. It does not make the OpenCode CLI use that path. I searched open and closed issues and pull requests for `opencode --dir`, `opencode cwd PWD`, and `opencode workspace directory`. I found no duplicate. **What happened?** Paperclip gives the OpenCode process the correct operating system `cwd`. However, the adapter does not pass OpenCode's `--dir` option. OpenCode uses `process.env.PWD` before `process.cwd()` when `--dir` is absent. A server `PWD` that differs from the realized workspace can therefore place the OpenCode session and its tools in the wrong directory. **Expected behavior** OpenCode must use the workspace that Paperclip realized for the run. An explicit `--dir` in `extraArgs` must remain valid and must not be duplicated. **Steps to reproduce** 1. Start the Paperclip server from directory A. 2. Configure an OpenCode agent with directory B, or realize directory B on a remote execution target. 3. Start a run without a manual `--dir` value in `extraArgs`. 4. Inspect the OpenCode session directory or run a directory-sensitive tool. 5. Before this change, OpenCode can use directory A from the inherited `PWD`. 6. After this change, OpenCode receives `--dir B`. **Paperclip version or commit** `b38d6ddb811b3ff3145ff5df60174eb9e7313fe6` **Deployment mode** Self-hosted server. The same adapter path is used in local development and remote execution. **Installation method** Built from source. **Agent adapter(s) involved** OpenCode. **Database mode** Not database-related. **Additional context** OpenCode v1.17.13 defines `--dir` and uses the inherited `PWD` as its fallback root. See the [OpenCode run command source](https://github.com/anomalyco/opencode/blob/10c894bdeef3618f5666fb506ef7f9491bb964d8/packages/opencode/src/cli/cmd/run.ts#L333-L339). ## What Changed - Add the realized execution workspace as `--dir` for OpenCode runs. - Preserve an explicit `--dir` or `--dir=...` value from `extraArgs`. - Stop option detection at the first `--` terminator. - Extend the remote execution tests for realized, explicit, and post-terminator directory arguments. ## Verification - `pnpm exec vitest run packages/adapters/opencode-local/src` passed: 7 files and 40 tests. - `pnpm --filter @paperclipai/adapter-opencode-local typecheck` passed. - `pnpm --filter @paperclipai/adapter-opencode-local build` passed. - `pnpm -r typecheck` passed for all 31 selected workspace projects. - `pnpm build` passed for all selected workspace projects. - `git diff --check` passed. I also ran `pnpm test:run`. It completed with 334 test files passing and 2 test files failing. It reported 3,790 passing tests and 4 skipped tests. The two failures are in `issue-monitor-scheduler.test.ts` and `workspace-runtime.test.ts`. Each failure reproduces on the unchanged base commit after this pull request is removed. Neither test imports or covers the OpenCode adapter. ## Risks - The behavior change is limited to OpenCode run arguments. - A custom old OpenCode binary that does not support `--dir` will reject the option. The current official OpenCode v1.17.13 CLI supports it. - An explicit operator `--dir` remains authoritative. The adapter does not add a duplicate option because OpenCode's parser converts repeated string options to an array. - This change does not modify schemas, migrations, APIs, telemetry, or user data. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used OpenAI Codex based on GPT-5. The runtime did not expose a more specific model ID or context-window size. The agent used high-depth reasoning, repository access, shell execution, and GitHub tools. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [ ] 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: commitperclip-bot <305974387+commitperclip-bot@users.noreply.github.com> |
||
|
|
3cfe5d58df |
Merge origin/master and preserve task workspace preparation
Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
40bb466d78 |
fix(ui): align chat connection and recovery cards (#15771)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task pages show external chat connections and recovery notices.
> - These cards use different widths, colors, and spacing.
> - The difference makes related task information harder to scan.
> - This pull request gives these cards one shared surface.
> - The benefit is a consistent task page for chat and email users.
## Linked Issues or Issue Description
**What happened?**
The external chat banner and recovery notice have different backgrounds,
padding, and widths. The first banner needs more space above it.
**Expected behavior**
Connection, recovery, workspace repair, and task email cards use the
same surface. The chat cards have the same width.
**Steps to reproduce**
Open a task with an external Slack conversation and a recovery notice.
Compare the two cards. Open the Tasks / Context notices story to compare
the updated cards with an AgentMail email card.
## What Changed
- Add a shared token-based surface for task context cards.
- Put chat recovery notices in the same width container as the
connection banner.
- Add space above the connection banner.
- Apply the surface to task email cards and workspace export repair.
- Add a Storybook view with per-card width, color, padding, and corner
checks.
- Give the workspace recovery card its own React key to prevent
duplicate connection banners.
## Verification
- Passed `pnpm check:token-gates`.
- Passed 57 tests for ExecutionBlockerNotice, WorkspaceExportRecovery,
and the external chat banner.
- Passed UI typecheck and UI production build.
- Captured desktop and mobile browser screenshots from the real
components with fixture data. The three cards have equal widths,
backgrounds, padding, and corners.
- Full local typecheck needs Cargo, which is absent in the task
environment. Full tests and build could not complete within the
environment memory limit. Remote typecheck, build, all test shards, and
security checks pass on commit
|
||
|
|
07fee85eaf |
refactor(heartbeat): extract workspace preparation (#15807)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The heartbeat service prepares the workspace and dispatches each agent run. > - After the runtime extraction, `heartbeat.ts` still had 6,417 lines. > - Workspace setup occupied one contiguous 857-line block inside `executeRun`. > - This pull request moves that block into one workspace preparation module. > - The executor keeps dispatch gates and terminal cleanup. Reviewers can inspect workspace setup as one phase. ## Linked Issues or Issue Description Refs #15803 and #15578. This continues the heartbeat refactor series. The search found no open duplicate workspace preparation extraction. **What existing behavior does this improve?** It makes heartbeat workspace preparation easier to maintain. It does not change the workspace policy or execution behavior. **Current behavior** `executeRun` contains workspace resolution, freshness decisions, provisioning, persistence, issue binding, environment acquisition, and realization inline. **Proposed behavior** `prepareHeartbeatWorkspace` owns that sequence in `server/src/services/heartbeat/workspace-preparation.ts`. `heartbeat.ts` calls it at the same point and uses its returned workspace, lease, target, and metadata. **Reason and benefit** The main file falls from 6,417 to 5,597 lines. The workspace phase has a typed boundary and direct regression tests. **Breaking changes** None. There are no schema, API, feature-flag, or execution-policy changes. ## What Changed - Extract the 857-line workspace preparation block without changing its operations or their order. - Preserve native recovery bindings, workspace persistence failure cleanup, issue rebinding after realization, and the cloned restore lease. - Keep environment selection, dispatch admission, terminalization, and final lease release in `heartbeat.ts`. - Add 12 boundary tests with disposable PostgreSQL, real Git repositories, and real local environment leases. - Make the existing Stop and plan-only recovery tests await executor completion instead of short polling deadlines. Assert exactly one continuation for the specific plan-only source run. - Keep server-only Node preload hooks out of the deterministic process adapter used by Stop browser acceptance. - Refresh workspace recovery browser acceptance for the current task composer, and give its disposable test-drive an explicit 10-second snapshot deadline. Production snapshot settings stay unchanged. - Document the workspace preparation boundary in `doc/DEVELOPING.md`. ## Verification - AST comparison: all 857 moved lines match the original block. The remaining module body differs only at the extraction call. - `pnpm build`: passed. - `pnpm -r typecheck`: passed. - Focused regression coverage: 651 distinct cases across eight suites passed across targeted runs, including all 12 new boundary tests. The complete 400-test recovery suite passed together on the final commit. - Workspace bootstrap browser acceptance: both journeys passed (4.2 minutes), including preserved source edits, ignored-file exclusion, bounded retries, and persistence across reload. - Stop/queue browser acceptance: both process and native runner journeys passed (2.2 minutes), covering queueing, Stop, subtree pause/cancel, resume, and unrelated-task isolation. - [Full repository CI](https://github.com/paperclipai/paperclip/actions/runs/38062408589): all 47 jobs passed, including the general and serialized test suites, eight browser shards, build, typecheck, native runner checks, and canary dry run. - The local `pnpm test:run` was interrupted to prioritize the recovery regression; the complete repository suite was verified in CI. - Greptile: 5/5 on `c28e698b885cf5fd12f36f7b4052e7a2e6f879d7`, with no open review threads. ## Risks - This code writes durable workspace bindings and acquires environment leases. A changed operation order could break recovery or cleanup after partial setup. The extraction preserves the original statements and exception boundaries. - The new tests cover creation, reuse, immutable recovery bindings, persistence failure, acquisition and realization failure, controller ownership loss, isolation, execution policy, and lease snapshot independence. - Browser acceptance uses real application services with deterministic local workers and a fake Codex protocol provider. It does not qualify live cloud providers. ## Model Used OpenAI GPT-6 through Codex, with code editing, shell execution, and test tools. The exact serving model identifier and context-window size are not exposed in this session. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
b014a5ad1e |
fix: align workspace source authorization with admission strategy
Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
0049e01678 |
fix: preserve task policy and authorize channel workspace admission
Keep task project environment and trust separate from workspace source configuration. Authorize channel creation against locked endpoint defaults, realize first native chat worktrees safely, and enforce source authorization on legacy PATCH. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
026010de0d |
merge: integrate runtime and chat refactors with source workspace authorization
Preserve task workspace finalization timing in the extracted runtime module. Separate source workspace write policy from task reassignment and retain board assignee context through chat defaults. Co-Authored-By: Paperclip <noreply@paperclip.ing> |
||
|
|
4d7f0c8fe6 |
fix: authorize actual workspace sharing and assigned board targets
Co-Authored-By: Paperclip <noreply@paperclip.ing> |