Commit Graph

4278 Commits

Author SHA1 Message Date
nickyleach 03af63e58c chore: retrigger pull request checks
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-10 15:26:18 +00:00
nickyleach 8f7422166f docs(server): correct the reviewer-projection claim in the recovery comment
The comment said no code reads responsibleUserId on a queued row. A
run-ledger projection does read it with no status filter, so a queued
recovery run can show a null responsible user in that display. The
projection makes no authorization decision, so the null value is still
safe to keep.

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-10 15:10:41 +00:00
nickyleach 0b81bda287 docs(server): record why the review-participant recovery insert leaves responsibleUserId null
Add a comment above the heartbeat_runs insert in
queueReviewParticipantRecoveryRun. It states that claimQueuedRun and
initializeRunIdentity set the value later, that no code reads it on a
queued row, and that resolving it here would add a throw inside the
release transaction that runs the review-recovery path.

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-10 07:43:33 +00:00
Nicky Leach 0bff1d5cb6
refactor(server): simplify the wake-queue module (#13139)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The server uses a deferred wake queue to release work at the correct
time
> - The wake queue had repeated decisions, helpers, queries, and
recovery data
> - This repetition made the module harder to read and left a drain
invariant implicit
> - This pull request moves pure decisions into the policy layer and
simplifies the queue flow
> - The benefit is a smaller, clearer module with the same behavior and
direct test coverage

## Linked Issues or Issue Description

Refs #13136

**Problem:** The deferred wake queue carried repeated logic across the
application and database layers.

**Expected behavior:** The queue keeps the same wake, release, recovery,
and escalation behavior after the refactor.

**Solution:** Move pure decisions into the policy layer, share repeated
data and predicates, and state the drain invariant in the application
layer.

## What Changed

- Remove the unused database port method and carry the blocked release
notice kind as a typed field.
- Move four pre-drain decisions into pure policy functions with
table-driven tests.
- Resolve the responsible user once in the application layer.
- Use the canonical helper for agent invokability checks.
- Share string helpers and run predicates across the module.
- Split the deferred-wake decision flow and share recovery facts with a
discriminator.
- Bound the drain loop and throw when it processes a wake identifier
twice.
- Rename queue ports to describe their behavior.
- Share row-loading code between stranded-issue escalation adapters.
- Restore the interaction-continuation integration test case.

## Verification

- Run the three wake-queue module test files. They pass 61 cases
locally.
- Run `pnpm check:module-boundaries`. It passes locally.
- Run the `server/` type-check and confirm that no error names the
changed wake-queue module or `heartbeat.ts`.
- Run continuous integration and confirm that `promotes an interaction
continuation after removing a coalesced self-authored comment` passes.

## Risks

The change refactors queue control flow and database adapter boundaries.
The main risk is a behavior change in deferred wake release or recovery.
The new policy tests and the restored integration case cover these
paths. The local environment cannot run the integration test because the
same dependency failure occurs on the base branch.

## Model Used

OpenAI Codex, GPT-5, context window not exposed by the runtime, with
reasoning, tool use, and code execution.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-10 00:03:22 -07:00
Nicky Leach 6dd48cad43
refactor(server): move the release half of the deferred wake state machine into a wake-queue module (#13132)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The heartbeat service releases deferred issue wakes and promotes the
next run
> - The release logic sat in a large service function, which made its
decisions and database effects hard to test
> - A promotion race could reopen an issue without creating the promoted
run
> - Company checks did not protect every read and write, and some
readers used a second transaction connection
> - This pull request moves the release logic into a layered wake-queue
module and closes these race and company-scope defects
> - The benefit is clearer decisions, safer writes, and focused tests
while existing callers keep the same entry point

## Linked Issues or Issue Description

Refs: #10195

## What Changed

- Move the release half of deferred issue execution from `heartbeat.ts`
into `server/src/modules/wake-queue/`.
- Add pure policy decisions with table-driven tests.
- Claim a wake before the reopen write and advance to the next wake when
the claim fails.
- Add company predicates to guarded reads and writes.
- Pass the transaction-scoped issue snapshot to reader ports.
- Keep `releaseIssueExecutionAndPromote` as the public wrapper.

## Verification

- Run `pnpm check:module-boundaries`.
- Run `pnpm exec tsc --noEmit` inside `server/` and compare the result
with the known baseline.
- Run the wake-queue unit and adapter tests in continuous integration.
- Check the promotion-claim ordering, guarded writes, transaction-scoped
reads, and cross-company outcomes.
- Search the pull request diff for internal issue identifiers.

## Risks

- The refactor changes the transaction path for deferred wake release.
- A stale or lost wake claim now skips that wake and continues with the
next queued wake.
- The public wrapper keeps its name and signature, which limits caller
risk.
- Continuous integration must confirm the full server test suite and
build.

## Model Used

OpenAI Codex, GPT-5, exact runtime model version not exposed, context
window not exposed, with shell, Git, and GitHub tool use.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-09 21:01:26 -07:00
Dotta 018ca5daaf
fix: verify ACP Stop and preserve safe continuation (#13119)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task controls coordinate provider execution and queued user
messages.
> - Stop could finish before an embedded ACP provider stopped its tools.
> - A later request could be held for reconciliation without a clear
task response.
> - A restored provider could also retain the stopped run's API
credential.
> - This pull request verifies provider termination and preserves safe
session continuation.
> - Operators can continue known-safe work and see why uncertain work
cannot start.

## Linked Issues or Issue Description

**What happened?**

Stop could leave an embedded ACP provider running. A queued follow-up
followed by “go” could fail before it reached the provider. Task chat
could show a generic missing-response message. Even a restored session
could use the previous run's credential and fail its task update.

**Expected behavior**

Stop waits for confirmed provider termination. A later explicit wake
continues the same compatible session only when recorded actions have
known outcomes. It carries pending comments and the current run's
environment. Uncertain actions retain a visible reconciliation hold.
Composer Stop preserves the existing pause rule: conversation can
continue while paused, but task work requires Resume.

**Steps to reproduce**

1. Start an embedded ACP task.
2. Send a second request while the provider is running.
3. Interrupt the run, then send “go”. Also test composer Stop followed
by Resume work.
4. Check that the request is delivered once and that the provider can
complete the task through the current run's API credential.
5. Repeat with an unfinished write. Confirm that the write stops and
that further execution stays blocked with a visible reason.

**Paperclip version or commit**

Built from source on master at `3bc60dd8b` plus this branch.

**Deployment mode**

Local source build with an isolated embedded PostgreSQL instance.

Refs #11183. Refs #12552. Those changes address recovery after operator
cancellation. This change also covers embedded ACP termination, session
proof, pending-comment delivery, and task feedback.

## What Changed

- Propagate Stop into embedded ACP and wait for bounded adapter cleanup
and provider exit. Retain the actual ChildProcess object for forced
termination on all platforms; never signal a recycled numeric PID.
- Preserve interrupted checkpoints only for acknowledged, local,
persistent sessions with settled reads or no tools. Keep writes,
incomplete actions, and forced termination blocked.
- Restore the same compatible provider session with the current run's
environment. Reject fresh-session fallback for an interrupted
checkpoint.
- Adopt pending comments on the next explicit wake. Stop alone does not
dispatch them.
- Share the execution-blocker rule across dispatch, Resume, and task
detail. Show Stopped or Couldn't start with the recorded reason. Resolve
the stopped agent for the run link, including reviewer runs.
- Keep execution reconciliation holds intact when generic recovery sees
queued comments or healthy child tasks.
- Add process, service, component, and browser regression coverage. Fix
disposable database cleanup and React test settling exposed by the full
suite.

## Verification

- Passed `pnpm -r typecheck`, `pnpm build`, and `pnpm
check:token-gates`.
- Passed all three `acp-stop-continuation.spec.ts` browser journeys.
They use an actual ACP child process and require task completion through
the agent API.
- Passed 165 adapter execution, operator-stop, and child-process control
tests, 17 queued-comment route tests, and 65 tests in the two adjusted
UI suites. Earlier focused recovery, heartbeat, and task-control tests
also passed.
- Manually used the browser to queue a request, Stop, send “go” while
paused, and Resume. The same session answered once and moved the task to
Done with the current run's credential.
- Manually interrupted an unfinished write. Its file size stayed fixed
for five seconds. “Go” showed the reconciliation reason and did not
start another provider prompt.
- Separate live Claude ACP smoke checks confirmed that Stop ended a
disposable local write and that a no-tool interruption could resume the
exact provider session. The browser fixture does not call Drive or
another external app.
- Passed all 5,615 UI tests and 3,090 other workspace tests. The CLI and
general server groups pass with targeted retries: two transient server
failures passed together on retry, and two embedded-database startup
failures passed after removing abandoned shared-memory segments from
this task's completed browser fixtures. All 144 serialized server suites
completed, with 2,189 tests passing after two transient HTTP socket
failures passed on retry.
- Passed all 135 heartbeat process/recovery tests, including a
deterministic regression that failed before the recovery-sweep fix.
- Passed 18 dispatch integration tests, including stopped-reviewer
links, company boundaries, and malformed run IDs.
- Greptile is 5/5 on `7dd170d83`, with zero unresolved review threads.
The security scan and all required CI gates pass for the same commit.

## Risks

- Safe continuation depends on complete tool reporting and a restorable
local provider session. Unknown outcomes remain blocked and require
reconciliation.
- Provider cleanup can take time. A timeout does not grant replay
permission.
- The change adds optional adapter context fields and an optional issue
projection. It does not change the database schema or require a
migration.
- Test cleanup truncates company data only in a disposable test
database.

## Model Used

OpenAI GPT-6, running as Codex with repository tools, code execution,
and browser interaction. The runtime does not expose a more specific
model deployment 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>
2026-09-09 22:06:06 -05:00
github-actions[bot] bd1fdc2887
chore(lockfile): refresh pnpm-lock.yaml (#13128)
Auto-generated lockfile refresh after dependencies changed on master.
This PR only updates pnpm-lock.yaml.

Co-authored-by: lockfile-bot <lockfile-bot@users.noreply.github.com>
2026-09-09 16:45:14 -07:00
Nicky Leach 04a9f89ede
fix(server): bundle the vendored paperclip-runner instead of hand-mirroring its deps (#13121)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The server package is published to npm, but its native-runtime
driver code lives in `packages/paperclip-runner`, a private workspace
package that is never published
> - So the server build vendors the runner's compiled code by copying it
in directly, instead of taking it as a normal npm dependency
> - But `cp -R` only copies code, not `node_modules`, so every npm
package the runner imports has to be re-declared by hand in
`server/package.json` to stay resolvable once vendored
> - That hand mirroring step is silent and easy to forget: it missed
`smol-toml` in #13110, and CI stayed green while production crash-looped
3 seconds into every start (#13116)
> - This pull request keeps the proven `cp -R` vendor step exactly as it
was, and adds a build check that derives the required dependency set
from an esbuild scan of the vendored entry points, failing loudly and
precisely if any package the runner actually needs isn't declared in
`server/package.json`
> - The benefit is the dependency list is now verified against the real
module graph instead of hand-copied, so this exact class of bug cannot
pass a green build again -- without changing how the runner's code is
laid out on disk, which several of its modules depend on for unrelated
filesystem lookups

## Linked Issues or Issue Description

Refs: #13110 (introduced the `smol-toml` import that the vendor step
could not resolve), #13116 (the follow-up fix for a different oversight
in the same PR), #11813 (the same "vendored package installed outside
the monorepo dependency graph loses a runtime dependency" failure shape,
in the Kubernetes plugin installer instead of the server build)

No issue exists yet for this specific incident, so per CONTRIBUTING.md
option (B):

**What happened?**
`packages/paperclip-runner/package.json` added `smol-toml` as a runtime
dependency in #13110. `server/package.json`'s existing convention (see
`acpx`, `ajv`) requires mirroring every runtime dependency the vendored
runner imports into `server/package.json` too, because the server build
copies the runner's compiled `dist/` tree with `cp -R` -- code only, no
`node_modules`. That mirroring step was missed. CI never runs the
compiled server (`node dist/index.js`); it only builds it, type-checks
it, and boots the app in dev mode via `tsx` against source, which never
touches the vendored path. So the PR merged green, and the deployed
server crash-looped in production:
```
Error [ERR_MODULE_NOT_FOUND]: Cannot find package 'smol-toml' imported from
/srv/paperclip/app/server/dist/vendor/paperclip-runner/drivers/codex/codex-startup-trust.js
```

**Expected behavior**
Any npm package the vendored runner code needs at runtime should either
be guaranteed present by construction, or the build should fail with a
clear, actionable error before the change ever reaches a PR -- not
silently pass CI and fail only once deployed.

**Steps to reproduce (the original incident)**
1. Add a new runtime dependency to
`packages/paperclip-runner/package.json` (e.g. a TOML parser) and use it
from a module reachable from the runner's `index.ts` export graph.
2. Do not add the same dependency to `server/package.json`.
3. Run `pnpm build` in `server/` -- it succeeds.
4. Run `node dist/index.js` -- it crashes with `ERR_MODULE_NOT_FOUND`
for the new package.

## What Changed

- **Revision note:** the first version of this PR replaced the `cp -R`
vendor step with an esbuild bundle of the runner's entry points.
Greptile's review correctly caught that this broke packaged
ACPX/OpenCode provider startup: several runner modules resolve sibling
build artifacts via `import.meta.url`-relative filesystem paths (not JS
imports) at whatever depth their source file sits at, and bundling
collapses/rearranges that layout. The current version keeps the file
layout untouched and only adds verification. See the second commit's
message for the full explanation.
- `server/scripts/verify-runner-vendor-dependencies.mjs`: a new build
step that runs esbuild with `write: false` (a pure module-graph scan --
nothing is written to disk) against the runner's two entry points server
actually imports (`index.js`, `testing.js`), with `packages: "external"`
so its metafile reports exactly which npm packages the code needs at
runtime. It fails with a precise, actionable error if any of them isn't
declared in `server/package.json`'s `dependencies`. This is deliberately
more precise than "mirror every dependency the runner declares": running
it against this repo's real manifests shows
`packages/paperclip-runner/package.json` declares dependencies
(`react-markdown`, the codex/opencode CLI packages, ...) that only its
unrelated `./react` and `./browser` export subpaths use -- server never
imports those, so a blanket mirror rule would demand dependencies server
doesn't actually need.
- `server/package.json`: added the new check into the `build` script
(right after the runner is built, before the expensive `tsc`/copy steps,
so it fails fast), and added `smol-toml` (`^1.4.2`, matching
`packages/paperclip-runner/package.json`) to `dependencies` -- the
actual missing piece from #13110. The vendor step (`cp -R
../packages/paperclip-runner/dist/. dist/vendor/paperclip-runner/`) is
unchanged from before this PR.
- Widened `server/vitest.config.ts`'s `include` to also run
`scripts/**/*.test.mjs`, and added
`server/scripts/verify-runner-vendor-dependencies.test.mjs` unit-testing
the pure dependency-diff function (`findMissingVendorDependencies`)
against the exact shape of the `smol-toml` incident, plus a case proving
an unreachable dependency (like `react-markdown`) is correctly never
flagged.
- Updated `server/src/__tests__/server-package-build-script.test.ts`'s
existing build-script assertions to match.

## Verification

- `node --check` on the new script -- syntax OK. `node -e` JSON-parsed
the edited `package.json` files after every edit.
- Unit-verified `findMissingVendorDependencies` directly against:
nothing missing, one missing (the `smol-toml` shape), and multiple
missing with stable sort order.
- Ran the actual check against this repo's real
`packages/paperclip-runner/package.json` and `server/package.json` (via
a standalone `node` invocation, since `pnpm build` needs a Rust
toolchain this sandbox doesn't have -- see below) to see its real
output. It correctly reported `smol-toml`, `acpx`, and `ajv` as already
satisfied, and did **not** flag `react-markdown`, `remark-gfm`,
`json-schema-to-ts`, `opencode-ai`, `@openai/codex`, or the
`@agentclientprotocol/*` packages -- confirming the "reachable from
index.js/testing.js" scoping works as intended and doesn't demand
dependencies server doesn't need.
- Built a fixture tree at a real filesystem location (not just
in-process) mimicking `packages/paperclip-runner`: a manifest declaring
both a reachable dependency (`smol-toml`, actually imported by the
fixture's `dist/index.js`/`testing.js`) and an unreachable one
(`react-markdown`, declared but never imported). Copied the real script
next to a fixture `server/package.json` and ran it as its own process
(`node server/scripts/verify-runner-vendor-dependencies.mjs`), twice:
- `smol-toml` missing from the fixture's server dependencies → the
script throws with the exact intended message and exits 1.
- `smol-toml` present, `react-markdown` absent → the script exits 0,
proving the unreachable dependency is correctly never flagged.
- Not verified locally: the real `packages/paperclip-runner` build, and
therefore the check running end-to-end against its true
`dist/index.js`/`dist/testing.js`. This sandbox has no Rust toolchain
(the runner's own build compiles a Cargo binary) and an incomplete
workspace install. CI's `Build` job (`.github/workflows/pr-trusted.yml`)
runs the real thing; I'll watch it on this PR.

## Risks

- The check's precision (scoping to what's reachable from
`index.js`/`testing.js`, rather than every declared runner dependency)
means a dependency that becomes reachable through some *other* export
subpath server starts importing later would need this check's
entry-point list updated too. That list is a 2-line array in the script
with a comment explaining why, and matches the only two paths server/src
actually imports today (verified by a repo-wide search).
- This only changes a build-time check; the actual vendored file layout
(`cp -R` of the runner's whole compiled tree) is byte-for-byte the same
as before this PR, so there's no behavioral change to the running server
beyond `smol-toml` now being present as intended.
- I could not exercise the real Rust-backed build locally (no Cargo in
this sandbox); see Verification. I am relying on CI's `Build` job to
confirm this end to end and will fix forward if it surfaces something
the fixture-based testing didn't.

## Model Used

Claude Sonnet 5 (`claude-sonnet-5`), via Claude Code. Standard
(non-extended) reasoning mode, with tool use (Bash, Read, Edit/Write,
`gh`) for repository exploration, local esbuild-based verification
against hand-built fixtures, and PR authoring. No extended thinking
mode.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass — see Verification: full
local verification was not possible (no Rust toolchain, incomplete
workspace install in this sandbox); watching CI's `Build` job on this PR
to confirm.
- [x] I have added or updated tests where applicable
- [ ] I have updated relevant documentation to reflect my changes — no
user-facing docs describe this internal build step; none needed
updating.
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green — pending, will monitor.
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups —
addressed the first review round; watching for re-review.
- [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 Sonnet 5 <noreply@anthropic.com>
2026-09-09 16:38:17 -07:00
scotttong 2d45f42e47
fix(ui): refine mobile task surfaces (#13122) 2026-09-09 16:35:38 -07:00
Tonio 5488a79eb5
ci(docker): build each architecture on a native runner instead of QEMU (#12821)
## Thinking Path

A fleet rollout failed with `image_manifest_not_found` for a commit that
had merged and gone green. Tracing that back: the fleet resolves
releases against the `-cloud` image, that image comes from `docker.yml`,
and `docker.yml`'s runs on master have been reading `cancelled` for a
long stretch. The cloud half was fine; the production half was hanging
and taking the run down with it — and, because a run holds the
concurrency slot for its whole duration, starving later commits of a
build at all.

## Linked Issues or Issue Description

No tracking issue — described inline, per CONTRIBUTING.md.

**What's wrong.** `build-and-push` builds `linux/amd64,linux/arm64` on
an x86 runner, so arm64 runs under QEMU. It hangs there —
deterministically, in the same step:

```
#111 [linux/arm64 build  8/10] RUN pnpm --filter @paperclipai/server build
```

…then emits nothing until `timeout-minutes: 60` kills it. Three
consecutive runs on 2026-09-04, silent for **38, 43 and 45 minutes**
respectively. The amd64 leg reached `production 5/5` minutes earlier in
every one.

**Why it stayed hidden.** A timed-out job is reported by GitHub as
**cancelled, not failed**. The run conclusion reads "cancelled", which
looks like supersession rather than breakage, so the production image
quietly stopped publishing.

**The knock-on.** A run that burns the full hour holds the top-level
concurrency slot for that hour. `cancel-in-progress: false` keeps
exactly one pending slot, so merges arriving faster than one an hour
supersede each other while queued. Sampling the last ten master commits,
**five produced no image at all** — their Docker runs have zero job
records because they never started.

**Expected.** Both architectures publish, and a commit merged during a
busy period still gets an image.

## What Changed

`build-and-push` becomes a two-leg matrix, each on a runner of its own
architecture:

| platform | runner |
|---|---|
| `linux/amd64` | `ubuntu-latest` |
| `linux/arm64` | `ubuntu-24.04-arm` |

Each leg pushes **by digest** (`push-by-digest=true`, untagged), and a
new `merge-and-push` job names the digests into one manifest list with
the real lane tags. Nothing is publicly tagged until the merge, so a
half-published multi-arch image is never a pullable state.

Two supporting changes:

- **Per-arch BuildKit cache refs** (`:buildcache-amd64` /
`:buildcache-arm64`). Separate runners sharing one ref would overwrite
each other on every build.
- **The PID-1 orphan-reaping check moves to the merge job**, since that
is where a tagged, pullable image first exists. It still runs against
the pushed image rather than a local build, for the same reason as
before.

**arm64 is kept, not dropped.** The cloud variant is amd64-only and can
be — managed hosts are amd64. This is the self-hosted image and ARM
hosts consume it, so dropping arm64 would break them. GitHub-hosted
arm64 runners are free for public repositories, which this is.

`build-and-push-cloud` is untouched. It was already `platforms:
linux/amd64` and has been succeeding in ~14 minutes throughout — that is
why `-cloud` images exist at all.

## Verification

Parsed the workflow and asserted its shape (jobs, matrix, `needs`, step
order, that the cloud job is unchanged). The artifact actions are pinned
by SHA with version comments, matching the repo's dominant convention —
`upload-artifact` v7 and `download-artifact` v8, the same pins used
across the other workflows; v8 is required for the `pattern` /
`merge-multiple` inputs the merge job uses.

**This PR's CI does not exercise the change.** `docker.yml` triggers on
master and tag pushes, never on pull requests — deliberately, since it
publishes release images. The first real run is after merge, so the
check is: the next master push produces a `Docker` run whose
`build-and-push (amd64)`, `build-and-push (arm64)` and `merge-and-push`
jobs all succeed, and whose conclusion is `success` rather than
`cancelled`.

## Risks

- **Not testable before merge**, per above. If the matrix is wrong the
next master push fails loudly rather than silently — which is already
better than the current state, where the failure mode is an invisible
"cancelled".
- **First use of `ubuntu-24.04-arm` in this repo.** No other workflow
uses an ARM runner. They are free for public repos, but if the label is
unavailable the arm64 leg will fail to schedule and the merge will not
run — no image, same as today, and visible.
- **Digest-push changes the publish shape.** Between the legs finishing
and the merge running, digests exist untagged in ghcr. Anything watching
for tags sees no intermediate state; anything enumerating untagged
manifests will see more of them.
- **Cache refs change name**, so the first build after this lands is
cold on both legs and will be slower than steady state.
- **Does not fix the underlying QEMU hang** — it avoids it. If arm64
ever has to build under emulation again, the same stall is presumably
still there.
- No application code, schema, server or persistence change.

## Model Used

Anthropic Claude — Opus 5, model ID `claude-opus-5`, run through Claude
Code.

Extended thinking enabled. Tool use throughout: GitHub Actions API to
correlate run/job outcomes and read build logs, `git` for ancestry
checks, and a YAML parser to validate the rewritten workflow's
structure.

## Checklist

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

A workflow change has no unit test to add, and `docker.yml` cannot run
on a PR; the verification section states what to check on the first
master run instead.

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

---------

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
Co-authored-by: Devin Foley <devin@paperclip.ing>
2026-09-09 15:56:28 -07:00
Dotta 3bc60dd8bf
fix(adapters): probe Git context in the remote workspace (#13116)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Remote environments realize the workspace at a provider-owned path.
> - Run startup probes Git and network context before it starts the
agent.
> - The probe used the controller path inside the remote environment.
> - A missing directory stopped the run before any provider work began.
> - This pull request uses the remote execution target's working
directory.

## Linked Issues or Issue Description

Refs #13094, which introduced this probe. Related: #8997 and #10419
address other workspace-directory handoffs; this patch fixes the newer
Git-context probe.

**What happened?**

Remote runs failed with `setup_failed: Could not read execution-target
Git context`, including tasks that did not use Git. The provider shell
could not enter the controller's workspace directory.

**Expected behavior**

Startup must inspect Git and network context in the realized remote
workspace.

**Steps to reproduce**

1. Select a remote sandbox whose workspace is
`/home/daytona/paperclip-workspace`.
2. Start a task whose controller workspace is under
`/paperclip/instances/default/workspaces/`.
3. The startup probe exits when the controller directory does not exist
in the sandbox.

**Paperclip version or commit**

Reproduced on `622376e99` and in the regression test before this patch.

**Deployment mode**

Docker controller with a remote Daytona sandbox. The same helper also
serves SSH targets.

## What Changed

- Use `remote.remoteCwd` for the remote Git-context probe.
- Cover a missing controller directory in both credential modes.
- Verify that SSH reads Git metadata from the remote workspace even when
the caller directory exists.

## Verification

- Before the fix, both new missing-directory tests failed with the
reported error.
- The launcher environment suite passes: 18 tests.
- The adapter-utils suite passes: 1,085 passed, 11 skipped.
- Adapter-utils typecheck passes.
- A disposable sandbox with the affected deployment's image reproduced
the old `cd` failure and passed with the corrected helper. The sandbox
was deleted afterward.
- Full repository typecheck and build pass locally. All CI gates pass at
`61b522c`: regular test shards, serialized server suites, browser
shards, Runner verification, build, policy, and security checks.
- The first CI Build attempt hit a Runner suspension-acknowledgment
timeout outside the changed code. The affected 13-case recovery group
passes locally with its Rust fixtures built, and the unchanged CI job
passed on its single retry.
- The full serial local test run was stopped in favor of the complete CI
matrix; it is not claimed as a local pass.

## Risks

The probe now uses the provider-resolved target directory for sandbox
and SSH execution. Local execution keeps its existing directory. There
are no schema or API changes. Existing local credential and Git metadata
tests pass.

## Model Used

OpenAI GPT-6 in Codex, with tool-assisted code analysis, implementation,
and live and automated testing. The exact model identifier and
context-window size are not exposed by this session.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-09 16:52:11 -05:00
Devin Foley 01ad858492
ci: raise the multi-arch Docker publish timeout to 120 minutes (#13114)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The Docker workflow publishes the server images that all deployments
pull, including the `sha-*` images that downstream consumers deploy
> - The `build-and-push` job builds for linux/amd64 and QEMU-emulated
linux/arm64, and that build now takes more than its 60 minute job
timeout
> - Every run dies at the timeout, and each doomed hour-long run holds
the per-ref concurrency slot, so queued master pushes supersede each
other and no image publishes at all
> - This pull request raises the multi-arch job timeout to 120 minutes
> - The benefit is that image publishing works again, with headroom for
the build to grow

## Linked Issues or Issue Description

Related: #12821 replaces the QEMU-emulated arm64 build with native
runners — that is the durable fix for the build duration itself. This PR
is the immediate unblock so images publish again while #12821 lands.

No existing issue for the outage. Description follows the bug report
template:

**What happened?**

The `docker.yml` `build-and-push` job hits its 60 minute
`timeout-minutes` cap on every run. The last fully successful
`docker.yml` run was September 2. Since then almost every run ends
`cancelled`: the multi-arch build is killed at the timeout, and runs
queued behind it are superseded by newer master pushes before they can
start. The amd64-only `build-and-push-cloud` job often still succeeds
inside those cancelled runs, which masked the breakage.

**Expected behavior**

Every master push and canary tag dispatch publishes its `sha-*`
production and cloud images, and the `promote_canary_channel` job runs.

**Steps to reproduce**

Look at the runs of the Docker workflow on master: `gh run list
--workflow docker.yml --branch master`. Nearly every run since September
5 ends `cancelled` or `failure`. Open a cancelled run: the
`build-and-push` job runs for 61+ minutes and its "Build and push" step
ends `cancelled` at the job timeout. The last runs that succeeded
(September 2) took 39 to 54 minutes for the same job.

## What Changed

- Raise `timeout-minutes` on the `build-and-push` job from 60 to 120,
with a comment that explains why. The amd64-only `build-and-push-cloud`
job keeps its 60 minute cap.

## Verification

- `actionlint .github/workflows/docker.yml` reports no issues in this
change (only pre-existing info-level shellcheck notes in untouched
steps).
- Compared job durations across the last successful runs (39-54 minutes)
and the recent timeout kills (61+ minutes) to confirm the cap is the
failure cause.
- After merge, the next master push should produce a `docker.yml` run
that completes with both build jobs green.

## Risks

Low risk. The change only gives the existing build more time. A
genuinely hung build now occupies a runner for up to 120 minutes instead
of 60. The slow arm64 emulated build itself is worth a separate look
(native arm runners or splitting the platforms), but that is a larger
change than this outage fix.

## Model Used

Claude (Anthropic) — Fable 5 (`claude-fable-5`), extended thinking,
agentic tool use via Claude Code.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass (no code paths changed;
workflow linted with actionlint)
- [x] I have added or updated tests where applicable (not applicable for
a CI timeout value)
- [x] I have updated relevant documentation to reflect my changes (the
workflow comment documents the rationale)
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-09-09 14:34:45 -07:00
github-actions[bot] 7cf9a37796
chore(lockfile): refresh pnpm-lock.yaml (#13106)
Auto-generated lockfile refresh after dependencies changed on master.
This PR only updates pnpm-lock.yaml.

Co-authored-by: lockfile-bot <lockfile-bot@users.noreply.github.com>
2026-09-09 20:53:36 +00:00
Dotta 3b550c80fa
fix(codex): correct startup trust, history reads, and resume usage (#13110)
## Thinking Path

> - Paperclip runs Codex locally and in remote sandboxes.
> - The runner must preserve startup configuration and session identity.
> - Missing project trust can disable repository configuration.
> - Full-history requests use deprecated provider fields.
> - Resume usage describes old work and must not become new run usage.
> - This change corrects startup trust, state reads, and usage
classification.

## Linked Issues or Issue Description

**What happened?**

Normal Codex runs could show repository-trust and history-deprecation
warnings.
Resume could report the preceding turn's token snapshot as a late-turn
warning.
The historical last-usage value could also be attributed to the new run.

**Expected behavior**

Trust the server-selected startup root in isolated configuration. Read
lightweight
provider state and paginated evidence. Use historical cumulative usage
as a
baseline without a new charge or user-facing warning.

**Steps to reproduce**

1. Start a native Codex task in a selected repository.
2. Finish the turn and resume the provider thread.
3. Inspect provider notices, history requests, and per-run usage.
4. Repeat startup and cold resume inside a Daytona sandbox.

**Paperclip version or commit**

Codex CLI 0.153.4 is the pinned runtime and reproduced baseline.
Replayed onto master at 6abeb6733. Related authority work: Refs #13092.
This PR retains its startup cleanup and protocol-integrity checks.

**Deployment mode**

Local source checkout and disposable Daytona sandbox.

## What Changed

- Classify the exact historical resume usage event before the generic
stale-turn warning.
- Persist cumulative usage baselines across recovery of the same run.
- Use excludeTurns on resume and lightweight thread reads.
- Page turn metadata and selected turn items with cursor and identity
validation.
- Reject unsupported or incomplete history instead of guessing that
execution is idle.
- Trust the startup execution root on its host, including Git worktree
trust keys.
- Start Codex in that root and retain the selected sandbox profile on
later turns.
- Keep unrelated isolated configuration and Codex's separate hook trust
policy.
- Add Rust, TypeScript, accounting, native integration, and local
run-log documentation.

## Verification

- Codex and native-transport TypeScript: 333 passed before PR replay.
- Adjacent OpenCode/ACPX driver and accounting tests: 49 passed.
- Rust library, serialized: 226 passed. Native Codex integration: 72
passed, 1 ignored, plus two pagination regressions.
- Repository typecheck and build passed. All repository test groups have
passing coverage after fixture and resource retests; the initial
monolithic command was not clean.
- Fresh real Codex native browser tasks returned correct answers without
the three targeted notices. Answers persisted after refresh and restart.
- Real same-thread TypeScript driver tests passed locally and in
Daytona, including cold resume, configuration, skills, and an approved
harmless hook.
- Local usage summed to 64,607 tokens. Daytona usage summed to 42,737
tokens. Each sum matched its final session total exactly.
- See doc/plans/2026-09-09-codex-integration-acceptance.md for the scope
and limits of the live tests.
- After replay onto current master and review fixes: 334 Codex, backend,
and live-session tests passed, including checkpoint serialization and
real-runner process restart. TypeScript checks passed.
- The native Codex integration run passed 83 tests; the large lineage
test passed separately with the release runner (its debug build exceeded
the test deadline).
- All GitHub checks passed on the final PR head. Greptile is 5/5 with no
unresolved review threads. CI regenerates the lockfile for the added
TOML dependency, per repository policy.
- The first server shard hit a timing-dependent duplicate-key failure in
the unchanged artifact-document concurrency test. Its focused 11-test
suite passed locally. One CI retry on the same head passed all 103 files
and 1,405 tests (2 skipped): [retry
result](https://github.com/paperclipai/paperclip/actions/runs/34398832930/job/102631274667).

## Risks

- Trust applies only to the server-selected startup root and isolated
configuration. Sandbox and tool permissions remain authoritative.
- Codex still requires approval of individual hook hashes. This change
does not bypass that policy.
- Providers without the required history APIs fail explicitly.
- Daytona acceptance used the production TypeScript driver. Remote
Paperclip UI and remote Rust execution were not tested.
- No new public API, database state, recovery policy, or UI control is
included.

## Model Used

OpenAI Codex, GPT-6 (`gpt-6-astra`). Used for reasoning, code edits,
tool use,
and test execution. The exact context-window limit is not exposed in
this
session. Real-provider acceptance used Codex CLI 0.153.4 with
`gpt-5.6-sol`.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-09 15:35:18 -05:00
Dotta ca96e1eb0a
fix(runner): keep streaming after task completion tools (#13108)
Keep receiving provider events after paperclip_finish, drain pending event persistence, and select the final assistant answer after the provider turn ends. Preserve cancellation, failure, and governed-wait behavior.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-09 15:22:00 -05:00
Dotta 5cb4f061dd
fix(ui): simplify provider notices and hide completion calls (#13109)
## Thinking Path

> - Paperclip shows agent work in task feeds.
> - Native tools and provider notices appear in that feed.
> - A routine completion call adds no useful action for the user.
> - A provider notice needs readable text to explain its warning.
> - This change hides the completion call and displays the notice
summary.
> - Saved run events remain available for inspection.

## Linked Issues or Issue Description

**What happened?**

The feed showed paperclip_finish as a normal tool call. Provider notices
showed
a generic name or a large key-value table instead of a clear warning
message.

**Expected behavior**

Hide the routine completion call from the task feed. Show a warning
icon,
a short severity heading, and the full notice summary across the row.

**Steps to reproduce**

1. Open a native Codex task that calls paperclip_finish.
2. Inspect its tool activity in the task feed.
3. Inspect a run with a repository-trust provider notice.

**Paperclip version or commit**

Reproduced on the implementation checkout. Replayed onto master at
6abeb6733.
The search found no duplicate PR for this display change.

**Deployment mode**

Local source checkout with native task feeds.

## What Changed

- Hide paperclip_finish calls and results in task-feed adapters.
- Preserve the raw events for run-log inspection.
- Carry provider-notice text into the transcript.
- Render notices with an icon, severity heading, and full-width summary.
- Cover the display and transcript mapping with regression tests.

## Verification

- The affected UI tests and the token gates passed on the implementation
checkout.
- Browser inspection confirmed the warning text is readable and
completion calls are hidden.
- Repository typecheck and build passed. Repository test groups passed
after resource retests.
- On this PR branch, 123 focused UI tests and the token gates passed.
All GitHub checks passed; Greptile is 5/5 with no unresolved threads.

## Risks

- The task feed hides one known internal tool. Raw run events remain
unchanged.
- Long warning text must wrap within the available width.
- No provider warning classification, accounting, or recovery behavior
changes here.

## Model Used

OpenAI Codex, GPT-6 (`gpt-6-astra`). Used for reasoning, code edits,
tool use,
and test execution. The exact context-window limit is not exposed in
this
session. Real-provider acceptance used Codex CLI 0.153.4 with
`gpt-5.6-sol`.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-09 15:06:20 -05:00
Dotta 6abeb67334
feat: add opt-in chat provider and data foundation (#13100)
Add dormant provider contracts, qualified patched adapters, tenant-scoped persistence and lifecycle ownership without activating chat routes. Preserve the experimental integration as dependent PR #13038.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-09 13:49:12 -05:00
Nicky Leach 7d84b183fb
test(heartbeat): pin 8 uncovered branches of the deferred issue-execution wake state machine (#13103)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The server manages issue execution, queued comments, and agent wake
state
> - Deferred wakes can pass through several failure, hold, rollback, and
steering branches
> - These branches had no direct tests, so a later change could fail
without clear evidence
> - This pull request adds characterization tests for eight uncovered
branches
> - The benefit is clear test evidence for future changes to deferred
issue execution

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The server behavior for deferred issue-execution wakes and
queued-comment steering transactions.

**Current behavior**

The code handles missing agents, cross-company agents, pause holds,
rollback, steering outcomes, and invalid reorder sets. These branches
had no direct tests.

**Proposed behavior**

Keep the current behavior and test each branch against the current
implementation.

**Reason and benefit**

The tests make silent behavior changes visible. They also protect the
wake queue while later work moves this logic into a dedicated module.

**Breaking changes**

None. This pull request changes no production code and no runtime
behavior.

Related public pull requests: #12671, #11168, and #10199.

## What Changed

- Add tests for missing and cross-company deferred agents.
- Add tests for pause-hold promotion and cancellation.
- Add a test for atomic rollback when the responsible user cannot
resolve.
- Add tests for successful, timed-out, and rejected native steering.
- Add a test for invalid queued-comment reorder input.

## Verification

- Run `server/src/__tests__/heartbeat-comment-wake-batching.test.ts`
against an embedded Postgres database.
- Run `server/src/__tests__/issue-queued-comments-routes.test.ts`
against an embedded Postgres database.
- Confirm that all eight new tests pass.
- Confirm that the pull request CI checks pass.

## Risks

Low risk. The diff changes test files only. It does not change
production code, database schema, API behavior, or runtime behavior.

## Model Used

Codex, GPT-5, tool use and code review support. The model did not author
production code.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-09 11:36:21 -07:00
Dotta 2991a59b17
fix(adapters): prevent engine fallback and preserve usable runtime defaults (#13105)
## Thinking Path

> - Paperclip manages agents that must write work and report task
outcomes through its API.
> - Local adapters select an execution engine and its permission
settings.
> - A higher ACP Node requirement can make an unchanged installation
lose access to its default engine.
> - The adapter then silently selects CLI, which can change permissions
and block API access.
> - This pull request keeps the engine choice fixed and reports missing
prerequisites before work starts.
> - It also gives explicit Codex CLI runs usable defaults and keeps
managed services on a supported Node runtime.

## Linked Issues or Issue Description

Refs #12215. Related changes: #11792 raised the Node requirement; #13094
addressed separate runner networking behavior. This change fixes the
engine-selection and managed-launcher paths.

**What happened?**

An unchanged agent could switch from ACP to CLI after an upgrade. Codex
CLI then used read-only permissions with networking disabled. The run
could finish without updating its task. Repeated recovery attempts used
the same unavailable setup. Managed updates also skipped the Node check
and did not refresh old launchers.

**Expected behavior**

An unavailable engine must fail with a clear setup error. It must not
silently select another engine. Explicit CLI runs must be able to write
workspace files and call the API unless the operator configures stricter
settings. Managed updates must validate Node and keep child tools on
that runtime.

**Steps to reproduce**

1. Run an ACP-default agent under Node 22 after the ACP minimum rises to
24.11.
2. Leave the engine unset and disable the approval/sandbox bypass.
3. Observe the old adapter select CLI and fail to write task disposition
through the API.
4. Start a managed service with an old launcher and a supervisor PATH
that selects a different Node for child tools.

## What Changed

- Remove automatic engine fallback for Codex, Claude, Gemini, and Kimi.
Check prerequisites for default and explicit ACP selections.
- Return a configuration error with proof that provider work did not
start. Stop automatic continuation retries for this error.
- Enable Codex ACP workspace networking at the actual turn boundary.
Upstream mode presets otherwise force it off even when config.toml
enables it. Preserve explicit network denial and read-only mode.
- Set workspace-write and network access defaults for explicit Codex CLI
runs. Preserve explicit sandbox modes, profiles, and network
restrictions.
- Pin the validated Node directory in managed launcher PATH. Refresh
legacy launchers during installs and npm/Git updates.
- Reject updates on unsupported Node. Keep update checks, dry runs, and
rollback available.
- Synchronize the qualified Codex ACP executable identity across server,
TypeScript runner, Rust runner, and provider-pack launch paths.
- Add regression tests and update engine and installation documentation.

## Verification

- [Full CI passed on the final
head](https://github.com/paperclipai/paperclip/actions/runs/34387099695):
typecheck, build/native runner verification, all general and serialized
test shards, all browser shards, release registry, canary dry run, and
policy checks.
- Greptile: 5/5 on `2c1d6e2815830a5cd39e36c8a082cc0c4441b6c0`, with no
unresolved review findings. Security gates are green.
- Full workspace typecheck and build also passed locally. The final
deployed Linux build passed.
- Full Codex, Claude, Gemini, and Kimi source test suites: 804 passed, 2
skipped. Installer, updater, and launcher tests: 47 passed. Installed
ACP turn-boundary tests: 3 passed. ACP packaging tests: 14 passed.
Focused recovery classification tests also passed.
- Real Linux Codex CLI runs, both fresh and resumed, wrote a workspace
file and reached the control-plane health API with the new defaults.
- Explicit read-only and network-disabled control probes retained those
restrictions.
- A real ACP run on the final deployed Linux build wrote a file and
reached the control-plane API with HTTP 200, without engine fallback.
The same probe failed DNS before the turn-policy patch.
- Executable-identity and installed-policy contracts: 12 passed.
Affected native server tests: 197 passed. Runner factory tests: 21
passed. Rust qualification and native provider integration tests: 11
passed.
- Deployed the production changes to a Linux service on Node 24.20 after
a verified database backup. Health, bootstrap readiness, static UI,
executable/cwd identity, and guarded restart checks passed. The restart
lost no runs.
- Corrected stale Kimi skill-default and Gemini remote-archive fixtures;
both suites pass.

## Risks

- Default or legacy auto engine settings now fail when ACP is
unavailable. Operators who intend to use CLI must select it explicitly.
- Codex CLI now permits workspace writes and networking by default, and
ACP workspace-write turns permit networking by default. Explicit
operator sandbox settings remain authoritative.
- Old managed launchers keep their pinned Node until they are
reinstalled under a supported runtime. An old updater cannot repair
itself; the documentation gives the current installer command.
- Custom service wrappers and global/source installations must configure
their runtime PATH. No database migration is required.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, repository inspection,
shell execution, and test tools. The exact serving model identifier and
context-window size are not exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-09 13:27:24 -05:00
Dotta 8cfd30fb07
feat(ui): add composer Stop and simplify task controls (#13104)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The task composer is where operators direct running agents.
> - Operators need to stop work without leaving the conversation.
> - Existing pause controls already hold task trees and interrupt both
runner types.
> - This pull request connects the composer to those controls and
removes repeated feedback.
> - Operators can pause work quickly and still queue messages while
agents run.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Task pause, resume, and cancellation in the task page and composer.

**Current behavior**

The empty composer cannot stop a running task. Task controls require
extra confirmation and reason text. Pause can show several notifications
for the task already on screen.

**Proposed behavior**

Show Stop while this task runs and the composer is empty. Text or
attachments switch it to Send. Stop and the menu use the same manual
pause hold. Parent pauses include descendants. Keep task cancellation in
the menu with a compact confirmation. Show one quiet pause row and gray
cancelled-run details.

**Reason and benefit**

Operators can interrupt execution with one click. Drafts and queued
messages keep their existing behavior. The UI waits for actual
termination, including native cancellation acknowledgment.

**Breaking changes**

No endpoint, schema, or task-status change. Pause no longer asks for
confirmation or a reason. Resume now honors the existing wake-agents
option. Task notifications are suppressed for the task and subtree
currently in view.

Related UI work: #8228 changes navigation and composer shortcuts. This
PR covers execution controls. No duplicate Stop-button PR was found. The
change improves existing controls and does not duplicate a roadmap
milestone.

## What Changed

- Add Stop, pending feedback, duplicate-click protection, and inline
errors to the composer.
- Share the pause mutation across the composer, active-run controls, and
menu.
- Poll affected runs after a pause request. Require native cancellation
acknowledgment.
- Remove pause confirmation and shared reason fields. Reduce cancel
confirmation to its task count and actions.
- Honor wake-agents for executable tasks only. Preserve the pause when
recovery review is needed; show partial wake failures inline.
- Preserve explicit legacy reconciliation decisions while their
continuation waits for dispatch.
- Suppress notifications for visible task trees. Use quiet pause and
cancellation feedback.
- Add interactive stories using production controls and native/legacy
end-to-end tests.

## Verification

- User reviewed the running feature and revised Storybooks in the
browser.
- Rebased focused checks passed: 295 original targeted tests, 161
updated route/page/notification/status tests, and 26 recovery
integration tests.
- Both isolated runner journeys pass on the final revision (1.7
minutes). Coverage includes queueing, parent and child interruption,
persisted holds, no automatic continuation, reconciled resume,
cancellation, terminal exclusions, and no Stop toast.
- Native coverage uses real runnerd with a deterministic provider
fixture. Legacy coverage checks actual process termination. Live
hosted-provider execution was not tested.
- Repository typecheck and build, Storybook build, and token gates
passed after rebase. The final server typecheck/build also passed.
- The broad local run completed its general-server stage with 7,219
passing tests, 48 skipped, and two failures from cached pre-fix source
and a stale native provider fixture. Both failed tests pass in fresh
final-head reruns after rebuilding the fixture; the script did not
continue to its later local stages. CI runs all test groups on the final
revision.
- Final revision: all 31 applicable CI checks passed; Storybook visual
regression was skipped by its workflow conditions. Greptile: 5/5, zero
unresolved comments.
- Review `Tasks / Execution Controls` in Storybook. Type and clear a
draft, stop a run, expand cancellation details, and test the menu on
desktop and mobile.

## Risks

- Stop pauses descendants for a parent task. This is the existing pause
contract.
- A held task can remain active if interruption fails. The UI shows an
error instead of claiming termination.
- Resume can start multiple assignees when wake-agents is selected.
Backlog, blocked, and terminal tasks stay excluded. Existing execution
reconciliation remains mandatory where required; Resume never invents
action-outcome evidence.
- Notification suppression uses the visible task and cached subtree.
Notifications for unrelated work remain enabled.

## Model Used

OpenAI GPT-6 through Codex. The exact runtime snapshot and
context-window limit are not exposed in this session. Used reasoning,
tool calls, code execution, and browser 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
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-09 12:18:56 -05:00
Dotta 622376e995
fix(ui): remove action buttons from agent list (#13101)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The agents list helps operators scan names, roles, and status.
> - Each row also shows task, heartbeat, trace, pause, and overflow
controls.
> - These controls add a second line to every row and crowd the list.
> - This pull request removes the action bar from list rows.
> - Operators can still use agent detail pages for those actions.

## Linked Issues or Issue Description

Related: #7543 introduced list actions. #13011 moved the row actions
onto a separate line. No duplicate fix was found.

**What happened?**

The `/agents/all` list shows Assign Task, Run Heartbeat, Run with
provider trace, Pause or Resume, and an overflow menu below each agent.

**Expected behavior**

The agent list should show agent information without this action bar.

**Steps to reproduce**

1. Open `/agents/all` in an organization with agents.
2. Select the list view.
3. Inspect the controls below each agent name.

**Paperclip version or commit**

Reproduced in source at fac07b42a.

**Deployment mode**

Board UI. The change is independent of deployment mode.

## What Changed

- Removed AgentActionButtons from both agent list implementations,
including filtered lists.
- Removed the unused board-access query and trace permission
calculation.
- Kept built-in setup controls, status, and membership actions.
- Enabled the normal Run now control on the streamlined agent detail
page so operators can still trigger a heartbeat there.
- Updated the existing mobile row test and added active/paused row
coverage for both list implementations.

## Verification

- Passed: `pnpm check:token-gates`.
- Passed: `pnpm exec vitest run ui/src/pages/Agents.test.tsx
ui/src/components/AgentActionButtons.test.tsx` (27 tests).
- Passed: `pnpm -r typecheck` and `pnpm build`. After the review fixes,
the UI typecheck, UI build, token gates, and targeted tests passed
again.
- Full CI passed on 6d527b02a, including all server/workspace test
shards, browser tests, typecheck, build, and canary dry run.
- Local `pnpm test:run` passed 520 server suites (7,234 tests). The
later UI batch had two Inbox failures under load; an isolated Inbox
rerun passed all 27 tests. Stopped the redundant local full run after
all CI shards passed.
- The CI build first hit an unrelated port collision in a
Codex-credentials test. One rerun passed runner verification and the
full build.
- Greptile: 5/5. Both review threads are resolved.
- Manual review: open the agent list and verify the action bar is
absent. Open an agent to access its actions.

## Risks

- Low risk. This changes agent list rendering and restores the normal
detail-page run control. Operators must open an agent to use the removed
shortcuts.
- No API, database, or permission contracts change.

## Model Used

- OpenAI GPT-6 (Codex). Exact deployment model ID and context window
size are not exposed in this session. Used reasoning, repository
inspection, code editing, and command execution.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-09 12:05:42 -05:00
Dotta fac07b42ad
fix(runner): preserve durable native session authority across recovery (#13092)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The native runner carries tool results and task output to the
control plane.
> - A lost connection must not change which run owns a result.
> - A session must not become reusable while provider output is still
pending.
> - This pull request adds strict recovery evidence and bounded drain
barriers.
> - It preserves current PRP version negotiation and session-goal
support.
> - The benefit is safer reuse of native sessions after a transport
failure.

## Linked Issues or Issue Description

Refs #13038. This is the first of two stacked pull requests. It contains
the native runtime prerequisites. The second pull request contains the
experimental chat-channel integration. It preserves the provider
identity and typed terminal-failure contracts in #13074 and the durable
recovery work in #13075.

**What happened?**

Native session failures could leave retained provider events, incomplete
tool results, or warm handoff state that was not safe to reuse. A later
run could observe output from an earlier authority.

**Expected behavior**

Recovery must preserve exact run, tool, process, artifact, and lease
evidence. Uncertain or corrupt state must fail closed. A successful
close must prove that retained provider output is settled.

**Steps to reproduce**

Run the transport and control-plane regressions. They hold and drop
authenticated frames, fail durable writes, and restart fresh controllers
and runner processes with retained state. Provider executables are local
test fixtures.

## What Changed

- Preserve pending provider cleanup and semantic-result evidence across
session close and restart.
- Add an authenticated warm handoff with exact old and new identities,
durable receipts, and completion acknowledgement.
- Drain retained provider events under the cumulative acknowledgement
fence.
- Reject corrupt tool-result contracts without unsafe provider replay or
reusable checkpoints.
- Keep ordinary PRP v1 sessions and current session-goal behavior.
Require negotiated PRP v2 and acknowledged native session evidence
before warm authority rotation.
- Preserve late semantic inputs and exact durable result receipts until
close can prove settlement.
- Add transport, crash-window, artifact, checkpoint, and final-output
regressions.
- Deduplicate resolved execution delivery under the current issue lock.
Reuse the exact existing successor after concurrent scans or a lost
acknowledgement. Preserve newer operator evidence.
- Persist idle provider integrity/capacity failures before process
retirement, retain permanent model-rejection classification, and keep
external question identifiers out of task instructions.
- Expose only the context source on native status events. Keep thin
dispatch projections compatible without exposing the complete context.

## Verification

- Review-fix revision: 128 runtime-context/native-session tests, five
idle-failure/adjacent Rust cases, 24 warm crash-window cases, three
startup-notification/close cases, and five attach/backlog cases passed.
The security and idle-failure cases were first reproduced failing.
- Prior merged revision: runner production build, TypeScript typecheck,
complete Rust workspace tests and formatting passed; 272 focused runner
tests and two real PostgreSQL regressions passed.
- Earlier full runner runs and CI Build failed on missing
semantic-result fixture receipts, stale local provider fixture bytes,
startup-notification ordering, and a confirmation-loss fixture that
could accidentally send its final ACK. Each cause was reproduced and
corrected without relaxing production authority or close assertions.
These earlier runs are retained as failures, not represented as passing
verification.
- The first local repository-wide run failed before later phases because
the isolated install omitted PostgreSQL's native-library aliases; it
also encountered an unrelated occupied-port fixture. Those results are
retained, not represented as a passing run.
- Exact `335b2ee52709afb3885d4d6ebb2a3ece4b5864d6`: the complete runner
suite passed 1,888 tests, with 10 existing skips. The full Rust release
workspace passed with serial test scheduling. The unchanged parallel
Rust run hit the five-second 300-descendant fixture deadline; that
failure is retained. No deadline or assertion was relaxed.
- The resolved-execution regression suite passed 57 tests, including
concurrent delivery, lost acknowledgement, superseded authority, and
newer operator evidence. Plain server typecheck passed. The
duplicate-delivery cases were first reproduced failing.
- Prior exact `335b2ee52709afb3885d4d6ebb2a3ece4b5864d6` CI passed all
required jobs and Greptile reported 5/5. Its local general-server run
passed 7,208 tests but failed one responsibility fixture; later phases
did not run. The fixture started the next wake while its bounded handoff
was active. It also used nonexistent comment IDs, which hid the current
stored-message-author identity rule. The updated tests use real message
authors, preserve task ownership, and await exact automatic handoffs. No
production identity policy changed.
- Current head `aa39275a1f300f7d1a0b16cd0885eea567cff6b0` includes
current master and the native context-source projection. The focused
identity/status cohort passed 27 tests and plain server typecheck
passed. Fresh full repository tests, types, build, required CI, and
Greptile review are pending. Final results will be updated before merge.
- This is deterministic local-provider evidence. It is not a claim of
complete live-provider qualification.

## Risks

- This changes authenticated recovery and close ordering. The TypeScript
transport and runner binary must be built from the same revision.
- Failed or incomplete evidence intentionally prevents reuse and can
require a fresh run.
- PRP v1 ordinary/cold sessions remain supported. A v1 connection lease
cannot upgrade in place. A current v2-capable runner held on a v1 lease
was qualified through owned-process retirement/join, fresh bootstrap on
the same old authority, v2 observation/ACK, then warm rotation. Legacy
binary replacement and adopted-owner migration are not qualified by that
test; rollout must not present them as automatic same-lease upgrades.
- This pull request has no database migration or chat-channel
activation. The second pull request keeps the channel feature
experimental.

## Model Used

OpenAI Codex assisted with implementation, tool execution, tests, and
reconciliation. The existing implementation records OpenAI `gpt-6-astra`
assistance. The current environment does not report a context-window
size. No private reasoning traces are included.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-09 11:02:59 -05:00
Dotta bf753b997a
fix(connections): keep task context through Cloud enrollment and OAuth (#13098)
## Thinking Path

> - Paperclip lets people manage agent work from tasks.
> - Agents can request app access in a task card.
> - Some apps first require Paperclip Cloud enrollment.
> - Enrollment could leave the task, and OAuth could lose the task
interaction ID.
> - This PR keeps enrollment in a separate window and retains the
interaction ID through OAuth.
> - The task can then recognize the connection and continue
automatically.

## Linked Issues or Issue Description

**What happened?**

A first Gmail connection could leave the task dialog during Cloud
enrollment. Setup resumed on the Apps page. Gmail connected, but the
task card could remain pending because OAuth did not retain its
interaction ID.

**Expected behavior**

Keep the task open and preserve its access choices. Resolve the card
after the server verifies connection access. Continue the agent
automatically.

**Steps to reproduce**

1. Start a fresh source test-drive instance without Cloud enrollment.
2. Ask an agent to read Gmail.
3. Open Connect on the task card.
4. Complete Cloud enrollment and Gmail authorization.
5. Check whether the task card updates without selecting the connection
again.

**Paperclip version or commit**

Reproduced on 35fdc0c66b. This branch
applies the fix to current master.

**Deployment mode**

Source test-drive in local-trusted mode. The shared setup code also
serves authenticated instances; live authenticated acceptance was not
performed.

Related PRs: #13058 introduced task connections. #12943 repaired expired
enrollment links. #12906 concerns Composio service matching and does not
fix this OAuth handoff.

## What Changed

- Open task enrollment in a reserved window, with a new-tab fallback.
- Refresh server enrollment status and the provider catalog while
keeping the task dialog and access choices.
- Return the enrollment callback to the verified task when available.
- Retain the interaction ID when OAuth resumes from the page host.
- Clear the server capability cache after enrollment and reject stale
cache writes.
- Close reserved popups on enrollment errors and invalid authorization
URLs.
- Add callback and setup regression tests, including blocked popups.
Update connection-intent documentation.

## Verification

- All 176 focused connector, enrollment, callback, OAuth, and setup
tests pass.
- Greptile gives commit `e58da6662` a 5/5 score. Both review threads are
resolved.
- `pnpm check:token-gates`, `pnpm build`, and `pnpm -r typecheck` pass.
- All CI checks pass for `e58da6662`, including the full test matrix,
browser tests, Runner verification, release registry, and canary dry
run.
- The serial local `pnpm test:run` was stopped after the complete CI
test matrix passed. It is not counted as a full local pass.
- Live browser test: fresh instance, native Codex runner, Gmail
read-only access, and a real Google account.
- Enrollment preserved the task dialog. The card changed to connected
and the agent called Gmail search and message-read tools without another
message or Run click.
- The connected card persisted after refresh. Google reused existing
consent during this attempt.
- The runner displayed only its completion summary. Full answer delivery
is a separate issue and is outside this PR.

## Risks

- Browsers can block or isolate authorization windows. The new-tab
fallback remains available, and the parent checks server state.
- Enrollment completion is only a prerequisite. It does not grant access
or resolve the task card by itself.
- No database migration, runner lifecycle change, or recovery UI is
included.

## Model Used

OpenAI Codex (GPT-6), with code editing, shell tools, and browser
automation. The session does not expose an exact runtime 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>
2026-09-09 10:55:11 -05:00
Dotta 82f662656a
fix(runner): restore legacy Git access and independent networking (#13094)
## Thinking Path

> - Paperclip runs agents for people with different GitHub accounts.
> - Managed operations must use the intended person's eligible
connection.
> - A failed duplicate connection must not hide a healthy grant for the
same account.
> - Legacy hosts also need their existing Git configuration when managed
access is not configured.
> - Runner networking and local Git operations must not depend on GitHub
broker availability.
> - This pull request separates those policies and improves failure
diagnostics.

## Linked Issues or Issue Description

**What happened?** New runs always cleared host Git credentials and
installed managed launchers. Network permission depended on GitHub
environment variables. A launcher failure could stop even local `git
status`. A newer unhealthy duplicate could take precedence over a
healthy connection, and generic health errors were shown as reconnect
requirements.

**Expected behavior:** Use a healthy eligible managed connection for the
intended account. Preserve host authentication only for unconfigured
standard-trust local or SSH execution. Permit local Git during broker
failures and keep network permission independent of GitHub credentials.

**Steps to reproduce:** Configure healthy and unhealthy grants for one
GitHub account, dispatch an agent, and execute Git commands. Separately
run an unconfigured legacy host with existing GitHub CLI authentication.
Stop the broker and run local `git status`.

**Paperclip version or commit:** Master at 668110469. **Deployment
mode:** Self-hosted. **Installation method:** Git checkout. **Agent
adapters involved:** Native Codex runner and shared adapter execution
setup. **Database mode:** Existing instance database; no migration.
**Access context:** Responsible person's managed grant, or explicitly
unconfigured legacy host. **Node.js version:** 26.4.0 locally.
**Operating system:** macOS development and Linux execution hosts.

**Relevant logs or output:** Previously `GitHub credential context
unavailable` hid configuration, transport, and capability errors. New
diagnostics identify these categories without credential values.

**Additional context:** Refs #13005 and #13022. Dependency provisioning
is addressed separately in #13093.

## What Changed

- Prefer healthy eligible grants and retry credential acquisition once
for the same principal and account before starting an operation.
- Preserve host Git configuration only when managed access is
unconfigured on a standard-trust local or SSH target.
- Project authentication mode and validated Git metadata into native
runner boundaries; refresh resumed provider settings when modes change.
- Enable network access through an explicit standard-trust controller
decision, independently of GitHub. Omitted or restricted decisions stay
disabled; replace warm providers when that decision changes.
- Run local Git with cleared credentials when the managed broker fails,
with specific redacted diagnostics.
- Retry access-refresh conflicts once without treating concurrency as
expired authorization.
- Show retry instead of reconnect for transient GitHub health failures.
Add optional authorization and run-diagnostic fields without a database
migration.

## Verification

- All latest-head CI gates are green, including typecheck, general and
serialized suites, browser tests, canary, native runner verification,
and build. Greptile is 5/5 with no remaining findings; the security scan
passed.
- Full recursive typecheck and build passed. UI token gates passed.
- Full general server run: 7,110 passed, one transient socket hangup;
that file passed on retry. All remaining workspace groups passed,
including 5,552 UI and 478 CLI tests. The complete serialized rerun
passed all 144 suites / 2,179 tests after the initial isolated timeout
passed on retry.
- 195 final launcher and native session tests passed, including
host/managed transitions, local/remote warm network-policy changes,
broker rotation, and attempts to override validated controller
filesystem roots.
- GitHub gateway fallback, duplicate connection selection, refresh
conflicts, per-user reauthorization, and native transport/security
suites passed.
- Additional live native fixtures passed SSH public-key authentication
and a Git credential helper in fresh and resumed host-mode sessions. An
unwritable managed configuration directory preserved local Git (exit 0)
while GitHub CLI failed with `configuration_directory_unavailable` (exit
4). The temporary SSH listener and keys were removed.
- Applicable Rust suites passed except two timing failures under load;
each failed case passed in isolation. The final environment contract
test passed.
- Linux native runner acceptance passed in both managed and legacy host
modes: DNS, HTTPS, npm package download, fresh-worktree Git status,
authenticated GitHub user lookup, repository read, and a new run
continuing the same provider conversation. Managed broker outage
preserved local Git and rejected authenticated access without host
fallback.
- Matching Linux server/runner artifacts and the separate provisioning
repair are deployed to the development instance. A fresh UI-dispatched
task and a new run after a server restart both passed all six shell
checks through the live controller and credential broker. Both runs
selected the expected healthy connection/grant and retained the same
provider conversation. Neither connection was repaired or reconnected.

The sandbox roots are assigned from the validated execution-target probe
**after** ordinary bindings are merged. Regression coverage supplies
forged roots and verifies they cannot override the controller paths.
Networking is enabled only for an explicit
`PAPERCLIP_RUNNER_NETWORK_ACCESS=enabled` controller decision; omitted
values remain disabled.

## Risks

Unconfigured standard-trust local and SSH runs regain access to host Git
authentication resources. Managed, sandbox, plugin, and low-trust runs
do not gain this fallback. Revoked managed access never falls back to
another account. Deploy server and runner artifacts together;
already-started operations retain their captured identity. An
unauthenticated command can still fail when it requires GitHub access.

## Model Used

OpenAI GPT-6 through Codex, with code editing, shell execution, tests,
and browser inspection. The exact model variant and context-window size
are not exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-09 10:15:10 -05:00
Dotta 2ceb630b6c
fix(workspaces): preserve dependency provisioning failures (#13093)
## Thinking Path

> - Paperclip runs agent work in isolated worktrees.
> - Each worktree needs dependencies that match its source and patches.
> - A failed install currently loses its exit status after an `if`
statement.
> - The provisioner can then record a successful dependency fingerprint.
> - This pull request preserves failures and bounds lockfile recovery.
> - Agents receive a usable workspace or an accurate provisioning
failure.

## Linked Issues or Issue Description

**What happened?** A nonzero pnpm install could return success and save
a fingerprint. Patch changes alone also did not invalidate the
fingerprint.

**Expected behavior:** Fail provisioning on an unsuccessful install.
Retry known frozen-lockfile mismatches once and record success only
after installation succeeds.

**Steps to reproduce:** Run the provisioner in a worktree with a pnpm
install that exits nonzero. The regression suite uses real shell
execution and a controlled pnpm fixture.

**Paperclip version or commit:** Reproduced on master before this
change. **Deployment mode:** Self-hosted. **Installation method:** Git
checkout. **Agent adapters involved:** Core workspace provisioning.
**Database mode:** Not relevant. **Access context:** Execution host.
**Node.js version:** 26.4.0 locally; supported minimum remains
unchanged. **Operating system:** macOS locally and Linux execution
hosts.

**Relevant logs or output:** `ERR_PNPM_LOCKFILE_CONFIG_MISMATCH`,
`ERR_PNPM_OUTDATED_LOCKFILE`, and ordinary installation failures.

**Additional context:** Related lockfile maintenance: #13061. No
lockfile or workflow changes are included.

## What Changed

- Capture the failed install status inside the `else` branch.
- Use the existing single retry for both frozen-lockfile mismatch
errors.
- Include patch contents in the dependency fingerprint.
- Add executable regression coverage and document the behavior.

## Verification

- All CI checks passed, including build, typecheck, tests, browser
suites, canary dry run, and security scans. Greptile: 5/5 with no
remaining findings.

- `node --test scripts/__tests__/provision-worktree-self-heal.test.mjs`:
19 passed; one existing test requires Linux flock and was skipped on
macOS.
- `bash -n scripts/provision-worktree.sh` and `git diff --check` passed.
- Full workspace typecheck and build passed in the companion runner-fix
worktree at the same base revision. This change only touches shell
provisioning, its tests, and documentation.
- CI and review are pending.

## Risks

The existing non-frozen recovery can update a worktree-local lockfile.
Committed lockfile updates remain bot-owned. Ordinary failures now
correctly stop provisioning and may expose previously hidden
installation problems.

## Model Used

OpenAI GPT-6 through Codex, with code editing, shell execution, tests,
and browser inspection. The exact model variant and context-window size
are not exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-09 10:14:58 -05:00
Dotta e9a5a07ab4
feat(ui): add task status badges and inline blocker removal (#13097)
Add navigable status badges to task relationships and a separate blocker remove button with stable hover geometry. Keep Storybook previews passive and cover navigation, removal, and query refresh rendering with regression tests.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-09 10:06:21 -05:00
Dotta cd4c4ed205
fix(ui): stabilize task loading and live feeds (#13095)
Coordinate initial conversation reveal, preserve message identity and reading anchors during live updates, and bound transcript reads with recoverable retries. Cover desktop/mobile navigation and rich task loading with actual-route browser tests.

Verified all Linux CI gates, 5,575 local UI tests, eight layout browser scenarios, and recorded native Codex walkthroughs. Greptile: 5/5; all review findings resolved.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-09 10:00:37 -05:00
Dotta 35fdc0c66b
fix: make task recovery durable and preserve current requests (#13075)
Make task recovery durable and preserve the latest user request across native and legacy continuations. Keep routine recovery quiet and prevent replay when action outcomes are uncertain.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-09 09:14:25 -05:00
Dotta 6681104692
fix(runner): preserve provider identity and terminal failures (#13074)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The runner translates provider events into durable task execution.
> - Provider notifications can refer to another thread or a descendant.
> - Root validation treated these notifications as fatal, and later
layers could lose the original failure.
> - This pull request classifies event identity and preserves structured
terminal failures.
> - The server can then choose safe recovery without weakening tool
authority.

## Linked Issues or Issue Description

Refs #13038. This change incorporates the shared protocol-integrity and
bounded-cleanup prerequisites from that PR. It does not include the chat
feature. Related failure classification work: #13028.

**What happened?**
An informational provider notification for another thread could
terminate the root session. A failed stream could then become a
missing-result error and lose its cause.

**Expected behavior**
Ignore unrelated informational notices with bounded diagnostics. Reject
invalid authoritative events. Preserve the original failure code and
recovery meaning through cleanup.

**Steps to reproduce**
Run a native Codex task. Deliver a notification for an unrelated thread,
or close the stream after a structured failure. Inspect the root outcome
and recorded failure.

**Paperclip version or commit**
Reproduced before e20010472. This branch includes the current
session-goal contract from that commit.

**Deployment mode**
Built from source. Native runner with Codex.

## What Changed

- Classify root, provider-confirmed descendant, stale, unrelated, and
invalid provider events.
- Keep tool requests bound to their original execution authority.
- Preserve typed failures through transport, session, and durable
control-plane cleanup.
- Keep bounded cleanup failures separate from the primary execution
failure.
- Add compatible shared contracts for continuation context, execution
status, and explicit reconciliation. The dependent PR adds their server
and UI consumers.

## Verification

- Runner TypeScript and ACPX suites: 1,733 passed, 7 skipped. Node
contracts: 38 passed.
- Real provider-process fixtures cover 300 descendant identities across
restart, the 4,096-identity capacity boundary, and rejection of
continuation after terminal acknowledgement and restart.
- Repository build, typecheck, and full `pnpm test:run` passed on the
rebased stack (18,448 tests passed, 49 skipped). The full Rust workspace
passed with `--test-threads=1`; parallel execution exposed an existing
fixture port-reservation race. All latest-head CI checks passed. One
unchanged artifact-document concurrency test failed on the first CI run
and passed on its single rerun.
- Added notification, streaming failure, protocol integrity, cleanup
quarantine, and durable failure tests.

## Risks

Provider event classification must retain the new session-goal behavior
on master. Descendant notifications must never gain root tool authority.
Shared contract fields are additive. This PR does not migrate data or
start replacement provider work.

## Model Used

OpenAI GPT-6 through Codex. The exact deployment ID and context window
were not exposed. Used reasoning, tool use, code execution, and browser
automation.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-09 08:53:07 -05:00
github-actions[bot] 5bb83e5b4b
chore(lockfile): refresh pnpm-lock.yaml (#13061)
Auto-generated lockfile refresh after dependencies changed on master.
This PR only updates pnpm-lock.yaml.

Co-authored-by: lockfile-bot <lockfile-bot@users.noreply.github.com>
2026-09-09 08:39:57 -05:00
scotttong 5acf56658b
feat(onboarding): first task opens as a chat with a chief of staff (#13068)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Onboarding ends by handing a new user to their first agent on a
seeded first task
> - Today the wizard asks for a mission up front, the UI composes what
the agent is told, and the agent starts running before the user says
anything
> - New users get a cold, ticket-shaped start, and nobody can edit the
agent's brief or persona without a code change
> - This pull request makes the first task a short chat: a four-step
wizard, a chief-of-staff persona, a greeting plus a two-option opening
card, server-owned markdown texts, and no run until the user answers
> - It also gives question cards one consistent action row (Cancel /
Skip / Next), makes agent hires idempotent within a run, and turns the
Paperclip Runner flag on by default for self-hosted instances
> - The benefit is a first run the user steers, with texts a board
operator can edit as markdown

## Linked Issues or Issue Description

No public GitHub issue exists for this change. The feature request
fields follow.

Related PRs and issues:

- Refs #11043 — an earlier draft of the first-task onboarding
experience. This PR supersedes it.
- Refs #11280 — a report about the onboarding first-task route test.
This PR extends that test file.

### Subsystem affected

Onboarding wizard, the seeded first task and its texts, task-chat
question cards, agent hiring, and the instance experimental settings.

### Problem or motivation

The onboarding wizard collects a mission through two extra steps and a
questionnaire. The UI then composes the first agent's instructions and
the first task description from those answers. The first task wakes the
agent at once, so the agent runs and posts before the user types a word.
Board operators cannot change the greeting, the brief, or the persona
without editing TypeScript. Question cards in chat behave differently
per adapter, and a single-select pick submits on click. A misread hire
response could create a duplicate agent that the creating agent cannot
remove.

### Proposed solution

Reduce the wizard to four steps and stop the UI from authoring agent
texts. Move the greeting, the brief, the chief-of-staff persona, and the
opening question into markdown and JSON files that the server loads at
runtime. Seed the persona onto the first agent through an explicit hire
marker. Do not wake the first task until the user answers the opening
card or types. Give every question card the same Cancel / Skip / Next
actions. Add an experimental toggle that switches the single-task
proposal between one confirmation card and a plan document with a
checkbox card. Make agent hires idempotent within a run.

### Alternatives considered

- Keep the mission questionnaire and feed it into the brief. Rejected:
the agent asks better questions in chat, and the wizard gets shorter.
- Keep the first task open-ended with a plain composer. Rejected: a
two-option card gives the user a clear first move.
- Derive the plan-document behaviour from the user's intent only.
Rejected in favour of an explicit experimental toggle so operators can
choose.
- Key the "pick does not submit" behaviour off the presence of a submit
label. Rejected: several adapters set a submit label on single-select
cards, and their cards would change behaviour.

### Roadmap alignment

`ROADMAP.md` lists no planned core work on onboarding or the first task.
This change refines the existing flow and does not duplicate planned
work.

## What Changed

- Wizard: four steps (Name your organization, Create your first agent,
Connect a model, Review). The front door and both mission steps are
removed with their state and saved-progress keys. The UI no longer
composes the first agent's instructions or the first task description.
- Server-owned texts: the greeting, the brief with two proposal
variants, the chief-of-staff persona, the opening question, and a README
live in `server/src/onboarding-assets/first-task/` and load at runtime.
The create route stores the assembled brief and ignores any client
description.
- Persona seed: an `onboardingFirstAgent` marker on the hire lets the
server seed the chief-of-staff persona over the first agent's entry
file. Board-authored hires only. The persona tells the agent the hire
response shape and to list agents before it acts on an unclear result.
- No auto-run: the first task does not queue an assignment wake. The
stranded-assignment reconciler leaves it idle until a user comment or an
answered card exists.
- Opening card: the server seeds an `ask_user_questions` card right
after the greeting with two options: "Interview me and propose a plan
and an agent team to execute it." and "I have a task in mind" with free
text. Answering wakes the agent.
- Experimental toggle `enableFirstTaskPlanProposal` (default off): the
single-task proposal is one confirmation card, or a plan document plus a
checkbox card when on.
- Question cards: every `ask_user_questions` card renders Cancel, Skip,
and Next (the submit label on the last question). Skip hides on required
questions. Picking an option no longer advances or submits by itself.
- Wizard guards: the dashboard's agentless offer ignores a cached empty
agent list while a refetch is in flight. The hire step adopts an agent
that already carries the typed name instead of hiring "Name 2".
- Agent hires are idempotent within a run: a retry of the identical
request under the same run id returns the existing agent with `200` and
`idempotent: true`. The fingerprint covers the whole validated request,
so a corrected payload is a new hire. Lookup, create, and activity
record run under one lock per company and run, so overlapping retries
cannot both create.
- The Paperclip Runner experimental flag defaults to on for self-hosted
instances. Cloud keeps its declared default: a managed instance whose
tenant row and managed overlay omit the flag resolves it to off.
- Question cards: a send that finds an earlier required answer missing
returns to that question with a message instead of failing silently.
- The two onboarding e2e specs follow the new wizard: the front door and
growth intake shots are gone, and the planning-mode spec dismisses the
opening card before it reads the composer.
- Docs: `docs/board-operator/editing-first-task-texts.md` explains how
to edit the texts and the toggle.

## Verification

Commands, run from the repo root:

```
pnpm -r --filter './packages/*' --filter '!@paperclipai/paperclip-runner' build
pnpm --filter ./packages/shared typecheck
pnpm --filter ./ui typecheck
pnpm --filter ./server exec tsc --noEmit
pnpm check:token-gates
pnpm --filter ./ui exec vitest run OnboardingWizard onboarding QuestionForm InteractionCard ProtocolCard TaskChatComposer Dashboard feature
PAPERCLIP_IN_WORKTREE=false pnpm --filter ./server exec vitest run onboarding-first-task heartbeat-process-recovery agent-hire-idempotency instance-settings agent-skills-routes issue-onboarding onboarding-greeting --testTimeout=90000
```

Results on this branch:

- Typecheck is clean for shared, ui, and server.
- Token gates: 4 of 4 clean.
- UI: 344 tests pass across 23 files.
- Server: all suites pass. The first test in `agent-skills-routes` has
its own 10 s cap and needs about 15 s on my laptop for the app cold
start. It passes with a longer cap. This PR does not change that cap.

Manual steps on a dev instance:

1. Open `/onboarding`. Confirm four steps: Name your organization,
Create your first agent, Connect a model, Review.
2. Finish the wizard. Confirm the first task shows the chief-of-staff
greeting and the opening card with two options. Confirm no run starts.
3. Pick "Interview me…". Confirm no run starts. Press Continue. Confirm
a run starts and an interview card of 3–4 questions arrives.
4. On a fresh organization, pick "I have a task in mind", type a task,
and press Continue. Confirm a proposal arrives as one confirmation card.
5. Turn on Settings → Experimental → "First task: propose with a plan
document" and repeat step 4. Confirm a plan document and a checkbox card
arrive.
6. Visit the dashboard after the hire. Confirm the wizard does not
reopen and one agent exists.
7. Open any question card. Confirm Cancel returns the plain composer
with the card still pending, Skip advances an optional question, and
Next moves to the next question.

Design reference with flow diagrams, chat mock-ups, and live captures:
https://pages.paperclip.ing/first-task-flow/proposed/

## Risks

- `pnpm dev` now builds the runner daemon because the Paperclip Runner
flag is on by default. Developers without a Rust toolchain must set
`PAPERCLIP_RUNNER_BINARY` or turn the flag off. Self-hosted instances
that never set the flag now let qualified agents use the runner.
- The wizard drops the mission steps and their saved-progress keys. A
user who is mid-wizard on an older build restarts at step 1 after an
upgrade. Existing organizations are not touched.
- The first task no longer runs on its own. A user who neither answers
the card nor types sees no agent activity. This is intended.
- The persona seed applies only to hires that carry the marker from the
wizard. API hires are unchanged.
- Hire idempotency is scoped to one run id and to the exact request.
Retries across runs, or with a changed payload, still create a second
agent. The lock is per server process, which matches how an instance
serves its API.
- Single-select question cards no longer submit on pick. Users of
adapters that relied on that behaviour now press Next.
- No database 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

- Claude (Anthropic) through Claude Code. `claude-fable-5-1` with
extended thinking, tool use, and code execution wrote most commits.
`claude-opus-4-8` wrote the toggle, texts, wizard, and idempotency
commits, as the `Co-Authored-By` trailers show.

## 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>
2026-09-08 20:19:14 -07:00
Dotta e200104727
feat: review connection actions from tasks (#13063)
Bring governed connection reviews into task history and composer approvals. Share resolution with Connections, add scoped remembered permissions, and resume agents through durable outcome receipts.

Keep cards compact, collapse raw results, isolate untrusted provider output, bound continuation payloads, and reconcile missed live events. Add Storybook coverage, browser journeys, and service regression tests.

Verification: all PR CI gates passed, Greptile 5/5, security scans passed, five connection-review browser journeys passed, and real native Codex approval/continuation was verified against the local MCP fixture.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-08 19:37:13 -05:00
Devin Foley fe5e68d7a5
fix: make Codex sign-in and the environment test agree on the credential a run uses (#13064)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The `codex_local` adapter signs agents in to OpenAI with a
device-code login, and the agent page has a Test button that probes the
sandbox with the credentials a real run would use
> - The login stored its credential where the Test never looked: the
company Codex home kept an old shape-valid credential, so the Test
failed with "authentication needed" right after a successful sign-in
> - The Test also staged a different Codex home than a real run
resolves, so the Test and real runs could disagree in both directions
> - This pull request makes the login, the seeding pass, and the Test
probe agree on one credential resolution
> - The benefit is that a sign-in from the agent page fixes the Test on
the next click, and a green Test means the same thing a real run
experiences

## Linked Issues or Issue Description

**What happened?**

Sign in with Codex works during onboarding but not on the agent detail
page. The operator completes the device-code login. The panel reports
success. The Test button still reports that authentication is needed. No
number of repeat logins changes the result.

Three defects combine to cause this:

1. The device-login promotion only wrote the company default Codex home
when that home held no shape-valid credential. A stale credential (for
example a symlink to an old host login) blocked the write forever, so
the fresh login stayed invisible to the Test.
2. The seeding pass that runs before every probe and execute replaced a
same-identity regular-file `auth.json` with a symlink to the host
credential, with no freshness comparison. Even a freshly promoted
credential was deleted on the next Test.
3. The sandbox hello probe always staged the company default home. An
agent with a configured `CODEX_HOME` was tested against one credential
and ran with another.

**Expected behavior**

A completed sign-in updates the credential the Test probes. The Test
stages the same Codex home a real run resolves. A stale credential never
outranks a strictly newer one from an interactive login.

**Steps to reproduce**

1. Configure a company whose Codex home holds a shape-valid credential
that no longer authenticates (for example an old host login symlink).
2. Open a `codex_local` agent's detail page with a sandbox environment
and press Test. The result shows the authentication-needed check.
3. Complete the "Sign in with Codex" device-code flow from the panel.
4. Press Test again. Before this change the result still shows
authentication needed.

## What Changed

- `packages/adapters/codex-local/src/server/adapter-auth-promotion.ts`:
the promotion writes the company default home unconditionally. The
shared `last_refresh` merge predicate scopes the write. It seeds an
absent or unusable slot, refreshes a same-identity slot only with a
strictly newer credential, and keeps a slot a different account or an
API-key file holds. The atomic rename replaces a symlinked `auth.json`
at the link itself. It never writes through into the host home.
- `packages/adapters/codex-local/src/server/codex-home.ts`: the
same-identity heal in `seedManagedCodexHome` is freshness-aware. A
regular-file credential is swapped for the shared symlink only when the
shared source is strictly fresher by `last_refresh`. Ties and
unparseable timestamps keep the file, which matches the predicate's
fail-closed direction. A genuine stale copy still heals as soon as the
host credential rotates past it.
- `packages/adapters/codex-local/src/server/test.ts`: the sandbox hello
probe prepares and stages the same home a real run resolves. The
identity-anchored cache vend runs first. A configured managed
`CODEX_HOME` is seeded in place and staged. A genuine external override
is staged as-is and never seeded or mutated.
- Tests: new pins for the strictly-newer company-home refresh, the
different-account keep, the symlink-replaced-without-writing-its-target
property, the freshness-aware heal (newer kept, older healed, ties
kept), the configured-home staging, and the external-home no-mutation
proof. The remote-probe suite now pins `CODEX_HOME`/`PAPERCLIP_HOME` to
scratch directories so no test can touch a real `~/.codex`.

## Verification

- `pnpm exec vitest run packages/adapters/codex-local/src --root
packages/adapters/codex-local` — 378 passed, 1 skipped.
- Server suites for device login, reconciliation, and the codex adapter
(8 files) — 128 passed, 15 skipped.
- `tsc --noEmit` clean in the adapter package.

## Risks

- Behavioral shift is scoped by the shared merge predicate: only a
strictly newer same-identity login can displace a company-home
credential, so a second account still never takes over the company slot,
and API-key files are never displaced.
- The heal keeps ties and unparseable timestamps instead of swapping. A
kept file self-corrects on a later seed once the source is provably
fresher; deleting a promoted credential is irreversible, so the failure
direction is chosen deliberately.
- The probe change makes the Test exercise the credential a run uses. A
Test that previously passed against the company home while the agent's
configured home was broken now fails honestly.

## Model Used

Claude (Anthropic) — Claude Fable 5 (`claude-fable-5`), extended
thinking, agentic tool use in Claude Code (terminal). Diagnosis traced
through the live promotion locks, the on-disk Codex homes, and the
adapter's credential-resolution code paths.

**Related PRs (searched; no duplicates found):** #12740, #12082, and
#9621 touch adjacent Codex credential sync paths; #8495 is the standing
hardening effort for probe auth seeding.

## 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
standalone docs cover this flow; the behavioral contracts are documented
in-line at each changed site)
- [x] I have considered and documented any risks above
2026-09-08 17:16:17 -07:00
Dotta 2043e0c735
fix: repair runner configuration, macOS execution, and artifact galleries (#13062)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent adapters select a provider, a model, and a runtime.
> - Runner conversion rejected existing Claude agents. The model list
mixed providers.
> - The native Claude runner rejected custom models and could not launch
on macOS.
> - This pull request fixes conversion, model selection, and verified
macOS execution.
> - It also groups configuration fields consistently across adapters and
opens artifact images in the task gallery.
> - Operators can change an agent configuration and run the selected
model on their Mac.

## Linked Issues or Issue Description

**What happened?**

Converting an existing Claude agent to Paperclip Runner failed with a
Codex-only restriction. ACPX Claude showed unrelated models and required
`claude-sonnet-5`. Its native runtime rejected macOS. Configuration
mixed common model settings with process controls. Artifact cards
labeled “Open gallery” navigated to attachment URLs instead of opening
the task gallery.

**Expected behavior**

Conversion keeps agent identity and compatible settings. ACPX Claude
uses the normal Claude catalog and accepts typed model IDs. Codex uses
the native runner. The verified Claude runtime can launch on macOS ARM64
and x64. Common configuration sections place the same fields together
across adapters. Artifact images open in the shared task gallery with
navigation and downloads.

**Steps to reproduce**

1. Open the configuration of an existing Claude agent.
2. Convert it to Paperclip Runner.
3. Select ACPX Claude and a different catalog model or a typed model ID.
4. Save the agent and run a disposable task on macOS.
5. Inspect configuration and advanced run-policy controls across
adapters.

**Paperclip version or commit**

The bugs were reproduced on `165ca56a22adb60e5fda56045442d9c8498116a8`.
This branch was rebased onto `7ed122911`.

**Deployment mode**

Built from source. Local test-drive instance on macOS ARM64 with an
isolated database.

Related work: #11798 addresses unsupported ACP session options in the
existing adapter path. #13048 addresses working-folder preservation.
This change fixes native runner configuration and launch behavior.

## What Changed

- Remove the Codex-only conversion restriction. Preserve agent identity,
instructions, directories, credentials, and compatible model settings.
Reset incompatible sessions while retaining history.
- Show ACPX Claude and native Codex as distinct provider choices. Remove
ACPX Codex from advertised configuration. Normalize legacy
configurations before fresh runs without rewriting historical run
descriptors.
- Select model catalogs and cache entries by provider. Support refresh
and typed model IDs. Pass exact Claude IDs through session creation,
model changes, and recovery.
- Add verified macOS ARM64 and x64 Claude SDK snapshots. Bound
executable allocation and total snapshot size. Preserve package checks,
dependency isolation, process ownership, cancellation, and Linux
descriptor loading.
- Probe local runtime readiness. Report remote platform checks as
incomplete until the remote runner verifies its runtime.
- Surface actual model rejection and allow correction and retry.
- Repair missing ACPX goal-capability helpers exposed by the post-rebase
live test. Persist and restore the optional capability without breaking
session startup.
- Put Agent identity first and intentionally remove the Capabilities
editor, as requested. This is removal of UI editing, not relocation:
preserve existing capability metadata and API compatibility without
adding another editor. Use the themed select for configurable permission
modes, with normal text instead of monospace.
- Put model and provider under Adapter. Give environment variables their
own section. Fold command and arguments under Configuration. Fold
lifecycle, timeout, and interrupt grace under Advanced Run Policy. Hide
single-option permission controls.

- Open image and video artifact cards in the existing task gallery,
including cards in the artifacts panel. Chat attachment images use the
same gallery. Preserve standalone media previews and download links.

## Verification

- Rebased focused UI/API/database suites: 293 tests passed.
- Rebased native runtime and ACPX suites: 242 passed, 7 skipped.
- Repository typecheck, build, and token gates passed for the runner
changes. Gallery follow-up UI typecheck, build, and token gates also
passed.
- Follow-up UI suites passed (86 tests), packaging checks passed (14
tests), and the final focused runtime suites passed (126 passed, 7
skipped).
- Linux container isolation and lifecycle fixtures passed before rebase
(57 passed, 2 skipped). Rust ACPX provider-session tests passed after
rebase (8 tests).
- Browser tests completed actual Claude and native Codex tasks on macOS
ARM64. They covered conversion, catalog refresh, a non-default catalog
model, a typed `haiku` ID, save/reload, cancel, follow-up session
continuity, invalid-model errors, and recovery.
- Final-revision live tests completed a typed Claude task, a follow-up
with the same provider session, and a native Codex task on macOS ARM64.
- Browser tests confirmed the moved interrupt-grace field saves and
survives reload. Cross-adapter tests cover Claude, Codex, Gemini,
process, gateway, and schema forms.
- Full local run: 7,080 passed, 30 skipped, and two timeouts. Both
timeout suites passed on isolated rerun (84 tests); the failures were
the plugin login-worker exit diagnostic and the runner real-server
vertical slice.
- Final follow-up checks: 50 registry tests and 45 snapshot/installation
tests passed (6 platform-specific skips). Oversized executable rejection
is covered before allocation or reading; unsupported-platform tests
invoke the real installation probe.
- Runner head `ddb5101c483a297f74875ab96b3c66035b002d50`: all CI gates
green, including full runner verification, repository build, typecheck,
general/serialized server suites, browser tests, and canary dry run. [CI
run](https://github.com/paperclipai/paperclip/actions/runs/34286178670).
- Greptile: 5/5 on that runner head. All four review threads resolved.
Superagent, Socket, and Snyk checks green.
- After snapshot hardening, another real Claude task completed on this
Mac using the rebuilt runtime.

- Gallery follow-up: 148 focused tests passed, covering artifact
selection, shared attachment collections, deduplication, image/video
cards, standalone previews, downloads, and closing. Live browser
verification completed on the settings follow-up: artifact selection,
6-image pagination with wrapping, download action, and closing all
stayed on the same task URL. All checks passed on gallery head
`96136da58ff195bf6ca00b281eb3022ad12d7bd8`: [CI
run](https://github.com/paperclipai/paperclip/actions/runs/34287987536).
Greptile returned 5/5 on that exact head with no unresolved threads.

- Final settings polish: 96 focused tests, UI typecheck/build, and token
gates passed. A real browser walkthrough verified readable permission
options, identity placement, Capabilities removal, and permission
save/reload. Original test-agent permission mode restored. All 31 checks
passed on final head `e46540d6bf32bfb0566dca16b2f4a75ba437618c`: [CI
run](https://github.com/paperclipai/paperclip/actions/runs/34292797886).
Greptile returned 5/5 with no unresolved threads.

## Risks

- Capabilities intentionally has no editable UI field after this change.
Existing values remain readable and API-compatible; removing the field
does not erase stored metadata.

- macOS launch now copies verified package files into private snapshots.
The implementation must retain isolation and clean up snapshots on exit.
- Runtime provider or model changes reset the current session.
Historical runs remain available.
- The macOS x64 SDK executable digest was verified, but a live Intel Mac
run was not available. Linux verification used container fixtures, not a
real Claude task.
- Remote environment tests report a warning when only the platform has
been checked. They do not claim package readiness from the server host.

## Model Used

OpenAI Codex, based on GPT-6. The exact served model identifier and
context-window limit are not exposed in this session. Used reasoning,
repository inspection, code execution, Rust and TypeScript tests, and
browser automation.

## 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 (focused suites and both
timeout suites on rerun; full-run counts above)
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-08 19:10:09 -05:00
Devin Foley 8f099c3f83
ci: dispatch a Docker build for every canary tag (#12950)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Every master merge publishes an npm canary, and downstream managed
deployments consume the matching `sha-<short>-cloud` Docker image.
> - The canary image relies on the master-push `docker.yml` run, whose
single pending concurrency slot is superseded by every newer push.
> - On a busy day the image build never survives: five consecutive
canaries shipped npm packages with no cloud image on 2026-09-06, and
downstream deploys starved for ~18 hours while master kept moving.
> - The nightly and beta lanes already solve exactly this by dispatching
`docker.yml` at their tag ref after pushing it.
> - This pull request wires the canary lane into the same mechanism, so
every canary tag gets an image build no master push can supersede.
> - The benefit is that a published canary always has its images, and
downstream release resolution stops walking back to day-old builds
during busy merge windows.

## Linked Issues or Issue Description

Refs #12769 / #12855 (the earlier image-publishing incident in the same
pipeline, different failure mode).

**What happened?**

`docker.yml` serialises per ref with one pending slot (`concurrency:
docker-${{ github.ref }}`, `cancel-in-progress: false`). Master pushes
arriving faster than the ~50-minute build supersede the pending build
indefinitely. On 2026-09-06, canaries `2026.906.0-canary.1` through `.3`
(and the commits between) published to npm with no `sha-<short>-cloud`
image on GHCR — the Docker run list shows `cancelled, cancelled,
cancelled` for their commits. Downstream managed rollouts correctly
refused to deploy image-less canaries and pinned at `canary.0` for ~18
hours.

**Expected behavior**

Every published canary has its Docker images. A busy merge window must
not be able to prevent image publication for tagged releases.

**Steps to reproduce**

1. Merge to master more often than the Docker build takes to complete,
for several hours.
2. Observe npm canaries advancing while every `Docker` run for their
commits completes `cancelled`.
3. Observe `ghcr.io/paperclipai/paperclip:sha-<short>-cloud` returning
404 for each of those canaries.

**Paperclip version or commit**

Master at `83987210` (diagnosis time); the starved canaries were
`2026.906.0-canary.1`–`.3`.

**Deployment mode**

GitHub Actions release + image pipeline; consumed by managed cloud
deployments.

## What Changed

- `.github/workflows/release.yml`, `publish_canary` job:
- New step after "Push canary tag": dispatch `docker.yml` at
`refs/tags/canary/v<version>` — identical to the nightly and beta lanes'
existing step, including the step-summary note. The dispatched run keys
its concurrency off the tag ref, so master pushes cannot supersede it,
and `docker.yml`'s `type=sha` mapping publishes `sha-<short>` /
`sha-<short>-cloud` for any ref.
  - The job gains `actions: write`, mirroring `publish_nightly`.
- No `docker.yml` changes: it already accepts `workflow_dispatch` for
exactly this pattern.
- No dry-run guard needed: `publish_canary` runs only on `push` events,
so the `dry_run` dispatch input cannot reach it.

## Verification

- YAML lints clean; the step is a line-for-line mirror of the proven
nightly-lane step (tag source swapped for the job's existing
`canary_tag` output).
- The concurrency claim is docker.yml's own documented behavior: groups
are per-ref, and a tag ref is distinct from `refs/heads/master`.
- Not run: a live canary publish — the next master merge after this
lands exercises it end to end; the worst case (double build for a canary
whose master-push run also survives) is benign, since both runs push
identical content-addressed tags.

## Risks

- Low. Roughly one additional Docker build per canary on busy days (on
quiet days the master-push run and the dispatched run both execute —
duplicate work, identical images, no conflict since the sha tags are
content-equivalent for the same commit).
- `actions: write` on `publish_canary` matches the grant
`publish_nightly` already carries for the same purpose.

> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.

## Model Used

Claude Fable 5 (Anthropic, model id `claude-fable-5`), extended
thinking, agentic tool use in Claude Code: GHCR manifest probing,
Actions run-list forensics, and workflow-lane comparison.

## 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 (workflow-only change; YAML
linted; no test harness targets release.yml)
- [x] I have added or updated tests where applicable (not applicable —
CI wiring mirroring an existing proven lane)
- [x] I have updated relevant documentation to reflect my changes (the
step's inline comment documents the invariant)
- [x] I have considered and documented the risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-09-08 16:56:35 -07:00
Dotta 7ed122911b
Add end-to-end session goals to Paperclip Runner
Add capability-aware slash-goal controls, durable provider goal state, PRP v2 negotiation, autonomous goal execution, and safe local session recovery. Integrate with current master, preserve provider session identity, and verify the browser goal/chat/replacement/clear workflow and unsupported-agent rejection.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-08 16:18:47 -05:00
Nicky Leach 6019e2bd6e
feat(adapter-utils): carry binary bodies and attachment routes over the HTTP/2 sandbox bridge (#12923)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip runs agents in local and remote sandboxes through adapter
utilities
> - The HTTP/2 sandbox bridge decoded every body as UTF-8 text and
rejected non-JSON content
> - This stopped agents from uploading or downloading issue attachments
through that bridge
> - This pull request carries raw bytes, permits the two attachment
routes, and enforces a shared body limit
> - The benefit is correct attachment transfer with a process-wide
memory guard

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The HTTP/2 sandbox bridge forwards request bodies between an agent
sandbox and the Paperclip host. It now supports binary bodies and the
issue attachment routes.

**Current behavior**

The bridge decodes each body as UTF-8 text. It returns HTTP 415 for
content types outside the JSON route list. An agent cannot upload or
download an issue attachment through this transport.

**Proposed behavior**

The bridge carries raw bytes through the forward path. It permits the
attachment upload and content routes. The queue transport and file
gateway keep their existing route behavior. A shared 10 MiB body limit
and process-wide byte reservation protect memory use.

**Reason and benefit**

Attachment clients need byte-preserving transfer. The shared limit keeps
the gateway and host aligned. The reservation prevents concurrent
streams from exceeding the accepted process memory ceiling.

**Breaking changes**

The HTTP/2 bridge accepts two attachment routes and permits binary
content. The queue transport and file gateway keep their previous route
lists and HTTP 415 behavior. No schema or external endpoint changes.

## What Changed

- Carry request and response bodies as raw bytes through the HTTP/2
bridge.
- Permit attachment upload and attachment content routes on the HTTP/2
bridge only.
- Raise the resolved per-body limit to 10 MiB and share it between the
gateway and host.
- Reserve body bytes before allocation and release each stream
reservation on every terminal path.
- Document the body limit, process ceiling, and reservation behavior.

## Verification

- Run `pnpm exec vitest run
packages/adapter-utils/src/http2-bridge-server.test.ts
packages/adapter-utils/src/execution-target-sandbox.test.ts
packages/adapter-utils/src/sandbox-callback-bridge.test.ts`; 226 tests
pass.
- Run `pnpm --filter @paperclipai/adapter-utils typecheck`; it passes.
- Run the direct server TypeScript check with `tsc --noEmit` in
`server/`; it passes with zero errors.
- Verify multipart upload and binary download round trips over HTTP/2
without corruption.
- Verify the queue transport and file gateway return HTTP 415 for the
same routes.
- Verify the host rejects bodies over the resolved limit.
- Verify a denied reservation returns HTTP 503 and allocates no copy.
- Verify stream cleanup releases reservations after completion, error,
abort, timeout, and close.

## Risks

The bridge now accepts larger bodies and binary content. The
process-wide reservation limits total live body bytes to 1 GiB. Route
behavior changes only for the HTTP/2 bridge. The security review found
no blocking issue for this commit range.

## Model Used

OpenAI Codex, GPT-5. The runtime used tool calls and code execution. The
runtime did not expose the context window size. No model-generated code
changes were made for this 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)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-08 14:18:12 -07:00
Dotta e095b84dab
feat(connections): connect services from native task feeds (#13058)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents use connections to reach external services.
> - A fresh native task can have no service tools installed.
> - The agent needs a way to discover services and ask the responsible
person for access.
> - This pull request brings the existing connection-intent flow into
native task execution.
> - The person can connect from the task, and the agent can continue
with updated tools.

## Linked Issues or Issue Description

**Subsystem affected**

Native runner tool authority, connection intents, task interactions, and
shared connection setup.

**Problem or motivation**

A task that needs an unconnected service cannot finish its work. Leaving
the task to configure access also loses context. A resolved request must
survive a restart and resume the correct agent once.

**Proposed solution**

Expose connection discovery and access requests as server-owned native
tools. Render a durable task card and use the shared setup dialog.
Persist outcome delivery and start a fresh provider session after access
is ready.

**Alternatives considered**

Sending the person to the Connections page adds navigation and does not
solve continuation. Polling for authorization consumes runs and can
create duplicate requests.

**Roadmap alignment**

This extends the existing connection-intent runtime and setup
experience. It reuses the shared access model and the native runner.

Related: #12345, #12347. The service-slug fix in #12906 is related but
separate. Companion evaluation PR:
https://github.com/paperclipai/paperclip-evals/pull/21.

## What Changed

- Expose `connections_search` and `connection_request` with server-bound
company, task, agent, and responsible user. Preserve the legacy entry
points.
- Discover catalog services and authorized custom connections. Check
installation, identity, health, and executable permissions before
reporting ready.
- Keep pending cards through ordinary messages. Reuse requests and
retire stale ownership. Put Connect at the right of Not now.
- Reuse the shared setup flow in a task dialog. Keep access additive and
default to the requesting agent. Recover from cancelled or blocked OAuth
windows with a new-tab fallback.
- Persist outcome delivery with an idempotent wake key. Resume in a
fresh session and recheck ownership before dispatch.
- Add native browser fixtures, offline Storybook states, server
contracts, and evaluation fixtures. Update guidance and documentation.

## Verification

- `pnpm build`: passed after replaying the change on current master.
- `pnpm -r typecheck`: passed.
- `pnpm check:token-gates`: passed.
- `pnpm --filter @paperclipai/ui build-storybook`: passed.
- New continuation-policy regression cases: 16 passed.
- Docker-backed PostgreSQL regressions passed for requester-only OAuth
access, assignment-only expiry, terminal expiry, and credential-free
setup metadata.
- Shared setup and task-card UI tests: 121 passed, including configured
MCP reconnect URL recovery and preserving user edits across refetch.
- Storybook browser checks: all 119 passed on the latest reconnect fix.
- `pnpm test:run`: 4,734 tests passed in the first server group, but
embedded PostgreSQL startup failures and resulting cleanup errors
prevented a complete local pass. All Linux CI lanes passed on the latest
reviewed commit. One external-object route test returned an unexplained
500 on the first run; it passed twice locally and the failed shard
passed on retry without code changes.
- Earlier feature-checkout evidence: three deterministic native browser
journeys passed, including restart delivery and an actual fixture tool
result. Legacy scripted coverage also passed. All 59 added stories were
inspected in light and dark themes.
- Live Notion testing recorded successful provider reads. The manual
test used a local-trusted instance. It does not prove
authenticated/cloud deployment or every provider journey.
- Native browser rerun reached the embedded PostgreSQL startup limit
before bootstrap, so the latest checkout’s full native browser journey
remains unverified. Both OAuth page/task regression cases passed against
isolated Docker-backed PostgreSQL 17. They verify no premature task
access, requester-only completion, additive retries, and reconnect
preservation.
- Applied both new migrations twice to isolated PostgreSQL 17. Foreign
keys remained intact, duplicate active delivery keys were rejected, and
failed delivery records did not block retries.

Reviewer path: start a fresh test drive, enable the native runner, use
an agent that can perform work directly, and ask it to summarize a
Notion page. Connect from the card, then verify the resumed provider
call and source-linked answer. The default test-drive CEO is instructed
to delegate, so it can introduce an unrelated hiring step.

## Risks

- Two additive migrations create durable deliveries and a partial unique
wake index. They are idempotent. The wake index can require a
maintenance window on large tables because migrations run in a
transaction.
- OAuth and continuation cross asynchronous boundaries. Tests cover
ownership changes, retries, additive access, and restart delivery; live
provider behavior still varies.
- The latest requester-scope fix has not yet been exercised through live
OAuth. GitHub, API-key, authenticated-user, and all recovery journeys
are not claimed as verified.

## Model Used

OpenAI GPT-6-based Codex assisted with implementation, tests, and review
using tools and code execution. The runtime does not expose the exact
model version, context window, or reasoning setting. Live evaluation
used `gpt-5.6-luna`; manual native testing used `gpt-5.6-sol`.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used and disclosed unavailable runtime
details
- [x] I have checked ROADMAP.md and confirmed this extends existing
connection work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have described the issue in-PR following the feature 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
ticket id
- [ ] I have run all required tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation
- [x] I have considered and documented risks
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-08 15:55:26 -05:00
Dotta 5128b4f323
fix(claude): default unset models to Opus 5 (#13055)
Resolve unset Claude models to Opus 5 across CLI and ACP execution, preserve explicit and provider-specific overrides, and show the default in agent configuration.

Verified 212 focused tests after merging master, UI typecheck and token gates, and all CI checks. Greptile reviewed the final head at 5/5.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-09-08 15:13:27 -05:00
Dotta ebaeba40ee
feat: simplify agent onboarding and configuration (#13011)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators create agents and configure their runtimes in the board
UI.
> - The old creation flow presents several choices and a large form
before an agent can start.
> - The existing onboarding controls already provide clear provider
connection steps.
> - This pull request uses those controls in a new-agent wizard and
organizes the full configuration pages.
> - Operators can connect, test, save, and assign a first task while
keeping the existing configuration tools.

## Linked Issues or Issue Description

Related: #10974. That earlier open PR also reorganizes agent
configuration. This PR follows the reviewed Storybook designs for agent
creation and the current configuration tabs.

**What existing behavior does this improve?**

Agent creation, provider connection, runtime tests, and full agent
configuration.

**Current behavior**

The creation dialog leads to a large manual configuration form. Provider
login controls differ from onboarding. Environment variables and secret
access appear in separate places.

**Proposed behavior**

Choose a name and adapter. Connect Claude or Codex through the existing
onboarding controls. Configure and test the runtime, save the agent, and
open a task dialog with that agent assigned. Use the same design on the
existing configuration tabs.

**Reason and benefit**

The first setup asks for fewer decisions. The full editor keeps
instructions, skills, runtime controls, secret access, permissions,
keys, and revisions available in clear sections.

**Breaking changes**

The board creation and configuration layouts change. The
test-environment API adds an optional, allowlisted `testCredentials`
field for one-shot probes. Database contracts stay the same. Native ACPX
tests now reject unsupported local platforms before a CLI login can mask
the runtime restriction.

## What Changed

- Added a new-agent wizard with numbered steps, adapter branding,
provider connections, editable model choices, runtime tests, and
confirmation.
- Added Codex app-server, Claude ACPX, and OpenCode runner choices.
- Stored API credentials through existing secret APIs and persisted
references in agent configuration. New setup keys are isolated from
credentials used by existing agents.
- Preserved external-agent invitations beside the wizard, including
optional messages, one-time prompts, and clipboard fallback.
- Added OpenRouter provider and secret bindings for Pi and OpenCode.
- Added adapter-specific prerequisite fields for Cursor, Gemini, Kimi,
and Hermes. Cursor Cloud keys are saved as new organization secrets.
- Fixed Cursor Cloud repository field mapping, omitted empty remote
environment values, and added useful model and repository error
messages.
- Preserved complete MCP assignments when multiple valid profiles
contain more than 250 tools in total. Generated profiles retain exact
tool selectors.
- Added service branding and deployment-aware adapter choices. Cloud
setup offers Claude, Codex, and OpenCode; local native runners require
the experimental setting.
- Made the agent list responsive at intermediate widths.
- Applied the reviewed design to the real agent configuration pages.
Kept the instruction editor, skills, and existing mutations.
- Combined secret access and environment variables under one Save and
Discard action.
- Added interactive Storybook screens for setup, configuration,
confirmation, authentication, and test results.
- Fixed Pi provider-error parsing and thinking-effort persistence.
Native ACPX validates Linux x64 on the actual local, SSH, or sandbox
target.
- Redacted the complete transient probe-credential field from HTTP error
logs, including rejected provider names.

## Verification

- Current head `df0292fe6` has a fresh Greptile 5/5 review with no
unresolved findings. All 31 executed CI checks passed, including the
aggregate verification gate and all browser E2E shards. Storybook visual
regression is skipped by its workflow; the local Storybook build passed.

- Browser tests completed real assigned tasks with direct Codex, Claude,
OpenCode, Pi, and native Codex.
- Verified external-agent invitation generation and automatic prompt
copying in the live browser.
- Pi and OpenCode used an existing OpenRouter secret. Browser checks
covered save and reload, instruction edits, skill selection,
environment-variable Save and Discard, and assigned task creation.
- Invalid Claude API credentials remained on the connection step with an
error. A live Pi/OpenRouter invalid-key probe returned a provider
failure and left the user-secret inventory unchanged (zero entries
before and after).
- Full workspace typecheck and build passed after rebasing onto current
master. After review fixes, server and UI typechecks, token gates, and
the full build passed again. Storybook built successfully.
- All 5,542 local UI tests passed. The Cursor Cloud and Pi adapter
regressions passed all 24 tests. Review regressions passed 69 server
tests and all 18 agent-list tests.
- The local full test command ran 6,971 general server tests
successfully. Editing review fixes during that long run caused nine
tests to use stale modules; fresh isolated runs passed. An unrelated
embedded-Postgres fixture hit the host shared-memory limit; its 15
affected tests passed when the fixture groups ran separately.
- Local workspace groups passed after rerunning 18 CLI tests
sequentially to avoid host database limits and parallel-load timeouts.
The local full command stopped at the general server phase, so
serialized server verification comes from the five passing CI shards.
- Browser testing at 390px confirmed that the agent action menu opens
and the page has no horizontal overflow. CI browser E2E shards passed.
- Review the `Onboarding / New agent` and `Agents / Configuration
refresh` Storybook groups. In the real app, create an agent, run its
connection test, save it, assign a task, and reload its configuration.

## Risks

- This changes the main agent setup and configuration UI. Regression
tests cover routing, persistence, secret bindings, and form actions.
- Native Claude ACPX requires Linux x64. Direct Claude works on macOS.
Remote checks execute a bounded platform probe and reject unsupported or
unverified targets.
- A native OpenCode task reached the provider context limit because of
its tool payload. Its provider connection test passed. Direct OpenCode
completed a task. This existing native execution limit is not fixed
here.
- Claude and Codex connection keys use the existing user-secret store.
Other runtime setup keys use distinct organization secrets. Existing
credentials are never rotated. Probes do not store entered keys. Failed
agent creation removes newly staged credentials.
- Cursor Cloud has not completed a live task. Its authenticated account
still needs GitHub repository access. The live run passed MCP
provisioning, remote environment validation, and explicit Auto model
selection before the repository prerequisite blocked execution.
- Generated runtime MCP profiles can exceed the public profile-edit
request limit. They still contain exact catalog selectors and preserve
permission boundaries.
- No database migration, dependency, lockfile, or workflow changes are
included.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, repository tools, shell
execution, and browser automation. 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>
2026-09-08 14:22:32 -05:00
Nicky Leach f65991a5f1
refactor(server): move scheduled-retry and queued-run dispatch into a run-dispatch module (#12920)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The server heartbeat service dispatches scheduled retries and queued
runs.
> - The service kept policy decisions and database writes in one large
file.
> - This layout made policy branches harder to test and transaction
boundaries harder to inspect.
> - This pull request moves the policy rules and database transactions
into a run-dispatch module.
> - The benefit is a smaller service, pure policy tests, and clear
transaction ownership.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The server heartbeat service promotes scheduled retries and cancels
stale queued runs.

**Subsystem affected**

server/ — REST API and orchestration services.

**Current behavior**

The heartbeat service contains the policy rules and the database writes
for these dispatch paths.

**Proposed behavior**

A run-dispatch module owns pure policy functions and semantic database
transactions. The public service contracts stay unchanged.

**Reason and benefit**

The new layout separates branch rules from database effects. It makes
each policy branch easier to test and keeps each operation’s row writes
in one transaction.

**Breaking changes**

None. The public service contracts stay unchanged.

## What Changed

- Move scheduled-retry promotion and queued-run staleness rules into
pure functions.
- Add table-driven unit tests for each policy branch.
- Move promotion and cancellation writes into semantic transactions.
- Keep row locking, company isolation, and post-commit effects
unchanged.

## Verification

- `node scripts/check-module-boundaries.mjs` passes.
- `tsc --noEmit` from `server/` reports no errors.
- The focused server test command passes 228 tests in six files.
- Full pull request CI passes.
- Greptile reports 5/5, and all review threads are resolved.

## Risks

The main risk concerns changed transaction boundaries in scheduled-retry
promotion and queued-run cancellation. The focused tests retain coverage
for locking, transactionality, company isolation, and transport
contracts. The public service contracts do not change.

## Model Used

Codex, GPT-5, with code execution and tool use. The context window is
not provided 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 (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 have addressed all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-08 11:00:55 -07:00
Dotta 3ad494aacd
fix(adapter-utils): preserve legacy sandbox PATH with managed GitHub (#13051)
## Thinking Path

> - Paperclip lets people manage AI agents and their work.
> - Remote agents need both their installed tools and managed GitHub
credentials.
> - The GitHub launcher replaces a missing remote PATH with a small
system path.
> - Legacy images install agent CLIs outside that path, so those agents
cannot start.
> - This pull request preserves the remote toolchain and puts managed
GitHub commands first.
> - Command checks now use the same environment as execution.

## Linked Issues or Issue Description

Refs #13005. Related: #10239 fixes a separate Cursor environment path
issue.
Searched open issues and PRs for sandbox PATH and GitHub launcher
changes. No duplicate of this launcher fix was found.

**What happened?**

A remote Claude or Codex run passes command discovery, then fails with
`command not found` and exit code 127. Managed GitHub launchers use only
their own directory and `/usr/local/bin:/usr/bin:/bin`. This drops NVM
and other toolchain directories from the sandbox path.

**Expected behavior**

Agent CLIs remain available on legacy and current sandbox images.
Managed `git` and `gh` still resolve first and use the responsible
person's credentials.

**Steps to reproduce**

1. Use a sandbox whose agent CLI is installed in an NVM or other
non-system bin directory.
2. Start an agent run with managed GitHub launchers and no explicit PATH
override.
3. Observe that command discovery succeeds but the agent command exits
with code 127.

**Paperclip version or commit**

Observed on `b97101893f0926f57ed0ce9ef1f8d3e4780c62c2`. The same
launcher behavior remains on the base commit `be6bb768b`.

**Deployment mode**

Hosted server with remote sandbox execution. The shared launcher also
supports SSH targets.

## What Changed

- Read the remote target's effective PATH when no remote override is
set. Do not copy an inherited controller PATH.
- Prepend the managed launcher directory and retain the combined path in
shell startup files.
- Stop startup if path discovery fails. Frame the response so login
banners cannot contaminate PATH.
- Pass the sanitized launch environment to sandbox command checks,
installation, and the second check.
- Add real shell tests for legacy and current CLI layouts, quoted paths,
managed GitHub command execution, explicit overrides, SSH, and failure
cases.
- Document the remote path contract.

## Verification

- Four focused adapter utility suites passed: 153 tests.
- The regression suite passed: 13 tests, including the Linux stdin
handling fix.
- `pnpm --filter @paperclipai/adapter-utils typecheck` passed.
- Full workspace `pnpm -r typecheck` and `pnpm build` passed.
- The regression suite fails on the unchanged base revision (12
failures, 1 pass) and passes with this change (13 passes). The baseline
ran in an isolated scratch copy.
- Full local test coverage was attempted using the official CI shards.
The run was stopped after macOS Postgres shared-memory exhaustion and
CLI timeouts under load. The affected server database suite passed in
isolation (31 tests), as did the five affected DB/CLI suites (89 tests).
- [Full Linux
CI](https://github.com/paperclipai/paperclip/actions/runs/34258333833)
passed on `4e1426f5b`: all general and serialized test shards, all
browser shards, typecheck and release registry checks, native runner
verification, application build, and release canary dry run.
- Greptile scored the latest commit 5/5 with no unresolved findings.
- Shell tests use isolated local fixtures and make no provider or model
requests. No live sandbox qualification is claimed.

## Risks

- Remote startup adds one bounded path query when no explicit override
exists. A failed query stops startup.
- Explicit remote path overrides still control which tools are
available. Invalid overrides now fail the command check earlier.
- Credential selection and GitHub broker policy are unchanged. Tests
verify managed wrappers stay first and can invoke underlying commands.
- No database migration or sandbox image replacement is required.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository editing, and
terminal tools. The exact serving model ID and context-window size are
not exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-08 12:56:42 -05:00
Dotta db85bf4b7a
fix(ui): simplify GitHub repository access controls (#13047)
## Thinking Path

> - Paperclip helps people manage AI agents and their connected apps.
> - GitHub permissions show which repositories an account can access.
> - The page adds an account dropdown and search above the repository
list.
> - These controls add clutter to a view meant to show the full access
list.
> - This change removes both controls and their filtering state.
> - People see all repositories directly, with the existing scrolling
and access controls.

## Linked Issues or Issue Description

Refs #12998.

**Current behavior**

GitHub permissions show an “All accounts” dropdown and a “Search
repositories” input above the repository list.

**Proposed behavior**

Show the full repository list directly. Keep the scroll limit,
repository links, private-repository icons, refresh button, and GitHub
configuration links.

**Reason and benefit**

Remove unnecessary controls from the access summary. Users manage
repository permissions on GitHub.

## What Changed

- Remove the account dropdown, search input, filtering state, and unused
imports.
- Rename the GitHub configuration button to “Add More Repos on GitHub”.
- Render every returned repository and simplify the empty-list message.
- Update the existing multi-account test and permissions documentation.

## Verification

- All 55 AppDetail tests passed.
- `pnpm check:token-gates` passed.
- Full `pnpm build` and `pnpm -r typecheck` passed. All 31 CI checks
passed on `cb9b8bb14a880d926393c62821265fbd2d8c5d31`. Storybook visual
regression was correctly skipped.
- Fresh Greptile review on the same commit: 5/5, with no unresolved
findings.

## Risks

Users can no longer narrow this list by account or search text. The list
still scrolls and shows each repository’s full owner/name. This change
does not alter GitHub permissions or credential selection.

## Model Used

OpenAI GPT-6 through Codex, with code editing and shell verification
tools. The exact model variant and context-window size are not exposed
in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-08 12:28:23 -05:00
Dotta be6bb768b1
fix(ui): restrict company navigation to accessible memberships (#13039)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board selects a company before it loads that company's inbox and
tasks.
> - Instance administrators can list companies where they have no
membership.
> - The board treated that directory as a list of companies the user
could enter.
> - This pull request gives navigation a list based on the existing
company access check.
> - Users can select their companies without landing on an inbox that
rejects their access.

## Linked Issues or Issue Description

Fixes #6090. Refs #4855 for the related account-recovery case; this PR
does not grant company membership.

**What happened?**

An instance administrator can select a company where they have no
membership. Its inbox then shows “User does not have access to this
company.” Company directory visibility and access to company contents
use different rules.

**Expected behavior**

Company navigation should show only companies the current user can
enter. A stored selection for an inaccessible company should fall back
to an accessible company. A direct link to an inaccessible company
should use the existing unavailable-company page.

**Steps to reproduce**

1. Create Company A and Company B with separate owners.
2. Sign in as an instance administrator who belongs only to Company A.
3. Select Company B through a stored selection or a link with its
prefix.
4. Observe that the board accepts the company selection, but
company-scoped requests return 403.

Related: #10524 lets cloud users enter additional companies where they
hold memberships. This fix preserves that access and excludes companies
where they have no membership.

## What Changed

- Added `scope=accessible` to `GET /api/companies`, using the existing
`hasCompanyAccess` predicate.
- Changed the board navigation list to request that scope. Instance
Access uses a separate unscoped, account-keyed directory so
administrators can manage all companies. Membership edits refresh
navigation.
- Reject empty, unknown, and repeated scope values with 400. Directory
loading errors offer a retry before access controls are shown.
- Added route tests for cloud, session, board-key, local trusted,
non-member, and agent access.
- Added client and component tests for navigation/admin request
isolation, grants outside the navigation list, self-membership refresh,
directory failure recovery, and forbidden administration.
- Updated the API guide and OpenAPI document.

## Verification

- Latest commit: 36 focused UI tests passed. The broader UI shard passed
all 281 files / 2,533 tests after correcting an asynchronous test
assertion.
- Server authorization and OpenAPI regression suites: 31 tests passed.
- UI typecheck and `pnpm check:token-gates`: passed.
- `pnpm -r typecheck` and `pnpm build`: passed after review fixes.
- Full local test runner: exercised the supported shards. Several
unrelated suites hit embedded PostgreSQL startup failures or startup
timeouts under local load. The UI regression issue found in the broad
run was corrected and its full UI shard passed. These local limitations
are not reported as a green full-suite result.
- [GitHub
CI](https://github.com/paperclipai/paperclip/actions/runs/34233416473):
all checks green on `4e1698cd4` — all server and workspace test shards,
all browser end-to-end shards, typecheck/release registry, build, canary
dry run, policy, and Docker context integrity. Security checks also
passed.
- Greptile: 5/5 on the latest commit; both initial findings addressed
and all review threads resolved.

## Risks

- The UI now excludes companies visible only through instance
administrator status. Company membership continues to control access to
contents.
- Additional companies with active memberships remain available.
- The client and server changes must ship together. An older server
ignores the new query parameter and retains the previous behavior.
- No database migration or permission grant changes.

## Model Used

- OpenAI GPT-6 through Codex, with reasoning, repository inspection,
code editing, and test execution. The exact served model identifier and
context window are not exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-08 11:57:23 -05:00
Jannes Stubbemann 5752d6bd93
fix(heartbeat): block runs on a stuck sandbox plugin and re-enable errored bundled plugins at boot (#12957)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents run inside environments. A sandbox environment gets its
sandbox from a provider plugin (for example the bundled
`paperclip.kubernetes-sandbox-provider`), and every run starts by
acquiring a lease through that plugin.
> - When a plugin activation fails once (on a hosted deployment: one
`RPC call "initialize" timed out after 15000ms`), the loader calls
`markError`. That persists `status = error` on the plugin row and
switches off worker auto-restart. Boot activation (`loadAll`), the
bundled-plugin bootstrap and the lazy worker recovery all consider only
`ready` plugins, so the plugin stays in `error` across restarts until an
operator enables it by hand.
> - Every run that needs the provider then fails before dispatch with
`Sandbox provider "kubernetes" is installed via plugin "...", but that
plugin is currently error.` That message matches neither the retryable
classifier (`... but its worker is not running`) nor any configuration
classifier, so the run is recorded as a plain `setup_failed`, the issue
is released, and the scheduler dispatches the same failing run again on
the next tick. On the hosted deployment one company produced about
11,300 identical failed runs, one every 30 seconds, for a week (#12953
is a customer's report of the same condition).
> - Two gaps cause this: the heartbeat treats a condition that only an
operator can change as a transient setup failure, and the bundled-plugin
bootstrap never gives a plugin in `error` another chance even though the
bundle ships with the release image.
> - This pull request classifies the "installed but not ready" lease
failure as `configuration_incomplete`, so the existing recovery path
moves the issue to `blocked` with one recovery action and an actionable
notice; and it re-enables a bundled plugin found in `error` once per
boot, so the next server restart heals the plugin.
> - The benefit is that a stuck provider plugin surfaces as one blocked
issue per task with clear next steps, instead of an endless stream of
identical failed runs, and a restart repairs the plugin without an
operator having to know the plugin API.

## Linked Issues or Issue Description

- Refs #12953 — hosted report: "that plugin is currently error" on every
run for six days, including runs that were retried by hand. This PR
stops the retry loop (issue goes to `blocked`) and makes a server
restart re-activate the bundled plugin. It does not change how a managed
Kubernetes environment is provisioned for a company, which the same
report also mentions.
- Related PR: #9760 pauses the agent for the permanent `Adapter "..." is
not in the configured adapter registry` setup failure. This PR handles a
different permanent condition (plugin not `ready`) and routes it through
the existing `configuration_incomplete` recovery path (issue-level block
with a recovery action) rather than an agent-level pause, because the
gap is on the plugin, not on the agent. The two do not overlap in code
paths.
- No existing issue covers the bundled-plugin re-enable. Bug
description:

**What happened**

A bundled sandbox provider plugin went to `status = error` after one
failed activation. It stayed in `error` across every later server
restart. Every run for every agent on that provider failed lease
acquisition in under a second with `... but that plugin is currently
error.` (`setup_failed`), and the heartbeat kept dispatching new runs
that failed the same way.

**Expected behavior**

A run that fails because its provider plugin is not `ready` is recorded
as a configuration gap and the issue is moved to `blocked` with a notice
that names the plugin and its status, so no further runs are dispatched
until an operator acts. A bundled plugin left in `error` gets a fresh
activation attempt on the next boot.

**Steps to reproduce**

1. Install a sandbox provider plugin and create a sandbox environment
that uses it; make it an agent's default environment.
2. Set the plugin row's status to `error` (or make its worker fail
`initialize` once so the loader does it).
3. Assign an issue to the agent and let the heartbeat run it.
4. Observe: the run fails with `... but that plugin is currently error.`
as `setup_failed`, the issue is released, and the next tick dispatches
another run that fails the same way. Restart the server: the plugin is
still `error`.

**Paperclip version**

master at 856813ba3 (`fix(connections): distinguish local setup from
provider handoff (#12947)`).

**Deployment mode**

Hosted (Kubernetes, bundled kubernetes sandbox provider plugin). The
heartbeat behavior is the same in self-hosted mode.

## What Changed

- `server/src/services/heartbeat.ts`
- New exported `parseSandboxProviderPluginNotReadyFailureMessage()`
recognises environment-runtime's `not_ready` lease message (`... is
installed via plugin "<key>", but that plugin is currently
error|disabled|upgrade_pending`) and returns the provider, plugin key
and status. It does not match the transient `... but its worker is not
running` message (still retried) or the permanent "not installed"
message (unchanged).
- In the setup-failure catch, a matched message sets `errorCode =
configuration_incomplete` and records (independently of whether the
agent lookup succeeded) a `configurationIncomplete` payload with
`reason: "sandbox_provider_plugin_not_ready"`, the provider,
`pluginKey`, `pluginStatus`, and a `fingerprint` of
`sandbox_provider_plugin:<key>:<status>`, so repeated failures on the
same stuck plugin reuse one recovery action. The existing recovery flow
then blocks the issue, skips the infra retry, and posts one notice.
- The two places that build the configuration-incomplete notice now pass
the run's payload so the notice can name the specific gap.
- `server/src/services/recovery/stranded-notice.ts`:
`buildConfigurationIncompleteRecoveryNoticeSeed` takes the optional
payload. For `sandbox_provider_plugin_not_ready` the body names the
plugin and its status and gives status-specific guidance
(`sandboxProviderPluginRemedy`): review and approve the upgraded
capabilities before enabling for `upgrade_pending`, enable again for an
operator `disabled`, enable or restart for `error`. Other reasons keep
the secret/env-binding wording. Exports
`SANDBOX_PROVIDER_PLUGIN_NOT_READY_REASON`.
- `server/src/services/recovery/service.ts`: the recovery action's
`nextAction` for this reason uses the same status-specific guidance
instead of "bind the missing secret(s)". Small refactor:
`readConfigurationIncompletePayload` backs the existing fingerprint
reader.
- `server/src/services/bundled-plugins.ts`
- `ensureBundledPlugins` no longer skips a present bundled plugin whose
status is `error`. It logs at `warn` with the row's `lastError`, resets
the row to `ready` with `lastError` cleared through
`registry.updateStatus` (a plain status reset, not `lifecycle.enable()`,
so no `plugin.enabled` event fires before the worker runs; the startup
`loadAll()` that follows does the activation and its events), and
continues boot on failure. This runs once per boot by construction; if
activation fails again the loader marks `error` again and nothing
retries until the next boot.
- `installed`, `ready`, `disabled` and `upgrade_pending` rows are still
skipped, so an operator's `disabled` stays untouched.
- `BundledPluginProvisionerDeps` gains `registry.updateStatus` and
`logger.warn`; `app.ts` already passes objects that have both.
- `doc/plugins/PLUGIN_SPEC.md`: one bullet in 12.4 Failure Policy about
the once-per-boot re-enable of bundled plugins.
- Tests
- `server/src/__tests__/bundled-plugins.test.ts`: re-enables an `error`
row exactly once with the `lastError` in the warn log and no reinstall;
continues boot and provisions later entries when `enable` throws; still
skips `installed`/`ready`/`disabled`/`upgrade_pending` without calling
`enable`.
- `server/src/__tests__/heartbeat-process-recovery.test.ts` (embedded
PostgreSQL): a plugin row in `error` plus a sandbox environment produce
a run with `errorCode = configuration_incomplete` and the expected
payload, the adapter is never dispatched, no retry or second run is
created, the issue is `blocked`, the recovery action is
`configuration_validation` with the plugin next action, and the notice
names the plugin key and status. Plus a unit case for the message parser
(positive for the three statuses and a wrapped message, negative for
both other sandbox messages).
- `server/src/services/recovery/stranded-notice.test.ts`: the
plugin-specific body, and the unchanged secret-binding body for other
reasons.

## Verification

- `cd server && pnpm typecheck` — passes.
- `cd server && pnpm exec vitest run
src/__tests__/bundled-plugins.test.ts` — 29 tests pass.
- `cd server && pnpm exec vitest run src/services/recovery/` — 77 tests
pass (includes the stranded-notice and classification suites).
- `cd server && pnpm exec vitest run
src/__tests__/heartbeat-process-recovery.test.ts -t "sandbox
provider|retryable pattern|secret ref has no binding"` — 5 tests pass:
the two new cases, the existing transient worker-restart retry, the
existing non-retryable "not installed" escalation, and the existing
secret-binding `configuration_incomplete` block (embedded PostgreSQL).
- Manual check for a reviewer: set a sandbox provider plugin row to
`status = 'error'`, run an agent on that provider, and confirm the issue
moves to `blocked` with a "Configuration incomplete" notice that names
the plugin, and that no second run appears. Restart the server and
confirm the boot log shows `bundled plugin is in error status from a
previous activation; re-enabling it for this boot` followed by normal
activation.

## Risks

- Behavior change: a run against a plugin in `error`, `disabled` or
`upgrade_pending` now blocks the issue instead of failing as
`setup_failed` and being re-picked. For `disabled` this is deliberate:
an operator switched the plugin off, and re-dispatching cannot help. The
block is reversible from the issue (retry or reassign) like every other
`configuration_incomplete` block.
- The classifier is anchored on the exact `... but that plugin is
currently <status>` phrase from `environment-runtime.ts`. If that
message changes, the run falls back to the previous `setup_failed`
behavior (no worse than today). A unit test pins the phrase.
- Bundled re-enable: a bundled plugin whose activation fails on every
boot now costs one activation attempt (the `initialize` timeout, 15 s by
default) per boot instead of none. It runs inside the existing
non-awaited bootstrap chain, so boot time is unaffected. Non-bundled
plugins are untouched.
- No migration, no schema change. The `configurationIncomplete` payload
is JSON in `heartbeat_runs.result_json`, read only by the recovery
service.

## Model Used

- Claude Fable 5.1 (`claude-fable-5-1`) via Claude Code, extended
thinking, tool use (file edits, shell, test runs). The change was
produced with the model and reviewed by the submitting human.

## 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

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

https://claude.ai/code/session_014t3bi2beVNVVHAxK36dmXm

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 09:00:10 -07:00
Jannes Stubbemann 023e640a7e
fix(db): reap idle pool connections, name the pool, and end it on shutdown (#12956)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The server keeps one postgres.js pool (`packages/db/src/client.ts`,
`createDb`) for every query it runs. #10795 made the pool tunable from
the environment, but the defaults stayed at the driver defaults: an idle
connection never closes, the pool reports itself as `postgres.js`, and
no code path ever calls `sql.end()`.
> - On a hosted Paperclip deployment the server entered a restart loop
(a bundled plugin failure that #12953 describes made every run fail, and
the pool saturated). Each generation opened its ten connections, died,
and left the backends open on the PostgreSQL side until TCP keepalive
reaped them hours later. After about 20 generations the backends
exceeded `max_connections`, and every later boot died on its first
bootstrap query with `sorry, too many clients already`, before
`server.listen()`. The loop could not heal itself. #9555 describes the
same shape on a launchd-supervised self-hosted install.
> - Three properties of the pool combine to make this possible: idle
connections are never reaped, the pool is never ended on any exit path,
and an operator cannot even find the leaked backends in
`pg_stat_activity` because they carry the generic driver name.
> - This pull request gives the pool a 60 second idle timeout and the
`paperclip` application name by default, exposes `max_lifetime` and
`application_name` through the same `DATABASE_*` environment contract
that #10795 introduced, and ends the pool on the orderly SIGINT/SIGTERM
path and on the fail-loud startup path.
> - The benefit is that a restarting or crash-looping server releases
its backends instead of accumulating them, and an operator can see and
count Paperclip's connections.

## Linked Issues or Issue Description

- Refs #9555 — database connection pool leak causes an infinite restart
loop under load. This PR closes the "pool never ends, idle connections
never close" part of that report.
- Refs #12953 — hosted outage report. The pool exhaustion is the second
half of that incident; the first half (a stuck sandbox provider plugin)
has its own PR.
- Related prior PRs: #9597 and #8780 both propose hard-coded
`idle_timeout` / `max_lifetime` values in `createDb`. Both predate
#10795 (merged), which made these options environment-driven; this PR
builds on the merged shape and adds the shutdown `end()` that neither
covers. #4006 and #7481 are closed earlier attempts in the same area.

## What Changed

- `packages/db/src/client.ts`
- New `resolveDatabaseClientOptions()` applies Paperclip defaults on top
of the environment: `idleTimeoutSeconds` defaults to 60
(`DEFAULT_DATABASE_IDLE_TIMEOUT_SECONDS`) and `applicationName` to
`paperclip` (`DEFAULT_DATABASE_APPLICATION_NAME`). `createDb` uses it
for both the environment path and explicit options.
- `DATABASE_IDLE_TIMEOUT_SECONDS` now accepts `0` to restore the driver
default (keep idle connections open). Negative or non-integer values
still throw.
- New environment variables: `DATABASE_MAX_LIFETIME_SECONDS` (positive
integer, maps to `max_lifetime`) and `DATABASE_APPLICATION_NAME`
(non-empty string, maps to `connection.application_name`).
  - `postgresJsOptions()` maps the two new options.
- `server/src/shutdown.ts`
- `finalizeServerShutdown` gains two optional ordered steps:
`closeHttpListener` runs first, before the application services stop;
`closeDatabase` runs after the application services and before the
embedded PostgreSQL stop. A failure in either is logged and does not
stop the teardown. Final order: listener → application services →
database pool → embedded PostgreSQL → instrumentation → Sentry.
- New `closeHttpListenerForShutdown()`: stops accepting requests, closes
idle keep-alive sockets, waits up to 5 s for open connections, then
closes whatever is left. Requests still in flight are drained while
every service is available, and none can reach a route after
`sql.end()`, on the signal path and the programmatic path alike (the
programmatic path's later `server.close` finds the listener closed and
skips).
- `server/src/app.ts`: the app shutdown hook (`shutdownAppServices`) now
stops the plugin job scheduler, whose tick queries the database, so a
programmatic `shutdown()` leaves no timer running against the ended
pool.
- `server/src/index.ts`
- `startServer()` is now a thin wrapper around the boot sequence. When
the boot sequence throws after the pool exists, the wrapper ends the
pool (and the separate migration pool, when configured) before it
rethrows. This covers the `process.exit(1)` path in the main module and
the CLI `paperclip run` path alike.
- The orderly shutdown passes the same `closeDatabaseClients` to
`finalizeServerShutdown`.
- `endDatabaseClient` tolerates a client without `$client` (test
doubles) and uses a 5 second end timeout.
- Docs: `docs/deploy/database.md` gets a "Connection Pool Settings"
table with every `DATABASE_*` pool variable, its default and its effect;
`doc/DATABASE.md` lists the two new variables.
- Tests
- `packages/db/src/client-options.test.ts`: parsing of the new
variables, `0` for the idle timeout, rejection of malformed values,
driver option mapping, and the `resolveDatabaseClientOptions` defaults.
- `packages/db/src/client.test.ts` (embedded PostgreSQL):
`createDb(url)` reports `application_name = paperclip` for its own
backend, and a pool with `idleTimeoutSeconds: 1` has zero backends in
`pg_stat_activity` after the timeout.
- `server/src/shutdown.test.ts`: the listener closes before the
application services, and the database close runs between the
application services and the embedded PostgreSQL stop; a failing
database close is logged while the teardown still finishes;
`closeHttpListenerForShutdown` closes idle sockets and resolves on
close, force-closes after the grace period, and is a no-op when the
listener was never bound.

## Verification

- `pnpm --filter @paperclipai/db typecheck` — passes (`check:migrations`
+ `tsc --noEmit`).
- `cd server && pnpm typecheck` — passes.
- `cd packages/db && pnpm exec vitest run src/client-options.test.ts
src/client.test.ts src/client-teardown-registry.test.ts` — 9 + 18 + 3
tests pass (the `client.test.ts` cases need embedded PostgreSQL; the new
one waits up to 10 s for the idle reap and passed in about 3 s).
- `cd server && pnpm exec vitest run src/shutdown.test.ts
src/__tests__/server-startup-feedback-export.test.ts
src/__tests__/bootstrap-claim-routes.test.ts` — 34 + 11 tests pass. The
startup-feedback suite exercises `startServer()` with a mocked
`createDb`, which is why `endDatabaseClient` tolerates a client without
`$client`.
- Manual check for a reviewer: start the server against any PostgreSQL,
then run `SELECT application_name, state, count(*) FROM pg_stat_activity
GROUP BY 1, 2;`. Paperclip's backends now show `paperclip`. Leave the
server idle for more than 60 s and the idle backends disappear. Send
SIGTERM and the backends close before the process exits.

## Risks

- Behavior change with no environment set: idle pooled connections now
close after 60 s. The next query after an idle period pays a reconnect
(single-digit milliseconds on a local socket). postgres.js reconnects
transparently. Set `DATABASE_IDLE_TIMEOUT_SECONDS=0` to keep the
previous behavior.
- `application_name` changes from `postgres.js` to `paperclip`. Anything
that filtered `pg_stat_activity` on the old name would need an update;
nothing in this repo does.
- The HTTP listener now closes at the start of the final teardown (after
the heartbeat run drain, which still needs the API for running agents).
The pool close runs after the application services. A late query from a
timer that survived the service shutdown would fail with a driver
"connection ended" error instead of running; the known database-backed
timer (the plugin job scheduler) is now stopped in the service shutdown.
- The listener drain adds at most 5 s to a shutdown while long-lived
connections (for example WebSocket clients) are open; after that they
are closed forcibly.
- `startServer()` is split into a wrapper and the boot sequence. The
exported signature and return type are unchanged.
- No migration, no schema change.

## Model Used

- Claude Fable 5.1 (`claude-fable-5-1`) via Claude Code, extended
thinking, tool use (file edits, shell, test runs). The change was
produced with the model and reviewed by the submitting human.

## 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

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

https://claude.ai/code/session_014t3bi2beVNVVHAxK36dmXm

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-08 08:56:39 -07:00
Dotta c723bb4dfc
fix(skills): reuse validated runtime revisions during preparation (#13042)
## Thinking Path

> - Paperclip manages AI agents and prepares their runtime inputs before
each turn.
> - Shared company skills are part of those inputs for native and legacy
adapters.
> - Runtime materialization refreshed the full inventory again for every
declared file.
> - Remote skill directories were also downloaded and rebuilt on every
turn.
> - Measured preparation took 42–73 seconds while runner execution took
7–9 seconds.
> - This change reads the inventory once and reuses validated installed
revisions.
> - Agents retain their selected skills while repeated preparation
avoids upstream work.

## Linked Issues or Issue Description

**What happened?**

One 114-skill preparation performed 407 inventory refreshes, 48
directory rebuilds, and 388 GitHub file fetches. Reusing existing local
copies took 151 ms.

**Expected behavior**

Each listing refreshes inventory once. Unchanged installed remote
revisions reuse complete, validated local copies. Local edits remain
visible. Explicit updates select new revisions.

**Steps to reproduce**

1. Import GitHub skills with supporting files.
2. Run an agent turn, then run another with the same installed
revisions.
3. Observe repeated inventory scans, downloads, and runtime directory
replacement before execution.

Related prior attempts: #2330 and #9268 (still open; #9268 last updated
July 9). Those use a marker compared with `updatedAt`. This patch
follows the required content validation, immutable revision, company
isolation, atomic publication, and read-only semantics, and removes
refresh-per-file multiplication.

## What Changed

- Split public file reading from reading an already loaded skill.
Runtime listing refreshes inventory once.
- Add a company-scoped revision cache with file manifests outside the
delivered skill directory. Fingerprints omit cosmetic metadata.
- Validate exact file inventory, sizes, and hashes before warm reuse.
Reject traversal and symlinks. Stage complete builds and serialize
atomic publication across processes.
- Preserve local/catalog direct sources, stored Markdown fallback,
explicit version snapshots, and legacy mutable-ref compatibility. Report
missing supporting files and keep older valid revisions readable.
- Clean both runtime layouts on rename/removal and record
`skills.prepare` under preparation timing.
- Add service/cache regressions and an isolated 114-skill benchmark,
including a new-process warm run.

## Verification

- Final targeted skill-service/cache/trace validation: 86 tests pass (61
embedded-PostgreSQL service tests, 19 cache tests, 6 trace tests).
Database tests executed rather than skipped. Focused skill routes,
adapter selection, and native runtime context also pass.
- `pnpm -r typecheck` and `pnpm build` pass locally at `22caa1fe4`.
- The full `pnpm test:run` matrix passes on supported Linux CI at the
final head: [CI
run](https://github.com/paperclipai/paperclip/actions/runs/34236097762).
Local full-suite execution encountered PostgreSQL startup contention, a
random allocated-port boundary, and a socket hang-up; every affected
suite passed on an isolated rerun. The interrupted local serialized run
is not claimed as a complete local pass.
- Repeatable benchmark: `pnpm --filter @paperclipai/server exec tsx
../scripts/benchmark-skill-preparation.ts`. Mixed 114-skill inventory
with 429 remote files on Linux: cold 286 ms, warm median 96 ms / maximum
153 ms including a new process. Every warm sample performs one refresh,
zero upstream fetches/rebuilds, and reports no missing entries; content
assertions pass.
- Controlled deployment against the previously deployed revision
completed with zero lost runs. Real inventory: 114 skills, 670 declared
files; 402 cached files match the prior installed copies byte-for-byte.
Ten post-deployment warm preparations: median 129 ms / maximum 208 ms;
new-process warm 194 ms, zero downloads/rebuilds/missing entries.
- Five sequential real browser questions persisted in 10.6–20.7 s
(median 12.2 s), versus 50–83 s before. Skill preparation median 240 ms,
with one 2.37 s outlier. Total preparation median 3.337 s / maximum
8.728 s **does not fully meet** the <3 s / <5 s target. The excluded
historical-run redaction query takes about 1.36 s per scan at two
preparation call sites; wider application latency coincided with the
outlier, without a cache rebuild. These residuals are reported rather
than discarded.
- Disposable skill reimport verified through actual selected-skill runs:
the next run read the changed code. Fixture removed and agent
configuration verified unchanged.
- Greptile 5/5, zero unresolved review threads, all final-head CI checks
green.

## Risks

- Cold preparation still requires upstream availability for supporting
files. An unavailable revision is reported missing and never falls back
to an older revision.
- Valid older revisions and quarantined invalid entries consume additive
disk space until skill cleanup. An abruptly killed publisher can leave a
lock that requires operator cleanup after confirming its PID is dead.
- Warm validation reads all cached file bytes. Very large inventories
still have proportional local I/O cost.
- No HTTP API, schema, agent configuration, or first-party Telemetry
changes. OpenTelemetry retains its operator endpoint gate.

## Model Used

OpenAI GPT-6 in Codex, with reasoning, repository inspection, code
editing, and test execution. The exact serving snapshot and
context-window size are not exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-08 09:35:32 -05:00
Dotta 0cc796b7bd
Build isolated preview artifacts for exact-source deployments (#13041)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Managed deployments need a cloud image and a database migration
package.
> - Branch commits can lack both artifacts until a normal release runs.
> - Operators need to test an exact commit without advancing release
aliases.
> - This pull request adds a preview build mode to the existing release
workflow.
> - Builds use an immutable source SHA and publish isolated, reusable
artifacts.

## Linked Issues or Issue Description

**Subsystem affected**

Release automation, cloud Docker images, and shared/database npm
packages.

**Problem or motivation**

An operator cannot deploy an unpublished branch with new migrations
using only
the normal release artifacts. Publishing it through a normal lane would
also
advance shared release aliases.

**Proposed solution**

Dispatch the trusted release workflow on master with a full source SHA
and a
request UUID. Build missing SHA images and, when needed, deterministic
preview
shared/DB packages. Publish packages under the preview dist-tag with
exact
workspace pins. Reuse matching artifacts on retries.

**Roadmap alignment**

This extends release tooling for operator validation. It does not add a
core
product feature or duplicate a planned product capability. Related PR
searches
found no duplicate preview deployment workflow.

## What Changed

- Add the preview channel, request correlation, artifact checks, and
result artifact.
- Compile source packages in a separate job from the npm publisher. The
publisher
  uses trusted master code and disables package lifecycle scripts.
- Publish only SHA cloud image tags. Preserve release aliases. Use
full-SHA tags and no shared build cache.
- Verify full source identity for reused packages and images. Both image
and npm publishers
use isolated jobs and the externally master-restricted npm-canary
environment. Fail on registry
  authentication errors, outages, or artifact identity mismatches.
- Let bundled-package preparation use patches from the requested source
checkout.
- Document publishing configuration, artifact contracts, and deployment
order.

## Verification

- Passed `pnpm -r typecheck` and `pnpm build`.
- Passed `pnpm test:release-registry`: 107 tests, including eight
preview tests.
- Passed `actionlint -shellcheck= .github/workflows/release.yml`.
- Built real shared and DB preview tarballs from an isolated exact-SHA
checkout.
Verified package source identity and all 244 SQL files and journal
entries.
- Verified the full revision behind an existing published SHA cloud
image.
- `pnpm test:run` exposed missing local embedded PostgreSQL library
symlinks.
The package's postinstall repair restored initdb; all 12 previously
affected
suites passed on rerun (95 tests). Additional local matrix reruns are in
progress.
The complete PR CI matrix is green, including general/serialized tests,
e2e,
typecheck, build, release registry, canary dry run, and the required
verify gate.
- Live preview publication and staging deployment require this workflow
on master
and the compatible control-plane backend. They have not run yet. No
production
  deployment was performed.

## Risks

Preview npm versions are immutable public artifacts. Both packages must
retain
their trusted publisher for release.yml in environment npm-canary.
Source builds
must remain separated from privileged npm publishing. The deploying
control plane
must verify source identity, integrity, and migration compatibility
before use.

Normal release jobs retain their existing conditions. Roll back by
stopping preview
dispatches and reverting the workflow/tooling. Published preview
versions remain
isolated from normal release tags.

## Model Used

OpenAI GPT-6 through Codex, with repository tools, code execution, and
test runs.
The session does not expose a more specific model version 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 the available 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
- [x] I have described the issue in-PR following the feature template
- [x] I have not referenced internal or instance-local issues or links
- [x] My branch name describes the change and contains no internal
ticket identifier
- [ ] I have run the full tests locally and they pass
- [x] I have added tests for the new behavior
- [x] I have updated relevant documentation
- [x] I have considered and documented risks
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open recommendations or follow-ups
- [x] I will address review comments before requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-08 09:21:58 -05:00
Dotta b97101893f
feat(projects): select multiple GitHub source repositories (#13010)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Projects give tasks a common source repository and execution
context.
> - The current project form asks for a raw URL and unrelated metadata.
> - Teams need to select several repos from GitHub connections they can
use.
> - This pull request implements the reviewed project form and
repository editor.
> - The server checks credential ownership and shared audiences before
discovery.
> - Existing workspace URLs and runtime identity rules remain
compatible.

## Linked Issues or Issue Description

**Problem or motivation**

Project creation accepts one raw repository URL. It does not help users
select repos from their usable GitHub connections or attach several
repos together.

**Proposed solution**

Add a shared GitHub repository picker to project creation and
Configuration. Support multiple selections, transactional persistence,
and the existing GitHub setup flow. Simplify the project form and
Configuration tab as reviewed.

**Alternatives considered**

Keep a raw URL field or add a separate repository table. The existing
workspace collection already supports several repositories and keeps
legacy URLs compatible.

**Roadmap alignment**

This builds on the shipped MCP Tool Gateway and Apps capability. It does
not change runtime credential delegation.

Related work: #11662 addresses the existing dialog's viewport limits.
#4552 addresses generic Git URLs; this change preserves those URLs in
existing workspaces.

## What Changed

- Add company-scoped repository discovery from usable personal and
shared GitHub grants, with provider-ID deduplication, PAT pagination,
and partial failure handling.
- Document the repository endpoints and board access requirements in
OpenAPI.
- Validate new selections and save projects with multiple repository
workspaces in one transaction. Preserve legacy URLs and existing
selections whose access was lost.
- Implement the reviewed Create project dialog, shared repository
editor, scrolling, and mobile layout.
- Move repositories above environment variables, remove Status and Goals
controls and env help paragraphs, move Created to the bottom, and
redirect Overview to Configuration.
- Reuse GitHub setup in dialogs, preserve project drafts, and verify
popup completion through the API.
- Replace the configuration story's DOM adapter with explicit production
composition. Keep the reviewed mobile and short-viewport stories.

## Verification

- Passed: `pnpm build`, `pnpm -r typecheck`, `pnpm build-storybook`, and
`pnpm check:token-gates`.
- Passed: focused repository access, database persistence,
configuration, and connection setup tests.
- Passed: `pnpm exec playwright test --config
tests/e2e/playwright.config.ts tests/e2e/project-repositories.spec.ts`.
- The browser tests use a real temporary server/database. They cover
create, forty persisted repos, mobile scrolling, save/reload, legacy URL
editing, and rejection without a partial project.
- GitHub responses and popup completion use deterministic fixtures. No
real GitHub account was authorized by the test suite.
- All CI general, serialized server, and browser test shards pass on the
final commit.
- The local full-suite run overlapped review edits and was stopped;
fresh repository, OpenAPI, UI/CLI, and connection tests pass. Unrelated
local worker, built-in-agent, and routine timing/socket failures passed
isolated reruns.
- Final commit `1b3308dca`: all CI gates pass, including build, runner
verification, typecheck, canary dry run, and security checks. Greptile
is 5/5 with no unresolved review threads.
- Storybook visual regression is opt-in and was skipped by CI; the
Storybook build passed locally.

## Risks

- Repository discovery depends on provider availability. Failed
connections are reported while successful results stay usable.
- Selections identify source workspaces; they do not grant agents new
credentials. The existing primary-workspace and responsible-user
identity rules still apply.
- No database migration is needed. Existing API status, goals, dates,
and manual workspace URLs remain supported.

## Model Used

OpenAI Codex, based on GPT-6, with repository inspection, code
execution, and browser tools. The runtime does not expose a more
specific model deployment 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>
2026-09-08 08:21:28 -05:00
Dotta 297d8741f5
fix: resolve duplicate connections to the same GitHub account (#13022)
## Thinking Path

> - Paperclip lets people share agents while keeping GitHub access
personal.
> - Each managed Git or GitHub operation selects an eligible connection
grant.
> - Connecting the same GitHub account twice creates two grants.
> - The old resolver counted grants and rejected them as competing
identities.
> - Managed commands then ran anonymously and reported a misleading
login failure.
> - This change compares GitHub account IDs and selects one eligible
grant for the same account.
> - The benefit is reliable access after reconnecting, with clear
diagnostics for real failures.

## Linked Issues or Issue Description

Refs #13005.

**What happened?**

Two active connections owned by one Paperclip user pointed to the same
GitHub account. Managed Git refused both as ambiguous. The agent could
not push, although the account was connected and had repository access.

**Expected behavior**

Multiple grants for the same GitHub account resolve to one eligible
authorization. Different accounts remain ambiguous. Unavailable access
explains its cause without blocking unrelated work.

**Steps to reproduce**

1. Connect the same GitHub account twice for one Paperclip user and
allow the shared agent through both connection audiences.
2. Start an instruction as that user.
3. Run managed gh or git push. Before this fix, no credential is
provided.

## What Changed

- Compare stable GitHub account IDs when more than one eligible grant
exists. Never deduplicate by login alone.
- Prefer an available grant, then the newest authorization with a stable
ID tie-breaker. Refresh and webhook timestamps do not change the
selection.
- Keep the selected credential and connection policy together. Do not
combine permissions or fall back from a dedicated account to a personal
account.
- Print the redacted unavailable reason in managed command output.
Unrelated local operations still work anonymously.
- Add database and executable launcher regressions, and document
selection behavior.

## Verification

- Final `pnpm -r typecheck` and `pnpm build` passed.
- Fourteen operation credential integration tests passed, covering
duplicate personal/dedicated grants, incomplete credentials, distinct
accounts with the same login, missing identity metadata, revocation,
membership, connection audiences, and A → B → A steering. Existing Git
credential and gateway suites and both executable launcher tests also
passed.
- The local broad test run encountered three embedded-Postgres lifecycle
timeouts and stale modules from edits made during that run. A fresh
process rerun of all four affected suites passed all 35 tests. The full
Node 24 CI test matrix passed on the final commit.
- CI passed all 31 checks on `797973b30beb16ba5fa69ed281835e1ab812b449`
(Storybook visual regression was correctly skipped). An unrelated
Company Settings UI test failed once; the focused local reproduction and
rerun of its CI shard both passed without code changes.
- Fresh Greptile review of the final commit: 5/5, with no open findings.
Security checks passed.
- Live acceptance passed with both duplicate connections enabled:
managed `gh api user` returned the expected account, managed `git push`
succeeded, and the agent created #13023 and pushed its review fixes. No
host login or credential changes were used.
- Applied the final source/compiled patch to the affected instance with
backups, after confirming no runs were active. Restarted service health
and the final resolver selection were verified. The patch is an overlay
on the existing deployment; this PR supplies the upstream fix.

## Risks

The resolver selects one authorization for an already permitted GitHub
account. It does not combine repository permissions across connections.
If the selected authorization has narrower access, that operation can
still be denied by GitHub. Different provider account IDs and unknown
duplicate identities continue to fail closed. No schema, host
credential, or connection permission changes are included.

## Model Used

OpenAI GPT-6 through Codex assisted implementation and verification with
shell, database, and browser tools. The exact model variant and
context-window size are not exposed in this session.

## Checklist


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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-09-07 22:43:37 -05:00