mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-06 10:48:12 +02:00
71231dfa38681c10cb2a453485ea402a5258ca8b
3381
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
71231dfa38 |
feat(audit): agent audit UI — company page + per-agent tab (#9744)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Operators need an audit record of agent actions across tasks, comments, documents, approvals, and runs > - The permission-gated audit read API provides that record, but operators cannot inspect it in the product > - A readable UI must preserve company boundaries, server-side permission decisions, and redaction > - Audit exports must also be safe to open in spreadsheet software and must record the export itself > - This pull request adds company and per-agent audit views plus a guarded CSV export > - The benefit is a searchable, filterable, and reviewable agent action history with direct links back to work ## Linked Issues or Issue Description **Feature.** This change adds the frontend and CSV export for the agent action audit log. Refs #9731 and #9735. - Problem: agent actions are recorded, but operators have no readable product surface to inspect or export them. - Solution: add a company audit page and a per-agent Audit tab that use the permission-gated audit API. - Alternative: build a separate plugin-only surface. This was rejected because the existing permission model already supports a unified, server-authoritative view. This pull request targets the audit epic branch, which contains the merged #9735 audit API. ## What Changed - Added a company Audit page and sidebar entry. - Added a per-agent Audit tab with a fixed agent filter. - Added filters for agent, responsible user, action domain, entity type, and date range. - Added task and run links, responsible-user context, cursor pagination, and readable action text. - Added a permission-denied Enterprise card for callers without `audit:view_agent_actions`. - Added a CSV export that is permission-gated, capped, self-audited, CSV-escaped, and protected against spreadsheet formula injection. - Preserved the merged audit API cursor validation, redaction, and sub-millisecond pagination behavior. ## Verification - `pnpm exec vitest run ui/src/pages/audit/AuditFeed.test.tsx` — 6 passed. - `pnpm --filter @paperclipai/server exec vitest run src/__tests__/agent-action-audit-routes.test.ts` — 8 passed with embedded PostgreSQL. - `pnpm -r typecheck` — passed across all workspaces. - `pnpm build` — passed across all workspaces. - `pnpm test:run` — all completed shards passed except one environment-sensitive CLI assertion caused by injected static AWS credential variables; the exact test passes 8/8 with those variables unset. - Manual Chromium QA exercised the populated feed, active filters, permission-denied card, per-agent tab, and CSV export. ## Screenshots and Manual QA - [All audit states exercised in Chromium](https://github.com/paperclipai/paperclip/pull/9744#issuecomment-4998997001) - [Detailed browser report and per-agent tab root cause](https://github.com/paperclipai/paperclip/pull/9744#issuecomment-4998771061) The per-agent redirect defect found during QA is fixed in this branch. ## Risks Low to moderate risk. The UI and export route are additive and use the existing company-scoped permission gate. The main risks are large exports and spreadsheet interpretation. The export is capped at 10,000 rows, records truncation accurately, and prefixes formula-like cells as text. There are no schema changes or migrations. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - Anthropic Claude Opus 4.8, 1M context, extended thinking, tool use, and code execution produced the original implementation. - OpenAI Codex, GPT-5 (deployment ID and context window not exposed), reasoning, tool use, code execution, browser-test orchestration, and GitHub review tooling repaired and verified the pull request. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |
||
|
|
86b265bb85 |
test(ui): isolate Cases routing regression (#10591)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The UI test suite protects the board route table > - The Cases routing regression test needs only the route table and sentinel pages > - The test initialized the full cloud access query flow for each route > - That unrelated setup made the two assertions spend several seconds polling > - This pull request isolates the routing dependency and removes the long timeout > - The benefit is faster and more focused route regression coverage ## Linked Issues or Issue Description **What happened?** The Cases routing regression test initialized cloud health, session, and board access queries. Its two route assertions spent about 6.69 seconds in test execution. **Expected behavior** The route regression test must bypass unrelated cloud access checks and resolve the two route assertions synchronously. **Steps to reproduce** 1. Run `pnpm --dir ui exec vitest run src/App.cases-routing.test.tsx` on the base commit. 2. Inspect the Vitest test duration. 3. Observe that the test waits through unrelated query transitions. **Paperclip version or commit** `7301fae942c3d5826974335cb40d6f1e0d95d1e0` **Deployment mode** Built from source. The defect affects the UI unit test suite. Related pull request: #9198 introduced the Cases route regression coverage. ## What Changed - Mock `CloudAccessGate` at the routing boundary. - Import the app after hoisted CSS setup and module mocks. - Remove the query client and three unrelated API mocks. - Replace long polling with a bounded three-turn route wait. - Remove the custom 20-second test timeouts. ## Verification - `pnpm --dir ui exec vitest run src/App.cases-routing.test.tsx` - `pnpm --filter @paperclipai/ui typecheck` - `pnpm check:token-gates` The focused run passed both tests. Test execution changed from about 6.69 seconds on the base commit to 40 milliseconds on this branch. ## Risks Low risk. The production route table is unchanged. The test still renders the real `App` route table and the same sentinel pages. > 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 with model ID `gpt-5`. The context-window size is not exposed to this run. The run used reasoning, repository tools, code 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 - [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.801.0-canary.3 |
||
|
|
a29b10510c |
fix(server): bound inherited workspace runtime services (#10589)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Execution workspaces can inherit runtime services from a project workspace > - A project workspace keeps current and historical runtime service rows > - The execution workspace read path returned all current rows, including services removed from the current configuration > - This pull request matches inherited rows to the current service definitions > - The benefit is bounded workspace payloads and accurate service summaries ## Linked Issues or Issue Description **What happened?** Shared execution workspaces returned current historical service rows that no longer matched the project workspace configuration. The response size multiplied across every shared execution workspace. **Expected behavior** Shared execution workspaces must return only the newest runtime service row for each service in the current project workspace configuration. **Steps to reproduce** 1. Create one project workspace with many historical runtime service rows. 2. Create many shared execution workspaces that inherit that project workspace. 3. List the execution workspaces and inspect each `runtimeServices` array. **Paperclip version or commit** `7301fae942c3d5826974335cb40d6f1e0d95d1e0` **Deployment mode** Built from source. The defect is in the server read model and is not deployment-specific. No duplicate or related public issue or pull request was found. ## What Changed - Select only runtime service rows that match the current project workspace service definitions. - Preserve each matched service definition index in the API result. - Avoid loading direct execution service rows for workspaces that inherit project services. - Add unit, integration, and volume regression coverage. ## Verification - `pnpm --dir server exec vitest run src/services/workspace-runtime-read-model.test.ts src/__tests__/execution-workspaces-service.test.ts -t 'selectConfiguredRuntimeServiceRows|returns full details at the observed volume|inherits only runtime-service rows'` - `pnpm --filter @paperclipai/server typecheck` The focused test run passed 4 tests and skipped 27 unrelated tests. ## Risks The read path now omits service rows that do not match the current configuration. This is the intended behavior for inherited runtime services. The change does not alter service persistence or lifecycle transitions. > 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 with model ID `gpt-5`. The context-window size is not exposed to this run. The run used reasoning, repository tools, code 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 - [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> |
||
|
|
2e4774ac90 |
fix(adapter-utils): correct confirmation wake semantics (#10588)
<!-- 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 > - Local agent wakes include a default execution contract > - That contract tells agents how issue-thread continuation policies behave > - The current text says `wake_assignee` resumes a confirmation only after acceptance > - The server actually wakes for every non-expired resolution and reserves acceptance-only behavior for `wake_assignee_on_accept` > - This pull request makes the default prompt match the server contract and strengthens the recovery follow-up regression case > - The benefit is that agents choose the correct continuation policy and recovery tests cover normalized agent name keys ## Linked Issues or Issue Description Related work: Refs #5473, Refs #5060, and Refs #10562. **What happened?** The default local-agent prompt described `wake_assignee` as acceptance-only for `request_confirmation`. This conflicts with the server. The server wakes on every non-expired resolution. A recovery follow-up test also used an already-normalized execution agent name key, so it did not exercise the normalization seam. **Expected behavior** The prompt must state that `wake_assignee` resumes after acceptance or rejection. It must direct acceptance-only flows to `wake_assignee_on_accept`. The recovery regression must use a display-style agent name key and prove that the follow-up path still works after normalization. **Steps to reproduce** 1. Read the default local-agent prompt in `packages/adapter-utils/src/server-utils.ts`. 2. Compare its confirmation continuation text with `queueResolvedInteractionContinuationWakeup` in `server/src/routes/issues.ts`. 3. Observe that the prompt gives acceptance-only semantics to `wake_assignee`. 4. Inspect the recovery hand-back test and observe that its execution name key is already normalized. **Paperclip version or commit** `7301fae942` **Deployment mode** Local dev. The prompt and test behavior are not deployment-specific. ## What Changed - Corrected the default agent prompt for `wake_assignee` and `wake_assignee_on_accept`. - Added focused prompt assertions for both the required and obsolete text. - Changed the recovery follow-up fixture to use a display-style agent name key. ## Verification - `pnpm exec vitest run packages/adapter-utils/src/server-utils.test.ts -t 'keeps the default local-agent prompt action-oriented'` passed: 1 test. - `pnpm exec vitest run server/src/__tests__/heartbeat-comment-wake-batching.test.ts -t 'defers recovery hand-back wakes until the resolving run exits'` passed: 1 test. - `pnpm --filter @paperclipai/adapter-utils typecheck` passed. - `pnpm --filter @paperclipai/server typecheck` passed. - `git diff --check origin/master...HEAD` passed. ## Risks - Low risk. The production change updates prompt text only. - Agents that followed the old text may now choose `wake_assignee_on_accept` for acceptance-only flows. - The server test change only broadens an existing regression fixture. > 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 with GPT-5. The exact serving model ID and context-window size are not exposed to the agent. The model used reasoning, repository tools, tests, Git, and GitHub CLI access. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
c9116686bd |
test(installer): cover cross-version update migrations (#10587)
## Thinking Path > - Paperclip is the open source control plane people use to manage AI-agent companies > - Managed updates can change both the application payload and its database schema > - Unit tests cannot prove that an older live install upgrades through a real migration and remains recoverable > - The managed-install work in #10045 needs a repeatable cross-version system test > - This pull request adds an isolated end-to-end harness for update, migration, backup, service restart, and rollback behavior > - The benefit is a direct proof that managed upgrades preserve the existing database and service lifecycle across versions ## Linked Issues or Issue Description Refs #10045 This test is a focused follow-up to the managed install integration. Merge #10045 first so the tested install, update, service, backup, and rollback commands are available. ## What Changed - Added a cross-version managed-update E2E script. - Installed an older Git ref, initialized its embedded PostgreSQL database, and updated to a ref with one additional migration. - Verified the pre-update backup, payload switch, service recovery, migration result, database-cluster reuse, and rollback behavior. - Isolated Paperclip state under a dedicated test home and cleaned up the service and managed install on success or failure. - Added regression tests for shell syntax, required-ref validation, side-effect-free preflight failure, and complete failure cleanup. ## Verification - `node --test scripts/__tests__/e2e-update-migrations.test.mjs` - `bash -n scripts/e2e-update-migrations.sh` - GitHub latest-head CI: build, typecheck, release registry, canary dry-run, general tests, serialized suites, and both browser E2E shards passed. - Full harness execution needs an isolated macOS or Linux host with a real launchd or systemd user service. It is intentionally not run on a live Paperclip server host. ## Risks - The script manages a real user service and downloads two Git refs. Run it only on an isolated test host. - The test needs #10045 because `origin/master` does not yet contain the managed install lifecycle. - The script uses a dedicated `PAPERCLIP_HOME`, refuses a pre-existing shim or test home, and removes its service and install during cleanup. > 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 deployment ID or context-window size. Reasoning, repository access, shell execution, and GitHub tooling were enabled. ## 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> |
||
|
|
70175d5b6f |
fix(garden-inbox): preserve declined candidates on reruns (#10586)
## Thinking Path > - Paperclip uses company skills to give agents repeatable operating workflows. > - The garden-inbox skill asks a user to confirm reversible archive candidates. > - A user can leave a candidate unchecked because they want to keep it visible. > - A later confirmation pass currently checks that candidate again by default. > - This pull request adds a repeatable `--unselect` option for candidates declined in an earlier pass. > - The benefit is that repeated confirmation cards preserve the user's prior choice and make that history visible. ## Linked Issues or Issue Description **What happened?** When an inbox gardening confirmation was created again, candidates declined in an earlier pass could start checked again. **Expected behavior** The caller can identify previously declined candidates. Those candidates start unchecked and explain why they are unchecked. **Steps to reproduce** 1. Create a garden-inbox scan with an archive candidate in bucket A or B. 2. Leave the candidate unchecked in a confirmation pass. 3. Create a later confirmation for the same candidate. 4. Observe that the default selection does not preserve the earlier decline. **Paperclip version or commit** `7301fae942c3d5826974335cb40d6f1e0d95d1e0` **Deployment mode** Built from source. ## What Changed - Added repeatable `--unselect ISSUE_ID` parsing to the garden-inbox confirmation command. - Removed those issue IDs from the default checked options. - Added a description note for candidates declined in an earlier pass. - Rejected `--unselect` values that are not offered by the current scan. - Documented the repeat-pass workflow and added regression coverage. ## Verification - `node --test .agents/skills/garden-inbox/scripts/garden-inbox.test.mjs` - `git diff --check origin/master...HEAD` ## Risks - Low risk. The new option is opt-in, and existing confirmation behavior is unchanged when it is omitted. - An invalid issue ID now fails before any confirmation card is posted. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - OpenAI Codex, GPT-5 family. The runtime does not expose the exact deployment model ID or context-window size. Reasoning, repository tools, shell execution, and GitHub tools were enabled. ## 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> |
||
|
|
627728bdde |
feat: add authoritative issue PATCH receipts (#10478)
<!-- 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. > - Agents update tasks through the issue API. > - The update response did not state which values changed. > - Blocker updates also did not echo the scalar blocker IDs. > - Agents therefore used an extra GET request to confirm a successful write. > - This pull request adds an authoritative change receipt and an optional small response. > - The benefit is fewer API calls with a clear and compatible write contract. ## Linked Issues or Issue Description No public GitHub issue exists for this change. ### Subsystem affected Cross-cutting: `server/`, `packages/shared`, and the UI issue cache. ### Problem or motivation A successful issue PATCH returned the updated issue, but it did not identify the effective changes. Blocker writes returned relation summaries without the scalar IDs. Agents could not distinguish a confirmed clear operation from missing data. The response must confirm committed field and blocker changes while existing UI clients continue to receive the full issue by default. ### Proposed solution Add a `changes` receipt. Add a conditional `blockedByIssueIds` echo. Support `Prefer: return=minimal`. Keep the full response as the default. ### Alternatives considered Make the small response the default for agent tokens. This would create different response contracts by actor type, so this pull request does not use that design. ### Roadmap alignment This is a focused control-plane reliability improvement. It does not duplicate an open roadmap milestone. ## What Changed - Compute committed issue row and relation changes in the issue service. - Omit no-op fields and truncate changed long text values to 200 characters. - Echo blocker ID arrays for blocker set and clear requests. - Add the opt-in `Prefer: return=minimal` response and `Preference-Applied` header. - Keep receipt metadata out of React Query issue caches. - Add route and embedded Postgres tests for the new contract. ## Verification - `pnpm exec vitest run server/src/__tests__/issue-activity-events-routes.test.ts` - `pnpm exec vitest run server/src/__tests__/issues-service.test.ts -t "returns authoritative update receipts for row fields and blocker relations"` - `pnpm --filter @paperclipai/shared typecheck` - `pnpm --filter @paperclipai/server typecheck` - `pnpm --filter @paperclipai/ui typecheck` - `pnpm check:token-gates` - `git diff --check` ## Risks - Low compatibility risk. The default response only adds receipt fields. - Minimal mode is opt-in. Existing clients do not receive a smaller body. - The receipt excludes `updatedAt` because the response already returns it as the freshness anchor. - Prose API and agent workflow guidance will follow after the server contract is available. > 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 exact deployment ID, context window size, and reasoning mode are not exposed to the agent. The agent used repository tools, code execution, and test execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [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> |
||
|
|
b1ac92f305 |
fix(ui): recover blockquotes the markdown editor escaped as backslash-gt (#10466)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The web UI uses one shared markdown editor for comments, issue descriptions, and documents. > - Users type `>` at the start of a line to insert a blockquote. > - The live editor shortcut does not always run in every browser and input method. > - The markdown exporter then changes the leading `>` to `\>` and saves literal text. > - The saved text does not render as a blockquote. > - This pull request restores the blockquote marker when markdown enters or leaves the editor. > - The benefit is reliable blockquote insertion on every surface that uses the shared editor. ## Linked Issues or Issue Description No public GitHub issue exists. Related prior attempt: #10465. **What happened?** The shared markdown editor sometimes saved a blockquote as literal text. This happened when the live shortcut did not run. The exporter saved `\> text`, which rendered as literal `> text`. **Expected behavior** A line that starts with `>` must render as a blockquote in comments, issue descriptions, and documents. **Steps to reproduce** 1. Open a task comment composer, description editor, or document editor. 2. Add `> ` to an existing line, or use an input method that does not run the live shortcut. 3. Save the content. 4. Observe that the saved line renders as literal text instead of a blockquote. **Paperclip version or commit** `master` at `78f8c6c3d4`. **Deployment mode** Self-hosted server. ## What Changed - Add `unescapeBlockquoteMarkers()` to restore block-level `\>` markers. - Keep indented code, list content, nested content, and fenced code unchanged. - Apply the helper when markdown enters and leaves `MarkdownEditor`. - Add focused tests for line position, indentation, container prefixes, and CommonMark fence rules. ## Verification - `pnpm exec vitest run ui/src/lib/blockquote-markdown.test.ts` passes with 22 tests. - `pnpm exec vitest run ui/src/components/MarkdownEditor.test.tsx` passes with 37 tests. - `pnpm --filter @paperclipai/ui typecheck` passes. - `pnpm check:token-gates` passes. - `git diff --check origin/master...HEAD` passes. - A browser harness used the real `MarkdownEditor` and `IssueChatThread` composer. It confirmed that `> text` renders as a blockquote and exports as `> text`. - The [Cutter preview](https://github.com/paperclipai/paperclip/pull/10466#issuecomment-5140558263) supplies a task-page screenshot and an editor interaction video. ## Risks - Low risk. The helper returns the input unchanged when it contains no `\>`. - A paragraph that deliberately starts with literal `\>` now becomes a blockquote. The editor has no literal-marker control, so this matches the available input behavior. - There are no database, API, or migration changes. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used - Anthropic Claude Opus 4.8 (`claude-opus-4-8`, 1M context), extended thinking, with tool use and code execution. - OpenAI Codex with GPT-5 (`gpt-5`; runtime build and context-window metadata were not exposed), with reasoning, tool use, code execution, and GitHub review 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 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 (none needed) - [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> |
||
|
|
fc5a30805e |
feat(cli): add managed install, update, and service lifecycle (#10045)
## Thinking Path > - Paperclip is the open source control plane people use to manage AI-agent companies > - Operators need a predictable installation path that survives beyond an ephemeral `npx` process > - A durable installation needs an owned per-user payload store, stable command shim, safe shell integration, and supported service lifecycle > - Updates must preserve recoverability by backing up data, installing side-by-side, verifying the new payload, and retaining rollback state > - Bootstrap scripts and privileged service operations must fail closed across download, filesystem, ownership, and consent boundaries > - This pull request integrates managed install, update, rollback, service, uninstall, doctor, bootstrap-installer, and runtime-serving support into one workflow > - The benefit is a recoverable, inspectable, and documented installation lifecycle with explicit safety boundaries across Linux, macOS, containers, WSL, npm, npx, and source checkouts ## Linked Issues or Issue Description ### Problem Paperclip lacks a first-class durable installation and lifecycle workflow. Operators currently have to assemble npm/npx installation, PATH setup, background-service management, updates, rollback, diagnostics, and uninstall behavior themselves. That makes upgrades harder to recover, creates inconsistent behavior across platforms, and leaves shell/download/service trust boundaries without one documented implementation. ### Proposed Solution Add a managed per-user install store and stable shim, a verified shell bootstrap installer, service lifecycle commands, install-mode-aware update/rollback behavior, doctor checks, and documentation. Managed updates back up the database, install and smoke-test a side-by-side payload, atomically switch `current`, and retain prior payloads. The shell installer pins registry/download trust boundaries and requires explicit consent for non-interactive privileged actions. ### Alternatives Considered - Keep recommending `npx`: simple for evaluation, but ephemeral and unsuitable for stable services, atomic updates, or rollback. - Require global npm installation only: familiar, but cannot provide the owned side-by-side payload store and retained rollback semantics. - Split the capability across multiple PRs: rejected because install, update, service, uninstall, bootstrap, and serving behavior share contracts and security boundaries that need review together. ### Related Pull Requests - Supersedes #10042 and #10044 with one integrated final diff. - Incorporates and replaces the closed preparatory work in #10032 and #10034. ## What Changed - Added `paperclipai install`, `update`/`upgrade`, rollback, uninstall, service lifecycle, onboarding integration, and managed-install doctor checks. - Added a private managed payload store, verified manifest/marker ownership, exclusive mutation locks, atomic manifest/current/shim writes, retained previous payloads, and provenance validation. - Added npm and GitHub-ref install sources with exact target resolution, registry isolation, database backup, side-by-side verification, atomic activation, service restart coordination, and failure rollback. - Made managed-update backups report actionable service-start and `--no-backup` recovery guidance for unreachable databases, while clean never-onboarded instances skip an empty backup. - Added systemd user and launchd service definitions, status/health/log commands, single-instance coordination, stale-port recovery, and explicit sudo/lingering consent handling. - Added the `scripts/install.sh` bootstrap path with checked two-stage downloads, pinned public npm registry usage, platform checks, dry-run/non-interactive controls, and Docker fixtures. - Added embedded Postgres/native bootstrap integration, hot-restart/systemd-notify serving support, passive update notices, configuration contracts, README/CLI/install documentation, and focused regression tests. - Security re-review should explicitly re-verify: (1) `addManagedPathBlock`/`removeManagedPathBlock` reject symlinked or non-regular rc files, assert current-user ownership, preserve restrictive modes, and replace atomically; (2) managed shim replacement rejects unsafe parents, foreign-owned or multiply linked files, and uses checked atomic replacement; (3) the shell installer and sudo path preserve explicit consent and checked downloads; and (4) installed service/runtime serving remains bound to the validated managed shim and instance configuration. ## Verification - `bash -n scripts/install.sh scripts/clean-install-git.sh scripts/clean-install-npm.sh scripts/test-install-sh-docker.sh` - `pnpm exec vitest run cli/src/__tests__/install-store.test.ts cli/src/__tests__/install-command.test.ts cli/src/__tests__/managed-install-check.test.ts cli/src/__tests__/onboard-service.test.ts cli/src/__tests__/service-health-check.test.ts cli/src/__tests__/service-manager.test.ts cli/src/__tests__/update-command.test.ts cli/src/__tests__/update-notice.test.ts packages/db/src/embedded-postgres-native.test.ts` — 9 files, 66 tests passed - `pnpm --dir cli typecheck` - `pnpm --dir cli build` - Follow-up verification: `pnpm exec vitest run cli/src/__tests__/update-command.test.ts` (14/14), `pnpm --dir cli typecheck`, `pnpm --dir cli build`, and `pnpm --filter @paperclipai/server typecheck`. - `pnpm -r typecheck` - `pnpm build` - Full `pnpm test:run` exercised all suites; an injected static AWS credential changed one unrelated doctor expectation, which passed when those credentials were removed. A second run cleared that case and exposed stale pre-existing adapter-utils `dist` output; rebuilding `@paperclipai/adapter-utils` made the isolated test pass. The updated PR CI is the authoritative clean-workspace full-suite run. ## Risks - Installer/update code writes executable shims, symlinks, shell rc blocks, service definitions, and managed payloads; ownership, regular-file, symlink, hard-link, marker, and path-containment checks fail closed before destructive changes. - The bootstrap installer executes downloaded tooling; downloads are staged and checked before execution, npm traffic is pinned to the public registry, and non-interactive privileged behavior requires explicit consent. - Linux lingering may invoke `sudo`; the command is surfaced and confirmed before execution, and unsupported service managers fall back to foreground-run guidance. - Database migrations remain forward-only; payload rollback does not reverse migrations, so managed updates create a backup before activation unless explicitly disabled. - Service restart and runtime serving touch process/port ownership; lifecycle locks, health/version checks, and stable-shim service definitions reduce split-brain and stale-process risk. > 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 coding agents using GPT-5.5 and GPT-5.6-sol, with reasoning, repository/API access, shell execution, and test tooling. The runtime did not expose a reliable 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> Co-authored-by: Claude Fable 5 <noreply@anthropic.com> |
||
|
|
492555aaf9 |
design(decisions): flatten decision cards to two task-borrowed types (#10474)
The Decisions queue ran five parallel colour/icon vocabularies chosen by source kind, plus a separate severity badge, so two rows needing the same response could look unrelated and none of it matched the task list. Every row now resolves to one of two kinds, each borrowing the task status it corresponds to: blocking renders as `blocked`, review as `in_review`, both through StatusGlyph and the existing --status-task-icon-* tokens. Source kinds keep their own wording; only colour and icon merge. Card anatomy follows the design mock: no left accent rail, rounded cards 16px apart, a "/"-separated meta breadcrumb, a named See more / See less control, and no separately tinted drawer when expanded. Verb order is fixed across both states. Severity moves from chrome to a toolbar filter. Four defects fixed along the way: - blocked rows reported themselves as their own blocker (server-side) - the task key was missing wherever the row's subject IS the task - the task quicklook stuck open, because closing handed focus back to a trigger that opens on focus - the card ring appeared on click, and only on cards with a toggle Also: the standard task preview is aligned to its trigger's text and scales out of it, the task eyebrow renders its project as a tile, and the first motion tokens land alongside the disclosure and crossfade. Supersedes #9574 and #9575. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>canary/v2026.801.0-canary.2 |
||
|
|
c0b875c46c |
fix(codex): let sandbox runs use the sandbox image's own Codex login (#10582)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Codex agents can run inside sandbox environments, and operators can bake a Codex login into the sandbox image during interactive image setup > - Two credential gates (the control plane's pre-dispatch configuration-incomplete gate and the adapter's execute-time fail-fast) required host-side Codex credentials — a usable `auth.json` in the managed home or a configured `OPENAI_API_KEY` — regardless of where the run executes > - On managed cloud hosts a local Codex login never exists, so every sandbox run of a Codex agent failed immediately with "configuration incomplete: no Codex credentials available for managed home …", even though the adapter's inbound auth merge already supports the image-login case end to end > - This pull request makes the execute-time gate probe the sandbox for its own `~/.codex/auth.json` before failing, and exempts sandbox-destined runs from the pre-dispatch host check > - The benefit is that a sandbox image signed in to Codex is a first-class credential source, matching what the auth-merge, precedence-warning, and copy-back machinery were already built for ## Linked Issues or Issue Description **What happened?** Running a `codex_local` agent in a sandbox environment whose image carries a Codex login failed instantly with `configuration incomplete: no Codex credentials available for managed home "…/codex-home". Sign in to Codex on the host with a ChatGPT subscription, or bind a per-agent OPENAI_API_KEY secret for this agent.` The host has no Codex login and never will on a managed cloud deployment; the sandbox's own login was never consulted. **Steps to reproduce** 1. Configure a sandbox environment and capture a custom image after signing in to Codex inside the interactive image setup. 2. Create a `codex_local` agent that uses that environment, on a host with no Codex login and no `OPENAI_API_KEY` bound. 3. Start a run: it fails pre-dispatch with the configuration-incomplete blocker above. **Expected behavior** The run launches and Codex authenticates with the sandbox image's own login, the same way the adapter's host↔sandbox auth merge already keeps the sandbox credential when the host ships none. A run should only fail fast when neither the host, a bound `OPENAI_API_KEY`, nor the sandbox has credentials. **Paperclip version** Current `master` (cloud image deployments). **Deployment mode** Managed cloud stacks (any deployment where the server host has no local Codex login). ## What Changed - Extracted the adapter's execute-time gate into `assertCodexCredentialsLaunchable`: when host readiness fails and the target is a sandbox, it probes `~/.codex/auth.json` in the sandbox (same command the auth-precedence warning uses) and proceeds with a log line naming the credential source; when the sandbox has no login either, the error now names all three remediation options (sandbox image sign-in, per-agent `OPENAI_API_KEY`, host sign-in). Non-sandbox targets keep today's strict behavior byte-for-byte. - The control plane's pre-dispatch gate in `resolveExecutionRunAdapterConfig` now takes the selected environment's driver and skips the host-credential check for sandbox-destined runs — only the adapter can probe the sandbox once it is up, so the execute-time gate is the authority there. Non-sandbox runs keep the early, well-attributed configuration-incomplete blocker. - The codex Test flow needed no change: it already seeds host credentials only when they exist and otherwise leaves the sandbox's `CODEX_HOME` alone; this aligns the run path with it. ## Verification - `cd packages/adapters/codex-local && pnpm vitest run` — 210 tests, including new gate cases: sandbox login present (proceeds + logs source), sandbox and host both credential-less (fails with the extended message), non-sandbox target (strict host requirement kept, no sandbox probe), per-agent API key (no probe at all). - `cd server && pnpm vitest run src/__tests__/heartbeat-project-env.test.ts src/__tests__/codex-local-adapter-environment.test.ts` — includes the new sandbox-exemption case next to the existing blocker tests. - `pnpm run typecheck` in `server` and `packages/adapters/codex-local`. ## Risks - Sandbox-destined misconfigurations (no credentials anywhere) now surface at adapter execute time instead of pre-dispatch, so they read as an adapter failure with a precise message rather than a configuration-incomplete blocker. The trade-off is deliberate: the sandbox must be up to know whether credentials exist, and the failure message names the exact remediations. - The sandbox probe adds one short (5s-capped) shell command to sandbox runs whose host has no credentials; runs with host credentials or a bound key are untouched. - Self-hosted behavior is unchanged for local and SSH targets. ## Model Used Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — extended thinking, agentic tool use (file edits, vitest/tsc runs). No other models involved. ## 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 mergecanary/v2026.801.0-canary.1 |
||
|
|
7301fae942 |
fix(heartbeat): atomically claim due timer intervals (#10584)
<!-- 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 coordinates AI agents through scheduled heartbeat runs. > - The heartbeat scheduler can call `tickTimers()` again before an earlier tick has finished. > - Each overlapping tick can read the same old `lastHeartbeatAt` value and decide that the same agent is due. > - The existing queue checks do not make that due-time decision atomic. > - This pull request atomically advances the timer baseline before it enqueues the wake. > - The benefit is that one timer interval can create at most one scheduled run for an agent. ## Linked Issues or Issue Description No public GitHub issue describes this exact scheduler race. Related pull requests address active-run overlap or queued-run buildup, but they do not atomically claim a due timer interval: #9457, #8416, and #3858. **What happened?** Two overlapping calls to `tickTimers()` could both read the same due timer baseline. Both calls could enqueue a timer run for the same agent and interval. **Expected behavior** Only one scheduler tick must claim a due timer interval. A second overlapping tick must observe that the interval was already claimed and skip it. **Steps to reproduce** 1. Create an active agent with a 60-second timer interval. 2. Set `lastHeartbeatAt` to more than 60 seconds in the past. 3. Call `tickTimers(now)` twice with `Promise.all()`. 4. Observe that the old code can enqueue two runs for the same interval. **Paperclip version or commit** Reproduced on `master` before this branch. **Deployment mode** Local development with embedded PostgreSQL. ## What Changed - Added an atomic conditional update that claims a due timer interval by advancing `lastHeartbeatAt`. - Made `tickTimers()` enqueue only after that conditional update succeeds. - Preserved first-heartbeat telemetry when the timer claim advances `lastHeartbeatAt` before run completion. - Added regression tests for concurrent claims and first-heartbeat telemetry. ## Verification - `pnpm --filter @paperclipai/server exec vitest run src/__tests__/heartbeat-stale-queue-invalidation.test.ts` — 24 tests passed on the final rebased commit. - `pnpm --filter @paperclipai/server exec vitest run src/__tests__/heartbeat-process-recovery.test.ts -t "preserves first-heartbeat telemetry after a timer interval claim|tracks the first heartbeat with the agent role"` — 2 tests passed. - `pnpm --filter @paperclipai/server typecheck` — passed after the review fix. - `pnpm -r typecheck` — passed. - `pnpm build` — passed. - `pnpm test:run` — 3,121 tests passed and 2 tests skipped. One unrelated runtime-skills test exceeded its 5-second limit under full-suite load. - `pnpm --filter @paperclipai/server exec vitest run src/__tests__/heartbeat-runtime-skills.test.ts` — the timed-out file passed in isolation, 2 tests passed. - `pnpm --filter @paperclipai/db exec vitest run src/status-card-migrations.test.ts` — the unrelated CI timeout passed in isolation. - The full PR CI matrix passed after one rerun of that unrelated timeout. - Greptile passed with zero new comments and no unresolved review threads. ## Risks - Low risk. The change only affects due timer claims. - If enqueue fails after the claim, the next timer attempt waits for one interval. This is safer than duplicate agent execution. - No schema, migration, API, UI, or dependency changes are included. > 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 exact deployment ID and context-window size are not exposed to this runtime. Agentic reasoning, shell tools, code execution, and GitHub operations were enabled. ## 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.801.0-canary.0 |
||
|
|
90ead239a8 |
feat(ui/server): name cross-company environment secret refs instead of calling them missing (#10577)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Environment configs (sandbox providers, SSH) can bind stored company secrets through `format: "secret-ref"` fields, picked in the environment editor's secret picker > - Environments are instance-scoped and shared by every company on an instance, but the picker lists only the current company's secrets, so a ref pointing at another company's secret renders as "Missing secret (…)" in destructive styling > - That state is indistinguishable from a genuinely deleted secret, so operators "fix" a healthy binding by creating a duplicate secret in their own company — the exact sequence that used to corrupt bindings before #10576 > - This pull request adds an instance-gated metadata endpoint for an environment's secret refs and teaches the picker to name a cross-company secret and its owner honestly > - The benefit is that operators can tell a healthy cross-company binding from a broken one, and stop creating duplicate secrets ## Linked Issues or Issue Description **Is your feature request related to a problem? Please describe.** In the environment editor, a secret-ref field that points at a secret owned by a different company shows "Missing secret (22095402…)" in red, with "The previously selected secret is no longer available. Pick another or remove the binding." The binding is actually healthy — the current company's picker just cannot list the other company's secrets. Operators react by creating a duplicate secret and re-pointing the field. **Describe the solution you'd like** The editor should know the referenced secret's name, status, and owning company (metadata only, never the value) and present a cross-company ref neutrally, a deleted secret as deleted, and only an unknown id as missing. Related: #10576 (fixes the binding corruption this UI state used to trigger). ## What Changed - New `GET /environments/:id/secret-refs` returns `{ refs: [{ configPath, secretId, name, status, companyId, companyName }] }` for the environment's config-derived secret refs. Values are never returned. The route sits behind `assertCanAccessInstanceEnvironments`, the same gate as environment editing. - New `secretService.describeSecretRefs` loads that metadata across companies; unknown ids are omitted. - `SecretBindingPicker` reads an optional `SecretRefHintsContext` (keyed by secret id). With a hint, a ref the company list cannot show renders as `NAME — Owning Company` with neutral styling and the note "Owned by the … company. The binding keeps working; selecting a secret from this list re-points it here." A hint with `status: "deleted"` reports the secret as deleted. Without hints, behavior is byte-identical to before — agent editors and other picker users are unaffected. - `CompanyEnvironments` fetches descriptors for the environment being edited and provides them through the context. ## Verification - `cd server && pnpm vitest run src/__tests__/environment-routes.test.ts src/__tests__/secrets-service.test.ts` — new endpoint happy path, agent 403 (descriptors never computed), and embedded-Postgres coverage proving cross-company names resolve and unknown ids drop out. - `cd ui && pnpm vitest run src/components/SecretBindingPicker.test.tsx src/components/JsonSchemaForm.test.tsx src/pages/CompanyEnvironments.test.tsx` — hinted cross-company rendering, hinted deleted secret, and unchanged no-hint fallback. - `pnpm run typecheck` in `server` and `ui`. - Manual: edit an environment whose secret-ref field references another company's secret; the field names the secret and its owning company instead of "Missing secret". ## Risks - The endpoint exposes secret names and company names across companies to instance-level environment editors. Those actors already manage instance-shared environments (and instance admins are implicit members of every company), so this reveals no secret material and no new reach; the service method documents that callers must sit behind an instance-level gate. - UI change is additive and context-gated; pickers without a provider render exactly as before. ## Model Used Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — extended thinking, agentic tool use (file edits, vitest/tsc runs). No other models involved. ## 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 mergecanary/v2026.731.0-canary.27 |
||
|
|
f51cba33fa |
fix(server): keep environment secret bindings consistent when re-pointing config secrets (#10576)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agents can run inside environments (SSH boxes, sandbox providers); a sandbox environment's config can reference stored company secrets (for example a provider API key) through `format: "secret-ref"` fields > - Environments are instance-scoped and shared by every company on an instance, but `company_secret_bindings` rows are company-scoped, and the environment routes synced config-derived bindings under one guessed "context company" resolved from the environment's existing bindings > - When a save re-pointed a secret-ref field at a secret owned by a different company, the binding sync threw after the config row had already been persisted: the config referenced the new secret, the binding still pointed at the old one, every later lease acquisition failed with `Secret is not bound to environment:<id> at apiKey`, and the stale cross-company binding made every later save fail with a company-context conflict — with no route-level way to recover > - This pull request makes config-derived bindings follow the company that owns each referenced secret, and makes the environment write and its binding syncs atomic > - The benefit is that environment saves can no longer strand an environment in a half-updated state that breaks all of its runs ## Linked Issues or Issue Description Refs #10577 (companion UX change: the editor state that nudges operators into this sequence). **What happened?** Saving an environment whose secret-ref config field points at a secret owned by a different company than the environment's existing binding partially applied: the config row updated, the binding sync failed server-side, and the environment was left referencing a secret it has no binding for. Every run that leased the environment then failed with `lease_acquire_failed: ... Secret is not bound to environment:<id> at apiKey`, and every later save of the environment returned 409 `Environment secret bindings already use a different company context.` — with no route-level way to recover. **Steps to reproduce** 1. On an instance with two companies, create a sandbox environment from company A with a picker-bound API-key secret owned by A (the binding lands in A). 2. From company B, create a new secret and re-point the environment's API-key field at it, then save. 3. The save persists the config but the binding sync throws, so no binding for B's secret exists. 4. Run any agent that uses the environment, or try to save the environment again. **Expected behavior** The save either fully applies (config and bindings consistent) or fully fails. Re-pointing a config secret ref to a secret owned by another company moves the binding with the secret. **Paperclip version** Reproduced on current `master` (also present on recent release images). **Deployment mode** Multi-company server deployment (any mode with more than one company on the instance). ## What Changed - New `secretService.replaceSecretRefsForInstanceTarget`: writes each config-derived binding under the company that owns the referenced secret, replaces all non-`env.*` bindings of the target across every company, and validates every ref (secret exists, not deleted, config-path and projection-class rules) before any row is written. `env.*` env-var bindings stay company-scoped and untouched. - The environment create and update routes now run the environment write and its binding syncs inside one `db.transaction`, threading the transaction through new optional executor seams on `environmentService.create/update` and the existing `SecretBindingDb` seam pattern, so an invalid ref rolls the whole save back instead of leaving a half-updated environment. - `resolveEnvironmentSecretContextCompanyId` no longer lets existing bindings veto the caller's context (the 409s above); it now only picks where new raw-pasted secrets are created and how env-var bindings and probes resolve: explicit route/query company first, then the single company the bindings live in, then the actor's company. ## Verification - `cd server && pnpm vitest run src/__tests__/environment-routes.test.ts src/__tests__/environment-instance-routes.test.ts src/__tests__/secrets-service.test.ts src/__tests__/environment-custom-image-routes.test.ts` (165 tests, includes new coverage below) - New embedded-Postgres tests prove: a re-point moves the binding to the new secret's company and deletes the stale row; refs across several companies each bind under their own secret's company; an unknown secret ref rejects without touching existing bindings; `env.*` rows survive config-ref replacement. - New route tests prove: a cross-company re-point that previously 409'd now saves, with the update and binding replacement on the same transaction executor; a failing ref surfaces as 422. - `cd server && pnpm run typecheck` ## Risks - Behavioral shift: environment saves no longer 409 on a company-context mismatch between the caller and existing bindings; bindings follow the referenced secret's company instead. Environment routes are instance-admin gated, and instance admins already had access to every company's secrets by passing the company explicitly, so this removes an ordering trap rather than widening access. - Runtime lease resolution is unchanged: a run still resolves environment secrets under the run's own company, so an environment referencing company B's secret still only leases for company B runs (fail-closed as before). - The delete route's per-company binding cleanup is unchanged. ## Model Used Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — extended thinking, agentic tool use (file edits, vitest/tsc runs). No other models involved. ## 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 mergecanary/v2026.731.0-canary.26 |
||
|
|
32c1a8576c |
fix(server): self-heal execution workspaces whose recorded branch no longer exists (#10578)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Each agent task gets an execution workspace (a git worktree) with a recorded branch name; workspace validation compares that record to the worktree before every run > - Agents sometimes rename their task branch (for example to a `feat/*` PR branch), so the recorded branch never existed or was deleted > - Validation then fails every run with "expected branch does not exist" — a deterministic `workspace_validation_failed` loop with no self-heal path > - A recorded branch with no resolvable commit has nothing to lose, so adopting a clean, registered checked-out branch is trivially forward-only > - This pull request routes that exact case through the existing audited forward-reconciliation path, in both the runtime and the manual board reconcile endpoint > - The benefit is that these stranded workspaces heal themselves while dirty worktrees, detached HEADs, unregistered paths, and ambiguous git states all stay fail-closed ## Linked Issues or Issue Description No public GitHub issue exists; the underlying bug is described here per `bug_report.yml`. Related PR: #10574 self-heals the sibling provisioning failure loop uncovered by the same incident diagnosis. **What happened?** An execution workspace whose recorded branch was renamed away failed every subsequent run with `workspace_validation_failed` ("expected branch does not exist"). The safe-repair matrix refused the case, so the task stayed blocked until a human intervened. **Expected behavior** When the recorded branch is confirmed absent and the worktree is clean and registered with its checked-out branch matching HEAD, Paperclip adopts the checked-out branch through the audited forward-reconciliation path and the next run proceeds. **Steps to reproduce** In an isolated workspace, rename the task branch (`git branch -m <recorded> feat/something`) or delete the recorded branch, leave the worktree clean, then start a new run on the task. Validation fails on every retry. **Paperclip version or commit** master as of the branch point of this PR. **Deployment mode** Local trusted deployment with git-worktree isolated workspaces. ## What Changed - `ensureGitWorktreeBranchCoherent` (workspace runtime): a missing recorded branch with a clean worktree, an existing checked-out branch, and a registered branch matching HEAD now goes through audited forward reconciliation instead of failing closed. Gated behind `enableWorkspaceBranchReconcileForward`. - `reconcileExecutionWorkspaceBranch` mode `forward` (service): accepts the same case so the board reconcile endpoint can repair it manually. - The service inspection now classifies each branch ref as `resolved` / `missing` / `error` (`git rev-parse --verify --quiet`, distinguishing an absent ref from git failing to inspect the repo). Adoption requires a confirmed-missing recorded ref **and** a resolved target ref, so a git error can never bypass ancestry validation and a nonexistent branch name is never persisted. - Removed the test that asserted this case fails closed; it is superseded by tests that assert the new behavior. - New tests: successful adoption, dirty-worktree refusal, refusal when the checked-out branch ref does not resolve either, and disabled-flag behavior. ## Verification - `server`: `npx vitest run src/__tests__/execution-workspaces-service.test.ts -t "reconcil"` — 11 passed. - `server`: `npx vitest run src/__tests__/workspace-runtime.test.ts -t "adopt"` — 7 passed. - `npx tsc --noEmit` in `server/` is clean. - Manually validated the underlying repair on a live stranded workspace before automating it: creating the recorded branch at the clean HEAD ended the validation-failure loop without touching the agent's PR branch. ## Risks - The change relaxes a fail-closed gate, so the main risk is over-adoption. Mitigations: the exception requires flag-on, clean worktree, registered worktree path, registered branch matching HEAD, a confirmed-missing (not merely unreadable) recorded ref, and a resolvable target ref; everything else still fails closed. Every adoption goes through the audited reconcile path with an issue comment trail. - No migrations, no API surface changes (the reconcile route returns the same hand-picked fields). ## Model Used Claude Fable 5 (Anthropic, model ID `claude-fable-5`), extended thinking, agentic tool use (Claude Code harness). ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
79eff0aea1 |
fix(scripts): self-heal isolated workspace provisioning when the base CLI is broken (#10574)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agents run tasks in isolated execution workspaces that are provisioned as git worktrees by `scripts/provision-worktree.sh` > - The script runs the base workspace's CLI (`cli/src/index.ts` via the base `tsx` install) to seed each new worktree, and it only checked that those files exist > - pnpm links each package's `node_modules` into a hash-versioned virtual store; a lockfile change followed by a partial or filtered install prunes old hashed dirs without relinking every package, leaving dangling symlinks > - A CLI with dangling symlinks fails ESM resolution (`ERR_MODULE_NOT_FOUND`) at boot, so provisioning aborts with `setup_failed` — deterministically, on every retry, with no self-heal path > - This pull request makes provisioning health-check the CLI by actually booting it, repair the base install when the check fails, and degrade to the no-CLI fallback config instead of failing the run > - The benefit is that a class of permanent `setup_failed` loops becomes self-healing, and workspace provisioning survives a broken base CLI ## Linked Issues or Issue Description No public GitHub issue exists; the underlying bug is described here per `bug_report.yml`. Related PR: #10578 self-heals the sibling workspace-validation failure loop uncovered by the same incident diagnosis. **What happened?** Isolated-workspace runs failed at provision time with `setup_failed`. Every retry failed identically. One observed incident burned 4 runs across two adapters before the task was stranded. **Expected behavior** Provisioning either succeeds or degrades gracefully; a broken base CLI install repairs itself instead of permanently blocking all new worktrees. **Steps to reproduce** In the base workspace, cause a lockfile-affecting dependency bump plus a partial/filtered `pnpm install` so a package symlink (e.g. `cli/node_modules/drizzle-orm`) dangles into a pruned virtual-store dir. Start any isolated-workspace run. Provision fails with `ERR_MODULE_NOT_FOUND` and the run ends `setup_failed`; retries never recover. **Paperclip version or commit** master as of the branch point of this PR. **Deployment mode** Local trusted deployment with git-worktree isolated workspaces. ## What Changed - `base_cli_healthy` now boots the base CLI (`--help`) instead of only testing file existence, which exercises the top-level import graph. - New `repair_base_workspace_install`: when the health check fails, run a non-interactive `pnpm install --prod=false --force --frozen-lockfile` in the base workspace. `--force` guarantees relinking when pnpm's up-to-date heuristics would skip dangling symlinks; `--frozen-lockfile` keeps the repair from mutating the shared lockfile. - The repair install is serialized with `flock` on a lock file inside the resolved git dir (`git rev-parse --absolute-git-dir`), so locking also covers base workspaces that are linked worktrees, where `.git` is a file. - If every CLI candidate is unusable (including a base CLI the repair could not fix), provisioning falls back to the existing no-CLI fallback config writer (loudly, on stderr) instead of failing the run. A CLI that runs and fails `worktree init` still fails provisioning with its real exit code — that deliberate fail-closed policy is unchanged and covered by an existing server regression test. - Fixed a latent bug: `run_isolated_worktree_init` returned 0 unconditionally after the init subshell, so callers treated a failed init as success. Exit codes now propagate. ## Verification - Reproduced the incident state (dangling `cli/node_modules/drizzle-orm` symlink); the base CLI failed with the exact `ERR_MODULE_NOT_FOUND` seen in the incident run logs. - Ran the patched script against a fresh scratch worktree: health check failed → locked repair install ran (~26 s warm) → symlink relinked → `worktree init` completed → exit 0 with `.paperclip/config.json` and `.env` written. - Happy path (healthy base CLI): provisioning behavior unchanged, exit 0. - Verified `git rev-parse --absolute-git-dir` resolves a real directory for both a normal checkout and a linked worktree. - New hermetic tests: `node --test ./scripts/__tests__/provision-worktree-self-heal.test.mjs` (4 tests: healthy CLI used, broken CLI degrades, locked repair end-to-end with a fake pnpm, init failure propagates). Not yet wired into a CI workflow. - `server`: the existing `realizeExecutionWorkspace` fail-closed regression test ("fails instead of writing an unseeded fallback config when worktree init errors after CLI detection succeeds") passes against the new script. - `bash -n scripts/provision-worktree.sh` is clean. ## Risks - Low risk overall: the script only adds recovery paths; the happy path is unchanged. - The repair install runs in the shared base workspace. It is bounded by `--frozen-lockfile` (no lockfile mutation) and serialized by `flock`, but it can add ~30 s to the first provision after a base install breaks. - If the repair cannot fix the CLI and no other CLI candidate exists, runs now continue with an unseeded fallback config instead of failing; that is intentional, and the fallback path already existed. Genuine `worktree init` failures from a working CLI still fail the run. ## Model Used Claude Fable 5 (Anthropic, model ID `claude-fable-5`), extended thinking, agentic tool use (Claude Code harness). ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com> Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.731.0-canary.25 |
||
|
|
3176e1f4b9 |
chore(lockfile): refresh pnpm-lock.yaml (#10573)
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> |
||
|
|
2cbbad16fd |
docs: add External Task Protocol specification (#10568)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Teams often keep their work in external ticket systems > - Paperclip must keep execution and governance control when those systems supply work > - Connector authors need one provider-neutral contract for links, routing, sync, and recovery > - This pull request adds that contract as a draft specification > - The benefit is a shared design for external task connectors without provider logic in Paperclip core ## Linked Issues or Issue Description - Refs: #3587 - Related prior connector work: #6271 - Supersedes the non-reopenable #7904. - Duplicate #10569 was opened during concurrent recovery; this PR contains the later review fixes and is the 5/5, green canonical replacement. Recommend closing #10569 without merging it. ## What Changed - Added a normative External Task Protocol for provider-neutral task data, link state, routing, synchronization, conflicts, and health. - Defined a Symphony-compatible execution profile that uses Paperclip checkout, heartbeat, workspace, budget, approval, and blocker controls. - Added provider profiles for Linear, Jira, Asana, Notion, Trello, and GitHub Issues. - Fixed the link-state transitions, reopen invariant, and section cross-reference found in review. - Updated the connector boundary to name the current capability-gated plugin host clients. - Aligned backlog wake semantics and issue-scoped run-stop conformance with the current host API. - Made terminal-to-active follow-up handoff retry-safe using a single-record `TaskLink` retarget and auditable handoff history, without assuming unavailable multi-record plugin transactions. - Preserved and refreshed Dotta's original specification work. ## Verification - Ran `git diff --check`. - Checked every numbered section reference against the document headings. - Compared the protocol integration points with the current plugin SDK and host issue contracts. - Ran the current PR template, linked-issue, duplicate-search, and test-coverage quality gates against this description and title. ## Risks - Low runtime risk. This pull request changes documentation only. - The specification can constrain future connector designs. It remains a draft and keeps provider-specific behavior in plugins. - The replacement keeps the legacy `PAP-10377-task-protocol` head ref because renaming it closed #7904 and GitHub would not reopen that PR after the ref was restored. > 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 - The original draft used Claude Code. This review repair used OpenAI Codex with GPT-5. The exact API snapshot and context window are not exposed. The run used reasoning, repository inspection, shell tools, and code editing. ## 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] Branch naming exception is documented above; the legacy public issue branch was retained to preserve the repaired head ref - [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> Co-authored-by: Dev Agent <dev@paperclip.ing>canary/v2026.731.0-canary.24 |
||
|
|
ea0dd3917e |
build: make image layer caching actually hit (#10571)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Published container images are the deployable unit for self-hosted and managed instances, so merge-to-image latency bounds every deploy iteration > - The docker workflow configures BuildKit caching, but builds still ran ~12+ minutes essentially cold > - Two causes: the most expensive layer (four CLI toolchains + apt) is ordered after the always-changing app copy so it can never cache, and the type=gha cache's 10GB repo cap means the two multi-arch mode=max jobs evict each other > - This pull request reorders the tool layer above the app copy (with a weekly epoch so @latest tools keep advancing) and switches both jobs to registry-backed cache in ghcr > - The benefit is that warm builds shrink to roughly the app build + push, targeting the sub-5-minute range together with the amd64-only cloud variant ## Linked Issues or Issue Description No existing public issue — inline description following the feature request template: **Subsystem affected** CI / release publishing (docker workflow, Dockerfile) **Problem or motivation** Despite `cache-from/cache-to` being configured, image builds run effectively cold: (1) the production stage installs four CLI toolchains + apt packages *after* `COPY --from=build /app /app`, and since the app copy changes every commit, that most-expensive layer rebuilds every build, per arch; (2) the `type=gha` BuildKit cache is capped at 10GB per repository, and two multi-arch `mode=max` jobs overflow and evict each other's entries. **Proposed solution** Order the tool/OS layer before the app copy (it references nothing from `/app`), refresh it weekly via a `CLI_TOOLS_CACHE_EPOCH` build arg so the `@latest` tools don't freeze in the cache, and move both jobs to registry-backed BuildKit cache (`:buildcache` / `:buildcache-cloud` refs in ghcr, no size cap, separate refs so the parallel jobs don't clobber each other). **Alternatives considered** Pinning CLI tool versions instead of the weekly epoch — more deterministic, but adds a version-bump chore; the weekly epoch preserves current freshness semantics with bounded staleness. Keeping type=gha with `mode=min` — smaller cache but loses intermediate-stage reuse, which is where most of the win is. **Roadmap alignment** Not on ROADMAP.md; CI/publishing speed improvement only. ## What Changed - `Dockerfile`: the production stage's tool/OS `RUN` (npm --global CLIs, apt, `/paperclip` setup) moves above `COPY --from=build /app /app`; new `CLI_TOOLS_CACHE_EPOCH` arg consumed by that layer. The `cloud` stage is unaffected — it only layers plugin dists on top of the finished production stage. - `.github/workflows/docker.yml`: both jobs stamp the ISO week into `CLI_TOOLS_CACHE_EPOCH`, and both switch `cache-from/cache-to` from `type=gha` to `type=registry` with per-job refs. - Includes the one-line amd64-only cloud-variant commit from #10570 so the two PRs can't conflict; if #10570 merges first, this PR rebases down to a single commit automatically. ## Verification - Image content is unchanged by layer reordering: the moved `RUN` references nothing from `/app`, and Docker layer ordering only affects caching, not the final filesystem (tool installs and app copy touch disjoint paths). - The cache ref is written only by this workflow — `docker.yml` runs on master/tag pushes, never on PRs — so the workflow's existing "no shared caches into build inputs" supply-chain stance is unchanged (BuildKit layer cache was already accepted via type=gha; the registry backend has the same writer trust). - Runtime proof lands with the first two master builds after merge: the first warms the cache, the second should show the tool layer and deps/build stages as CACHED in the build log, with wall clock dropping accordingly. I'll be watching those as part of managed-deploy work. ## Risks - Low. Worst case the registry cache misses (cold-build behavior, same as today). The weekly epoch means CLI tools update at most a week late inside images; a release built mid-week ships the tools from that week's first build. Cache refs add two small artifacts to ghcr. ## Model Used Claude Fable 5 (`claude-fable-5`, extended thinking, via Claude Code with tool use and code execution). ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass (no test-affecting changes) - [x] I have added or updated tests where applicable (n/a — build config and layer ordering only) - [x] I have updated relevant documentation to reflect my changes (in-file comments document both mechanisms) - [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 mergecanary/v2026.731.0-canary.23 |
||
|
|
1ee1275f11 |
fix(adapters): persist ACPX process identity for hot restart (#9838)
## Thinking Path > - Paperclip is the open source control plane people use to manage AI agents for work. > - Local agent heartbeats need durable process identity so the server can supervise them. > - The ACPX runtime owns the child process used by `codex_local` sessions. > - ACPX did not expose the child PID and start time to the Paperclip adapter. > - Warm ACPX runtimes can also serve a later heartbeat without a new spawn event. > - A hot restart could therefore classify a live Codex run as lost because its heartbeat row had no process identity. > - This pull request forwards ACPX spawn identity, reuses it for compatible warm heartbeats, and fails closed when identity cannot be persisted. > - The benefit is reliable hot-restart adoption for eligible local Codex runs. ## Linked Issues or Issue Description No matching public GitHub issue was found. **What happened?** A `codex_local` heartbeat could run through ACPX without a persisted `processPid` or `processStartedAt`. A Paperclip hot restart then had no durable identity for the live ACP child. Recovery could classify the run as `process_lost` even while the child was still alive. **Expected behavior** ACPX reports the real child PID and start time before the first prompt. A compatible warm runtime reports the same known identity to each later heartbeat that reuses the child. ACPX stops the child if the identity is invalid or persistence fails. Hot-restart recovery can then adopt the live run. **Steps to reproduce** 1. Start a `codex_local` heartbeat through the ACPX execution lane. 2. Keep the run active during a Paperclip hot restart. 3. Inspect the heartbeat row before this change. 4. Observe that the process identity can be null and recovery cannot adopt the live child. **Reproduced on** - Paperclip `master` before this change. - Linux source deployment. - `codex_local` with ACPX `0.12.0`. ## What Changed - Add an awaited `onAgentSpawn` lifecycle hook to the patched ACPX runtime. - Forward the ACP child PID and start time through the adapter `onSpawn` callback. - Keep a mutable callback sink for cached runtimes so a later respawn updates the current heartbeat. - Reuse the last known process identity when a compatible warm heartbeat reuses the existing child. - Kill the ACP child and fail session startup when the PID is invalid or identity persistence rejects. - Add ACPX and heartbeat recovery tests for callback ordering, warm reuse, failure cleanup, durable row identity, and hot-restart adoption. - Document the one-time drain required when an installed pre-fix run already lacks process metadata. ## Verification - `PAPERCLIP_HOME="$PAPERCLIP_RUN_SCRATCH_DIR/test-home-execute-escalated" pnpm exec vitest run packages/adapter-utils/src/acpx-engine/execute.test.ts` — 89 passed. - `PAPERCLIP_HOME="$PAPERCLIP_RUN_SCRATCH_DIR/test-home-recovery-escalated" pnpm exec vitest run server/src/__tests__/heartbeat-process-recovery.test.ts` — 92 passed. - `PAPERCLIP_HOME="$PAPERCLIP_RUN_SCRATCH_DIR/test-home-remote-smoke-escalated" pnpm exec vitest run packages/adapter-utils/src/acpx-engine/remote-spawn-smoke.test.ts` — 3 passed. - `PAPERCLIP_HOME="$PAPERCLIP_RUN_SCRATCH_DIR/test-home-ci-repro-escalated" pnpm exec vitest run server/src/__tests__/heartbeat-dependency-scheduling.test.ts` — 6 passed. - `pnpm --filter @paperclipai/adapter-utils typecheck` — passed. - Reverse and forward dry-run application of `patches/acpx@0.12.0.patch` — passed. - `git diff --check` — passed. - `git diff --exit-code origin/master...HEAD -- pnpm-lock.yaml` — passed. - `git diff --exit-code origin/master...HEAD -- .github/workflows` — passed. ## Risks - Runtime risk is low to moderate. ACPX now awaits process-identity persistence during child startup. - ACPX kills the child when persistence fails. This prevents an unsupervised process, but it makes that heartbeat fail visibly. - A compatible warm heartbeat reuses the identity of the existing ACP child. Regression tests verify that identity is persisted before the next prompt. - The change updates the vendored ACPX patch. Package installation must apply that patch. - There are no schema, migration, public API, UI, workflow, or lockfile changes. > 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 used GPT-5.3-Codex for the earlier implementation. - OpenAI Codex used GPT-5 for the lifecycle-hook revision and the current fail-closed review fix. The runtime did not expose a more specific snapshot ID or context-window size. Both runs used reasoning, repository tools, and code execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.731.0-canary.22 |
||
|
|
54e2031e87 |
fix(ui): accessibility and cleanup for IssueWorkspaceCard copy button (#1832)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The board UI shows the workspace attached to an issue in `ui/src/components/IssueWorkspaceCard.tsx` > - That card renders values such as the branch name and the workspace path through a small `CopyableInline` component, each with an icon-only copy button > - The button has a `title` attribute only. Screen readers do not announce `title` reliably. A screen reader user hears no useful name for the button, because the button contains an icon and no text > - The button also starts a 1.5 second `setTimeout` to reset its "copied" state. Nothing clears that timer. If the card unmounts first, the callback sets state on an unmounted component > - This pull request adds a dynamic `aria-label` to the button and clears the timer in a `useEffect` cleanup > - The benefit is a copy control that assistive technology can announce, and no stray timer after the card unmounts ## Linked Issues or Issue Description No existing GitHub issue covers this. The problem is described below with the fields from [`bug_report.yml`](.github/ISSUE_TEMPLATE/bug_report.yml). **What happened?** Open an issue that has a workspace attached. Tab to the copy button next to the branch or the workspace path in the workspace card. The screen reader announces an unlabeled button, because the button holds only a lucide `Copy` icon and a `title` attribute. Separately, copy a value and navigate away within 1.5 seconds. The pending `setTimeout` then calls `setCopied(false)` on an unmounted component. **Expected behavior** The copy button has an accessible name that says what it copies, and the name changes to confirm the copy. The reset timer is cleared when the component unmounts. **Steps to reproduce** 1. Run the app locally with `pnpm dev`. 2. Open an issue that has a workspace attached, so `IssueWorkspaceCard` renders. 3. Turn on a screen reader (VoiceOver, NVDA). 4. Tab to the copy button next to the workspace path or the branch name. The button has no useful accessible name. 5. Click the copy button, then navigate away from the issue in under 1.5 seconds. The reset timer is still pending. **Paperclip version or commit** Reproducible on `master` at this pull request's base commit. **Deployment mode** Local dev (pnpm dev). Related pull request, not a duplicate: #3531 makes copy-to-clipboard buttons work in non-secure contexts. That pull request changes the clipboard write path. This one changes the button label and the timer cleanup, so the two do not overlap. ## What Changed - Added an `aria-label` to the `CopyableInline` copy button in `ui/src/components/IssueWorkspaceCard.tsx`. The label reads `Copy <label>` (for example "Copy branch"), falls back to `Copy value` when the component gets no `label` prop, and changes to `Copied to clipboard` after a copy. - Added a `useEffect` cleanup that calls `clearTimeout(timerRef.current)` on unmount, so the 1.5 second reset timer cannot fire after the component unmounts. ## Verification - CI is green on this pull request. - Static check: `pnpm -r typecheck`. - Test suite: `pnpm test`. - Manual, screen reader: open an issue with a workspace, tab to the copy button next to the path or the branch, and confirm the announcement is "Copy path" or "Copy branch". Activate the button and confirm the announcement changes to "Copied to clipboard". - Manual, timer: click the copy button and navigate away from the issue immediately. Confirm the console shows no unmounted-component state update. ## Risks Low risk. The change adds one ARIA attribute and one unmount cleanup in a single presentational component. No behavior changes for mouse users, no API or schema change. `clearTimeout(undefined)` is a no-op, so the cleanup is safe when the user never copied. ## Model Used - Anthropic Claude Opus, model ID `claude-opus-4-6`, 200K context window, extended thinking enabled, with tool use for file edits. - Recorded by a maintainer while bringing this description up to the current template. The original description predates the Model Used requirement, so the author did not state a model. Author: please correct this line if the model was different. ## 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 - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge Notes on the checklist: no test or documentation change applies to a two-line ARIA and cleanup fix in one component. The Greptile box stays unchecked until the current review round closes.canary/v2026.731.0-canary.21 |
||
|
|
2137f85e1d |
fix(ui): correct Last checked timestamp in workspace close dialog (#2179)
## Problem
In the ExecutionWorkspaceCloseDialog, the "Last checked" timestamp was
using \`new Date()\` which show the current render time, not when the
readiness check API call actually completed.
\`\`\`tsx
Last checked {formatDateTime(new Date())} // always NOW
\`\`\`
This mean every time React re-render the component (which happen
frequently), the timestamp update to the current moment. User see "Last
checked 2:45:30 PM" and think the check just ran, but actually it might
have ran 30 seconds ago. The timestamp is lying.
## What I changed
Changed from \`new Date()\` to \`new
Date(readinessQuery.dataUpdatedAt)\` which is the actual timestamp from
React Query tracking when the API response was last received.
\`\`\`tsx
Last checked {formatDateTime(new Date(readinessQuery.dataUpdatedAt))} //
actual check time
\`\`\`
Now the timestamp accurately show when the close readiness check was
performed. It stay stable between re-renders until the query actually
refetch.
## How to test
1. Open an execution workspace > click Close button to open the dialog
2. The "Last checked" timestamp should show when the API call completed
3. Wait a few seconds - timestamp should NOT update (it's the query
time, not render time)
4. Click "Recheck" or trigger refetch - timestamp should update to new
fetch time
1 file, 1 line changed.
canary/v2026.731.0-canary.20
|
||
|
|
ce40343b9c |
build: publish the cloud image variant for amd64 only (#10570)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Published container images are how both self-hosted users and managed-deployment hosts run it > - The repository publishes two variants: the self-hosted image and a cloud variant for managed deployments > - The cloud variant was built for amd64+arm64, but its only consumers are managed-deployment hosts, which run amd64 > - The QEMU-emulated arm64 half dominates the build's wall clock, delaying every merge-to-deployable-image cycle > - This pull request drops arm64 from the cloud variant only, keeping the self-hosted image multi-arch > - The benefit is roughly halving the time from merge to a deployable cloud image, with no change for any actual consumer ## Linked Issues or Issue Description No existing public issue — inline description following the feature request template: **Subsystem affected** CI / release publishing (docker workflow) **Problem or motivation** The cloud image variant builds for `linux/amd64,linux/arm64`, but the arm64 half runs under QEMU emulation and dominates the job's wall clock — while no consumer of the cloud variant runs arm64 (managed-deployment hosts are amd64). Every deploy iteration pays ~double the necessary build time. **Proposed solution** Build the cloud variant amd64-only. The self-hosted image keeps `amd64+arm64` so ARM users (Apple Silicon, ARM servers) are unaffected. **Alternatives considered** Keeping multi-arch but building arm64 on native arm64 runners with a manifest merge — faster than QEMU and worth doing for the self-hosted image if its build time becomes a pain point, but unnecessary complexity for a variant with no arm64 consumers. **Roadmap alignment** Not on ROADMAP.md; CI/publishing speed improvement only. ## What Changed - `.github/workflows/docker.yml`: the `build-and-push-cloud` job's `platforms` is now `linux/amd64` (with a comment explaining why). The self-hosted `build-and-push` job is untouched. ## Verification - Build-config-only change; the workflow runs on merge to master. The published `-cloud` manifest will be amd64-only, which its consumers already pull. - No test changes: nothing at runtime differs on any platform that actually runs the image. ## Risks - Low. If an arm64 consumer of the cloud variant ever appears (e.g. local `docker run` on Apple Silicon for debugging), it would fall back to emulation on the consumer's machine or need this reverted — a one-line change. The self-hosted image's platform matrix is unchanged. ## Model Used Claude Fable 5 (`claude-fable-5`, extended thinking, via Claude Code with tool use and code execution). ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass (no test-affecting changes) - [x] I have added or updated tests where applicable (n/a — CI platform matrix only) - [x] I have updated relevant documentation to reflect my changes (in-workflow comment documents the rationale) - [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 mergecanary/v2026.731.0-canary.19 |
||
|
|
53bcf3897f |
feat: sync @-mentioned projects into remote sandboxes (confined sandbox transport, flag ON) (#10564)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The run layer must move project context into the sandbox that executes the agent > - Local sandboxes already stage referenced projects for @-mentions > - Remote confined sandboxes dropped the whole referenced set, so the agent lost needed files and paths > - This pull request keeps the confined sandbox transport aligned with the local behavior for referenced projects > - It does this behind a remote-only flag that defaults on, while SSH keeps the old drop-only path > - The benefit is that remote runs can read the same referenced project context that local runs already provide ## Linked Issues or Issue Description **Subsystem affected** Cross-cutting. This change touches server orchestration, sandbox transport, and observability. **Problem or motivation** A run can @-mention another project. Local targets stage each referenced project and give the agent a path. Remote confined sandboxes dropped the full referenced set, so the agent could not read those project files or paths. **Proposed solution** Enable referenced-project sync for the confined sandbox transport behind `PAPERCLIP_MULTI_PROJECT_WORKSPACE_SYNC_REMOTE`, which defaults on. Keep the SSH transport out of scope and keep it dropping referenced projects. Repoint each referenced workspace hint at its staged `project-<projectId>` sandbox directory. Publish `PAPERCLIP_WORKSPACES_JSON` on the confined sandbox lane. Count each per-project remote staging failure as a `staging` failure in the requested-vs-synced metrics. **Alternatives considered** Keep the remote path drop-only. That keeps the gap open. Move the change into SSH too. That expands scope beyond the target transport and adds risk. **Roadmap alignment** No matching item in `ROADMAP.md` showed up in this review. **Additional context** The change lands in three commits. The first commit opens the gate for the confined sandbox transport. The second commit repoints the workspace hints and publishes the workspace map. The third commit records per-project staging failure data. ## What Changed - Opened remote referenced-project sync for the confined sandbox transport behind `PAPERCLIP_MULTI_PROJECT_WORKSPACE_SYNC_REMOTE`. - Repointed referenced workspace hints to the staged `project-<projectId>` sandbox directories and published `PAPERCLIP_WORKSPACES_JSON`. - Counted per-project remote staging failures as first-class `staging` failures in the requested-vs-synced observability. ## Verification - The pushed ref `refs/heads/feat/sync-referenced-projects-remote-sandbox` resolves to the authorized submit SHA. - `git log --oneline origin/master..origin/feat/sync-referenced-projects-remote-sandbox` shows exactly the three expected commits. - The handoff reports server typecheck clean, adapter-utils typecheck clean, and the listed unit tests passing. - The handoff also reports no open review comments and no Greptile score yet. ## Risks - The change touches authorization and sandbox path handling, so regressions could block remote runs or expose the wrong project context. - The new flag defaults on, so any bug in the remote path affects normal remote use. - SSH stays out of scope, so the two transport paths must remain distinct. ## Model Used OpenAI GPT-5 via Codex. Tool use enabled. Context window not reported in this 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) - [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.731.0-canary.18 |
||
|
|
c062600b3d |
chore(lockfile): refresh pnpm-lock.yaml (#10567)
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> |
||
|
|
521271ebb7 |
build: bake the build commit into published images (#10566)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Instances commonly run from published container images, and operators need to observe which build a container actually serves > - `/api/health` now reports the running build commit, and server-info already falls back to `PAPERCLIP_BUILD_COMMIT` when git is unavailable > - But published images carry no `.git` and never received `PAPERCLIP_BUILD_COMMIT`, so containers report `commit: null` — verified live against a current image > - That leaves the new deployment-verification field inert exactly where it matters most: containerized deploys > - This pull request bakes the exact build commit into both image variants at build time, mirroring how `PAPERCLIP_BUILD_VERSION` is already stamped > - The benefit is that containers report their true commit on `/api/health`, so deploy tooling can verify a rollout actually shipped ## Linked Issues or Issue Description Companion to #10563 (which exposed the `commit` field on `/api/health`). Inline description following the bug report template: **What happened?** A container from a published image responds to `GET /api/health` with `"commit": null`. The image has no `.git` directory and the `PAPERCLIP_BUILD_COMMIT` fallback that `server-info` supports is never provided at build time, so git metadata resolves as unavailable. **Expected behavior** A container reports the commit it was built from, the same way it already reports its build version via the baked `PAPERCLIP_BUILD_VERSION`. **Steps to reproduce** Run any published image (e.g. `ghcr.io/paperclipai/paperclip:sha-c4f6264-cloud`) and `curl /api/health` — `commit` is `null` even though the build commit is known at image-build time. **Paperclip version or commit** `sha-c4f6264-cloud` (first image containing #10563). ## What Changed - `Dockerfile`: new `PAPERCLIP_BUILD_COMMIT` build arg, exported as an ENV in the production stage (the `cloud` stage inherits it), directly parallel to `PAPERCLIP_BUILD_VERSION`. Empty for local `docker build`, which keeps the normal fallbacks. - `.github/workflows/docker.yml`: both build jobs pass `PAPERCLIP_BUILD_COMMIT=${{ github.sha }}`. ## Verification - Reviewed the plumbing end-to-end: `build-commit.ts` reads `PAPERCLIP_BUILD_COMMIT` (validated as a full SHA), `server-info.ts` `readGitInfo` falls back to it when the git CLI fails, producing `available: true, fullSha` — which `/api/health` surfaces as `commit`. - Verified live that a current published image reports `commit: null`; this change repairs that on the next build. Post-merge, the first master image should report its commit — I'll be verifying that as part of managed-deploy validation. - No test changes: the fallback path is already covered by existing server-info tests; this PR only supplies the env at image build. ## Risks - Low. Two build-time stamps; no runtime code changes. A wrong SHA would only mislabel the build (same failure mode `PAPERCLIP_BUILD_VERSION` already carries), and `${{ github.sha }}` is the exact commit the workflow builds. ## Model Used Claude Fable 5 (`claude-fable-5`, extended thinking, via Claude Code with tool use and code execution); diagnosis included live probes of a running container's `/api/health`. ## 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 (no test-affecting changes; server suites unaffected) - [x] I have added or updated tests where applicable (n/a — build-time stamps only) - [x] I have updated relevant documentation to reflect my changes (Dockerfile comments document the arg) - [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 mergecanary/v2026.731.0-canary.17 |
||
|
|
b01f423cd7 |
fix(server): export manual OpenTelemetry spans (#10565)
## Thinking Path > - Paperclip uses the server to coordinate agent work. > - The server emits manual OpenTelemetry spans for startup, heartbeat, and sandbox execution. > - Those spans need the shared OpenTelemetry API package and a type-safe exporter path. > - Without the direct API dependency, the tracer stays no-op and the spans do not reach the collector. > - This pull request adds the direct dependency and the exporter cast. > - The benefit is that the manual spans can export cleanly at runtime. ## Linked Issues or Issue Description **What happened?** The server resolved the tracer with a runtime import, but `server` did not declare `@opentelemetry/api`. The manual spans stayed no-op, so the collector did not receive them. **Expected behavior** The server should load the shared OpenTelemetry API package, create the manual spans, and export them. **Steps to reproduce** 1. Start the server with telemetry enabled. 2. Run startup, heartbeat, or sandbox execution paths. 3. Observe that the manual spans do not export before this change. **Paperclip version or commit** `f91df236dfd8e5e6210941c80efeb0a7953bbe50` **Deployment mode** Built from source with `pnpm dev` or `pnpm build`. ## What Changed - Added `@opentelemetry/api` as a direct `server` dependency. - Cast the `traceExporter` value to `never` so the type check passes without a static `SpanExporter` import. - Kept the optional OTLP and SDK packages behind dynamic import. ## Verification - `pnpm build` in `server/` passed. - `server/src/instrumentation.ts` does not import `SpanExporter`. - `server/package.json` lists `@opentelemetry/api` at `^1.9.0`. ## Risks - Low risk. The change touches dependency metadata and one type cast. - Runtime telemetry still needs live collector QA. ## Model Used - OpenAI Codex, GPT-5, tool use enabled. ## 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] 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.731.0-canary.16 |
||
|
|
277857a7e9 |
chore(repo): ignore macOS AppleDouble metadata files (#4720)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Contributors clone this repository and work in it on macOS > - macOS writes AppleDouble sidecar files named `._<filename>` next to real files > - The repository ignores `.DS_Store`, but it does not ignore these sidecars > - `git status` then lists the sidecars together with real untracked files > - The noise hides real changes during development and review > - This pull request adds the standard macOS sidecar patterns to `.gitignore` > - The benefit is a clean `git status` for every macOS contributor ## Linked Issues or Issue Description No existing issue covers this. The problem is described below, in the format of `.github/ISSUE_TEMPLATE/enhancement.yml`. **What existing behavior does this improve?** The root `.gitignore` file, and the `git status` output that developers see. **Subsystem affected** Cross-cutting (multiple of the above) — repository tooling, not application code. **Current behavior** The root `.gitignore` ignores `.DS_Store`. It does not ignore the AppleDouble sidecar files that macOS writes. macOS creates a file named `._<filename>` next to a regular file when the volume cannot store extended attributes. This happens on exFAT and NTFS volumes, on FUSE mounts, and on network shares. Git shows every one of these files as untracked. The author reports approximately 58,000 such files in one local checkout. Real untracked files, such as new scaffold directories, are lost in that list. **Proposed behavior** `.gitignore` ignores `._*`, `.AppleDouble`, and `.LSOverride`. `git status` then shows only real files. These three patterns are the macOS set from GitHub's gitignore collection: https://github.com/github/gitignore/blob/main/Global/macOS.gitignore **Reason and benefit** Developers lose time when they read `git status` output that contains thousands of operating system files. They can also commit a sidecar file by accident. The repository already suppresses these same files in its own code: `ssh.ts` sets `COPYFILE_DISABLE=1` to stop `bsdtar` from writing them, and the Daytona file-sync path excludes them from tarballs. This change applies the same rule to git. **Breaking changes** None. No tracked file matches the new patterns. Git continues to track any file that is already tracked, because `.gitignore` applies to untracked files only. ## What Changed - Added `._*`, `.AppleDouble`, and `.LSOverride` to the root `.gitignore`, below the existing `.DS_Store` entry. ## Verification - `git status` no longer lists `._*` files after the change. - `git ls-files | grep -E '^\._|\.AppleDouble|\.LSOverride'` returns no output. No tracked file becomes ignored. - `.DS_Store` behavior does not change. ## Risks Low risk. This is a configuration change with no runtime effect. A file that starts with `._` becomes ignored, so a contributor who needs such a file must add it with `git add -f`. Git refuses to add an ignored path without that flag, so the effect is visible and not silent. No such file exists in the repository. Note for contributors who already have these files in a working tree: the ignore rule does not delete them. Remove them with: `find . -name '._*' -not -path './node_modules/*' -not -path './.git/*' -delete` ## Model Used - Provider: Anthropic Claude, through Claude Code. - The original description recorded only "Generated with Claude Code". It did not record an exact model ID. - This description was rewritten to the repository template by Claude Opus 5 (1M context, extended thinking, tool use), on behalf of the maintainers. The code in this pull request is unchanged. ## 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 (no duplicates found) - [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 - [x] My branch name describes the change (`chore/gitignore-appledouble`) and contains no internal ticket id - [ ] I have run tests locally and they pass — not applicable, no code is executed by this change - [x] I have added or updated tests where applicable (not applicable — `.gitignore` only) - [x] I have updated relevant documentation to reflect my changes (none required) - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s — pending re-review of this updated description - [x] I will address all Greptile and reviewer comments before requesting merge Co-authored-by: User <user@example.com> Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>canary/v2026.731.0-canary.15 |
||
|
|
c4f62644b0 |
docs(daytona): document operator enablement for the advisory bwrap wrapper (#10560)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The Daytona sandbox provider now uses an advisory bwrap wrapper > - Operators need clear host and image setup for bubblewrap, sudo, and user namespaces > - The repo should document that setup, but it should not own provisioning > - This pull request adds the operator guidance to the shared sandbox requirements and the Daytona README > - The benefit is that operators can enable the wrapper with the same steps the code expects ## Linked Issues or Issue Description Refs #10554 and #10541. ## What Changed - Added an advisory bwrap prerequisites section to `packages/plugins/sandbox-providers/SANDBOX-REQUIREMENTS.md`. - Added an operator enablement section to `packages/plugins/sandbox-providers/daytona/README.md`. - Documented the install commands, the sudoers rule, the user namespace setting, and the verification command. - Kept provisioning out of the repo and left it to the image or snapshot layer. ## Verification - `git diff --check origin/master...HEAD` - `gh pr checks 10560` ## Risks - Low risk. This change updates documentation only. - The docs can drift if the host setup changes later. - Provisioning still lives outside the repo. ## Model Used - OpenAI Codex, GPT-5, tool use enabled. ## 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 with `Fixes: #` / `Closes: #` / `Refs #` OR described the issue in-PR following the relevant issue template - [x] I have not referenced internal or instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing> |
||
|
|
51bb41c7e3 |
Expose the running build commit on the unauthenticated health response (#10563)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Instances run self-hosted or under hosting/deploy tooling, and operators need to observe what build a server is actually running > - `/api/health` carries the git SHA only inside `serverInfo`, which is gated to board/agent actors — anonymous callers get a redacted body with no version signal at all > - Deploy tooling that manages instances from outside (fleet rollouts, hosting providers, upgrade scripts) therefore cannot ground-truth that a deploy actually shipped without holding credentials > - A build commit is a plain git SHA of this public repository — it is not a secret, and gating it buys no security while blocking legitimate verification > - This pull request surfaces the running build commit as a top-level `commit` field on every `/api/health` response, including the redacted anonymous one > - The benefit is credential-free deploy verification: any operator or tool can confirm which commit an instance serves, while the fuller `serverInfo` block stays access-controlled as before ## Linked Issues or Issue Description No existing public issue — inline description following the feature request template: **Subsystem affected** Server (API, runs, routes) **Problem or motivation** An anonymous `GET /api/health` returns a redacted body with no version information; the running git SHA exists only in `serverInfo.git.fullSha`, which requires a board/agent actor. External deploy tooling (fleet rollouts, hosting providers, upgrade scripts) therefore cannot verify that an instance is actually serving the build it was just upgraded to — a rollout that silently keeps running the old image is indistinguishable from a successful one at the health endpoint. **Proposed solution** Surface the running build commit as a top-level nullable `commit` field on every `/api/health` response shape, including the redacted anonymous one, while keeping the fuller `serverInfo` block access-controlled as before. A build commit is a plain git SHA of this public repository — exposing it costs nothing and enables credential-free deploy verification, like the `version` endpoints on most server software. **Alternatives considered** Authenticating deploy tooling as a board actor to read `serverInfo` — rejected: it forces credential plumbing into infrastructure that only needs a public SHA, and adds a whole class of auth-misconfiguration failure to deploy verification. **Roadmap alignment** Not on ROADMAP.md; a small operational observability improvement, no overlap with planned core work. ## What Changed - `server/src/routes/health.ts`: derive `commit` from the server info snapshot (`serverInfo.git.fullSha` when git metadata is available, else `null`) and include it as a top-level field on every `/api/health` response shape — the redacted anonymous body, the full-details body, the no-db body, and the 503 database-unreachable body. - `serverInfo` itself remains gated to full-details responses exactly as before; only the bare commit is newly public. - `server/src/__tests__/health.test.ts`: updated exact-shape assertions to include `commit`, and added an assertion that `commit` is `null` (not omitted) when git metadata is unavailable. The redacted-response tests now pin that anonymous callers receive the commit. ## Verification - `pnpm vitest run src/__tests__/health.test.ts` in `server/` — 13 tests pass, including the redacted-anonymous shapes (which now pin the `commit` field) and the git-unavailable `null` case. - `tsc -p server/tsconfig.json --noEmit` — clean. - Manual: `curl -s https://<instance>/api/health` as an anonymous caller returns `"commit": "<full sha>"` alongside the existing redacted fields. ## Risks - **Version disclosure:** anonymous callers can now fingerprint the exact running commit. This is a deliberate trade-off: the builds are of a public repository (the SHA reveals no private code), the endpoint already responds to anonymous callers, and the operational value — verifying deploys actually shipped — outweighs the marginal fingerprinting surface. Operators who consider this sensitive are typically fronting `/api` with their own access controls already. - Otherwise low risk: no behavioral change to any gated field, no schema or API-surface removal; `commit: null` keeps the field shape stable when git metadata is absent (e.g. non-git installs). ## Model Used Claude Opus 4.8 (`claude-opus-4-8`, extended thinking, via Claude Code with tool use and code execution) authored the change and tests; finalized and PR'd under Claude Fable 5 (`claude-fable-5`). ## 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 (none needed beyond code comments — health endpoint has no standalone doc) - [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 mergecanary/v2026.731.0-canary.14 |
||
|
|
d295251550 |
feat(adapter-claude): add Claude Sonnet 5 to the static model fallback (#10280)
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents pick their model from a dropdown in agent config, populated
per-adapter by `listAdapterModels()` → each adapter's live provider
catalog merged over a static fallback list
> - For `claude_local`, newer model ids only reach the dropdown via the
live Anthropic `/v1/models` fetch, which needs a server
`ANTHROPIC_API_KEY`, a <5s round-trip, non-Bedrock mode, and account
entitlement; on any miss it silently falls back to the static `models`
array
> - Claude Sonnet 5 (`claude-sonnet-5`) is a current flagship but was
absent from that static fallback, so it appeared only when live
discovery happened to succeed — i.e. "the newest model doesn't
consistently show up"
> - This pull request adds `claude-sonnet-5` to the `claude_local`
static model list so it is selectable regardless of the live-discovery
path
> - The benefit is a consistent, reliable dropdown that no longer
depends on a flaky live fetch to surface a shipped flagship model
## Linked Issues or Issue Description
No public GitHub issue. The bug is described inline following the
bug-report template:
**What happened**
The `claude_local` agent-config model dropdown intermittently omitted
Claude Sonnet 5. `claude-sonnet-5` was missing from the adapter's static
fallback `models` array (`packages/adapters/claude-local/src/index.ts`),
so it only surfaced when the live Anthropic `/v1/models` discovery
happened to succeed.
**Expected behavior**
Claude Sonnet 5 is a shipped flagship model and should always be
selectable in the dropdown, independent of whether live discovery
succeeds.
**Steps to reproduce**
1. Run the server without a working live Anthropic `/v1/models` path (no
`ANTHROPIC_API_KEY`, Bedrock mode, a discovery timeout, or a cache
miss).
2. Open agent config for a `claude_local` agent and inspect the model
dropdown.
3. Observe that `claude-sonnet-5` is absent because the static fallback
list omitted it.
**Deployment mode**
Self-hosted / local adapter (`claude_local`); the server process reads
`ANTHROPIC_API_KEY` from its environment.
## What Changed
- Added `{ id: "claude-sonnet-5", label: "Claude Sonnet 5" }` to the
`claude_local` static `models` fallback, immediately after
`claude-opus-4-8` (so Opus 4.8 stays the default first option).
- Added an explicit regression assertion in
`server/src/__tests__/adapter-models.test.ts` that `claude-sonnet-5` is
present in the `claude_local` fallback when live discovery is
unavailable.
## Verification
- `pnpm -C server exec vitest run src/__tests__/adapter-models.test.ts
-t "claude fallback"` — **passes** (the new `claude-sonnet-5` assertion
included).
- Reviewed the consuming tests: the fallback test also asserts
`models[0]?.id === "claude-opus-4-8"` (still index 0 — Sonnet 5 is index
1, unaffected); `adapter-registry.test.ts` reads `builtIn?.models`
dynamically, so no exact-array snapshot breaks.
- Change is a single static-data addition plus a test assertion; no
control-flow change.
## Risks
- Low risk. Pure additive change to a fallback list; no control-flow
change. Worst case is an id that a given account isn't entitled to,
which the existing "current"/manual-model UI paths already tolerate.
## Model Used
Claude (Anthropic), model id `claude-opus-4-8` (Opus 4.8), extended
thinking + tool use, run as the Paperclip CTO agent.
## 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 (branch is the assigned
execution-workspace branch and cannot be renamed this run)
- [x] I have run tests locally and they pass (server adapter-models
"claude fallback" case)
- [x] I have added or updated tests where applicable (explicit
`claude-sonnet-5` fallback assertion)
- [x] I have updated relevant documentation to reflect my changes (n/a —
no docs reference this list)
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green (pending CI)
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(pending review)
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
canary/v2026.731.0-canary.13
|
||
|
|
cd7f84965c |
fix(recovery): preserve hand-back wake liveness (#10562)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The heartbeat service delivers issue work to assigned agents. > - Recovery can hand an issue back to its agent while the recovery run is still active. > - The hand-back wake can merge into that active run and disappear when the run exits. > - The stranded-work scan also treats the successful recovery run as proof that the handed-back issue is live. > - This pull request keeps the hand-back wake for follow-up delivery and lets the scan repair a lost wake. > - The benefit is that an assigned issue continues after recovery without manual operator action. ## Linked Issues or Issue Description No public issue exists. This is related to the wake reconciliation work in #8943. **What happened?** A recovery action could hand an assigned issue back from `blocked` to `todo`. The `issue_recovery_action_restored` wake then merged into the recovery run that made the change. The wake disappeared when that run exited. The stranded-work scan did not repair the issue because it treated the successful recovery run as current liveness. **Expected behavior** Paperclip must dispatch the hand-back wake after the recovery run exits. If that delivery is lost, the stranded-work scan must enqueue the assigned `todo` issue again. **Steps to reproduce** 1. Start a recovery run for an assigned blocked issue. 2. Resolve a recovery action with the `handed_back` outcome. 3. Move the issue to `todo` while the recovery run is still active. 4. Observe that the wake merges into the active run and no new run starts after it exits. 5. Run the stranded-work scan and observe that the successful latest run prevents repair. **Paperclip version or commit** `131d476a7e` **Deployment mode** Local dev (`pnpm dev`). The defect is in the core server and is not deployment-specific. **Agent adapter(s) involved** Not adapter-specific. This is a core heartbeat and recovery defect. ## What Changed - Added `issue_recovery_action_restored` to the wake reasons that require follow-up delivery when an issue run is active. - Made the stranded-work scan detect a resolved hand-back that occurred during or after the latest successful run. - Added focused regression tests for the heartbeat coalescing seam and the stranded-work repair shape. - Documented the hand-back liveness guarantee in execution semantics section 9.1. ## Verification - `pnpm exec vitest run server/src/__tests__/heartbeat-comment-wake-batching.test.ts server/src/__tests__/heartbeat-process-recovery.test.ts` passed: 104 tests. - `pnpm --filter @paperclipai/server typecheck` passed. - `pnpm -r typecheck` passed. - `pnpm build` passed. - `pnpm test:run` passed the server shard (3,095 passed, 2 skipped) and UI shard (3,182 passed). One unrelated CLI test failed because the agent environment exports static AWS credentials. `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY -u AWS_SESSION_TOKEN pnpm exec vitest run cli/src/__tests__/secrets.test.ts` passed all 8 tests. - `git diff --check` passed. - All GitHub checks passed on commit `8b380e67e6`. - Greptile gave 5/5 confidence with no comments or unresolved threads. ## Risks - Low risk. The follow-up rule affects only a recovery hand-back wake that arrives while the same issue already has an active run. - The backstop adds one indexed recovery-action lookup for an assigned `todo` issue whose latest run succeeded. - The timestamp check uses the latest run start time. This includes hand-backs made by that run and later hand-backs, but excludes older resolved actions. ## Model Used - OpenAI Codex with GPT-5 (`gpt-5`), agentic reasoning, tool use, and code execution. The serving context-window size is not exposed to the agent. ## 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.731.0-canary.12 |
||
|
|
bd53b99686 |
feat(gemini-local): export detectGeminiQuotaExhausted for plugin adapter reuse (#3416)
## Thinking Path
> - Paperclip orchestrates AI agents via pluggable server-side adapters,
one per provider / CLI backend
> - Each adapter package exposes a server entry point
(`@paperclipai/adapter-<name>/server`) that downstream consumers —
including plugin adapters that wrap or extend the stock behavior —
re-use for helper functions
> - `gemini-local`'s server entry re-exports a curated set of parse
helpers from `./parse.js` (`parseGeminiJsonl`,
`isGeminiUnknownSessionError`, `describeGeminiFailure`,
`detectGeminiAuthRequired`, `isGeminiTurnLimitResult`) so consumers can
classify provider output without reaching into the package's internals
> - `detectGeminiQuotaExhausted` lives in the same `parse.ts` file next
to `detectGeminiAuthRequired`, is defined with `export function`, and is
the package's single source of truth for "is this output a Gemini quota
hit?" — but it is missing from the entry-point re-export block
> - As a result, any consumer that wants to classify quota exhaustion
either has to deep-import from `./server/parse.js` (brittle against
future `exports`-field changes) or reimplement the regex locally (drift
risk against the authoritative heuristic)
> - This pull request adds `detectGeminiQuotaExhausted` to the existing
re-export block, placed next to its thematic sibling
`detectGeminiAuthRequired`, with no other changes
> - The benefit is one extra supported public symbol on
`@paperclipai/adapter-gemini-local/server` — a purely additive ergonomic
improvement with no behavior change and no existing consumer impact
## What Changed
- `packages/adapters/gemini-local/src/server/index.ts`: added
`detectGeminiQuotaExhausted` to the `export { ... } from "./parse.js"`
block, inserted between `detectGeminiAuthRequired` and
`isGeminiTurnLimitResult` (thematic grouping — both `detect*` helpers)
## Verification
Local verification against the branch commit (base: `upstream/master` at
`b649bd45`):
```
$ pnpm --filter @paperclipai/adapter-gemini-local typecheck
> @paperclipai/adapter-gemini-local@0.3.1 typecheck
> tsc --noEmit
(exit 0)
$ pnpm --filter @paperclipai/adapter-gemini-local build
> @paperclipai/adapter-gemini-local@0.3.1 build
> tsc
(exit 0)
$ cd server && pnpm exec vitest run src/__tests__/gemini-local-execute.test.ts
RUN v3.2.4
✓ src/__tests__/gemini-local-execute.test.ts (3 tests) 1487ms
✓ gemini execute > passes prompt via --prompt and injects paperclip env vars
✓ gemini execute > always passes --approval-mode yolo
✓ gemini execute > uses a compact wake delta instead of the full heartbeat prompt when resuming a session
Test Files 1 passed (1)
Tests 3 passed (3)
```
Existing-consumer check — all references to `detectGeminiQuotaExhausted`
anywhere in the tree:
```
packages/adapters/gemini-local/src/server/index.ts:9 (this PR's new re-export)
packages/adapters/gemini-local/src/server/parse.ts:253 (the definition)
packages/adapters/gemini-local/src/server/test.ts:19 (intra-package import from "./parse.js")
packages/adapters/gemini-local/src/server/test.ts:174 (intra-package usage)
```
No cross-package consumer references the symbol today, so the new
re-export cannot break any existing import. It is strictly additive to
the public surface of `@paperclipai/adapter-gemini-local/server`.
## Risks
None. Purely additive re-export of a symbol that is already a named
export on `./parse.ts`. The clean-success path, failure paths, and all
other adapter behavior are untouched. No existing consumer is affected.
## Model Used
- **Provider**: Anthropic
- **Model**: Claude Opus 4.6 (1M context)
- **Interface**: Claude Code CLI
- **Role**: Upstream state verification (grep + diff against current
master), PR drafting against the `CONTRIBUTING.md` template, local
typecheck / build / test execution
- **Reasoning Mode**: Extended thinking enabled
- **Human oversight**: Noah Kellner reviewed the one-line re-export
addition and approved submission
## 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 run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] If this change affects the UI, I have included before/after
screenshots
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] I will address all Greptile and reviewer comments before
requesting merge
Co-authored-by: Noah Kellner <noah.kellner@xenoscloud.com>
|
||
|
|
bec04da2c2 |
fix(ui): make task header status and priority controls interactive (#10526)
<!-- 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 > - Operators use the task page header to read and change task state > - The status and priority icons already had picker logic, but the compact triggers were not semantic controls > - This made pointer and keyboard interaction unreliable in the task header > - This pull request makes both compact icon triggers real buttons and keeps the existing picker behavior > - The benefit is that operators can change status and priority directly from the header with pointer or keyboard input ## Linked Issues or Issue Description ### Subsystem affected `ui/` — React + Vite board UI. ### Problem or motivation The compact status and priority icons can receive change handlers, but their popover triggers are plain icon elements. They do not provide a reliable click target, keyboard focus, or control label. Operators need to change both values directly from the task header. ### Proposed solution Use semantic button triggers in the shared status and priority components. Keep the existing API update wiring and picker options. Keep task-row navigation links separate from editable row controls. An operator can select either icon, open its picker, and choose a new task status or priority. ### Alternatives considered A task-page-only wrapper would duplicate control behavior. Moving the controls would also change the page layout. The shared components already own the picker behavior, so a shared trigger fix is smaller and more consistent. ### Roadmap alignment This is a focused board UI usability and accessibility fix. It does not duplicate a planned roadmap feature. ### Additional context The task page already passes change handlers to these shared components. This change makes that existing path interactive and accessible. ## What Changed - Added semantic button triggers for compact and labeled status controls. - Added semantic button triggers for compact and labeled priority controls. - Added accessible current-state labels and keyboard focus styles. - Separated issue-row navigation links from row controls to avoid nested interactive elements. - Added real popover interaction coverage and row semantics regressions. - Added task-page regression tests for update requests. ## Verification - Focused task-header and row suites passed with 131 tests. - The final row, inbox, and picker regression suites passed with 83 tests. - `pnpm --filter @paperclipai/ui exec tsc -b --force` passed. - `pnpm run typecheck:build-gaps` reproduced the CI typecheck before the fix. The forced UI build passed after the fix. - `pnpm check:token-gates` passed. - `pnpm -r typecheck` passed. - `pnpm build` passed. - `pnpm test:run` completed the server and UI partitions. One unrelated CLI AWS doctor assertion saw injected static AWS credentials and returned `warn` instead of `pass`. The exact test passed after those two environment variables were removed. - `git diff --check origin/master...HEAD` passed. - All latest-head GitHub checks passed, including both e2e shards. - Greptile passed at the required threshold with zero open review threads. ## Risks - The shared issue-row DOM now uses a full-area navigation link beside native action buttons. - Existing visual layout, pointer navigation, keyboard navigation, and action behavior remain covered by row, inbox, and list tests. - Read-only status and priority icon uses 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 - OpenAI Codex with the `gpt-5` model. The runtime did not expose its context window size. The agent used reasoning, repository tools, code execution, and GitHub CLI integration. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes: #` / `Refs: #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.731.0-canary.11 |
||
|
|
b4a7a12985 |
feat: make recovery updates quieter (#10542)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The recovery subsystem restores work after an agent run stops or loses state > - Recovery notices currently use the same visual weight as normal work comments > - Recovery agents can also post long narratives that obscure the useful hand-off > - The server must identify recovery output because agents cannot set presentation controls > - This pull request adds compact recovery notices, structured action references, and brief recovery prompts > - The benefit is a quieter issue thread that still keeps recovery state inspectable ## Linked Issues or Issue Description **Subsystem affected** Cross-cutting: `server/`, `packages/shared`, and `packages/adapter-utils`. **Problem or motivation** Recovery notices and recovery-run comments can dominate an issue thread. Operators must scan routine recovery narration before they find the work hand-off. **Proposed solution** Give routine recovery output a compact system-notice presentation. Derive the presentation on the server so agents cannot hide arbitrary comments. Keep the successful missing-state summary fully visible because that comment is the recovery deliverable. **Alternatives considered** The UI could detect recovery text. That approach is fragile and does not provide structured action references. Agents could also set presentation directly, but that would weaken the current board-only security boundary. **Roadmap alignment** This change refines the completed “Self-healing runs & automatic recovery” and “Enforced Outcomes” roadmap areas. It does not add a competing roadmap capability. **Additional context** The scope covers shared comment validation, server recovery notices, agent-comment derivation, and recovery prompt text. No database migration is needed because presentation data already uses JSON. ## What Changed - Add the `compact` issue-comment presentation density to shared constants, types, and validation. - Give recovery escalation, waiting, and in-place notices compact titles and structured recovery-action metadata. - Use recovery-action metadata for notice deduplication, with the legacy text marker as a compatibility fallback. - Derive compact presentation for comments from recovery-scoped runs while preserving the board-only presentation boundary. - Keep successful missing-state recovery summaries fully visible. - Ask recovery participants to record outcomes in `resolutionNote` and keep source-issue comments brief. - Add shared, route, service, and prompt tests for the new behavior and exceptions. ## Verification - `pnpm -r typecheck` - Focused Vitest coverage: 320 tests passed across shared validators, adapter prompts, issue comments, recovery actions, and heartbeat recovery. - Full server phase: 292 files passed, 3,094 tests passed, and 2 tests skipped. - Full UI phase: 386 files passed and 3,182 tests passed. - `pnpm build` - Known master baseline: `cli/src/__tests__/secrets.test.ts` expects `pass`, but the current implementation returns `warn` when strict secret mode is disabled for Postgres. This branch does not change CLI secrets code. ## Risks - Low migration risk. The presentation column is JSON and needs no database migration. - Recovery-run detection depends on the persisted run context snapshot. - Structured metadata becomes the primary deduplication key. The existing body marker remains as a fallback for older comments. > 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 with `gpt-5.6-sol`. The runtime did not expose the context-window size. The model used agentic reasoning, repository tools, code execution, and test execution. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [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> Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>canary/v2026.731.0-canary.10 |
||
|
|
131d476a7e |
fix(ci): make PR-template inline-description contract explicit (#10558)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Contributors use the PR template to describe changes before review > - The linked-issue gate reads literal template labels, not freeform prose > - The template did not state that rule, so a good prose summary could fail on first submission > - The gate also skipped two issue template paths that the PR template already points to > - This pull request makes the template and the gate match the same contract > - The benefit is that a good-faith author can pass the check on the first try ## Linked Issues or Issue Description No public issue exists. Related public PR: #7762. The problem is a mismatch between the PR template and the linked-issue gate. The template gave a bare placeholder and did not explain the literal-label rule. The gate also missed the enhancement and docs issue templates. ## What Changed - Replaced the bare PR-template placeholder with labeled inline skeletons for bug, feature, and adapter paths. - Added one sentence that says the gate reads literal labels on separate lines. - Added the enhancement and docs issue template field sets to the linked-issue gate. - Added and updated tests for prose-only bodies, template skeleton bodies, and extra issue template coverage. ## Verification - `node --test .github/scripts/tests/check-pr-linked-issue.test.mjs` - The branch contains one commit: `561f1ed3a1434ed4562306f74d40968163ef1444` ## Risks Low risk. The three-field minimum stays in place. The main change is clearer author guidance in the PR template. ## Model Used OpenAI Codex, GPT-5, tool use. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes: #` / `Refs: #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.731.0-canary.9 |
||
|
|
8c910b9a40 |
feat(daytona): activate the advisory bwrap wrapper at the execute seam (#10554)
## Thinking Path > - Paperclip is the control plane for autonomous AI companies > - Daytona runs code in a remote sandbox > - The execute seam decides how agent commands run > - The pure bwrap builder and the capability probes already merged in #10541 > - This pull request activates the advisory bwrap wrapper at the execute seam > - The wrapper uses bwrap when the lease has the needed data and keeps plain execution when wrap is not safe > - The benefit is safer isolation without changing the current runtime contract ## Linked Issues or Issue Description - No public GitHub issue exists for this work. - Related PR: #10541 Problem: The Daytona execute seam can run commands without the advisory bwrap wrapper when the lease does not yet provide the wrapper data. Proposed solution: Activate the advisory bwrap wrapper when the lease reports bwrap support and a known uid or gid. Keep plain execution when wrap is not safe. Alternatives: - Always wrap every command. This can break current behavior and can create root-owned files when the uid or gid is missing. - Reject execution when bwrap is missing. This can fail a lease for a best-effort wrapper and would change the current runtime contract. Roadmap alignment: This work fits the Cloud / Sandbox agents roadmap area. ## What Changed - `executeOneShot` now accepts a bwrap execution plan. - `onEnvironmentExecute` now reads the lease bwrap flags and the writable sync paths. - A helper resolves when the seam should wrap the command. - The writable set now includes the workspace path and the collected read-write sync destinations. - The wrapper re-binds stdin after the fresh `/tmp` mount. - The README explains the advisory wrapper behavior and the writable set model. ## Verification - `pnpm --filter @paperclipai/plugin-daytona exec vitest run plugin.test.ts` - `pnpm --filter @paperclipai/plugin-daytona exec tsc --noEmit` ## Risks - A missing bwrap tool, a missing `sudo -n` rule, or a blocked user namespace keeps plain execution. - The change shifts the execute seam, so command setup needs careful review. - The wrapper is advisory, so it does not add a security boundary by itself. ## Model Used - OpenAI Codex, GPT-5, tool use, 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 linked existing issues or described the issue in this PR - [x] I have not referenced internal or instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.731.0-canary.8 |
||
|
|
70816c18e5 |
feat(daytona): add advisory bwrap command builder and capability probes (#10541)
## Thinking Path > - Paperclip helps people manage AI agents for work > - The Daytona sandbox provider needs a clear advisory wrapper path and best-effort capability checks > - The wrapper must not change the security model or block a lease when the host lacks bubblewrap support > - The lease metadata must carry the capability result so later steps can make a stable choice > - This pull request adds a pure command builder for the advisory wrapper > - This pull request adds non-throwing probes for bubblewrap and sandbox uid or gid data > - The benefit is a safer advisory path with no behavior change in the execution seam ## Linked Issues or Issue Description ### Problem or motivation The Daytona sandbox provider needs a clear advisory wrapper path and best-effort capability checks. The provider must not fail a lease when the host lacks bubblewrap support. The wrapper must stay advisory only. It must not change the security model. ### Proposed solution Add a pure command builder for the advisory wrapper and non-throwing probes for bubblewrap and sandbox uid or gid data. Store the probe result on the lease metadata so later steps can make a stable choice. Keep the execution seam unchanged. ### Alternatives considered Do nothing and keep the current execution seam unchanged. That path gives no signal when a file change is not durable. This pull request adds the signal without changing runtime behavior. ### Roadmap alignment This work fits the Daytona sandbox provider path and keeps the advisory wrapper outside the execution seam. It does not change the current security model. ### Additional context The wrapper is advisory only. It adds no security. The read-only root is a feedback signal. ## What Changed - Added `buildBwrapCommand` as a pure string builder for the advisory wrapper command. - Added `detectBwrapAvailable` and `detectSandboxUidGid` as best-effort probes that never throw. - Stored `bwrapAvailable`, `sandboxUid`, and `sandboxGid` on the lease metadata in the acquire, resume, and probe hooks. - Added a README section that describes the advisory wrapper model. - Kept the execution seam unchanged. ## Verification - `vitest run` for `packages/plugins/sandbox-providers/daytona/src/plugin.test.ts` - The run passed all 106 tests, including the new builder and probe coverage. - The standalone `tsc` run showed only the known baseline noise that already exists on `master`. ## Risks - Risk is low because the execution seam does not change. - The new wrapper stays advisory and does not alter the sandbox security model. - The probe results only add metadata and do not fail the lease on missing host support. ## Model Used OpenAI Codex, GPT-5, tool use. ## 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 - [x] My branch name describes the change and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [x] All Paperclip CI gates are green - [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.731.0-canary.7 |
||
|
|
4813ed3f0c |
fix(db): harden embedded Postgres test start with bounded retry (#10540)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The database layer uses embedded Postgres for isolated test runs > - A port probe can fail when another process takes the same port before Postgres binds it > - That race can make a test fail even when the code under test is fine > - This pull request adds bounded retry and clearer error text to the embedded Postgres start path > - The benefit is more stable tests and faster diagnosis when startup still fails ## Linked Issues or Issue Description No public GitHub issue exists for this change. This PR fixes a flaky embedded Postgres test start path. The helper can lose a port between probe and bind. This PR retries the start with a fresh port and a fresh data directory. Related public context: - Refs #7259 - Refs #9769 ## What Changed - Add bounded retry around embedded Postgres initialization and start. - Stop each failed attempt and remove its data directory before the next attempt. - Capture Postgres output in the thrown error so the failure is easier to read. - Add unit coverage for retry success, retry exhaustion, and the improved error text. ## Verification - `pnpm --filter @paperclipai/db exec vitest run src/test-embedded-postgres.test.ts src/embedded-postgres-error.test.ts` - `pnpm --filter @paperclipai/db exec vitest run` - `pnpm --filter @paperclipai/db exec vitest run` passed in the worktree after the change. - `worktree.test.ts > quarantines copied live execution state in seeded worktree databases` passed. - A real cluster loop of 100 starts passed with 0 failures. ## Risks - The retry can hide a real startup fault until the fifth try. - The bound keeps the wait short, and the final error still shows the captured Postgres log. - This change only affects the embedded Postgres test start helper. ## Model Used OpenAI Codex, GPT-5, tool-use enabled. ## 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] 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.731.0-canary.6 |
||
|
|
6a3cbe1c58 |
fix(ui): load the full selected timeline window (#9576)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The timeline page visualizes company activity across a selected date window > - The UI requested only the first paginated issue batch even when the selected zoom covered seven or thirty days > - A busy company could therefore render an incomplete timeline while the controls implied the full window was loaded > - The timeline query needs to exhaust the API pagination for the selected date range and combine each page without duplicating shared timeline records > - This pull request adds a paginated window loader, merges the returned timeline data, and covers the multi-page behavior with a regression test > - The benefit is that the visible timeline matches the selected zoom window instead of silently omitting later issues ## Linked Issues or Issue Description ### Pre-submission checklist - [x] I searched existing open and closed issues and pull requests; no matching report or implementation was found. - [x] I reproduced the behavior against the pre-change `master` implementation. - [x] I confirmed the error originates in Paperclip's core timeline UI, not an adapter, provider, or local configuration. ### What happened? Selecting the default seven-day timeline range loaded only the first API page (up to 500 issues). Companies with more activity therefore displayed incomplete data even though the controls showed the full selected window. ### Expected behavior The timeline should load all issue pages that fall within the selected date window. ### Steps to reproduce 1. Open the company timeline for a date range containing more than 500 issues. 2. Keep the default seven-day range or select another multi-day preset. 3. Observe that only the first page of issue-backed timeline data is shown. ### Paperclip version or commit Pre-change `master`. ### Deployment mode Local dev source build. The behavior is not adapter-specific and is independent of database mode and access context. ### Privacy checklist - [x] No logs, configuration, personally identifiable information, or user data are included. ## What Changed - Added pagination parameters to the timeline API client contract. - Added a timeline window loader that requests every issue page and deduplicates actors, spans, events, and edges while preserving pagination metadata. - Switched the timeline query to use the complete-window loader. - Added a regression test proving a 501-issue window loads both API pages and combines their records. - Preserved delegation events and edges when parent and child issues fall on different API pages, with a server regression test. ## Verification - `pnpm exec vitest run server/src/__tests__/work-timeline-service.test.ts ui/src/pages/Timeline.test.tsx` — 16 tests passed. - `pnpm --filter @paperclipai/ui typecheck` — passed. - `pnpm --filter @paperclipai/server typecheck` — passed. - `pnpm check:token-gates` — all gates clean. - `git diff --check origin/master...HEAD` — passed. - Remote CI: build, typecheck, both e2e shards, canary, policy, security, every general/serialized test shard, and the aggregate `verify` gate passed on head `24784b28e9`. ## Risks - Low risk: the change is isolated to timeline data loading and has no schema or API endpoint changes. - Large date windows now make sequential requests for all issue pages, increasing request count for very active companies; the 500-item page size bounds each response. - Merged records rely on stable identifiers or composite event/edge keys; the regression test covers cross-page combination and deduplication behavior. > 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 using GPT-5.4 with reasoning, repository tool use, shell execution, and test execution. The runtime does not expose the exact 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.731.0-canary.5 |
||
|
|
dd1a7f5290 |
Ensure app-home ownership before the privilege drop, not only on remap (#10530)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - The Docker image persists all instance state (project checkouts, worktrees, run logs, uploads) under `PAPERCLIP_HOME`, and deployments mount a volume there for durability > - The entrypoint starts as root and drops privileges to the `node` user, but it fixes `PAPERCLIP_HOME` ownership only when it remaps the user's UID/GID > - A freshly mounted volume arrives root-owned and shadows the image's build-time `chown`, so a default-UID boot drops privileges onto an unwritable home and the server crashes on its first `mkdir` > - This pull request makes the entrypoint probe the home's ownership and chown whenever it does not match the runtime user, before the privilege drop > - The benefit is that the image works out of the box on any platform-managed volume, with the common already-correct boot staying chown-free ## Linked Issues or Issue Description No public issue exists — describing the bug inline (per the bug report template). **What happened?** Running the image with a freshly created volume mounted at `/paperclip` (a Docker named volume, a Kubernetes PV, or any platform-managed volume) and the default `USER_UID`/`USER_GID` crashes on boot: `Error: EACCES: permission denied, mkdir '/paperclip/instances/default/logs'`. **Expected behavior** The container boots and initializes its instance tree on the mounted volume, exactly as it does when `/paperclip` is the image's own (build-time chowned) directory. **Steps to reproduce** 1. `docker volume create paperclip-data` 2. `docker run -v paperclip-data:/paperclip ghcr.io/paperclipai/paperclip:<any current tag>` 3. Observe the EACCES crash on the first `mkdir` under `/paperclip`. **Root cause** `scripts/docker-entrypoint.sh` chowns `/paperclip` only inside its UID/GID remap branch (`changed=1`). A fresh volume mount is root-owned and shadows the image's build-time `chown node:node /paperclip`; with the default 1000:1000 no remap happens, so no chown happens, and `gosu node` drops onto an unwritable home. **Paperclip version or commit:** reproduces on `master` and any published image. **Deployment mode:** any; observed on managed-cloud volume mounts and reproducible with plain Docker named volumes. **Installation method:** Docker image (`ghcr.io/paperclipai/paperclip`). **Related PRs (dedup search):** no open or merged PR touches the entrypoint ownership logic; the entrypoint's privilege-handling tests were added previously and this extends them. No duplicate found. ## What Changed - `scripts/docker-entrypoint.sh`: the remap-conditional `chown` is replaced by an ownership probe — after any UID/GID remap, the entrypoint stats `PAPERCLIP_HOME` (default `/paperclip`) and runs `chown -R node:node` only when the owner does not match the runtime user, before `exec gosu node`. Covers fresh root-owned mounts and trees written under a previous UID mapping; the already-correct boot performs no chown. The unprivileged (non-root start) branch is unchanged. - `server/src/__tests__/docker-entrypoint.test.ts`: `stat` stub added to the harness; new cases for the fresh root-owned mount with default UID/GID and for `PAPERCLIP_HOME`-relative probing; the remap case now models the post-remap ownership mismatch. ## Verification - `pnpm vitest run server/src/__tests__/docker-entrypoint.test.ts` — 7 passed (5 existing behaviors unchanged, 2 new). - Live on a managed deployment: a container that crash-looped with the EACCES above boots cleanly once the home is chowned before the drop (the same effect this entrypoint change produces; forced there by a UID remap as an interim workaround). ## Risks - Low. Behavior changes only for boots where `PAPERCLIP_HOME` exists with mismatched ownership — exactly the boots that crash today. `chown -R` on a large previously-mismatched tree adds one-time boot latency; correctly-owned homes skip it entirely. Kubernetes restricted / OpenShift non-root starts keep the existing exec-directly path untouched. ## Model Used Claude Fable 5 (`claude-fable-5`, Anthropic; Claude Code CLI with extended thinking and tool use; tests executed locally via Vitest). ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above (no duplicates; extends the existing entrypoint privilege tests) - [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 mergecanary/v2026.731.0-canary.4 |
||
|
|
075951f6bd |
Fix import completion UX: inbox flood, false-failure message, stale company list (#10538)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Company Import/Export (#10507, hardened in #10523 and #10531) now imports a large company end to end via an async job > - A real 1,418-issue import succeeded, but three rough edges showed up in that success > - Imported issues flooded the inbox, a completed import surfaced a false "failed" message after its in-memory result expired, and the new company didn't appear in the switcher until a manual refresh > - This pull request keeps imported issues out of the inbox, treats an expired-but-completed import as success, and refreshes the company list on completion > - The benefit is that a successful import looks and feels successful, and doesn't bury the user's inbox in historical tasks ## Linked Issues or Issue Description - Refs #10507 / #10523 / #10531 (Import/Export and its hardening). No open issue; three post-import bugs described above. ## What Changed - **Imported issues no longer flood the inbox.** The inbox "mine" tab is a query: an issue is "touched" if the user authored a comment on it, and import re-attributes bundled user comments to the importing user — so every imported issue appeared. Import now seeds a per-user `issue_inbox_archives` row for each imported issue (via a batched `issues.archiveImportedInbox`), the exact table the inbox visibility query excludes. Gated on an actor user id, so agent/system imports and normal issue creation are untouched; genuine new activity still resurfaces the issue. - **A completed import no longer shows a false failure.** The in-memory job's terminal retention was 5 minutes, so a poll after that 404'd and the UI showed "failed." Retention is extended to 60 minutes — the real mitigation for a user who steps away during a long import. `watchImportJob` additionally treats a *server-confirmed* success whose full result is no longer retained (a `succeeded` status carrying only the compact summary — a cloud tenant job, or a board job whose full in-memory result aged out) as a soft success ("import completed — open the company"), navigating by the summary's company id. A 404 while the job is still being watched is *not* treated as success: a running job is never dropped by the retention sweep, so its disappearance means a restart mid-import that may not have finished, and it surfaces the honest "may have restarted while the import ran" error. A first-poll 404 (the id never existed) is likewise a real error. - **The imported company appears without a refresh.** `onSuccess` now invalidates the companies/switcher query unconditionally (covering both the full-result and expired-but-completed paths) and navigates by the job's company id. ## Verification - shared/server/ui typechecks clean; 15 UI tests in the touched spec green, plus the embedded-Postgres import batching and portability-routes suites. - New tests: embedded-Postgres test that imported touched issues are archived for the actor and excluded from the inbox query while a normally-created issue still appears; job resolvable at the old window+1 and only 404s past 60 min; UI soft success on a server-confirmed `succeeded` job without a retained full result (no error, list invalidated, navigates by company id), a running-then-gone job → honest error (restart mid-import), and a first-poll 404 → error. ## Risks - Low and import-scoped: the inbox archive only affects imported issues for the importing user; normal issue creation and non-user (agent/system) imports are unchanged. Retention extension is a constant; the async job store remains in-memory by design. A restart mid-import still 404s and is surfaced honestly as a possible failure (never masked as success); only a server-confirmed success whose full result has expired is reported as a soft success. ## Model Used - Implementation: Claude Fable 5 (`claude-fable-5`, Anthropic). Review hardening (the confirmed-success narrowing): Claude Opus 4.8 (`claude-opus-4-8`, Anthropic). Both via the Claude Code CLI with extended thinking + tool use; root-caused against the live import. ## 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 - [x] My branch name describes the change and contains no internal ticket id - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] 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 |
||
|
|
9f7565f4ce |
feat: activate OpenTelemetry spans on the sandbox start path (#10536)
## Thinking Path > - Paperclip moves agent work through sandboxed execution and control-plane services. > - The sandbox start path now has a no-op span seam. > - This change turns that seam on when OTLP export is configured. > - It keeps the default path unchanged when export is off. > - The result is structured startup traces with low-cardinality attributes and explicit parent links. > - The benefit is better observability without changing normal behavior. ## Linked Issues or Issue Description No public GitHub issue exists for this change. ### Problem The sandbox start path has a tracer seam, but it stays a no-op unless the OTLP export path is active. ### Proposed solution Enable the server tracer on sandbox bring-up, open a root span, parent each startup boundary to that root, and keep the export path opt-in behind `OTEL_EXPORTER_OTLP_ENDPOINT`. ### Alternatives considered - Keep the start path as a no-op. I rejected that path because it leaves sandbox start opaque when OTLP export is already configured. - Add broad attributes for commands and paths. I rejected that path because the span allowlist must stay low-cardinality. ### Roadmap alignment This follows the current OTel sandbox-start work and keeps the default path unchanged. ## What Changed - Add a root sandbox startup span and child spans for each named startup boundary. - Keep concurrent bridge spans parented to the root span. - Inject the server tracer through the adapter deps without OpenTelemetry imports in the engine. - Attach host-received provider duration attributes only when the values are finite. - Keep span attributes inside the allowlist and keep command, path, id, and error text out of span data. ## Verification - The pushed branch already passed `pnpm --filter @paperclipai/adapter-utils exec tsc --noEmit`. - The pushed branch already passed `pnpm exec vitest run packages/adapter-utils/src/acpx-engine/`. - The pushed branch already passed `pnpm --filter @paperclipai/server exec vitest run src/__tests__/environment-execution-target.test.ts src/__tests__/instrumentation.test.ts`. - The pushed branch already passed `pnpm --filter @paperclipai/server exec tsc --noEmit`. ## Risks - OTel export changes trace volume when the endpoint is set. - The allowlist limits trace detail, so new fields need care. - The change stays no-op when OTLP export is off. ## Model Used - OpenAI GPT-5, tool use enabled. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used with 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 - [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 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> |
||
|
|
5cffd5c72e |
feat(sandbox): declare and collect the advisory read-write intent (#10521)
## Thinking Path > - Paperclip is the control plane for AI agent companies > - Sandbox providers move files between the host and the sandbox > - The runtime needs advisory data for which sync paths are writable > - The host already knows that intent when it prepares the sync map > - Daytona can record that intent for later use > - This pull request adds the advisory access field and collects writable paths > - The benefit is that later runtime work can use the data without changing current flow ## Linked Issues or Issue Description No public GitHub issue exists for this change. The description below follows the feature-request template fields. ### Problem or motivation An agent runs inside an ephemeral sandbox. Some sandbox paths keep agent changes; other paths do not. Today the runtime has no declared signal for which sync destinations the agent may change and keep. A later feedback wrapper needs this signal to give the agent real-time feedback when a write lands on a non-persistent path. ### Proposed solution Add an optional advisory `access: "rw" | "ro"` field to the sync file-mapping types. The host sets `rw` for the workspace, git-history, and asset destinations, and `ro` for referenced-project trees. An absent value defaults to `ro`. The Daytona provider collects the `rw` target directories into a per-lease writable set for later use. This change adds the metadata and the collection only. No execution path reads the writable set yet, so runtime behavior does not change. ### Alternatives considered Derive a static writable set in provider code. This alternative is weaker: the layer that authors each sync destination already knows the intent, so a per-operation declaration is more accurate and does not hard-code a path list. ### Roadmap alignment This is the first step toward an advisory sandbox feedback wrapper. The wrapper is best-effort and adds no security. The ephemeral sandbox stays the only boundary. ### Additional context The field is advisory metadata. It does not change the file transfer and adds no security. ## What Changed - Added an optional `access` field to the sync file mapping types. - Set the workspace, git history, and asset destinations to writable. - Set referenced project destinations to read only. - Added a writable set store in the Daytona provider. - Recorded the parent directory of each writable sync mapping during sync in. ## Verification - The handoff reports these checks before push. - `pnpm --filter @paperclipai/adapter-utils exec vitest run sandbox-managed-runtime.test.ts` - `pnpm --filter @paperclipai/plugin-sdk exec tsc --noEmit` - `vitest run src/plugin.test.ts` in the Daytona package - `tsc --noEmit` in the Daytona package ## Risks - Low risk. - The new field is advisory. - The writable set store is best effort and in memory. - A cold store falls back to the workspace baseline. ## Model Used OpenAI Codex, GPT-5, tool use, 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 linked existing issues or described the issue in the PR - [x] I have not referenced internal or instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change and contains no internal Paperclip ticket id or instance-derived details - [ ] I have run tests locally and they pass - [ ] I have added or updated tests where applicable - [ ] I have updated relevant documentation to reflect my changes - [ ] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [ ] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.731.0-canary.3 |
||
|
|
5ec7ce76e5 |
Upload company import packages as compressed zip uploads (fix large-company imports through Cloud) (#10531)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Company Import (#10507, hardened in #10523) lets a user upload a company package on the Import page > - The page expanded the user's `.zip` into a files map and POSTed it as ONE inline JSON body — ~40MB for a real company because attachment blobs get base64-inflated > - On Paperclip Cloud that body travels browser → harness proxy → tenant, where it truncated in transit → body-parser 400 → the browser saw "Failed to fetch", and nothing imported > - Two compounding causes: the giant inline body itself, and the board async opt-in riding an `x-paperclip-cloud-*` header that the Cloud harness strips as anti-spoofing (so async never engaged and the import held one fragile synchronous connection) > - This pull request uploads the raw compressed `.zip` as a multipart request (about a third the size, already compressed) parsed server-side into the same bundle the importer consumes, and moves the async opt-in to a proxy-safe `?async=1` > - The benefit is that a large-company import actually completes through Cloud: a small compressed upload, a real async job that survives dropped connections ## Linked Issues or Issue Description - Refs #10507 / #10523 (Import/Export and its hardening). No open issue; problem described above (large-company browser import through a proxy: inline JSON body truncates → 400 → "Failed to fetch"; async opt-in header stripped by the front door → async never engages). ## What Changed - **Multipart zip transport.** The Import page uploads the raw `File` as `multipart/form-data` (field `package`, import options in a JSON `meta` field); the server unzips it into `{ rootPath, files }` and runs the exact existing preview/import logic. The `application/json` inline path is byte-identical for CLI/programmatic callers. Bare `application/zip` (meta via `?meta=`) is also accepted for programmatic use. - **Shared node zip reader.** `packages/shared/src/portability-zip.ts` (node-only subpath, not re-exported to the browser bundle — same pattern as `portability-hash.ts`); the CLI's `zip.ts` becomes a thin re-export. Identical codec (STORE + DEFLATE via `inflateRawSync`, rejects data descriptors/zip64). - **Proxy-safe async signal.** `wantsAsyncImport` = `?async=1` (board browsers, survives the harness) OR the existing `x-paperclip-cloud-async-import` header (cloud tenants, set server-side). The UI async client now uses `?async=1`. Backward compatible. - **Size + preflight.** New `PORTABLE_ZIP_UPLOAD_LIMIT_BYTES = 128MB`; the inline 56MB preflight no longer gates the zip path (it shows the compressed size instead). Async submit/poll/resume, the duplicate-guard fingerprint (now over the resolved bundle), pause-on-import, progress/error panels, and activation all apply to the multipart path. - OpenAPI documents json + multipart + zip bodies and the `async` query param. ## Verification - Full typecheck chain (shared, server, ui, cli) clean. - 152 tests across 8 files: new `portability-zip.test.ts` (STORE/DEFLATE/base64-blob byte-exact round-trip, truncation throws, data-descriptor rejection); `company-portability-routes.test.ts` +7 (multipart import+preview equals the inline bundle; async multipart 202→poll→success; board async via `?async=1` with no cloud header; cloud-tenant async via header; sync fallback with neither; truncated-zip 400, nothing imported); `CompanyImport.test.tsx` asserts the local zip sends the raw File and the inline preflight no longer blocks; `openapi-routes.test.ts` green. - NOT yet measured: the end-to-end browser upload through the live Cloud harness — verified on staging after deploy before closing out. ## Risks - Import semantics unchanged — only transport changed; the JSON inline path is byte-identical, the cloud-tenant header async path untouched. Multipart parsing is server-side (memory-bound: a ~13MB zip → ~30MB files map, fine on the server). - The bare `application/zip` path is programmatic-only and covered by content-type dispatch but not a dedicated route test (the multipart path is). ## Model Used - Claude Fable 5 (`claude-fable-5`, Anthropic), Claude Code CLI, extended thinking + tool use; root-caused against live logs/DB and the harness proxy source. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting mergecanary/v2026.731.0-canary.2 |
||
|
|
740554acc6 |
feat: add a no-op OpenTelemetry span seam for the sandbox startup path (#10522)
## Thinking Path > - Paperclip helps people run and govern AI agent work > - Sandbox startup needs a safe place to add telemetry spans without forcing OpenTelemetry on every run > - This change adds a no-op span seam, so the startup path can accept a tracer later and still stay inert now > - The server gets a lazy tracer accessor, and the adapter timing helper gets an injected tracer hook > - The change keeps the default path free of OpenTelemetry and keeps the existing startup event path unchanged > - The benefit is a future-safe seam with no runtime change today ## Linked Issues or Issue Description This PR addresses a feature gap in the sandbox startup path. ### Problem Sandbox startup has no safe span seam. A direct OpenTelemetry import would load telemetry packages on every run. ### Proposed Solution Add a lazy tracer accessor in the server. Add an injected no-op tracer seam in startup timing. ### Alternatives Import OpenTelemetry directly in the startup path. Reject that path because the default startup flow must stay inert. ## What Changed - Added a lazy startup tracer accessor in `server/src/instrumentation.ts`. - Added an injected startup tracer seam in `packages/adapter-utils/src/acpx-engine/startup-timing.ts`. - Kept the startup event path unchanged. - Kept `adapter-utils` free of OpenTelemetry imports. ## Verification - `pnpm exec vitest run packages/adapter-utils/src/acpx-engine/startup-timing.test.ts` - `pnpm exec vitest run server/src/__tests__/instrumentation.test.ts` - `tsc --noEmit` for `@paperclipai/adapter-utils` and `@paperclipai/server` ## Risks Low risk. The default tracer is a no-op, so the runtime path stays inert until a later change injects a real tracer. ## Model Used OpenAI GPT-5. 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 found none - [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 or instance-local Paperclip issues or links - [x] My branch name describes the change and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [ ] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [ ] I will address all Greptile and reviewer comments before requesting merge --------- Co-authored-by: Paperclip <noreply@paperclip.ing>canary/v2026.731.0-canary.1 |
||
|
|
7cfb655f60 |
fix(worktree): disable automatic database backups (#10520)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Paperclip creates isolated instances for linked git worktrees so development does not affect the primary instance > - Those instances inherited the source instance's automatic database-backup setting and also repaired older configs without overriding it > - As worktrees accumulated, each isolated instance could schedule its own backup stream, producing redundant backup churn for disposable database clones > - This pull request makes backup disablement an invariant of worktree config creation and repair > - The benefit is that automatic backups remain focused on the durable primary instance while isolated development instances stop accumulating redundant backup files ## Linked Issues or Issue Description No public GitHub issue exists for this bug, so the report is included here. The closest related open change is Refs #10266, which hardens where worktree config repair may write; this PR changes the backup policy applied by that repair and by worktree initialization. ### What happened? Isolated worktree instances copied `database.backup.enabled` from their source config. When the source instance enabled automatic backups (the normal default), every linked worktree also enabled a scheduled backup stream. Existing worktree configs kept that state during startup repair, so the redundant backups continued after the policy changed. ### Expected behavior Automatic database backups are disabled for isolated worktree instances created by `paperclipai worktree init` or `paperclipai worktree:make`, and legacy worktree configs are migrated to that policy during normal startup repair. The durable primary/default instance keeps its existing backup behavior. ### Steps to reproduce 1. Start from a Paperclip instance whose database backup setting is enabled. 2. Create or initialize a linked worktree with `paperclipai worktree init`. 3. Inspect the generated worktree config and environment. 4. Before this change, the config retained `database.backup.enabled: true` and the environment had no disabling override; after this change, the config is false and `PAPERCLIP_DB_BACKUP_ENABLED=false` is persisted. ### Paperclip version, deployment mode, and environment - Reproduced against `master` before commit `ea5e0a0269`. - Deployment mode: local trusted development with linked git worktrees and embedded PostgreSQL. - Environment: Node.js 22, pnpm workspace install. ## What Changed - Always generate isolated worktree configs with automatic backups disabled. - Persist `PAPERCLIP_DB_BACKUP_ENABLED=false` in generated worktree environments. - Repair existing isolated worktree configs and environments that still enable backups. - Add CLI and server regression coverage for creation and legacy repair paths. - Document the worktree-specific backup policy and primary-instance exception. ## Verification - `pnpm exec vitest run cli/src/__tests__/worktree.test.ts server/src/__tests__/worktree-config.test.ts` — 52 tests passed. - `pnpm -r typecheck` — passed. - `pnpm build` — passed. - All repository commands above were run with inherited worktree runtime identity variables removed. ## Risks - Low operational risk: the change is limited to explicitly isolated worktree instances. - Operators who intentionally relied on automatic backups of disposable worktree databases will now need to run a manual backup or explicitly manage those files outside the scheduled worktree runtime. - No schema, migration, API, UI, lockfile, or workflow changes. > 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 does not expose a more specific snapshot ID or context-window value), using reasoning, tool use, local code execution, and GitHub CLI integration. ## 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.731.0-canary.0 |
||
|
|
276ae3a75d |
Harden company import: durable UI, async jobs, integrity guard, batched inserts (#10523)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Company Import/Export (#10507) moves whole companies between instances as portability bundles > - Real-world use on a large company (1,418 issues, ~10.6k comments) surfaced a cluster of related failures: the import took hours and the browser connection died while the server kept running, a retry silently produced a second partial import, the progress/error UI gave no durable signal, and a cloud-tenant user couldn't even open the companies afterward > - Root cause of the slowness: importBundle inserted every issue, comment, and document as a separate round-trip to a network Postgres — an N+1-over-network pattern > - This pull request hardens the whole import path: durable progress/error UI, an async server-side job so imports survive dropped connections (with a duplicate-submit guard), a fail-closed guard against incomplete payloads, and batched inserts that cut a large import from hours to minutes > - The benefit is that migrating a real, large company actually completes, is legible while it runs, and can't half-import twice ## Linked Issues or Issue Description - Refs #10507 (the Import/Export feature this hardens). Supersedes #10513 (the progress/error-UI piece, folded in here). No open issue; problem described above (large-company import: slow, connection-fragile, silently duplicable, opaque UI). ## What Changed - **Batched inserts (perf):** importBundle pre-generates entity ids in JS and inserts in chunked multi-row statements, so children no longer wait on parents' generated ids. A 1,418-issue import drops from ~15,600 insert statements to **82** (190×); benchmark below. Import semantics — collision handling, pause-on-import, label/blocker/monitor/attachment/embedded-asset handling, blob sha verification — are unchanged (full portability suite green). - **Async import jobs for board sessions:** the existing cloud-tenant async job path opens to board sessions with per-actor job keys; the import page submits, polls, and resumes watching after a reload or dropped connection instead of holding one fragile request. A non-terminal job blocks a duplicate submit (409 returns the running job), preventing the double-import. - **Fail-closed completeness guard:** an optional `expectedFileCount` on inline imports; the server rejects (422 `import_payload_incomplete`) a body carrying fewer files than declared, so a re-framed/short payload fails loudly instead of half-importing. - **Durable progress/error UI (was #10513):** persistent progress panels with size-aware copy, persistent error panels with retry guidance, and inline explanation when the preview button is disabled; request-lifecycle guards so stale previews/imports can't publish or detach. ## Verification - `pnpm -r` typechecks (shared, server, ui) clean. - `company-portability.test.ts` (76) + `company-portability-routes.test.ts` (30) green — the import correctness net — plus new `CompanyImport.test.tsx` async/resume/409 coverage and a new batching regression test (a 50-issue import issues <50 issue-insert statements; rows land unchanged). - **Batching benchmark (embedded Postgres):** at 1,418 issues × 7 comments × 1 doc — 82 insert statements vs ~15,598 one-per-row (190×), ~1s wall-clock; a row-verifying run at that scale imports all 1,418 issues / 9,926 comments / 1,418 documents with unique identifiers and no warnings (no rows dropped by chunking). Over a network DB the round-trip reduction is the hours→minutes lever. - What is NOT directly measured here: wall-clock against a real network Postgres (that happens on a staging deploy); the local timing is network-free. ## Risks - Batching is the load-bearing change: it rewrites the import write path. Mitigated by the unchanged 106-test correctness suite, a new scale/row-integrity test, and per-writer transactions (a failure rolls back its table group; not a single outer transaction across writers — noted, correctness preserved). - Async jobs are in-memory (lost on server restart → pollers 404 and can resubmit); matches the pre-existing cloud-tenant job semantics. - `expectedFileCount` is optional (older callers unaffected); over-count is allowed, only under-count fails closed. ## Model Used - Claude Fable 5 (`claude-fable-5`, Anthropic), Claude Code CLI, extended thinking + tool use; implementation across Fable 5 subagents with live diagnosis against a running instance. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting mergecanary/v2026.730.0-canary.10 |
||
|
|
1c52f02d34 |
Let cloud tenant sessions reach companies they hold memberships in (#10524)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - On Paperclip Cloud, each stack authenticates its users to the tenant app through trusted headers (`resolveCloudTenantActor`), which seed a primary company for the stack > - That actor was pinned to exactly one company — the seeded primary — regardless of any other companies the user actually holds a membership in > - Companies created later (via the import flow, or company creation) write real membership rows for the user, but the pinned actor ignored them, so those companies showed up in listings yet returned "User does not have access to this company" when opened > - This pull request unions the pinned primary with the user's own active membership rows, exactly as a locally authenticated session already does > - The benefit is that a Cloud user can reach every company they belong to — most visibly, a company they just imported ## Linked Issues or Issue Description - Refs #10507 (Import/Export — imported companies were unreachable on Cloud stacks). No open issue; bug described above (companies visible in listing but unreachable; expected: reachable when the user holds an active membership). ## What Changed - Extracted the session path's own active-membership query into `loadActiveUserCompanyMemberships(db, userId)` (single-sourced; the session path now calls it too). - `resolveCloudTenantActor` unions its result with the pinned primary: `companyIds = [primary, ...others]`, memberships likewise, primary first. Strictly per-user; a membership-read failure degrades to primary-only (mirrors the existing fail-closed owner-elevation pattern). No change to owner instance-admin elevation, grant seeding, the stale instance-admin purge, or trusted-header validation. - Grants are seeded at membership creation across all flows (company create, invite/join, import), not per request — so no extra seeding was added here. ## Verification - `@paperclipai/server` typecheck clean. - `cloud-tenant-actor.test.ts` (+ union / other-user-excluded / inactive-excluded / no-rows-identical cases), `auth-session-route.test.ts` (route-level: trusted headers reach a unioned company through `assertCompanyAccess`), plus agent-auth, authz-company-access, cross-company-authz, portability-routes — 83 tests green. ## Risks - Low and tightly scoped: only widens a Cloud actor's reachable companies to those it already holds active memberships in; users without extra memberships, other users' rows, and owner elevation are all unaffected. Read failure fails closed to primary-only. ## Model Used - Claude Fable 5 (`claude-fable-5`, Anthropic), Claude Code CLI, extended thinking + tool use. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] All Paperclip CI gates are green - [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups - [x] I will address all Greptile and reviewer comments before requesting mergecanary/v2026.730.0-canary.9 |
||
|
|
a5d252d058 |
feat(skills): add confirmed inbox gardening workflow (#10462)
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - Operators use the Mine inbox to track work that needs their attention. > - Old terminal work can make that inbox difficult to review. > - Inbox cleanup must not change issues, branches, or workspaces. > - Operators also need to approve each cleanup set before Paperclip hides any item. > - This pull request adds a garden-inbox skill with scan, confirmation, and apply stages. > - The benefit is a safe and repeatable inbox cleanup workflow with an explicit undo path. ## Linked Issues or Issue Description ### Subsystem affected Cross-cutting. This change adds an agent skill that calls existing inbox, workspace, interaction, and archive APIs. ### Problem or motivation Old terminal inbox entries can hide current work. Manual cleanup is slow and can select the wrong entries. A single global result limit can also hide rows from a large inbox. ### Proposed solution Add a zero-dependency skill that classifies inbox rows, requests checkbox confirmation, and archives only accepted rows for the current user. Scan each relevant status separately, remove duplicate rows, and report partial coverage if a status reaches its result limit. ### Alternatives considered Automatic archive was rejected because it removes operator control. A report-only tool was rejected because it does not complete the cleanup workflow. ### Roadmap alignment This is an agent skill. It does not add a new core product capability or duplicate a planned roadmap item. ### Additional context The apply stage changes only per-user inbox visibility. It provides the archive API undo path for every successful item. A GitHub search found no separate duplicate implementation; this pull request is the existing result for the search terms. ## What Changed - Added the `garden-inbox` skill workflow and command reference. - Added a zero-dependency Node script with `scan`, `confirm`, and `apply` commands. - Added four exclusive classification buckets with machine-readable reasons. - Added per-status Mine scans, cross-status deduplication, and partial-coverage reports for capped status results. - Added chunked checkbox confirmations with deterministic idempotency keys. - Added strict accepted-option validation, per-item archive results, and undo instructions. - Added Node tests for classification, fail-safe inspection, accepted selections, tampering, JWT decoding, URL normalization, and capped scans. ## Verification - Passed `node --check .agents/skills/garden-inbox/scripts/garden-inbox.mjs`. - Passed `node --test .agents/skills/garden-inbox/scripts/garden-inbox.test.mjs`. - Passed `pnpm -r typecheck` with inherited Paperclip runtime identity variables removed. - Passed `pnpm build` with inherited Paperclip runtime identity variables removed. - Ran `pnpm test:run`: 3,241 tests passed and 1 test skipped. One unrelated current-base server integration assertion failed because it received `heartbeat.scheduling_suppressed` instead of `issue_commented` in `plugin-orchestration-apis.test.ts`. The exact test failed the same way on one focused rerun. This pull request changes only `.agents/skills/garden-inbox/**`. ## Risks - Low risk. The skill is opt-in and does not change server or UI behavior. - `scan` and `confirm` do not mutate issues. `apply` only updates per-user inbox archive visibility for accepted option IDs. - API or workspace inspection failures keep items out of the archive candidate set. - A status result at the configured limit produces a visible partial-coverage warning instead of claiming a complete scan. > 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. See `CONTRIBUTING.md`. ## Model Used - OpenAI `gpt-5.6-sol` through the Codex local adapter. The runtime did not expose the context window size. The model used high-reasoning coding, shell, API, and code-execution 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> |