Commit Graph
5206 Commits
Author SHA1 Message Date
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
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
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
976cdc77ed fix(recovery): expose settled holds and their preserved decisions (#16169)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task recovery preserves work when execution or tool outcomes are
uncertain.
> - Automatic recovery can settle its record while keeping replay
blocked.
> - The recovery-actions API omitted those records unless they held
workspace files.
> - Operators and agent tools could therefore see an empty recovery
history for a task that still had an execution hold.
> - This pull request lists those holds and shows their matching
decision in the existing stopped-run notice.
> - Existing stop verification, continuation, and file repair gates
remain in force.

## Linked Issues or Issue Description

**What happened?**

A cancelled or failed task can retain a settled recovery record with
`automaticRecovery.replay: blocked`. The task remains held, but the
recovery-actions API returns no action. The board notice also lacks that
record's decision.

**Expected behavior**

List settled execution holds that still block replay. Let an authorized
board user inspect the current action and run in Recovery details.
Preserve the existing guarded continuation and required-file repair
paths.

**Steps to reproduce**

1. Stop a run whose outcome requires inspection.
2. Let automatic recovery settle its record with replay blocked.
3. Read the task's recovery actions and open its stopped-run notice.
4. Before this change, the action list is empty unless workspace
recovery is required.

**Paperclip version or commit**

This PR includes current master through `66424362a85`.

**Deployment mode**

Authenticated and local deployments with persisted execution recovery
holds.

The original API correction and route regression are by Tao Xie
(@TaoXieSZ). This update preserves those commits and adds the board
inspection path, current documentation, and regression coverage.

Related: #14840 describes controller lease loss. This change exposes
recovery records; it does not change controller lease renewal.

## What Changed

- Include settled execution reconciliation holds with replay blocked in
the company-scoped recovery-actions response.
- Read the matching action from the existing stopped-run notice. Show
its decision under Recovery details, including when active recovery is
empty.
- Reject mismatched historical records in the notice. Bind history to
the current action/run and refresh visible decisions every five seconds.
Keep history read errors visible beside the current guidance.
- Preserve existing run inspection, new-message continuation, and
required-file repair controls. No new reconciliation form is added.
- Update the execution documentation and both recovery capability maps.

## Verification

- Recovery route suite with real embedded PostgreSQL: 80 passed, none
skipped.
- Notice and retry-client suites: 36 passed.
- `pnpm -r typecheck`: passed.
- `pnpm build`: passed.
- `pnpm check:token-gates`: passed.
- `node scripts/check-maps.mjs`: passed. This checks structure only.
- `git diff --check`: passed.
- Full `pnpm test:run` was started. It is not yet a full local pass.
Initial database tests were affected by missing packaged macOS library
aliases; the aliases were restored in this worktree's dependencies, and
the complete recovery route suite then passed.
- All CI checks passed on `09e4461e4d3a0960a4dc8bc270a2a10c628bf524`,
including the full test matrix, typecheck, build and browser checks.
Optional Storybook jobs were skipped.
- Apex: 5/5 on the same head. Both review threads are resolved; the
reviewer confirmed the existing guarded continuation design.

## Risks

- The API response now includes settled records that still block replay.
Consumers must distinguish a settled record from permission to continue.
- Recovery history is supplemental. A failed history read keeps the
current blocker and guidance visible.
- The UI binds the decision to both the current action and run. It does
not certify tool outcomes or grant execution authority.
- No schema, dependency, telemetry, or runtime admission changes.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, tool use, and code
execution, assisted with the board inspection and verification
follow-up. The exact serving snapshot ID and context window size are not
exposed in this session. The original API work is retained from Tao
Xie's contribution.

## Checklist

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

---------

Co-authored-by: Devin Foley <devin@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
canary/v2026.1015.0-canary.2
2026-10-14 17:32:35 -07:00
Devin FoleyandPaperclip f467a239f0 chore: remove obsolete monitor visibility design assets (#16351)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The repository includes pages and wireframes used during product
design.
> - The old monitor visibility prototype is no longer needed.
> - Repository searches found no references to this folder outside the
folder itself.
> - This pull request removes the unused page, images, wireframes, and
publishing state.
> - The repository no longer carries these obsolete design assets.

## Linked Issues or Issue Description

**What happened?**

An obsolete monitor visibility prototype remains in the repository after
its design work ended.

**Expected behavior**

Remove the unused prototype and its supporting assets.

**Steps to reproduce**

Inspect the design directory. The unused monitor visibility page and
wireframes are still present.

**Paperclip version or commit**

Based on `66424362a85`.

**Deployment mode**

All deployment modes. This is repository cleanup.

## What Changed

- Remove eleven unused design files: the prototype page, publishing
state, two screenshots, and seven SVG wireframes.
- Preserve the agent's saved patch exactly. Its deletion patch applies
cleanly to the current base.

## Verification

- `git apply --check` passed for the saved patch.
- Repository search found no external references to the deleted folder.
- `git diff --check` passed.
- `node scripts/check-maps.mjs` passed. This is a structural check.
- `pnpm build` and `pnpm -r typecheck`: passed.
- `node --test scripts/check-maps.test.mjs`: passed.
- `pnpm test:run`: incomplete locally. The run encountered embedded
PostgreSQL startup failures and skipped affected database suites. This
is not a full local test pass.
- All CI checks passed on `fe340de5c052cb680f8803c106368a0439025103`,
including the full test matrix, build, and typecheck. Optional Storybook
jobs were skipped.
- Apex review: 5/5 on the same head, with no unresolved review threads.

## Risks

- Someone may still use the old design files outside this repository.
Git history preserves them.
- No application source, dependencies, API contracts, or deployment
settings change.

## 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. The patch was recovered from an earlier
Paperclip agent run.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above (none found)
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (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 map suite; full-suite
limits are described above)
- [x] I have added or updated tests where applicable (no test applies to
deletion of unused design assets)
- [x] I have updated relevant documentation to reflect my changes
(obsolete design documentation removed)
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

Co-authored-by: Paperclip <noreply@paperclip.ing>
canary/v2026.1015.0-canary.1
2026-10-14 16:59:19 -07:00
DottaandPaperclip 66424362a8 perf(runtime): skip discovery without a Browser Use connection (#16345)
Skip full tool discovery when no enabled, active Browser Use connection exists for the run company. Configured connections retain existing authorization and discovery.

Verified exact head 7fdf51eae8: full CI, E2E, Superagent and Apex 5/5; 29 affected lifecycle tests and full repository typecheck/build pass.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
canary/v2026.1015.0-canary.0
2026-10-14 18:18:00 -05:00
DottaandPaperclip c41e00bca4 fix(server): index workspace source task enumeration (#16342)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent startup checks access to every source retained by a workspace.
> - Repository admission repeats these checks while it holds a company
lock.
> - PR #16334 indexed the workspace privacy query. The next source-task
query still cast the task primary key to text.
> - That second query scanned company history for every workspace and
delayed other agent launches.
> - This pull request keeps the task column indexed and guards the
retained identifiers before conversion.
> - The benefit is faster startup with the same source validation and
access decisions.

## Linked Issues or Issue Description

Refs #16334.

**What happened?**

Agent launches still spent minutes preparing repositories after the
first authorization query fix. The source-task lookup scanned 14,191
task rows for one matching source.

**Expected behavior**

Source validation should use the task primary key and workspace-binding
index. It must still reject missing, malformed, noncanonical, and
foreign-company sources.

**Steps to reproduce**

1. Seed company task history and a workspace with attached and retained
source tasks.
2. Call `assertTaskWorkspaceAccess` and capture its source-task SQL.
3. Run `EXPLAIN (ANALYZE, FORMAT JSON)` on that query. The baseline
scans unrelated task history.

## What Changed

- Convert retained text identifiers with the existing canonical UUID
guard. Keep the indexed task column as a UUID.
- Exercise the real source-access service against PostgreSQL. Verify
complete source results, malformed and uppercase input behavior, and
rows examined by the plan.
- Document the second source-validation query and its regression
coverage.

## Verification

- Red: the new regression failed because the source lookup examined
1,202 rows for two sources.
- Green: four focused workspace suites passed, 147 tests total. The
final regression also checks malformed and uppercase query inputs.
- Live read-only comparison: two warmups, then five samples per variant
in alternating order. Median client query time changed from 4.88 ms to
0.35 ms. Complete result rows matched. Shared buffer hits changed from
3,984 to 5. These measurements cover this SQL statement, not total agent
startup.
- The complete source-access helper also passed for the same retained
reader in read-only transactions. Its median changed from 15.75 ms to
10.75 ms; repository admission still repeats other checks.
- `pnpm -r typecheck` and `pnpm build` passed locally. The staged Linux
build and all seven final regression cases passed on Node 24.20.0.
- All 55 current-head checks/statuses passed; two Storybook checks were
skipped. Greptile reviewed `6cfb755` at 5/5 with no findings.
- The full local test run encountered a tracing-test failure in
`native-session-executor.test.ts`. Both that test alone and the complete
file passed on rerun (698 tests). The remaining full-suite run is still
in progress.

## Risks

- This changes SQL planning on an authorization path. The canonical
guard preserves the former exact lowercase text match and avoids UUID
parse errors.
- Company filters, missing-source checks, project checks, and per-source
access decisions remain in place.
- No schema migration or new index is required. Repeated checks across
large repository histories remain a separate source of startup work.

## Model Used

- OpenAI Codex, GPT-6 family, with reasoning, shell tools, code
execution, and browser verification. The exact model ID and
context-window size are not exposed in this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` 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:15:04 -05:00
DottaandPaperclip 90a45ef254 perf(skills): skip inventory work for empty runtime selections (#16343)
Return before company skill inventory work for a known empty native or stored-key selection. Preserve nonempty stored-key precedence, current access/version checks and legacy automatic skills.

Verified head f3f89eedff: 88 affected tests, full typecheck/build, full CI/E2E, Superagent, legacy status and Apex 5/5; no unresolved findings.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
canary/v2026.1014.0-canary.17
2026-10-14 18:01:52 -05:00
Devin FoleyandPaperclip cda77edc60 perf(server): stop readiness waiting on startup housekeeping (#16329)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - On restart, `/api/health` reports `starting` until startup recovery
finishes, and deploy tooling waits for `ok`
> - After heartbeat recovery, readiness also waits for housekeeping that
every scheduler tick repeats and that serving requests does not need
> - #16330 added OpenTelemetry spans for the main startup phases; native
recovery and orphan reaping are the slow ones, and both run the native
finalization reconciliation pass, which has no inner breakdown
> - This pull request stops readiness waiting on that housekeeping, and
records the phases of slow reconciliation passes as spans
> - The benefit is faster readiness on every restart, and traces that
show where a slow reconciliation pass spends its time

## Linked Issues or Issue Description

Builds on #16330, which records `controller.startup.<phase>` spans.
Related to #16328.

**What existing behavior does this improve?**
Server readiness time after a restart, and diagnosis of slow native
recovery.

**Current behavior**
- After heartbeat recovery, readiness waits for the custom-image setup
cleanup, tool connection health (remote MCP checks), decision expiry,
the adapter and setup-token login reapers, environment lease cleanup
(provider calls) and the retention sweep. Each also runs on every
scheduler tick. On one instance they added 11–28 s to every measured
restart. A throw in any of them fails startup.
- On the same instance, native recovery took from 33 s to 11.5 min and
orphan reaping from 30 s to 4 min across restarts in one hour. Both run
the native finalization reconciliation pass. The #16330 spans time each
step as a whole, but not the phases inside that pass.

**Proposed behavior**
- The housekeeping steps run after readiness. The scheduler interval
still starts after them.
- With an OTLP endpoint, a reconciliation pass of at least one second
records a span with one child span per phase.

**Reason and benefit**
Readiness depends only on what serving requests and run recovery need.
Slow reconciliation passes become attributable to a phase in the
existing traces, without adding trace volume for the periodic pass.

**Breaking changes**
None. A failed housekeeping step no longer fails startup; it is logged
and the next step runs.

## What Changed

- `server/src/index.ts`: moves the housekeeping steps into
`runStartupHousekeeping`, which runs after readiness under
`trackHeartbeatSchedulerWork` (idle sleep and shutdown wait for it).
Each step catches and logs its own failure (`startup housekeeping step
failed`). The heartbeat scheduler interval starts when housekeeping
finishes, unless the scheduler was stopped, so the reapers still finish
before the first timer tick.
- New
`server/src/services/native-runtime/native-reconciliation-timing.ts`:
records the phase boundaries of one pass. When the pass took at least
one second and an OTLP endpoint is configured, it emits a
`controller.native_reconciliation` root span
(`paperclip.native-reconciliation`) with child spans
`legacy_workspace_exports`, `obsolete_policy_reviews`,
`automatic_completion_reviews`, `candidate_scan` and `candidates`, using
the recorded start and end times. It records `scoped` and the candidate
count only. A failed pass marks the pass span and the running phase.
Tracing failures are swallowed.
- `native-finalization-reconciler.ts`: the exported function wraps the
pass in that timing and finishes it in `finally`, so failed passes are
recorded; the original error is rethrown.
- `doc/execution-semantics.md`: what readiness waits for, and the
housekeeping order. `doc/observability.md`: the reconciliation spans.

## Verification

- `pnpm exec vitest run
src/__tests__/server-startup-feedback-export.test.ts
src/services/native-runtime/native-reconciliation-timing.test.ts
src/services/native-runtime/native-finalization-reconciler-timing.test.ts
src/startup-recovery-timing.test.ts` (in `server/`): 52 passed. New
tests:
- Readiness is `ready` while a housekeeping step hangs, and the
heartbeat interval starts only after the step settles.
- A failed housekeeping step is logged, and the scheduler still starts
after the remaining steps.
- Span timing: nothing for a fast pass or without an endpoint; a slow
pass emits the pass span and one child per phase with the recorded
times; a failed pass marks the running phase and the pass; a failing
tracer does not throw.
- The reconciler records its phases and a failed outcome when a pass
throws, and rethrows.
- Three existing startup tests settle housekeeping before they read the
scheduler interval or the setup cleanup.
- `pnpm exec vitest run src/services/native-runtime`: 4,399 passed.
- `pnpm typecheck` in `server/`: passed.

## Risks

- Housekeeping now runs concurrently with the first requests and with
`waitForExternalAdapters`. None of these steps is needed to serve
requests, and each one also runs on every scheduler tick.
- Readiness is reported earlier; the heartbeat timer starts at the same
point relative to housekeeping as before.
- The new spans exist only with an OTLP endpoint and only for passes of
at least one second.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), 1M context, through 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
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 15:55:13 -07:00
DottaandPaperclip aa04c5c2a7 fix(runtime): keep the copyback lock transaction active (#16340)
Keep the dedicated workspace-copyback transaction active with bounded SELECT 1 probes while filesystem work runs. Preserve existing connection, owner-token, takeover and publication fences.

Verified head 7fb0ad45bf: full CI, E2E, Superagent, legacy statuses and Apex 5/5; no unresolved review findings. Real PostgreSQL idle-timeout reproduction and 71 affected cases pass. Strengthened disabled-timeout controls fail when their guard is removed.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 17:52:26 -05:00
DottaandPaperclip cc96c9f832 fix(server): probe libc asynchronously before serving requests (#16335)
Linux runner qualification generated a full Node diagnostic report to read one libc version field. Reverse DNS on open sockets could stop the event loop for minutes.

Replace report generation with a one-time asynchronous getconf probe during module initialization. Bound it to one second and 1 KiB of output, cache the measured facts, and preserve unknown-libc failure behavior.

Verification: 149 focused tests, repository typecheck and build, full CI, and fresh Greptile 5/5 with no comments. A timed-out local heartbeat fixture passed when rerun alone.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 17:46:50 -05:00
Devin FoleyandPaperclip 35b3345033 fix(server): support issue monitor deadlines in idle sleep reports (#16325)
## Thinking Path

> - Paperclip manages work for companies and AI agents.
> - Hosted workspaces can sleep when their required work has a safe wake
path.
> - The current idle check treats every open issue as background work.
> - Backlog, unassigned and human-waiting records can therefore keep an
idle process running.
> - Future automated monitors still need their saved check time and
execution claims.
> - This PR adds a signed host capability for passive issues and
verified monitor deadlines.
> - The host can save one alarm for the earliest required routine or
monitor time.

## Linked Issues or Issue Description

**What happened?**

A quiet workspace with a backlog or unassigned todo issue reports
background work as present. A future issue monitor also prevents sleep
even when the host can save a durable wake alarm.

**Expected behavior**

Proven passive records can sleep. A validated future monitor can sleep
only after the host saves its alarm and validates the same owned report.
Active, unknown and unresolved work stays awake.

**Steps to reproduce**

1. Create a quiet workspace with an unassigned todo issue and no other
work.
2. Acquire a bounded, signed Cloud idle hold.
3. Request the routine-only safety report with `scheduledWake=1`. The
issue blocks sleep.
4. Request the new capability with `scheduledWake=2`. A passive issue
can now report no work.
5. Add a valid future monitor to an assigned in-progress issue. The
second capability returns its saved deadline for host acknowledgement.

**Paperclip version or commit**

Base: `c30fb7d2b75ea30f72d63656b2a7cfff7fe0929d`.

**Deployment mode**

Cloud-managed authenticated instance. Local scheduling remains in the
existing scheduler.

Related protocol:
[#16215](https://github.com/paperclipai/paperclip/pull/16215). Open
scheduler and monitor PRs were checked for overlap. This change leaves
monitor admission, dispatch and quota recovery with their existing
owners. The roadmap already includes scheduled work; this is a focused
idle-safety extension.

## What Changed

- Add signed scheduling capability `2`; capability `1` retains the
routine-only rules.
- Allow untouched onboarding prompts with idle assignees to sleep using
the same awaiting-user predicate as recovery; typing, answering the
opening card or a prior run removes the exemption.
- Inspect passive issue state and future monitors in the same bounded,
read-only snapshot as other work.
- Check ownership, company scope, known states, claims, policy/deadline
agreement, attempts, timeout and dependency repair.
- Combine routine and monitor deadlines into a known proof kind and
catalog generation.
- Preserve other sleep blockers, including task watchdogs,
conversations, live runs, external credentials and recovery.
- Update the protected route, OpenAPI contract, documentation and
PostgreSQL regression tests.

## Verification

- Full workspace typecheck and build passed. Final server typecheck and
full build passed after the final guards.
- Initial safety, scheduling, protected-route and control-authentication
checks: 326 tests passed.
- Final safety and heartbeat scheduling checks: 253 tests passed.
Includes reporting a future deadline, restoring scheduling from
PostgreSQL and one dispatch across competing schedulers.
- Local general server checks: 20,260 passed; UI: 8,460 passed; CLI: 534
passed, including isolated reruns of two embedded-PostgreSQL startup
failures. All remaining workspace groups and 158 serialized suites
passed. The suite was completed in groups after the isolated
infrastructure retries; the final onboarding change also passed the
focused 674-test recovery/safety run. Earlier unchanged Git fixtures
failed under Xcode's system `init.defaultBranch=main`, reproduced on the
clean base and passed with a test-scoped fixture default; no source or
machine configuration was changed.
- Rebased issue, protected-route and scheduler checks: 115 tests passed;
server typecheck passed.
- The expanded change passed 674 idle-safety, scheduling and recovery
regression tests, including six onboarding admission/invalidation cases.
Final server typecheck and build passed.
- Current-head Apex is 5/5 with no unresolved threads. All 54
current-head checks passed, with two expected skips. Four jobs were
interrupted by runner shutdowns or lost communication on their first
attempts; their targeted reruns passed. Live hosting qualification
awaits the verified master release and matching host capability.

## Risks

This intentionally covers a limited set of passive states and valid
future monitors. Assigned active tasks without a proved waiting path,
unknown states, claimed monitors and quota recovery still block sleep.
It does not make every open issue safe to sleep.

A matching host must recognize `routines_and_issue_monitors`,
acknowledge the durable alarm and revalidate the exact owner, generation
and deadline before stopping. The report alone grants no stop authority.
Older hosts retain capability `1`. Independent database writers remain
outside the admission contract. Existing scheduler claims and execution
authority are unchanged. No schema migration is required.

Roll back host negotiation to capability `1` to restore conservative
admission, while retaining alarm delivery until outstanding alarms have
been handled.

## Model Used

- OpenAI GPT-6 (Codex), with repository editing, command execution and
GitHub tools. The exact deployment ID and context-window metadata 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 15:39:46 -07:00
Devin FoleyandPaperclip aec80cbc8c fix(server): keep workspace compaction out of the startup sweep (#16328)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - When the server starts, `/api/health` answers "starting" until
startup recovery finishes, and deploy tooling waits for "ok"
> - Startup recovery waits for the first sweep of the terminal-workspace
worker
> - #16304 added workspace compaction to that sweep. Compaction walks
worktrees, runs Git, and deletes dependency trees, and it re-checks
every eligible workspace after a restart
> - Compaction is never needed to serve requests, so it should not delay
readiness
> - This pull request skips compaction in the startup sweep only; later
sweeps compact as before
> - The benefit is that a compaction backlog cannot make a restart
slower

## Linked Issues or Issue Description

Follow-up to #16304.

**What existing behavior does this improve?**
Server startup time after #16304.

**Current behavior**
`await terminalWorkspaceWorker.ready` blocks the readiness flip until
the first terminal-workspace sweep ends. That sweep compacts every
eligible workspace in its page. The per-cycle compaction memo is in
memory, so after each restart every eligible workspace is walked and
checked with Git again, and any directories found are deleted before the
sweep ends.

**Proposed behavior**
The first sweep skips compaction. Pending compactions keep their
deadlines, so the worker wakes and a later sweep compacts them.

**Reason and benefit**
Readiness depends only on work that is needed to serve requests. On one
measured instance the first sweep took 5–8 s with compaction; with a
larger backlog, compaction could add seconds per workspace.

**Breaking changes**
None. Compaction happens one sweep later after a restart.

## What Changed

- `sweepTerminalWorkspaces(limit, signal, { compact })`: `compact:
false` skips compaction for that sweep and keeps each pending compaction
deadline.
- `registerTerminalWorkspaceWork` passes `compact: false` to its first
sweep (the one readiness waits for) and `compact: true` after that.
- `server/src/index.ts` forwards the option through the logging wrapper.
- `doc/DEVELOPING.md`: the compaction section says the startup sweep
never compacts.

## Verification

- `pnpm exec vitest run src/__tests__/terminal-workspace-work.test.ts
src/__tests__/execution-workspace-compaction.test.ts
src/__tests__/workspace-compaction.test.ts` (in `server/`): 35 passed.
- New: the worker passes `compact: false` only to the startup sweep and
`compact: true` to the next one.
- New: a sweep with `compact: false` leaves the files in place, the
compaction deadline stays due, and the next sweep compacts.
- `pnpm typecheck` in `server/`: passed.

## Risks

- Low risk. After a restart, compaction runs a few seconds later than
before. Archive behavior does not change.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), 1M context, through 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
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 15:27:28 -07:00
DottaandPaperclip 538bbd5478 fix(ui): configure Grok Bot through its external connection (#16338)
## Thinking Path

Paperclip coordinates an external Grok Bot through its authenticated
agent connection and routine. The shared configuration form only
excluded OpenAI Dot from local runtime controls, so Grok Bot inherited a
model picker, local paths, an AI account picker, and a generic runtime
test. These controls cannot configure the external Bot and the generic
test is not evidence of its webhook-to-MCP connection. Extend the
existing external-provider presentation gates and retain the dedicated
connection verification workflow.

Related implementation: #16219 and #16305. Searched open Grok PRs and
issues; the remaining matches concern the separate coding runtime or
unrelated coverage.

## Linked Issues or Issue Description

**What happened?**
An existing Grok Bot agent's Runtime page shows local model and runtime
configuration alongside its actual external connection controls.

**Expected behavior**
Identify the harness as Grok Bot, show its connection and routine
controls, and use Verify connection for authenticated two-way readiness.
Omit inapplicable local-runtime controls and model catalog queries.

**Steps to reproduce**
Create a Grok Bot through Invite external agent, then open its Runtime
settings. Before this correction, the shared form renders a model
selector and Test your agent despite the Bot owning its runtime.

**Paperclip version/commit**
Master after #16219; reproduced against the shared form at
8ffc5748a2.

## What Changed

- Treat Grok Bot and Dot as external providers when resolving local
configuration and model catalog queries.
- Identify Grok Bot directly in the harness field and keep its dedicated
connection, routine, and verification controls.
- Hide the generic runtime test for Grok Bot, including compact feedback
and externally registered test actions.
- Add regressions for compact and inline configuration and production
ConfigurationTab stories for connected, disconnected, and mobile states.
- Isolate generated documentation previews in separate iframes; verified
connected and disconnected states remain distinct across polling.
- Document the Runtime management path.

## Verification

- New regression failed in both variants before the correction; the four
focused UI suites pass: 131 tests, including existing native-provider
configuration coverage.
- Repository typecheck, production build, and token gates pass.
Storybook builds successfully.
- Used the built production ConfigurationTab in the browser: connected
health, credential replacement fields, disconnected reconnect, and a
375-pixel mobile layout with no horizontal overflow. These are synthetic
connection fixtures, not live-provider qualification.
- A full local database-backed suite is unavailable because this Mac has
reached its SysV shared-memory limit. The earlier diagnostic fixture
confirmed that limitation; no unrelated databases were stopped. Full
repository CI passes on 60119f1ce1: 55
checks successful and two intentional skips.

- Fresh Greptile review is 5/5 on the final head, with the
preview-isolation finding addressed and its thread resolved.

## Risks

Low risk: shared form presentation and unnecessary model queries change;
no authorization, secret storage, Runner execution, schema, or transport
behavior changes. Existing stored configuration remains intact.
Native-provider form regressions run in the focused suite; full
repository CI passes on the final head.

## Model Used

OpenAI Codex, based on GPT-6, with repository inspection, code editing,
shell tests, and browser verification. The host does not expose a more
specific serving-model ID or context-window size.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 17:23:18 -05:00
DottaandPaperclip ebceb2a744 trace(server): time workspace, native setup and startup recovery (#16330)
## Thinking Path

> - Paperclip manages agents and their work.
> - The controller prepares workspaces before it starts a Runner.
> - A slow run can spend 30–80 seconds inside the combined workspace phase.
> - Startup recovery can also delay queued work for minutes.
> - Native runs also have gaps before Runner startup and after the provider result.
> - Existing traces do not identify the awaited steps inside those intervals.
> - This change adds closed timing boundaries around the existing operations.
> - Operators can locate the wait without changing execution or exposing content.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Operator-configured OpenTelemetry diagnosis of controller preparation and startup recovery. Related: #16178, #16236 and #16285 introduced the existing preparation timing and sampling contract. #16306 and #16312 reduce separate configuration and copyback work.

**Current behavior**

The workspace interval combines source resolution, trust checks, provisioning, database persistence, binding and repository admission. Startup awaits accounting and native recovery before queued work resumes, but neither has a separate duration. Native backend setup and post-turn cleanup also contain unmeasured awaits. CPU profiles and a fast database probe cannot identify which await is slow.

**Proposed behavior**

Record workspace steps and native controller setup and cleanup with the existing hashed run ID. Record startup phases as independent, operator-sampled root spans without custom attributes. Keep the same operation order, results, failures and authorization.

**Reason and benefit**

These boundaries distinguish source work, database writes, repository admission and restart recovery. This is a diagnosis change. It does not claim a response-time improvement or a solved latency budget.

## What Changed

- Add 14 closed workspace step names around 16 existing awaits. Repeated updates and bindings produce separate spans.
- Time startup tool reviews, gateway sessions, delivery-worker readiness, accounting, plugin readiness, native recovery, hot restart, orphan reaping, retries, queued work and goal recovery.
- Time native GitHub setup, wake binding, backend construction, connector assignments, MCP discovery, tool authority, companion selection and binary resolution. Time selected post-turn GitHub, coordinator and instruction cleanup awaits.
- Move preparation timing to a pure shared module. Keep the existing heartbeat import as an alias. Both heartbeat and native timing skip tracer lookup when the endpoint is unset or blank, including when another library registered a provider.
- Tracing lookup, callback, status and end faults cannot repeat work or replace its value or error.
- Add real PostgreSQL workspace timing coverage and silent no-op/fault controls. Update the tracing contract and paired run maps.
- Keep span attributes closed. Startup spans add none; workspace steps use only the existing run hash. No commands, paths, returned records or exception messages are recorded.

## Verification

- Diagnostic-gap baseline: the new real workspace test fails against the unchanged workspace implementation because the expected step spans are absent. It passes with the wrappers.
- Final affected coverage: **804 distinct cases pass** across seven complete suites: 698 native-executor cases, 39 real PostgreSQL workspace cases, 61 timing/context/startup cases, and six execution-configuration integration cases. The final native and unit invocations pass with no selected skips. The workspace suite passed in the initial six-file invocation on the same final workspace source.
- Preserve the initial six-file attempt as failed: **7 failed and 789 passed**. All seven errors occurred during embedded PostgreSQL startup. The log reports local macOS shared-memory exhaustion. A fresh 15-case database group passes, followed by the complete 698-case native suite. These runs overlap; do not add their counts.
- The new native timing assertion fails against unchanged native code because the expected spans are absent. It passes with the wrappers and retains the credential-isolation assertions.
- Full `pnpm -r typecheck` and `pnpm build`: passed on the final production source. Earlier nullable-closure errors are fixed with erased assertions inside existing guards. The failed attempts remain recorded.
- Normalized AST comparison: removing only timing wrappers, timing imports/local handles and erased assertions restores the original workspace, startup and native executor programs. This checks call order and policy expressions; it is not live acceptance.
- The expanded head first failed two configuration integration assertions because their test providers had no endpoint. Configure the endpoint explicitly in those two tracing tests, and restore it after each test. All six cases then pass with the original credential cleanup, redaction and exact span assertions. Production is unchanged by this repair.
- The first PR head failed one unchanged attachment test at its 15-second deadline in CI. The complete 32-case attachment suite passes locally with that deadline. Do not label the failed CI attempt green. The expanded head needs fresh full CI.
- `node scripts/check-maps.mjs`: passed, 35 areas and 176 capability IDs. `node scripts/storybook-catalog.mjs`: 283 files and 2,728 stories. Structural checks only; no browser, play or Storybook build claim.
- `git diff --check`: passed. No added prose comments, lockfile changes or new 1,000-line crossings.
- A duplicate full local test run was not started. Fresh exact-head full CI and E2E pass on `15a683bb2419960f611584f8c07cdcfc9ce3af0b`. All 56 check runs are successful or optional skipped. Superagent and legacy Snyk pass. Completed Apex review is 5/5 on this same head, with zero unresolved review threads.

Commands:

```sh
pnpm exec vitest run --project @paperclipai/server --maxWorkers=1 --fileParallelism=false server/src/services/native-runtime/native-session-executor.test.ts server/src/services/execution-preparation-timing.test.ts server/src/startup-recovery-timing.test.ts server/src/services/heartbeat/preparation-timing.test.ts server/src/services/heartbeat/preparation-timing-context.test.ts server/src/services/heartbeat/workspace-preparation.test.ts
pnpm -r typecheck
pnpm build
node scripts/check-maps.mjs
node scripts/storybook-catalog.mjs
```

## Risks

- Configured operators receive more spans. They still follow the configured sampler and exporter.
- Startup spans measure the awaited phase, including remaining readiness waits. They do not measure earlier worker construction or a provider turn.
- Workspace and native setup intervals overlap their containing phases. Do not add them to the parent duration. Native timing covers named awaits, not every statement or every error path.
- These spans are not yet deployed or tested against a live latency run. No first-party Telemetry event, run-log write, schema or API changes are included.

## Model Used

OpenAI GPT-6 through Codex, with tool use and code execution. The exact served variant and context-window size are not exposed to this task.

## Checklist

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

Co-Authored-By: Paperclip <noreply@paperclip.ing>
canary/v2026.1014.0-canary.16
2026-10-14 17:16:07 -05:00
DottaandPaperclip 445feaa7aa fix(server): index retained workspace task authorization (#16334)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Before an agent starts, repository admission checks access to
retained workspace files.
> - Admission holds a company provenance lock while it checks each
source and reader.
> - The retained-task query cast the indexed task ID to text. PostgreSQL
could evaluate privacy across company history before it matched one
source ID.
> - Repeated slow checks kept the lock for hours and blocked other agent
launches.
> - This change looks up the canonical source UUID through the existing
primary key before it checks privacy.
> - The query does less work and keeps the same access decisions.

## Linked Issues or Issue Description

**What happened?**

Nine runs appeared active but had no provider process or log file.
Read-only database inspection found a transaction holding the company
repository-provenance lock for more than ten hours. Eight other
transactions waited for that lock. The owner repeatedly ran workspace
authorization queries. The company had about 13,400 tasks and 8,500
workspaces.

**Expected behavior**

A retained-source check should find the referenced task before it
evaluates that task's privacy. Unrelated task history should not
multiply the privacy work for every source and reader.

**Steps to reproduce**

1. Run the new `workspace-authorization-query.test.ts` against
authorization code from `5c7a025032`.
2. The real PostgreSQL fixture has 1,200 unrelated tasks and one
retained source. The regression fails with 2,402 recursive privacy
traversals, above the bound of 100. The other five access tests pass.
3. Apply this change and run the same test. All six tests pass.

Related: Refs #16330 adds workspace timing evidence. This PR fixes a
measured SQL plan. It does not change those tracing boundaries.

## What Changed

- Compare the UUID task column with a guarded canonical UUID from the
retained-source JSON key.
- Reuse `canonicalExecutionUuid`. Invalid and noncanonical keys still
match nothing and cannot cause a UUID parse error.
- Add real PostgreSQL coverage for query work, missing and foreign
sources, malformed and uppercase IDs, both agent and sponsor grants, and
grant revocation.
- Add a reproducible baseline comparison and document its scope in
`doc/DATABASE.md`.

## Verification

- Red: the unchanged query fails the new work-bound regression. Five
access cases pass.
- Green: 101 tests pass across the new query suite, workspace binding,
and the production privacy review suite.
- Full `pnpm -r typecheck` and `pnpm build`: pass. Dependencies use the
repository's pinned pnpm 9.15.4.
- All current-head CI checks pass, including the full server, chat,
workspace, serialized, Runner, and eight E2E test lanes. Build,
typecheck, canary, and security checks pass. Head:
`ad907f6e14c2d773fa8c20692e5ccc10215c9297`.
- Greptile: 5/5 on that head, with no actionable findings or unresolved
review threads. The branch is mergeable.
- The local full `pnpm test:run` was started, then stopped after a
concurrent disposable-database check hit the macOS shared-memory limit.
This is not counted as a local full-suite pass. The benchmark passed
when rerun alone; the complete suite passed in CI.
- `git diff --check`: pass. The branch has no lockfile or workflow
changes.

Synthetic SQL comparison on PostgreSQL 18.1, with 13,429 tasks and one
retained source:

| Query | Median | Five measured samples | Recursive privacy traversals
|
| --- | --- | --- | --- |
| Baseline `5c7a025032` | 42.57 ms | 42.57, 47.71, 44.51, 41.29, 41.44
ms | 26,860 |
| Fixed | 2.37 ms | 2.76, 2.35, 2.37, 2.11, 3.33 ms | 4 |

Both versions return identical complete rows. The benchmark alternates
variants after two warmup rounds. It loads the baseline authorization
module with current dependencies; the UUID helper is unchanged. The
measured gain is about 18x for this SQL fixture.

The final committed benchmark also passed a second isolated run: 55.44
ms baseline and 3.26 ms fixed, with identical complete rows (about 17x).

Two read-only queries on the affected database also returned identical
complete rows. One changed from 129 ms to 7 ms; a workspace with 49
retained sources changed from 576 ms to 11 ms. These are single paired
measurements, not endpoint or startup benchmarks.

Reproduce:

```sh
pnpm exec vitest run server/src/__tests__/workspace-authorization-query.test.ts server/src/__tests__/task-workspace-binding.test.ts server/src/__tests__/privacy-production-review.test.ts
pnpm --filter @paperclipai/server exec tsx ../scripts/benchmark-workspace-authorization.ts --baseline-ref=5c7a025032
pnpm -r typecheck
pnpm test:run
pnpm build
```

## Risks

- A direct UUID cast could broaden or break access for malformed keys.
The existing canonical guard and regression cases preserve the former
text-match behavior.
- Planner choices can differ across PostgreSQL versions and data sets.
The regression bounds real privacy work rather than elapsed time or one
exact plan shape.
- Admission still checks every retained source and reader under its
existing lock. This removes a measured query cost; it does not claim a
bound for the full admission process.
- No schema migration, permission change, lock removal, or live
deployment is included. Recovery of the affected runs remains unverified
until rollout.

## Model Used

OpenAI GPT-6 through Codex, with tool use and code execution. The exact
served model variant and context-window size are not exposed to this
task.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` 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 16:55:34 -05:00
Devin FoleyandPaperclip 8ffc5748a2 fix(server): stop libc detection from reverse-resolving every socket (#16333)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - #15683 made new Codex agents default to Paperclip Runner on Linux,
which needs the host's glibc version
> - `localAgentRunnerTarget()` reads that version from
`process.report.getReport()`
> - Unless `excludeNetwork` is set, Node's diagnostic report resolves
the endpoints of every open socket with synchronous reverse DNS on the
main thread
> - `GET /adapters` calls it once per adapter on every request, so on a
host whose proxy address has no fast PTR answer, the whole server stops
responding
> - This pull request excludes network data from the report and detects
libc once per process
> - The benefit is that runner selection costs a few milliseconds once,
instead of blocking the server on every request

## Linked Issues or Issue Description

Regression from #15683.

**What happened?**
After #15683, a server running behind a reverse proxy stopped
responding. Requests, database replies, log output and health checks all
stalled for minutes. The Node main thread was idle on CPU but blocked in
a synchronous DNS read: reverse (PTR) lookups of the proxy's address,
each waiting for the resolver timeout.

**Expected behavior**
Detecting the C library must not block the event loop on DNS, and must
not repeat on every request.

**Steps to reproduce**
1. Run the server on Linux where the reverse DNS lookup of a connected
peer's address is slow (for example, a proxy address whose PTR lookup
times out).
2. Open the board, which polls `GET /adapters`.
3. The server stops answering. Each `getReport()` call reverse-resolves
every open socket synchronously.

Measured on Node 24.21 with one open TCP connection to such a peer:
`process.report.getReport()` took **9,774 ms** with network data and **4
ms** with `excludeNetwork`. A busy server has hundreds of open sockets.

**Paperclip version/commit**
`master` after #15683.

## What Changed

- `server/src/lib/agent-runner-platform.ts`:
- Generates the report with `process.report.excludeNetwork = true` and
restores the previous value in `finally`.
- Detects libc once per process and caches the result, because the C
library cannot change while the process runs. A failed report also
caches "unknown", so it is never retried per request.
  - Adds `resetLocalAgentRunnerTargetCacheForTests()`.
- Reads `excludeNetwork` through a narrow typed view, because the
installed Node type definitions do not declare it yet.
- New `server/src/__tests__/agent-runner-platform.test.ts`.
- `agent-runner-selection.test.ts` and
`dev-runner-native-binary.test.ts` reset the cache between cases,
because they change the mocked glibc version per case.

## Verification

- `pnpm exec vitest run src/__tests__/agent-runner-platform.test.ts
src/services/agent-runner-selection.test.ts
src/__tests__/dev-runner-native-binary.test.ts
src/services/local-ai-browser-login.test.ts` (in `server/`): 165 passed.
New tests:
- The report is generated with `excludeNetwork` set, and the previous
setting is restored.
  - Five calls generate one report.
- A failing report gives `libc: "unknown"` once and restores the
setting.
  - No report is generated off Linux.
- `pnpm typecheck` in `server/`: passed.
- Measured on Node 24.21 (above): 9,774 ms → 4 ms for one report with
one slow-PTR socket open.

## Risks

- Low risk. The detected glibc version is the same; only the network
section of the report is skipped, and nothing reads it.
- The cached result does not change until restart. The C library cannot
change while the process runs.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), 1M context, through Claude Code
with tool use (shell, file edits, test runs, live diagnosis of the
stalled process).

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 16:48:05 -05:00
082423000e fix(claude-local): retain probe budget for unlimited runs (#15329)
## Thinking Path

> - Paperclip starts Claude agents through the local adapter.
> - The CLI engine checks its version and effort flag before a run.
> - An unset run timeout resolves to zero and means unlimited.
> - The probe code turns that zero into a one-second timeout.
> - A valid CLI that starts slowly then fails the version gate.
> - This pull request gives unlimited runs the existing 20-second probe
cap.
> - The probe can confirm a valid installed CLI before the run starts.

## Linked Issues or Issue Description

Fixes: #15258.
Related PRs #13927 and #14954 change version/model gates. This change
fixes the timeout for the probes used by those gates.

## What Changed

- Use a 20-second process timeout for version and effort probes when the
run timeout is zero or negative.
- Keep the existing cap and one-second floor for positive timeouts.
- Add real slow-CLI regressions.

## Verification

- A real fixture CLI prints its version and help after 1.5 seconds. The
unlimited-timeout case fails on the original source and passes after the
fix.
- An explicit one-second timeout still stops both probes.
- The complete Claude adapter source suite passes: 346 tests, 1 skipped.
The command excludes generated `dist/` tests, as the repository runner
does.
- Full workspace typecheck and build pass in the verification checkout.
- The full `pnpm test:run` attempt was stopped after it reported the
unchanged rapid-Slack ordering assertion. A clean checkout at `f858207`
reproduces it (3 comments, expected 8). Native V8 coverage shows that
the functions changed by these three fixes are not called in that
control. Additional broad checks reported timeouts and assertions in
unmodified files; those extra failures were not all reproduced as
clean-base controls; this is not a claim that the full repository suite
is green.
- Repository-wide typecheck and build were run with three independent
fixes together. This PR includes only the Claude probe change. The
affected source suites listed above pass.

## Risks

Unlimited run settings now use a 20-second process timeout for
capability probes. A probe for a CLI that hangs can therefore fail
later. The real executable fixture runs on POSIX hosts and skips
Windows; no live Claude account or remote provider was used.

## Model Used

OpenAI Codex, a GPT-6-based coding agent, with reasoning, repository
inspection, editing and local test execution. This session did not
expose the exact deployed model ID or context-window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] 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

CI and Greptile must finish on the updated head after removal of the
documentation change.

---------

Co-authored-by: Codex <noreply@openai.com>
Co-authored-by: Andrew Aymeloglu <aaymeloglu@gmail.com>
canary/v2026.1014.0-canary.15
2026-10-14 16:24:41 -05:00
DottaandPaperclip 5c7a025032 fix(ui): clear consumed messages from the pending queue (#16317)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task conversations keep follow-up messages pending while an agent
works.
> - The screen combines optimistic sends, queue snapshots, and run input
records.
> - A late send response can restore a local queue flag after a run
takes the message.
> - A stale queue snapshot can then keep the same message in pending.
> - This pull request matches the input record to its source queue and
clears stale pending flags.
> - The benefit is that consumed messages leave pending while unsent
messages stay visible.

## Linked Issues or Issue Description

**What happened?**

A follow-up can remain in the task composer's pending row after a run
takes it as input. Local queue state can outlive the send request. The
old projection cleared that state for steering, but not for ordinary
run-start input. A stale queue read can also retain the consumed entry.

**Expected behavior**

A message that a started run takes as input appears once in history and
leaves pending. Other pending messages remain available. A message
assigned to a run that has not started remains pending.

**Steps to reproduce**

1. Send a follow-up while a task has a live run.
2. Let a started run record that message as input before or after the
send response arrives.
3. Let a queue read still include the consumed message.
4. Observe the message remain pending on the base revision.

**Paperclip version or commit**

Reproduced against master at `33d24e48ad` with controlled request
ordering.

**Deployment mode**

Source checkout. Browser verification used the production task screen
with a simulated native runner and delayed API responses. It did not run
a live model or change a deployed instance.

Related: [PR
#15030](https://github.com/paperclipai/paperclip/pull/15030) addresses
acknowledgement matching in the runner console. This change addresses
the task-detail composer and does not modify that console.

## What Changed

- Clear local pending flags when a started run records the comment as
input.
- Use live-run input IDs while the run-history query catches up.
- Match stale queue entries by their primary or coalesced source wake
ID, even when the snapshot targets the previous run or has no target.
- Retain coalesced source queue IDs in run context and include receipt
IDs in compact task run reads.
- Keep unstarted run inputs and authoritative retries pending. Cover
send ordering, same-run and successor delivery, queued snapshots,
coalesced inputs, and retry consumption.
- Add the delivery-race checks to the steering feature map.

## Verification

- Before the fix, the first four request-order regressions failed with
the consumed message still in pending. Coverage now includes 16
combinations of receipt source, send-response order, and stale snapshot
kind.
- Focused UI checks: 5 files and 233 tests passed. These cover task
detail, queue projection, optimistic comments, queued-message controls,
and the task-chat adapter.
- `pnpm -r typecheck` passed.
- Server receipt checks: 5 files and 139 tests passed with
`PAPERCLIP_TEST_POSTGRES_TEMPLATE=0`. These exercise real database
reads, coalesced heartbeat input, and polling routes. The default
cached-template path timed out during setup on this host; the
fresh-cluster path passed without longer timeouts.
- Token gates and `node scripts/check-maps.mjs` passed.
- Browser: sent a follow-up with a delayed response in the production
task screen. The consumed message moved into history. A second unsent
follow-up stayed queued. Reload preserved both states. The API and
runner were simulated.
- `pnpm build` passed.
- The full local repository test run was started, then stopped to
isolate database setup timeouts. All 55 GitHub CI checks passed on
`4ec6820cf1f89880b801fd5f938d72f54591c5e2`, including the full test
matrix. One serialized server shard passed on its single rerun after a
15-second route-test timeout; the same 97-test route suite also passed
locally.
- Greptile reviewed the final head at 5/5. All review threads are
resolved.

## Risks

- Queue display depends on matching comment IDs, source queue IDs, and a
recorded start time. Rows without start evidence stay pending. Newer
queues remain visible until their own input receipt arrives.
- Run context gains additive receipt metadata. No schema migration is
required. Dispatch order, stored queue state, company access, and
steering authority stay unchanged.
- Browser evidence covers controlled response ordering. It does not
establish deployment or live-provider behavior.

## Model Used

- OpenAI Codex, GPT-6, with reasoning, tool use, and code execution. The
exact serving model ID and context-window limit 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
#` 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 16:11:29 -05:00
b226336762 ci: skip full CI for release-notes-only pull requests (#16207)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Each beta release ends with a pull request that adds one release
notes file under `releases/`
> - PR CI runs the full build, test, and e2e matrix for that pull
request
> - No build, test, or e2e lane reads `releases/v*.md` or
`releases/beta/v*.md`
> - The full matrix takes about 30 minutes, and lost runners or flaky
e2e specs can make it run again
> - This pull request lets the existing CI scope gate skip the heavy
lanes when a pull request changes only release notes
> - The benefit is that release notes pull requests get a green result
in minutes, and code pull requests keep full CI

## Linked Issues or Issue Description

**Issue type**

CI efficiency enhancement.

**Where is the issue?**

The `Select stacked PR CI scope` step in
`.github/workflows/ci-routing.yml`. This step sets the `full_ci` output
for PR CI.

**What's wrong?**

The step sets `full_ci=false` only for middle pull requests in a stack.
A pull request that changes only one release notes markdown file runs
the full matrix. For example, #16053 changed one file in
`releases/beta/`. Its CI ran two times: on the first run, 8 shards lost
their runners, and on the second run, one Playwright spec flaked and
needed a rerun.

**Suggested fix**

Read the pull request diff from the GitHub compare API in the scope
step. When every changed path is release notes, set `full_ci=false`. The
callers already support this value. I found no open pull request or
issue for this change.

## What Changed

- `ci-routing.yml`: the scope step (renamed to `Select PR CI scope`)
also sets `full_ci=false` when every path in the diff matches
`releases/v*.md` or `releases/beta/v*.md`.
- The step reads the diff from `compare/<event base SHA>...<event head
SHA>`. The SHAs come from the event, so a later push cannot change the
decision for this run.
- For a renamed file, the step checks the old path and the new path.
- The step keeps full CI when the event is not `pull_request`, a SHA is
malformed, the API call fails, the diff is empty, or the file list has
300 entries. The compare API can truncate a list at 300 entries.
- The step does not change the stacked pull request rules. It checks the
paths only when the stack rules select full CI.
- `ci-routing.test.mjs`: the GitHub stub now returns `files` for compare
calls. 17 new cases cover release notes paths, mixed diffs, renames,
non-markdown files, nested folders, empty and truncated lists, API
failure, non-PR events, malformed SHAs, and stack interaction.
- `e2e-shard.test.mjs`: the test now uses the new step name.
- `doc/DEVELOPING.md`: added a short description of the CI scope rules.

## Verification

- `node --test .github/scripts/tests/ci-routing.test.mjs
scripts/__tests__/e2e-shard.test.mjs`: 73 pass, 0 fail.
- `node --test '.github/scripts/tests/*.test.mjs'`: all pass.
- I ran the jq filter on real compare API responses. #16053 (one file in
`releases/beta/`) returns `true`. #16082 (10 files) returns `false`.
- After merge: open a pull request that changes only a
`releases/beta/v*.md` file. The `Select trusted runner` job shows the
notice `release notes only; full_ci=false`. The heavy lanes show as
skipped. The `policy`, `verify`, and `e2e` checks pass.

Note: `pr.yml` calls `pr-trusted.yml@master`, and that workflow calls
`ci-routing.yml@master`. This pull request does not change its own CI
run. The change applies after merge.

## Risks

- Low risk. The skip uses the existing `full_ci=false` path. The
`verify` and `e2e` aggregate checks already accept that value, and the
`policy` job always runs.
- A future job that reads release notes files would not run for
notes-only pull requests. Today, only the release workflows read these
files, and those workflows do not run on pull requests.
- The step now uses `GH_TOKEN` to call one more read-only API endpoint.
The workflow still does not check out or run pull request code, and its
permissions do not change.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic. Agentic coding with
tool use (shell, file edits, GitHub CLI).

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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 <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
canary/v2026.1014.0-canary.14
2026-10-14 13:16:34 -07:00
Nicky LeachandPaperclip c30fb7d2b7 refactor(core): share Runner execution lifecycle (#16318)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Core starts Runners in local, SSH, and sandbox environments.
> - Coding runs, Dot runs, and setup probes repeat connection setup
code.
> - These paths must keep the same preparation, authentication, and
cleanup order.
> - This pull request gives them one execution lifecycle contract and
one server connection driver.
> - Core keeps the existing PRP control plane after connection.
> - The benefit is one place to maintain environment routing before
adding another execution provider.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Runner environment preparation and connection setup across local, SSH,
and sandbox execution.

**Current behavior**

The coding transport owns the environment hook type. Dot borrows that
type. The server repeats connection selection, registration, ingress
activation, and release in task execution and setup probes. Changes to
one path can miss another path.

**Proposed behavior**

Use a shared `RunnerExecutionDriver` contract. Route built-in task
execution and remote setup probes through
`createNativeRunnerExecutionDriver`. Keep provider session setup,
process supervision, PRP commands, and recovery logic on their existing
paths.

**Reason and benefit**

Environment code can prepare resources and provide a connection without
adding another PRP execution path. The same driver preserves local
loopback, remote outbound WSS, and authorized sandbox ingress.

**Breaking changes**

None intended. Existing flat SDK options remain compatible. This is an
in-process contract. It does not add a plugin wire contract or Launcher
APIs.

**Additional context**

Related: #15406 proposes plugin-provided Runner execution. This change
starts from `master` and does not depend on that PR. It extracts the
existing lifecycle before adding another provider. Related: #16125
covers remote environment support for external adapters; this PR does
not implement that feature.

## What Changed

- Extract and export `RunnerExecutionDriver` from the coding transport.
Use it for coding and Dot options.
- Share server connection negotiation across coding runs, Dot runs, and
remote setup probes.
- Preserve outbound validation before staging, capability checks before
ingress, process dispatch before ingress connection, and verified
process readiness.
- Preserve warm run registration, restart guards, checkpoint hooks, and
cleanup ownership.
- Add 13 focused driver tests and document the lifecycle boundary.

## Verification

- Targeted server execution and recovery tests: 728 passed, 35 skipped.
These cover `native-session-executor`, `native-ssh-command-runner`, the
new driver, warm restart transitions, and Runner restart recovery.
- Targeted setup and driver tests: 109 passed after the review fix.
Twelve driver tests overlap with the preceding count. The added test
verifies that failed CA staging cannot leave a PRP route behind.
- Runner connectivity tests: 11 passed.
- Runner package lifecycle tests: 287 passed across coding transport,
Dot, restart continuation, and settlement. These exercised the compiled
Rust Runner.
- `pnpm -r typecheck`: passed.
- `pnpm build`: passed.
- `git diff --check`: passed. Local secret-pattern scan: clear.
- The PRP transport runtime and remote process launcher remain
unchanged. A byte comparison against the base confirmed this.
- CI passed on `b10789dfce30a837ce890f44e352064c7a74366f`: all 55 active
checks are green, including the full test shards, Runner checks, browser
tests, typecheck, build, and aggregate gate. Greptile scored this commit
5/5 with no unresolved findings.
- An earlier broad local shard run was not green: 34,888 passed, 12
failed, and 4,125 skipped. Failures were ten timeouts and two Git
fixture assertions affected by local `init.defaultBranch=main`. Isolated
retry passed all six non-route timeout suites (427 tests), two route
suites, and both Git assertions with a test-process-only
`init.defaultBranch=master`. The agent live-run route retry still timed
out. No unrelated source changes were made. Further broad local runs
were stopped at the user's request.

Targeted commands:

```sh
cd server
node ../node_modules/vitest/vitest.mjs run src/services/native-runtime/native-session-executor.test.ts src/services/native-runtime/native-ssh-command-runner.test.ts src/services/native-runtime/native-runner-execution-driver.test.ts src/services/native-runtime/native-warm-transition-restart.integration.test.ts src/services/native-runtime/native-runner-restart-recovery.integration.test.ts --config vitest.config.ts
node ../node_modules/vitest/vitest.mjs run src/services/native-runtime/setup-readiness.test.ts src/services/native-runtime/native-runner-execution-driver.test.ts --config vitest.config.ts
cd ../packages/paperclip-runner
node ../../node_modules/vitest/vitest.mjs run src/live/runnerd-codex-transport.test.ts src/drivers/dot/runnerd-dot-driver.test.ts src/live/runnerd-restart-continuation.test.ts src/live/runnerd-codex-transport-settlement.test.ts --maxWorkers=2
cd ../..
node node_modules/vitest/vitest.mjs run packages/adapter-utils/src/runner-connectivity.test.ts
```

## Risks

- Startup ordering and connection cleanup are sensitive. Focused tests
cover local registration, outbound validation, CA mapping, ingress
ordering, release, and warm authority guards.
- No live SSH host or sandbox provider was used. Existing mocked remote
tests and real local Runner tests passed.
- No database, PRP wire protocol, or Launcher API changes are included.

## Model Used

OpenAI GPT-6 via Codex, with tool use and code execution. The served
model variant and context window were not exposed to this task.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
canary/v2026.1014.0-canary.13
2026-10-14 12:58:05 -07:00
DottaandPaperclip 82884443be perf(adapter-utils): skip host scans for unchanged restores (#16312)
Skip the host snapshot scan when an unchanged workspace return matches its baseline, while retaining writer locking, both authority hooks, and changed-export conflict handling.

Verified with 270 affected tests, final-head CI/E2E, Superagent, and Apex 5/5.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
canary/v2026.1014.0-canary.12
2026-10-14 12:54:31 -05:00
DottaandPaperclip 51b56706fb fix(grok): verify file completion and recover failed sessions (#16305)
## Thinking Path

> - Paperclip manages work and authority for AI agents.
> - Grok Bot runs outside Paperclip and uses authenticated upload grants
for files.
> - Native completion requires a verified file publication receipt.
> - The upload saved the file but did not create that receipt. Real Grok
work could not finish.
> - A failed run also left an assignment that the Stop action could not
fence.
> - This pull request connects verified uploads to normal completion and
repairs that recovery action.
> - The result preserves file integrity and current work-session
authority.

## Linked Issues or Issue Description

Refs: #16219, #16274.

**What happened?**

A real Grok Bot saved a task document and CSV. Semantic completion
rejected the CSV because its upload had no verified publication receipt.
After the managed run failed, Stop rejected its remaining assignment as
a changed session. The driver also discarded the remote monitor's known
failure reason.

**Expected behavior**

An authenticated upload of the exact requested bytes can satisfy normal
completion. Stop can fence the remaining assignment of the selected
failed run. An old Stop action cannot stop newer work. Known remote
lease and reconnect errors remain visible without raw diagnostics or
credentials.

**Steps to reproduce**

1. Connect an experimental Grok Bot agent and verify its routine.
2. Assign a task that requires a document and a file.
3. Upload the file through its accepted assignment and cite the
attachment in semantic completion.
4. Observe the missing publication receipt error on the previous
implementation.
5. For recovery, select Stop after that run fails but its assignment
remains active.

**Paperclip version or commit**

The live defects were observed after #16274. The current candidate
merges master `739f9e40a4` and is
`26c5b9601d1cb2b8f5bb7fcf730f019523763f5b`.

## What Changed

- Record a server-owned publication receipt in the verified upload
transaction. Bind it to the real native Grok run and exact stored file
metadata.
- Validate external publication against the actual upload and admitted
assignment. Keep existing native and person upload rules.
- Recover the committed receipt after a lost response. Reverify the same
bytes when an older upload lacks proof.
- Hold assignment authority through publication with
grant-before-assignment and task-before-run lock order. Disconnect does
not wait for remote file storage.
- Allow Stop to fence the exact lingering assignment of a terminal run.
Reject stale Stop calls for successor work.
- Preserve only known lease/reconnect failure codes from the remote
monitor. Do not expose raw stderr.
- Extend real Rust, overlapping document/upload and disconnect/upload,
file retry, completion and Stop regressions. Document setup, files and
recovery.

## Verification

- The real Rust Grok regression requests a file, checks an overlapping
native document save, concurrent retries and lost-response recovery,
rejects missing delivery evidence, then uses normal semantic completion
and finalization.
- Focused tests cover person MCP, native file handoff, completion, Stop
and both external driver providers.
- Local repository typecheck and production build pass on the current
candidate. The five focused protocol suites pass (264 tests); all 17
external Runner driver tests pass, with the driver unchanged since the
master merge.
- Token gates and Storybook build pass. Nine existing Grok fixture
journeys pass their browser interaction assertions; the static probe
separately records five fixture API routes returning 404. This is UI
fixture evidence only.
- Full repository coverage passed in CI. The duplicate local `pnpm
test:run` was interrupted after CI completed; its partial log is
retained and is not reported as a local full-suite pass. The earlier
aggregate invocation was interrupted for the second review fix.
- All CI gates pass on `26c5b9601d1cb2b8f5bb7fcf730f019523763f5b`: 55
successful checks and two skipped Storybook jobs (no UI diff). This
includes the complete sharded repository suites, native Runner, release
checks, build and security scans.
- Current-head Greptile is 5/5 with zero unresolved findings. The PR has
no merge conflicts.
- The prior live attempt is failed acceptance evidence. Real Grok
requalification, proactive work, controller restart and active-session
Stop remain release gates. This PR does not claim those passed.

## Risks

- Upload and completion now share a durable run receipt. Lock order and
current assignment authority must remain consistent with native
mutations.
- Publication fails closed if the file, run or grant changes. A retry
must keep the same upload identity and bytes.
- Person connections gain no agent authority. No database migration is
required.
- Known exit diagnostics do not prove global Grok termination. Grok
spending remains unreported.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository tools, code
execution and computer use. The exact deployment model ID and
context-window size are not exposed in this session.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 12:54:05 -05:00
DottaandPaperclip ad0bd26bb7 perf(heartbeat): avoid Git inspection for stored workspace reads (#16306)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Heartbeat selects and restores a saved execution workspace before
dispatch.
> - These reads used the workspace detail reader, which also scans Git
and resolves delivery state.
> - Configuration reads do not use those live display fields. Each extra
process can delay other controller work.
> - This pull request adds a stored-record reader and uses it in
configuration, environment selection, and finalization.
> - It also derives ancestry from the existing ahead/behind result and
skips delivery HEAD reads for unfinished tasks.
> - The benefit is fewer Git processes while live execution and cleanup
guards remain in place.

## Linked Issues or Issue Description

Related: #16282, #16302, #13619. Those changes reduce background or
display scan demand. This change removes live display inspection from
execution configuration reads.

**What existing behavior does this improve?**

Heartbeat workspace configuration reads and workspace close-readiness
checks.

**Current behavior**

Reading saved workspace configuration also loads runtime health, Git
status, ancestry, and delivery state. Close readiness walks history
twice to determine ancestry.

**Proposed behavior**

Execution reads stored configuration through a company-checked method.
Live branch validation and destructive cleanup still inspect current Git
state. Close readiness uses its ahead/behind result to determine
ancestry.

**Reason and benefit**

A synthetic benchmark of the actual service returned identical stored
fields for 20 workspaces. Detail reads launched 60 Git commands and took
689 ms. Stored reads launched zero Git commands and took 8 ms. This was
a local macOS measurement, not an end-to-end cloud latency claim.

**Breaking changes**

None to public APIs or saved data. Workspace detail responses keep their
live fields.

## What Changed

- Add `getStoredById(companyId, id)` and use it in three heartbeat
reads.
- Preserve the access-denied error for a foreign-company workspace.
- Remove the extra `merge-base` process and the unused HEAD read for
unfinished tasks.
- Add recovery, company-boundary, ancestry, and cleanup regressions.
- Add a repeatable database/Git benchmark and document the read
boundary.

## Verification

- The new environment-selection regression failed before the fix with
`Live Git inspection is unavailable` and passes after it. All eight
environment-selection tests pass.
- On the final rebased commit, workspace service, environment selection,
and branch finalization: 89 tests passed. Branch-containment,
runtime-execution, and display-cache suites also passed during
development.
- `pnpm -r typecheck`: passed.
- `pnpm build`: passed.
- `pnpm test:run`: started locally, then stopped after all full-suite CI
test shards passed on the final commit. The complete local run was not
finished; CI provides the full-suite result.
- Final commit `9a13ef0481b7bf6a60b2b7ac13f35f78a14e232c`: 55 successful
checks and 2 expected skips. Greptile: 5/5, no actionable findings.
- `pnpm --filter @paperclipai/server exec tsx
../scripts/benchmark-workspace-record-reads.ts`: identical stored
fields, 60 to zero Git commands, 689 ms to 8 ms for 20 reads.

## Risks

- Execution callers must use saved fields only. Detail readers and live
branch and cleanup guards remain separate.
- A failed ancestry comparison stays unknown. Automatic cleanup still
rejects unknown delivery state and rechecks Git before deletion.
- This patch has not been deployed. It does not prove the full slow-run
latency is resolved.

## Model Used

OpenAI Codex, GPT-6. The exact model identifier and context window are
not exposed in this session. Used reasoning, repository inspection, code
editing, and local command execution.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 12:53:41 -05:00