## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - It runs each agent in a remote sandbox and emits OpenTelemetry spans
for the sandbox bring-up and the run.
> - A real trace showed two gaps. `stage.sync` had about 3 seconds of
unattributed host work before its `pack` span. The persistent agent
process showed a `sandbox.exec` span that outlived its parent by about
50 seconds.
> - The gaps hide real cost and make the trace read as a sequencing bug,
so an operator cannot see where startup time goes.
> - This pull request wraps the two pre-`pack` host steps in their own
spans. It also homes the long-lived process in a run-scoped
`sandbox.agentProcess` span.
> - The benefit is that startup time is fully attributed and the process
reads as a resource that overlaps the turn, not a child that outlives
its parent.
## Linked Issues or Issue Description
No public issue exists. This is an enhancement to existing telemetry. It
is described inline below, following
`.github/ISSUE_TEMPLATE/enhancement.yml`. Prior related work: the merged
PR #10999 added the run-time wrapper spans and the telemetry
data-contract section this PR extends.
**What existing behavior does this improve?**
The sandbox bring-up and run OpenTelemetry trace. It closes two
attribution gaps in that trace.
**Subsystem affected**
Observability for sandbox execution. The code lives in
`packages/adapter-utils`. The span contract lives in
`packages/shared/src/telemetry`.
**Current behavior**
`stage.sync` opens a `pack` span, but the git enumeration and the
baseline content-hash walk that run before `pack` have no span, so about
3 seconds read as a gap. On the streamed process-session path the agent
process launches fire-and-forget inside the ~2.3 second
`bridge.process-session` bring-up step, so its `sandbox.exec` span
parents to that step and then runs about 50 seconds. The child dangles
past its parent and overlaps `agent.turn`.
**Proposed behavior**
Wrap the two pre-`pack` host operations in `snapshot.git` and
`snapshot.baseline` spans under `stage.sync`. Wrap the streamed launch
in a run-scoped `sandbox.agentProcess` span that parents to the live run
root (`task.run` at launch).
**Reason and benefit**
Startup time is fully attributed. The long-lived process reads as a
resource that overlaps the sibling `agent.turn`, not a mis-parented
child.
**Breaking changes**
None. The spans are opt-in and export only when an OTLP endpoint is
configured. The span seam is a no-op when no runner is injected. No
first-party telemetry event changes.
## What Changed
- `sandbox-managed-runtime.ts`: add `snapshot.git` and
`snapshot.baseline` spans around the git enumeration and the baseline
content-hash walk, nested under `stage.sync`, through a shared
`runStepSpan` helper that `pack` now also uses.
- `execution-target.ts`: wrap the fire-and-forget streamed launch in a
run-rooted `sandbox.agentProcess` span, so it parents to the live run
root and holds the inner `sandbox.exec`. The `.then`/`.catch` chain
became try/catch inside the span callback, with identical
frame-ingestion behavior.
- `packages/shared/src/telemetry/README.md`: update the span table and
the parenting prose. Add `snapshot.git`, `snapshot.baseline`, `pack`,
and `sandbox.agentProcess`, and document the intended
`sandbox.agentProcess` / `agent.turn` overlap.
- Tests: update the executor span-tree test (`childNames` and parent
assertions), update the `sandbox-managed-runtime` span-set and nesting
tests, and add two `execution-target-sandbox` tests (the launch opens
`sandbox.agentProcess`; it parents to the run root, not the bring-up
step).
## Verification
- Run `npx vitest run` on the three affected test files. Result: 174
tests pass. This includes the updated executor span-tree test and the
new `sandbox.agentProcess` open and parenting tests.
- Run `tsc --noEmit` in `packages/adapter-utils`. Result: no errors in
the changed source or test files.
- The full 37-test streamed process-session suite passes unchanged. This
confirms the try/catch restructure preserves frame delivery and
exit/error behavior.
- Pre-existing and unrelated to this PR (present on `master`): `tsc`
errors in `execute.ts` / `execute.test.ts` /
`remote-spawn-smoke.test.ts` (`onAgentStderr` / `spawnCwd`), and a
`check:forbidden-tokens` failure from internal `PAP-###` ids in
`ui/src/components/IssueRecoveryActionCard.test.tsx`. This PR does not
touch those files, and its own diff is token-clean.
## Risks
Low. The change adds instrumentation on the opt-in span path and does
not change control flow on the default path. The one production
restructure is the streamed launch, which stays fire-and-forget, so
bring-up does not block on it. Only the streamed path gains
`sandbox.agentProcess`; the legacy poll path launches the process
detached and has no host-side long-lived span to home.
## Model Used
Anthropic Claude Opus 4.8 (`claude-opus-4-8`), about 200K-token context,
agentic tool use through Claude Code. The trace was reviewed through the
Honeycomb MCP. The code was written and tested with the model.
## 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: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip helps people manage AI agents for work.
> - Agent adapters connect Paperclip to tools such as the Codex command
line tool.
> - A sandboxed Codex agent may start without a credential.
> - The operator needs a safe sign-in flow that does not expose
credentials to the shared package or the sandbox.
> - This pull request adds a company-scoped device-login flow with a
temporary Daytona sandbox.
> - The flow promotes the credential only after readiness checks pass
and removes the temporary sandbox after use.
> - The result lets an operator sign in to a sandboxed Codex agent from
the agent form.
## Linked Issues or Issue Description
**Subsystem affected**
Cross-cutting (multiple of the above)
**Problem or motivation**
A Codex adapter that runs in a sandbox cannot authenticate when the
company has no pre-provisioned Codex credential.
**Proposed solution**
Add a company-scoped device-login session. Start a temporary sandbox,
run `codex login --device-auth`, stream the code and URL, verify
readiness, promote the credential, and delete the sandbox.
**Alternatives considered**
Pre-provisioning a credential does not support first-time sandbox login.
Keeping the credential in the login sandbox does not provide a durable
company credential.
**Roadmap alignment**
This supports the roadmap item for cloud and sandbox agents.
**Additional context**
The flow uses a five-minute cleanup reaper, compare-and-set status
changes, and a PostgreSQL advisory lock to protect promotion and
cleanup.
## What Changed
- Add the adapter login-session contract, database table, and migration.
- Add company-scoped server routes and a service for sandbox device
login.
- Add credential promotion, readiness checks, and cleanup after login.
- Add restart-safe cleanup for abandoned login sandboxes.
- Add sandbox login controls to the agent creation and edit forms.
- Keep device-login and vendor identifiers out of public shared and
adapter UI symbols.
## Verification
- `pnpm --filter @paperclipai/adapter-codex-local exec vitest run`
passed with 310 tests at the submitted commit.
- The server login route, service, and reaper tests passed with 45 tests
at the submitted commit.
- The agent form render tests passed with 26 tests at the submitted
commit.
- The public-symbol leak check passed at the submitted commit.
- A live Daytona sign-in flow still requires confirmation by a user with
a live sandbox.
## Risks
The migration adds a new company-scoped table. A promotion or cleanup
race could remove a credential or leave a sandbox active, so the service
uses claims, compare-and-set transitions, and an advisory lock. The live
Daytona flow needs operator confirmation because local tests do not
provide a real browser sign-in.
## Model Used
OpenAI Codex, GPT-5, tool use and code execution, extended reasoning.
## 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Company import moves large packages into an instance, and since the
upload cap rose to 1 GB, the transport is the weak point: one HTTP
request, buffered fully in memory, with no resume
> - A dropped connection at 90% of an 800 MB upload starts the whole
transfer over, and a server restart loses all progress
> - This pull request adds the server side of chunked resumable import
transfers: a durable run ledger and routes that accept the same import
zip as verified ~32 MB parts spooled to disk
> - An interrupted transfer resumes from the parts already uploaded —
across dropped connections, page refreshes, and server restarts — and
peak upload memory drops from the whole package to one part
> - The benefit is that large imports become reliable on real-world
connections instead of all-or-nothing
## Linked Issues or Issue Description
**What happened?**
Large company imports travel as a single HTTP upload. On a slow or flaky
connection, any interruption discards all progress and the upload
restarts from zero. The server buffers the entire compressed package in
memory during upload. A server restart mid-upload loses the transfer
entirely. With the upload cap now at 1 GB, these failure modes govern
exactly the imports the cap was raised for.
**Expected behavior**
A large import upload survives interruptions: already-transferred data
is kept and verified, only the missing remainder is re-sent, and the
server's memory use during upload is bounded by a part, not the package.
**Steps to reproduce**
1. Import a multi-hundred-MB company package over a connection that
drops mid-upload.
2. The upload fails; retrying starts from byte zero.
3. Repeat on an unstable connection and the import may never complete.
## What Changed
- New `company_transfer_runs` table (drizzle schema + migration) and
`companyTransferRunService`: one row per transfer with a content-derived
idempotency key, per-part completion recorded atomically and
idempotently, resume scoped to actor and direction, completed runs
short-circuiting retries of identical content.
- New transfer routes beside the existing import routes, same
authorization: declare a sliced zip (`POST /import/transfers` —
validates cap, 64 MB part ceiling, contiguity, size sums, sha256
format), upload parts (`PUT .../parts/:n` — raw body, hash-and-size
verified before an atomic write to a disk spool under the instance root;
re-uploads are no-op successes), poll resume state (`GET .../:id` —
missing parts recomputed from disk), and apply (`POST .../:id/apply` —
requires all parts, re-verifies the assembled zip against the whole-file
hash fail-closed, then feeds the existing import pipeline through
factored helpers rather than duplicated logic).
- Hourly sweep fails and cleans spools idle for 24 h; a swept transfer
honestly reports all parts missing on resume.
- Strict UUID gating on run ids before any filesystem path construction.
- The existing single-shot upload path is untouched; clients arrive in
the follow-up PR.
## Verification
- Transfer route suite (embedded Postgres): create/upload/status/apply
round-trip with a real imported company, out-of-order parts, wrong-hash
part rejected and unrecorded, re-upload no-op, apply-with-missing-parts
rejection, resume after failure with prior progress intact,
assembled-hash mismatch failing closed with spool deletion, actor
scoping 404s, async-job apply, sweep followed by honest resume.
- Ledger suite (embedded Postgres): part idempotency, actor/direction
scoping, completed-run short-circuit, cancelled runs staying cancelled.
- Existing portability route suite unchanged and green; server + db
typechecks clean. Exact counts in the PR checks.
## Risks
- New routes are additive; the existing import path is untouched. The
transfer routes carry the same board authorization as the import routes
they sit beside.
- Disk spool: bounded by the existing upload cap per transfer, cleaned
on success, failure, hash mismatch, and by the 24 h sweep. Spool paths
are strict-UUID-gated.
- The apply step still materializes the assembled zip in memory once
(same profile as today's single-shot import at apply time); upload-time
memory drops to one part.
- Known limitation, deliberate: transfers are keyed on content alone, so
identical package content cannot be imported twice without re-exporting
(surfaced explicitly to the caller). Acceptable for v1; noted for
review.
## Model Used
- Claude Fable 5 (`claude-fable-5`) via Claude Code CLI, with extended
thinking and tool use (multi-agent implementation with independent
verification).
## 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Environments give each agent run an execution target: the local
host, SSH, or a sandbox provider
> - A managed deployment can provision one platform-managed sandbox
environment through the `PAPERCLIP_MANAGED_CONFIG` `environments`
section
> - That row is fully locked today. A tenant cannot add environment
variables for their agents. There is also no way to hide local execution
— run selection falls back to the local row
> - A platform that manages the sandbox for its tenants needs both: the
tenant adds env vars (and nothing else), and local execution is neither
visible nor reachable
> - This pull request opens exactly one tenant edit (env vars) on the
managed sandbox row, and adds an `enableManagedSandboxOnly` mode that
hides local and makes run selection fail closed
> - The benefit is a complete managed-sandbox experience with no change
for self-hosted instances
## Linked Issues or Issue Description
**Subsystem affected**
Environments (managed sandbox provisioning, environment routes, run
environment selection) and the environments UI.
**Problem or motivation**
Platform-provisioned sandbox environments
(`metadata.managedByPaperclip`) reject every write on cloud-managed
instances. Agents often need environment variables inside their sandbox.
The tenant has no way to set them on the managed row. Separately, an
operator cannot remove local execution: the environment list always
shows the local row, and run selection falls back to it when no default
is set.
**Proposed solution**
Allow an envVars-only PATCH on the managed sandbox row, and echo those
env vars back for editing. Add a managed-tier feature
(`enableManagedSandboxOnly`) that hides the local environment from all
read surfaces and redirects local-landing run selection to the managed
sandbox environment, failing closed when it is unavailable.
**Alternatives considered**
UI-only hiding of the local row. This was rejected: it does not stop a
run from resolving to local, so it is presentation without enforcement.
Full unlock of the managed row was also rejected: name, driver, and
config stay platform-owned so boot reconciliation cannot fight tenant
edits.
## What Changed
- `server/src/routes/environments.ts`: the platform-provisioned write
floor admits an envVars-only PATCH on the generalized managed sandbox
row (sandbox driver, `managedByPaperclip`, not legacy kubernetes-marker
rows). Name, driver, config, status, metadata, and DELETE stay rejected.
The read floor stops blanking env vars on that row; credential-shaped
config keys stay redacted for every actor. Legacy kubernetes-marker rows
keep the full floor.
- Same file: under `enableManagedSandboxOnly`, the environments list and
the by-id read omit the local row for every actor, including instance
admins.
- `server/src/services/execution-workspace-policy.ts`:
`resolveExecutionWorkspaceEnvironmentId` gains the managed-sandbox-only
inputs. A selection that lands on the local environment is redirected to
the managed sandbox environment. With no active managed row it throws
`ManagedSandboxUnavailableError` — never local. Non-local selections
(ssh, user-created sandboxes) are untouched.
- `server/src/services/heartbeat.ts`: the run path reads the flag, looks
up the managed row (`findManagedSandboxEnvironment`, new read-only
finder in `environments.ts`), and passes both to the resolver. Mirrors
the forced-kubernetes precedent, which keeps precedence when both
regimes are on.
- `server/src/services/managed-environments.ts`: after a successful
reconcile, the instance default environment moves to the managed sandbox
row when the current default is unset, local, or dangling. A
tenant-chosen custom environment is never overridden.
- `packages/shared`: new `enableManagedSandboxOnly` key (schema default
false, catalog tier `managed`, cloudDefault false, selfHostedDefault
false) and the matching interface field.
- UI: managed rows show a "Managed by Paperclip" lock badge; editing one
opens a dedicated env-vars-only editor that sends the one PATCH shape
the server admits (the old full form failed with a 403 on save). New
`ui/src/lib/managed-sandbox-environment.ts` mirrors the local filter for
cached lists (applied in the project picker; the agent picker already
excluded local). The experimental settings page gains the toggle at its
alphabetical card position. `environmentsApi.update` now declares the
`envVars` field it already sent.
- Tests: environment route floor coverage (envVars-only accepted, mixed
bodies rejected, legacy rows still blanked and locked, local hidden and
404 under the flag, self-hosted unchanged), an embedded-postgres service
test pinning that boot reconciliation never touches tenant env vars,
resolver redirect/fail-closed cases, managed-environments
default-stamping cases, and UI lib/settings tests.
## Verification
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/environment-routes.test.ts
src/__tests__/environment-service.test.ts
src/__tests__/execution-workspace-policy.test.ts
src/services/managed-environments.test.ts` — all pass.
- `pnpm --filter @paperclipai/shared exec vitest run` — 425 pass
(catalog/schema default parity is pinned by an existing test).
- `pnpm --filter @paperclipai/ui exec tsc --noEmit` and the affected UI
suites (CompanyEnvironments, InstanceExperimentalSettings incl.
card-order test, new lib test) — all pass.
- Full workspace `pnpm test`: 3,414 passed. 17 files report failures on
this machine; the identical 17 fail on a clean `origin/master` worktree
in the same environment (git-worktree/skills/embedded-postgres
environment dependencies and plugin-SDK zero-test collections). One
additional file (`issue-monitor-scheduler.test.ts`) failed one
timing-sensitive test in one of two full-suite runs and passes 7/7 in
isolation on this branch — a flake in a domain this diff does not touch.
The branch introduces no new failures.
- Self-hosted zero-delta: every new behavior is gated on the
cloud-managed instance check or the new flag, which defaults to false in
schema and catalog; pinned by the "does not floor platform-marked rows
on self-hosted instances" and flag-off tests.
## Risks
- Behavior is opt-in twice over: the write-floor exception applies only
to rows the managed-config provisioner stamps, and the hiding/forcing
applies only when `enableManagedSandboxOnly` is on (default false
everywhere). Self-hosted instances see no change.
- The env-vars echo is scoped to the generalized managed sandbox row;
legacy kubernetes-marker rows keep the blanket floor because
pre-generalization builds may have written platform values there.
- Fail-closed run selection means a managed instance with the flag on
and an archived managed row (provider plugin down) refuses runs with a
precise error instead of running locally. That is the intended posture.
## Model Used
Claude Fable 5 (`claude-fable-5`), extended thinking, agentic tool use
via Claude Code CLI.
## 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task comments can contain references to files in project and
execution workspaces.
> - Paperclip detected path-shaped inline code and showed it as an
actionable file chip.
> - The UI did not first confirm that the current board session could
open the file.
> - Missing, denied, ambiguous, remote, and unsupported files therefore
looked actionable and failed after a click.
> - This pull request adds an issue-scoped availability check and
promotes only confirmed files to chips.
> - The benefit is that the task thread shows a file action only when
that action can succeed.
## Linked Issues or Issue Description
**What happened?**
Task comments promoted path-shaped inline code to file chips before
Paperclip checked the file. A chip could point to a missing, denied,
ambiguous, remote, or non-previewable file. The action then failed after
the user selected it.
**Expected behavior**
Paperclip must show a file chip only after the server confirms that the
current board session can open the exact file reference. All other
path-shaped text must stay ordinary inline code.
**Steps to reproduce**
1. Add a task comment that contains inline code with a missing or
inaccessible workspace path.
2. Open the task thread as a board user.
3. Observe that the path looks like an actionable file chip.
4. Select the chip and observe that the file cannot open.
**Paperclip version or commit**
`19be4cf927` and earlier.
**Deployment mode**
Local dev and self-hosted server.
**Access context**
Board user.
## What Changed
- Added shared request, response, and validation contracts for batched
workspace-file availability checks.
- Added an issue-scoped server endpoint that resolves file references
with company, issue, workspace, and preview-access checks.
- Added bounded batch concurrency and tests for missing, denied,
ambiguous, remote, unsupported, and available files.
- Added an issue-scoped UI availability registry that deduplicates,
batches, caches, and invalidates file checks.
- Changed task-comment markdown rendering so only confirmed files get
chip styling and file-viewer behavior.
- Bound each chip to the exact workspace target that passed the
availability check.
## Verification
- `pnpm exec vitest run
packages/shared/src/workspace-file-resource.test.ts
server/src/__tests__/file-resources.test.ts
ui/src/components/MarkdownBody.test.tsx
ui/src/components/WorkspaceFileMarkdownBody.availability.test.tsx
ui/src/lib/remark-workspace-file-refs.test.ts
ui/src/lib/workspace-file-availability.test.ts` — 93 passed, 35 skipped.
- `pnpm check:token-gates` — clean.
- `pnpm -r typecheck` — passed.
- `pnpm build` — passed.
- `pnpm test:run` — all server and UI groups passed. One unchanged CLI
test saw the run-injected static AWS credentials and expected only its
local `AWS_PROFILE`. The same test passed, 8 of 8, after removing only
`AWS_ACCESS_KEY_ID` and `AWS_SECRET_ACCESS_KEY` from its process
environment.
## Risks
- File chips now appear after an asynchronous availability check, so
path-shaped text can briefly render as inline code.
- Availability results use the existing 30-second file-resource cache
window. File-resource invalidation forces a new check.
- The endpoint limits each request to 100 references and the client
chunks larger sets.
> 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 service did not expose a more specific
model ID or context-window size. The agent used high-reasoning mode,
repository tools, command 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
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip gives operators a summary for each workspace.
> - An execution workspace detail page used the parent project-workspace
summary slot.
> - Two execution workspaces under one project workspace could therefore
show the same summary.
> - This pull request gives each execution workspace its own summary
scope.
> - It also limits the summary snapshot and generated issue to that
execution workspace.
> - The benefit is that a new or parallel execution workspace cannot
inherit unrelated status.
## Linked Issues or Issue Description
**What happened?**
An execution workspace detail page read and refreshed the summary slot
for its parent project workspace. Parallel execution workspaces could
show the same status and include issues from each other.
**Expected behavior**
Each execution workspace must have one isolated summary slot. Its
generated snapshot must include only issues assigned to that execution
workspace.
**Steps to reproduce**
1. Create two execution workspaces under one project workspace.
2. Add different issues to each execution workspace.
3. Generate the summary in the first execution workspace.
4. Open the second execution workspace.
5. Observe that the old implementation could reuse the first summary.
**Paperclip version or commit**
The problem exists on `master` before this pull request.
**Deployment mode**
The issue affects both local trusted and authenticated deployments.
## What Changed
- Added `execution_workspace` to the shared summary-slot scope contract.
- Validated execution-workspace ownership and stored generated summary
issues on the correct execution workspace.
- Limited execution-workspace snapshots to issues with the matching
execution workspace ID.
- Updated the execution workspace page to use its own summary slot.
- Updated Summarizer instructions, routine options, catalog metadata,
documentation, and regression tests.
## Verification
- `NODE_ENV=test pnpm exec vitest run
packages/shared/src/summary-slot.test.ts
server/src/__tests__/summary-slots.test.ts
ui/src/pages/ExecutionWorkspaceDetail.test.tsx` — 30 focused tests
passed; the embedded-Postgres server tests were run outside the
process-restricted sandbox.
- `pnpm check:token-gates` — passed.
- `pnpm --filter @paperclipai/skills-catalog validate` — passed with 17
catalog skills.
- [Latest-head GitHub
Actions](https://github.com/paperclipai/paperclip/actions/runs/31491475405)
— all 22 jobs passed on `beea14cbaf`, including typecheck, build,
server/workspace tests, serialized suites, e2e, canary, and aggregate
verification. One unrelated adapter cleanup test initially hit an
`ENOTEMPTY` temp-directory race; its single permitted rerun passed.
- Greptile — 5/5 confidence on `beea14cbaf`, 12 files reviewed, zero
comments added, and zero unresolved threads.
## Risks
- Low risk. The new scope is additive.
- Existing project and project-workspace summary slots keep their
current keys and behavior.
- A summary generated for an execution workspace now excludes sibling
workspace issues by design.
> 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 deployment does not expose a more
specific model ID or context-window value. It used agentic reasoning,
repository tools, code execution, and GitHub tooling.
## 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Company export/import moves a whole company — agents, tasks,
comments — between instances as a portable bundle
> - The bundle never carried task timestamps or parent links: the export
writes neither, the importer lets database defaults stamp "now", and
sub-tasks arrive flattened
> - Boards sort by recency, so every imported task showing "created just
now" collapses the task list into import order, and the task hierarchy
the user built is gone
> - This pull request adds created/updated/started/completed/cancelled
timestamps and a parent link to the bundle (schema v7), preserves them
end to end on import, and keeps comment imports from clobbering a
preserved updated time
> - The benefit is that an imported company reads like the company the
user left: same recency order, same task tree
## Linked Issues or Issue Description
**What happened?**
After a company import, every task showed as created at import time.
Recency sorting collapsed to import order, and parent/child task nesting
disappeared. The user called out losing "the meaningful task hierarchy
and recency sorting". Cause: the export bundle has no fields for task
timestamps or parent links, the importer lets `defaultNow()` win on
insert, and the comment importer bumps every touched task's `updatedAt`
to now.
**Expected behavior**
An imported company preserves each task's
creation/update/start/completion times and its position in the task
tree, so sorting and nesting on the destination match the source.
**Steps to reproduce**
1. On a source instance, create tasks over several days, including
sub-tasks nested under parents.
2. Export the company and import it into another instance.
3. Every task shows the import moment as its creation/update time and
all tasks are top-level.
## What Changed
- Export writes
`createdAt`/`updatedAt`/`startedAt`/`completedAt`/`cancelledAt` (ISO,
only when set) and `parent: <taskSlug>` into each task's bundle
extension; a parent outside the export selection drops the edge with an
aggregate warning, mirroring the existing blocker-edge warning
(`server/src/services/company-portability.ts`).
- Bundle schema version 6 → 7. All new fields are optional: v5/v6
bundles import unchanged with a version-aware downlevel warning; bundles
newer than the board still fail closed.
- Manifest parsing validates the new timestamps like comment timestamps
(invalid → warn and ignore, never a hard failure); shared types and the
zod validator carry the new optional fields.
- Import resolves parent slugs to pre-generated destination ids, drops
self-references and cycles from tampered bundles with warnings, and
orders rows parents-first because the self-referencing FK is checked per
insert chunk.
- `importIssues` writes the preserved timestamps (falling back to insert
time when absent; `startedAt` stays null unless bundle-carried, per
#11191's semantics) and `parentId`.
- `addImportedComments` no longer blanket-bumps `updatedAt = now()`; it
takes `GREATEST(updated_at, newest imported comment createdAt)`, so a
preserved update time never regresses while unpreserved rows keep the
old behavior.
## Verification
- `pnpm vitest run server/src/__tests__/company-portability.test.ts
server/src/__tests__/company-portability-import-batching.test.ts
server/src/__tests__/productivity-review-service.test.ts` — 102 passed,
1 pre-existing opt-in benchmark skip. Includes: full round-trip with
exact timestamp equality and a 3-deep parent chain against embedded
Postgres; v6 back-compat (defaults + warning); forward-compat rejection
(v8); cycle/self-reference/invalid-timestamp tampered-bundle handling;
comment-bump preserve-awareness in both directions.
- `pnpm --filter @paperclipai/server typecheck` and `pnpm --filter
@paperclipai/shared typecheck` — clean.
## Risks
- **Rollout ordering**: a board on the previous build (max schema v6)
refuses bundles exported by this build (stamped v7) — the existing
newer-than-supported rejection, working as designed. Cross-instance
moves need the importing board upgraded first. Called out here so
operators aren't surprised during the transition window.
- Parent edges from tampered bundles are dropped with warnings rather
than failing the import; blocker relations already behave this way.
- Timestamps are data-only; no destination schema migration.
Stacked on #11191 (its commit is included here) — merge #11191 first;
this PR then shows only the v7 changes.
## Model Used
- Claude Fable 5 (`claude-fable-5`) via Claude Code CLI, with extended
thinking and tool use (multi-agent implementation with independent
verification).
## 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Company import/export lets an operator move a full company package
between instances, with the Import page uploading the package as one
compressed `.zip`
> - The server caps that upload at 128 MB, and real company packages
with attachments now exceed it — imports fail at the preview step
> - The failure message tells the user to use the CLI folder import, but
that path posts inline JSON capped at 64 MB, so the advice is a dead end
for exactly these packages
> - This pull request raises the zip upload cap to a 1 GB default, makes
it operator-configurable through an environment variable, scales the
decompression-bomb guards from the cap in effect, and replaces the
misleading hint
> - The benefit is that large real-world company packages import
successfully, and operators with unusual needs can tune the cap without
a code change
## Linked Issues or Issue Description
**What happened?**
A company import fails at the preview step with `Preview failed: Import
package exceeds 134217728 bytes`. The package is a valid Paperclip
export. Its compressed size is larger than the 128 MB server cap (one
reported package is 257 MB). The error panel suggests the CLI folder
import, but that path sends the package as one inline JSON body capped
at 64 MB, so it also fails.
**Expected behavior**
A valid company package of realistic size imports successfully through
the Import page. If a package is too large, the error must state the
limit clearly and suggest a step that can work.
**Steps to reproduce**
1. Export a company with enough attachments to make the compressed
package larger than 128 MB.
2. Open the Import page and upload the `.zip`.
3. Click "Preview import".
4. The preview fails with `Import package exceeds 134217728 bytes`.
**Deployment mode**
Reported from a managed deployment; the limit applies to all deployment
modes.
## What Changed
- Raise `PORTABLE_ZIP_UPLOAD_LIMIT_BYTES` from 128 MB to a 1 GB default
(`server/src/http/body-limits.ts`).
- Add the `PAPERCLIP_IMPORT_ZIP_MAX_BYTES` environment override. Invalid
or non-positive values fall back to the default.
- Scale the zip decompression-bomb guard from the configured cap at the
import route: the aggregate inflated ceiling is 4x the cap. The
per-entry ceiling stays at 512 MB because V8's string length limit
applies to an entry regardless (`server/src/routes/companies.ts`,
`packages/shared/src/portability-zip.ts`).
- Report the 422 limit error in MB instead of raw bytes.
- Replace the "use the CLI folder import for very large packages" hint
on preview failure with advice that works: re-export the package without
large attachments (`ui/src/pages/CompanyImport.tsx`).
- Update the stale comment in `ui/src/lib/import-preflight.ts` that made
the same CLI claim.
- Add tests for the new default, the env override, and the
invalid-override fallback.
## Verification
- `pnpm vitest run server/src/__tests__/body-limits.test.ts
packages/shared/src/portability-zip.test.ts
server/src/__tests__/company-portability-routes.test.ts
server/src/__tests__/company-portability.test.ts
server/src/__tests__/company-portability-import-batching.test.ts` — all
pass.
- `pnpm vitest run ui/src/pages/CompanyImport.test.tsx` — passes,
including the updated failure-panel copy assertion.
- `pnpm typecheck` — clean across the workspace.
- Manual: upload a `.zip` larger than the configured cap; the preview
fails with `Import package exceeds the 1024 MB upload limit` and the new
hint. A package between 128 MB and 1 GB now previews and imports.
## Risks
- Peak per-import memory rises with the cap: the upload is buffered in
memory and unzipped in one pass. A 1 GB compressed package can use
several GB transiently. Imports are instance-admin actions, so the
exposure is a deliberate operator action, not anonymous traffic.
Operators on small hosts can lower the cap with
`PAPERCLIP_IMPORT_ZIP_MAX_BYTES`.
- The aggregate bomb guard moves from a fixed 512 MB to 4x the
configured cap. It still bounds expansion far below what a decompression
bomb needs.
- No migration and no API shape change. The 422 message text changes; no
code matches on the old text.
## Model Used
- Claude Fable 5 (`claude-fable-5`) via Claude Code CLI, with extended
thinking and tool use (file edits, local test runs, live-instance
inspection).
## 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip posts system comments when automatic run recovery cannot
continue
> - These comments currently mix the main event with recovery
identifiers and routing details
> - The task chat shell also renders these comments as large raw text
blocks
> - Operators need a short explanation first and inspectable evidence on
demand
> - This pull request emits structured recovery notices and renders them
as compact humanized rows
> - The benefit is a quieter task thread that keeps the full recovery
evidence available
## Linked Issues or Issue Description
Related prior extraction source: #11070. This pull request replaces only
its structured recovery notice slice with a focused branch based on
current master.
**What existing behavior does this improve?**
Paperclip recovery escalations and the experimental task chat
system-comment renderer.
**Current behavior**
Recovery escalation comments put action identifiers, owner details, run
details, and failure codes into the visible markdown body. The task chat
shell renders the complete system comment as a large text block.
**Proposed behavior**
The server emits a short system notice with typed metadata sections. The
task chat shell classifies known recovery families and renders one
compact row. An operator can expand the row to inspect the full body and
metadata.
**Reason and benefit**
The main thread stays readable during repeated recovery activity. Typed
links and evidence remain available without exposing raw failure text in
the default view.
**Breaking changes**
The visible recovery comment body is shorter. Recovery action
deduplication now reads the structured metadata and still recognizes
legacy body markers. No API schema or database migration changes.
## What Changed
- Emit stranded recovery escalations with `system_notice` presentation
and typed recovery, owner, run, and failure-code metadata.
- Share bounded metadata row builders across recovery notice producers
and preserve legacy deduplication compatibility.
- Humanize known recovery notice families and render compact expandable
task-chat rows.
- Route system-authored comments ahead of derived agent authorship so
recovery notices do not appear as agent bubbles.
- Add focused server and UI regression coverage.
## Verification
- `pnpm check:token-gates` — 3/3 clean.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm --filter @paperclipai/shared typecheck` — passed.
- `pnpm --filter @paperclipai/shared exec vitest run
src/validators/issue.test.ts` — 32 tests passed.
- `pnpm --filter @paperclipai/server exec vitest run
src/services/recovery/stranded-notice.test.ts
src/__tests__/issue-recovery-actions.test.ts` — 57 tests passed.
- `pnpm --filter @paperclipai/server exec vitest run
src/services/recovery/successful-run-handoff.test.ts
src/services/recovery/stranded-notice.test.ts` — 39 tests passed.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-process-recovery.test.ts -t 'escalates an
exhausted failed successful-run handoff without using generic
continuation recovery first|escalates an exhausted successful handoff
run that still leaves no disposition'` — 2 tests passed.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-process-recovery.test.ts -t 'blocks assigned
todo work after the one automatic dispatch recovery was already used'` —
passed.
- `pnpm --filter @paperclipai/ui exec vitest run
src/lib/system-notice-humanizer.test.ts
src/components/task-chat/TaskChatSystemNotice.test.tsx
src/components/task-chat/task-chat-adapter.test.ts` — 15 tests passed.
- Storybook visual baselines were not updated because this chat-shell
path has no affected snapshot baseline. Focused rendering tests and
token gates cover this change.
## Risks
- Consumers that parse recovery action identifiers from comment markdown
must move to structured metadata. Server deduplication remains backward
compatible with legacy comments.
- The humanizer uses stable recovery-family phrases. Unknown notices use
a generic truncated first-sentence fallback.
- The UI changes only the experimental task chat presentation. The
stored comment body and expanded metadata remain 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 with GPT-5, reasoning mode, repository tools, shell
execution, and GitHub integration. The runtime did not expose a
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>
## Thinking Path
> - Paperclip is the open source app that manages AI agents for work
> - Sandbox providers let agents run in remote and isolated environments
> - Daytona session commands need a path that sends agent output to the
host without host polling
> - Host polling adds delay and repeats provider output work
> - This pull request adds typed execute.log notifications and a log
sink for incremental output
> - This pull request adds an optional ACP session stream with
final-result replay protection
> - The benefit is lower output delay while the default flags keep
current behavior unchanged
## Linked Issues or Issue Description
**Subsystem affected**
Cross-cutting. The change spans the plugin SDK, Daytona provider,
adapter utilities, and server execution services.
**Problem or motivation**
The Daytona ACP bridge polls a host output file while an agent command
runs. This adds delay and can repeat work. The host also needs a safe
route for provider output chunks.
**Proposed solution**
Add a typed `execute.log` notification with host-issued invocation
correlation. Add an ordered log sink to the environment execute path.
Add an optional ACP session-log path that parses newline-delimited JSON
frames and removes the host output poll for that path.
**Alternatives considered**
Keep the output-file poll as the only path. This keeps the current
behavior but does not provide timely output. The new path stays behind
flags, so the existing path remains the default fallback.
**Roadmap alignment**
This change supports the shipped Cloud / Sandbox agents milestone in
`ROADMAP.md`, including Daytona support.
## What Changed
- Add the typed `execute.log` worker-to-host notification and
company-scoped host route.
- Add ordered `stdout` and `stderr` chunk delivery before the final
execute result.
- Add the Daytona session log sink and the optional ACP streamed session
path.
- Add monotonic frame handling so live and final output reach the host
once.
- Keep `useLogStream` and `streamAgentSessionOutput` off by default.
- Add unit and integration coverage for the notification, execution
target, runtime, and Daytona paths.
## Verification
- Run adapter-utils tests: 445 tests pass locally.
- Run server environment tests: 73 tests pass locally.
- Run Daytona plugin tests: 131 tests pass locally.
- Run TypeScript checks for shared, adapter-utils, and server.
- Review the pull request checks after GitHub completes them.
- All required GitHub checks pass on the current head.
## Risks
The new paths change output delivery only when a feature flag enables
them. The final execute result remains available for parsing and
fallback. The main risk is a provider stream or frame-order error; the
final-result parser limits that risk.
## Model Used
OpenAI Codex, GPT-5, tool use and code execution. The runtime did not
supply a context-window value.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
No operator documentation change applies because both new flags remain
disabled by default.
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Apps subsystem connects company tools through governed provider
connections.
> - A company can need more than one account for the same provider.
> - The current database constraint and Apps flow assume one named
connection per company.
> - New quarantined actions also need an explicit review decision before
activation.
> - This pull request supports multiple provider connections and
complete action review decisions.
> - The benefit is safer access control and a clear multi-account Apps
workflow.
## Linked Issues or Issue Description
Refs: #11040
**Subsystem affected**
Cross-cutting. This change affects the Apps UI, the tool access API, the
shared request contract, and the database schema.
**Problem or motivation**
The connection name constraint prevents a company from keeping more than
one connection for a provider. The Apps UI also reuses an existing OAuth
connection when a user asks to connect another account. Action review
can enable selected entries without recording a decision for every
quarantined action.
**Proposed solution**
Remove the company and connection name uniqueness constraint. Let users
open, count, edit, and create multiple provider connections. Require the
finish request to cover every quarantined action exactly once before the
server activates reviewed entries.
**Alternatives considered**
The UI could generate unique internal names and keep the database
constraint. This would preserve a one-connection assumption in the data
model and would make display names part of identity. The server could
also infer review decisions from enabled actions. This would not
distinguish a reviewed disabled action from an action that the user did
not review.
**Roadmap alignment**
This change extends the completed MCP Tool Gateway and Apps milestone.
It also supports the Connected Apps roadmap item. It follows the
navigation and connection management work in #11040.
## What Changed
- Remove the company-scoped connection name uniqueness index with an
ordered and idempotent migration.
- Add a reviewed action list to the finish-app contract and reject
incomplete or duplicate review decisions.
- Activate reviewed entries and keep unreviewed quarantined entries
blocked.
- Enable a completed connection and preserve the company and connection
scope in all updates.
- Show provider connection counts and open the provider setup page from
Browse.
- Let users edit existing connections or connect another account without
reusing an active OAuth connection.
- Update focused server and UI coverage for multiple connections and
action review.
## Verification
- Ran the focused Apps UI suite. All 116 tests passed in 11 files.
- Ran the focused server and CLI suite. All 276 tests passed in 3 files.
- Ran `pnpm --filter @paperclipai/db check:migrations`. The migration
safety check passed.
- Ran `pnpm -r typecheck`. All projects passed.
- Ran `pnpm build`. All projects built successfully.
- Ran `pnpm test:run`. It passed 3,735 tests and skipped 4 tests. One
worktree-safety assertion failed because the execution workspace reloads
its worktree marker. The same test passed with an isolated non-worktree
marker.
- Ran `pnpm check:token-gates`. It reports 12 existing violations in the
unchanged `PaperclipOrbit3D.tsx` file from the target branch.
- Started the six affected Playwright specifications. Chromium could not
start because the host does not provide `libatk-1.0.so.0`. The GitHub
e2e jobs will verify these specifications.
- GitHub Actions passed every final-head CI gate, including all three
e2e shards and the aggregate `e2e` and `verify` jobs.
- Greptile reviewed final commit `9af9200426` at 5/5 with zero review
threads.
## Risks
- Removing the name uniqueness index permits duplicate display names.
Stable connection IDs and UIDs remain unique within a company.
- The finish-app endpoint accepts the new review field as optional for
backward compatibility. When clients send it, the server requires a
complete decision for all quarantined actions.
- Multiple OAuth connections depend on the explicit new-connection route
flag. Focused tests cover active and draft connection reuse.
- The migration is ordered after migration 0210. Its `DROP INDEX IF
EXISTS` statement is safe to repeat.
> 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.6-sol` model assisted this change. The
agent used repository tools, code execution, test execution, and agentic
reasoning. The Codex runtime manages the context window.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The CLI and server both update Paperclip values in `.env` files
> - The server preserved operator content, but the CLI rebuilt the
complete file
> - A CLI rerun could remove comments, custom values, ordering, and
newline style
> - Both paths need one editor with one value encoding and duplicate key
policy
> - The final integration also needs one regression test across the
related setup and sync safety mechanisms
> - This pull request moves the editor to the shared package and adds
cross-cutting rerun-survival coverage
> - The benefit is safe setup and worktree repair reruns that preserve
operator edits
## Linked Issues or Issue Description
**What happened?**
The CLI rebuilt the complete `.env` file when it wrote a managed
Paperclip value. This action removed comments, blank lines, custom keys,
original quoting, and the original newline style.
**Expected behavior**
Paperclip must update only the managed assignments. It must preserve all
unrelated bytes. It must skip the file replacement when all managed
values are current.
**Steps to reproduce**
1. Add comments, custom keys, quoted values, and CRLF newlines to the
Paperclip `.env` file.
2. Run a CLI path that calls the agent JWT secret setup.
3. Observe that the old writer replaces the complete file.
**Paperclip version or commit**
The problem exists on `master` before this pull request.
Related public context: Refs #437.
## What Changed
- Add one shared line-preserving `.env` editor for the CLI and server.
- Define minimal and JSON value encodings in the shared helper.
- Update every stale duplicate of a managed key and preserve current
duplicate encodings.
- Preserve comments, ordering, blank lines, unknown keys, export
prefixes, trailing comments, and newline style.
- Write changed files through a same-directory temporary file and atomic
rename.
- Limit CLI updates to non-empty `PAPERCLIP_*` entries.
- Skip the write when all managed values are current.
- Add shared, CLI, and server regression coverage.
- Refresh the branch after the related config, sandbox, and skill safety
changes landed.
- Add a cross-cutting integration test for config, env-file,
managed-sandbox, and managed-instructions rerun survival.
## Verification
- `pnpm exec vitest run packages/shared/src/env-file.test.ts
packages/shared/src/config-schema.test.ts
cli/src/__tests__/agent-jwt-env.test.ts
cli/src/__tests__/config-store.test.ts
server/src/__tests__/config-file.test.ts
server/src/__tests__/worktree-config.test.ts` passes 39 tests.
- `pnpm exec vitest run
server/src/__tests__/rerun-survival.integration.test.ts` passes 4 tests.
- `pnpm -r typecheck` passes on the previous head. GitHub CI reruns it
on the refreshed head.
- The previous head passed the complete general, serialized, workspace,
and E2E matrix. GitHub CI reruns that matrix on the refreshed head.
- `pnpm build` passes on the previous head. GitHub CI reruns it on the
refreshed head.
## Risks
- Low risk. The production change only affects managed `.env`
assignments.
- Existing managed assignments can keep their original quoting when
their decoded values are current.
- Changed CLI values keep the prior minimal encoding policy. Changed
server values keep the prior JSON encoding policy.
- Duplicate managed assignments now follow one explicit rule: Paperclip
updates each stale occurrence.
- The master refresh had one import-block conflict. The resolution keeps
both the config merge imports and the env-file imports.
- The added integration file is test-only. It has no database, API, or
UI contract effect.
> 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 from the GPT-5 family produced this change with reasoning,
tool use, and code execution. The runtime did not expose the exact model
ID or context window size.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents can select company skills and synchronize them to adapter
runtimes
> - The skill sync API replaced the complete selection without an
explicit destructive choice
> - Company package import also replaced conflicting skills by default
> - These defaults could remove operator edits during setup and import
reruns
> - This pull request adds explicit assignment merge modes and safe
package conflict handling
> - The benefit is that reruns preserve operator work unless the caller
explicitly requests replacement
## Linked Issues or Issue Description
**What existing behavior does this improve?**
This change improves agent skill synchronization and company package
import.
**Subsystem affected**
This is a cross-cutting change across the shared contracts, server, CLI,
and UI.
**Current behavior**
Agent skill synchronization replaces the full desired skill set from a
modeless request. Package import replaces a conflicting skill when the
caller does not select a conflict mode.
**Proposed behavior**
Agent skill synchronization requires `add`, `remove`, or `replace`.
Package import skips conflicts by default. Each imported skill reports
whether it was created, renamed, replaced, or skipped.
**Reason and benefit**
Setup and import reruns must preserve operator edits by default.
Explicit destructive modes make data loss less likely and make each
outcome inspectable.
**Breaking changes**
Callers of the agent skill sync API must now send `mode`. Callers that
need the former behavior must send `replace`. Package import now uses
`skip` when `onConflict` is absent.
## What Changed
- Added required `add`, `remove`, and `replace` modes to the shared
agent skill sync contract.
- Added actionable `422` validation for missing or invalid modes.
- Updated first-party UI and CLI callers with explicit modes.
- Changed package skill conflict handling to use `skip` by default.
- Kept plugin-owned and built-in stock skill imports on explicit
`replace`.
- Added created, renamed, replaced, and skipped results to company
imports.
- Added regression coverage for merge modes and package conflict
outcomes.
## Verification
- `pnpm check:token-gates`
- `pnpm -r typecheck`
- `pnpm build`
- `pnpm test:run:serialized` (128 suites passed)
- `pnpm --filter @paperclipai/skills-catalog test` (20 tests passed)
- Focused agent skill route, company skill service, portability, CLI,
and UI tests passed.
- GitHub CI passed build, typecheck, canary, all general and serialized
test shards, all browser shards, policy, security, and final
verification on commit `2cfbb3e4c5`.
- Greptile reviewed the latest commit at 5/5 with zero unresolved
threads.
## Risks
- This change intentionally rejects modeless agent skill sync requests.
- The safe package default can leave an existing skill unchanged where
the old default overwrote it.
- All first-party callers now select a mode. Regression tests cover each
outcome.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI `gpt-5.6-sol` through Codex. The runtime used agentic
reasoning, tool use, code execution, and repository editing. The runtime
did not expose the context window size.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The CLI and server share a JSON configuration contract for local
installations and worktrees.
> - Existing config writes removed extension keys because Zod stripped
unknown object properties.
> - Invalid config files could also be replaced with defaults before an
operator preserved the original bytes.
> - Configuration updates must preserve operator edits and must not
rewrite files when the effective value is unchanged.
> - This pull request adds extension-preserving merges, guarded
invalid-config repair, atomic writes, and focused regression tests.
> - The benefit is safe setup and configuration reruns without data loss
or unnecessary mtime changes.
## Linked Issues or Issue Description
**What happened?**
Known-field updates through the CLI or server removed unknown top-level
and nested config keys. Non-interactive configure and onboard paths
could replace a present but invalid config with defaults.
**Expected behavior**
Writers preserve extension keys, skip semantic no-op writes, and require
explicit interactive confirmation before an invalid config is replaced.
Repair preserves an exact collision-safe backup first.
**Steps to reproduce**
1. Add an unknown top-level key and an unknown nested provider key to
`config.json`.
2. Update a known field through the CLI or worktree config writer.
3. Observe that the extension keys are removed on the base branch.
4. Write invalid JSON and run configure or onboard without an
interactive terminal.
5. Observe that the original file can be replaced without a durable
invalid-file backup on the base branch.
**Paperclip version or commit**
`master` at the pull request base commit.
## What Changed
- Accept unknown properties at each extensible config object boundary
while keeping every known field validated.
- Merge known-field updates into the parsed source config and preserve
only unknown extension data.
- Warn about near-match key names without deleting or changing them.
- Skip writes when the effective config is unchanged, which keeps file
mtimes stable.
- Write config changes through a temporary file, file sync, rename, and
directory sync.
- Distinguish a missing config from an invalid config in configure and
onboard.
- Back up invalid bytes as `config.json.invalid-N` and verify the source
still matches that backup before repair.
- Require interactive repair confirmation and reject non-interactive
replacement with an actionable message.
- Document the config preservation and repair behavior.
## Verification
- `pnpm exec vitest run packages/shared/src/config-schema.test.ts
cli/src/__tests__/config-store.test.ts
cli/src/__tests__/configure-repair.test.ts
cli/src/__tests__/configure.test.ts cli/src/__tests__/onboard.test.ts
server/src/__tests__/config-file.test.ts
server/src/__tests__/worktree-config.test.ts`
- `pnpm -r typecheck`
- `AWS_ACCESS_KEY_ID= AWS_SECRET_ACCESS_KEY= VITEST_MAX_WORKERS=1 pnpm
test:run`
- `pnpm build`
- Confirm all pull request checks are green on the latest commit.
- Confirm Greptile reports 5/5 with no unresolved comments.
## Risks
- Passthrough keeps misspelled keys. Near-match warnings make this
visible without destructive cleanup.
- Merge behavior must distinguish unknown extension keys from optional
known keys. Schema-aware regression tests cover preservation and
known-key deletion.
- Repair must not overwrite bytes that changed after backup. The writer
compares the current source with the selected backup before atomic
replacement.
- The change does not alter database schema, company scoping, or
activity logging.
> 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 model family. The exact deployment model ID and
context window are not exposed. Agentic reasoning, tool use, and code
execution 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip uses adapter and sandbox code to start agents and run
sandbox work
> - The current sandbox spans use mixed names and do not group related
run-time work
> - Mixed names make traces harder to read and compare across providers
> - This pull request renames provider spans, adds run-time wrapper
spans, and keeps the host allowlist closed
> - The benefit is clearer traces with the same sandbox behavior and
trust boundary
## Linked Issues or Issue Description
**What existing behavior does this improve?**
This improves OpenTelemetry span names and grouping for sandbox startup,
execution, callback relay, and agent session work.
**Subsystem affected**
Cross-cutting (multiple of the above): adapter utilities, sandbox
providers, shared telemetry documentation, and server instrumentation.
**Current behavior**
Sandbox provider spans use mixed names. Related run-time operations
expose inner `sandbox.exec` spans without a named wrapper span. The host
mapper uses a closed allowlist for provider span names.
**Proposed behavior**
Use descriptive provider-scoped span names. Add wrapper spans for agent
session input, agent session output polling, and callback relay. Keep
the host mapper allowlist closed and map unknown names to `other`.
**Reason and benefit**
Clear names make traces easier to read and reduce ambiguity during
sandbox operation analysis. Wrapper spans show the full operation while
preserving the inner execution spans.
**Breaking changes**
None. This change updates telemetry span names and grouping only. It
does not change sandbox behavior, endpoint behavior, or the host trust
boundary.
**Additional context**
Related prior work:
[#10758](https://github.com/paperclipai/paperclip/pull/10758).
## What Changed
- Rename Daytona provider sync and session spans with descriptive
provider-scoped names.
- Add three run-time wrapper spans for agent session input, output
polling, and callback relay.
- Add a shared span runner that preserves no-op behavior without a real
tracer.
- Keep the host mapper allowlist closed and map unknown names to
`other`.
- Update telemetry documentation and span-name tests.
## Verification
- Focused adapter-utils span tests pass for startup timing, callback
relay, and sandbox execution.
- Focused Daytona plugin span tests pass for renamed leaf spans and
session open or close spans.
- Focused server tests pass for host mapping and instrumentation.
- The stacked diff contains one commit on top of
`feat/daytona-persistent-session-model`.
## Risks
- Span names change for existing telemetry consumers.
- The wrapper spans add trace structure but do not change sandbox
execution.
- The host mapper keeps the existing closed allowlist and `other`
bucket.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI GPT-5 (Codex agent); exact deployment revision and context window
are not exposed in this run; tool use and code execution 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Apps give agents governed access to external tools.
> - The Apps gallery lists Notion, but the server required manually
configured OAuth credentials.
> - Notion's hosted MCP server supports OAuth discovery and dynamic
client registration.
> - Notion also requires HTTPS or a loopback HTTP redirect URI.
> - This pull request adds a direct Notion MCP OAuth path with PKCE and
reusable dynamic clients.
> - It also adds the current Apps UI states for connect and
reauthorization.
> - The benefit is a secure Notion connection with no manual client
credential setup.
## Linked Issues or Issue Description
**What existing behavior does this improve?**
The Apps gallery, Apps connect route, OAuth token lifecycle, and managed
MCP gateway.
**Subsystem affected**
`server/`, `packages/shared/`, `scripts/`, and `ui/`.
**Current behavior**
The Notion gallery cards are disabled. The server uses the classic
Notion OAuth endpoints and requires operator-supplied client
credentials. It does not register an OAuth client from provider
metadata. Concurrent refreshes can also replay a rotating refresh token.
**Proposed behavior**
Enable the Notion Apps flow. Discover OAuth metadata from
`https://mcp.notion.com/mcp`. Register and reuse a public RFC 7591
client with PKCE. Require HTTPS or loopback HTTP callbacks. Serialize
refreshes, store each rotated refresh token before the new access token
can be used, and show a reconnect state for `invalid_grant`.
**Reason and benefit**
Operators can connect the built-in Notion MCP app without creating or
copying OAuth credentials. Paperclip keeps dynamic clients and rotating
tokens in the company secret store.
**Breaking changes**
None. Explicit environment client credentials still take priority.
Existing Slack and Linear OAuth endpoint hints remain unchanged. Other
OAuth apps remain disabled unless they are allowlisted.
**Additional context**
PR #10910 is a related, broader Connections v3 wizard replacement. This
PR is the focused current Apps flow. The MCP Tool Gateway and Connected
Apps items in `ROADMAP.md` cover this planned capability.
## What Changed
- Classify all 20 reviewed Notion MCP tools with provider-scoped read
and write defaults.
- Require approval for selected Notion mutations, including move,
duplicate, and convert actions that generic verb matching missed.
- Preserve company-scoped connection and catalog resolution for Notion
profiles and policies.
- Add RFC 7591 dynamic client registration with
`token_endpoint_auth_method=none` and mandatory PKCE.
- Store the dynamic client ID on the connection and store any returned
client secret in the company secret store.
- Reuse the registered client for later connects and keep explicit
environment credentials as the first choice.
- Discover protected-resource and authorization-server metadata from the
Notion MCP endpoint.
- Add `redirectConstraints: "https-or-loopback-http"` to the generated
Notion app definition and shared contract.
- Reject non-loopback plain HTTP callbacks before network access with a
TLS setup error.
- Serialize client registration and token refresh operations within the
server process.
- Store a rotated refresh token before publishing the refreshed access
token.
- Treat `invalid_grant` as terminal and move the connection to a clear
reauthorization state.
- Add focused coverage for registration reuse, callback constraints,
refresh rotation, and terminal grants.
- Enable the Notion Apps route and add connect, redirect, success,
error, and reconnect UI states.
- Keep non-allowlisted OAuth apps blocked and cover the UI policy with
regression tests.
## Verification
- The focused Notion policy integration test passed with embedded
PostgreSQL.
- The focused 20-tool classification test passed.
- The server typecheck passed on the governance head.
- `pnpm -r typecheck` passed on the rebased head.
- `pnpm --filter @paperclipai/server typecheck` passed after the
security follow-up.
- `pnpm exec vitest run server/src/__tests__/tool-access-service.test.ts
-t \u0027DCR|refresh tokens|invalid_grant|abandoned lease\u0027` passed
10 focused security tests.
- `pnpm build` passed on the rebased head.
- `pnpm exec vitest run server/src/__tests__/tool-access-service.test.ts
-t 'OAuth|oauth'` passed 14 tests.
- `pnpm exec vitest run packages/shared/src/app-definitions.test.ts`
passed 5 tests.
- The complete server group passed 3,686 tests with 4 skipped.
- The complete UI group passed 3,656 tests.
- The full local runner found one environment-only CLI failure because
this agent runtime injects static AWS credentials into a test that
expects `AWS_PROFILE` only. `env -u AWS_ACCESS_KEY_ID -u
AWS_SECRET_ACCESS_KEY pnpm exec vitest run
cli/src/__tests__/secrets.test.ts` passed all 8 tests.
- The prior UI verification passed 55 focused tests, `pnpm
check:token-gates`, the Storybook build, and review of six 1440 x 1000
screenshots.
- OAuth request sequence: protected-resource metadata `GET
https://mcp.notion.com/.well-known/oauth-protected-resource/mcp`;
authorization metadata `GET
https://mcp.notion.com/.well-known/oauth-authorization-server`; dynamic
registration `POST https://mcp.notion.com/register`; authorization `GET
https://mcp.notion.com/authorize`; token exchange and refresh `POST
https://mcp.notion.com/token`; MCP traffic `POST
https://mcp.notion.com/mcp`.
- The live metadata and registration probe confirmed that Notion accepts
HTTPS and loopback HTTP redirects. It rejects a plain HTTP private
hostname.
- A later QA task owns the full browser consent and managed gateway
tool-list dry run against a configured HTTPS deployment.
## Risks
- Notion can add tools. Unrecognized names use the generic classifier,
and new or changed risky tools stay quarantined after connection
activation.
- A deployment that uses a private non-loopback hostname must configure
HTTPS before it can connect Notion.
- Dynamic registration creates a provider-side client. Paperclip reuses
it because registration does not provide a standard delete operation.
- Refresh coordination uses a database CAS lease across service
instances. An unclean crash leaves an uncertain lease and requires
reconnect instead of risking refresh-token replay.
- The current Apps surface overlaps with PR #10910. Merge order can
require a small conflict resolution if that PR lands first.
> 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 on a GPT-5 runtime. The exact deployment ID and context
window are not exposed. The runtime used reasoning, repository tools,
code execution, and network 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>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Issue reviews use thread confirmations to record explicit verdicts
> - The server allowed users to resolve review confirmations but
rejected all agent actors
> - This one-way rule prevented an eligible agent reviewer from
completing a review
> - The existing review policy already defines which actor can submit a
verdict
> - This pull request applies that policy to agent confirmation verdicts
on writable issues
> - The benefit is a consistent review gate for users and agents with
preserved audit attribution
## Linked Issues or Issue Description
- Builds on: #10931 (merged into master before this PR)
- Refs #8617
## What Changed
- Allow eligible agents to accept or reject pending review confirmations
on issues they can write.
- Allow a creator agent to withdraw its own pending review confirmation
when the review policy permits it.
- Reuse the review verdict policy check for users and agents.
- Require an explicit, same-run review-confirmation binding so unrelated
board-only confirmations stay protected.
- Preserve board-only tool action confirmations and existing user
attribution.
- Add route and service tests for agent accept, reject, withdrawal,
human-only denial, and user attribution.
## Verification
- `pnpm exec vitest run packages/shared/src/validators/issue.test.ts
server/src/__tests__/issue-execution-policy-routes.test.ts
server/src/__tests__/issue-review-policy.test.ts
server/src/__tests__/issue-thread-interaction-routes.test.ts
server/src/__tests__/issue-thread-interactions-service.test.ts
server/src/__tests__/issue-stalled-review-decision-routes.test.ts` (256
passed after rebasing onto master and the atomic binding fix)
- `pnpm --filter @paperclipai/shared typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm -r typecheck`
- `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm test:run`
- `pnpm build`
## Risks
- The change expands who can resolve pending review confirmations. The
existing issue write checks and review policy limit this access.
- Tool action confirmations remain board-only.
- The pull request depends on the review policy helper from #10931,
which is now merged into master.
> 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`.
The roadmap marks Agent Reviews and Approvals as shipped. This pull
request fixes a narrow server behavior gap in that shipped capability.
## Model Used
- OpenAI Codex, model `gpt-5.6-sol`, with reasoning, tool use, and code
execution. The runtime does not expose the context window size.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Issues use `in_review` to request a final decision from an
authorized writer
> - The server rejected an assignee agent that tried to close its own
review, even when the issue had no independent-review rule
> - This rejection stopped the default agent workflow and did not
represent the configured execution-stage rules
> - Paperclip needs an open default and explicit issue-level constraints
for teams that require an independent or human verdict
> - This pull request removes the unconditional rejection and adds
`anyone`, `not_creator`, and `human_only` review policies
> - The benefit is a working default path with opt-in, authenticated
verdict controls
## Linked Issues or Issue Description
Refs #10635, #4429, and #10671.
The related public work covers execution-stage independence,
self-approval fallback behavior, and durable review paths. This change
is distinct. It controls who can resolve an issue review verdict. It
keeps configured execution stages active.
## What Changed
- Added a nullable `review_policy` issue column. Null has the same
meaning as `anyone`. The migration does not backfill existing issues.
- Added shared create, update, response, and compact issue contracts for
`anyone`, `not_creator`, and `human_only`.
- Removed the unconditional agent self-approval rejection for
`in_review` issues.
- Added one reusable verdict-actor check for terminal status changes and
pending interaction accept or reject actions.
- Used the authenticated principal type for `human_only`. Agent keys and
run tokens remain agent principals.
- Used the latest transition into `in_review` to identify the requester
for `not_creator`.
- Added actionable 403 responses that name the policy, the allowed
actor, and the next step.
- Kept the configured execution-stage transition and signoff behavior.
- Added focused contract, helper, status-route, interaction-route, and
execution-stage regression tests.
- Updated the implementation specification for the new issue field.
## Verification
- `pnpm exec vitest run packages/shared/src/validators/issue.test.ts
server/src/__tests__/issue-review-policy.test.ts
server/src/__tests__/issue-stalled-review-decision-routes.test.ts
--reporter=dot` passed: 42 tests.
- `pnpm --filter @paperclipai/shared typecheck` passed.
- `pnpm --filter @paperclipai/db typecheck` passed, including migration
numbering and safety checks.
- `pnpm --filter @paperclipai/server typecheck` passed.
- `pnpm run typecheck:build-gaps` passed across server, CLI, plugin
SDK/examples, plugin wiki, and UI.
- `git diff --check origin/master...HEAD` passed.
- SecurityEngineer review approved the authenticated-principal checks
and accepted policy-relaxation tradeoff with no required changes.
- Greptile reviewed the latest head at 5/5 with zero inline comments or
follow-ups.
- The latest-head GitHub rollup passed build, typecheck,
server/workspace tests, serialized suites, canary, e2e, and external
security checks.
## Risks
- The migration adds one nullable text column. It has no default and no
backfill.
- `not_creator` reads the latest recorded transition into `in_review`.
It denies the verdict when it cannot identify the requester.
- Agents can change or relax `reviewPolicy` when they have issue write
access. This is intentional for this issue-level control.
- Null and `anyone` do not add a database query to the verdict path.
- Configured execution-stage checks still run after the issue-level
policy check.
> This work aligns with the completed "Agent Reviews and Approvals" and
"Enforced Outcomes" roadmap items. It does not add a new roadmap
capability.
## Model Used
- OpenAI Codex, GPT-5. The exact deployment ID and context-window size
are not exposed to the agent. The run used reasoning, repository tools,
code execution, 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 (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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents ask humans for decisions through interaction blocks: plan
confirmations, structured questions, suggested tasks, and checkbox
prompts
> - Each agent writes these decision prompts in its own style, so
operators can get long or unclear text at the exact moment they must
decide
> - There is no instance-level control that makes agents use a
controlled language for these decision points only
> - This pull request adds an experimental setting that tells agents to
write all user-interaction content in ASD-STE100 Simplified Technical
English, with the context the user needs and the effect of each choice
> - The benefit is faster, clearer human decisions, with agent thinking
and normal responses unchanged
## Linked Issues or Issue Description
Refs #10410 (optional `/simplified-english` skill in the skills catalog;
this PR adds the instance-level toggle for interactions).
**Problem or motivation**
Agent-posted user interactions (plan confirmations, structured
questions, suggested-task proposals, checkbox prompts) are written in
each agent's default style. Operators who want fast, unambiguous
decisions have no way to ask agents to use a controlled language for
exactly those decision points.
**Proposed solution**
Add an experimental instance setting,
`enableSimplifiedEnglishInteractions` ("Simplified English
Interactions"). When it is on, the server sets
`simplifiedEnglishInteractions: true` in the heartbeat wake payload. The
shared wake-prompt renderer, used by every adapter, then emits a
directive: write all user-interaction content in ASD-STE100 Simplified
Technical English, state what information the user needs to decide, and
state what happens for each choice. The directive applies to interaction
content only. Thinking, comments, documents, and other responses keep
their usual style.
**Alternatives considered**
Per-agent instructions work today, but someone must maintain them on
every agent. An instance-level toggle applies uniformly and turns off in
one place. Server-side rewriting of interaction payloads was rejected:
post-hoc translation is lossy and cannot add the decision context that
only the agent has.
**Roadmap alignment**
Extends the experimental settings surface with another opt-in
agent-behavior refinement, consistent with existing prompt-side flags.
## What Changed
- Added `enableSimplifiedEnglishInteractions` to the experimental
instance-settings zod schema, mirror type, and feature catalog (default
off, tier preference) in `packages/shared`.
- Server `instance-settings.ts` normalizes the flag on both read
branches; `heartbeat.ts` reads it once and passes
`simplifiedEnglishInteractions` into `buildPaperclipWakePayload`.
- Shared adapter renderer
(`packages/adapter-utils/src/server-utils.ts`): added the field to
`PaperclipWakePayload`, normalization, and an `- interaction language
(experimental): ...` directive emitted in both fresh and resume prompt
lanes, so one injection point covers all adapters.
- UI: new experimental settings card "Simplified English Interactions"
in `ui/src/pages/InstanceExperimentalSettings.tsx`, in alphabetical card
order; fixtures updated.
- Tests: renderer coverage for flag on/off in both lanes, plus
schema/catalog/UI fixture updates.
## Verification
- From the repo root: `node_modules/.bin/vitest run
packages/adapter-utils` (88/88), `packages/shared` validators (25/25),
server instance-settings + heartbeat suites (47/47 and 70/70 consumer
tests), `ui` settings tests (32/32).
- Typecheck is clean in all four touched packages.
- Manual check: turn the flag on in Settings → Experimental, wake an
agent, and confirm the wake prompt contains the interaction-language
directive; turn it off and confirm the directive is absent.
## Risks
- Low risk: the flag defaults to off, and the only behavior change is
one extra directive line in the wake prompt when an operator turns it
on.
- The directive is advisory to the agent; models can still deviate from
STE. No data or API shape changes; no migration.
## Model Used
- Claude Fable 5 (Anthropic, model ID `claude-fable-5`), extended
thinking, agentic tool use via Claude Agent SDK.
## 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>
## Thinking Path
> - Paperclip is the control plane people use to manage AI-agent
companies.
> - Agents can encounter credentials during work.
> - Directly creating live secrets or bindings would bypass human
governance.
> - Proposal records must remain inert and separate from live secret
resolution until an authorized human approves them.
> - Approval must reuse the existing secret-create and protected
agent-config write paths.
> - This pull request adds the propose, review, approve, and reject
lifecycle.
> - The benefit is that agents can safely hand credentials into
Paperclip without exposing plaintext or gaining authority to activate
them.
## Linked Issues or Issue Description
Follow-on to #9921, which established run-bound agent secret access.
**Problem / motivation:**
Agents can receive credentials during work. There is no governed way for
them to propose a credential or binding without exposing plaintext in
work artifacts or immediately creating live access.
**Proposed solution:**
Store agent-authored proposals outside live secret tables. Encrypt each
proposed value and register exact-value redaction when Paperclip
receives it. Require an authorized human to approve or reject each
proposal. Approval executes through the normal write paths as the human
approver. Binding proposals can target only the proposer or its downward
reporting chain under the restrictive V1 policy.
**Alternatives considered:**
We rejected live secrets with a `proposed` status. That design would put
untrusted rows in resolver, list, and sync paths. It would also allow
uniqueness squatting. We rejected direct agent binding writes because a
binding is an agent-config write and must keep the existing human
permission gate.
**Roadmap alignment:**
This change extends the run-bound agent secret-access foundation in
#9921 with a governed proposal workflow.
## Security Verdict
Q0 SecEng verdict: **PASS-with-required-changes**. The review accepted
the separate proposal-table design and required the implementation to:
- fail closed unless both encryption and exact-value run redaction
registration succeed;
- scrub ciphertext idempotently on reject, withdraw, and expiry, with
audit-visible state;
- treat agent justification as hostile input and foreground action,
target, provenance, and approver permissions;
- snapshot and re-check the target agent plus reports-to chain at
approval to prevent org-chart laundering;
- make cascade approval atomic and fail closed if either secret creation
or binding authorization fails;
- deny low-trust, `skill_test`, `task_bridge`, and non-run-bound sources
consistently; and
- execute approval through the normal human secret/config write paths,
including protected-change gates.
Those requirements are implemented and covered by focused service,
route, and UI tests. Residual V1 risk remains the accepted 14-day
encrypted retention window. Proposal-time redaction also cannot clean a
value that leaked before the propose call.
## What Changed
- Added `company_secret_proposals`, migration `0207`, shared proposal
contracts, and a state-machine service for create, approve, reject,
withdraw, cascade, expiry, and ciphertext scrubbing.
- Added run-bound agent proposal routes and board review routes. The
routes derive provenance from authentication and enforce source
restrictions, company isolation, chain-of-command checks,
approval-as-approver, wake-on-resolution, and dual audit trails.
- Added durable per-run exact-value redaction registration so proposal
values remain redacted on later read surfaces.
- Added the Secrets **Proposals** tab and agent configuration **Proposed
access** rows. The UI shows fingerprint and length only. It also frames
agent justification as untrusted input, runs permission preflight,
supports approve and reject actions, and confirms cascades.
- Updated OpenAPI, agent skill guidance, API reference documentation,
and focused server and UI regression coverage.
- Rebased the branch onto current `master` and renumbered the proposal
migration after `0206`.
## QA Acceptance Results
Q5 QA verdict: **PASS — 9/9 acceptance criteria met**, with one Minor
non-blocking follow-up.
- **AC1:** proposed values never echo, never appear in live
lists/resolvers, and expose only fingerprint + length to board
reviewers.
- **AC2:** restrictive `self_and_reports` matrix passes: self/downward
allowed; upward/lateral denied.
- **AC3:** secret approval uses the normal create path, honors rename
overrides, records proposer/approver provenance, and scrubs ciphertext.
- **AC4:** approved bindings materialize and resolve through the target
agent's runtime list/fetch routes.
- **AC5:** pending-secret bindings require cascade; cascade succeeds
atomically and permission failures leave nothing applied.
- **AC6:** reject, withdraw, dependent rejection, and expiry paths scrub
ciphertext and preserve reasons/audit state.
- **AC7:** token/source and approver denial matrix passes through live
checks plus focused route tests.
- **AC8:** proposal lifecycle events and reused
`secret.created`/config-write events form the required dual audit trail;
origin-issue notification and wake are queued.
- **AC9:** both review surfaces render and execute correctly; UI
approval materializes the binding.
QA also confirmed zero plaintext occurrences for all exercised proposal
values in server logs. The single finding is that the company-level
`bindingTargetPolicy` toggle is not wired yet. V1 is hardcoded to the
restrictive `self_and_reports` policy. The matrix is correct and the
follow-up is tracked separately, so QA classified it as non-blocking.
## Verification
- Focused server proposal and redaction suite: 83 tests pass.
- Focused proposal review UI suite: 54 tests pass.
- Embedded-Postgres migration reapply test: 1 test passes with the
documented 30-second timeout.
- `pnpm --filter @paperclipai/db typecheck` passes, including migration
numbering and safety checks.
- `pnpm --filter @paperclipai/shared typecheck` passes.
- `pnpm --filter @paperclipai/ui typecheck` passes.
- `pnpm check:token-gates` passes with all gates clean.
- Q5 exercised the complete propose, review, approve, bind, and
runtime-resolve flow over real HTTP, JWT, and database paths. It
verified 9/9 acceptance criteria.
## Risks
- Proposal ciphertext is retained encrypted for up to 14 days while
pending. Terminal-state and expiry scrub paths reduce but do not remove
server-compromise risk during that window.
- The V1 target policy is restrictive but not yet company-configurable.
A separate follow-up owns that change.
- A new migration can require another renumber if another migration
lands before maintainers merge this pull request.
> 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 Codex coding agent. The exact runtime model ID and
context-window size are not exposed. The agent used reasoning,
repository editing, terminal execution, Paperclip API, and GitHub CLI
capabilities. Q3 UI work also records Claude Opus 4.8 assistance in its
commit trailers.
## 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 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>
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Thinking Path
> - Paperclip separates workspace provisioning lifecycle from whether
the work was actually delivered.
> - Git ancestry alone cannot recognize squash merges or deliveries into
a branch other than the workspace base.
> - A merged pull request linked from a terminal issue is stronger
delivery evidence for those cases.
> - The read contract should expose that evidence without changing
persisted workspace schema.
> - Cleanup must remain conservative: terminal descendants, delivered
work, and no active run checkout are all required.
> - Reusing the existing cleanup primitives keeps service shutdown,
lease cleanup, activity logging, and archival behavior consistent.
> - Focused regression coverage locks in both the honest read signal and
the fail-closed reaper guards.
## Linked Issues or Issue Description
**What existing behavior does this improve?**
Execution workspace close-readiness payloads and terminal workspace
cleanup.
**Current behavior**
Delivered squash-merged or cross-branch workspaces can remain `active`
and report a permanent “not merged” warning because git ancestry does
not contain their original commits.
**Proposed behavior**
Read payloads distinguish PR-confirmed delivery, ancestry delivery,
unmerged work, and unknown state. Fully terminal delivered workspace
trees are archived only when no active run holds the checkout.
**Reason and benefit**
Operators and automation receive an honest delivery signal, while
shipped worktrees stop looking active forever and genuinely unmerged
work retains its warning.
**Breaking changes**
The workspace payload gains a derived field. Existing fields and
persistence remain unchanged; no database migration is required.
**What happened?**
A delivered workspace can remain `active` and warn that it is not merged
forever after its issue ships through a squash or cross-branch pull
request.
**Expected behavior**
Pull-request delivery should be represented honestly, and a fully
terminal delivered workspace should become cleanup-eligible when no run
holds its checkout.
**Steps to reproduce**
1. Create an issue workspace with commits ahead of its configured base.
2. Deliver those commits with a squash merge or into a different target
branch.
3. Mark the source issue and descendants done, then read workspace close
readiness.
Before this change, the workspace remains active with a “not merged”
warning indefinitely.
## What Changed
- Added the derived `deliveryState` workspace contract: `merged_via_pr`,
`merged_by_ancestry`, `unmerged`, or `unknown`.
- Extracted a shared GitHub pull-request merge classifier and reused it
for merge confirmations and workspace delivery checks.
- Suppressed false ancestry warnings when a terminal issue has
ground-truth merged-PR evidence.
- Added an idempotent terminality reaper with descendant-terminal,
active-run, and delivered-work guards.
- Restricted PR delivery evidence to the source issue, then required
live merged state plus matching GitHub repository, head branch, and
current workspace HEAD; persisted status, stale PRs, lexical mentions,
inbound references, and descendant PRs cannot authorize cleanup.
- Preserved workspaces with modified or untracked files even when their
committed HEAD was delivered.
- Bounded both long-lived pull-request state caches to 1,000 entries
with oldest-entry eviction.
- Routed eligible workspaces through existing runtime shutdown, lease
cleanup, activity logging, and archival machinery with exclusive Git
index, HEAD, and branch-ref locks plus non-forced removal.
- Added regression coverage for delivery derivation, warning behavior,
reaper guards, scheduler wiring, and squash/cross-branch delivery.
## Verification
- `pnpm -r typecheck`
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/execution-workspaces-service.test.ts
src/__tests__/merged-pr-confirmation-sweep.test.ts
src/__tests__/server-startup-feedback-export.test.ts --reporter=verbose`
— 63 passed
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/execution-workspaces-service.test.ts --reporter=verbose`
after review hardening — 43 passed
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/execution-workspaces-service.test.ts
src/__tests__/merged-pr-confirmation-sweep.test.ts
src/__tests__/external-objects-service.test.ts --reporter=dot` on the
final local head — 73 passed
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-workspace-busy.test.ts --reporter=verbose` — 15
passed
- `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm test:run` —
server 3,662 passed (4 skipped), UI 3,599 passed, CLI 327 passed, shared
415 passed, and skills catalog 20 passed; the aggregate DB stage ran
both source and built copies of one unrelated embedded-Postgres
migration test and both reached its 5-second timeout
- `pnpm --filter @paperclipai/db exec vitest run
src/status-card-migrations.test.ts --reporter=verbose` — isolated
aggregate-timeout verification passed in 3.99 seconds
- `NODE_ENV=production pnpm build`
- `pnpm check:token-gates`
## Risks
The reaper intentionally fails closed when issue terminality,
pull-request state, git ancestry, or checkout ownership cannot be
proven. GitHub lookups can delay classification and cleanup but cannot
cause an unproven workspace to be archived. Automated terminal archival
holds exclusive Git index, HEAD, and branch-ref locks across validation
and removal, skips configured destructive hooks, and uses non-forced
removal so dirty writes fail closed. Reopening a source issue does not
restore an archived workspace; it emits an audit event so a human or
agent can re-provision explicitly.
## Model Used
OpenAI Codex, GPT-5. The runtime did not expose a more specific model ID
or context-window size. Reasoning, tool use, repository editing, test
execution, and GitHub CLI access 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 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: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task assignment policies control which agents can receive work.
> - Protected-agent policy flags currently stop assignment.
> - The existing error says that the assignment requires approval.
> - Paperclip has no approval workflow for this policy.
> - This pull request models the policy as a hard block and gives the
operator an action that exists.
> - The benefit is accurate API guidance without weakening the existing
fail-closed behavior.
## Linked Issues or Issue Description
Refs #6386
**What happened?**
A protected-agent assignment denial said that approval was required. No
approval record or approval action existed for this policy, so the
message sent agents and operators to a dead end.
**Expected behavior**
The authorization result must state that protected-agent policy blocks
assignment. It must tell a company administrator to remove the block
before retrying.
**Steps to reproduce**
1. Set `authorizationPolicy.protectedAgent.requiresApproval` to `true`
on a target agent.
2. Give another agent the `tasks:assign` permission.
3. Preview or attempt assignment to the protected agent.
4. Observe that the old response promises an approval step that does not
exist.
**Paperclip version or commit**
`c54936e2e9` on `master`.
**Deployment mode**
Built from source. The behavior is in the core authorization service and
is not deployment-specific.
**Agent adapter(s) involved**
Not adapter-specific.
## What Changed
- Added canonical `protectedAgent.blockAssignment` and
`protectedAgent.blockReason` policy fields.
- Kept the legacy approval-named flags as fail-closed compatibility
aliases.
- Changed denial copy to name the hard block and the administrator
action.
- Added authorization and plugin-host regression coverage for canonical
and legacy policy data.
- Updated the V1 implementation contract with the protected-assignment
rule.
## Verification
- `pnpm exec vitest run
server/src/__tests__/authorization-service.test.ts
server/src/__tests__/plugin-access-authorization-host-services.test.ts`
— 2 files passed, 61 tests passed.
- `pnpm --filter @paperclipai/shared typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `pnpm --filter @paperclipai/shared build` — passed.
- `pnpm --filter @paperclipai/server build` — passed.
- `pnpm check:token-gates` — all gates clean.
- `git diff --check public-gh/master...HEAD` — passed.
The repository-wide local wrappers exceeded the execution host resource
limit before they printed a final summary. The PR check loop will use
GitHub CI as the complete test and build authority.
## Risks
- Low: assignment remains fail-closed. The change corrects the policy
name and denial guidance.
- Low: legacy fields remain supported, so existing plugin-owned policy
data does not change behavior.
- Low: the new policy schemas allow unknown keys for forward
compatibility, as the existing authorization policy schema already does.
> 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, exact model ID `gpt-5`, tool-enabled coding agent with
reasoning, shell, Git, and GitHub CLI access. The runtime does not
expose the context-window size.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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>
<!-- Write all pull request text in Simplified Technical English
(ASD-STE100): short sentences, one instruction per sentence, simple
approved vocabulary, and the active voice. -->
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The decisions desk shows pending decisions that need an operator
response
> - A strict decision cannot apply its effects after its target task
changes
> - A decision still remained pending when every target task finished
after proposal
> - The card also linked only the origin task, even when the decision
acted on another task
> - This pull request expires those moot decisions and links their
target tasks
> - The benefit is an accurate queue and a clear path to the work that
each decision affects
## Linked Issues or Issue Description
Related PR: #10801 removes the issue-page decision strip, which makes
clear queue provenance more important.
**What happened?**
A strict decision stayed pending until its time-to-live limit after
every target task reached `done`. The decision card linked only the
origin task. The origin task is where the agent proposed the decision,
and it can differ from the task that the decision affects. An operator
could therefore open a finished task with no visible decision and no
explanation of the real target.
**Expected behavior**
Paperclip must expire a strict decision when all of its targets finish
after the decision is proposed. The card must show and link every target
task that differs from the origin task.
**Steps to reproduce**
1. Create a strict decision that targets an active task from a different
origin task.
2. Move the target task to `done` without resolving the decision.
3. Run the decision expiry sweep.
4. Observe that the old code keeps the decision open until its
time-to-live limit.
5. Observe that the old card links only the origin task.
**Paperclip version or commit**
The bug reproduces on upstream `master` before this pull request.
**Deployment mode**
Local dev and self-hosted server modes are affected because the behavior
is in the shared decision service and board UI.
## What Changed
- Expire an open strict decision with reason `target_completed` when
every strict target reached `done` after proposal.
- Keep decisions that intentionally target an already-finished task.
- Keep lenient-only decisions open.
- Keep continuation delivery consistent with other expiry reasons.
- Add target-task links to the decision card provenance line.
- Use one shared target-ID helper across signing, execution, expiry,
card provenance, and resolver preloading.
- Add service and UI regression tests for primary, secondary, and
target-completed cases.
## Verification
- `pnpm exec vitest run ui/src/components/DecisionCard.test.tsx
server/src/__tests__/decisions-service.test.ts` — 51 tests passed.
- `pnpm --filter @paperclipai/shared typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm check:token-gates` — all gates clean.
- `git diff --check origin/master...HEAD` — passed.
## Risks
- Low migration risk. This change does not alter the database schema.
- The expiry sweep performs the existing strict-target query and adds a
snapshot comparison before expiry.
- A decision remains open if any strict target is active or if a target
was already `done` at proposal time.
> The roadmap lists work queues as planned. This pull request fixes the
existing decisions desk. It does not add a new queue subsystem.
## Model Used
- Implementation: Anthropic Claude through Claude Code. The runtime did
not expose the exact model snapshot or context-window size. The model
used reasoning, repository tools, code execution, and test execution.
- PR preparation: OpenAI Codex with GPT-5. The runtime did not expose a
dated model snapshot or context-window size. The model used 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents write to tasks they do not own. They comment, they change
fields, and the control plane now permits this by default for
standard-trust agents on any task they can read
> - This makes a task thread ambiguous. A reader sees a comment from an
agent that is not the assignee, but no surface says whose authority that
write rode
> - The same gap applies to field edits. The activity stream named the
verb, but it did not show the before value, the after value, or the
reason the write was permitted
> - The remaining refusals are also opaque. An agent that hits a wall
receives a 403 with no boundary name, no actor who can act, and no
sanctioned path. One real incident spent a full detour to find the
workaround
> - This pull request adds the three surfaces that make open cross-task
writes legible: an attribution chip, a field-level audit receipt, and an
actionable denial contract shared by the API and the UI
> - The benefit is that a reader can answer "who did this, on whose
authority, and was it allowed?" on the task itself, and a blocked writer
is told what to do next
## Linked Issues or Issue Description
No public issue exists for this work, so the enhancement is described
here.
**What existing behavior does this improve?**
Cross-task agent writes are permitted, but they are not explained. A
task thread can hold comments from agents that are not the assignee, and
the activity stream can hold field changes made by those agents. Neither
surface names the responsible user behind the write. When a write is
refused, the error text does not name the boundary or the way forward.
**Subsystem affected**
Issue detail UI (comment thread and activity stream), the issue write
authorization responses in the server, and the shared copy contract that
both consume.
**Current behavior**
- An agent comment on a task the agent does not own looks the same as an
assignee comment.
- An `issue.updated` activity row states the verb only. It does not show
the field-level before and after values, the responsible user, or the
authorization reason.
- A refused write returns a short message such as an ownership error.
The message does not state which rule fired, who is able to perform the
action, or which alternative path is sanctioned.
**Proposed behavior**
- An agent comment on a task the agent does not own carries a chip that
reads "for {user}". The chip names the responsible user. Its tooltip
states that the author is not the assignee and cannot exceed that user's
permissions.
- Each `issue.updated` row shows a receipt: the changed fields with
before and after values, the responsible user, and the authorization
reason. This applies to board edits as well as agent edits.
- Each refusal states three things: the boundary that fired, who is able
to act, and the sanctioned path. The API error body and the in-app
notice use the same words, because both read one shared contract.
Related pull requests, found by searching this repository:
- Refs #10837 — merged. It added the default-open cross-task write rule,
the comment attribution data, and the per-run containment cap that this
pull request makes visible.
- Refs #10114 — open. It proposes a narrower authorization change in the
same area.
- Refs #7998 — open. It proposes append-only cross-assignee comments as
an alternative to opening writes.
## What Changed
- Adds `packages/shared/src/issue-write-denial.ts`. This is one copy
contract for eight ways an issue write can be refused: not visible,
responsible-user ceiling, responsible user unavailable, excluded actor
class, assignee run lock, per-run cross-task cap, missing run context,
and rejected attribution. Each entry names the boundary, who can act,
and the sanctioned path.
- Maps server authorization decisions onto that contract in
`server/src/routes/issues.ts` and
`server/src/services/cross-issue-influence-limit.ts`. The flattened
`error` string carries all three obligations, and `details.code` lets
the UI render the same words. The two cap codes keep the names they
already ship under.
- Adds `CommentAttributionChip`. It renders "for {user}" beside the
author name on agent comments where the author is not the assignee. It
renders nothing when no responsible user is recorded, so older rows stay
clean. It is wired into both `IssueChatThread` and the flagged
`TaskChatThread` redesign.
- Adds `IssueFieldChangeReceipt`. It renders the change receipt under
`issue.updated` rows in the activity stream. Ids resolve to agent and
user names where the directory is loaded. Server-truncated text is
labelled as a preview, so the receipt never implies that it shows a
whole value.
- Adds `IssueWriteDenialNotice`. It renders the shared copy in the app,
keyed off the denial events the server logs on a task.
- Adds a public `/ux-lab/cross-issue-collaboration` page. It renders all
three surfaces and their edge cases for review without a seeded thread.
This follows the existing `ux-lab` pages.
## Verification
Automated, all green:
```
pnpm --filter @paperclipai/shared exec vitest run src/issue-write-denial.test.ts # 17 tests
pnpm --filter @paperclipai/ui exec vitest run src/components/IssueWriteDenialNotice.test.tsx \
src/components/IssueFieldChangeReceipt.test.tsx src/components/CommentAttributionChip.test.tsx \
src/lib/issue-change-receipt.test.ts src/lib/comment-attribution.test.ts # 46 tests
pnpm --filter @paperclipai/server exec vitest run src/__tests__/cross-issue-influence-limit.test.ts \
src/__tests__/issue-comment-attribution-audit-routes.test.ts \
src/__tests__/issue-agent-mutation-ownership-routes.test.ts \
src/__tests__/low-trust-red-team-routes.test.ts # 98 tests
```
`tsc --noEmit` passes for the shared, ui, and server packages.
Manual, in a browser:
1. Start the UI only: `pnpm --filter @paperclipai/ui exec vite`.
2. Open `/ux-lab/cross-issue-collaboration`. No session is needed,
because `ux-lab` routes are public.
3. All three surfaces were captured at 1440x900 in light mode and dark
mode, and at 390x844. The page reported no errors.
4. The chip tooltip was opened by a hover and by a keyboard focus.
Rendering the page found defects that the tests had missed. Three copy
and contrast defects were fixed, and two of them are now pinned by a
test. A design review then found three layout defects, which are also
fixed: the denial notice orphaned its label when a value wrapped, the
receipt icon wrapped onto its own line at narrow widths, and the chip
tooltip was reachable by hover only.
## Risks
Low risk, and additive.
- Every new surface renders nothing when its data is absent. Comments
without a recorded responsible user show no chip, and activity events
without a receipt show no receipt, so existing rows do not change.
- No migration is included. The data these surfaces read already ships.
- The wire values of the two per-run cap denial codes are unchanged.
Only the human-readable text changes, plus six codes that had no
`details.code` before.
- The denial copy is read by agents as well as people. If wording must
change later, one shared module is the only place to change it.
- Roadmap check: this extends the completed "Activity log & action
attribution" area rather than duplicating planned core work.
## Model Used
Claude Opus 5 (Anthropic), model id `claude-opus-5[1m]`, 1M context
window, extended thinking, with tool use and code execution. It ran as
an agent in Claude Code and drove a real browser to capture the review
screenshots.
## 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 Opus 5 (1M context) <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents move issues to `in_review` and rely on a "review path" (an
interaction, an approval, a monitor, or a named reviewer) to tell them
who decides next.
> - That review path can silently disappear. A user comment supersedes
the pending interaction, a monitor is exhausted, or a run ends without
restoring a path. The issue then sits in `in_review` with nobody
reviewing it and no visible action.
> - Such issues become invisible zombies. Nobody knows a decision is
owed, so the work stalls forever.
> - This pull request makes the review path a maintained invariant,
exposes a `reviewAttention` surface, and gives every stalled review
three inline actions in the UI.
> - The benefit is that an `in_review` issue always shows who reviews
it, or shows an amber "nobody is reviewing this" notice with one-click
Approve, Request changes, and Send back to work.
## Linked Issues or Issue Description
This pull request describes the problem inline. The tracking issue is
internal.
**Subsystem affected**
The review and attention loop that agents and humans share: the
`in_review` status, the `reviewAttention` surface, the /decisions
attention feed, and the issue-page review panel.
**Problem or motivation**
Agent-owned issues in `in_review` can lose their last review path. A
user comment supersedes the pending interaction. A monitor is exhausted.
A run ends without restoring a path. The issue then sits in `in_review`
with no reviewer and no visible action. It becomes an invisible zombie
and the work never progresses.
**Proposed solution**
Maintain the review path as a server invariant. Expose a
`reviewAttention` field that says what is under review, who decides, and
since when. Render a persistent review panel on the issue page and
inline actions on the /decisions feed. Keep human PATCHes into
`in_review` ungated, but record the requesting user so the panel never
renders empty.
**Alternatives considered**
A pure background auto-recovery sweep. This stays opt-in and is not
enough on its own, because it is invisible to the human. A bare status
banner. This is rejected, because it gives no action to resolve the
stall.
**Roadmap alignment**
This improves the core review and attention loop that both agents and
humans use every day.
## What Changed
- **Server — maintained review-path invariant:** when an issue enters or
sits in `in_review`, the server derives and persists a review path
(interaction, approval, monitor, or the requesting user) and recovers a
stale path with one bounded wake instead of leaving the issue pathless.
- **Server — `reviewAttention` surface:** a new field describes what is
under review (bound target with links), who decides, since when, and
whether the review is stalled. Stalled agent-assigned reviews are now
included in the attention feed.
- **Server — inline stalled-review decisions:** secured routes let a
permitted responder Approve (→ `done`), Request changes (→ `todo` + wake
carrying the note), or Send back to work (→ `todo` + wake) directly from
the attention feed.
- **Server — resume-intent wake:** an `in_review -> todo` transition now
wakes the assigned agent so a resumed review is not dropped.
- **Server — user-entry symmetry:** user PATCHes into `in_review` stay
ungated (no 422 for humans) and record the requesting user, who becomes
the named responder when no other path exists.
- **UI — review panel:** a persistent `IssueReviewPanel` renders above
the thread whenever status is `in_review`. The covered state shows the
bound target, responder, and outcomes and hoists the pending
interaction/approval card. The stalled state shows the amber notice plus
the three actions.
- **UI — decisions card actions:** the same three actions render inline
on the /decisions `AttentionQueueRow`.
- **UI — responsive fix:** the stalled action row stacks to full-width
buttons at phone width and returns to a horizontal row at `sm` and up.
New 390px stories capture the phone layout.
## Verification
- `cd ui && npx vitest run src/components/IssueReviewPanel.test.tsx
src/components/AttentionQueueRow.test.tsx src/lib/attention.test.ts
src/api/issues.test.ts` — 91 tests pass.
- Server suites added and updated: `issue-review-attention`,
`issue-stalled-review-decision-routes`, `review-path-recovery`,
`recovery-observability`, and related route/liveness tests (run by CI).
- A designer reviewed the UI at 390px and desktop in light and dark
themes on both the issue-page panel and the /decisions card. The stalled
action row stacks cleanly at phone width with no overlap and keeps the
horizontal row on desktop.
## Risks
- **Migration:** adds migration `0200` (next after master `0199`, no
renumber). It extends the agent-wakeup-requests schema and is additive.
- **Behavioral shift:** `in_review -> todo` now dispatches a wake. This
is intended (resume intent) and covered by tests.
- **Authz:** the inline decision routes are permission-gated. Only a
permitted responder sees and can trigger the actions.
- Overall risk is moderate and contained to the review and attention
loop.
## Model Used
- Claude, Opus 4.8 (`claude-opus-4-8`), extended thinking, tool use and
code execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the control plane that coordinates autonomous agent
work.
> - Agents need to collaborate on issues beyond their current
assignment.
> - Cross-issue comments and updates are useful, but an unbounded run
can create cascading side effects.
> - The control plane must preserve company-wide collaboration while
containing each run's influence.
> - Comment attribution must also show the responsible user and the
acting agent in audits.
> - This pull request adds run-bound cross-issue containment,
attribution, and agent-class wake rules.
> - The benefit is safer collaboration without restoring issue-assignee
ownership restrictions.
## Linked Issues or Issue Description
**What existing behavior does this improve?**
Agent-authenticated issue comments, updates, reopen behavior, and
assignee wake routing.
**Subsystem affected**
Cross-cutting: server routes and services, shared contracts, database
schema and migration, and implementation documentation.
**Current behavior**
An authenticated agent can collaborate across company issues, but one
heartbeat run has no per-run side-effect boundary. Comment records also
do not persist the responsible user separately from the acting agent.
**Proposed behavior**
Require a valid heartbeat run for agent cross-issue comments and
updates. Audit each attempt and cap a run at 20 cross-issue effects.
Keep the cap in log-only mode until it automatically changes to
enforcement at 2026-08-11 00:00 UTC. Preserve same-issue writes. Use
agent-class wakes for agent comments. Keep same-run completion comments
from reopening completed work. Record the responsible user on
agent-authored comments and activity.
**Reason and benefit**
Agents can collaborate on other issues without an assignment gate, while
each run has an atomic and inspectable side-effect limit. Operators can
identify both the acting agent and the responsible user.
**Breaking changes**
After 2026-08-11 00:00 UTC, the twenty-first cross-issue comment or
update from one heartbeat run returns a containment error. Agent
cross-issue writes without valid run context are rejected. The migration
is additive and backfills existing agent-authored comment attribution
where the source data is available.
## What Changed
- Added an atomic per-run counter for cross-issue agent comments and
updates.
- Added audit events for allowed and rejected cross-issue effects.
- Added the automatic log-only to enforcement flip at 2026-08-11 00:00
UTC.
- Added responsible-user attribution to agent-authored comments,
activity records, shared types, and validators.
- Added an additive migration and migration coverage for existing
comments.
- Updated reopen, resume, and wake behavior so agent comments create
agent-class wakes and same-run completion comments remain inert.
- Updated the implementation specification and regression coverage.
## Verification
- `pnpm exec vitest run
server/src/__tests__/cross-issue-influence-limit.test.ts
server/src/__tests__/issue-comment-attribution-audit-routes.test.ts
server/src/__tests__/issue-comment-reopen-routes.test.ts
packages/db/src/issue-comment-on-behalf-migration.test.ts` — 97 tests
passed.
- `pnpm -r typecheck` — passed, including migration safety checks.
- `pnpm test:run` — server batch: 3,364 passed and 2 skipped; UI batch:
3,504 passed. One unrelated CLI doctor test warned because this agent
runtime injects static AWS credentials.
- `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm exec vitest
run cli/src/__tests__/secrets.test.ts` — 8 tests passed and confirmed
the CLI failure was ambient-environment sensitive.
- `pnpm build` — passed.
## Risks
- The fixed enforcement timestamp changes production behavior
automatically on 2026-08-11 00:00 UTC. Audit logs before that time
provide rollout visibility.
- The per-run counter serializes on the heartbeat-run row. This prevents
concurrent attempts from racing past the cap but adds a small lock scope
for cross-issue writes.
- Existing comments can only be backfilled when their acting run or
agent attribution is recoverable.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI GPT-5 in the Codex agent runtime. The runtime did not expose a
context-window size. Reasoning, shell tools, code editing, and test
execution 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>
## Thinking Path
> - Paperclip is the control plane that lets humans govern companies of
AI agents.
> - Issue-thread interactions are the structured handoff point for
confirmations, questions, suggested tasks, and other governed decisions.
> - Those interactions previously assumed that only board users could
resolve them, preventing one agent from explicitly addressing another
agent for a response.
> - Agent resolution needs company-level governance, auditable resolver
identity, safe terminal-state handling, and attention routing so
authorization is enforced server-side rather than inferred from UI
behavior.
> - This pull request adds governed agent resolution, withdrawal and
terminal expiry semantics, explicit agent addressees, lifecycle
reconciliation, and attention-feed filtering.
> - The benefit is that agents can participate in structured decisions
without weakening board control, company isolation, wake behavior, or
audit invariants.
## Linked Issues or Issue Description
### Subsystem affected
Issue-thread interactions across database, shared contracts, server
authorization/services, adapter callbacks, agent skill guidance, API
docs, and UI governance surfaces.
### Problem or motivation
Structured interactions were board-only, had no explicit agent
addressee, and lacked durable withdrawal/terminal-expiry semantics. That
made peer-agent decisions impossible to authorize and audit safely.
### Proposed solution
Persist requested/effective resolver policy and addressee identity,
enforce company governance and eligible agent resolution, reconcile
addressee lifecycle changes, expose withdrawal and terminal expiry, and
route attention to the intended active agent with board fallback.
### Alternatives considered
Implicitly authorizing the issue assignee or mentioned agents was
rejected as ambiguous and difficult to audit. Using comments alone was
rejected because it loses structured outcomes and continuation behavior.
### Roadmap alignment
Supports the ROADMAP direction for lightweight leadership-agent
communication that still resolves into governed decisions and work
objects.
### Additional context
Public GitHub issue/PR search found no duplicate implementation; open PR
search for interaction resolver governance and agent addressees only
returned this PR.
## What Changed
- Add company-scoped interaction resolver governance contracts and
persistence.
- Add requested/effective resolver policy, resolver identity,
withdrawal, and terminal-expiry behavior.
- Add explicit `addresseeAgentId` validation, authorization,
persistence, lifecycle reconciliation, API documentation, and skill
guidance.
- Route pending addressed interactions to the intended invokable agent
and fall back to board attention when that agent becomes ineligible or
is deleted.
- Preserve sandbox callback identity fields required by governed
resolution paths.
- Add migrations `0193` and `0194` plus route, service, attention,
adapter, CLI, and UI coverage.
- Add governance state and company settings UI, including responsive
mobile behavior and distinct withdrawn/expired audit presentation.
## Verification
- `pnpm check:token-gates` — passed.
- `pnpm -r typecheck` — passed, including migration numbering and safety
checks.
- `pnpm test:run` — feature/server and UI workspace suites passed; one
unrelated CLI AWS doctor test observed injected static AWS credentials
and warned instead of passing.
- `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm exec vitest
run cli/src/__tests__/secrets.test.ts --project paperclipai` — 8 tests
passed, confirming the failure was environment-sensitive.
- `pnpm build` — passed.
- Latest rebased head `e24cece6be9f1877bdbac7691bcb44fd583c0161`
completed all GitHub CI jobs successfully.
## Risks
- Migrations add interaction and company-governance fields; numbering is
conflict-free on current `master`, additive statements are idempotent,
and migration safety checks pass.
- Agent authorization behavior expands beyond board-only resolution, but
defaults remain board-only and coverage exercises company boundaries,
resolver eligibility, lifecycle invalidation, wake behavior, withdrawal,
expiry, and attention fallback.
- Attention routing depends on current agent invokability;
reconciliation and read-time filtering prevent stale addressees from
retaining visibility or resolution authority.
> 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.6-sol` with reasoning, terminal tool use,
code execution, Git/GitHub integration, and Paperclip control-plane
tools. Context-window metadata was not reported by the runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The decisions desk and queue help operators find work that needs a
human decision.
> - The current views use different grouping, sorting, and labels.
> - Repeated confirmation requests can also leave stale pending actions
in the queue.
> - Blocked-work attention can point at an intermediate issue instead of
the terminal blocker.
> - This pull request aligns the server contract and both user
interfaces.
> - The benefit is a smaller, clearer queue that ranks the decisions
with the largest impact.
## Linked Issues or Issue Description
Related PR: #10774
**What existing behavior does this improve?**
The decisions desk and queue currently use different triage rules. They
can show stale repeated confirmations and can rank blocked work by an
intermediate issue.
**Subsystem affected**
This change affects attention aggregation, issue thread interactions,
shared attention contracts, and the decisions user interface.
**Current behavior**
The desk uses a can-wait group that has no clear arrival meaning. The
queue has fewer controls than the desk. Repeated pending confirmations
remain actionable. Blocked-work rows do not always identify the terminal
actionable blocker.
**Proposed behavior**
Group desk items by arrival date, and reserve Decide now for explicit
due dates. Use one toolbar and shelf model on both pages. Supersede
older repeated pending confirmations. Aggregate blocked work under the
terminal actionable blocker and rank it by impact.
**Reason and benefit**
Operators get one consistent triage model. The badge reflects new and
overdue work. High-impact blockers move to the top. Duplicate
confirmation work no longer consumes attention.
**Breaking changes**
The attention summary field `decideNowCount` changes to
`deskBadgeCount`. Consumers must use the new field. Older repeated
confirmation interactions can now finish with the
`superseded_by_newer_request` outcome.
## What Changed
- Supersede older pending confirmation requests for the same issue and
record the mutation in activity history.
- Resolve blocked-work attention to actionable terminal blockers,
suppress live blocker trees, and rank rows by blocked-work impact.
- Group the decisions desk into New today and Earlier, and count new
plus overdue work in the desk badge.
- Share the decision toolbar and shelf components across the desk and
queue.
- Add queue grouping, sorting, filtering, aging, visible training
controls, and clearer recommendation copy.
- Add server, shared-contract, and user-interface tests for the new
behavior.
## Verification
- `pnpm check:token-gates`
- `pnpm -r typecheck`
- `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm test:run` (all
server, UI, CLI, shared, and catalog tests passed; one fixed five-second
DB timeout flaked under full-suite load)
- `NODE_ENV=test pnpm --filter @paperclipai/db exec vitest run
src/status-card-migrations.test.ts` (passed in isolation)
- `pnpm build`
## Risks
- The attention summary field rename requires synchronized consumers.
- Terminal-blocker traversal uses cycle and depth guards. A malformed
dependency graph can stop at the last safe node.
- The new arrival grouping changes which items contribute to the
decisions badge.
- Superseding repeated confirmations changes the terminal state of older
pending interactions.
> 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 runtime did not expose a dated model
snapshot or context-window size. The model used 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>
## Thinking Path
> - Paperclip tracks agent work for the team.
> - The startup timing path already emits detailed spans.
> - The run log repeats the same detailed timing.
> - That makes the event larger than it needs to be.
> - This pull request removes the redundant per-step timing fields from
the run-log payload.
> - The benefit is a smaller log with the same detail still available in
traces.
## Linked Issues or Issue Description
No public GitHub issue or open pull request matches this change. I
described the enhancement below.
**What existing behavior does this improve?**
`run.startup.step` event output.
**Subsystem affected**
Cross-cutting (multiple of the above)
**Current behavior**
`run.startup.step` writes `roundTrips`, `providerExecMs`,
`providerGetMs`, `createRuntimeMs`, and `ensureSessionMs`. The same
detail already exists in the spans.
**Proposed behavior**
`run.startup.step` keeps only `step`, `durationMs`, and `outcome`. The
heartbeat lifecycle timestamps stay unchanged.
**Reason and benefit**
This change removes redundant data from the run log. It keeps the useful
detail in trace spans. It also makes the payload smaller and easier to
read. The revert path stays clear because the removed data has one
producer chain.
**Breaking changes**
Yes. Consumers that read the removed fields must switch to span data or
the remaining event fields. The heartbeat lifecycle timestamps do not
change.
**Additional context**
I searched GitHub for related open PRs and issues. I found no match.
## What Changed
- Removed the redundant per-step timing fields from the run-log payload.
- Removed the now-dead producer chain that fed those fields.
- Kept the span-level timing data and the heartbeat lifecycle
timestamps.
## Verification
- `adapter-utils` typecheck clean
- `server` typecheck clean
- `startup-timing` suite: 30 passed
- `adapter-utils` `execute` suite: 99 passed
- `server` `environment-execution-target` suite: 21 passed
## Risks
- Consumers that still read the removed fields will need a code change.
- This is a one-way-door data removal for the run log.
## 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 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>
## Thinking Path
> - Paperclip uses pull requests to ship control-plane changes with
review and audit.
> - This change extends sandbox provider telemetry.
> - The first PR added startup and exec spans.
> - This PR adds provider spans for cache hits, plugin tracer flow, and
Daytona file sync steps.
> - The host must keep the trust boundary and reject unsafe span data.
> - The result is fuller OTel coverage with bounded labels and safe
parentage.
## Linked Issues or Issue Description
**Problem or motivation**
Sandbox provider telemetry lacks an explicit cache-hit signal, plugin
span context, and file-sync pack and transfer spans.
The current proxy for a cache hit is fragile.
The plugin SDK also needs a safe tracer surface and a per-call parent
context.
**Proposed solution**
Add an explicit `cacheHit` flag from the Daytona sandbox handle lookup.
Expose `ctx.tracer` on the plugin context and pass a `traceparent`
string with each call.
Record provider spans on the host with allowlist clamping and capability
checks.
Wrap Daytona file sync pack and transfer steps in spans.
**Alternatives considered**
Keep the old `providerGetMs == 0` proxy.
Reject that path because the cache-hit decision belongs at the handle
lookup, not in a timing proxy.
The host stays the trust boundary for worker span data.
It clamps labels, rejects bad parent data, and gates the worker-to-host
span RPC by capability.
A security review completed before this PR opened.
## What Changed
- Added an explicit `cacheHit` flag from the Daytona sandbox handle
lookup.
- Added `ctx.tracer` on the plugin context and a `traceparent` field in
the per-call context.
- Added host-side span recording with attribute clamping and capability
checks.
- Wrapped Daytona file sync pack and transfer steps in spans.
- Added tests for the host trust boundary, the plugin tracer no-op path,
and the Daytona span paths.
## Verification
- `pnpm --filter @paperclipai/plugin-daytona exec vitest run`
- `pnpm --filter @paperclipai/plugin-sdk exec vitest run`
- `pnpm --filter @paperclipai/server exec vitest run
plugin-host-services environment-execution-target instrumentation
plugin-worker-manager`
- `tsc --noEmit` for server, plugin-sdk, and adapter-utils
## Risks
- Span data now crosses the worker boundary, so the host allowlist and
capability gate must stay strict.
- The `traceparent` path must stay valid and host-owned.
- Daytona file sync spans must not add new round trips.
## 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 linked existing issues with `Fixes:` / `Closes:` /
`Refs:` or described the issue in the PR body
- [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>
<!-- Write all pull request text in Simplified Technical English
(ASD-STE100): short sentences, one instruction per sentence, simple
approved vocabulary, and the active voice. -->
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The heartbeat service starts agent runs in local, SSH, sandbox,
plugin, or Kubernetes environments.
> - Runs can share one project working tree.
> - PR #10699 made every shared working tree single-file, including
trusted local and SSH hosts that support coordinated concurrent work.
> - Operators need a policy that keeps remote environments safe and
restores local multi-agent work.
> - This pull request adds an `auto`, `serialize`, or `allow`
concurrency policy and applies it to the final execution environment.
> - The benefit is safe serialization by default for sandboxed targets
and useful concurrency by default for persistent local targets.
## Linked Issues or Issue Description
Related work: #10699 introduced the busy gate that this change makes
configurable. #7852 covers a separate environment-lease race and does
not provide this dispatch policy.
**Subsystem affected**
`server/` heartbeat orchestration and `packages/shared/` workspace
policy contracts.
**Problem or motivation**
The shared-workspace busy gate always defers a second run. This behavior
prevents local multi-agent projects from running concurrently even when
operators expect agents to coordinate through commits.
**Proposed solution**
Add `sharedWorkspaceConcurrency` with `auto`, `serialize`, and `allow`
values. Default `auto` permits local and SSH concurrency. It serializes
sandbox, plugin, and forced Kubernetes execution.
**Alternatives considered**
Keeping unconditional serialization is too restrictive for persistent
host working trees. Always allowing overlap removes the protection that
sandboxed and remote targets need.
**Roadmap alignment**
This is a focused correction to the shipped cloud and sandbox execution
milestone. It does not add a new roadmap feature.
## What Changed
- Added the optional tri-state field to project policy and issue
override contracts and validators.
- Added a pure resolver with issue override, project policy, and `auto`
default precedence.
- Moved final environment and Kubernetes resolution before the
shared-workspace busy gate.
- Kept the existing deferral and retry behavior for every path that
resolves to serialization.
- Added a task-context warning and a structured log when a run
dispatches beside a live holder.
- Added policy and heartbeat coverage for all requested policy and
environment combinations.
- Documented the new policy and its default behavior.
## Verification
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/execution-workspace-policy.test.ts
src/__tests__/heartbeat-workspace-busy.test.ts` — 32 tests passed.
- `pnpm -r typecheck` — passed.
- `PAPERCLIP_IN_WORKTREE=false
PAPERCLIP_DATABASE_RESTORE_IN_PROGRESS=false
PAPERCLIP_RESTORE_IN_PROGRESS=false pnpm test:run` — all 3,528 server
tests passed. The later UI/workspace phase passed 3,384 tests and hit
one unrelated `CompanyEnvironments` navigation timing failure.
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/CompanyEnvironments.test.tsx -t "opens the edit form on a
standalone page with existing values and closes after save"` — the
unrelated UI test passed in isolation.
- `pnpm build` — passed.
Policy matrix:
| Policy | Final target | Expected result | Result |
| --- | --- | --- | --- |
| `auto` | local | Dispatch with holder note | Passed |
| `auto` | sandbox | Defer with `workspace_busy` | Passed |
| `auto` | instance-forced Kubernetes | Defer with `workspace_busy` |
Passed |
| `serialize` | local | Defer with `workspace_busy` | Passed |
| `allow` | sandbox | Dispatch with holder note | Passed |
Existing serialization, retry, and stale-holder tests also pass.
## Risks
- `auto` changes the post-#10699 local and SSH behavior back to
concurrent dispatch. Concurrent agents can mutate the same working tree,
so each dispatched run receives an explicit coordination warning.
- `allow` is an operator override and can permit overlap in sandbox or
plugin environments.
- Unknown environment drivers serialize in `auto` mode. This keeps the
fallback conservative.
- There is no database migration. An absent field resolves to `auto`.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI GPT-5 (Codex). The exact API snapshot and context-window size are
not exposed to the agent. Reasoning, repository editing, terminal tool
use, and local code execution 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>
## Thinking Path
> - Paperclip coordinates AI agent work and records execution data.
> - The sandbox startup path and the host-to-sandbox execution path need
clearer OpenTelemetry spans.
> - The trace data now shows wait time, critical path data, and bounded
labels.
> - This pull request adds bounded spans and closed attribute helpers
for sandbox startup and execution.
> - The result is better trace data with low cardinality and no secret
leakage.
## Linked Issues or Issue Description
- No public GitHub issue exists for this branch.
- Related PR: #10536
- This PR extends the sandbox OpenTelemetry work with exec spans, root
timing, and bounded labels.
## What Changed
- Added a closed span-attribute contract for sandbox startup data.
- Added a bounded label helper for command and region values.
- Threaded the active step context into the execution path.
- Added a `sandbox.exec` span with true timestamps and bounded
attributes.
- Added skipped-step and root-span timing data with low-cardinality
context.
- Moved handshake sub-times and bridge batch data onto spans.
## Verification
- `pnpm --filter @paperclipai/adapter-utils run typecheck`
- `pnpm --filter @paperclipai/server run typecheck`
- `pnpm --filter @paperclipai/adapter-utils exec vitest run`
- `pnpm --filter @paperclipai/server exec vitest run
environment-execution-target`
- `git log --oneline
origin/master..origin/feat/sandbox-startup-otel-spans`
- `git diff origin/master...origin/feat/sandbox-startup-otel-spans
--stat`
## Risks
- Span attribute rules could still miss a future field if new code skips
the shared helper.
- Low-cardinality labels may hide some detail, but that is the intended
tradeoff.
- Tracing stays fail-open, so a missing tracer still hides data rather
than stopping work.
## Model Used
- OpenAI Codex, GPT-5, tool use enabled. The runtime did not expose the
context window size.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change 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] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
The current head still has one failing required check: `e2e`. The PR
stays unready for board handoff until that check passes.
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The issue properties panel can show external objects such as GitHub
pull requests.
> - Those objects are resolved by external-object providers and then
displayed as compact status labels.
> - A GitHub pull request could remain in the fallback `unknown` state
and appear as `Not yet resolved`.
> - That label is confusing when the object is known but has not been
refreshed yet.
> - This pull request refreshes due external objects from the heartbeat
scheduler and improves the unknown-status copy.
> - The benefit is a properties panel that moves from pending refresh to
the real pull request state without a manual refresh.
## Linked Issues or Issue Description
No public GitHub issue exists for this bug. I searched for related
public issues and pull requests using the terms `Not yet refreshed`,
`external objects refresh`, and `external PR status`, and did not find a
duplicate implementation.
**What happened?**
The issue properties panel could show a GitHub pull request as `Not yet
resolved` even when the referenced pull request was valid. The object
stayed stale unless a manual refresh path ran.
**Expected behavior**
A known external object should show pending-refresh copy while it waits
for provider data. When the scheduler refreshes it, the properties panel
should show the provider status such as open, merged, or closed.
**Steps to reproduce**
1. Create or view an issue that references a GitHub pull request.
2. Open the issue properties panel.
3. Observe the external object row before a manual refresh has run.
**Paperclip version or commit**
Current `master` before this pull request.
**Deployment mode**
Local dev and self-hosted server.
**Installation method**
Built from source.
**Agent adapter(s) involved**
Not adapter-specific.
**Database mode**
Not database-related.
**Access context**
Board view.
**Privacy checklist**
I reviewed this description and did not include logs, credentials,
private URLs, internal issue IDs, or PII.
## What Changed
- Added a heartbeat scheduler tick that refreshes due external objects
for active companies.
- Kept manual external-object refresh behavior on the same service path.
- Changed display copy so known provider objects use liveness labels
such as `Not yet refreshed`, while fresh unknown provider statuses show
`Status unavailable`.
- Added server and UI tests for scheduled refresh and label behavior.
## Verification
- `corepack pnpm install --frozen-lockfile`
- `pnpm check:token-gates`
- `pnpm exec vitest run
server/src/__tests__/external-objects-service.test.ts
server/src/__tests__/server-startup-feedback-export.test.ts
ui/src/components/ExternalObjectPill.test.tsx
ui/src/components/IssueProperties.test.tsx
ui/src/lib/external-objects.test.ts`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/server build`
- `pnpm --filter @paperclipai/ui build`
- `pnpm run typecheck:build-gaps`
- GitHub PR checks passed on head `0e7fcd30`
- Greptile reported 5/5 on head `0e7fcd30` with no unresolved review
threads
Notes:
- I ran recursive typecheck and build first. Both hit container resource
limits with exit 137 during concurrent package work, so I reran the
affected server and UI targets separately.
- An unrelated workspace-runtime auto-port test fails in this container
with a PID ownership mismatch. It is outside the files changed here.
## Risks
Low to medium risk.
The scheduler does more periodic external-object work, so the main risk
is extra provider refresh load. The implementation bounds the work to
active companies, due non-terminal objects, and 50 objects per company
per tick. The path also stays behind the external-objects experimental
setting.
## Model Used
OpenAI GPT-5 Codex in the Codex execution environment, with shell and
GitHub CLI tool use. The runtime did not expose a more specific internal
model ID or context window.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `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>
## Thinking Path
> - Paperclip is the control plane teams use to manage AI agents and
their reusable capabilities.
> - Skills Manager lets operators discover and import skills from
project workspaces.
> - Automatic discovery only surfaces skills in conventional locations,
so valid skills stored elsewhere in a project are invisible.
> - Operators need a safe way to navigate project folders without
exposing paths outside the selected workspace.
> - This pull request adds company-scoped workspace folder browsing and
selection to the project skill import flow.
> - The benefit is that operators can find and import valid skill
folders regardless of repository layout while preserving workspace
boundaries.
## Linked Issues or Issue Description
- **Subsystem affected:** Cross-cutting (`server/`, `ui/`, and
`packages/shared`).
- **Problem or motivation:** Project skill imports rely on conventional
directory discovery, which prevents operators from selecting valid
`SKILL.md` folders stored in atypical locations.
- **Proposed solution:** Add a company-scoped browse endpoint and a
folder browser in the import dialog. The server resolves real paths,
rejects traversal outside the workspace, skips symlinks and high-noise
directories, identifies skill directories/files, and caps listings at
250 entries.
- **Alternatives considered:** Expanding the automatic scan to every
directory would be slower and noisier, while accepting arbitrary
filesystem paths would weaken project/workspace scoping.
- **Roadmap alignment:** This extends the completed “Skills Manager,
Skill Studio & Skills Store” capability in `ROADMAP.md` without
duplicating planned core work.
- **Additional context:** GitHub search found no duplicate or closely
related public issues or pull requests.
## What Changed
- Added shared browse request/result contracts and validation for
project workspace navigation.
- Added a company-scoped API route and service that safely lists local
workspace folders and detects `SKILL.md` entries.
- Added project workspace/folder navigation to the import dialog,
including parent navigation, workspace switching, truncation feedback,
and direct skill selection.
- Added service and route regression tests for browsing, skill
detection, company isolation, and traversal rejection.
- Added shared response schemas and OpenAPI documentation for the browse
endpoint.
- Hardened explicit skill selections with realpath containment so
symlinked directories cannot escape the project workspace.
## Verification
- `pnpm exec vitest run
server/src/__tests__/company-skills-service.test.ts
server/src/__tests__/company-skills.test.ts` — 64 tests passed.
- `pnpm --filter @paperclipai/shared typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm exec vitest run server/src/__tests__/openapi-routes.test.ts` — 3
tests passed.
- Focused post-review reruns: `company-skills-service.test.ts` — 45
tests passed; shared/server typechecks passed.
- GitHub latest-head checks — all green after one transient e2e rerun;
no pending or failing checks.
- `pnpm check:token-gates` — all gates clean on the rebased head.
## Risks
- Low-to-moderate risk: this adds a filesystem browsing surface.
Realpath containment checks prevent workspace escape, symlinks are
excluded, remote-managed workspaces are rejected, and directory listings
are capped.
- The browser intentionally hides `.git` and `node_modules`; skills
inside those directories cannot be selected through this flow.
> 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, exact model ID `gpt-5.5`, reasoning-enabled with
terminal/tool use and code execution; runtime context-window size is not
exposed.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
— the execution harness requires preserving the assigned branch name.
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes — no
user-facing docs changes are needed beyond this PR description because
the flow is self-explanatory UI behavior.
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The Decisions desk shows work that needs a human decision
> - The queue foundation can group and rank decision work
> - Operators also need a focused daily view and a safe way to handle
old work
> - This pull request adds the desk controls, the aging shelf, and
reversible retention
> - It also binds bulk archive decisions to the exact reviewed item set
> - The benefit is a smaller daily queue without lost or orphaned work
## Linked Issues or Issue Description
**Subsystem affected**
Cross-cutting: server, UI, database, and shared contracts.
**Problem or motivation**
Decision work can grow into one large company list. Operators need quick
queue and date controls. Old items also need a safe retention path that
does not delete work.
**Proposed solution**
Add queue and date controls to the Decisions desk. Compute the aging
shelf on the server. Archive idle items after 90 days unless an operator
keeps them. Keep archived items searchable and revivable. Notify origin
agents in one batch per sweep. Bind bulk archive proposals to a signed,
exact item manifest.
**Alternatives considered**
Client-only aging can drift across browsers and source kinds. Deleting
old rows removes audit and recovery paths. An unsigned dynamic bulk
query can archive items that the reviewer did not inspect.
**Roadmap alignment**
This work supports the Work Queues and decision-memory directions in
`ROADMAP.md`. It extends the Decisions and attention-feed foundation
from #10651. Related earlier work includes #9380, #10010, and #10474.
## What Changed
- Added the queue rail, date chips, decide split, triage strip, queue
page, and aging shelf UI.
- Added server-owned shelf state with per-queue retention overrides.
- Added reversible retention state, archive history, and an idempotent
notification outbox.
- Added the 90-day archive sweeper, Keep exemption, archived feed query,
and revive actions.
- Added one origin-agent notification per agent and sweep.
- Added signed bulk archive proposals with exact-set and version checks.
- Persisted queue-exclusion reasons atomically and kept cross-domain
source resolution per-item until it has an exact-set transaction
contract.
- Added API contracts, OpenAPI entries, migration coverage, focused
tests, and Storybook screens.
## Verification
- `pnpm -r typecheck`
- `pnpm test:run` (server: 329 files and 3,453 tests passed; UI: 408
files and 3,362 tests passed)
- `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm exec vitest
run cli/src/__tests__/secrets.test.ts`
- `pnpm build`
- `pnpm check:token-gates`
- Focused retention, attention, decisions, migration replay, startup,
and UI API tests.
- Complete queue snapshot regression with 51 items across the normal
50-item page boundary.
The unmodified CLI test reports one warning assertion in this runtime
because the harness injects static AWS credential variables. The
isolated test passes when those two variables are removed.
## Risks
- The migration adds retention and notification outbox tables. It uses
idempotent table, index, and foreign-key creation.
- Retention runs on the heartbeat scheduler interval. Compare-and-set
version checks prevent stale archive writes.
- Bulk archive acceptance fails closed when authority, activity,
version, or the reviewed set changes.
- Archive is reversible and does not delete source records.
> 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 model used tool calls, code
execution, database migration generation, and test execution. The
context-window size is not exposed in this runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Isolated workspaces give each task a safe and reproducible checkout.
> - The existing setup cloned the development database before an agent
needed to run the app.
> - This made worktree creation slower and heavier for tasks that never
start a service.
> - Runtime services already use one server start path for heartbeat,
operator, and startup recovery flows.
> - This pull request moves heavy setup to that start path and keeps
worktree creation lean.
> - The benefit is faster isolated workspace creation with the same
reliable runtime setup when a service starts.
## Linked Issues or Issue Description
Related pull request: #10652 covers the initial deferred
database-seeding slice. This pull request supersedes it with end-to-end
runtime provisioning and safe cleanup.
**What existing behavior does this improve?**
This improves isolated worktree creation, runtime service startup, and
isolated instance cleanup.
**Subsystem affected**
Cross-cutting: CLI worktree setup, server runtime orchestration, shared
workspace contracts, and development scripts.
**Current behavior**
Paperclip seeds an isolated development database during worktree
creation. It can also leave an isolated instance directory after
workspace teardown. This work happens even when no runtime service
starts.
**Proposed behavior**
Paperclip creates the worktree with a lean eager setup. It runs an
idempotent runtime provision command before the first managed service
spawn. Concurrent starts share one provision attempt. Teardown removes
the isolated instance safely.
**Reason and benefit**
Many agent tasks only edit and test code. They do not need a running
Paperclip instance. Deferring the database seed reduces workspace
startup cost while preserving automatic setup for tasks that start the
app.
**Breaking changes**
None. The new runtime provision command is optional. Existing workspace
behavior is unchanged when it is absent.
## What Changed
- Split Paperclip worktree setup into a lean eager script and an
idempotent runtime provision script.
- Added `runtimeProvisionCommand` to project, issue, realized workspace,
and persisted workspace contracts.
- Added a per-workspace provision mutex before local service spawn for
heartbeat, operator, and startup recovery flows.
- Added a persisted `provisioning` service state and the
`workspace_runtime_provision` operation phase.
- Kept provision time outside the service readiness timeout and made
failed attempts visible and retryable.
- Reclaimed isolated instance data during safe workspace teardown.
- Serialized deferred database seeding across processes and bound
teardown to the instance root captured in persisted workspace metadata.
- Added tests for config flow, concurrency, retry, no-op behavior,
readiness timing, scripts, CLI commands, and cleanup.
- Documented the eager and runtime provisioning contracts.
## Verification
- `pnpm -r typecheck`
- `pnpm build`
- `pnpm test:run` (server: 3,201 passed; UI: 3,345 passed; the CLI phase
exposed one environment-sensitive AWS doctor assertion because the agent
runtime injects static AWS credentials)
- `env -u AWS_ACCESS_KEY_ID -u AWS_SECRET_ACCESS_KEY pnpm exec vitest
run cli/src/__tests__/secrets.test.ts -t 'passes AWS doctor checks when
non-secret provider config is present'`
- Focused runtime tests cover serialized provisioning, retry after
stderr failure, absent-command no-op behavior, operation logging,
persisted state order, and readiness timeout exclusion.
- Focused CLI and cleanup tests cover concurrent seed serialization,
stale-lock fail-closed behavior, persisted instance ownership, and
rewritten sibling pointers.
## Risks
- A faulty runtime provision script blocks service startup. Paperclip
records stderr, marks the service failed, and retries on the next start.
- Concurrent service requests share an in-process provision attempt,
while the seed command uses an atomic filesystem lock across processes.
A stale lock fails closed and requires an operator to verify no seed is
running before removing it.
- Isolated instance cleanup is destructive. The cleanup service
validates ownership and path containment before removal.
> 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.6-sol`, with agentic reasoning, tool use, and
code execution. The service does not expose the context-window size.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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>
<!-- 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 control plane for companies of AI
agents.
> - Operators use the attention feed to find decisions that need action.
> - The feed has eleven source kinds, but it has no durable queue or
triage state.
> - The feed also returns every item and lacks decision deadlines,
snooze state, and decision-focused ordering.
> - This pull request adds secure queue sidecars and enriches the
attention feed with triage data, filters, cursor pagination, and
decide-now ranking.
> - The benefit is a bounded feed that can show the most urgent
decisions first without weakening source visibility rules.
## Linked Issues or Issue Description
This pull request replaces the closed
[#10634](https://github.com/paperclipai/paperclip/pull/10634). It
combines that queue foundation with the dependent attention-feed change
as one review unit.
**Subsystem affected**
Database schema, shared contracts, server authorization and REST APIs,
and the UI attention client library.
**Problem or motivation**
The attention feed can contain hundreds of mixed decision items.
Operators cannot group them into durable queues, set a decision
deadline, snooze an item, or request a bounded page ordered by urgency.
The current client must download the full feed on each refresh.
**Proposed solution**
Store queue membership and triage state by stable attention identity.
Re-authorize each source during queue reads and writes. Enrich attention
items with queue, deadline, snooze, expiry, rule, and origin data. Add
activity and queue filters, opaque cursor pagination, decide-focused
ordering, and a decide-now count.
**Alternatives considered**
Adding queue fields to every source would duplicate schema and
authorization logic across eleven source kinds. Client-only filtering
and sorting would still transfer the full feed and would make pagination
unstable.
**Roadmap alignment**
This change improves the core decision-attention surface and operator
oversight. It does not implement the separate general-purpose work queue
milestone in `ROADMAP.md`.
## What Changed
- Added company-scoped queue, membership, triage, and append-only event
tables with actor and run provenance.
- Added queue CRUD, item membership, starter-rule discovery, and
decide-by and snooze endpoints.
- Kept source authorization on each queue mutation, read, and count.
- Added attention fields for expiry, rule, origin agent, queues,
decide-by attribution, and snooze state.
- Added activity date filters, queue filters, opaque cursor pagination,
and configurable page limits.
- Added decide-now ordering by deadline, expiry, severity, and activity.
- Added `decideNowCount` and excluded actively snoozed items from the
default feed.
- Updated the shared and UI client contracts.
- Added focused server, route, OpenAPI, and UI client tests.
## Verification
- `pnpm exec vitest run server/src/__tests__/attention-service.test.ts
server/src/__tests__/decision-queues-routes.test.ts
server/src/__tests__/openapi-routes.test.ts ui/src/api/attention.test.ts
ui/src/lib/attention.test.ts` (72 tests passed)
- `pnpm --filter @paperclipai/db check:migrations`
- `pnpm -r --filter @paperclipai/db --filter @paperclipai/shared
--filter @paperclipai/server --filter @paperclipai/ui typecheck`
- `git diff --check origin/master...HEAD`
## Risks
- The migration adds four company-scoped tables and provenance foreign
keys. Migration numbering and safety checks pass.
- Attention reads can lazily create starter queues and memberships.
Inserts are idempotent, audited, and transactional.
- Cursor validity depends on the filtered feed. The API returns a clear
validation error when the cursor item no longer exists in that feed.
- Queue reads re-check source visibility. This favors correct
authorization over fewer queries.
> 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, model `gpt-5`. The runtime used agentic reasoning,
repository tools, code execution, and test execution. The runtime did
not expose the context-window size.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents escalate work up the org chart (`reports_to`), and operators
pause agents — notably, instance imports pause every agent by default
> - A paused manager does not invalidate the chain (subordinates stay
invokable), so nothing surfaces when an operator unpauses workers but
leaves their manager paused
> - Escalations then dead-letter silently: agent-created issues assigned
to the paused manager sit in a queue nothing will ever run
> - This pull request computes paused ancestors in the existing
org-chain health model and surfaces a non-blocking warning on the agent
read models and detail page
> - The benefit is that the operator learns their escalation paths are
dead before work vanishes into them
## Linked Issues or Issue Description
Fixes#10647 (companion to #10648, which refuses agent-initiated
assignment to paused agents at write time — this PR makes the standing
hazard visible)
## What Changed
- `AgentOrgChainHealth` gains two additive, optional fields:
`pausedAncestors` (paused agents in the `reports_to` chain) and
`escalationWarning` (human-readable, only set when the agent itself can
work — a paused/terminated agent's escalation path is moot). Chain
validity, invokability, and assignability are byte-identical.
- No server route changes needed: the fields flow through every existing
agent read model (list, detail, org chart) since they ride the same
`getAgentWorkEligibility` computation.
- Agent detail page shows an amber "Escalation path is paused" banner
(same visual language as the invalid-chain banner, but non-blocking)
with the warning text naming the paused manager and the two remedies.
## Verification
- `pnpm vitest run packages/shared/src/agent-eligibility.test.ts` — 5
new cases: paused direct manager warns; paused grandparent through a
healthy manager warns; the agent itself paused → no warning (but
ancestors still reported); fully active chain → no warning, empty list;
terminated ancestor keeps the invalid-chain classification without
double-counting as paused.
- Full `@paperclipai/shared` suite (392 tests) and
`agent-eligibility-routes` (54) unchanged.
- `tsc --noEmit` in shared, server, and ui.
## Risks
- Low. Purely additive fields plus one UI banner; no behavior gates on
the new data.
## Model Used
Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — extended
thinking, agentic tool use. 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 merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Execution policies let one agent implement and another review,
cycling through changes-requested → addressed rounds
> - Nothing bounds that cycle: no round counter, no escalation, no
termination signal — two agents can ping-pong indefinitely, especially
when the review's success criteria drift to something the implementer
cannot satisfy
> - On a real multi-agent instance this produced 6+ unattended rounds
(~8 runs) that continued even after the human had merged the PR under
review
> - This pull request counts consecutive agent-initiated
changes-requested rounds and, at a configurable cap, hands the
still-pending review to the responsible human instead of bouncing back
to the implementer
> - The benefit is that unattended review loops terminate in a human
decision instead of burning runs forever
## Linked Issues or Issue Description
Fixes#10643
## What Changed
- `IssueExecutionState.changesRequestedCount` (schema + type, default
0): consecutive agent-initiated changes-requested rounds on the current
stage. Carries through executor resubmissions, resets to 0 on approval,
and resets when a **human** makes the changes-requested decision — the
cap targets unattended agent↔agent ping-pong, never human review.
- `IssueExecutionPolicy.maxReviewRounds` (optional, 1–50, default null →
server default `DEFAULT_MAX_REVIEW_ROUNDS = 3`).
- At the cap, the transition records the reviewer's changes-requested
decision as usual but keeps the stage **pending** with the responsible
human (`responsibleUserId`, falling back to `createdByUserId`) as the
participant: the issue is assigned to that human and the pending review
surfaces through the existing attention/review UI. The human then
approves, requests changes (resetting the counter and handing back to
the implementer), or re-scopes.
- The escalated hold is sticky: transitions from anyone other than the
escalated human no longer re-select a configured agent participant for
the stage (which would have silently undone the escalation on the next
unrelated PATCH). The escalated human's own decisions flow through the
normal participant decision branch.
- Issues with no responsible human keep today's hand-back behavior; the
counter still accumulates so operators can see the churn.
## Verification
- `pnpm vitest run server/src/__tests__/issue-execution-policy.test.ts`
— 8 new cases: round counting on hand-back, count carried through
resubmission, escalation at the default cap, sticky hold across
unrelated transitions, human changes-requested resets the counter, human
approval completes the stage, no-responsible-human fallback, and a
`maxReviewRounds: 1` policy override.
- `pnpm vitest run
server/src/__tests__/issue-execution-policy-routes.test.ts` and the full
`@paperclipai/shared` suite (387 tests) — schema additions are backward
compatible (both fields optional with defaults; persisted states without
the counter parse as 0).
- `pnpm --filter @paperclipai/shared exec tsc --noEmit` and `cd server
&& pnpm run typecheck`.
## Risks
- Behavior change: an agent-only review loop that previously ran forever
now escalates to a human after 3 agent rounds by default. Instances that
want longer loops can set `maxReviewRounds` per policy. Flows where a
human participates are unaffected (human decisions reset the counter).
- Escalation requires a `responsibleUserId`/`createdByUserId` on the
issue; without one, behavior is unchanged.
- Persisted execution states from before this change parse with
`changesRequestedCount: 0` — no migration needed.
## Model Used
Claude Fable 5 (`claude-fable-5`, Anthropic) via Claude Code — extended
thinking, agentic tool use. 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 merge
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators spend most of their time on the issue detail page. They
talk to the assigned agent there through comments.
> - The current page reads as a ticket form. The thread sits below
properties, the composer sits mid-page, and live agent activity renders
as dense transcript logs.
> - Talking to an agent is a conversation. A chat-first layout matches
that mental model better than a ticket form.
> - A layout change this large must not disrupt current users. It needs
a safe opt-in path and full parity with the existing thread features.
> - This pull request adds a chat-style task view behind a new
"Chat-Style Tasks" experiment toggle. The flag is off by default and the
existing page is unchanged when it is off.
> - The benefit is a focused, readable conversation with the agent: live
tool activity folds into compact summaries, the composer stays at the
bottom, and properties, plan, and artifacts move into header tabs.
## Linked Issues or Issue Description
Refs #49 (chat with agents is a much-wanted feature).
Related PRs found in the dedup search:
- #4489 — an earlier, closed attempt to promote the conversation to the
primary surface on issue detail. This PR is a fresh, flag-gated take on
the same goal.
- #8837 — an open PR that proposes a two-column task layout. It
restructures the same page but keeps the ticket paradigm; this PR is
orthogonal because it is opt-in and chat-first.
**Subsystem affected**
UI (issue detail page).
**Problem or motivation**
The issue detail page presents agent conversations as a ticket:
properties first, thread below, composer in the middle of the page, and
raw transcript noise during live runs. Users who mainly converse with
their agents must scroll past chrome to follow the conversation, and
live activity is hard to read.
**Proposed solution**
An opt-in chat-style view of the issue detail page, gated by a new
"Chat-Style Tasks" experiment toggle in Settings → Experimental. With
the flag on, the thread fills the center pane, the composer docks to the
bottom of the viewport, Properties / Plan / Artifacts become header
tabs, live turns show a status pill with the current tool action and
elapsed time, and settled turns collapse to a "Worked · N tools" summary
that expands into per-tool rows. With the flag off, nothing changes.
**Alternatives considered**
Restyling the existing layout in place (rejected: too disruptive without
an opt-out), and a separate chat page beside the issue page (rejected:
splits the task's single source of truth). A per-request lab page
(`/task-chat-lab`, dev-only) was kept for design iteration instead.
**Roadmap alignment**
ROADMAP.md "CEO Chat" wants lighter conversations that still resolve to
real work objects. This PR keeps the core task-and-comments model — it
only changes presentation, opt-in — so it does not duplicate that
planned work.
## What Changed
- New `enableTaskChatRedesign` instance setting, exposed as a
"Chat-Style Tasks" experiment card in Settings → Experimental (shared
feature catalog, validators, server instance-settings service, and UI
settings page).
- New `ui/src/components/task-chat/` component family: chat thread with
turn grouping, agent reply bubbles, live status pill, collapsible turn
summaries with per-tool rows, plan tab with a sticky CTA action bar,
inline interaction cards, per-request mode chips, and a bottom-docked
composer.
- A shared tool taxonomy (`tool-taxonomy.ts`) maps tool names to verbs
and icons; the status pill, tool rows, and the classic transcript view
all use it.
- A transcript adapter converts stored run logs into chat turns; it
dedupes tool-call updates by `toolUseId` so tool counts match the
expanded rows, and it keeps a tool row's first real name when later
generic updates arrive.
- Composer: posts on Cmd/Ctrl+Enter, supports image paste with
object-URL thumbnail previews (revoked on clear/unmount), and uploads
through the issue attachments route.
- `IssueDetail.tsx`: with the flag on, pane tabs move to the header bar,
the header is not sticky, and the chat fills the center; with the flag
off, the previous layout renders unchanged.
- Motion tokens for the new animations live in `ui/src/index.css` with a
`motion-tokens.ts` catalog and a test that keeps the two in sync (the
catalog now also covers the shared enter/exit/swap tokens that the
decision/quicklook block declares).
- A dev-only `/task-chat-lab` page with fixtures and a tweak panel for
motion tuning.
## Verification
- `pnpm typecheck` — clean across the workspace.
- `pnpm check:token-gates` — 3/3 CLEAN.
- `cd ui && pnpm vitest run` — 3,344 of 3,345 tests pass locally. The
one failure is `IssueProperties.test.tsx` monitor-row time formatting,
which is timezone-sensitive: it also fails on unmodified `origin/master`
in a non-UTC timezone and passes with `TZ=UTC`. It is not related to
this change.
- `cd server && pnpm vitest run
src/__tests__/instance-settings-service.test.ts` — 21/21 pass (covers
the new setting).
- Manual: start the dev server, open Settings → Experimental, enable
"Chat-Style Tasks", and open any issue. The thread fills the page, the
composer docks to the bottom, and Properties / Plan / Artifacts appear
as header tabs. Assign an agent and comment to watch a live run: the
status pill shows the current tool action with elapsed time, and the
finished turn folds into a "Worked · N tools" summary. Disable the
toggle and confirm the classic page is unchanged.
- Visual snapshot baselines are intentionally not updated: per
`doc/design/DECISION-SHEET.md`, "Per-change snapshot verification
demoted to dormant (Jul 13 2026)".
## Risks
- The flag-off path goes through the same `IssueDetail.tsx` file, so a
regression there would affect current users. Mitigation: the classic
markup renders through the same components as before behind explicit
flag conditionals, and the full UI suite passes.
- The transcript adapter interprets stored run-log formats, including
legacy entries without `toolUseId`. Malformed logs degrade to generic
tool rows rather than crashing.
- The new view changes no server behavior other than one additive
instance setting; it is additive and default-off. Overall risk with the
flag off is low.
## Model Used
- Claude (Anthropic), model id `claude-fable-5` (Claude Fable 5),
extended thinking enabled, agentic tool use (file editing, shell, test
execution) via Claude Code / Claude Agent SDK.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents can currently perform many mutations directly, while humans
often need a durable review point before cross-issue or destructive
actions occur
> - Existing approvals and issue-thread interactions do not provide a
standalone, reusable object for presenting options, collecting typed
inputs, detecting stale targets, and auditing effect execution
> - The control plane therefore needs a first-class propose mode that
separates an agent's recommendation from the governed mutation it may
cause
> - This pull request adds Decisions v1 across the database, shared
contracts, server execution and telemetry, agent skill guidance, and
operator UI
> - The benefit is that agents can propose multi-option actions safely
while operators get explicit provenance, fail-closed execution,
per-effect results, and a focused attention workflow
## Linked Issues or Issue Description
### Subsystem affected
Cross-cutting: `packages/db`, `packages/shared`, `server`, and `ui`.
### Problem or motivation
Agents need a governed way to propose consequential work without
immediately mutating issues, especially when one choice can affect
several issue trees. Existing approvals and issue-thread interactions do
not provide a standalone object with typed options, target snapshots,
effect-level authorization, expiration, execution outcomes, and reusable
attention-feed presentation.
### Proposed solution
Add first-class Decisions that store options and typed inputs, surface
open proposals in the operator attention feed, validate target freshness
and the origin-agent/operator authorization intersection at decision
time, execute a bounded set of auditable effects, and retain terminal
outcomes. Decisions v1 supports comments, status and assignee changes,
follow-up issue creation, blocker resolution, and issue-tree
cancellation, plus bundle grouping, expiration/dismissal, rule-key
telemetry, and agent-facing API guidance.
### Alternatives considered
- Extend approvals with arbitrary effects: rejected because approvals
represent governed yes/no actions and would become an unsafe generic
mutation envelope.
- Model every proposal as an issue-thread interaction: rejected because
decisions can span several targets and need independent lifecycle,
telemetry, idempotency, and effect results.
- Let agents perform the mutation and ask for retrospective review:
rejected because it removes the pre-execution governance boundary this
feature is meant to provide.
### Roadmap alignment
Aligns with `ROADMAP.md` sections **Agent Reviews and Approvals**,
**Enforced Outcomes**, **MCP Tool Gateway & Apps (governed tool
access)**, and **Activity History** by making explicit decisions,
authorization gates, auditable execution, and terminal outcomes
first-class control-plane objects.
### Additional context
This does not replace existing approvals or issue-thread interactions,
and it does not add an unrestricted generic mutation effect.
## What Changed
- Added company-scoped decision, option, target, and effect-execution
schema plus migration and shared TypeScript/Zod contracts.
- Added decision routes and services for propose, list/get, decide,
dismiss, cancel, target freshness checks, authorization intersection,
idempotency, activity logging, and execution auditing.
- Added rule-key decision telemetry and attention-feed metadata so open
decisions are visible and measurable.
- Added agent skill documentation for proposing and resolving decisions
through the Paperclip API.
- Added the Decisions UI: API client, query keys, inline attention
resolver, bundle grouping, target-issue strip, terminal history,
destructive confirmation, and per-effect result rendering.
- Added server service coverage, DecisionCard state tests, and Storybook
stories for the supported visual states.
## Verification
- `pnpm -r typecheck` — passed.
- `pnpm test:run` — 2,876 passed, 1 skipped, with one unrelated
cross-suite cleanup-order failure in
`heartbeat-responsible-user-invariant.test.ts`; the failing file passes
in isolation (`6/6`).
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-responsible-user-invariant.test.ts` — passed.
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/DecisionCard.test.tsx` — passed (`9/9`).
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/authz-existence-oracle-guard.test.ts
src/__tests__/openapi-routes.test.ts` — passed (`5/5`).
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/decisions-service.test.ts` — passed (`16/16`).
- `pnpm --filter paperclipai exec vitest run
src/__tests__/company-import-export-e2e.test.ts` — passed (`1/1`).
- `pnpm --filter @paperclipai/server typecheck` and `pnpm --filter
paperclipai typecheck` — passed.
- `pnpm build` — passed.
- Rebased-head focused suite — passed (`6` files, `88` tests): shared
decision contracts, Decisions service, OpenAPI routes, startup feedback
export, DecisionCard states, and attention helpers. The follow-up
stale-secondary-target regression passes in the DecisionCard suite
(`10/10`).
- Rebased-head scoped typechecks — passed for `@paperclipai/shared`,
`@paperclipai/db`, `@paperclipai/server`, and `@paperclipai/ui`.
- Rebased-head migration numbering and safety checks — passed after
renumbering the additive migration to `0193` and making it replay-safe
for environments that applied the earlier feature-branch number.
- `pnpm check:token-gates` — passed with all gates clean.
- GitHub PR workflow and Greptile review for
`1f9f7645882d05dfdd9c99377c03a1f53f20e8be` — running after the
stale-secondary-target fix and PR metadata refresh on July 27, 2026.
- `pnpm --filter @paperclipai/ui build-storybook` exposes an existing
Storybook version mismatch (`storybook` 10.4.6 vs
`@storybook/addon-docs` 10.5.0); Decisions stories were validated with
the docs addon temporarily disabled and the tracked config remains
unchanged.
## Risks
- **Migration:** Adds replay-safe migration `0193`; migration numbering
and safety checks pass. The new tables and indexes are additive.
- **Authorization:** Effect execution intersects the proposing agent's
permissions with the responsible user context and fails closed; mistakes
could reject a valid proposal rather than silently over-authorize it.
- **Concurrency:** Target snapshots and idempotency keys protect against
stale or duplicate execution, but reviewers should focus on mixed-effect
partial outcomes and retry behavior.
- **UI:** Decisions are integrated into the existing attention feed
rather than a separate navigation surface, reducing routing risk but
increasing the importance of attention-item metadata compatibility.
> 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 CLI using `gpt-5.6-sol` for final PR preparation, review
fixes, and verification; repository tools and code execution were
enabled, and context-window size is not exposed in this runtime.
- Anthropic Claude Opus 4.8 with 1M context assisted with the Decisions
UI implementation, as recorded in the relevant commits.
## 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 (1M context) <noreply@anthropic.com>
## 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>
<!-- 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>
## 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>
## 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>
## 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>
## 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 merge
## 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 merge
## Thinking Path
> - Paperclip is the open source control plane people use to manage
AI-agent companies.
> - Company skills are reusable capabilities that operators install,
edit, assign, and materialize for agents.
> - Managed local skills currently lack a safe backend operation for
changing their display name and canonical slug/key together.
> - Treating rename as an ordinary save can leave duplicate records,
stale runtime materializations, or agent assignments pointing at the old
key.
> - This pull request adds a company-scoped managed-skill rename
contract, service operation, and REST endpoint with focused
authorization and activity logging.
> - The benefit is an atomic-enough, recoverable rename path that keeps
disk state, database identity, and agent skill assignments synchronized.
## Linked Issues or Issue Description
- Refs #2121
- Problem: managed company skills need a dedicated rename operation
rather than save-time duplication behavior.
- Expected behavior: renaming a managed skill updates its name, slug,
key, source directory, frontmatter, runtime materialization, and
assigned-agent references while preserving version pins.
## What Changed
- Added shared request/result types and Zod validation for managed skill
rename requests.
- Added `POST /api/companies/:companyId/skills/:skillId/rename` with
`skills.edit` policy checks and `company.skill_renamed` activity
logging.
- Restricted renames to Paperclip-managed local skills and added slug,
key, and target-directory conflict handling.
- Moved the managed directory, rewrote only the `SKILL.md` frontmatter
name, updated the database row, and rolled filesystem changes back when
persistence fails.
- Rewrote assigned agents' desired-skill keys while preserving pinned
version IDs and removed stale runtime materialization.
- Added focused route and service coverage for success, no-op, name-only
changes, conflicts, unsupported sources, assignment rewrites,
rollback-sensitive behavior, and runtime cleanup.
- Rejected multiline rename names before they can inject extra
`SKILL.md` frontmatter fields.
## Verification
- `pnpm exec vitest run
server/src/__tests__/company-skills-service.test.ts
server/src/__tests__/company-skills-routes.test.ts` — 106 tests passed.
- `pnpm --filter @paperclipai/shared typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
## Risks
- Filesystem and database updates cannot share one native transaction;
the service stages filesystem changes and explicitly restores the
original directory and markdown when the database transaction fails.
- Renames intentionally reject catalog, remote, project-scanned, and
unmanaged local skills to avoid changing identities owned by external
sources.
- No database migration is required; the endpoint updates existing
company-skill and agent configuration fields.
> 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 agent (exact underlying model ID and
context-window size were not exposed to this runtime), with reasoning,
repository tool use, code execution, and test execution. The rescued
source commit also records assistance from Claude Opus 4.8.
## 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>