* origin/master:
fix(server): validate project goal ids exist and belong to the company (#12779)
fix(server): retry cloud-tenant auth sync once on a dropped DB connection (#12773)
fix(security): harden privileged server boundaries (#12776)
fix(agents): redact plaintext env values in agent read and mutation responses (#9860)
fix(runner): materialize pinned OpenCode binary (#12782)
# Conflicts:
# packages/paperclip-runner/package.json
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Projects can link to goals, through the `goalIds` list or the legacy
`goalId` field. The project service writes those links on create and
update.
> - The service never checked the goal ids. A nonexistent id died at the
`projects.goal_id` foreign key as an opaque 500, and the caller got no
actionable feedback — observed live on 2026-09-03, where one caller
retried the same bad id four times.
> - The foreign key also only proves a goal exists, not who owns it. A
goal id from another company linked silently on a multi-company
instance.
> - This pull request asserts every resolved goal id exists under the
caller's company before any write, and rejects with a 422 that names the
unknown ids.
> - The benefit is a clear, actionable client error instead of a 500,
and no cross-company goal links.
## Linked Issues or Issue Description
**What happened?**
`POST /companies/:companyId/projects` with a `goalIds` entry that does
not exist fails with an internal error: `insert or update on table
"projects" violates foreign key constraint
"projects_goal_id_goals_id_fk"`. The caller sees a 500 and retries. A
goal id that exists but belongs to a different company is accepted and
linked.
**Expected behavior**
The request fails fast with a 422 that names the unknown goal id(s).
Goals from other companies are rejected the same way. Valid links behave
exactly as before.
**Steps to reproduce**
1. Create a company and no goals.
2. `POST /companies/:companyId/projects` with `{ "name": "Rocket",
"goalIds": ["<any-uuid>"] }`.
3. Before this change: 500 from the foreign key. After: 422 naming the
id.
**Deployment mode**
Any; observed on an authenticated public deployment.
## What Changed
- `assertGoalsBelongToCompany` in the project service: one query for the
resolved ids scoped to the company; unknown ids produce `unprocessable`
(422) with the ids in the message and details
- called on create (before the project row insert, so no partial writes)
and on update (scoped to the existing project's company); both `goalIds`
and the legacy `goalId` field flow through the same resolution
- new embedded-Postgres test file: valid link, nonexistent id on create
with no partial insert, legacy field, another company's goal on create,
and a foreign-goal update that leaves existing links unchanged
## Verification
- `pnpm vitest run src/__tests__/project-goal-validation.test.ts` — 5
passed
- adjacent suites (`project-icon-persistence`,
`project-shortname-resolution`, `issue-goal-fallback`,
`project-goal-telemetry-routes`, `heartbeat-referenced-projects`,
`projects-list-archived-routes`) — 35 passed
## Risks
- Low risk. One extra indexed select per create/update that carries goal
ids. Requests that previously 500ed now 422; requests that silently
linked a foreign goal now fail — both are corrections, not regressions.
- Existing rows with foreign links (written before this check) are
untouched; only new writes validate.
## Model Used
Claude Fable 5 (claude-fable-5) via Claude Code, extended thinking with
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 (none found for goal-id validation)
- [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 (doc
comments; no user-facing docs affected)
- [x] I have considered and documented any risks above
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Managed-cloud deployments authenticate tenant requests through
trusted headers. The middleware syncs the tenant's user, company, and
membership rows on the way through.
> - Pooled Postgres endpoints sometimes close an established connection
under an in-flight query (pooler recycle, compute suspend). The driver
reconnects on the next query, but the statement on the wire fails.
> - In this path a single dropped statement fails the whole request with
a 500. This happened live on 2026-09-03: the idempotent company
bootstrap insert died with `write CONNECTION_CLOSED`.
> - This pull request retries the actor resolution exactly once when the
error chain carries a postgres.js closed-connection code. The sync is
idempotent end to end, so the replay is safe.
> - The benefit is that a routine pooler blip no longer fails an
authenticated request on the entry path.
## Linked Issues or Issue Description
**What happened?**
A cloud tenant request hit the trusted-header authentication middleware
while the pooled Postgres endpoint closed the connection mid-query. The
insert failed with `write CONNECTION_CLOSED <host>:5432` wrapped in a
`Failed query: insert into "companies" …` error, and the request failed.
**Expected behavior**
The driver reconnects on the next query, and every statement in the
tenant sync is idempotent (upserts, on-conflict inserts, deletes; the
write debounce records only after the full sync succeeds). One
in-request retry should absorb the blip and serve the request.
Non-transient failures must keep failing fast.
**Steps to reproduce**
1. Run an authenticated public deployment against a pooled Postgres
endpoint.
2. Have the pooler close the connection while the middleware's tenant
sync insert is on the wire.
3. Before this change the request fails with a 500; after it the retry
serves the request.
**Deployment mode**
Authenticated public (managed cloud), external pooled PostgreSQL.
## What Changed
- `resolveCloudTenantActor` now delegates to the (unchanged) resolution
body through `retryOnTransientDbConnectionError`, which retries exactly
once on a transient closed-connection failure
- `isTransientDbConnectionError` walks the error `cause` chain (drizzle
wraps the driver error) for the postgres.js codes `CONNECTION_CLOSED`,
`CONNECTION_ENDED`, `CONNECTION_DESTROYED`; both helpers are exported
for tests
- New unit test file `cloud-tenant-transient-db-retry.test.ts`:
detection matrix (including a `23505` staying non-transient),
retry-once-then-succeed, no-retry on non-transient,
propagate-on-second-failure
## Verification
- `pnpm vitest run
src/__tests__/cloud-tenant-transient-db-retry.test.ts` — 5 passed
- `pnpm vitest run
src/__tests__/cloud-tenant-company-provisioning.test.ts` — 7 passed
against embedded Postgres, driving the real resolution path through the
new wrapper
## Risks
- Low risk. The retry is bounded to one attempt, gated on three explicit
driver codes, and wraps an operation that is already idempotent by
design. Every other failure propagates unchanged.
- A genuinely down database now fails after two attempts instead of one
— a few milliseconds of added latency on an already-failing request.
## Model Used
Claude Fable 5 (claude-fable-5) via Claude Code, extended thinking with
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 (none found for connection-retry work in this path)
- [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 (doc
comments; no user-facing docs affected)
- [x] I have considered and documented any risks above
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The server controls secrets, host files, outbound requests, and
workspace commands
> - A red-team review found cases where restricted callers could cross
these trust boundaries
> - These cases could expose credentials or let untrusted input reach
privileged resources
> - This pull request applies least-privilege checks at each affected
server boundary
> - The benefit is safer agent execution without changing the
private-instance bootstrap contract
## Linked Issues or Issue Description
**What happened?**
Several server paths used authorization, redaction, or content-delivery
rules that were too broad. Restricted agent keys could obtain
company-level operational data. Some adapter and instruction paths could
reach server-owned network or file resources without the required owner
approval.
**Expected behavior**
Paperclip must redact credential values, enforce restricted-key scopes,
guard outbound network access, prevent same-origin script execution, and
reserve host-level file and command controls for authorized operators.
**Steps to reproduce**
1. Configure an authenticated development instance at the parent commit.
2. Exercise the affected APIs with a restricted agent key or a
non-instance-admin company user.
3. Observe that the parent commit returns privileged data or accepts a
privileged operation.
4. Repeat on this branch and observe a redacted response, a safe
download, or an HTTP 403 response.
**Paperclip version or commit**
The findings reproduce from commit `39898ab22` and are fixed by this
pull request.
**Deployment mode**
Authenticated self-hosted server and local development modes.
**Installation method**
Built from source with pnpm.
## What Changed
- Redact generic secret `value` and `token` fields recursively in
structured logs.
- Classify exact and separator-suffixed `KEY` environment names as
secrets in company exports.
- Limit restricted self-identity responses and protect company run, log,
and secret catalog APIs.
- Route HTTP adapter requests through DNS-pinned SSRF protection with
exact private-origin allowlisting.
- Download HTML, SVG, and other script-capable assets with `nosniff` and
a sandbox CSP.
- Require instance-admin access for external instruction roots and
exports that read them.
- Block agent-authenticated host command persistence across supported
workspace runtime shapes.
- Apply the central runtime-management decision before workspace command
controls.
- Keep the documented first-user instance-admin claim contract
unchanged.
- Add regression tests and server-owner configuration documentation.
## Verification
- `pnpm -r typecheck` passes.
- The Node 24 remediation suite passes with 365 tests. It skips 25
environment-gated tests.
- `pnpm build` passes under Node 24.
- `git diff --check` passes.
- The full local runner reaches known macOS-only general-server harness
failures before the serialized route lane. The Linux PR matrix is the
authoritative full-suite gate.
## Risks
- Restricted agent keys now receive HTTP 403 responses from company-wide
run, log, and secret catalog endpoints.
- Script-capable assets now download instead of rendering inline.
- External instruction roots now require instance-admin access.
- Private HTTP adapter endpoints now require an exact origin in
`PAPERCLIP_HTTP_ADAPTER_PRIVATE_ENDPOINT_ALLOWLIST`.
- Public HTTP adapter endpoints remain enabled. Redirects and metadata
or link-local targets remain blocked.
- No database migration is required.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5. The exact serving snapshot and context-window size
are not exposed. The model used tool-enabled reasoning, repository
access, code execution, and test execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents are configured through `adapterConfig`, whose `env` block
holds the credentials an agent needs to talk to its provider (API keys,
tokens, and similar)
> - Those bindings come in several shapes: a legacy bare string, `{
type: "plain", value }`, and the indirection forms `{ type: "secret_ref"
}` / `{ type: "user_secret_ref" }`
> - Every endpoint that serializes an agent returned `adapterConfig` as
stored, so every `plain` binding was returned verbatim in the API
response
> - That means any caller able to read an agent — including the agent
itself via `GET /api/agents/me` — received live credentials in
plaintext, and those values then propagate into client state, logs, and
network traces
> - The exposure spans three response families that share no common
serializer: the single-agent detail reads, the company agent-list read,
and the create/update/lifecycle routes that echo the stored row straight
back
> - This pull request routes all three through one presenter that
redacts `plain` env bindings, so the leak is closed server-side and
cannot be bypassed by the caller
> - The benefit is that agent credentials stop appearing in API
responses while `secret_ref` indirection continues to work unchanged
## Linked Issues or Issue Description
No public upstream issue exists for this, so the underlying bug is
described inline below following
`.github/ISSUE_TEMPLATE/bug_report.yml`.
**What happened?**
Every endpoint that serializes an agent returned the full plaintext
value of each `adapterConfig.env` entry whose `type` was `"plain"` (and
each legacy bare-string binding). Any actor authorized to read an agent
received that agent's live credentials in the response body. Three
distinct response families were affected:
- **Single-agent reads** — `GET /api/agents/{id}` and `GET
/api/agents/me`, via `buildAgentDetail`.
- **Company agent list** — `GET /api/companies/{companyId}/agents`,
which serializes rows directly and therefore does not inherit any fix
applied to `buildAgentDetail`. Callers that pass the configuration-read
check received unredacted rows for every agent in the company in a
single request, making this the broadest of the three.
- **Mutation responses** — agent create, `PATCH /api/agents/{id}`, and
the `pause` / `resume` / `clear-error` / `approve` / `terminate` routes,
each of which echoes the stored row back to the caller.
**Expected behavior**
Read endpoints should never emit stored plaintext credentials. `plain`
bindings should be replaced with a redaction sentinel before
serialization, while `secret_ref` and `user_secret_ref` bindings — which
contain no secret material — pass through untouched.
**Steps to reproduce**
1. Configure an agent with an `adapterConfig.env` entry such as `{
"OPENAI_API_KEY": { "type": "plain", "value": "sk-example" } }`.
2. Call `GET /api/agents/{id}` (or authenticate as that agent and call
`GET /api/agents/me`).
3. Observe `sk-example` returned verbatim in the response body.
4. Call `GET /api/companies/{companyId}/agents` as a
configuration-reading caller and observe `sk-example` returned verbatim
for that agent alongside every other agent's credentials.
5. Call `PATCH /api/agents/{id}` with any unrelated field (for example
`{ "title": "Renamed" }`) and observe `sk-example` returned verbatim in
the mutation response.
**Paperclip version or commit**
Reproduced on `master` at `f12bb27b`.
**Deployment mode**
Self-hosted / local development server.
## What Changed
- `server/src/redaction.ts`: adds `redactAgentAdapterConfig`, which
rewrites every bare-string or `{ type: "plain", value }` env binding to
`{ type: "plain", value: "***REDACTED***" }` and passes `secret_ref` /
`user_secret_ref` bindings through unchanged. Reuses the existing
`REDACTED_EVENT_VALUE` and `isSecretRefBinding` /
`isUserSecretRefBinding` / `isPlainBinding` helpers — no new
dependencies.
- `server/src/redaction.ts`: `env` is destructured out and sanitized
only by `redactAgentEnvBinding`, while the remaining adapter keys go
through `redactEventPayload`. Previously the already-redacted `env` was
passed back through `sanitizeRecord`, so each binding was processed
twice — safe only because the sentinel is a fixed point of that second
pass. The two paths are now disjoint, making the invariant structural
rather than coincidental.
- `server/src/routes/agents.ts`: `buildAgentDetail` applies
`redactAgentAdapterConfig` before serialization, so `GET
/api/agents/{id}` and `GET /api/agents/me` both redact at the response
layer. Restricted views inherit the same protection.
- `server/src/routes/agents.ts`: adds `redactAgentRowForResponse`, the
single presenter for every response that emits a raw agent row, and
applies it to the company agent-list route and to the create / update /
pause / resume / clear-error / approve / terminate routes. It composes
with `redactForRestrictedAgentView` rather than replacing it: that
helper is an authorization filter (blank the whole config for low-trust
actors), this one is secret hygiene (mask values for every actor scope),
and the two invariants stay independent. `buildAgentDetail` now
delegates to the same presenter instead of inlining the call.
- `server/src/routes/agents.ts`: adds `restoreRedactedAgentEnv` on the
PATCH path so a client that round-trips a redacted detail response back
through `PATCH /api/agents/{id}` does not zero out stored values —
redacted-sentinel entries matching an existing key are restored from
storage.
## Verification
- `pnpm --filter @paperclipai/server exec tsc --noEmit` — clean.
- `pnpm --filter @paperclipai/server exec vitest run
agent-permissions-routes.test.ts` — 57 tests pass.
- Adjacent suites (`redaction`, `agent-adapter-validation-routes`,
`agent-cross-tenant-authz-routes`, `agents-pending-approval-config`,
`agents-service-secret-bindings`, `built-in-agent-routes`,
`plugin-managed-agents`, `agent-skills-routes`) — 8 files, 72 tests
pass, no regressions.
- Both new route tests were confirmed to **fail** with the route changes
reverted and pass with them applied, so they genuinely pin the behaviour
rather than passing incidentally.
Tests added:
- `server/src/__tests__/redaction.test.ts`: covers legacy-string, `{
type: "plain" }`, `secret_ref`, and `user_secret_ref` bindings,
asserting the plaintext value never appears in the serialized result;
plus coverage that non-env adapter keys are still sanitized, that env
binding shapes survive intact, and that configs with no `env` block are
handled.
- `server/src/__tests__/agent-permissions-routes.test.ts`: `GET
/api/agents/{id}` asserts redaction rather than plaintext passthrough;
new `GET /api/agents/me` redaction test across the same binding shapes;
new test asserting the `PATCH` round-trip preserves stored values; new
test asserting the board `GET /api/companies/{companyId}/agents`
response redacts every binding shape; new test asserting a mutation
response redacts rather than echoing the stored plaintext.
No real secret values appear in any test, fixture, or commit message.
## Risks
- **Behavioral change for API consumers.** Any client that read a
plaintext credential out of an agent detail, agent-list, or mutation
response will now receive `***REDACTED***`. This is the intended
security fix, but it is a breaking change for such consumers, which must
move to `secret_ref` indirection.
- **Mutation responses are redacted too.** Callers that previously
relied on a create or update response to echo back the credential they
had just written must now read it from their own request. This is
consistent with the `restoreRedactedAgentEnv` round-trip path, which
already assumes the client holds a redacted copy.
- **Round-trip data loss, mitigated.** A client that GETs an agent and
PATCHes the object straight back would otherwise persist the sentinel
over the real value. `restoreRedactedAgentEnv` restores redacted entries
from storage; the round-trip is covered by a regression test. A PATCH
that *intentionally* sets a value literally equal to the sentinel is not
distinguishable and would be treated as "unchanged" — an acceptable
trade-off given the sentinel is not a plausible credential.
- **No migration.** Stored data is untouched; redaction happens purely
at serialization time, so the change is fully reversible by revert.
- **Overlap with existing PRs** — see the duplicate-search note below.
Maintainers may prefer to consolidate rather than merge this in
isolation.
## Model Used
Claude Opus 4.8 (`claude-opus-4-8`), extended thinking enabled, with
tool use and local test execution.
## Duplicate search
Searching open and closed PRs for prior art surfaced several overlapping
efforts against the same defect. Linking them for maintainer triage — I
am not claiming this PR supersedes them, and consolidation may well be
preferable:
- #9823 — `fix(security): redact adapterConfig secrets on all agent read
endpoints` (closest overlap)
- #8779 — `fix(server): redact agent config secrets in read and mutation
responses`
- #8330 — `fix(server): redact adapterConfig.env for cross-actor agent
reads`
- #4856 — `fix(server): redact adapter env secrets in agent API
responses`
- #4763 — `fix(server): redact adapter_config secrets in agent detail
responses`
- #1839 — `fix: redact secret env vars from agent API responses`
- #4967 — `fix(routines): redact adapterConfig.env in GET
/api/routines/{id}` (same class, routines surface)
## 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 searched the GitHub PR list (open and closed) for similar or
duplicate PRs and linked them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [ ] My branch name describes the change and contains no internal
Paperclip ticket id — **not met**: the branch and title carry an
internal ticket id. Renaming the branch would invalidate this PR; happy
to reopen from a clean branch if maintainers prefer.
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes (no
user-facing docs affected)
- [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 —
the one P2 (env entries processed twice) is addressed above
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Matthew Glover <5413384+glovario@users.noreply.github.com>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
Co-authored-by: Andrew Aymeloglu <aaymeloglu@gmail.com>
## Thinking Path
> - Paperclip manages AI agents and their provider runtimes.
> - Paid runner validation installs target dependencies with lifecycle
scripts disabled.
> - OpenCode leaves a sentinel executable until its package lifecycle
script runs.
> - Running arbitrary lifecycle code would weaken the paid-secret
boundary.
> - This pull request materializes one exact pinned binary before
secrets are exposed.
> - The benefit is working OpenCode validation without trusting
dependency install scripts.
## Linked Issues or Issue Description
**What happened?**
Every local OpenCode paid cell stopped before provider startup because
`pnpm install --ignore-scripts` correctly retained
`opencode-ai/bin/opencode.exe` as a sentinel.
**Expected behavior**
The trusted workflow must make the exact lockfile-pinned OpenCode
executable available without running package lifecycle scripts.
**Steps to reproduce**
Run a local legacy or native OpenCode paid cell from the trusted
workflow after the target dependency install. The provider health check
reports that the OpenCode postinstall script was not run.
**Paperclip version or commit**
Default branch commit `865b4854fb44d3689f1c0ff17e3e715d52aaea73`.
## What Changed
- Materialize only `opencode-linux-x64-baseline@1.18.17` into the
matching `opencode-ai@1.18.17` package.
- Verify package identity, version, regular-file type, SHA-256 equality,
executable permissions, and runtime `--version`.
- Invoke the helper for local OpenCode and breadth cells and for remote
provider-pack assembly.
- Retain `pnpm install --ignore-scripts`.
- Add helper and trusted-workflow security regressions.
## Verification
- Helper syntax checks passed.
- Helper unit tests passed: 2/2.
- Workflow-security tests passed: 5/5.
- Prettier, actionlint, and diff whitespace checks passed.
## Risks
Risk is low and contained to paid runner setup. The helper supports only
Linux x64, fails closed on package or version drift, and runs before
provider credentials enter the job.
## Model Used
OpenAI GPT-5 Codex with repository tools and code execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change.
- [x] I have specified the model used.
- [x] I have checked ROADMAP.md and confirmed this does not duplicate
planned core work.
- [x] I have searched GitHub for duplicate or related PRs and found
none.
- [x] I have described the issue in this PR with the bug template
labels.
- [x] I have not referenced internal or instance-local issues.
- [x] My branch name describes the change.
- [x] Focused local tests pass.
- [x] I added tests for the change.
- [x] I updated the runner E2E security documentation.
- [x] I documented the risks above.
## Thinking Path
> - Paid cells repeated the same TypeScript and Rust builds even when
one campaign selected dozens of cells.
> - The trusted workflow can compile once without provider credentials
and distribute run-scoped, digest-verified artifacts.
> - The paid cell can then disable install lifecycle scripts, verify
each artifact before extraction, and expose provider credentials only to
the final test step.
> - Local JS-backed providers also need the setup-node interpreter
permission-qualified before Rust verifies the launch artifact.
## Linked Issues or Issue Description
Run 33786122875 proved target-lock setup and catalog selection, then
failed before provider creation because trusted master did not yet
qualify the setup-node interpreter. The same workflow also rebuilt
TypeScript and Rust inside every matrix cell.
## What Changed
- Build runner TypeScript and native binaries once per campaign in a
credential-free job.
- Build the remote provider pack once only when selected Daytona cells
require it.
- Upload run-scoped bundles with SHA-256 manifests and verify before
extraction in each paid cell.
- Remove repeated TypeScript, provider-pack, and Rust builds from paid
cells.
- Qualify the local provider Node interpreter before verified launch.
- Propagate the resolved target lockfile through all five target-code
jobs.
- Keep local-only selection off Daytona and exclude Xiaomi from the
67-cell catalog.
## Risks
A shared build artifact could fan out a bad payload to many cells. The
producing jobs receive no provider credentials, use the exact authorized
target SHA and resolved lockfile, and publish run-scoped artifacts.
Every consuming job verifies SHA-256 before extraction. Paid dependency
setup keeps lifecycle scripts disabled and provider credentials remain
scoped to the final test step.
## Verification
- Focused runner workflow-security, catalog, and Daytona-image tests:
25/25 passed.
- Prettier passed.
- Actionlint passed with only the two pre-existing SC2129 style notices
ignored.
- Git diff check passed.
## Model Used
OpenAI Codex, GPT-5.
## Checklist
- [x] Build jobs are credential-free.
- [x] Paid installs disable lifecycle scripts.
- [x] Artifacts are run-scoped and digest-verified before extraction.
- [x] Trusted report and history jobs remain isolated from target
artifacts.
- [x] No Daytona or Xiaomi paid run was started for this change.
## Thinking Path
> - The trusted target-branch runner workflow checks out PR code before
paid tests.
> - PR policy intentionally forbids manual lockfile commits.
> - Some runner changes legitimately alter pnpm patch hashes.
> - Frozen installs therefore fail before test selection.
> - Resolve one script-disabled lockfile from the authorized immutable
target SHA and distribute it by exact artifact ID and digest.
> - Keep provider credentials and trusted reporting outside this
resolution job.
## Linked Issues or Issue Description
Target-branch paid runner campaigns currently fail frozen install when a
PR changes pnpm patch content, even though ordinary PR CI regenerates
the lockfile.
## What Changed
- Added one credential-free target-lock job that resolves the authorized
immutable target SHA with lifecycle scripts disabled.
- Uploaded the resolved lockfile with its SHA-256 and restored it by
exact artifact ID before every target-code frozen install.
- Left trusted reporting and history jobs on the workflow SHA.
- Changed the disabled-AWS fallback from unavailable ubuntu-latest-m to
ubuntu-latest.
## Risks
The workflow evaluates pnpm lockfile resolution from authorized target
code. That job receives no provider credentials, disables lifecycle
scripts, rejects unrelated workspace mutations, and exposes only a
digest-verified lockfile artifact. Paid-secret jobs consume only that
lockfile after exact artifact-ID and SHA-256 validation.
## Verification
- Runner workflow-security focused tests pass.
- actionlint passes.
- Prettier and git diff checks pass.
## Model Used
OpenAI Codex, GPT-5.
## Checklist
- [x] Change is narrowly scoped to paid runner orchestration.
- [x] Target lock resolution has no provider credentials and disables
lifecycle scripts.
- [x] Downloaded artifacts are selected by exact artifact ID and
verified by SHA-256.
- [x] Trusted reporting and history jobs remain on the workflow SHA.
Bumps
[paperclipai/paperclip/.github/workflows/pr-trusted.yml](https://github.com/paperclipai/paperclip)
from 39b8ee2960 to
f038633bf5.
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/paperclipai/paperclip/blob/master/doc/RELEASE-AUTOMATION-SETUP.md">paperclipai/paperclip/.github/workflows/pr-trusted.yml's
changelog</a>.</em></p>
<blockquote>
<h1>Release Automation Setup</h1>
<p>This document covers the GitHub and npm setup required for the
current Paperclip release model:</p>
<ul>
<li>automatic canaries from <code>master</code></li>
<li>manual stable promotion from a chosen source ref</li>
<li>npm trusted publishing via GitHub OIDC</li>
<li>protected release infrastructure in a public repository</li>
</ul>
<p>Repo-side files that depend on this setup:</p>
<ul>
<li><code>.github/workflows/release.yml</code></li>
<li><code>.github/CODEOWNERS</code></li>
</ul>
<p>Note:</p>
<ul>
<li>the release workflows intentionally use <code>pnpm install
--no-frozen-lockfile</code></li>
<li>this matches the repo's current policy where
<code>pnpm-lock.yaml</code> is refreshed by GitHub automation after
manifest changes land on <code>master</code></li>
<li>the publish jobs then restore <code>pnpm-lock.yaml</code> before
running <code>scripts/release.sh</code>, so the release script still
sees a clean worktree</li>
</ul>
<h2>1. Merge the Repo Changes First</h2>
<p>Before touching GitHub or npm settings, merge the release automation
code so the referenced workflow filenames already exist on the default
branch.</p>
<p>Required files:</p>
<ul>
<li><code>.github/workflows/release.yml</code></li>
<li><code>.github/CODEOWNERS</code></li>
</ul>
<h2>2. Configure npm Trusted Publishing</h2>
<p>Do this for every public package that Paperclip publishes.</p>
<p>At minimum that includes:</p>
<ul>
<li><code>paperclipai</code></li>
<li><code>@paperclipai/server</code></li>
<li><code>@paperclipai/ui</code></li>
<li>public packages under <code>packages/</code></li>
</ul>
<h3>2.1. In npm, open each package settings page</h3>
<p>For each package:</p>
<ol>
<li>open npm as an owner of the package</li>
<li>go to the package settings / publishing access area</li>
<li>add a trusted publisher for the GitHub repository
<code>paperclipai/paperclip</code></li>
</ol>
<h3>2.2. Add one trusted publisher entry per package</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="f038633bf5"><code>f038633</code></a>
feat(runner): reduce ACPX provider state (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12417">#12417</a>)</li>
<li><a
href="7bb6cebeae"><code>7bb6ceb</code></a>
feat(runner): normalize ACPX provider events (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12416">#12416</a>)</li>
<li><a
href="fe2ddfad2b"><code>fe2ddfa</code></a>
feat(runner): validate ACPX event payloads (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12415">#12415</a>)</li>
<li><a
href="3db24d9366"><code>3db24d9</code></a>
feat(runner): bind ACPX event scope (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12414">#12414</a>)</li>
<li><a
href="75708fec6d"><code>75708fe</code></a>
feat(runner): add ACPX sidecar transport (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12412">#12412</a>)</li>
<li><a
href="9ad8dbffa0"><code>9ad8dbf</code></a>
feat(runner): add Codex ACPX sidecar (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12410">#12410</a>)</li>
<li><a
href="b93ad538b6"><code>b93ad53</code></a>
test(runner): add question adapter conformance (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12409">#12409</a>)</li>
<li><a
href="4fe3189f02"><code>4fe3189</code></a>
feat(runner): bridge Codex ACPX questions (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12408">#12408</a>)</li>
<li><a
href="96421b0663"><code>96421b0</code></a>
feat(runner): recover settled Codex ACPX sessions (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12407">#12407</a>)</li>
<li><a
href="30ef14edd4"><code>30ef14e</code></a>
feat(runner): wire the Codex ACPX backend (<a
href="https://redirect.github.com/paperclipai/paperclip/issues/12406">#12406</a>)</li>
<li>Additional commits viewable in <a
href="39b8ee2960...f038633bf5">compare
view</a></li>
</ul>
</details>
<br />
Dependabot will resolve any conflicts with this PR as long as you don't
alter it yourself. You can also trigger a rebase manually by commenting
`@dependabot rebase`.
[//]: # (dependabot-automerge-start)
[//]: # (dependabot-automerge-end)
---
<details>
<summary>Dependabot commands and options</summary>
<br />
You can trigger Dependabot actions by commenting on this PR:
- `@dependabot rebase` will rebase this PR
- `@dependabot recreate` will recreate this PR, overwriting any edits
that have been made to it
- `@dependabot show <dependency name> ignore conditions` will show all
of the ignore conditions of the specified dependency
- `@dependabot ignore this major version` will close this PR and stop
Dependabot creating any more for this major version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this minor version` will close this PR and stop
Dependabot creating any more for this minor version (unless you reopen
the PR or upgrade to it yourself)
- `@dependabot ignore this dependency` will close this PR and stop
Dependabot creating any more for this dependency (unless you reopen the
PR or upgrade to it yourself)
</details>
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The server refuses to boot when its database is not migrated, or
when an authenticated public deployment has no `DATABASE_URL`. These
refusals are deliberate and correct.
> - In managed cloud, a supervisor creates each stack, migrates its
fresh database, applies configuration, and restarts the app. The app
container often boots before those steps finish.
> - Each early boot hits one of the two refusals, exits, and captures
the refusal to Sentry. One fleet build batch produces hundreds of
identical expected events. Real errors get buried.
> - This pull request classifies exactly those two refusals as expected
transients when `PAPERCLIP_CLOUD_API_ORIGIN` marks a supervised
deployment, and skips only the Sentry capture for them.
> - The benefit is a clean error signal: expected provisioning noise
stops, and every real failure still reports.
## Linked Issues or Issue Description
**What happened?**
A managed-cloud stack boots its app container before the supervisor
migrates the empty database or finishes applying configuration. The
container refuses to start, crash-loops briefly, and converges after the
supervisor restarts it. Every refused boot sends an error event to
Sentry. A batch of new stacks produces hundreds of these expected
events.
**Expected behavior**
The refusal logs and exits nonzero, so the supervisor can act. Sentry
receives no event for an expected provisioning transient. Sentry still
receives events for real failures: schema drift, malformed
configuration, and every refusal outside managed cloud.
**Steps to reproduce**
1. Set `PAPERCLIP_MIGRATION_AUTO_APPLY=false`,
`PAPERCLIP_MIGRATION_PROMPT=never`, `SENTRY_DSN`, and
`PAPERCLIP_CLOUD_API_ORIGIN`.
2. Point `DATABASE_URL` at an empty database and start the server.
3. The server refuses to start. Before this change it also captures the
refusal to Sentry on every boot.
**Deployment mode**
Authenticated public (managed cloud).
## What Changed
- New `server/src/startup-refusals.ts`: a `StartupRefusalError` class
for refusals whose remedy belongs to the deployment supervisor,
`migrationRefusalError()` to classify a pending-migrations refusal (zero
applied migrations = never migrated = supervised transient; any applied
history = drift = plain always-reported `Error`), and
`shouldReportStartupFailure()` for the capture decision.
- `server/src/index.ts`: the pending-migrations refusal uses the
classifier; the missing-`DATABASE_URL` refusal under the
authenticated-public contract becomes a `StartupRefusalError` (the
malformed-URL refusal stays a plain `Error`); the startup crash handler
consults `shouldReportStartupFailure()` before `captureException`.
Logging and the nonzero exit are unchanged.
- New `server/src/__tests__/startup-refusals.test.ts` covering the
classification and decision matrix, including the unchanged self-hosted
paths.
## Verification
- `pnpm vitest run src/__tests__/startup-refusals.test.ts` — 7 passed.
- Review the decision matrix in the test file: refusals report when
`PAPERCLIP_CLOUD_API_ORIGIN` is absent or blank; non-refusal errors and
non-`Error` throwables always report; drift always reports.
## Risks
- Low risk. The change only skips a Sentry capture in one narrow,
marker-gated case. Boot behavior, logging, and the exit code do not
change.
- Self-hosted deployments do not set `PAPERCLIP_CLOUD_API_ORIGIN`, so
their reporting is unchanged, and the tests pin that.
- A supervised deployment with a genuinely stuck migration runner loses
per-boot Sentry events for that stack. The supervisor's own health
checks and monitoring own that signal, and the container logs still
carry the refusal.
## Model Used
Claude Fable 5 (claude-fable-5) via Claude Code, extended thinking with
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 (none found for startup Sentry suppression)
- [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
(module doc comment; no user-facing docs affected)
- [x] I have considered and documented any risks above
Accept and persist Cloud-signed canonical runtime identity before activation, then route absolute self-URLs through the durable runtime identity provider.
Co-Authored-By: Codex <codex@openai.com>
Bumps
[@vitejs/plugin-react](https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react)
from 4.7.0 to 6.1.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite-plugin-react/releases">@vitejs/plugin-react's
releases</a>.</em></p>
<blockquote>
<h2>plugin-react@6.1.1</h2>
<h3>Add <code>compiler.logDiagnostics</code> option</h3>
<p>Recoverable React Compiler diagnostics are no longer logged by
default. Set <code>compiler.logDiagnostics</code> to <code>true</code>
to log them through Vite. Fatal diagnostics are always logged and fail
the transform.</p>
<h3>Respect environment sourcemap option for React Compiler transform
when <code>builder.sharedPlugins</code> is enabled (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1439">#1439</a>)</h3>
<p>The React Compiler transform was using the top-level sourcemap option
instead of the environment sourcemap option. This caused a problem when
the experimental <code>builder.sharedPlugins</code> was enabled.</p>
<h2>plugin-react@6.1.0</h2>
<h3>Add experimental native React Compiler support (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1419">#1419</a>)</h3>
<p>Add experimental native React Compiler support.</p>
<p>You can use it by installing <code>oxc-transform-react</code> and
enabling it via the <code>compiler</code> option:</p>
<pre lang="sh"><code>npm install -D oxc-transform-react
</code></pre>
<pre lang="js"><code>import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
<p>export default defineConfig({<br />
plugins: [<br />
react({ compiler: true })<br />
]<br />
})<br />
</code></pre></p>
<h2>plugin-react@6.0.5</h2>
<h3>Fixed the react compiler preset filter to be linear (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1353">#1353</a>)</h3>
<p>The improved filter in v6.0.3 was non-linear and caused a performance
regression (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/issues/1349">#1349</a>).
The filter was changed to be linear to avoid that.</p>
<h2>plugin-react@6.0.4</h2>
<h3>Fixed <code>$RefreshSig$ is not defined</code> error when running
<code>vite dev</code> with <code>NODE_ENV=production</code></h3>
<p>When running <code>vite dev</code> with
<code>NODE_ENV=production</code>, the app errored with
<code>$RefreshSig$ is not defined</code>.
This error is now fixed.</p>
<h2>plugin-react@6.0.3</h2>
<p>No release notes provided.</p>
<h2>plugin-react@6.0.2</h2>
<h3>Allow all options in reactCompilerPreset (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1189">#1189</a>)</h3>
<p>This is a type only change. Only <code>compilationMode</code> and
<code>target</code> options were available for
<code>reactCompilerPreset</code>.</p>
<h2>plugin-react@6.0.1</h2>
<h3>Expand <code>@rolldown/plugin-babel</code> peer dep range (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1146">#1146</a>)</h3>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/vitejs/vite-plugin-react/blob/main/packages/plugin-react/CHANGELOG.md">@vitejs/plugin-react's
changelog</a>.</em></p>
<blockquote>
<h2>6.1.1 (2026-08-28)</h2>
<h3>Add <code>compiler.logDiagnostics</code> option</h3>
<p>Recoverable React Compiler diagnostics are no longer logged by
default. Set <code>compiler.logDiagnostics</code> to <code>true</code>
to log them through Vite. Fatal diagnostics are always logged and fail
the transform.</p>
<h3>Respect environment sourcemap option for React Compiler transform
when <code>builder.sharedPlugins</code> is enabled (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1439">#1439</a>)</h3>
<p>The React Compiler transform was using the top-level sourcemap option
instead of the environment sourcemap option. This caused a problem when
the experimental <code>builder.sharedPlugins</code> was enabled.</p>
<h2>6.1.0 (2026-08-19)</h2>
<h3>Add experimental native React Compiler support (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1419">#1419</a>)</h3>
<p>Add experimental native React Compiler support.</p>
<p>You can use it by installing <code>oxc-transform-react</code> and
enabling it via the <code>compiler</code> option:</p>
<pre lang="sh"><code>npm install -D oxc-transform-react
</code></pre>
<pre lang="js"><code>import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
<p>export default defineConfig({<br />
plugins: [<br />
react({ compiler: true })<br />
]<br />
})<br />
</code></pre></p>
<h2>6.0.5 (2026-07-30)</h2>
<h3>Fixed the react compiler preset filter to be linear (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1353">#1353</a>)</h3>
<p>The improved filter in v6.0.3 was non-linear and caused a performance
regression (<a
href="https://redirect.github.com/vitejs/vite-plugin-react/issues/1349">#1349</a>).
The filter was changed to be linear to avoid that.</p>
<h2>6.0.4 (2026-07-22)</h2>
<h3>Fixed <code>$RefreshSig$ is not defined</code> error when running
<code>vite dev</code> with <code>NODE_ENV=production</code></h3>
<p>When running <code>vite dev</code> with
<code>NODE_ENV=production</code>, the app errored with
<code>$RefreshSig$ is not defined</code>.
This error is now fixed.</p>
<h2>6.0.3 (2026-06-23)</h2>
<h3>Improve the react compiler preset filter to reduce false-positives
(<a
href="https://redirect.github.com/vitejs/vite-plugin-react/pull/1138">#1138</a>)</h3>
<p>Improved the filter in the react compiler babel preset to reduce the
false-positives so that less modules are processed by the react
compiler.</p>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="04cac5020e"><code>04cac50</code></a>
release: plugin-react@6.1.1 (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1440">#1440</a>)</li>
<li><a
href="82d35abe49"><code>82d35ab</code></a>
fix(react): respect environment sourcemap option when
<code>builder.sharedPlugins</code>...</li>
<li><a
href="397e8471a5"><code>397e847</code></a>
fix(react): make logging diagnostics an opt-in for React Compiler (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1431">#1431</a>)</li>
<li><a
href="61006e6f52"><code>61006e6</code></a>
fix(deps): update all non-major dependencies (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1433">#1433</a>)</li>
<li><a
href="e2a649cbaa"><code>e2a649c</code></a>
chore: use <code>deps.neverBundle</code> instead of
<code>external</code> in tsdown config (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1430">#1430</a>)</li>
<li><a
href="fb2d6f3635"><code>fb2d6f3</code></a>
fix(deps): update all non-major dependencies (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1427">#1427</a>)</li>
<li><a
href="39b31735bf"><code>39b3173</code></a>
release: plugin-react@6.1.0 (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1428">#1428</a>)</li>
<li><a
href="f1340b0c76"><code>f1340b0</code></a>
feat(react): add native React Compiler support (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1419">#1419</a>)</li>
<li><a
href="9ab698eafc"><code>9ab698e</code></a>
fix(deps): update all non-major dependencies (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1375">#1375</a>)</li>
<li><a
href="68c0cb8796"><code>68c0cb8</code></a>
release: plugin-react@6.0.5 (<a
href="https://github.com/vitejs/vite-plugin-react/tree/HEAD/packages/plugin-react/issues/1362">#1362</a>)</li>
<li>Additional commits viewable in <a
href="https://github.com/vitejs/vite-plugin-react/commits/plugin-react@6.1.1/packages/plugin-react">compare
view</a></li>
</ul>
</details>
<details>
<summary>Maintainer changes</summary>
<p>This version was pushed to npm by <a
href="https://www.npmjs.com/~GitHub%20Actions">GitHub Actions</a>, a new
releaser for <code>@vitejs/plugin-react</code> since your current
version.</p>
</details>
<br />
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Use full dependency resolution in automated lockfile repair paths, add regression coverage, and refresh the stale Rollup snapshot.
Co-Authored-By: Dotta <cryppadotta@users.noreply.github.com>
Co-Authored-By: Codex <codex@openai.com>
Co-Authored-By: lockfile-bot <lockfile-bot@users.noreply.github.com>
Bumps
[@aws-sdk/client-s3](https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3)
from 3.1115.0 to 3.1120.0.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/aws/aws-sdk-js-v3/releases">@aws-sdk/client-s3's
releases</a>.</em></p>
<blockquote>
<h2>v3.1120.0</h2>
<h4>3.1120.0(2026-08-27)</h4>
<h5>Documentation Changes</h5>
<ul>
<li><strong>client-opensearch:</strong> Updating SDK and CLI
documentation for AttachDataSource API. (<a
href="d696fe7602">d696fe76</a>)</li>
</ul>
<h5>New Features</h5>
<ul>
<li><strong>client-lambda-microvms:</strong> Added
InsufficientCapacityException to RunMicrovm for capacity-related
failures. Added lifecycle status field (AVAILABLE, DEPRECATED) to
ListManagedMicrovmImageVersions. Added ConflictException to
CreateMicrovmAuthToken and CreateMicrovmShellAuthToken for unregistered
MicroVMs. (<a
href="72a8ff8092">72a8ff80</a>)</li>
<li><strong>client-codedeploy:</strong> Added a deploymentMode parameter
to CreateDeployment. Set it to RESTART to restart an EC2 and on-premises
fleet, using the last successful revision, honoring Deployment
Configuration. (<a
href="78d4f9640b">78d4f964</a>)</li>
<li><strong>client-cloudwatch-logs:</strong> Added resultCount to
QueryStatistics in GetQueryResults. This field returns the total number
of output rows in the final result set, helping customers
programmatically determine whether a query produced results after all
operations including post-aggregation filters. (<a
href="0e4d242b71">0e4d242b</a>)</li>
<li><strong>client-datazone:</strong> Add cascadeDelete to DeleteDomain.
When specified, DataZone recursively deletes all projects, environments,
subscriptions, and their underlying AWS resources before removing the
domain. Deletion progress is reported via deleteProgress and resource
failures via failureReasons on GetDomain. (<a
href="3a74dc4b94">3a74dc4b</a>)</li>
<li><strong>client-rds:</strong> Adding support for the full snapshot
size, in bytes, of DB instance snapshots. (<a
href="ab2f66f5f5">ab2f66f5</a>)</li>
<li><strong>client-ec2:</strong> EC2 allows AMI owners to define
compatible instance types on their AMIs, blocking RunInstances calls
automatically for launches on non-permitted instance types. (<a
href="311b3b26db">311b3b26</a>)</li>
<li><strong>client-cognito-identity-provider:</strong> Adds the
AdminDeleteSoftwareToken API operation, enabling administrators to
remove a user's registered TOTP (software token) MFA configuration from
a user pool. (<a
href="f661bebc4d">f661bebc</a>)</li>
</ul>
<hr />
<p>For list of updated packages, view
<strong>updated-packages.md</strong> in
<strong>assets-3.1120.0.zip</strong></p>
<h2>v3.1119.0</h2>
<h4>3.1119.0(2026-08-26)</h4>
<h5>Chores</h5>
<ul>
<li><strong>codegen:</strong> smithy-aws-typescript-codegen 0.53.0 (<a
href="https://redirect.github.com/aws/aws-sdk-js-v3/pull/8276">#8276</a>)
(<a
href="dffb383bdc">dffb383b</a>)</li>
</ul>
<h5>New Features</h5>
<ul>
<li><strong>client-sagemaker:</strong> Amazon SageMaker AI now supports
ml.g7 instances for model optimization. You can now run model
optimization jobs on ml.g7 instances, in supported AWS Regions. (<a
href="6d5e106634">6d5e1066</a>)</li>
<li><strong>client-devops-agent:</strong> AWS DevOps Agent now supports
trigger filter groups for Release Readiness Review, letting you control
when the capability auto-triggers based on webhook events and target
branches. (<a
href="bc3d53d550">bc3d53d5</a>)</li>
<li><strong>client-license-manager-user-subscriptions:</strong> Released
support for License Expiry field in ListProductSubscriptions API (<a
href="454d7f7ffb">454d7f7f</a>)</li>
<li><strong>client-ec2:</strong> Adds deleting state to possible VPC
States. (<a
href="43091d55b3">43091d55</a>)</li>
<li><strong>client-network-firewall:</strong> Adding new status enum for
Firewalls. (<a
href="4cb21cb3b8">4cb21cb3</a>)</li>
</ul>
<hr />
<p>For list of updated packages, view
<strong>updated-packages.md</strong> in
<strong>assets-3.1119.0.zip</strong></p>
<h2>v3.1118.0</h2>
<h4>3.1118.0(2026-08-25)</h4>
<h5>Documentation Changes</h5>
<ul>
<li><strong>client-marketplace-metering:</strong> Updated documentation
to clarify duplicate-billing prevention and BatchMeterUsage retry
guidance (<a
href="322310259e">32231025</a>)</li>
</ul>
<h5>New Features</h5>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Changelog</summary>
<p><em>Sourced from <a
href="https://github.com/aws/aws-sdk-js-v3/blob/main/clients/client-s3/CHANGELOG.md">@aws-sdk/client-s3's
changelog</a>.</em></p>
<blockquote>
<h1><a
href="https://github.com/aws/aws-sdk-js-v3/compare/v3.1119.0...v3.1120.0">3.1120.0</a>
(2026-08-27)</h1>
<p><strong>Note:</strong> Version bump only for package
<code>@aws-sdk/client-s3</code></p>
<h1><a
href="https://github.com/aws/aws-sdk-js-v3/compare/v3.1118.0...v3.1119.0">3.1119.0</a>
(2026-08-26)</h1>
<p><strong>Note:</strong> Version bump only for package
<code>@aws-sdk/client-s3</code></p>
<h1><a
href="https://github.com/aws/aws-sdk-js-v3/compare/v3.1117.0...v3.1118.0">3.1118.0</a>
(2026-08-25)</h1>
<p><strong>Note:</strong> Version bump only for package
<code>@aws-sdk/client-s3</code></p>
<h1><a
href="https://github.com/aws/aws-sdk-js-v3/compare/v3.1116.0...v3.1117.0">3.1117.0</a>
(2026-08-24)</h1>
<p><strong>Note:</strong> Version bump only for package
<code>@aws-sdk/client-s3</code></p>
<h1><a
href="https://github.com/aws/aws-sdk-js-v3/compare/v3.1115.0...v3.1116.0">3.1116.0</a>
(2026-08-21)</h1>
<p><strong>Note:</strong> Version bump only for package
<code>@aws-sdk/client-s3</code></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="d6be6f8dd3"><code>d6be6f8</code></a>
Publish v3.1120.0</li>
<li><a
href="ba4e4498a7"><code>ba4e449</code></a>
Publish v3.1119.0</li>
<li><a
href="c65dd6533d"><code>c65dd65</code></a>
Publish v3.1118.0</li>
<li><a
href="78b069ac77"><code>78b069a</code></a>
Publish v3.1117.0</li>
<li><a
href="d760a00859"><code>d760a00</code></a>
Publish v3.1116.0</li>
<li><a
href="8369ada75d"><code>8369ada</code></a>
chore(codegen): update to sync with the latest smithy-ts (<a
href="https://github.com/aws/aws-sdk-js-v3/tree/HEAD/clients/client-s3/issues/8272">#8272</a>)</li>
<li>See full diff in <a
href="https://github.com/aws/aws-sdk-js-v3/commits/v3.1120.0/clients/client-s3">compare
view</a></li>
</ul>
</details>
<br />
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Thinking Path
> - Paperclip uses paid runner tests to qualify agent execution.
> - The runner workflow controls provider secrets and AWS runner access.
> - The trusted workflow must stay on the protected default branch.
> - The code under test often exists on a branch before merge.
> - CODEOWNERS need a safe way to select that branch.
> - This pull request separates workflow authority from the code under
test.
> - The benefit is pre-merge AWS testing without target-controlled
workflow code.
## Linked Issues or Issue Description
**What existing behavior does this improve?**
The manual Runner Full-Stack E2E workflow can test only the default
branch.
**Subsystem affected**
GitHub Actions and the paid runner E2E security boundary.
**Current behavior**
A CODEOWNER must merge runner changes before the trusted AWS workflow
can test them.
Selecting another branch as the workflow ref is rejected.
**Proposed behavior**
A CODEOWNER starts the workflow from `master` and supplies a
same-repository branch in `target_branch`.
The authorization job resolves the branch to one commit SHA.
Catalog, image, and paid test jobs check out that SHA after
authorization.
Report sanitization and AWS publication use the trusted workflow SHA.
**Reason and benefit**
This permits paid pre-merge qualification on AWS.
It keeps the workflow definition, report sanitizer, history publisher,
environment deployment, and runner-group permission on `master`.
**Breaking changes**
None.
The new input is optional.
An omitted input still tests the default branch.
## What Changed
- Add the optional `target_branch` workflow input.
- Resolve only a branch in `paperclipai/paperclip` to an immutable SHA.
- Pin catalog, image, paid test, and Daytona provenance to the target
SHA.
- Pin report sanitization and AWS history publication to the trusted
workflow SHA.
- Disable persisted checkout credentials in every job.
- Key cancellation by the selected target branch.
- Add policy regression coverage and operator documentation.
## Verification
- `pnpm test:e2e:runner:unit` passes with 65 tests.
- `actionlint -ignore SC2129
.github/workflows/runner-full-stack-e2e.yml` passes.
- Prettier checks pass for all changed files.
- `git diff --check` passes.
## Risks
A CODEOWNER can authorize selected branch code to receive a cell-scoped
provider credential.
This is the intended trust decision.
The workflow rejects fork refs and target-controlled workflow
definitions.
The trusted workflow SHA owns report sanitization and AWS history
publication.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5.
The exact serving snapshot and context-window size are not exposed.
The model used tool-enabled reasoning and code execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 11.16.1 to
11.17.2.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/mermaid-js/mermaid/releases">mermaid's
releases</a>.</em></p>
<blockquote>
<h2>mermaid@11.17.2</h2>
<h3>Patch Changes</h3>
<ul>
<li><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8125">#8125</a>
<a
href="178d7c79fc"><code>178d7c7</code></a>
Thanks <a
href="https://github.com/knsv-bot"><code>@knsv-bot</code></a>! - fix:
restore the <code>edgePaths</code> class on the edge group in rendered
SVG, and point the flowchart, block and user journey stylesheets at
it</li>
</ul>
<h2>mermaid@11.17.1</h2>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8092">#8092</a>
<a
href="31ce60a596"><code>31ce60a</code></a>
Thanks <a
href="https://github.com/pbrolin47"><code>@pbrolin47</code></a>! -
fix(c4): wrap element labels to <code>c4.width</code> again</p>
<p>C4 element labels (<code>System</code>, <code>Container</code>,
<code>Component</code>, <code>Person</code> and their <code>_Ext</code>
variants) stopped wrapping in 11.17.0, so long descriptions rendered on
one unbroken line and the shape grew sideways well past the configured
<code>c4.width</code>. The unified-shapes label helper gated wrapping on
the root-level <code>wrap</code> option, which has no schema default and
is therefore <code>undefined</code>; it now gates on
<code>c4.wrap</code> (default <code>true</code>), which is what the
legacy renderer used.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8088">#8088</a>
<a
href="c66200bc23"><code>c66200b</code></a>
Thanks <a
href="https://github.com/ashishjain0512"><code>@ashishjain0512</code></a>!
- fix: neo-look arrowheads and crow's-foot markers no longer fall back
to default theme colours/stroke widths on the first render with
<code>layout: elk</code>. State diagram arrowheads stayed dark on dark
themes, and ER / requirement markers were drawn at the default stroke
width, because markers were created from the layout package's own
bundled copy of mermaid, whose config had not been initialized yet.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8079">#8079</a>
<a
href="281cd7b070"><code>281cd7b</code></a>
Thanks <a
href="https://github.com/ashishjain0512"><code>@ashishjain0512</code></a>!
- fix(class): class diagram relation markers (composition, aggregation,
extension, dependency, lollipop) no longer scale with the edge stroke
width, so they stay outside the class box boundary in themes that set
<code>strokeWidth: 2</code> (<code>redux</code>,
<code>redux-dark</code>, <code>redux-color</code>,
<code>redux-dark-color</code>, <code>neo</code>, <code>neo-dark</code>)
with the default <code>classic</code> look.</p>
</li>
</ul>
<h2>mermaid@11.17.0</h2>
<h3>Minor Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/7842">#7842</a>
<a
href="3670b4e2d9"><code>3670b4e</code></a>
Thanks <a
href="https://github.com/filipsajdak"><code>@filipsajdak</code></a>! -
feat(c4): render C4 elements through the unified shape system, using the
new person shape</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/7812">#7812</a>
<a
href="cdfc0ea65f"><code>cdfc0ea</code></a>
Thanks <a
href="https://github.com/knsv-bot"><code>@knsv-bot</code></a>! -
feat(class): route <code>classDiagram</code> to the unified (v2)
renderer by default</p>
<p>Set <code>class: { defaultRenderer: 'dagre-d3' }</code> in the config
to restore the legacy renderer.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/7785">#7785</a>
<a
href="c45cde9582"><code>c45cde9</code></a>
Thanks <a
href="https://github.com/knsv-bot"><code>@knsv-bot</code></a>! -
feat(flowchart): add collapsible flowchart subgraphs via
<code>subgraphId@{ view: collapsed }</code></p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/7828">#7828</a>
<a
href="8eb3afc08c"><code>8eb3afc</code></a>
Thanks <a
href="https://github.com/knsv-bot"><code>@knsv-bot</code></a>! -
feat(elk): add <code>elk.keepEntryNodeOnTop</code> config option to keep
a recursive flow's entry node on top</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/7803">#7803</a>
<a
href="74e44ebf86"><code>74e44eb</code></a>
Thanks <a
href="https://github.com/knsv-bot"><code>@knsv-bot</code></a>! -
feat(elk): add <code>elk.nodePlacementAlignment</code> config option</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/7792">#7792</a>
<a
href="ea55b31bcf"><code>ea55b31</code></a>
Thanks <a
href="https://github.com/RodrigojndSantos"><code>@RodrigojndSantos</code></a>!
- feat(er): add subgraph support to ER diagrams.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/7970">#7970</a>
<a
href="a2c0fb6cdf"><code>a2c0fb6</code></a>
Thanks <a
href="https://github.com/filipsajdak"><code>@filipsajdak</code></a>! -
feat(flowchart): add <code>folder</code>, <code>bucket</code>,
<code>console</code> (terminal window) and <code>browser</code>
shapes</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/7842">#7842</a>
<a
href="ae3e1157c1"><code>ae3e115</code></a>
Thanks <a
href="https://github.com/filipsajdak"><code>@filipsajdak</code></a>! -
feat(flowchart): add <code>person</code> shape (circular head above a
rounded body), usable in flowcharts via <code>A@{ shape: person
}</code></p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/7724">#7724</a>
<a
href="0fd7a9fe0d"><code>0fd7a9f</code></a>
Thanks <a
href="https://github.com/xdumaine"><code>@xdumaine</code></a>! -
feat(xyChart): add legends for named line and bar series</p>
</li>
</ul>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/7847">#7847</a>
<a
href="215fe89d3e"><code>215fe89</code></a>
Thanks <a
href="https://github.com/filipsajdak"><code>@filipsajdak</code></a>! -
fix(c4): named attributes such as <code>$tags</code>, <code>$link</code>
and <code>$sprite</code> are no longer clobbered to undefined when they
arrive in an earlier positional slot of
Person/System/Container/Component/Boundary/Rel statements.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/7871">#7871</a>
<a
href="8d874c49fa"><code>8d874c4</code></a>
Thanks <a
href="https://github.com/knsv-bot"><code>@knsv-bot</code></a>! -
fix(flowchart): stop dagre layout from spamming <code>warn</code>-level
logs on every node/edge/cluster</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8071">#8071</a>
<a
href="b3d1f63167"><code>b3d1f63</code></a>
Thanks <a
href="https://github.com/pbrolin47"><code>@pbrolin47</code></a>! -
fix(block): sibling blocks overlapping in block diagrams when one has a
label wider than 200px</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/7870">#7870</a>
<a
href="71b8843fb5"><code>71b8843</code></a>
Thanks <a
href="https://github.com/knsv-bot"><code>@knsv-bot</code></a>! - fix: a
<code>RangeError: Invalid array length</code> crash when rendering
certain edges.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/7924">#7924</a>
<a
href="9cbef5d94f"><code>9cbef5d</code></a>
Thanks <a
href="https://github.com/nightt5879"><code>@nightt5879</code></a>! -
fix(treeView): icons disappearing after strict security
sanitization.</p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="dcb694ddb5"><code>dcb694d</code></a>
Version Packages (<a
href="https://redirect.github.com/mermaid-js/mermaid/issues/8130">#8130</a>)</li>
<li><a
href="178d7c79fc"><code>178d7c7</code></a>
fix: restore edgePaths class on the edge group (<a
href="https://redirect.github.com/mermaid-js/mermaid/issues/8125">#8125</a>)</li>
<li><a
href="569f46e261"><code>569f46e</code></a>
Version Packages (<a
href="https://redirect.github.com/mermaid-js/mermaid/issues/8114">#8114</a>)</li>
<li><a
href="3054836688"><code>3054836</code></a>
Version Packages</li>
<li><a
href="5b17e0a38f"><code>5b17e0a</code></a>
Merge pull request <a
href="https://redirect.github.com/mermaid-js/mermaid/issues/8092">#8092</a>
from mermaid-js/hotfix/11.17.1</li>
<li><a
href="655211a065"><code>655211a</code></a>
Reverted change of wrap-options</li>
<li><a
href="8a23480200"><code>8a23480</code></a>
Updated doc from feedback</li>
<li><a
href="412c80abb7"><code>412c80a</code></a>
Update doc and consistent handlig in c4 as for seq diags</li>
<li><a
href="b433a9aad5"><code>b433a9a</code></a>
Updated changeset to describe specifik diagram affected</li>
<li><a
href="6bae15eb59"><code>6bae15e</code></a>
Updated tests to Playwright API</li>
<li>Additional commits viewable in <a
href="https://github.com/mermaid-js/mermaid/compare/mermaid@11.16.1...mermaid@11.17.2">compare
view</a></li>
</ul>
</details>
<br />
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
## Thinking Path
> - Paperclip manages AI agents that perform work.
> - The paid runner matrix verifies complete runner behavior with real
providers.
> - Each matrix job currently repeats work on GitHub-hosted runners.
> - Paperclip has an ephemeral AWS runner fleet for trusted workflows.
> - The paid workflow needs a reviewed and fail-closed route to that
fleet.
> - This pull request adds that route and keeps the existing hosted
runner as the disabled-state fallback.
> - The benefit is faster paid campaigns with the same actor,
environment, and secret boundaries.
## Linked Issues or Issue Description
**What happened?**
The Runner Full-Stack E2E workflow always uses `ubuntu-latest-m`. It
limits the matrix to 57 parallel jobs. The repository AWS fleet can run
100 ephemeral jobs, but the paid workflow cannot select it.
**Expected behavior**
An explicit repository flag must select the reviewed AWS fleet label. A
missing or invalid flag must keep the existing hosted runner. The
workflow must authorize the stable actor identity before it routes any
paid job.
**Steps to reproduce**
1. Dispatch the Runner Full-Stack E2E workflow from `master`.
2. Inspect a paid matrix job.
3. Observe that the job requests `ubuntu-latest-m` even when the AWS
fleet should be used.
**Paperclip version or commit**
`da0947d3582ac7779d6bf11851c9938eca6c5c8c`
**Deployment mode**
GitHub Actions paid runner campaign.
## What Changed
- Add a fail-closed `RUNNER_E2E_AWS_ENABLED` switch.
- Select only the reviewed AWS fleet label or the existing hosted label.
- Permit up to 100 parallel jobs in AWS mode.
- Keep the hosted-runner limit at 57.
- Reauthorize paid execution before checkout and provider access.
- Stop paid checkouts from storing GitHub credentials.
- Cancel superseded validation-ref campaigns while preserving `master`
audit runs.
- Add workflow policy checks and operator documentation.
## Verification
- `git diff --check`
- `actionlint -ignore SC2129
.github/workflows/runner-full-stack-e2e.yml`
- The organization runner group permits this workflow only from
`refs/heads/master`.
- The repository AWS switch remains disabled until this pull request is
merged and a one-cell probe succeeds.
## Risks
- A wrong fleet policy can leave jobs queued. The disabled state keeps
the existing hosted runner.
- The AWS fleet uses paid compute. The workflow validates a configured
maximum of 100 jobs.
- The runner group, actor allowlist, and paid environment remain
separate enforcement layers.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex based on GPT-5 with agentic reasoning, repository
inspection, code editing, Git, GitHub API coordination, and static
workflow analysis. The exact deployed model identifier and
context-window size are not exposed to this task.
## 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
> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip uses local adapters to connect agent sessions to the
control plane
> - The Codex adapter emits turn events from response and notification
channels
> - A terminal notification can arrive before the turn/start response
> - This pull request gates the terminal event on turn.accepted
> - The result keeps the event order stable for consumers and tests
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip uses local adapters to connect agent sessions to the
control plane
> - The Codex adapter emits turn events from response and notification
channels
> - A terminal notification can arrive before the turn/start response
> - This pull request gates the terminal event on turn.accepted
> - The result keeps the event order stable for consumers and tests
## Linked Issues or Issue Description
**What happened?**
The Codex harness session emitted `turn.accepted` only after the
`turn/start` response resolved. A terminal notification could arrive
before that response and reach consumers first.
**Expected behavior**
The Codex driver must emit `turn.accepted` before any terminal event for
the same turn.
**Steps to reproduce**
1. Start a Codex harness session.
2. Keep the `turn/start` response pending.
3. Send `turn/started` and `turn/completed` notifications.
4. Observe the event order.
**Paperclip version or commit**
`afbcd28dae9e51108738c4258929b95ca359186c`
**Deployment mode**
Built from source with the Codex driver test harness.
**Agent adapter(s) involved**
Codex.
## What Changed
- Add session state that tracks a pending `turn/start` operation.
- Resolve the state when `turn/start` succeeds or fails.
- Wait for that state before the terminal notification handler emits its
event.
- Add a regression test that delivers a terminal notification while
`turn/start` remains pending.
## Verification
- The regression test failed 5 of 5 times before this change and passed
5 of 5 times after it.
- The Codex driver suite passed 189 of 189 tests.
- The affected live transport test file passed 46 of 46 tests on 10
consecutive runs.
- The TypeScript check exited with status 0.
- Continuous integration must pass before merge.
## Risks
The change affects only Codex turn event ordering. It adds no sleep,
retry, or timeout. The main risk is a provider path that does not settle
`turn/start`; existing provider response handling still controls
completion.
## Model Used
OpenAI GPT-5. The exact deployment identifier is not exposed in this
environment. Tool use and code execution assisted this change.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` /
`Closes: #` / `Refs #` OR (b) described the issue in-PR following the
relevant issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Paperclip runs agent heartbeats and stores their run state in a
database
> - The direct-adapter native-isolation tests start heartbeat runs and
then clear database state
> - A terminal run status does not prove that its background database
work has stopped
> - The teardown can then deadlock with a live run during PostgreSQL
`TRUNCATE`
> - This pull request drains active runs before teardown and adds a
guard for queued or running runs
> - The benefit is stable test teardown without a production code change
## Linked Issues or Issue Description
This change fixes an intermittent test deadlock in the direct-adapter
native-isolation suite.
**What happened?**
The test teardown could run PostgreSQL `TRUNCATE` while a heartbeat
execution still held a write transaction. PostgreSQL then returned error
`40P01` during some test runs.
**Expected behavior**
The test teardown must wait until all heartbeat executions finish before
it clears the test database.
**Steps to reproduce**
1. Run
`server/src/__tests__/heartbeat-direct-adapter-native-isolation.test.ts`
repeatedly.
2. Run the suite against PostgreSQL-backed native isolation.
3. Observe intermittent deadlock error `40P01` during teardown.
**Paperclip version or commit**
Commit `57515726d3ef45a07df9b5ee2dfaf7d108556478`.
**Deployment mode**
Built from source with the native-isolation test suite.
**Agent adapter(s) involved**
Not adapter-specific. The test covers the direct adapter path.
**Database mode**
External PostgreSQL used by the native-isolation test suite.
**Additional context**
Related prior attempt:
[#12715](https://github.com/paperclipai/paperclip/pull/12715). This pull
request starts from current `master` and does not depend on that pull
request.
## What Changed
- Drain active heartbeat run executions before `afterEach` runs
`TRUNCATE`.
- Assert that no heartbeat run remains `queued` or `running` before
teardown.
- Drain active executions before `afterAll` removes the temporary
database.
- Create one shared `heartbeatService` instance in `beforeAll` so the
drain tracks the test runs.
## Verification
- Run
`server/src/__tests__/heartbeat-direct-adapter-native-isolation.test.ts`
20 times. All 20 runs pass.
- Run the target suite with
`server/src/__tests__/native-run-finalizer.test.ts`. Both files pass
with 19 tests.
- Run `tsc --noEmit`. The branch adds no new error compared with
`master`.
- Run the pull request checks after GitHub starts them.
## Risks
Low risk. The change affects one test file and no production code. The
added drain can expose an incomplete test run before teardown, which is
the intended guard.
## Model Used
OpenAI GPT-5. Exact runtime model ID: GPT-5. The context window is not
exposed to this agent. The model used tool calls 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>