Commit Graph
5227 Commits
Author SHA1 Message Date
DottaandPaperclip 4d468f998b Merge master and preserve bounded policy reads in admission batches
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-15 00:19:11 -05:00
DottaandPaperclip a15798e752 test(server): reuse seeded delivery workspace fixture (#16114)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-15 00:12:27 -05:00
DottaandPaperclip 1315aa22c2 test(ui): retire duplicate company settings checks (#16113)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-15 00:10:50 -05:00
DottaandPaperclip 86816ff965 test(adapters): consolidate Grok stale-auth promotion coverage (#16112)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-15 00:09:21 -05:00
DottaandPaperclip 982ff62949 test(shared): consolidate ZIP conversion observations (#16118)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-15 00:06:50 -05:00
DottaandPaperclip 5e62927a8f test(adapters): consolidate Codex staging permission guards (#16111)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-15 00:06:35 -05:00
DottaandPaperclip 1897080a6d test(scripts): retire duplicate provisioning fallback launch (#16110)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-15 00:02:38 -05:00
DottaandPaperclip 5c65525219 test(scripts): consolidate stable shard selection cases (#16109)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-15 00:02:23 -05:00
DottaandPaperclip a05052f77d test: retire duplicate heartbeat disposition recovery executions (#16102)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-15 00:00:51 -05:00
DottaandPaperclip 67e45e869e test(ui): retire duplicate CompanyExport render checks (#16108)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:59:17 -05:00
DottaandPaperclip 662a20281d test(cli): retire duplicate ZIP codec owner tests (#16116)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:57:43 -05:00
DottaandPaperclip 0de90e25bf test(adapter-utils): share pre-READY work-bound bridge execution (#16098)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:56:07 -05:00
DottaandPaperclip 1ff08a53cb test(tailscale): retire duplicate substituted-listener withdrawal (#16106)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:51:52 -05:00
DottaandPaperclip 522619744b test(ui): retire duplicate IssueChatThread render cases (#16092)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:51:37 -05:00
DottaandPaperclip ae6e701821 test(ui): share repeated IssueDetail render proofs (#16091)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
canary/v2026.1015.0-canary.7
2026-10-14 23:47:38 -05:00
DottaandPaperclip 997ea5fd17 test(server): retire duplicate process-loss recovery execution (#16088)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:47:22 -05:00
DottaandPaperclip 3b6d1452f2 test(daytona): retire duplicate pack span execution (#16099)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:45:50 -05:00
DottaandPaperclip aa6910939b test(runtime): share the projectless local lease acquisition (#16086)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:41:48 -05:00
DottaandPaperclip d6e4b36c8c test(server): move Git redaction proof to its owner (#16081)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:35:43 -05:00
76164b53cb fix(heartbeat): retry a legacy run that lost its lease before dispatch (#16358)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The heartbeat runner executes legacy agent runs. A controller lease
watchdog keeps durable ownership of each legacy run, and it aborts the
run when it loses the lease.
> - #16084 records a lease loss as a `controller_lease` cancellation
when the adapter returns or throws. It does not cover a lease loss
during setup, before the adapter starts.
> - In setup, the abort reason goes to the setup catch. The catch
records an opaque `setup_failed` and sends the raw
`LegacyControllerLeaseLostError` to Sentry with no cause label.
> - Issue continuation recovery treats `setup_failed` as non-retryable.
The task moves to `blocked`, but no provider work started.
> - This pull request records a lease loss before dispatch as
`controller_lease_lost`, with the same `controller_lease` evidence.
Recovery retries it as transient infrastructure, and so does the
accepted-interaction (plan approval) continuation retry.
> - The benefit is that a host lease loss during setup no longer strands
the task, and the next Sentry event shows which renewal failure caused
the loss.

## Linked Issues or Issue Description

Refs #16084
Refs #16355
Refs #16359

**What happened?**
A staging server reported `LegacyControllerLeaseLostError: Legacy
controller lease lost` from the heartbeat. The run failed during setup
with `setup_failed`. Continuation recovery then said "non-retryable
failure on this issue's continuation run (`setup_failed`)" and moved the
task to `blocked`. A scanner had to move the task back to `in_progress`
by hand.

**Expected behavior**
A lease loss before adapter dispatch is a host infrastructure stop. The
run records the `controller_lease` cause, like the adapter-phase path.
The run keeps `providerWorkStarted: false`. Recovery retries the
continuation with its bounded transient budget, and it does not block
the task.

**Steps to reproduce**
1. Start a legacy run that has a controller lease.
2. Before the adapter dispatch, make the lease renewal fail: the update
matches no row, the query errors, or the lease deadline passes.
3. `controllerLease.assertOwned()` throws the abort reason in workspace
preparation or before dispatch.
4. Before this change: the run is `failed`/`setup_failed`, `resultJson`
has no `cancellation`, and continuation recovery blocks the task. After
this change: the run is `failed`/`controller_lease_lost` with
`cancellation.source: "controller_lease"` and a closed cause, and
continuation recovery classifies it as `transient_infra`.

**Additional context**
#16355 handles a different path: a restart orphan that the next boot
reaps as `process_lost`. This change handles a live controller that
loses its own lease during setup. This change does not find the root
cause of the lease loss. The closed cause label in the next Sentry event
gives that fact.

## What Changed

- `server/src/services/heartbeat/run-completion.ts`: `failRunSetup`
receives the execution abort signal. When the abort reason is a
`LegacyControllerLeaseLostError`, it writes error code
`controller_lease_lost`, adds the `controller_lease` cancellation
evidence, and keeps the existing bootstrap recovery facts. The agent
goes back to idle, not `error`, because the stop is a host fault. This
applies only before provider dispatch: `failRunSetup` also receives
`providerDispatchStarted`, and a setup-path error after a legacy adapter
or native dispatch began keeps `setup_failed`, so started work is never
retried.
- `server/src/services/legacy-controller-lease.ts`: add the
`CONTROLLER_LEASE_LOST_FAILURE_CODE` constant.
- Merged with #16359 (lease-loss timings): the setup failure keeps its
`resultJson.controllerLeaseLoss` timings and adds this PR's
`controller_lease` cancellation from the abort signal. #16359's setup
test now aborts the signal first, as `assertOwned()` does, and expects
`controller_lease_lost`.
- `server/src/services/heartbeat.ts`: pass the execution abort signal
and `providerDispatchStarted` (`legacyAdapterEntered ||
nativeDispatchStarted`) to `failRunSetup`.
- `server/src/services/recovery/service.ts`: add `controller_lease_lost`
to the transient-infrastructure continuation codes (3 bounded attempts
with backoff).
- `server/src/services/heartbeat/retries.ts`: add
`controller_lease_lost` to the accepted-interaction continuation retry
allowlist, next to `process_lost`. Without it, a plan-approval
continuation that lost its lease before dispatch was escalated and
blocked before generic recovery could see it.
- `server/src/services/heartbeat/run-lifecycle.ts`: add
`controller_lease_lost` to the pre-adapter setup codes, so the comment
policy does not queue a missing-comment retry.
- `doc/run-log-events.md`: document the new error code.
- Tests: setup lease loss when the abort reason is thrown, and when a
setup step throws a different error after the abort. A unit test shows a
lease loss after dispatch keeps `setup_failed`. A classifier test shows
`controller_lease_lost` is transient and `setup_failed` stays
non-retryable. Two assembled heartbeat tests
(`heartbeat-process-recovery.test.ts`) lose the real lease during
workspace preparation: one for a plain issue continuation (one
`transient_failure` retry) and one for an accepted plan-approval
interaction continuation (one `interaction_continuation_infra_retry`
retry). Both check the saved run, no adapter dispatch, and the issue
still `in_progress`.

## Verification

- `cd server && npx vitest run
src/services/heartbeat/run-completion.test.ts`: 25 passed (embedded
Postgres), including the new post-dispatch test.
- `cd server && npx vitest run src/services/run-cancellation.test.ts
src/services/recovery/provider-failure-classification.test.ts`: passed
on the first head (29 with run-completion; 25 classifier).
- `cd server && npx vitest run
src/services/legacy-controller-lease.test.ts`: 13 passed (needs `pnpm
--filter @paperclipai/plugin-sdk... build` first).
- After merging master (head `2148b27`): `run-completion`,
`legacy-controller-lease`, `run-cancellation`,
`provider-failure-classification` and `run-failure-report` suites, 310
passed; `heartbeat-process-recovery.test.ts -t "lease|dispatch"`, 31
passed.
- Head `c52825c`: `retries`, `run-completion`,
`heartbeat-retry-scheduling`, `legacy-controller-lease` and
`provider-failure-classification` suites, 134 passed;
`heartbeat-process-recovery.test.ts -t "lease|dispatch|interaction"`, 41
passed.
- Negative controls: with the lease-loss branch in `run-completion.ts`
disabled, the plain-continuation assembled test fails (`setup_failed`
instead of `controller_lease_lost`). With the `retries.ts` allowlist
change reverted, the accepted-interaction assembled test fails because
no retry is scheduled.
- Not run locally: full `tsc` and `pnpm build`. The sandbox has a 4 GB
memory limit and the OOM killer stops `tsc`. CI is the typecheck and
build gate.

## Risks

- Low risk. The change applies only when the execution abort reason is a
`LegacyControllerLeaseLostError` in the setup catch before dispatch.
Other setup failures keep their current codes.
- The retry uses the existing continuation recovery budgets and
admission checks (agent state, budget, pause). The
interaction-continuation retry keeps its existing 2-attempt budget. The
retry is a new run. The revoked controller cannot dispatch, because its
renewal compare-and-set fails.
- A run that the reaper settles first keeps the reaper outcome.
`setRunStatusIfRunning` makes the first terminal write win.
- Sentry still gets this unexpected stop, with the closed
`controllerLeaseLossCause` label. If the lease loss is frequent, the
event volume does not change.

## Model Used

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with
tool use (shell, file edits, test runs).

## 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 (targeted suites pass;
typecheck/build not possible in this sandbox, see Verification)
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

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

---------

Co-authored-by: Devin Foley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 21:31:42 -07:00
DottaandPaperclip 3ea39f75c1 test: consolidate duplicate owner proofs (#16075)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:17:13 -05:00
DottaandPaperclip 8541dfad29 test(e2e): share initial Settings sidebar browser proof (#16083)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:14:20 -05:00
DottaandPaperclip 9076329149 test: consolidate duplicate runtime credential proofs (#16079)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:13:45 -05:00
DottaandPaperclip 5f160421e3 test(server): run status corpus fixtures once (#16078)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:13:31 -05:00
DottaandPaperclip 0e1bb3748b test(db): retain owner nullability in the active-slot keeper (#16076)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:13:15 -05:00
DottaandPaperclip ccbfefef5f test(ui): retire 33 duplicate executions and share render setup (#16072)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 23:12:34 -05:00
DottaandPaperclip f64d05adf4 test: consolidate repeated ACPX startup metadata scenarios (#16068)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
canary/v2026.1015.0-canary.6
2026-10-14 23:02:09 -05:00
DottaandPaperclip 422728fc36 test: retire seven repeated ACPX trace executions (#16061)
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 22:50:25 -05:00
8b27af77db fix(heartbeat): wait for a live holder of a reused shared execution workspace (#16371)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The heartbeat pre-dispatch gate decides if a run can use a shared
workspace in its execution environment.
> - A child task can inherit the shared execution workspace of its
parent. The parent run is often still live when the child run starts.
> - On a sandbox environment, the gate failed the child run with
`configuration_incomplete`. It did this for every concurrency policy,
also for the default `auto` policy.
> - Recovery then blocked the task and told the operator to bind missing
secrets. That was not the real cause. An operator had to select a
different workspace by hand.
> - Shared project workspaces already use the `auto`/`serialize` policy
to wait through the `workspace_busy` retry path.
> - This pull request applies the same policy to a reused shared
execution workspace.
> - The benefit: the child task waits for the parent run and then
continues automatically. It does not fail and become blocked.

## Linked Issues or Issue Description

**What happened?**
A parent agent created a child task. The child inherited the parent's
shared execution workspace. The child agent used a sandbox environment.
The child run started about 1.5 seconds after the parent created the
task, while the parent run was still live. The run failed before the
adapter started with this error: "This environment cannot place
concurrent agents in one physical shared folder. Use local/SSH sharing
or isolated task workspaces." Recovery set the task to `blocked` with
the message "required secret/env bindings are missing".

**Expected behavior**
With the default `auto` policy, the child run waits for the parent run
(`workspace_busy`) and then continues automatically. This is the same
behavior as for a shared project workspace on a sandbox.

**Paperclip version or commit**
Staging release `8ffc5748`. The same code is on `master`.

**Deployment mode**
Hosted cloud (staging), agent on a sandbox execution environment.

**Steps to reproduce**
1. Use an agent with a sandbox environment.
2. Bind two issues to the same `shared_workspace` execution workspace.
Set `executionWorkspacePreference: reuse_existing` on the second issue.
3. Keep a live run on the first issue.
4. Wake the agent on the second issue. Before this change, the run fails
with `configuration_incomplete`. After this change, the run is deferred
with `workspace_busy`.


## What Changed

- `environment-selection.ts`: When a reused shared execution workspace
has a live holder on a non-local/non-SSH environment, the gate now reads
`sharedWorkspaceConcurrency`. With `auto` or `serialize`, it throws
`WorkspaceBusyDeferral`. Only `allow` still fails with
`shared_realization_unsupported`.
- `retries.ts`: Added `findExecutionWorkspaceHolder` next to
`findSharedWorkspaceHolder`. It keeps the old match rule. A running
holder that has had no output for more than one hour (the existing
staleness cutoff) is still returned, with `stale: true`. A live holder
is preferred over a stale one.
- `environment-selection.ts`: A stale holder still owns the folder,
because silence does not prove that the run stopped. The child run
therefore fails with `configuration_incomplete`
(`shared_realization_unsupported`, `holderStale: true`). It does not
share the folder with the holder, and it does not wait without limit.
This is the same result as before this pull request, with a more
specific message.
- `retries.ts`: `WorkspaceBusyDeferral` can now carry an
`executionWorkspaceId`. The run result, the run event, and the
cancellation reason name the execution workspace when it applies.
- `stranded-notice.ts`: Added a specific recovery notice for
`shared_realization_unsupported`, and a second notice for a stale
holder. They no longer tell the operator to bind missing secrets.
- `doc/experimental/issue-worktree-support.md`: Documented that `allow`
is only concurrent on local/SSH, and that a reused shared execution
workspace follows the same rules.

## Verification

- `cd server && npx vitest run
src/__tests__/heartbeat-workspace-busy.test.ts -t "reused shared
execution workspace"` covers 4 tests:
- `auto` on a sandbox with a live holder: the run is cancelled with
`workspace_busy`, a retry is scheduled, no Sentry run failure is
reported, and the issue does not become blocked.
- After the holder finishes, the scheduled retry is promoted and runs.
The adapter gets the inherited shared folder, and the issue keeps its
execution workspace.
- `allow` on a sandbox: the run still fails with the shared-folder
message and no retry is scheduled.
- A holder that is still running but silent past the staleness cutoff:
the run fails with `holderStale: true`, the adapter does not start, no
retry is scheduled, and the holder is not touched.
- `npx vitest run src/services/recovery/stranded-notice.test.ts
src/services/heartbeat/environment-selection.test.ts
src/services/heartbeat/retries.test.ts` covers the notice and the
selection unit tests.
- First commit (`d0b7a092`): the 3 original integration tests and the
unit suites passed locally, and all CI checks passed.
- Follow-up commit (`3c7e515`, which addresses the Greptile review):
these tests were **not** run locally. Dependency install was killed
twice by the sandbox's 4 GB memory limit. CI is the first run of the new
and changed tests.
- Known local failure: `does not hide ambiguous, malformed, provider, or
transport cancellations behind workspace_busy` fails in this sandbox
also on unchanged `master` (checked with `git stash`). This change does
not cause it.
- Not run locally: the full server `tsc --noEmit`, for the same memory
reason. CI runs the typecheck.

## Risks

- Behavior change: before, a run with a live holder of a reused shared
execution workspace on a sandbox failed at once. Now it waits with
`auto`/`serialize`. A long parent run can delay the child. If the holder
has no output for more than one hour, the child fails as before instead
of waiting without limit.
- An explicit `allow` policy keeps the old failure.
- Local and SSH environments do not change.
- No schema or migration change.

## Model Used

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`), with tool use and code
execution in the Paperclip agent 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
- [ ] I have run tests locally and they pass (first commit only; see
Verification)
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

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

---------

Co-authored-by: Devin Foley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:45:29 -07:00
DottaandPaperclip 66724b8ce0 perf(auth): project only required run context fields (#16380)
Project only the required JSON fields for saved-run policy and direct-parent report authorization. Preserve current reads, JSON types, identity checks, permission decisions and legacy task-key precedence.

Validation: 186 affected cases; 17 database transfer-budget cases after the shared startup-timeout correction; full typecheck/build; exact-head full CI, E2E, Superagent and Apex 5/5.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 22:45:08 -05:00
DottaandPaperclip 1d5e2c89f0 test(server): retire duplicate guards and share issue seed fixtures (#16059)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Server tests protect task scheduling, session continuation,
transport decisions, and issue data.
> - Four cases repeat the same registered inputs and assertions as
existing owner cases.
> - Issue service tests also repeat 103 database seed statements.
> - Each distinct input, assertion, lifecycle, and security contract
must keep its proof.
> - This pull request removes the four exact duplicates and shares only
exact seed statements.
> - The benefit is 584 fewer test lines and four fewer registered
executions.

## Linked Issues or Issue Description

Refs #16047. This draft is stacked on
`codex/test-audit-round-six-delivery` at
`e868e6645ce0d598d756d3458f0b3d71afd6db4a`.

**What existing behavior does this improve?**

Maintenance of server owner tests. Production behavior does not change.

**Current behavior**

Four registered cases repeat identical executable bodies. The issue
service suite repeats 80 company rows, ten project rows, and thirteen
agent rows.

**Proposed behavior**

Keep each contract in its existing owner suite. Use three direct async
seed helpers for the exact repeated database rows. Preserve each
caller's database instance, IDs, awaited insert position, field order,
and fresh object allocations.

**Reason and benefit**

Remove 584 net test lines and four registered executions. No measured
wall-time improvement is claimed. No production, dependency, workflow,
telemetry, or skip change is included.

## What Changed

- Share exact company, Workspace project, and CodexCoder rows in
`issues-service.test.ts`. All 137 registered cases remain.
- Retire `heartbeat-scheduling-suppression.test.ts` → `suppresses
heartbeat scheduling while a task drain is active`. Keeper:
`heartbeat-task-drain.test.ts` →
`start_task_drain_suppresses_admission`.
- Retire `heartbeat-timer-wake-session-reset-pf4.test.ts` → `does not
reset for issue_commented (preserve continuation context)`. Keeper:
`heartbeat-workspace-session.test.ts` → `does not reset for comment
wakes`.
- Retire `setup-token-session.test.ts` → `denies a direct non-loopback
HTTP receive-token request, so it delivers no token (SR-6)`. Keeper in
the same describe: `denies a direct non-loopback HTTP request (SR-6)`.
- Retire `setup-token-session.test.ts` → `denies a spoofed forwarded
HTTPS receive-token request under a broad proxy setting (SR-7)`. Keeper
in the same describe: `denies a spoofed X-Forwarded-Proto when no
trusted proxy is configured (SR-6)`.

The retired receive-token cases only call
`evaluateConfidentialTransport`. They neither request a route nor
configure `TRUST_PROXY`. The keepers protect the exact guard inputs and
output. The route now uses the guard to select a transport advisory;
this PR makes no route-denial or token-delivery proof claim.

## Verification

- Complete matched six-file baseline: **414/414 pass**, no failures or
skips. Candidate: **410/410 pass**, no failures or skips.
- Run: `pnpm exec vitest run --project @paperclipai/server
server/src/__tests__/issues-service.test.ts
server/src/__tests__/heartbeat-scheduling-suppression.test.ts
server/src/__tests__/heartbeat-task-drain.test.ts
server/src/__tests__/heartbeat-timer-wake-session-reset-pf4.test.ts
server/src/__tests__/heartbeat-workspace-session.test.ts
server/src/services/setup-token-session.test.ts`.
- Expand the actual three helper bodies at all 103 call sites. The
complete issue-service AST is identical to the original after expansion.
Independent review checked the exact inputs, lexical bindings, awaited
order, and fresh nested configs.
- Deliberate regressions disabled task-drain suppression, forced comment
wakes to reset, and allowed the final insecure transport branch. All
four retained keepers failed at their intended equality assertions. Each
production file was restored byte for byte against HEAD.
- An optional project-inheritance mutation was ineffective because
another workspace path still derives project identity. It passed and is
excluded from accepted preservation evidence.
- `git diff --check` passes. Independent cross-review approved the exact
source and proof before this draft.
- Broad typecheck, full test, and build gates passed in PR CI on
`a9713432b1e498e023b4828ffa283a351abe856f`. All 52 check-runs concluded
success. Greptile completed on that exact head with confidence 5/5 and
no findings.

## Risks

- Low risk: only test code changes. Four exact duplicate bodies are
removed.
- The fixture helpers preserve all original IDs and DB operations. They
do not cache data or generate assertions.
- Same-named explicit-project and derived-project inheritance cases
remain because their inputs differ. Separate login PTY and duplex
lifecycle cases also remain.
- No wall-time speed gain is claimed from fixture line reduction.

## Model Used

- OpenAI GPT-6.1-Sol, exact model ID `gpt-6.1-sol`, through Codex. Tool
use, code execution, and reasoning assisted the audit. The runtime does
not expose its 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-10-14 22:38:16 -05:00
DottaandPaperclip d73d7a8651 perf(skills): batch repository dependency reads (#16377)
Batch dependency Markdown reads through the bounded Git snapshot reader and queue newly discovered dependencies once. Preserve package contents, limits, authorization, audits, and reference order.

Validation: 63 affected cases; full typecheck/build; exact-head full CI, E2E, Superagent and Apex 5/5. Local Git comparison: 502 individual reads reduced to three batches with identical entries.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 22:31:38 -05:00
DottaandPaperclip 32ede08918 fix(grok): copy pairing codes during agent reconnect (#16373)
Grok Bot reconnect showed a masked one-use code without a copy action. Add the shared Copy code control, preserve masking and copy-error feedback, and keep the code out of Bot instructions. Cover the reconnect flow and readiness wait in tests and Storybook, and update the user guide.

Verified head 1346f7a909: 55 checks passed, two optional skips, full tests/build/typecheck/E2E, and Greptile 5/5 with no unresolved threads. Local typecheck, build, token gates, Storybook build and focused regressions pass. Scoped Chrome copying and a fresh built story were verified with synthetic data. Actual Grok runtime qualification remains a release gate.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 22:22:54 -05:00
275189d8d6 fix(native): report why an unclassified Codex turn failed (#16354)
<!-- Write all pull request text in Simplified Technical English
(ASD-STE100): short sentences, one instruction per sentence, simple
approved vocabulary, and the active voice. -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Native runs use the Paperclip runner to drive a provider session,
for example the Codex app server
> - When a Codex turn fails, two classifiers look for known failures: a
model rejection and a server capacity failure
> - When no classifier matches, the run fails with the generic message
`Native session failed` and the error code `adapter_failed`
> - The run failure report to Sentry then has no data about the cause,
because the server drops the Codex `codexErrorInfo` value
> - This pull request records a closed-vocabulary diagnostic for that
failed turn and sends it to the Sentry `provider_failure` context
> - The benefit is that operators can see which Codex failure kind
causes these generic failures, and then write a targeted fix

## Linked Issues or Issue Description

**What happened?**
Hosted staging reports many run failures with the title `Error: Native
session failed`. The events have `agent_adapter=paperclip_runner`,
`error_code=adapter_failed`, `driverKind=codex_app_server`, and
`exitCode=1`. The events do not show why the Codex turn failed.

**Expected behavior**
A run failure report for a failed Codex turn shows the failure kind, so
that an operator can find the cause. The report must not contain
provider response text, prompts, or model names.

**Steps to reproduce**
1. Start a native Codex run.
2. Make the Codex app server end the turn with `status: "failed"` and an
error that is not a model rejection or `serverOverloaded`. For example,
use `codexErrorInfo: "contextWindowExceeded"`.
3. Look at the Sentry event for the run failure. Before this change, the
`provider_failure` context is empty.

**Agent adapter(s) involved**
Codex (native runner, `codex_app_server` driver).

## What Changed

- `native-provider-failure.ts`: The provider failure observation now
records a diagnostic for each failed Codex turn. The diagnostic has only
closed values: `provider: "codex"`, `category: "turn_failed"`, a known
`CodexErrorInfo` kind (or `unrecognized` / `absent`), an HTTP status
from 100 to 599 for the HTTP variants, and a runner failure code from an
explicit allowlist of the codes the runner itself writes on a failed
Codex turn (any other string becomes `unrecognized`, because a provider
error object can carry its own `code`). It never copies the provider
message.
- New `unclassifiedTurnFailure()` gives this diagnostic only for a
failed terminal turn that `forTerminal()` does not classify.
`forTerminal()`, error codes, error messages, retries, and finalizer
decisions do not change.
- New `readNativeTurnFailureDiagnostic()` rebuilds a persisted value
from the closed vocabulary and rejects all other values.
- `native-session-executor.ts`: The adapter result keeps the generic
message. It adds `resultJson.nativeTurnFailure` when the turn failure is
unclassified.
- `run-failure-diagnostics.ts`: The run failure report copies the
validated diagnostic into the `provider` context. Sentry sends this as
`provider_failure`.
- Tests: unit tests for the vocabulary, HTTP status bounds, unknown
kinds, runner codes, and privacy; a diagnostics test; and an update to
the end-to-end executor test for the `unknown-error` scenario.

## Verification

- `pnpm exec vitest run
src/services/native-runtime/native-provider-failure
src/services/__tests__/run-failure-diagnostics.test.ts
src/services/__tests__/run-failure-report*.test.ts
src/__tests__/sentry*.test.ts` (in `server/`): 6 files, 432 passed, 5
skipped.
- `pnpm exec vitest run
src/services/native-runtime/native-session-executor.test.ts` (in
`server/`): 698 passed.
- I could not run the full `tsc --noEmit` in my sandbox: the memory
limit stopped the process, and the runner vendor build needs `cargo`. CI
runs the full typecheck.
- To check by hand: make a Codex turn fail with an unclassified error.
Then make sure that the Sentry event `provider_failure` context shows
`category: turn_failed` and the `codexErrorInfo` kind, and no provider
text.

## Risks

- Low risk. The change adds diagnostic data only. Run status, error
code, error message, retry, and finalizer logic do not change.
- Privacy: the diagnostic uses an allowlist of Codex enum names, a
bounded integer, and an allowlist of runner-issued failure codes. Tests
make sure that provider text does not get through.
- If Codex adds a new `CodexErrorInfo` kind, the diagnostic shows
`unrecognized` until the allowlist gets the new kind.
- This change does not fix the root cause. It gives the data that is
necessary to find the cause.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, used through Claude
Code with tool use (shell, file edit, test runs).

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

Related: #15347 (capacity classifier), #15304 (model rejection
diagnostics).

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

---------

Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
canary/v2026.1015.0-canary.5
2026-10-14 20:14:10 -07:00
DottaandPaperclip f6c2906b78 fix(runner): qualify Claude native executable on Linux ARM64
Verify the pinned 0.3.286 platform package, executable digest, and declared npm dependency graph using the existing custody checks.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 22:13:20 -05:00
DottaandPaperclip 7e5c5448a8 fix(server): support large retained reader sets
Use PostgreSQL array constructors and cover private project grant changes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 22:08:36 -05:00
3a440d28c4 fix(server): read existing grants before seeding role defaults (#16365)
`insertMissingPrincipalGrants` now reads the existing permission keys first and inserts only missing grants. In the steady state no INSERT runs, so cloud-tenant auth resync no longer waits on row locks held by concurrent grant edits until the statement timeout (Sentry PAPERCLIP-BACKEND-3S).

Co-Authored-By: Paperclip <Paperclip-Paperclip@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 20:01:34 -07:00
DottaandPaperclip be0d39b8d6 fix(server): share repository source reads across retained readers
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 21:59:38 -05:00
DottaandPaperclip d660c420ef test(server): recheck authority between admission batches
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 21:59:38 -05:00
DottaandPaperclip df7a8c02bd fix(server): batch retained repository admission authorization
Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 21:59:38 -05:00
ccc77c5b5e fix(server): record legacy controller lease-loss timings (#16359)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The heartbeat runner executes legacy agent runs, and a controller
lease watchdog keeps durable ownership of each run
> - The watchdog renews the lease every 10 seconds and stops the run
when a renewal does not finish within the 60-second lease
> - A staging run failed in setup with `LegacyControllerLeaseLostError`
and the `renewal_deadline` cause, after about eight minutes of
preparation and with no server restart
> - The setup failure path did not record the cause, and no evidence
showed why the renewal did not finish: a stalled database query or a
blocked event loop
> - This pull request records bounded watchdog timings with each lease
loss, on the setup failure path and on the cancellation path
> - The benefit is that the next event shows which of the two causes
stops renewal, which is the missing fact to fix the root cause

## Linked Issues or Issue Description

Refs #16084 (added the closed lease-loss cause for cancelled runs)
Refs #16355 (retries a run that a restart stopped before dispatch; a
different path)

**What happened?**
A legacy `paperclip_runner` run on staging failed with `setup_failed`
and the message "Legacy controller lease lost". The stack shows the
watchdog deadline timer (`renewal_deadline`). The run was in
`executionStage: "preparing"` for about 467 seconds. The server started
about 11 minutes before the run, so this was not a restart. The run
record and the Sentry event do not show the lease-loss cause, because
the setup failure path does not read it. Nothing shows if a renewal
query was in flight at the deadline, or if the deadline callback ran
late.

**Expected behavior**
When the watchdog loses the lease, the run records the closed cause and
the watchdog timings. This applies when the loss fails setup and when it
stops the adapter. The failure report contains only the closed cause and
bounded numbers.

**Steps to reproduce**
1. Start a legacy run with a controller lease.
2. During setup, make a lease renewal query hang for more than 60
seconds, or block the event loop past the deadline.
3. Let the watchdog abort the run.
4. Read `resultJson` on the failed run. Before this change, it has no
lease-loss evidence.

## What Changed

- `legacy-controller-lease.ts`: The watchdog tracks the last successful
renewal, the renewals in flight, the scheduled deadline, and the renewal
count. `LegacyControllerLeaseLostError` now carries `leaseLossTiming`:
`sinceRenewalMs`, `renewalPendingMs` (or `null`), `deadlineLateMs` (only
for `renewal_deadline`), and `renewalCount`. Ownership behavior does not
change.
- `run-cancellation.ts`: Add the `LegacyControllerLeaseLossTiming` type
and `readControllerLeaseLoss()`. The reader accepts only a closed cause
and non-negative finite numbers, and clamps each value to 24 hours.
- `heartbeat/run-completion.ts`: Persist
`resultJson.controllerLeaseLoss` with the cause and timings. This
applies to a lease loss that stops the adapter and to a lease loss that
fails setup (the thrown abort reason).
- `run-failure-diagnostics.ts`: Report `controllerLeaseLossCause` and
the four bounded timing fields from `controllerLeaseLoss`. A setup
failure now gets the cause label too.
- `doc/run-log-events.md`: Document the new record and how to read the
timings.
- Tests: watchdog timings for a stalled query, a blocked event loop, and
a failing query; completion records the evidence for an adapter stop and
for a setup failure; the failure report clamps values, drops unknown
keys, and drops a record without a closed cause.

## Verification

- `pnpm --filter @paperclipai/server exec vitest run
src/services/legacy-controller-lease.test.ts
src/services/heartbeat/run-completion.test.ts
src/services/__tests__/run-failure-report.test.ts
src/services/run-cancellation.test.ts`
- I could not run these tests locally. The sandbox has a 4 GB memory
limit, and `pnpm install` was stopped by the out-of-memory killer during
lockfile resolution. CI is the test run for this change.
- After deployment, a new lease loss shows
`controllerLeaseRenewalPendingMs` and `controllerLeaseDeadlineLateMs` in
the Sentry execution context. A pending time near 50 seconds or more
points at the database. A large deadline lateness points at the event
loop.

## Risks

- Low risk. This change adds diagnostics. It does not change when the
watchdog stops a run, and it does not prevent the lease loss.
- The new `resultJson.controllerLeaseLoss` field is server-written.
Readers ignore unknown values. Old rows do not have it.
- The failure report gets five closed or numeric fields. It gets no
reason text, IDs, or user content.
- The event-loop test blocks the loop for about 80 ms to make the
deadline late. It uses real timers, so a very slow CI host makes the
lateness larger, not smaller.

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

## Model Used

- Anthropic Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with
tool use (shell, file edits, Sentry and GitHub connectors). The exact
context-window size is not exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [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 (not possible in this
sandbox; see Verification)
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

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

---------

Co-authored-by: Devin Foley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:52:23 -07:00
dependabot[bot] 5a9c74d1c8 build(deps): bump mermaid from 11.17.2 to 12.1.0 (#16018)
Bumps [mermaid](https://github.com/mermaid-js/mermaid) from 11.17.2 to
12.1.0.
<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@12.1.0</h2>
<h3>Minor Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8303">#8303</a>
<a
href="https://github.com/mermaid-js/mermaid/commit/9140716e72d1c1a3eb82bd5b25786e44f0cb6163"><code>9140716</code></a>
Thanks <a
href="https://github.com/filipsajdak"><code>@​filipsajdak</code></a>! -
feat: add the <code>elk.orientFeedbackEdges</code> option, enabled by
default. With the ELK layout, an edge from a node that a subgraph feeds
back into that subgraph is now routed downstream instead of around the
subgraph. This changes the layout of existing ELK diagrams that contain
such edges; set <code>elk.orientFeedbackEdges: false</code> to keep the
previous routing.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8250">#8250</a>
<a
href="https://github.com/mermaid-js/mermaid/commit/fd4f5f26546110b4da281b44d60f94644f9a146c"><code>fd4f5f2</code></a>
Thanks <a href="https://github.com/kartben"><code>@​kartben</code></a>!
- feat: add a <code>bitOrder</code> option to packet diagrams. It
defaults to <code>ascending</code>, which is the current
behaviour, and <code>descending</code> mirrors every row so it reads
from that row's highest bit down to its
lowest. Fields are still declared lowest bit first and keep their width,
so switching a diagram
between the two conventions only means changing
<code>bitOrder</code>.</p>
</li>
</ul>
<h3>Patch Changes</h3>
<ul>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8330">#8330</a>
<a
href="https://github.com/mermaid-js/mermaid/commit/50c9e555a17725e50a9cf92f4a0876710e44a53a"><code>50c9e55</code></a>
Thanks <a
href="https://github.com/ashishjain0512"><code>@​ashishjain0512</code></a>!
- fix: upgrade chevrotain to 13 so mermaid no longer pulls in vulnerable
<code>lodash-es@4.17.23</code></p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8259">#8259</a>
<a
href="https://github.com/mermaid-js/mermaid/commit/4c90f5c4487c5c5628e28073454e88708af26bfb"><code>4c90f5c</code></a>
Thanks <a
href="https://github.com/afonsojanu"><code>@​afonsojanu</code></a>! -
fix(sequence): allow whitespace between an actor name and its <code>@{
... }</code> config object</p>
<p><code>participant Bob@{ &quot;type&quot; : &quot;database&quot;
}</code> parsed fine, but adding a single space before the
config object (<code>participant Bob @{ &quot;type&quot; :
&quot;database&quot; }</code>) failed with a confusing parse error,
even though the plain form without a config object tolerates trailing
whitespace just fine.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8339">#8339</a>
<a
href="https://github.com/mermaid-js/mermaid/commit/c7fa1a550cf024ce1c74faece36ecf18a5935c5e"><code>c7fa1a5</code></a>
Thanks <a
href="https://github.com/ashishjain0512"><code>@​ashishjain0512</code></a>!
- fix: keep ELK class-diagram cardinalities off namespace frames</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8334">#8334</a>
<a
href="https://github.com/mermaid-js/mermaid/commit/d67331d927d90e53ed0cd6da7e031f3513866d41"><code>d67331d</code></a>
Thanks <a
href="https://github.com/ashishjain0512"><code>@​ashishjain0512</code></a>!
- fix(class): place cardinality labels beside their relation ends on
dagre's sides with ELK, centre dagre's end labels, and stop clipping
their text</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8344">#8344</a>
<a
href="https://github.com/mermaid-js/mermaid/commit/99a050ba56c834569df4d9891c70fd23ffe92203"><code>99a050b</code></a>
Thanks <a
href="https://github.com/pbrolin47"><code>@​pbrolin47</code></a>! - fix:
with the ELK layout, an edge label could sit up to 16px beside its edge
instead of centred on it, when the edge's terminal jog was straightened
after the label's position was computed. The label is now re-projected
onto the straightened route</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8276">#8276</a>
<a
href="https://github.com/mermaid-js/mermaid/commit/3101c7da2add126d0e1f3e256f408620efdd987e"><code>3101c7d</code></a>
Thanks <a
href="https://github.com/mir-ashiq"><code>@​mir-ashiq</code></a>! -
fix(error): show the actual error message in the error diagram</p>
<p>When a diagram fails to parse, the error diagram now draws the real
error message below the
&quot;Syntax error in text&quot; headline, wrapped to at most four
lines. Hosts that only show the SVG
(GitHub, GitLab, Obsidian, exported images) no longer hide what actually
went wrong, e.g. that the
flowchart edge limit was exceeded and <code>maxEdges</code> needs
raising via <code>mermaid.initialize</code>.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8296">#8296</a>
<a
href="https://github.com/mermaid-js/mermaid/commit/aa29345b9a5c58346fb457396614cfd349a94e68"><code>aa29345</code></a>
Thanks <a href="https://github.com/pentaoa"><code>@​pentaoa</code></a>!
- fix: preserve explicit source relations on event modeling reset
frames</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8297">#8297</a>
<a
href="https://github.com/mermaid-js/mermaid/commit/967bbdeb1b47a46f9f73c65d009d1dd19381cc05"><code>967bbde</code></a>
Thanks <a href="https://github.com/pentaoa"><code>@​pentaoa</code></a>!
- fix: reject duplicate event modeling frame IDs before rendering</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8337">#8337</a>
<a
href="https://github.com/mermaid-js/mermaid/commit/147f343c863f537e1acd4d1df527eccfc88574c8"><code>147f343</code></a>
Thanks <a
href="https://github.com/knsv-bot"><code>@​knsv-bot</code></a>! - fix: a
flowchart that declares the same subgraph id more than once now renders
as one merged subgraph with the ELK layout instead of producing NaN
geometry. Classes and <code>view: collapsed</code> set on a repeated
subgraph now apply to it, whichever declaration they follow.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8203">#8203</a>
<a
href="https://github.com/mermaid-js/mermaid/commit/40ef7b47259cb9dd335583298917c69d1174e2cb"><code>40ef7b4</code></a>
Thanks <a href="https://github.com/MFA-G"><code>@​MFA-G</code></a>! -
perf(frontmatter): replace the quadratic front matter regex on hot
paths</p>
<p><code>frontMatterRegex</code> backtracks polynomially on
whitespace-heavy input, so a
diagram well inside the default <code>maxTextSize</code> could stall
parsing for over a
second. <code>detectType</code> and <code>extractFrontMatter</code> now
use a linear scanner that
matches the regex result exactly, leaving no document stripped
differently.</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8254">#8254</a>
<a
href="https://github.com/mermaid-js/mermaid/commit/351d7d21af7f8e1dba6e021bef7e0c30f3deb92f"><code>351d7d2</code></a>
Thanks <a href="https://github.com/galshir"><code>@​galshir</code></a>!
- fix: warn when a gantt task references an unknown
<code>after</code>/<code>until</code> task id, or when its end value is
neither a valid date nor a valid duration</p>
</li>
<li>
<p><a
href="https://redirect.github.com/mermaid-js/mermaid/pull/8249">#8249</a>
<a
href="https://github.com/mermaid-js/mermaid/commit/b657a2c0735e60d9049d5078a550e0625cf42b2e"><code>b657a2c</code></a>
Thanks <a
href="https://github.com/mir-ashiq"><code>@​mir-ashiq</code></a>! -
fix(sequence): allow hyphenated actor and participant names when a
config object is attached</p>
</li>
</ul>
<!-- raw HTML omitted -->
</blockquote>
<p>... (truncated)</p>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/mermaid-js/mermaid/commit/21f72f07ea22c0af48a3149c550654e80d8e40cb"><code>21f72f0</code></a>
Merge pull request <a
href="https://redirect.github.com/mermaid-js/mermaid/issues/8360">#8360</a>
from mermaid-js/changeset-release/master</li>
<li><a
href="https://github.com/mermaid-js/mermaid/commit/3f89f7b130eb0bc3424344d01b57197f329053d3"><code>3f89f7b</code></a>
Version Packages</li>
<li><a
href="https://github.com/mermaid-js/mermaid/commit/4ef021859a94353ca35ea9cc0652b518cd142539"><code>4ef0218</code></a>
Merge pull request <a
href="https://redirect.github.com/mermaid-js/mermaid/issues/8358">#8358</a>
from mermaid-js/release/12.0.1</li>
<li><a
href="https://github.com/mermaid-js/mermaid/commit/f45c6913ab771a72d22bb5b89b8d5d72cc3dbc1d"><code>f45c691</code></a>
Fix error from build docs</li>
<li><a
href="https://github.com/mermaid-js/mermaid/commit/d9cb282b6faf2ee33005cf0f5724bd3e848ff7d4"><code>d9cb282</code></a>
Fix label overlap bug</li>
<li><a
href="https://github.com/mermaid-js/mermaid/commit/47fffa05c044f460f8b9645c7a24429ed200a084"><code>47fffa0</code></a>
Merge pull request <a
href="https://redirect.github.com/mermaid-js/mermaid/issues/8297">#8297</a>
from pentaoa/fix/eventmodeling-duplicate-frame-ids</li>
<li><a
href="https://github.com/mermaid-js/mermaid/commit/12c280e7cb7ff3effa7285b54390761d5eb0a583"><code>12c280e</code></a>
Merge develop and preserve eventmodeling regression tests</li>
<li><a
href="https://github.com/mermaid-js/mermaid/commit/c0f7052343a8015aabf5dd28a62cee6facc6956c"><code>c0f7052</code></a>
Merge pull request <a
href="https://redirect.github.com/mermaid-js/mermaid/issues/8296">#8296</a>
from pentaoa/fix/eventmodeling-reset-explicit-relations</li>
<li><a
href="https://github.com/mermaid-js/mermaid/commit/4f6ac96ac95fd0ee0f66c986985680eb98dce1ec"><code>4f6ac96</code></a>
Merge pull request <a
href="https://redirect.github.com/mermaid-js/mermaid/issues/8345">#8345</a>
from mermaid-js/bug/8283_xychart-legend-clip-title-h...</li>
<li><a
href="https://github.com/mermaid-js/mermaid/commit/679093a791bfd2b3e851b41db2742640391aa58a"><code>679093a</code></a>
Merge pull request <a
href="https://redirect.github.com/mermaid-js/mermaid/issues/8344">#8344</a>
from mermaid-js/pebr/issue-8292-elk-label</li>
<li>Additional commits viewable in <a
href="https://github.com/mermaid-js/mermaid/compare/mermaid@11.17.2...mermaid@12.1.0">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=mermaid&package-manager=npm_and_yarn&previous-version=11.17.2&new-version=12.1.0)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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>
2026-10-14 19:45:58 -07:00
DottaandPaperclip 3dffd2197d test: consolidate owner fixtures and retain real regression proof (#16047)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Tests protect sandbox execution, run recovery, credentials and the
board UI.
> - Several owner suites repeat the same setup and prove the same result
more than once.
> - These copies add maintenance work and can hide weak assertions.
> - Each cut needs a retained test that proves every distinct contract.
> - This pull request consolidates repeated setup and removes twelve
redundant executions.
> - It also replaces two weak assertions with checks of real outputs.
> - The benefit is 2,831 fewer test lines with clearer regression proof.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The maintenance and regression strength of the existing Vitest owner
suites.

**Subsystem affected**

Sandbox adapter utilities, server heartbeat recovery and environment
capabilities, and the board UI.

**Current behavior**

Tests copy database rows, query builders, render calls and runtime
inputs. Some cases repeat an existing contract. One token test omits a
real bridge credential. A PostHog host comparison can compare absent
controls.

**Proposed behavior**

Use small local helpers with fresh values and the same action order.
Keep each distinct contract in an owner test. Check real bridge
credentials and fixed PostHog method states. Carry the exact persisted
first-run success assertion into the stronger cancellation keeper.

**Reason and benefit**

The ten test files shrink by 2,831 net lines. Twelve redundant
executions retire. All other case tables, inputs, security boundaries,
timeouts and cleanup rules remain.

**Breaking changes**

None. This round changes tests and audit evidence only.

**Additional context**

Refs: #15978, the merged first cleanup. Refs: #16022, the merged prior
cleanup. Its squash merge is `dabb8c5daf430a1987af611e2e7ca4c7b23ef7d4`.

Related open work: #14634 repairs feedback fixture completion; #14477
supplies explicit sandbox fixture tools; #14198 changes cleanup retry
behavior in another heartbeat file. This audit preserves those separate
action and cleanup contracts. It does not apply their changes.

This PR was first stacked on `codex/test-audit-followup`. After #16022
was squash merged, only the two Round 6 commits were replayed onto
merged master `dabb8c5daf430a1987af611e2e7ca4c7b23ef7d4`. The original
stack head is preserved in a local snapshot branch. The current diff
contains only ten test files and two evidence files. No merge is
requested.

## What Changed

- Consolidate repeated setup in three sandbox adapter suites. Remove
seven redundant executions and retain the real protocol, cleanup and
scratch-file checks. Save 875 lines.
- Consolidate heartbeat database fixtures and read queries. Retain exact
wake cancellation, total run count and the actual first-run success
receipt. Remove one duplicate execution. Save 1,372 lines.
- Remove two repeated rejected-capability cases. Carry the absent
snapshot assertion into the retained runner case. Save 22 lines.
- Consolidate UI fixtures and render calls. Remove two repeated
connection cases. Replace the weak PostHog comparison with fixed method
and required-key checks in both real hosts. Save 562 lines.
- Record audit scope, decision and expansion counts, all twelve keeper
mappings and deliberate mutation results in the [audit
report](https://github.com/paperclipai/paperclip/blob/codex/test-audit-round-six-delivery/doc/plans/2026-10-12-test-audit-round6.md)
and [controls
evidence](https://github.com/paperclipai/paperclip/blob/codex/test-audit-round-six-delivery/doc/plans/2026-10-12-test-audit-round6-controls.json).

## Verification

Source baseline: `a090035cafb29c06f6fca2dbe43561ad866b7c1b`. All ten
original test programs are byte-identical at the stacked base
`58c5c4aa241c85f4665a3159c192888bbb2a5cfa`.

- Baseline: 1,841/1,841 cases passed in the main thirty-file
owner/sibling run. Another 219/219 cases passed in nine extra files.
- Preservation: all four proposal packages passed independent review.
Whole-program helper expansion retains ordered inputs, callbacks,
awaits, queries, fresh allocations and hooks outside the declared
assertion migrations and repairs.
- Candidate: all 2,048 cases passed across 39 complete owner/sibling
files. The main run passed 1,831 cases. The extra run passed 217 cases.
Both runs had zero failures and zero skips.
- PostHog control: the repaired positive case passed. The original weak
case passed with authentication choices removed. The repaired case
failed at its fixed OAuth aria-checked assertion under the same
mutation.
- Eight accepted mutations failed at their intended assertions. They
cover bridge-token exclusion, fallback cleanup, READY bytes, real
scratch-file preservation, exact wake cancellation, the persisted
first-run success receipt, rejected capability snapshots and reachable
PostHog method controls. Two original weak cases passed with the same
defects. All production source bytes were restored.
- Rejected control: changing run status before legacy release timed out
before wake cancellation. This result is not counted as assertion proof.
Its replacement changed only the persisted receipt after real drain in
the existing transaction. It failed at the direct fixed succeeded-row
assertion.
- No new code comments or 1,000-line threshold crossings were
introduced. No production, schema, lockfile or workflow changes are
included.

On the exact final test sources, later committed as
`d8de9422f0703d4508a9dc880b67a07f13e0f110`, `pnpm -r typecheck`, `pnpm
check:token-gates` and `pnpm build` passed. The required full `pnpm
test:run` completed with **exit 1** after **52.14 minutes** on clean,
immutable `d8de9422f0703d4508a9dc880b67a07f13e0f110`. HEAD and tracked
sources remained unchanged.

The first general-server group ran 889 files: **882 passed, one failed
and six skipped**. Its 18,032 cases were **17,925 passed, two failed and
105 skipped**. The only failing file was
`server/src/__tests__/remove-test-home.test.ts`. On macOS, the read-only
runtime bundle and symlink cleanup cases returned `ENOTEMPTY`. The
permission-error case expects `EACCES` or `EPERM`. Four reported failure
entries cover these two cases, including their cleanup errors. The test
and helper are byte-identical to previous master
`4b766b872993045a2e787404f0475503683e5ded` and audit master
`e364b92c82`.

The default wrapper stopped after that group failed. **Later local
groups did not run.** The earlier public-MCP, HTTP log redaction and
workspace snapshot failures did not recur. The 39 focused owner/sibling
files and all 48 remote CI jobs completed independently. This result is
an unresolved macOS cleanup limitation, not full local success. No test,
skip, retry, timeout or cleanup rule was changed to hide it.

The three adapter files received a whitespace tidy after focused
execution. Full runtime AST and string/template/regex byte equality link
those tested sources to the final sources. All 134 adapter helper calls
were checked again. Replayed code commit
`3d3defd2a60595a623045d9e2c6c586f4acb3452` has the exact final test
bytes. Evidence commit `e868e6645ce0d598d756d3458f0b3d71afd6db4a` adds
the report and controls JSON, with one historical ancestry
clarification. All ten accepted test hashes and all seven
production-owner source guards match. No package, lockfile, workspace,
TypeScript, Rust or Vitest dependency manifest changed since the audit
snapshot.

The [original stack CI
workflow](https://github.com/paperclipai/paperclip/actions/runs/38312876910)
passed all 48 jobs on head `c171d6c0d603d22bb997c1f9a9bdcd5bcc5d22aa`.
Those results are historical stack proof.

The [fresh master-context CI
workflow](https://github.com/paperclipai/paperclip/actions/runs/38318223856)
completed successfully with **all 48 jobs passed** on current head
`e868e6645ce0d598d756d3458f0b3d71afd6db4a`. Its tested merge commit was
`41292af60c6eb60d477637d23822bb17b08dc07e`, combining workflow base
`88bf4c6c1d222f818faa56257efe79207c892a6c` with that head. The delivery
commits remain based on pinned master
`dabb8c5daf430a1987af611e2e7ca4c7b23ef7d4`. Both required master checks,
`ci / verify` and `ci / e2e`, passed. Two separate optional Storybook
checks were skipped by their scope gate.

[Fresh Greptile
review](https://github.com/paperclipai/paperclip/pull/16047#issuecomment-6133323165)
gives the current head **5/5**, with no findings, inline comments or
unresolved review threads. The [current-head commitperclip
review](https://github.com/paperclipai/paperclip/actions/runs/38318223666)
also completed successfully. This PR remains open and draft; no merge is
requested. The local macOS failure above remains unresolved.

## Risks

- A helper could share a value or capture the wrong binding. Independent
expansion and lexical binding checks guard the actual helper calls.
- A retired case could contain a distinct assertion. Each cut has an
exact keeper map and carried observations.
- A mutation could fail before the intended assertion. Controls reject
timeouts and earlier failures. The rejected receipt mutation remains in
the evidence.
- The delivery ancestry changed after the parent squash merge. Exact
source hashes preserve the audited tests and producer receipts. Fresh CI
and review passed in the current master context; the broad local run
remains evidence from its immutable audit snapshot.
- The complete local default failed in two unchanged macOS cleanup
cases. Later local groups were not run. This limitation remains visible
even though focused checks and remote CI passed.

## Model Used

OpenAI Codex, based on GPT-6, assisted with this work. The exact served
model ID and context window are not exposed in this session. It used
repository tools, code execution, AST analysis and parallel audit
agents.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 21:43:12 -05:00
cafa25029c fix(native-runtime): recover unclaimed remote startup (#16349)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The native runtime preserves task execution across server restarts.
> - Remote workspace preparation can finish before the executor claims
its first provider attempt.
> - The recovery classifier required a provider checkpoint even when the
durable coordinator proved that execution had never started.
> - A deployment during this interval therefore left safe startup work
on a permanent recovery hold.
> - This pull request admits that narrow startup state to the existing
same-run bootstrap path. Existing execution and ownership checks remain
in force.
> - The original preparation yields when recovery owns the coordinator.
It cannot fail or clean up the recovered run.
> - Tasks can continue after this interruption without an operator retry
or a duplicate provider turn.

## Linked Issues or Issue Description

**What happened?**

A server deployment interrupted a prepared remote workspace before the
native executor's first claim. The coordinator had attempt zero,
controller generation zero, and no owner, process, provider event, or
checkpoint. Restart classification returned
`ambiguous_remote_recovery_evidence` and blocked the task.

**Expected behavior**

The server should recognize this durable never-claimed state and
continue the original run through normal execution admission. A prior
claim, uncertain ownership, or contradictory execution evidence must
retain the recovery hold.

**Steps to reproduce**

1. Prepare a remote workspace for a native run.
2. Stop the controller before the executor commits its first claim.
3. Restart the controller and classify the running native task.
4. Observe that the old classifier blocks it because no provider
checkpoint exists.

Related: #16341 preserves remote recovery failure reasons. #16344
strengthens remote restart qualification. Neither changes admission for
an untouched remote startup.

## What Changed

- Require a server-owned fencing version written only with fresh native
identities. Older unmarked preparations stay held during rolling
upgrades; caller profile data cannot add that marker to an existing run.
- Leave the current server's active preparation with its existing
execution.
- Yield through the controller-detachment path when a different
execution owns the coordinator lease. Lease expiry does not grant
takeover authority.
- Preserve run-scoped gateway tokens during detachment so stale
preparation cannot revoke the recovered worker's tools.
- Admit a valid prepared remote workspace only when the locked
coordinator proves attempt and controller generation zero, no owner, and
no prior session or execution evidence.
- Check launch receipts and provider source events before granting
bootstrap recovery.
- Keep the original run and session. Preserve atomic recovery claims,
executor durable-state checks, and normal admission gates.
- Add real PostgreSQL regression tests for competing controllers and
seven contradictory evidence cases.
- Update recovery documentation, the run-log reason, and both capability
maps.

## Verification

- The competing-controller regressions failed before the ownership fix
and pass for both live and expired foreign leases.
- On the final revision, 77 runtime-selection, restart recovery, real
PostgreSQL, and heartbeat boundary tests pass with no skips, including
all local runner process fixtures. The full native executor file passes
all 698 tests.
- Another 91 live-run route tests pass locally. An earlier CI timeout in
that unrelated file passed on retry; the complete final-head CI passed
without that failure.
- `pnpm -r typecheck`, `pnpm build`, `node scripts/check-maps.mjs`, and
`git diff --check`: passed locally. The map check verifies structure
only.
- Local `pnpm test:run` did not finish: an early attempt had
build-related collection failures, and later full-suite execution was
stopped after complete CI passed. Targeted suites passed after build
preparation. This is not a local full-suite pass.
- The complete [PR
workflow](https://github.com/paperclipai/paperclip/actions/runs/38669996667)
passed on `2baffb3652bfeb4705a30ca3706fbda6c3d72aa4`, including all test
shards, build/typecheck, runner verification, and browser E2E. All 55
applicable review/security/CI checks succeeded; two Storybook checks
were skipped.
- [Greptile
Apex](https://github.com/paperclipai/paperclip/pull/16349#issuecomment-6180848957):
5/5 on the same final head, with no unresolved findings.
- No live remote provider restart is claimed by the database regression.

## Risks

- A bare executor can no longer take a foreign coordinator lease after
expiry. A verified recovery claim must transfer authority first.
- Existing preparations without the server-owned fencing marker retain
their recovery hold during rolling upgrades.
- Incorrect admission could duplicate provider work. This path requires
a positive durable never-claimed receipt, rejects contradictory launch
evidence, and keeps the executor's existing durable-root verification.
- Previously claimed or incomplete remote recovery evidence remains
blocked. This change does not clear historical terminal holds.
- No schema, API, dependency, or UI changes.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, tool use, code execution,
and browser inspection. The exact serving snapshot 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>
Co-authored-by: Devin Foley <devin@paperclip.ing>
2026-10-14 19:32:39 -07:00
DottaandPaperclip ecc0ab8174 test(e2e): require remote restart identity and copyback receipts (#16344)
Require original remote run identity, sandbox lease continuity, native receipt continuity and successful workspace finalization in the Codex Daytona controller-restart cell.

Add positive and negative calibrations, including immediate rejection when the original run ends before fault injection. All current-head CI checks pass and Greptile is 5/5. Live Daytona qualification remains separate.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 21:24:22 -05:00
DottaandPaperclip ff0f976de1 fix(native-runtime): retain remote recovery failure reasons (#16341)
Preserve closed marker-read and process-verification reasons through native recovery failures and operator reports. Keep existing transport errors and reattachment ownership checks.

Add shell, process-probe, persistence and privacy regression coverage. All current-head CI checks pass and Greptile is 5/5.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 21:24:02 -05:00
3935e300dd fix(acpx): bound Claude MCP tool calls when runtime MCP servers attach (#16360)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Hosted `claude_local` runs use the ACPX engine. Paperclip gives each
run its runtime MCP servers (for example the `paperclip-assigned` tool
gateway) as ACP `mcpServers` entries.
> - Claude's default MCP tool timeout is far longer than a run. ACP
`mcpServers` entries have no per-server timeout field. So the agent
waits on an MCP call until the call answers.
> - Hosted error reports show runs that end with "Run exceeded the
adapter execution timeout (sandbox)". The stop-time diagnostics from
#15962 show the problem: the agent went silent about 50 seconds into the
run, three tool calls of ACP kind `other` stayed pending, and nothing
ended them until the 4h sandbox backstop.
> - The gateway bounds the remote provider call (10s default, 60s max).
But other steps on the gateway `tools/call` path have no deadline: DB
queries, row locks and pool acquisition, the credential steps before the
provider timer starts, and the stdio child exit wait. One stuck step
holds the agent turn for hours.
> - This pull request gives the agent's MCP client a deadline:
`MCP_TOOL_TIMEOUT` for ACP Claude runs that receive runtime MCP servers.
> - The benefit is that a stalled gateway call becomes an MCP error that
the model can see. The turn continues and does not spend the full 4h
execution budget.

## Linked Issues or Issue Description

- Refs #15962 (diagnostics that identified the silent turn with pending
tools)
- Refs #15448, #15456, #15850 (related gateway timeout and
connection-health work; this PR does not overlap with them)

**What happened?**

A hosted `claude_local` run sent parallel MCP tool calls. No call
answered. The run stayed silent for about 4 hours. Then the sandbox
execution timeout ended it with "Run exceeded the adapter execution
timeout (sandbox)".

**Expected behavior**

A stalled MCP call ends with an error after a bounded time. The agent
sees the error and continues.

**Steps to reproduce**

1. Start an ACP `claude_local` run that has runtime MCP servers.
2. Make the gateway `tools/call` request stall (for example, hold a lock
that the request needs).
3. See that the run stays silent until the adapter execution timeout.

**Paperclip version or commit**

`master` at 976cdc7.

## What Changed

- Add `ACPX_CLAUDE_MCP_TOOL_TIMEOUT_MS` (15 minutes) in
`acpx-engine/constants.ts`. The value has wide headroom over the
gateway's longest legitimate wait (60s provider calls, 2 minutes of
approval preparation, and the 10-minute connection cap proposed in
#15850).
- The ACPX engine sets `MCP_TOOL_TIMEOUT` in the agent env for Claude
when the run has runtime MCP servers. If the adapter `env` already sets
`MCP_TOOL_TIMEOUT`, the engine keeps that value. Other agents and runs
without MCP servers do not change.
- Add an engine test for the bound, the no-server case, the user
override, and a non-Claude agent.
- Document the client-side deadline in `doc/MCP-RUNTIME-OPERATIONS.md`.

## Verification

- `npx vitest run src/acpx-engine/` in `packages/adapter-utils`: 21
files, 423 tests pass.
- The new test fails without the env assignment (`expected undefined to
be '900000'`), and passes with it.
- `npx tsc --noEmit -p packages/adapter-utils`: passes.
- The env reaches Claude through the same per-session env path as
`ANTHROPIC_MODEL`. `claude-agent-acp` merges this env into the Claude
SDK env.

## Risks

- A legitimate MCP call that takes longer than 15 minutes now fails. No
gateway path is designed to wait that long. Operators can set
`MCP_TOOL_TIMEOUT` in the adapter `env` to change the value.
- The value also applies to other MCP servers in the same Claude
session, for example servers in user settings. The same override is
available.
- Warm sessions get the env when the agent process starts again. The env
is not part of the session fingerprint, so this change does not reset
resumable sessions.
- This PR does not fix the server-side step that stalls. It bounds the
effect on the run. Server-side deadlines on the gateway `tools/call`
path (DB lock and statement timeouts, a request-level deadline, SIGKILL
escalation for stdio children) are possible follow-ups.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`) through Claude Code, with tool use
and code execution. It read the repository, traced the gateway path,
edited the code, and ran the tests.

## Checklist

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

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

Co-authored-by: devinfoley <devinfoley@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-14 19:01:42 -07:00
1b58ac6ec9 build(deps): bump smol-toml from 1.8.0 to 1.9.1 (#16020)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Codex runner must set trust for the exact startup project.
> - smol-toml 1.9.1 returns tables with null prototypes.
> - Object spread replaces those prototypes and causes strict validation
to reject valid trust edits.
> - This PR keeps the parsed tables and adds tests for the dependency
update.
> - Codex can start while unrelated settings and trust boundaries stay
intact.

## Linked Issues or Issue Description

Refs #16028. That merged PR contains the same table-preservation fix and
tests for both prototype styles.
This branch retains that work and adds more regression cases.
Related dependency updates: #15293 and #15364.

**What happened?**

Eight startup trust tests fail after the update to smol-toml 1.9.1.
The error is `codex_startup_trust_cannot_preserve_configuration`.

**Expected behavior**

Set trust for the exact startup project.
Keep unrelated configuration bytes and sibling project trust unchanged.

**Steps to reproduce**

1. Check out the original PR head,
`90b133dfd963af4f19b4df11fdd6c11b4eeb2a2d`.
2. Install dependencies with `pnpm install --frozen-lockfile`.
3. In `packages/paperclip-runner`, run `pnpm exec vitest run
src/drivers/codex/codex-startup-trust.test.ts`.

**Paperclip version or commit**

The verified head is `1a8e8ad8b2e1049ebb68595d85c92e3c0621559d`.

**Deployment mode**

Built from source on Linux. The affected code runs before Codex starts.

## What Changed

- Keep Dependabot's update from smol-toml 1.8.0 to 1.9.1 in the runner
and server.
- Keep parsed project tables. Use `parse("")` to create missing tables
with the parser's prototype.
- Keep strict candidate validation and the upstream tests for both
prototype styles.
- Add tests for nested values, dates, times, special numbers,
prototype-named keys, sibling trust, and repeated calls.
- Preserve the original Dependabot commit and lockfile. The two
follow-up commits change only the trust source and tests.

## Verification

- All 28 startup trust cases pass at the verified head. Run the command
above.
- The focused strict TypeScript check passes. Use `tsc --ignoreConfig
--noEmit --module nodenext --moduleResolution nodenext --target es2023
--types node --esModuleInterop --strict --skipLibCheck
src/drivers/codex/codex-startup-trust.ts
src/drivers/codex/codex-startup-trust.test.ts`.
- The runner TypeScript build passes. The full local build stops because
the test workspace has no Cargo executable.
- The broader Codex suite passed 271 of 272 cases before the source
conflict resolution. One unrelated fixture expects the runtime's
workspace path to be absent.
- The repository type check exited 137 in the DB package. The full local
test command was stopped after resource exhaustion.
- The final files merge cleanly with master
`976cdc77ed564dc3e014621505890b647e554a7f`. `git diff --check` passes.
- [CI for the verified
head](https://github.com/paperclipai/paperclip/actions/runs/38664681165)
passed. All 55 active checks passed; two Storybook checks were skipped.
Snyk also passed. [Greptile cycle
3](https://github.com/paperclipai/paperclip/pull/16020#issuecomment-6182103775)
scored this head 5/5 with no actionable findings and no open review
threads.

## Risks

- This update changes the parser used by the runner and server. CI must
check both packages.
- The trust edit must preserve unrelated TOML values and bytes.
Regression tests cover this behavior.
- Local resource and tool limits prevent a complete local validation
result. The full remote CI run passed at the verified head.

## Model Used

OpenAI GPT-6 through Codex helped with the compatibility fix, tests, and
review.
The runtime does not expose the exact model snapshot or context window
size.
The agent used repository tools, code execution, and GitHub tools.
Dependabot produced the original dependency and lockfile update.

## 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 — the focused 28-case suite
and strict type check pass; broader limits are listed above
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes — this
PR records the compatibility behavior and verification
- [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

---

## Original Dependabot Update

Bumps [smol-toml](https://github.com/squirrelchat/smol-toml) from 1.8.0
to 1.9.1.
<details>
<summary>Release notes</summary>
<p><em>Sourced from <a
href="https://github.com/squirrelchat/smol-toml/releases">smol-toml's
releases</a>.</em></p>
<blockquote>
<h2>v1.9.1</h2>
<h2>What's Changed</h2>
<ul>
<li>fix: reject invalid extra exponent sign by <a
href="https://github.com/cyyynthia"><code>@cyyynthia</code></a></li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/squirrelchat/smol-toml/compare/v1.9.0...v1.9.1">https://github.com/squirrelchat/smol-toml/compare/v1.9.0...v1.9.1</a></p>
<h2>v1.9.0</h2>
<p><strong>Huge update</strong>!!! This is most likely the largest
update the library received since its release, with lots of new features
and improvements.</p>
<h3>Performance improvements</h3>
<p>Significant parts of the internal parse logic have been rewritten,
improving performance by <strong>1.5x-2x</strong>. The library was
already comfortably ahead of the others, but it is now faster than ever,
sitting at <strong>4x faster parse performance</strong> than the closest
maintained implementation.</p>
<p>Problematic code paths have also been replaced by safer
implementations, solving potential DoS vectors. See
GHSA-r4xh-jqrq-34v2.</p>
<p>Note: the objects returned by the library now have a <em>null
prototype</em>. This is a transparent change for 99.9% of users, and is
one of the most significant contributors to the major performance gains
in this version.</p>
<h3>Full Temporal support</h3>
<p>Version 1.8.0 brought support for Temporal in <code>stringify</code>;
now the library is also able to emit Temporal objects instead of its own
ad-hoc <code>TomlDate</code> object. It is not enabled by default, but
it will become the default in v2. Enable by setting <code>useLegacyDate:
false</code> in the parser's options.</p>
<h4>Better Temporal support in stringify</h4>
<p>Temporal support has been improved since it released: Temporal
objects that cannot be represented (such as
<code>Temporal.PlainMonthDay</code>) now throw an error (instead of
silently emitting a bogus object).</p>
<p>A new option has been added to <code>stringify</code> to disallow
Temporal objects that cannot be fully represented in TOML. This includes
<code>ZonedDateTime</code> objects with a IANA timezone attached instead
of a plain offset, and dates with a specific calendar value set. Enable
by setting <code>strictTemporal: true</code> in the options.</p>
<h3>Handling of unsafe keys</h3>
<p>Since its release the library has been protected against prototype
pollution attacks, setting properties like <code>__proto__</code> using
safe mechanisms that do not trigger prototype pollution. However, while
the returned objects are safe on their own, they may become problematic
if used carelessly.</p>
<p>Inspired by <code>secure-json-parse</code>, the library now offers a
way to either drop unsafe properties from the returned object, or to
throw an error and reject documents altogether. By default, these
potentially unsafe keys are preserved and returned.</p>
<h3>Miscellaneous updates</h3>
<ul>
<li>Unicode BOM is now gracefully accepted and ignored.</li>
<li>Table array headers are now properly checked again. Reported in <a
href="https://redirect.github.com/squirrelchat/smol-toml/issues/65">#65</a>.</li>
<li>Closed certain gaps where invalid whitespace would be accepted.
Reported in <a
href="https://redirect.github.com/squirrelchat/smol-toml/issues/61">#61</a>.</li>
<li>Bogus local date and local time values with a UTC offset are no
longer accepted.</li>
<li>Certain error messages are more accurate and handle errors at line
boundaries better.</li>
<li>The default export of the lib is now formally deprecated; use a
<code>import *</code> instead. Proposed in <a
href="https://redirect.github.com/squirrelchat/smol-toml/issues/50">#50</a>.</li>
<li>On Node 20+, strings that contain lone surrogates are now normalised
to well-formed strings.</li>
<li>On Node 20+, keys that contain lone surrogates are now
rejected.</li>
</ul>
<p><strong>Full Changelog</strong>: <a
href="https://github.com/squirrelchat/smol-toml/compare/v1.8.0...v1.9.0">https://github.com/squirrelchat/smol-toml/compare/v1.8.0...v1.9.0</a></p>
</blockquote>
</details>
<details>
<summary>Commits</summary>
<ul>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/69aa050fef018b71799519bb2cbb54f158a0c5f9"><code>69aa050</code></a>
fix: actually properly fix exponent issues</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/7be7084d24c85909cec84ecaafe39737ef4e4472"><code>7be7084</code></a>
chore: version bump</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/be3290da3309502447e020ce106bb8a11d99e580"><code>be3290d</code></a>
chore: upgrade deps</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/86c76134ad7a50e8e101ef7fd82cb0f155b278d8"><code>86c7613</code></a>
fix: reject invalid extra exponent sign</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/6f9739a5ab481f57c3112024bc4947534b580b58"><code>6f9739a</code></a>
fix: gate <code>[is|to]WellFormed</code> (Node 18 compat)</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/a73ca32c7c9237f744c880d900ad15307a0cef56"><code>a73ca32</code></a>
fix: no Temporal with toml-test when Node &lt; 26</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/7727890604580bd3fc2d7d2f79ad6e67b745ebe7"><code>7727890</code></a>
chore: version bump</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/641903d60d47900080602b6a83e919d2a526547f"><code>641903d</code></a>
chore: rewrite README.md</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/2df14c57945fec5a3737bc0cdce06a436bf9a77e"><code>2df14c5</code></a>
fix(types): make it work if Temporal doesn't exist</li>
<li><a
href="https://github.com/squirrelchat/smol-toml/commit/3eaa44ef54b859990969935664b12023b26d0eae"><code>3eaa44e</code></a>
chore: update benchmark harness</li>
<li>Additional commits viewable in <a
href="https://github.com/squirrelchat/smol-toml/compare/v1.8.0...v1.9.1">compare
view</a></li>
</ul>
</details>
<br />


[![Dependabot compatibility
score](https://dependabot-badges.githubapp.com/badges/compatibility_score?dependency-name=smol-toml&package-manager=npm_and_yarn&previous-version=1.8.0&new-version=1.9.1)](https://docs.github.com/en/github/managing-security-vulnerabilities/about-dependabot-security-updates#about-compatibility-scores)

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>
Co-authored-by: Devin Foley <devin@devinfoley.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 18:46:09 -07:00
Devin FoleyandClaude Opus 5.5 5e8956c207 fix(heartbeat): retry legacy runs a restart stopped before dispatch (#16355)
A hard server restart can stop a legacy run after it is claimed but before the adapter is dispatched. The next boot reaped it as failed/process_lost with no retry and held it for operator reconciliation, so the task wake was lost.

The legacy controller only writes executionStage "dispatching" in the lease renewal that admits the adapter. The orphan reaper now revokes the expired foreign-boot controller first, and uses the stage and process identifiers returned by that same UPDATE ... RETURNING. If the run is still "preparing" with no process identifiers, it records bootstrap evidence (providerWorkStarted: false) and uses the existing one-time process-loss retry. In "unexpected" mode, run-failure reporting does not send this retried restart interruption to Sentry. Runs that reached dispatching, native runs, and runs owned by the current boot are unchanged.

Verified exact head 91f1352097: full CI green, Greptile 5/5, no unresolved review threads, no conflicts. The tests were not run locally (sandbox memory limit); CI is the test run.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
canary/v2026.1015.0-canary.3
2026-10-14 18:27:11 -07:00
Devin FoleyandPaperclip 6fae6f2cb8 fix(wake-queue): preserve pending work when a queued run is stopped (#16357)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The wake queue releases task locks and can promote saved follow-up
work when a run ends.
> - An operator can stop a queued run before dispatch selects its
runtime.
> - The legacy release path can promote the old follow-up after that
Stop.
> - For an obsolete routine cycle, this can conflict with the current
active cycle after cancellation has already committed.
> - This pull request preserves the deferred input without promoting it
at that release boundary.
> - The benefit is that a queued Stop stays stopped and does not return
an error from starting an obsolete follow-up.

## Linked Issues or Issue Description

Related: Refs #14300 and Refs #16339 describe other pre-dispatch
cancellation paths. Related PR #16238 changes former-owner wake
selection. This change addresses expected operator cancellation before
legacy runtime selection, without changing those contracts.

**What happened?**

Stopping an unstarted legacy run committed its cancellation, then tried
to promote a deferred wake. When another cycle of the same routine was
already active, the issue lock update failed against
`issues_open_routine_execution_uq`. The cancel request returned HTTP 500
even though the run was cancelled. Without a competing cycle, the Stop
launched the old follow-up.

**What did you expect to happen?**

Release the stopped run's own locks and preserve deferred input for a
later eligible instruction. Do not start the old queue as a side effect
of Stop.

**Steps to reproduce**

Create two cycles of the same routine. Keep the current cycle active.
Give the old cycle an unstarted legacy run with a verified bootstrap
record and a deferred wake. Cancel that run as a board operator, then
release its issue execution. The added database regression reproduces
the failed lock update before the fix.

**Paperclip version / commit**

Reproduced on parent commit `976cdc77ed564dc3e014621505890b647e554a7f`.

**Deployment mode**

A remote runtime exposed the symptom. The regression uses embedded
PostgreSQL and the real wake-queue adapter.

## What Changed

- Recognize an expected operator Stop only when the legacy run has no
runtime-selection time, start time, process-start time, or process
identity.
- Use the existing release boundary to preserve deferred input without
promoting it.
- Add database regressions for ordinary Stop, a competing routine cycle,
repeated release, provider cancellation, runtime selection, and
dispatched-run reconciliation.
- Document the behavior and update both recovery maps.

## Verification

- Before the fix, both queued Stop cases failed. The competing-cycle
case raised `issues_open_routine_execution_uq`. The three control cases
passed.
- `node node_modules/vitest/vitest.mjs run --project @paperclipai/server
server/src/modules/wake-queue`: 7 files and 214 tests passed, including
all five new database cases.
- `node scripts/check-maps.mjs` and `git diff --check` passed.
- `pnpm -r typecheck`, `pnpm build`, and all 10 map-script tests passed
locally. The complete CI suite is green on
`8e204eef87f1a593df5c183abef764e4b9eb5fd1`. The remaining serial local
full-suite run was stopped once that unchanged-head CI verification
passed; a local full-suite pass is not claimed. Apex review passed at
5/5 on the same head with no review threads.

## Risks

- The change is limited to recorded operator cancellation before legacy
runtime selection. Selected runtimes, dispatched work, provider
cancellation, and native stop acknowledgement keep their existing paths.
- Deferred work is retained. A later eligible instruction must still use
normal admission and all current gates.
- No schema migration or new authority is required. This regression is
not proof of a live provider stop.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, tool use, and code
execution. The exact serving snapshot 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-10-14 18:18:26 -07:00