Commit Graph
5765 Commits
Author SHA1 Message Date
DottaandPaperclip 623f1dc289 fix(server): schedule secret-proposal expiry from committed changes (#17446)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents can propose secrets and bindings for approval.
> - Pending proposals already contain their expiry and encrypted value.
> - The server scans for expired proposals on every maintenance tick,
even when none exist.
> - Committed proposal changes and saved expiry times provide the
required triggers.
> - This PR uses those triggers and preserves scrubbing, approval
checks, and recovery.
> - Empty and completed proposal history no longer causes recurring
expiry queries.

## Linked Issues or Issue Description

Refs #17443. This is the second PR in the sleep migration stack and
targets its branch.

**What problem does this solve?**
The periodic proposal scan contacts PostgreSQL when no proposal needs
maintenance. That obstructs idle database behavior across tenant
instances.

**What is the proposed solution?**
Use transaction-aware notifications for creation and resolution.
Reconstruct the earliest pending expiry at startup. Run cleanup at that
deadline, or retry while reconciliation remains unfinished.

**Why is this useful?**
It removes an idle scan while retaining security checks and physical
ciphertext cleanup. No queue table or migration is required.

## What Changed

- Added intent notifications before secret/binding creation, approval,
terminal transitions, and linked interaction resolution. The existing
outer transaction owns settlement.
- Kept expiry selection, ciphertext scrubbing, dependent rejection, and
authorization in the secret-proposal service.
- Registered a startup and deadline worker in place of the aggregate
maintenance sweep. Empty/terminal states disarm; overdue batches and
failures retain retries.
- Reused shared scoped deadline dispatch for secret expiry. The service
owns company-filtered SQL and expiry policy. Custom-image expiry
continues to use `registerDeadlineWork`.
- Added real PostgreSQL producer, outer-transaction, concurrent-write,
expiry, restart, interaction resolution, and drain/resume tests. Updated
startup wiring fixtures and documented the supported writer/lifecycle
contract.

## Verification

- Landing reconciliation at `c10eb1bcf752b058713e05d3345e5cf38dd3fa72`:
160 tests pass across the complete secret-proposal, scoped deadline,
company batch, coordinator, startup, custom-image service and
custom-image worker suites; no cases skipped in the accepted runs.
Server typecheck passes. This integrates master after #17443 landed;
fresh remote CI and Ponytail review are required before merge.

Current head: `5bbe17ad9ebc4573b08ed1a9323644ef9608ca09`. This remains a
single-duty PR stacked on #17443.

- Integrated pinned master `8548173fc9efdc0fa07644fb92dcc0b6851a95e5`
with normal merges. Every branch passed generated-schema checks and its
target-branch migration-order gate. Existing SQL is unchanged; the 25
stack migrations follow master’s new migration.
- Duty-specific producer coverage, lifecycle tests, and earlier failure
records:
[2026-10-08-instance-sleep-and-scheduler-inventory.md](https://github.com/paperclipai/paperclip/blob/5bbe17ad9ebc4573b08ed1a9323644ef9608ca09/doc/plans/2026-10-08-instance-sleep-and-scheduler-inventory.md),
[2026-10-24-recurring-server-sleep-stack.md](https://github.com/paperclipai/paperclip/blob/5bbe17ad9ebc4573b08ed1a9323644ef9608ca09/doc/plans/2026-10-24-recurring-server-sleep-stack.md).
- Combined tip `51e6cbe67979b55b340ea9f0f434c5b5c682ccd6`: **355
startup/cancellation/recovery/dispatch/preservation tests**, **75
connection-management tests**, and production server typechecking pass.
These combined checks do not mean every intermediate PR independently
ran the full repository suite.
- Real master-to-stack database upgrade passes: **379 unchanged prior
migration receipts plus 25 new migrations**, with the gateway
responsibility fixture preserved.
- Owning-branch checks: 84 runner installation/settlement cases plus
runner typechecking and generated TypeScript/Rust profile agreement; 253
GitHub review workflow cases. Stop-attribution and held-worker readiness
regressions failed before their fixes.
- Failure accounting: the initial combined run passed 349 cases and
failed six idle assertions in a reused preservation database containing
old cancelled runs. Repeating the combined command with a fresh
preservation database passed. Earlier invalid project selection, merge
import/type errors, and the interrupted full local suite are not counted
as passing checks.
- **Latest-head CI and review remain required.** Prior CI findings
include GitHub idle observation, route teardown, attention cleanup,
touch-picker E2E, workspace health, and voice-cleanup timing; local
passes do not erase those failures. No full local repository-suite,
deployed sleep, fleet cost, or live Copilot qualification claim is made.

## Risks

- Notifications cover supported writers sharing the database signal
owner. Independent SQL writers and other process roots require a
separate ingress contract; startup recovery alone is not live replica
support.
- The shared coordinator and scoped worker also serve later stack
duties. Their defaults remain intact; existing voice, chat, native, and
startup consumers are included in integrated verification.
- Startup now checks overdue proposals immediately. During drain,
physical cleanup waits; approval still rejects expired proposals
synchronously.
- Each due company pass remains bounded to 100 rows. Failed scopes back
off from 30 seconds to five minutes; healthy overdue work keeps
draining. Self-generated write hints coalesce and do not erase a failed
sweep's backoff.
- Heartbeat-disabled behavior and Cloud sleep-safety gates remain
unchanged. This does not establish deployed suspension or external wake
coverage. Tool review and gateway token cleanup remain separate slices.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning and code execution. The exact
serving model identifier and context window were not exposed.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
canary/v2026.1027.0-canary.8
2026-10-27 14:36:28 -05:00
DottaandPaperclip 5982246771 fix(server): schedule custom-image setup expiry from committed changes (#17443)
## Thinking Path

> - Paperclip manages AI agent work and its execution environments.
> - Interactive image setup sessions must expire even when users leave.
> - The server currently scans these sessions on every maintenance tick.
> - Empty scans keep the instance and database active.
> - Existing session rows already record status and expiry.
> - This pull request uses transaction signals and the shared scheduler
for those rows.
> - Empty and completed setup history no longer create recurring cleanup
queries.

## Linked Issues or Issue Description

**What happened?**

The server checks custom-image setup expiry every heartbeat maintenance
tick, even when no active session exists.

**What did you expect?**

Session writes should update the next cleanup deadline. Existing
provider cancellation and cleanup ownership must remain intact. Empty
state should stay quiet.

**Steps to reproduce**

Run an instance with heartbeat scheduling enabled and no custom-image
setup sessions. Observe repeated setup-expiry reads.

Refs #17145. This is the first PR in the recurring server sleep stack.
Its base is master. The full requested scope and verification contract
are recorded in `doc/plans/2026-10-24-recurring-server-sleep-stack.md`.
Follow-up PRs target their predecessor and each own a separate reason
for recurring work.

## What Changed

- Carry the historical cleanup retry-observation fixture repair and its
existing external test-database hook at the stack foundation. Runtime
scheduling is unchanged.

- Carry the previously verified login-cleanup fixture settlement
correction at the stack foundation so every intermediate CI run receives
it. This is test-only maintenance; custom-image expiry behavior is
unchanged.

- Add a custom-image expiry worker to the existing delivery coordinator.
Remove its maintenance-tick scan.
- Signal initial creation, provider refresh, cancellation/failure, and
promotion inside their owning transaction.
- Keep the active-status predicate, earliest saved expiry, and provider
behavior in the custom-image service. Shared code gains only a queue
topic.
- Preserve startup recovery, bounded retries, concurrent write
generations, and drain deferral. No tables or external alarm changes.
- Add PostgreSQL lifecycle tests and repair this suite's database
cleanup callback. A dedicated external test URL permits disposable
PostgreSQL when the host cannot start another embedded cluster.

## Verification

Current head: `3507c6be1e054fdd93353d5dcf77d4899b44518f`. This remains a
single-duty PR stacked on master.

- Integrated pinned master `8548173fc9efdc0fa07644fb92dcc0b6851a95e5`
with normal merges. Every branch passed generated-schema checks and its
target-branch migration-order gate. Existing SQL is unchanged; the 25
stack migrations follow master’s new migration.
- Duty-specific producer coverage, lifecycle tests, and earlier failure
records:
[2026-10-08-instance-sleep-and-scheduler-inventory.md](https://github.com/paperclipai/paperclip/blob/3507c6be1e054fdd93353d5dcf77d4899b44518f/doc/plans/2026-10-08-instance-sleep-and-scheduler-inventory.md),
[2026-10-24-recurring-server-sleep-stack.md](https://github.com/paperclipai/paperclip/blob/3507c6be1e054fdd93353d5dcf77d4899b44518f/doc/plans/2026-10-24-recurring-server-sleep-stack.md).
- Combined tip `51e6cbe67979b55b340ea9f0f434c5b5c682ccd6`: **355
startup/cancellation/recovery/dispatch/preservation tests**, **75
connection-management tests**, and production server typechecking pass.
These combined checks do not mean every intermediate PR independently
ran the full repository suite.
- Real master-to-stack database upgrade passes: **379 unchanged prior
migration receipts plus 25 new migrations**, with the gateway
responsibility fixture preserved.
- Owning-branch checks: 84 runner installation/settlement cases plus
runner typechecking and generated TypeScript/Rust profile agreement; 253
GitHub review workflow cases. Stop-attribution and held-worker readiness
regressions failed before their fixes.
- Failure accounting: the initial combined run passed 349 cases and
failed six idle assertions in a reused preservation database containing
old cancelled runs. Repeating the combined command with a fresh
preservation database passed. Earlier invalid project selection, merge
import/type errors, and the interrupted full local suite are not counted
as passing checks.
- **Latest-head CI and review remain required.** Prior CI findings
include GitHub idle observation, route teardown, attention cleanup,
touch-picker E2E, workspace health, and voice-cleanup timing; local
passes do not erase those failures. No full local repository-suite,
deployed sleep, fleet cost, or live Copilot qualification claim is made.

## Risks

- Signals cover supported service producers sharing the DB owner.
Independent SQL and another process require the existing
restart/reconciliation contract.
- Failed provider cancellation retains the existing failed-session and
historical-cleanup behavior. This PR changes scheduling, not teardown
authority.
- Active rows without an expiry preserve the existing behavior; no
deadline is invented. Cloud sleep eligibility remains conservative.
- The worker remains in the existing heartbeat-enabled startup path. It
does not change disabled-scheduler behavior or prove deployed
suspension.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning and code execution. The exact
serving model identifier and context window were not exposed.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [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

Review follow-up: Added a real producer commit during a paused deadline
read. Updated startup assertions for coordinator ownership and removed
added code comments. The three affected suites pass (102 tests). The
separate login-cleanup suite also passes locally (16 tests); its prior
CI consume test timed out, and the fresh complete CI run passed.
External PostgreSQL tests now use the repository guard for disposable
databases ending in `_test`.

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 14:17:01 -05:00
DottaandPaperclip 50b932a142 fix(server): require approval before onboarding task creation (#17914)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/78/changes/paperclip/78
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip manages agent work and human decisions.
> - Onboarding asks the first agent to propose a task before it starts
work.
> - The first-task skill requires human acceptance before task creation.
> - The native tool could create and schedule that task without
acceptance.
> - REST task creation had the same gap.
> - This change checks saved acceptance in the shared creation
transaction.
> - Valid approvals and ordinary delegation keep their existing
behavior.

## Linked Issues or Issue Description

**What happened?**

An agent could create a task from the canonical onboarding first task
before the user accepted its proposal. A real journey caught this after
the agent read the first-task skill. A local database regression
reproduces the missing check in the native tool and both REST creation
endpoints.

**Expected behavior**

New task creation must wait for saved human acceptance of the latest
ordinary proposal. A different parent or a standalone task must not
bypass that check.

**Steps to reproduce**

1. Bind an authenticated agent run to a server-created onboarding first
task.
2. Leave its proposal unaccepted.
3. Call native `create_task`, `POST /api/companies/:companyId/issues`,
or `POST /api/issues/:issueId/children`.
4. Before this fix, creation succeeds. After this fix, it is denied
before a child or wake is committed.

**Paperclip version or commit**

Reproduced on `3be54534c24fc279533ff421e62a77ad15b72954` before the fix.

## What Changed

- Add a shared check for new task creation from an authenticated
onboarding run.
- Read the latest ordinary confirmation across both confirmation kinds
under the source task lock.
- Require a human card decision or validated conversational acceptance.
Require the current revision for a saved plan.
- Preserve ordinary delegation, committed replay, and dispatch recovery.
- Add database regressions and update execution and onboarding map
documentation.

## Verification

- Real PostgreSQL regression before the fix: 17 failures and four
passing approval/delegation controls. No skips.
- Exact published source: 102 tests passed across admission,
conversational confirmation, and project tools, including all 25
admission regressions. Real Linux PostgreSQL, no skips.
- `pnpm build` and `pnpm -r typecheck` passed locally.
- Map checks and `git diff --check` passed.
- `pnpm test:run` was attempted locally. Existing suites failed because
the host exhausted PostgreSQL semaphore sets. The remaining owned test
process was stopped; its failed log is preserved. No unrelated database
was stopped. Current-head Linux CI passed, including build, typecheck,
and browser E2E. The first CI attempt lost runners; its evidence is
preserved, and interrupted jobs passed one CI rerun.
- The live Cloud attempt remains failed. Local tests do not qualify
Cloud onboarding, artifact delivery, or native tracing. No deployment or
model retry is part of this change.

## Risks

A stricter creation check could reject a valid approval. Tests cover
direct, conversational, checkbox, dismissed-card, and clock-skew cases.
The check applies only to new tasks from the canonical onboarding run.
It does not change existing task updates, execution policy, or wake
recovery.

## Model Used

OpenAI GPT-6 through Codex, with tool use and code execution. The
session 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-27 12:30:50 -05:00
DottaandPaperclip 6e781e0ab2 fix(connections): preserve removed grants when saving installs (#17906)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Connections give agents access to external tools through access
profiles.
> - Operators can remove a broad app profile and keep a limited profile.
> - Saving a connection's agent list restored the broad profile to
existing installation targets.
> - Those restored grants could permit write tools that the limited
profile omitted.
> - This pull request grants the app profile only to new installation
targets.
> - The benefit is stable permission choices when an operator edits an
installation list.

## Linked Issues or Issue Description

**What happened?**

`PUT /api/tool-connections/{connectionId}/installs` attached the app
profile to every target in the submitted list. An existing target
received the profile again if an operator had removed it. Adding an
agent, removing another agent, or saving the same list could therefore
restore broad tool permissions. The activity log recorded each restored
binding as `tool_connection.install_access_extended`.

**Expected behavior**

Targets that remain installed keep their saved profile assignments. A
removed app-profile assignment stays removed. New installation targets
receive the current app-profile defaults. Removing an installation still
removes its install-derived binding.

**Steps to reproduce**

1. Install a connection with read and write tools on two agents.
2. Give one agent a read-only profile and remove its broad app-profile
assignment.
3. Save the installation list, add an agent, or remove the other agent.
4. Check the first agent's permission to use the write tool.

Before this change, the write tool becomes allowed again. After this
change, it stays denied.

Related: #17422 adds an agent-edit permission check to direct profile
bind and unbind operations. This PR preserves removed assignments during
installation-list updates. The duplicate search found no open PR for
this defect.

## What Changed

- Attach app-profile bindings only for new installation targets.
- Preserve removed grants for existing agent and company installations.
- Keep legacy connection-wide include cleanup on every non-empty
installation save, including saves with no added targets.
- Add six PostgreSQL regressions. All six use the real install HTTP
route with a local operator fixture.
- Check effective read/write decisions, saved assignments, new-agent
defaults, uninstall cleanup, legacy-rule cleanup, and access-extension
audit records.
- Document the behavior in the connector runbook and both access maps.

## Verification

- Red: all four new regressions fail before the fix. The policy returns
`allow_profile` where the tests require `deny_default`.
- Review regression: the two legacy-rule cases fail before the review
correction. The original four cases still pass.
- Green after review: all six regressions and five existing
profile-precedence tests pass after the correction.
- Command: `pnpm exec vitest run
server/src/__tests__/tool-connection-installs.test.ts
server/src/services/tool-profile-binding-precedence.test.ts`.
- The local regression used `PAPERCLIP_TOOL_ACCESS_TEST_DATABASE_URL`
with a separate, fully migrated PostgreSQL 17 test database. Embedded
PostgreSQL could not start because the host had reached its semaphore
limit. Skipped tests were not counted as a regression pass.
- `pnpm -r typecheck` and `pnpm build` passed for the initial fix.
Server `tsc --noEmit` also passes after the review correction.
- `node scripts/check-maps.mjs`, `node
scripts/check-module-boundaries.mjs`, and `git diff --check` pass.
- `pnpm test:run` was started, then stopped after 16 tests in the
native-session suite failed during embedded PostgreSQL startup. A
focused rerun of its `collect` case confirmed the database startup
error. Direct `initdb` output reported `could not create semaphores: No
space left on device`. The full local suite is incomplete. All six new
regressions executed against the separate test database and passed.
- [Initial-fix
CI](https://github.com/paperclipai/paperclip/actions/runs/40541191695)
passed at `23253ee332b90f142e48df3f553aeaaa7f6fa298`. Greptile gave that
commit 5/5. Ponytail found the legacy cleanup gap; this revision
restores that cleanup and adds both regression cases.
- [Current-commit
CI](https://github.com/paperclipai/paperclip/actions/runs/40547366181)
passes for `729c487cf2ffaae261a820de0cf9a033a0365fd4`, including type
checks, build, test shards, and both `ci / verify` and `ci / e2e`
aggregate checks.
- The first CI attempt hit a 30-second module-import timeout in the
unchanged `issue-agent-mutation-ownership-routes.test.ts` suite. All 179
tests passed locally. One [job
retry](https://github.com/paperclipai/paperclip/actions/runs/40547366181/job/122349226221)
passed without a code change. The cause of the import slowdown was not
established. The failed attempt remains recorded in CI history.
- Greptile and Ponytail both give the current commit 5/5 with no
actionable findings. Ponytail confirms the legacy cleanup finding is
fixed. The review thread is resolved, and the PR has no merge conflicts.
Ponytail's advisory check has a neutral conclusion with a 5/5
assessment.

## Risks

- New installations retain the current app-profile defaults. Removing
and then reinstalling a target is a new installation.
- A repeated installation save no longer restores a missing profile
assignment. Operators must use an explicit profile assignment when they
want to restore that grant.
- Existing unwanted grants still require a separate access review and
repair. This PR does not change stored grants, reconnect identity
behavior, or the default access model.
- No database migration or API schema change is required.

## Model Used

- OpenAI GPT-6 in Codex, with reasoning, repository tools, and code
execution. The session does not expose the exact routed 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-27 12:25:01 -05:00
DottaandPaperclip f6ba9afab0 perf(server): narrow explicit continuation run reads (#17896)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Recovery must prove that old execution stopped before it admits new
user input.
> - Explicit continuation reads the source run before and after it takes
the run lock.
> - Both reads transfer the saved prompt and the full Runner profile.
> - The preflight needs only identity, comment, adapter, and workspace
receipt fields.
> - This pull request narrows those reads and keeps the complete locked
profile for session proofs.
> - The benefit is less database transfer with the same admission and
recovery rules.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Explicit native continuation admission after a failed or stopped run.

**Current behavior**

The preflight and locked reread select complete heartbeat run rows.
Large saved contexts and execution profiles cross the database
connection twice.

**Proposed behavior**

Read only required scalar fields and typed context keys. Read only
historical adapter identity and workspace sync receipts before the lock.
Keep the complete Runner profile in the locked reread. Keep lock order,
company scope, text lookup of historical run IDs, stop proofs, queued
input, and replay rules.

**Reason and benefit**

A synthetic PostgreSQL 17 fixture removes 74.9% of the two queries'
DataRow bytes. This result is local query evidence. It is not a claim
about deployed latency or monthly transfer.

**Breaking changes**

None intended. No schema, API, migration, workflow, or dispatch contract
changes.

**Additional context**

Related: #17491 narrows the separate execution blocker read path. This
PR covers explicit continuation admission. The PR has no dependency on
the other byte reduction PRs.

## What Changed

- Select required continuation fields and typed context keys instead of
complete run rows.
- Read a small historical adapter/workspace profile during preflight.
Preserve the full locked profile for exact suspended-session and
quarantined-cleanup proofs.
- Narrow four helper input types to their actual field requirements.
Their runtime behavior is unchanged.
- Add real PostgreSQL admission comparison coverage and a reproducible
baseline/treatment benchmark.
- Document the query boundary and benchmark command.

## Verification

- Baseline: `663fe667d66d3863d0919a8724f1d99500feef0b`.
- Disposable PostgreSQL 17.11. One source run has a 192 KiB
incompressible context and two 96 KiB profile payloads.
- Command: `pnpm --filter @paperclipai/server exec tsx
../scripts/benchmark-continuation-run-reads.ts
--baseline-ref=663fe667d66d3863d0919a8724f1d99500feef0b`. Set
`PAPERCLIP_CONTINUATION_BENCHMARK_DATABASE_URL` to a disposable database
ending in `_test` to use an external server.
- The harness captures SQL generated by both real service versions.
Baseline imports use current helpers; only their TypeScript input types
changed in this PR. Their runtime behavior stays fixed.
- Preflight DataRow bytes: 394,067 to 466 (99.88% less). Locked reread:
394,067 to 197,168 (49.97% less). Combined: 788,134 to 197,634 (74.92%
less).
- Byte counts cover exact PostgreSQL text-format DataRow frames. They
exclude RowDescription, command metadata, TLS, TCP, and other queries in
the service.
- Seven measured samples per query follow two warmups. The order
alternates. Final-head median preflight time: 6.53 to 2.28 ms (ranges
5.34–7.41 and 1.84–4.46 ms). Median locked read: 8.06 to 5.26 ms (ranges
5.94–13.84 and 4.61–6.81 ms). These are warm local SQL measurements
under shared machine load. The deterministic byte counts are the
acceptance criterion; these noisy timings do not establish a deployed
latency gain.
- The harness compares the admitted continuation, blocked reasons, every
retained scalar column, kept JSON, and the full locked profile. It fails
unless the fixture removes more than half of the two reads' DataRow
bytes. It records SQL, parameters, revision identity, samples, and
EXPLAIN plans.
- All 291 continuation tests pass together on the final head with no
skips in a clean isolated PostgreSQL 17 database. This includes all
seven new projection comparisons and six malformed/missing JSON cases.
- Edited server TypeScript check passes: `node
node_modules/typescript/bin/tsc --noEmit -p server/tsconfig.json`.
- `pnpm -r typecheck` and `pnpm build` pass on the final head. The
normal server build verifies the complete pinned Copilot closure. Local
full `pnpm test:run` is not claimed: host embedded PostgreSQL startup
limits prevent that gate. The isolated PostgreSQL regressions pass; the
full exact-head test matrix runs in CI. The targeted exact-head test
retry passes all 4,086 tests in 98 files. All CI verification checks and
the dependent aggregate are complete and successful on head
`47c1b5f7d17a58310f7a3ebd494fdcaa814777c2`. Greptile completed cleanly
on head `47c1b5f7d17a58310f7a3ebd494fdcaa814777c2`: 5/5, no findings,
and no unresolved review threads.
- Local setup evidence: embedded initdb could not load its ICU data
library. Tests therefore use a dedicated PostgreSQL 17 container. A run
against reused test fixtures produced stale sweep failures; a new clean
database passed all 285 cases. The first comparison mock replaced an
unrelated aliased query; it was narrowed to the two admission reads
before the clean pass.

- The first [server test shard
6/12](https://github.com/paperclipai/paperclip/actions/runs/40529210562/job/122277218565)
failed one existing workspace-recovery case: a third export stayed
`scheduled` at its 20-second poll deadline; 4,085 other tests passed.
The exact case passes on the pinned baseline and final head with the
same PostgreSQL 17 fixture. Temporary spies recorded zero calls to both
changed admission functions in either replay. Other touched helpers have
type-only changes. This supports one timing-flake retry; the exact
scheduler delay remains unproven. The single [targeted shard
retry](https://github.com/paperclipai/paperclip/actions/runs/40529210562/job/122285744135)
passes all 4,086 tests in 98 files. The exact case completes in 17.25
seconds. The dependent aggregate is complete and successful. No timeout
or unrelated runtime code was changed.

- The separate Paperclip Review check ended `NEUTRAL` without a final
report. The completed source review is the exact-head Greptile 5/5
result above.

## Risks

- A future admission check may need an additional saved context key.
Keep the projection and helper contracts in sync with that consumer.
- Exact suspended-session and quarantined-cleanup proofs still require
the complete Runner profile. This PR retains it under the lock rather
than weakening those checks.
- JSON values retain their types and missing keys stay absent.
Malformed-context comparisons preserve the existing controller hold.
- These measurements use synthetic local data. Deployed transfer savings
require a fresh capture after rollout. Nothing was deployed.

## Model Used

OpenAI GPT-6 through Codex. The exact runtime model ID and context
window are not exposed by this session. The work used repository tools,
code execution, and real PostgreSQL validation.

## 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-27 12:05:51 -05:00
DottaandPaperclip 6e5b9794d6 perf(github): avoid duplicate webhook body reads (#17894)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - GitHub webhooks enter a durable action queue before the worker
processes them.
> - A worker must check retry eligibility and win an atomic claim before
it replays a delivery.
> - The current eligibility read returns the complete webhook body, and
the successful claim returns it again.
> - This pull request reads small eligibility fields first and returns
the body only to the successful claimant.
> - The worker keeps its retry, ownership, runtime, credential, and
recovery checks.
> - The benefit is less database transfer without removing durable
replay data.

## Linked Issues or Issue Description

Refs: #17666, #15981. Those changes own the existing recovery scheduler
and ingress module. This PR changes the result projections and
validation order within that module. It has no dependency on another
open PR. A public issue and PR search found no duplicate change.

**What existing behavior does this improve?**

The durable GitHub webhook worker reads action eligibility and replays
claimed deliveries.

**Current behavior**

The first read returns all action columns and the complete webhook body.
A successful claim returns them again. An active claim or future retry
also downloads a body that this worker cannot process.

**Proposed behavior**

Read only ID, status, result, and update time for eligibility. Return
ID, endpoint ID, and payload from the successful atomic claim. Validate
that claimed payload before replay.

**Reason and benefit**

A synthetic 131,105-byte webhook body produces about 263 KB of response
values before this change. The changed service returns about 132 KB for
successful processing and fewer than 150 bytes for skipped work. This
removes duplicate body transfer.

**Breaking changes**

There is no public API or schema change. Malformed active or delayed
rows now remain under their current claim or retry deadline. The
eligible claimant validates and fails them later. A worker that does not
own the claim can no longer clear their payload.

## What Changed

- Use a small projection for initial action eligibility.
- Return the replay body only from a successful claim. Keep the existing
status, stale-time, and previous-attempt guards.
- Validate the row returned by the claim. Fence invalid-payload
settlement by kind, processing status, and the claimed attempt.
- Add 19 real PostgreSQL regressions for complete replay bodies, skipped
work, retries, stale-claim recovery, contention, corrupt bodies, claim
loss, replacement bodies, and payload retention.
- Wait for the saved chat authentication link in one existing async UI
test. Keep its exact destination and all other assertions. This repairs
a second independent CI test failure without changing product UI.
- Accept the two precise invalid-repository diagnostics from Git 2.55
and 2.56 in one existing test. This repairs an independent CI
compatibility failure. It changes no Git expiration service behavior.
- Add a reproducible benchmark that loads the real baseline service from
an explicit commit. It captures generated SQL and parameters, query
plans, response-value estimates, timings, and complete valid-work
behavior comparisons.

## Verification

- Baseline: `663fe667d66d3863d0919a8724f1d99500feef0b`.
- PostgreSQL 17.11, Node 24.22.1. The fixture uses synthetic random body
content. No hosted database or provider was changed.
- `pnpm exec vitest run
server/src/services/chat-channels/github/webhook-ingress.test.ts`: 19
passed with a dedicated disposable PostgreSQL database.
- Existing assembled GitHub tests: recovery from pre-ingress failure,
scheduler retry and restart, exact terminal redelivery, runtime
pause/resume, recovery tombstones, and credential rotation passed.
- `node scripts/check-module-boundaries.mjs` and `git diff --check`:
passed.
- Benchmark: `node --import ./server/node_modules/tsx/dist/loader.mjs
scripts/benchmark-github-webhook-ingress.ts
--baseline-ref=663fe667d66d3863d0919a8724f1d99500feef0b`. The default
uses disposable embedded PostgreSQL. `PAPERCLIP_TEST_DATABASE_URL` can
select a dedicated migrated test database with a name that ends in
`_test`.
- Ten timed samples after two warmups, with alternating
baseline/treatment order. The main acceptance criterion is response
size, rather than a timing threshold. The 0.51 ratio bound is
deterministic for this fixed synthetic fixture; it is not a promise for
arbitrary webhook bodies or result metadata sizes. Focused tests measure
the body transfer reduction directly. Actual wire savings require
post-rollout measurement.

| Scenario | Baseline response-value bytes | Changed response-value
bytes |
| --- | ---: | ---: |
| Received | 263,276 | 131,547 |
| Due retry | 263,342 | 131,613 |
| Stale processing | 263,291 | 131,563 |
| Future retry | 131,692 | 136 |
| Active processing | 131,642 | 86 |
| Processed | 131,659 | 103 |

The benchmark compares response status/body, all stable durable action
fields, and the replay body, signature, delivery ID, and provider
routing. All six valid scenarios match. It excludes generated row IDs
and timestamps. The final measurement records treatment commit
`f1ad8ce838700237b56f895cc61a9c6ed0850cd6`, an empty initial worktree
status, and the SHA-256 of the actual source. It ran from the server
directory to verify that Git paths resolve from the repository root.
Local timing ranges overlap and vary with host load. An earlier loaded
repeat had a due-retry median of 19.61 ms before and 22.58 ms after, so
no consistent latency gain is claimed. The byte reduction is the
verified result. Response-value estimates exclude PostgreSQL
protocol/TLS overhead and JSONB whitespace. These are local
measurements, not a claim about deployed latency or a monthly bill.

`pnpm -r typecheck` and `pnpm build` passed on production-change commit
`6f4f36bff525494dadda30b49ea6a9a33976e1ab`. Greptile completed with 5/5
and no review threads on that commit. Commit
`2abf9b1e50c568b632e7568398a5826a6122e456` changes only the Git test
matcher. Commit `f1ad8ce838700237b56f895cc61a9c6ed0850cd6` fixes
benchmark provenance after automated review. Commit
`ee2a96dad549c72da3132d2b93fd11eb1c45bc31` repairs an existing async UI
test. Final commit `994d398d12be65f595b305d1c2f8a61b9d587ff9` extends
real PostgreSQL contention coverage after review. Full CI completed
successfully on that final head: [workflow run
40537698349](https://github.com/paperclipai/paperclip/actions/runs/40537698349).
All 55 successful checks, one neutral review, and two optional skipped
checks are terminal; none are pending or failing. Fresh Greptile and
Paperclip reviews are both 5/5, with zero unresolved review threads. The
production source SHA-256 remains
`402f9c66cd030c9ded9f39a3bc72d26c56627752c0950b102adcab15c4512df3`, so
the production proof is unchanged. The full local `pnpm test:run` was
not completed because this host exhausted embedded PostgreSQL semaphore
resources; the focused real PostgreSQL tests used a private disposable
container. Current-head remote CI passed the full suite. A direct server
typecheck before runner preparation reported missing runner build
outputs; supported preparation and the full typecheck then passed. The
first complete build found a partial local Copilot closure from the
dependency cache. The complete pinned closure was restored and verified,
then the complete build passed. The first local test run also found a
fixture teardown order error; the teardown was corrected, then all 17
tests passed. Frozen dependency installation failed on the existing
master patch metadata. Independent cached dependency directories were
cloned, their workspace links were checked, and no dependency manifest
or lockfile was changed.

The first CI server4 shard failed one existing Git expiration test: Git
2.56 reported `gitfile does not point to a valid repository`, while the
test expected the Git 2.55 phrase `not a git repository`. The service
returned the correct `inspection_failed` result and preserved the
workspace. Separate test-only commit
`2abf9b1e50c568b632e7568398a5826a9c6ed0850cd6` accepts either precise
diagnostic. The affected assembled test passed locally with real
PostgreSQL 17.11 and Git 2.55; the captured CI diagnostic also matches,
and unrelated permission errors do not. This was a compatibility
failure, not a transient failure or an unchanged rerun.

Paperclip Review found that the benchmark recorded its own scratch
directory as an uncommitted change. The final benchmark-only repair
captures repository status before scratch creation, runs Git commands
from the repository root, records the source hash before measurement,
and rejects source edits during measurement. A full benchmark run from
the server directory then reported clean provenance and passed all six
byte/behavior comparisons. The review thread was marked addressed after
this proof.

The next full CI run found a separate unchanged UI test failure in
`ConnectionIntentInteractionBody.test.tsx`: the saved authentication
link was still absent after one fixed flush. The same failure reproduced
locally: one failed and 50 passed. Separate test-only commit
`ee2a96dad549c72da3132d2b93fd11eb1c45bc31` uses the existing bounded
`waitForAssertion` helper around the identical destination assertion.
The whole file then passed all 51 tests, and UI token gates passed. The
no-extra-setup and no-automatic-completion assertions remain. No UI
runtime or timeout changed.

Paperclip Review also asked for parallel claims from stale-processing
and due-failed rows. The contention test now covers those states and
received rows. Each case holds the claims until all 12 workers have read
the same eligible attempt, then uses real PostgreSQL to assert one
replay and exactly one attempt increment. The whole focused suite passed
all 19 tests. This coverage change does not modify production code or
the measured queries. The review thread was marked addressed after this
proof.

## Risks

- Payload validation now follows claim commit. If the process stops
there, the existing stale-claim recovery path retains the body and
retries it. Tests cover fresh and stale processing rows.
- A malformed future retry now fails when due instead of being cleared
by an unrelated early worker. The claimed-attempt guard prevents invalid
settlement from overwriting a later claim. Terminal invalid-payload
settlement intentionally retains the baseline result shape (`code` and
`retryable: false`, without `attempts`). Changing that result contract
is outside this byte reduction; the non-retryable row cannot become due
again.
- Existing runtime-generation, credential, recovery-source,
replay-barrier, redaction, and retry checks remain in place. Existing
assembled tests cover these boundaries.
- There is no migration, startup job, dependency change, or deployment
in this PR.

## Model Used

- OpenAI GPT-6 through Codex. The exact runtime model ID and context
window are not exposed by this environment. Capabilities used: source
analysis, code editing, shell tools, PostgreSQL experiments, and
automated 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 (The
public contract is unchanged; benchmark usage and evidence are
documented above.)
- [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-27 12:05:30 -05:00
DottaandPaperclip 7a0c9c23cf perf(github): reduce inherited review-wait transfer (#17893)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - GitHub review tasks can retain a passive wait after a review is
published.
> - The wait check reads current requester, task, agent, repository, and
publication bindings.
> - It also transfers the complete assessment for one commit comparison.
> - Large review summaries and findings add repeated database transfer.
> - This pull request selects only the typed reviewed commit value.
> - The wait keeps its existing checks and transfers less data.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The inherited GitHub review-wait check in the server.

**Current behavior**

The joined query selects the complete assessment JSON. The caller uses
only `assessment.reviewedCommit`.

**Proposed behavior**

Select `assessment->'reviewedCommit'`. Keep its JSON type and the strict
comparison with the head SHA. Keep all joins, filters, ordering, and
publication checks.

**Reason and benefit**

The synthetic PostgreSQL 17 fixture reduces one result DataRow from
6,874 bytes to 423 bytes (93.85%). This measurement excludes response
metadata, TLS, and TCP. It does not predict a monthly bill or deployed
savings.

**Breaking changes**

None. No public API, stored data, schema, or execution policy changes.

**Additional context**

Related work: #17562 narrowed a different external-review candidate
query. This PR changes the inherited published-review lookup. The
duplicate search found no matching open change.

## What Changed

- Select the commit as JSON, rather than text, to preserve strict
comparison for legacy numeric, object, array, missing, and null values.
- Add real PostgreSQL regressions for narrow responses, unchanged stored
assessments, revocation, tenant and agent bindings, and a newer queued
head.
- Add a synthetic benchmark that captures the real baseline and
treatment SQL, compares outputs, and measures raw DataRow bytes.
- Document the read contract and benchmark command.

## Verification

- Baseline: `663fe667d66d3863d0919a8724f1d99500feef0b`.
- `vitest run server/src/__tests__/github-review-wait.test.ts`: 14 tests
passed on disposable PostgreSQL 17.11.
- Restoring the baseline query makes the response-size regression fail:
8,823 column-value bytes exceed the 1,000-byte bound. Restoring
treatment passes.
- Benchmark: `pnpm --filter @paperclipai/server exec tsx
../scripts/benchmark-github-review-wait.ts
--baseline-ref=663fe667d66d3863d0919a8724f1d99500feef0b`.
- The benchmark compares every unchanged SQL column and full service
output. It also compares 15 JSON cases and live connection revocation.
- Two warmups and seven alternating samples had overlapping latency
ranges. No latency improvement is claimed.
- `pnpm -r typecheck` and `pnpm build` passed locally.
- Twenty existing published-review consumer regressions passed on
PostgreSQL 17.11. These cover continued review, a newer head,
publication state, and a connection-disable race at final commit.
- Final-head focused tests passed again after the benchmark-only review
fix.
- Final benchmark treatment: `d94410a6e6bb83e727afdf09cff450a91a89edd6`,
with an empty source diff. DataRow bytes remained 6,874 to 423. Warm
medians were 3.11 ms before and 3.43 ms after, with overlapping ranges.
This proves byte reduction, not a latency improvement.
- A provenance check with one staged and one unstaged source edit
confirmed that the benchmark records both from the documented server
working directory. Those temporary edits were removed.
- All final-head CI checks passed for
`d94410a6e6bb83e727afdf09cff450a91a89edd6`. Greptile scored this exact
commit 5/5. There are zero unresolved review threads or recommendations.
The independent Paperclip review also scored this commit 5/5.
- The first local embedded PostgreSQL attempt failed before assertions
because the host exhausted System V semaphores. A direct `initdb`
reproduced `could not create semaphores: No space left on device`. The
passing focused proof used an owned disposable Docker PostgreSQL 17
container. `pnpm test:run` was not completed locally because of that
host limit. The full final-head CI suite passed.
- The final CI aggregate briefly reported `in_progress` after all of its
steps and the workflow had completed successfully. One rerun of only
that aggregate produced a terminal success. The test matrix was not
rerun.
- The initial frozen install hit the existing upstream patch/lock
configuration mismatch. A cached non-frozen local install succeeded. The
generated lock change is excluded from this PR.

## Risks

The selected commit must retain its JSON type. Using `->>` could turn
numeric `1` into text and accept a malformed assessment against head
`1`. The regression covers this case. The complete assessment remains
available for actual review and publication. Existing fresh authority
checks remain in place. No migration is required.

## Model Used

OpenAI GPT-6, accessed through Codex. The exact runtime model ID and
context-window size were not exposed in this session. Used repository
inspection, code execution, and PostgreSQL validation.

## 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-27 12:05:01 -05:00
DottaandPaperclip 8548173fc9 feat(evals): connect hosted Runner costs and traces (#17889)
## Thinking Path

> - Paperclip manages AI agents and measures their behavior with Runner
evaluations.
> - The cost controller, native tracing, private receipts and portable
runtime are on master.
> - The maintained live workflow still launches the provider CLI
directly.
> - Independent workers need one shared selection and disjoint budget
allocations.
> - Reports must keep unresolved allocations when a worker produces no
evidence.
> - This PR connects the maintained workflow to the existing controller
and private archive.
> - Operators can inspect known costs, conservative holds and trace
evidence before qualifying paid recurrence.

## Linked Issues or Issue Description

Refs #17452, #17469, #17493, #17506, #17523.

**Current behavior**

The live Runner workflow does not use the merged cost/tracing
controller. Attempt-only aggregation cannot reconstruct a campaign when
every worker archive is absent. Ordinary Actions artifacts in this
public repository are readable by authenticated public GitHub users.

**Proposed behavior**

Controlled runs prepare one manifest, bind every selected request,
enforce disjoint allocations and retain evidence in private storage. The
report starts from the full retained manifest. Missing workers keep
their allocations. The workflow rejects retries, unsupported providers,
changed requests and absent or stale operating bounds before provider
work.

**Reason and benefit**

The maintained workflow needs auditable costs and traces, including
failures and missing receipts, before paid recurrence can be qualified.

## What Changed

- Add controlled mode as the manual default and the only scheduled mode.
Legacy mode remains explicit for other manual provider qualifications.
- Prepare the full request selection and one shared manifest. Bind
current run, first attempt, code, definitions, executable hashes and
exact profile allocations.
- Verify the copied controller and manifest before provider credentials.
Require the controlled target to equal the trusted workflow revision
before private archive access.
- Reuse private archive retention for the controller policy, cell
attempts and canonical report. AWS credentials remain transfer-step
outputs. Controlled raw evidence is never uploaded as an Actions
artifact.
- Replay a retained manifest with partial or empty worker evidence.
Count operating holds once and preserve known partial charges.
- Pin the controller to an explicitly reviewed compatible lockfile
digest. Reject resolver or byte drift before a frozen, script-free
install into a job-local store. Record the lock digest in the runtime
receipt and verify it again in private preparation.
- Build the portable controller without secrets, private environment
access or OIDC. Private preparation verifies that artifact and performs
no dependency install. OIDC jobs use no mutable dependency resolution or
shared cache.
- Add synthetic source/copied-runtime tests, executable workflow
admission tests, private archive tests and an operator runbook.

## Verification

- Controller, campaign, portable runtime and workflow tests: 104 passed.
This includes real aggregation from an isolated reporter without source
dependencies, safe diagnostics, driver drift, distinct evidence binding
and reviewed-lock drift. Real private Codex/Claude requests also pass
through an independently verified copied runtime with fake transports.
- Private transport and workflow contracts: 35 passed. Executable/static
workflow guards: 13 passed.
- Paired private report tests: 166 passed; Evalbook discovery: 7 passed.
- `pnpm -r typecheck` and `pnpm build`: passed.
- `pnpm test:e2e:runner:typecheck`, `actionlint
.github/workflows/runner-protocol-live-evals.yml` and `git diff
--check`: passed.
- `pnpm test:run` hit repeated local embedded PostgreSQL initialization
failures in unrelated DB-dependent server tests. Its owned process tree
was stopped after preserving the log. This is a local verification
limitation; current-head CI remains required.
- An earlier head hit one unchanged Rust cleanup assertion. The exact
test passed locally and the final head passed the full Rust lane; the
initial failure log is retained.
- On the final head, one unchanged chat idle-queue assertion failed once
in CI. Its single scoped retry passed. All test jobs and the final
aggregate gate passed on commit
`2e328fa53dd296174d59d19f14f068aeeb0b82d8` (CI run 40535692048, attempt
2).
- No paid model, Braintrust or cloud calls. No live variables, secrets,
schedules or resources changed.

## Risks

- Controlled mode fails closed until a reviewed
`RUNNER_PROTOCOL_EVAL_CONTROLLER_POLICY_JSON` and a compatible private
eval SHA exist. `all` does not silently become a supported subset.
- The reviewed policy must cover setup, model requests, trace/gateway
processing, archive transfer and retained storage. Hash binding does not
establish vendor billing bounds. This PR does not enable recurrence or
release historical holds.
- The controlled policy must pin `controllerLockSha256`; it never
accepts a resolver-generated digest automatically. No lockfile is
committed. A changed compatible lock needs review.
- Legacy browser verification now requires the committed frozen
lockfile. A stale lockfile fails closed; controlled preparation has no
dependency install.
- Workflow cancellation can prevent the final report. The full manifest
is retained before provider work and remains the starting point for
reconciliation.
- The workflow exceeds 1,000 lines because the shared shard anchor,
dependencies, environment expressions and credential boundaries belong
to one declarative job graph. Request preparation, accounting, archive
safety and runtime verification stay in separately tested modules.

## Model Used

OpenAI Codex, GPT-6-based coding agent with tool use and code execution.
The host does not expose an exact deployment 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 the relevant tests locally and they pass; the broad
local suite limitation is documented 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 on the final head
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups;
the secondary reviewer is also 5/5
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 11:38:16 -05:00
DottaandPaperclip 585b4e6af8 fix(chat): preserve deferred Retry source batches (#17883)
## Thinking Path

> - Paperclip manages agent work through tasks and governed execution.
> - Connected conversations retain the source and authority for each
request.
> - Task Retry pins the full saved GitHub input batch before it waits
for cleanup.
> - Resuming that wait could append an independent board queue after
authorization.
> - Dispatch then rejected the changed batch before the provider
started.
> - This pull request keeps the authorized batch intact and restores
certified undelivered input from historical failed attempts.
> - Each board message retains a separate turn and its own admission
checks.

## Linked Issues or Issue Description

**What happened?**
A deferred GitHub task Retry could consume a separate board message
while promoting its saved receipt. The new run then failed with
`chat_failed_run_retry_not_authorized`. A second Retry could also fail
because the historical run retained the mixed batch.

**Expected behavior**
Retry must preserve the source batch it authorized. Separate board input
must keep its own execution path. Historical input may be restored only
when durable evidence proves that the rejected attempt never started a
provider.

**Steps to reproduce**
1. Retain ten authorized GitHub requests behind a failed native turn.
2. Start task Retry while the predecessor still lacks its exact
termination receipt.
3. Save an independent board message.
4. Complete cleanup and resume the same Retry receipt.
5. Observe the mixed batch rejected during dispatch. Retry that failed
attempt to reproduce the second refusal.

Refs #17725 for the existing saved-input Retry contract. This change
repairs the later queue merge and its retained failed state. Related
#17878 covers snapshot continuation and later turns in recovered
directories; its workspace repair can be reviewed independently.

## What Changed

- Keep deferred chat Retry promotion scoped to its exact receipt.
Ordinary board Retry coalescing retains its existing behavior.
- Let a new explicit task Retry restore a historical mixed batch only
after strict never-started, source, ownership, allocation, board-comment
and prior-delivery checks.
- Preserve failed-run history and original budget-skipped receipts.
Record restored queue ownership in activity.
- Add production queue and HTTP-route regressions. Update the execution
contract and verification maps.

## Verification

- Before the repair, the deferred ten-input batch failed before identity
initialization. The same regression now reaches the real initializer
with the exact GitHub batch. The independent board message then reaches
its own initializer in a separate turn.
- Before historical recovery, ordinary task Retry refused the persisted
mixed failed batch. The repair passes that case without changing the
failed run. HTTP duplicate requests retain one Retry owner. An
additional compatibility probe restored the old queue candidate
predicate and removed all historical mixed-state fixture writes: the
real producer created the exact batch and the complete historical HTTP
recovery case passed (1/1). The fixed-source historical fixture now uses
the production serializers.
- The affected Retry group passes 86 cases on commit
`325c00158df5569f5b42731cda4ecc4584909d99`, including already-coalesced
external receipts. Refusals cover edited sources, active controllers,
unresolved allocations, changed receipts, foreign external receipt
ownership and prior delivery of either board or GitHub input, including
immutable budget-skipped receipts.
- Full workspace `pnpm -r typecheck` and `pnpm build` pass with these
source changes. Map, module-boundary and diff checks pass.
- Full local `pnpm test:run` was started. The host cannot create
additional PostgreSQL semaphores; database-dependent suites fail or skip
at startup. The affected database group runs against a separate migrated
PostgreSQL 17.10 test database with Node 24.22.0 on macOS arm64.
- The regression deliberately stops after the real identity initializer
to avoid provider allocation. It proves HTTP, queue, source
authorization, identity and separate continuation behavior. It does not
claim a live model result, GitHub publication, deployment or recovery of
the original hosted task.
- Current head `325c00158df5569f5b42731cda4ecc4584909d99`: all checks
are terminal with no failures or pending checks; required `ci / verify`
and `ci / e2e` pass. Conditional Storybook checks are skipped. Greptile
and Paperclip each reviewed this exact commit at 5/5. Both earlier
review threads are resolved. Paperclip retains one optional robustness
suggestion for batch ordering; the verified historical producer order
remains a strict recovery contract.

## Risks

Restoring input could repeat work if the prior turn had executed. The
existing strict bootstrap proof rejects provider output, native state,
process or controller evidence, and allocations. Restoration also checks
current external authority, unchanged board revisions and other run
history. An uncertain executor remains held. No migration, provider
fallback or relaxation of dispatch validation is included.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, repository inspection,
code editing and test execution. The exact runtime model ID and
context-window size are not exposed in this session. A separately shared
regression supplied independent reproduction evidence.

## 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 (affected database
regressions; full local suite has the host limitation described above)
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
canary/v2026.1027.0-canary.7
2026-10-27 11:34:18 -05:00
DottaandPaperclip 3be54534c2 perf(server): decode workspace run context keys once (#17898)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Managed Git workspaces retain task and run obligations until cleanup
is safe.
> - Activity, expiration, retention and rehome share a run association
query.
> - That query repeatedly extracted three keys from wide historical JSON
contexts.
> - This pull request decodes those keys together and projects the six
fields its consumers use.
> - Native bindings, cleanup vetoes, lifecycle locks and fresh deletion
checks stay in place.
> - The benefit is less database work while preserving the same cleanup
decisions.

## Linked Issues or Issue Description

**What happened?**

Workspace activity and eligibility queries repeatedly decompress large
run contexts. An OR predicate extracts issue, task and workspace
compatibility keys separately. The run scope also materializes columns
its lifecycle consumers do not read.

**Expected behavior**

Read the same relevant runs and preserve all deletion and rehome vetoes
with less repeated JSON work. Keep existing native, historical and
pointer bindings independent.

**Steps to reproduce**

Run the synthetic PostgreSQL comparison below against baseline
`db70c5acd92f50fa37fb4f0dea3da0e7685089a2`. Repeat the small, large-tree
and combined controls documented in the PR.

Related work: #17464 preserves workspace descendants. #17877
reconstructs native workspace obligations. This change preserves both
contracts and adds no scheduling change.

## What Changed

- Decode the three legacy context keys together with `jsonb_to_record`.
A strict root-object guard preserves scalar, array and null behavior.
Independent native and pointer bindings still match.
- Materialize the full textual tree-ID set once. This avoids inflated
recursive estimates triggering JIT compilation for the correlated
decoder.
- Project the six run lifecycle columns used by activity, expiration,
orphan retention and rehome.
- Add binding, lease, malformed-shape and fresh-read regression cases.
Add a reproducible four-control benchmark and detailed evidence
documentation.

## Verification

- Full repository typecheck passed on the integrated final source.
- Six relevant suites passed all 209 tests against real PostgreSQL,
including the 20 new binding/shape cases, 25 eligibility cases, full Git
expiration/restoration, task-directory retention and configuration, and
isolated repository rehome. These cover conflicting bindings, company
isolation, duplicate admission, pending cleanup, controller leases,
non-object contexts and newly active consumers.
- PostgreSQL 17.11 comparisons use two warmups and five alternating
samples per variant. Each sample runs eight sequential workspace
queries. Every complete caller result and six-column relevant-run
multiset matches.
- Stress fixture: 4,813 runs and about 332 MB of run storage, with
unique wide contexts. Last-activity median: 1,071.54 to 743.45 ms per
eight workspaces, 1.44 times faster. Closed eligibility: 814.29 to
713.86 ms, 1.14 times faster. Idle eligibility: 721.46 to 481.69 ms,
1.50 times faster.
- Small-history control improves 1.23–1.36 times. The 250-descendant
stress control improves 1.24–1.30 times. Combined
small-history/large-tree activity and idle checks improve 1.19–1.22
times. Combined closed eligibility is neutral: 61.11 to 62.82 ms per
eight, about 0.21 ms extra per workspace.
- Stress last-activity ranges do not overlap: 920.27–1,111.22 ms before
and 689.14–880.40 ms after. Closed-eligibility ranges overlap; its
smaller median gain is directional.
- Last-activity buffer accesses fall from 113,202 to 38,081. Eligibility
run width falls from about 1,414 to 57 planner bytes. All final plans
retain one history scan with no JIT.
- Reproduce: `pnpm --filter @paperclipai/server exec tsx
../scripts/benchmark-workspace-activity.ts
--baseline-ref=db70c5acd92f50fa37fb4f0dea3da0e7685089a2
--docker-postgres=17 --reject-new-jit
--output-dir=/tmp/workspace-activity-stress`.
- Repeat with `--small`, `--descendants=250`, and both flags, using
distinct output directories. Each run creates and removes its own
synthetic database.
- Full CI passed on `bc4edd700f983e22f093d0a01e2f4c795075b982`: 54
successful checks, one neutral review and two expected Storybook skips,
plus successful Snyk status. Greptile is 5/5 on this exact head. There
are zero unresolved threads and the merge state is clean. [CI
run](https://github.com/paperclipai/paperclip/actions/runs/40530494291).

- Full repository typecheck and build passed on the integrated final
source. The local full test command was stopped after native PostgreSQL
initialization failed repeatedly, producing 16 failures and skipped
database suites. It is incomplete, not a pass. Focused real-database
suites used disposable Docker PostgreSQL. Current-head CI passed all
full gates separately.

## Risks

- Cleanup safety depends on complete run association. Tests compare
every retained run field and exercise downstream vetoes, not only row
counts.
- PostgreSQL plan estimates matter. Rejected per-key index probes were
up to 56 times slower on a small-history/large-tree control. An ANY
variant also regressed. The final shape passes all four controls under
default JIT settings.
- The final combined eligibility control is neutral. Synthetic SQL
timings do not establish deployed latency, provider behavior or actual
compute sleep.
- Review suggested a CASE/jsonb_typeof root guard as an optional
alternative. The strict JSONPath guard is retained because its scalar,
nested-array and independent-native behavior passes real PostgreSQL
tests and all four measured controls. No correctness defect was
identified. A replacement needs its own comparative measurements.
- Future consumers that need another run column must extend this
explicit projection.
- No migration, index, startup job, polling change or deployment is
included.

## Model Used

OpenAI Codex, configured model `gpt-6.1-sol` with xhigh reasoning, tool
use and code execution. Independent blast-radius review also used
`gpt-6-astra` and `gpt-6.1-sol`. The runtime context window was not
exposed.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [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-27 11:23:09 -05:00
DottaandPaperclip 475f7df2e7 perf(server): prefilter chat milestone run candidates (#17892)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Chat publications connect a run to its authorized originating
conversation.
> - The milestone selector evaluates detailed lineage and final-answer
predicates.
> - A local plan evaluated these predicates across many unrelated run
and conversation pairs.
> - This pull request materializes recent runs that already match an
active or waiting conversation.
> - The existing query still makes each publication decision.
> - The benefit is less JSON work when most run history is unrelated to
active chat.

## Linked Issues or Issue Description

**What happened?**

The milestone candidate query repeatedly reads large run context values
for unrelated conversations. A synthetic baseline discarded 60,962 run
and conversation pairs through the complex join filter.

**Expected behavior**

Apply the existing same-company conversation binding before expensive
lineage checks. Preserve every candidate column, order, publication
decision and continuation rule.

**Steps to reproduce**

Run the two synthetic PostgreSQL comparisons below against baseline
`db70c5acd92f50fa37fb4f0dea3da0e7685089a2`. They capture complete SQL
from the actual service implementations.

Related work: #17606 changes chat lifecycle scheduling. #15802 deferred
milestone cadence. This SQL-only change leaves scheduling, startup
reconciliation and recovery intact. It has no dependency on those PRs.

## What Changed

- Materialize recent runs with an active or waiting same-company
conversation, using the unchanged native and legacy binding expression.
- Retain every outer lineage, continuation, final-response, privacy,
cursor, ordering and idempotency check.
- Add a real PostgreSQL historical-binding regression and
selective/dense performance controls.
- Document the measured gain and the dense-history tradeoff.

## Verification

- Dedicated binding, publication reconciliation and app coordination
suites passed all 27 tests across three files with real PostgreSQL where
required.
- The selective fixture has 2,615 runs, 55 conversation issues and about
192 KiB of pseudorandom context padding per run. Five alternating
measured samples follow two warmups on PostgreSQL 17.11.
- Final-harness selective milestone median: 1,495.85 ms before, 130.96
ms after, 11.42 times faster. Ranges: 1,483–1,716 ms and 125.59–135.85
ms. All 60 complete ordered rows match, including 15 explicit
historical-binding cases.
- Dense control: 736.71 ms before, 809.08 ms after, with overlapping
ranges. All 200 complete ordered rows match. This is inconclusive
performance evidence with possible materialization overhead.
- The unchanged native progress control is neutral in both fixtures. It
returns 41 equal complete ordered rows.
- The added large-window control has 12,215 runs, including 12,000
recent native history rows. A caller limit of one produces the real
service page size of 25. First-page medians 762.65 to 800.63 ms overlap
and are inconclusive. The 90% cursor improves 234.84 to 54.36 ms; the
99.99% tail improves 352.10 to 12.88 ms; the finished cursor improves
340.00 to 58.58 ms. Complete ordered results match: 25, 25, seven and
zero rows. Same-run later-conversation rows remain present.
- Large-window CTE materialization itself takes about 4.6–10 ms with
zero temp spill. This does not establish a materialization regression or
justify a larger production query rewrite.
- Reproduce the added window control with `--cursor-window` and a
distinct report path.
- Reproduce selective: `pnpm --filter @paperclipai/server exec tsx
../scripts/benchmark-chat-milestones.ts
--baseline-ref=db70c5acd92f50fa37fb4f0dea3da0e7685089a2
--docker-postgres=17 --report=/tmp/chat-milestones.json`.
- Add `--dense-chat` and a distinct report path for the dense control.
The harness applies normal migrations to a disposable container and uses
no hosted database.
- The existing real-service test at
`server/src/__tests__/chat-channels.integration.test.ts:34584` calls
enqueue with limit=1 four times, crosses 30 resolver-rejected
candidates, expects admissions [1,1,1,0], and asserts the exact three
publication keys plus internal-run exclusion. It passed and supplies the
full composition/publication-set proof requested in review.
- Assembled chat integration passed 37 selected tests. Together with the
27 focused tests, 64 checks passed. The selector covers bounded
draining, exact continuations, private summaries, final-answer
precedence, lifecycle idempotency and native progress races.
- Final head `1b3a6f50c9e7ec712dcbaf82c7fc3df141ec3f28`: [full CI
passed](https://github.com/paperclipai/paperclip/actions/runs/40534934737),
with 55 successful checks, two skipped and one neutral. Greptile is 5/5
on this head; zero unresolved review threads. Paperclip Review is 4/5,
neutral, explicitly with no must-fix correctness or security defect. Its
remaining payload-memory/dense-overhead caution is retained here as a
measured residual tradeoff; an identity-only projection needs production
evidence and separate comparison.

- After the Drizzle metadata correction, both generated service queries
and every parameter remain byte-identical to the selective and dense
measured versions. The real PostgreSQL binding regression passed again.
- Full repository typecheck and build passed on the integrated final
source. The local full test command was stopped after native PostgreSQL
initialization failed repeatedly, producing 16 failures and skipped
database suites. It is incomplete, not a pass. Focused real-database
suites used disposable Docker PostgreSQL. Current-head CI runs the full
gates separately.

## Risks

- A materialized source can add overhead when most recent runs match
active conversations. The dense control does not establish a gain.
- Historical JSON comparisons retain string and whitespace semantics.
Test fixtures disable the normalization trigger only while inserting
retained historical shapes in disposable databases.
- Synthetic SQL measurements exclude provider delivery and do not
establish deployed latency, actual compute sleep or fleet-wide cost
reduction.
- No schema migration, index, startup job, polling change or deployment
is included.

## Model Used

OpenAI Codex, configured model `gpt-6.1-sol` with xhigh reasoning, tool
use and code execution. Independent blast-radius review also used
`gpt-6-astra` and `gpt-6.1-sol`. The runtime context window was not
exposed.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [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-27 11:22:25 -05:00
DottaandPaperclip a26d217e94 fix(server): coalesce Cloud membership synchronization writes (#17890)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Cloud requests synchronize a human user's profile and company
membership.
> - Concurrent requests repeated the same writes before the debounce
could record success.
> - Upserts also updated unchanged rows and caused avoidable tuple work.
> - This pull request shares identical pending writes and guards
unchanged updates.
> - Each request still reads current authority. Bounded profile lookup
also reaches older memberships.
> - The benefit is less database work during request bursts with the
same actor results.

## Linked Issues or Issue Description

**What happened?**

A burst of 20 trusted Cloud requests issued 20 membership upserts and 40
profile plus membership tuple updates for one unchanged user. The
completed-write debounce did not share in-flight work.

**Expected behavior**

Identical in-flight requests share persistence work. Distinct contexts
retain their order. Failures remain retryable. Each request resolves its
own current company and role authority.

**Steps to reproduce**

Run the synthetic PostgreSQL comparison below at baseline
`db70c5acd92f50fa37fb4f0dea3da0e7685089a2`. The harness imports the real
resolvers and counts actual writes with database triggers.

Related work: #17663 covers adjacent Cloud authorization and projection
behavior. This change preserves fresh imported memberships,
owner-elevation settings and stale administrator removal.

## What Changed

- Serialize synchronization per database client and user. Share only the
last identical queued context. Preserve A/B/A transitions and retry
after partial failure.
- Guard user and membership updates with null-safe value comparisons.
Retain default-grant repair when `RETURNING` is empty.
- Keep authority reads outside the write debounce.
- Resolve exact principal IDs through the existing index, then use at
most one company-scoped alias query. This prevents both the old 200-row
profile cutoff and repeated membership-page scans. Exact IDs take
precedence over namesake aliases.
- Add concurrency tests, PostgreSQL tests, a reproducible benchmark and
behavior documentation.
- Accept both Git 2.55 and 2.56 diagnostic messages in an existing
workspace safety assertion, after full CI exposed that portability
failure. No expiration behavior changes.

## Verification

- Five focused suites passed, 63 tests total. These include Cloud actor
concurrency, transient database retry, company provisioning, real
PostgreSQL synchronization and the user profile route.
- The auth-to-profile regression returned 404 before the lookup repair
and 200 after it. The final fixture has 601 memberships and tests raw
and normalized non-UUID IDs, missing slugs, alias ordering and exact-ID
priority. A counterfactual with the old paging repair performs four
membership queries for a missing slug; the final code performs two, each
returning at most one row. Cross-company access returns 403 before
lookup.
- Local PostgreSQL 17.11 benchmark uses two warmup pairs and five
alternating measured pairs. Each pair starts a fresh debounce scope. All
20 complete actor results match on every sample.
- Burst median: 51.07 ms before, 25.71 ms after. Ranges: 47.21–63.25 ms
and 24.17–29.32 ms. Membership statements: 20 to one. Tuple updates: 40
to zero.
- Reproduce: `pnpm --filter @paperclipai/server exec tsx
../scripts/benchmark-cloud-membership-sync.ts
--baseline-ref=db70c5acd92f50fa37fb4f0dea3da0e7685089a2
--docker-postgres=17`.
- Real PostgreSQL tests compare all four alias fields with the route's
own JavaScript canonical slug output across 76 fixtures. Exhaustive
Unicode lowercase inspection validates the special SQL mappings.
- A real SQL_ASCII database reproduces the previous en-dash alias 404.
The final whole-character replacements precede ASCII-only translation.
Four alias fields and ten multibyte edge cases pass, including rejection
of a false euro-to-k alias. The disposable database is migrated
normally, then removed.
- Server TypeScript passed with no diagnostics.
- PostgreSQL tests used the normal migrations with a temporary Docker
startup override because this host's native PostgreSQL semaphore
allocation is exhausted. No tracked test infrastructure changed.
- The first full CI run had an unrelated CLI company-import/export
health-start timeout. Review also identified repeated profile scans;
this follow-up repairs that finding. A later run passed 3,882 tests in
server shard 4/12 and failed one existing assertion because Git 2.56
changed its error wording for a missing gitfile target. The safety
behavior still refused expiration and preserved the workspace. The
assertion now accepts both valid messages; the actual Git/PostgreSQL
regression passes locally. Final head
`3a644e600ab0ae7315f9828b1ea9dffdef407e4a` passed [full
CI](https://github.com/paperclipai/paperclip/actions/runs/40537153522),
including the previously failing shard. There are 55 successful
checks/statuses, two skipped and one neutral. Fresh Greptile and
Paperclip reviews are both 5/5 on this exact head, with zero unresolved
threads. GitHub reports mergeable and CLEAN.

- Full repository typecheck and build passed on the integrated final
source. The local full test command was stopped after native PostgreSQL
initialization failed repeatedly, producing 16 failures and skipped
database suites. It is incomplete, not a pass. Focused real-database
suites used disposable Docker PostgreSQL. Current-head CI runs the full
gates separately.

## Risks

- Coalescing is local to one process and database client. Other replicas
can still take conflict locks.
- `updated_at` now records actual profile and membership changes. Access
lists sorted by it can show a different order. Profile resolution no
longer depends on the first 200 entries. Exact principal IDs take
precedence over namesake aliases. The single alias fallback still scans
company candidates; no constant-CPU claim is made.
- Default role grants retain their existing additive semantics. This
change does not define a new demotion policy.
- Local synthetic service timings exclude HTTP and network latency. No
hosted deployment or production speedup is claimed.
- No migration, startup job, polling change or deployment is included.

## Model Used

OpenAI Codex, configured model `gpt-6.1-sol` with xhigh reasoning, tool
use and code execution. Independent blast-radius review also used
`gpt-6-astra` and `gpt-6.1-sol`. The runtime context window was not
exposed.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [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-27 11:21:44 -05:00
DottaandPaperclip 2f3516f6fd fix(github): authenticate external contributor lookups (#17902)
## Thinking Path

> - Paperclip manages AI agents and their access to external services.
> - A GitHub bot connection can add external contributors by username.
> - The lookup sent an anonymous request even when the bot had an App
installation.
> - Anonymous requests share the server IP quota and can fail for a
valid username.
> - This pull request uses the selected bot installation for the lookup.
> - It also gives users clear errors without exposing credentials or
provider response text.

## Linked Issues or Issue Description

**What happened?**

The Access tab could reject a valid external contributor username with a
generic GitHub error. The lookup did not use the connected bot's
identity.

**Expected behavior**

The lookup uses the selected bot's active App installation. It returns
the numeric user identity or a clear account, rate limit, or
installation error.

**Steps to reproduce**

1. Configure a GitHub review bot and open its Access tab.
2. Exhaust the server IP's anonymous GitHub quota.
3. Look up a valid external contributor username.
4. Observe the generic rejection even though the bot has an
installation.

The original provider HTTP status was not retained. Source inspection
proves the anonymous request. The historical cause remains unconfirmed.

**Paperclip version or commit**

The defect is present at `eda2a03bfe5213ffef2fbbdefdf572be775f1e14`.

**Deployment mode**

Server deployment with an active GitHub App bot connection.

## What Changed

- Pass the endpoint ID from the existing authorized lookup route.
- Issue a short-lived installation token with metadata read permission.
- Reject inactive connections and invalid installation IDs without an
anonymous fallback.
- Distinguish missing accounts, rate limits, and installation access
failures.
- Add 25 lookup tests and update the connector guide and both access
maps.

The GitHub management service owns the provider behavior. The shared
route only passes its authorized endpoint ID. The request body and
response shape stay the same.

## Verification

- Passed: [full
CI](https://github.com/paperclipai/paperclip/actions/runs/40535979115)
on `38d1ebcebb08553f159b3b81ee1bb31d1fbbba8e`, including typecheck,
build, server and workspace tests, Runner checks, serialized suites,
eight E2E shards, and the final verification gate.
- Passed: Greptile 5/5 for the same head, with no open review threads.
- Passed: 25 lookup regression tests on the final test fixtures.
- Passed: 91 tests across five GitHub service suites before the
fixture-only change.
- Passed: `node scripts/check-maps.mjs` and `git diff --check`.
- The tests exercise actual GitHub request construction with provider
response fixtures. They assert the installation identity, metadata-only
token request, request count, and sanitized error details.
- `pnpm build` and `pnpm -r typecheck` could not complete because this
environment has no Cargo executable.
- Two standalone server TypeScript attempts were killed. The second ran
after the full suite was stopped, with a 5120 MiB Node heap limit. The 8
GiB container recorded two OOM kills. Neither attempt produced a
typecheck verdict.
- `pnpm test:run` was started, then interrupted after resource
exhaustion. Its first server phase did not produce a final verdict.
These local attempts remain incomplete. Full CI later passed for the
same head. A live authenticated bot lookup remains unverified.
- The initial frozen install stopped on an existing patch configuration
mismatch. Installation then succeeded with lockfile writes disabled. No
dependency or lockfile changes are included.
- GitHub documents installation-token support for [Get a
user](https://docs.github.com/en/rest/users/users#get-a-user) without
extra user permissions.

## Risks

- The lookup now requires an active bot connection and a saved
installation ID.
- GitHub rate limits can still occur. The error keeps the retry metadata
and tells the user to retry.
- No database migration or permission-policy change is included.
- This PR does not prove that a deployment contains the fix.

## Model Used

OpenAI Codex, GPT-6 family, with code editing, tool use, and test
execution. The recovered implementation was assisted by `gpt-6.1-sol`.
This session does not expose its exact model ID or context window limit.

## 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-27 11:19:14 -05:00
DottaandPaperclip ac22b1b1f9 fix: preserve recovered input and task directory continuity (#17878)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/60/changes/paperclip/60
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip lets people manage AI agents and their work.
> - Recovery must preserve files and the authority of each human
request.
> - Some failed native runs preserve a snapshot without completing
native copyback.
> - The saved input could remain blocked after recovery settled.
> - A later turn could also treat the new task directory as a Git base
because it inherited the agent default.
> - This change continues the same authorized input once and keeps its
saved directory usable on later turns and Retry after a pre-provider
preparation failure.

## Linked Issues or Issue Description

Refs #14905. Related: #17724 covers cleanup handoff after native
copyback. This change covers snapshot-only preservation and later turns
in its directory. #17814 covers a separate same-run checkpoint restore
defect. The open legacy-adapter change in #13291 does not cover this
native saved-input path.

## What Changed

- Forward the trusted deferred inbound request to native continuation
admission. Verify the exact unconsumed receipt and its source, actor,
company, agent, and task.
- Let historical holds for the same failed run use its settled recovery
strategy. Keep each controller and termination check.
- Keep the old native sync proof unchanged. Attach recovered files
through the verified snapshot path in a fresh attempt.
- Recognize only the current same-task, active local directory in the
canonical task or attempt namespace. Keep it on later turns instead of
applying inherited Git defaults.
- Keep the current healthy directory when canonical Retry follows the
exact pre-allocation Git-base failure. Require the committed source
link, strict server-produced failure events, no execution evidence or
unfinished owner, and the current canonical binding. Recognize the known
configuration warning without accepting arbitrary or provider-origin
warnings.
- Keep owned-directory readiness separate from repository-free strategy
selection. A repaired real Git checkout with explicit
task/project/assignee Git policy uses normal Git validation and
preparation. Preserve explicit fresh, new sources, immutable native
bindings, and locked path/binding checks.
- Add real PostgreSQL and filesystem regressions for admission, file
preservation, and the complete later-turn heartbeat policy chain.

## Verification

- Real PostgreSQL callback/queue regressions are RED on unchanged
production and GREEN with this repair: one successor from the same
authenticated saved request, with filesystem snapshot preservation. The
24-case source/Retry matrix passes and retains termination, allocation,
generation and revocation negatives.
- Actual browser use exposed both later-turn Git-base failure and
canonical Retry selecting an empty sibling. The real heartbeat
selection/configuration/preparation/Retry regressions reproduce each.
All 81 workspace-preparation cases passed before the final repaired-Git
edge; all 17 final affected phase-chain/safety cases pass, including
that new case. Its finalized oracle checks effective Git policy, an
actual Git checkout and saved-file bytes. Known preparation warnings
pass; unknown/provider warnings and execution contradictions do not
certify reuse.
- Full workspace typecheck, full build with matching `5342bd52da` stamp,
maps and diff checks pass. Runtime commit `5342bd52da` passed CI with 53
successful checks and 2 conditional Storybook skips, and Greptile 5/5
with zero unresolved threads. The follow-up `9096fe8f62` removes only
the feature/access map additions as requested; runtime and test files
are byte-identical. Map structural and diff checks pass. Greptile
reviewed this exact final commit at 5/5 with no unresolved threads. All
test and build jobs passed. The final required `ci / verify` and `ci /
e2e` aggregate gates are queued without runners; one bounded gate-only
requeue retained every successful job. GitHub auto-merge is enabled and
waits for those gates.
- Normal UI acceptance on exact final source with actual OpenCode,
configured DeepSeek model and Daytona passes both turns: snapshot
continuation writes a useful review/report; reload and a second ordinary
message retain the same workspace and logical native/provider session.
All six files remain byte-identical, the report's computed SHA and
unpredictable marker are confirmed, the concrete result is actual 250
versus correct 350, native finalization/copyback commit, and normal
lease cleanup succeeds. A separate normal UI canonical Retry on
preceding `9ac1d29147` preserves the current directory/report/marker and
writes a continuity note; the final Git-policy split leaves that
exercised non-Git path unchanged.
- Boundaries: local app/server source with real remote native execution;
seeded historical failure/stop receipts and actual stored snapshot, not
a replay of the original staging allocation or external review
publication. Prior local verification rewrote installed dependency
metadata; its generated lock diff was retained and reverted. Final
launch/end source and dependency metadata hashes match; no installed
dependency byte-identity claim beyond that observed freeze.
- Required full local suite was attempted but remains incomplete:
embedded PostgreSQL bootstrap failures prevented assertions in several
suites, so only its owned test tree was stopped. A prior
workspace-recovery test timed out and the following seed collided with
an active hold; both unchanged tests passed in isolation. Runtime-head
`5342bd52da` hosted CI passed the full gate; the documentation-only
follow-up waits for the aggregate jobs noted above. Its [first Rust
attempt](https://github.com/paperclipai/paperclip/actions/runs/40528918372/job/122276159874)
failed an unchanged ACPX shutdown cleanup assertion; the preceding
same-Rust head passed. [One bounded Rust-only
retry](https://github.com/paperclipai/paperclip/actions/runs/40528918372/job/122281990961)
passed. No test assertion or production guard was weakened.

## Risks

- Incorrect input authority could repeat work. The existing owner
reauthorizes the same durable source; admission verifies its receipt
under the task lock.
- Directory recognition could select unrelated files. It requires the
current scoped binding and canonical namespace, then preparation
rechecks the stored binding and path under the source lock. Missing or
symlinked retained roots are not recreated.
- Snapshot preservation does not certify native copyback or old external
outcomes. The old run and its unknown outcomes stay unchanged.
Unconfirmed controllers or termination remain blocked.
- There is no schema migration or new public API.

## Model Used

OpenAI `gpt-6.1-sol` through Codex, with reasoning, code execution, and
tool use. The host does not expose the context-window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass — targeted suites; the full
local attempt limitation and final CI proof are documented 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
- [ ] 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-27 16:02:43 +00:00
DottaandPaperclip eda2a03bfe fix(ui): keep responder avatars visible through comment acknowledgement (#17885)
## Thinking Path

> - Paperclip gives people a shared place to manage and talk with
agents.
> - Task chat and Agent Chat show the responding agent as soon as a
person sends a message.
> - Comment storage and run discovery finish in separate requests.
> - Both chat surfaces removed the startup identity when comment storage
finished.
> - A slow run lookup therefore made the avatar disappear and appear
again.
> - This pull request keeps startup feedback until the run appears or a
newer execution outcome ends admission.
> - The agent stays visible while the response starts.

## Linked Issues or Issue Description

**What happened?**

The responding agent's avatar appeared after Send, disappeared when the
human comment was saved, and appeared again when the run loaded.
Existing conversations made the flash easy to see.

**Expected behavior**

Keep the agent identity visible across comment storage, queued
admission, and runtime startup. Clear temporary startup feedback on
failure, backoff, task completion, or a completed run.

**Steps to reproduce**

1. Open an existing agent conversation.
2. Send another message.
3. Let the comment POST finish before the task and live-run reads
finish.
4. Observe the responder identity disappear during that gap.

**Paperclip version**

Source baseline: `db70c5acd92f50fa37fb4f0dea3da0e7685089a2`.

**Deployment mode**

Local trusted mode with an isolated database. The browser test used the
real app, APIs, and native runner with a deterministic Codex provider.
It held read responses to expose the acknowledgement gap. It did not
call a live model provider.

Related: #4485 addressed human-comment alignment during session loading.
This change addresses agent-response startup.

## What Changed

- Share submission-to-run state between the task and classic chat
surfaces.
- Keep the identity mounted after comment acknowledgement. End startup
on a visible run or a newer admission outcome.
- Keep old execution projections and older loaded history from
acknowledging a new response. Retain slow-start refresh and
uncertain-save handling.
- Extend the startup tests through the missing acknowledgement interval
on desktop and mobile. Add existing-thread, paused-agent,
fast-completion, and clock-skew cases for both surfaces.
- Match completed runs to the saved comment and its server time. Use the
shared status rules and server-reported reporting-chain health for known
agent blockers and confirmed task pauses. Keep submitted feedback
visible through pause-state read errors. No browser/server clock
comparison is used.
- Clarify the identity continuity rule in `DESIGN.md`.

## Verification

- Red: the actual Send tests fail against the original components in
eight cases.
- Green: both chat suites pass, 420 tests total. Extra red/green cases
cover paused or ineligible agents, missing/terminated managers,
reporting cycles, browser clocks five minutes ahead or behind, and
pause-state read failures.
- Chromium on an existing conversation: the original avatar appeared at
58 ms and disappeared at 326 ms. On final commit `f9602e3e48`, the
avatar appeared at 68 ms and stayed visible through the same held-read
interval. Frame sampling found no disappearance after it appeared.
- Native fixture turns succeeded and stored their final provider
replies. Repeated the existing-thread turn and normal navigation/reload
on final commit `f9602e3e48`: both runs succeeded, both replies were
stored, and the conversation returned to its waiting state.
- `pnpm -r typecheck`, `pnpm build`, `pnpm check:token-gates`, and the
final UI typecheck passed.
- `pnpm test:run` was attempted. The host exhausted PostgreSQL
semaphores. The supported external test database let some suites run,
but other suites still attempted embedded clusters and failed or skipped
setup. The run was stopped after repeated setup failures.
- The broader UI run passed 9,251 tests and failed one existing
connection-authentication test. The same test fails on an untouched copy
of the base commit. This PR does not change that component or test.
Full-suite CI remains the complete-suite check.

- Final-head CI attempt 1 lost seven workers to shutdown or lost
communication and hit one timing assertion in an unchanged scheduler
test. Retried only failed jobs once on the same commit; the scheduler
test and interrupted jobs recovered. All retried jobs and both aggregate
gates passed. Final CI is green on
`f9602e3e48c75580935465eb0b4713fc7a4bcc56`.

## Risks

Low scope: this changes client startup feedback only. There are no API,
data, auth, or token changes. If run discovery is slow, the existing
slow-start message remains visible until current execution evidence
arrives. Completed runs are matched to the submitted comment by
consumption identity or server timestamps, so loading old history and
browser clock differences cannot end its feedback incorrectly.

## Model Used

OpenAI Codex, GPT-6 family. This session does not expose the exact
deployment ID or context-window limit. It used reasoning, code editing,
command execution, and Chromium automation.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 10:25:56 -05:00
DottaandPaperclip e71a23c01c fix(ui): animate late task sidebar loading (#17880)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/61/changes/paperclip/61
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task pages load the conversation and related sidebar data
separately.
> - The sidebar host did not exist until its content arrived.
> - Its first appearance skipped the width transition and moved the
thread at once.
> - This pull request keeps an empty host at zero width until content is
available.
> - The sidebar and thread now move together, with immediate changes for
reduced motion.

## Linked Issues or Issue Description

**What happened?**
A cold task load showed a wide skeleton. The right sidebar then appeared
at full width and shifted the thread at once.

**Expected behavior**
The sidebar should enter smoothly. The task thread should move with it.
Unknown or hidden sidebars should take no space.

**Steps to reproduce**
1. Open a populated task in a fresh browser context.
2. Delay the task metadata response.
3. Watch the skeleton and thread as the right sidebar becomes available.

Related work: [task cold-open
reads](https://github.com/paperclipai/paperclip/pull/17756).

## What Changed

- Keep the shared sidebar host mounted at zero width while its content
is unknown. Make hidden hosts inert and hide them from assistive
technology.
- Use shared motion tokens for width and opacity. Disable motion for
reduced-motion preferences and keep pointer resizing immediate.
- Clamp late content to the current viewport without overwriting the
saved width. Keep the motion catalog aligned with the reveal tokens.
- Clip the panel body during reveal. Keep the resize handle inside the
viewport during the first frame.
- Clear empty panes immediately when content is removed. Release
maximized geometry on that render. Remove the unused width/opacity
token.
- Add four replayable Storybook scenarios using the production skeleton
and panel components. Add browser checks for both arrival orders, absent
and hidden sidebars, reduced motion, resizing, and maximize/restore.
- Document the shared sidebar loading behavior in DESIGN.md.
- Load the comment-route test modules in suite setup. This prevents a
slow module load from timing out the first test and affecting the next
test. This test-only change fixes a reproducible CI failure; route
behavior and assertions stay the same.

## Verification

- 201 focused task-detail, properties-panel, and motion-catalog tests
passed. New host-continuity regressions failed against the original
component in both task interfaces.
- Eight focused Storybook browser checks passed without retries after
the review fix. They measure intermediate widths and matching thread
widths, check overflow, exercise resize and maximize controls, and
confirm immediate removal of docked and maximized content.
- The first removal checks sampled before replay cleanup committed.
Sampling now starts after the host becomes inert. All three removal
checks fail against the prior commit. Restoring the source during that
comparison left a stale Storybook module in the preview. After a preview
restart, all eight cases passed without retries on the fixed source.
- Verified a real populated local task in a fresh browser context with
delayed real API responses. Recorded a screencast for visual review. The
captured reveal had 20 intermediate widths, no horizontal overflow, and
no browser errors.
- Repository typecheck, full build, Storybook build, token gates, map
checks, and 30 catalog/map tests passed. Full typecheck, full build,
focused UI tests, and UI typecheck were repeated after the upstream
rebase.
- Full `pnpm test:run` was attempted and stopped after repeated embedded
PostgreSQL initialization failures. The host has exhausted its semaphore
pool. The local app test used a disposable container database instead.
- Replay `tasks-sidebar-loading--sidebar-during-skeleton` in Storybook.
Run the focused browser suite with `STORYBOOK_TEST_BASE_URL` set to that
Storybook server.

- CI serialized shard 6/9 failed when the first comment-route test hit
its 15-second timeout. Its work then affected the next test's mock
count. Both failures reproduced locally on Node 24. After moving module
loading to suite setup, all 103 tests and server typecheck passed with
the same test timeout. A first local attempt could not bind the
Supertest listener inside the sandbox; it was repeated with local
network access.

## Risks

- The shared host also serves other properties panels. Hidden
preferences, default widths, pointer resizing, maximize/restore, and
both task interfaces have focused coverage.
- Width animation can reflow text while the panel enters. Reduced motion
removes the transition. Navigation removes empty panes immediately.
- Visual approval is complete. The full CI suite must pass before merge.
No schema or API contracts change.

## Model Used

- OpenAI GPT-6 through Codex, with reasoning, code execution, and
browser tools. The exact deployed 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
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 10:10:45 -05:00
DottaandPaperclip 663fe667d6 fix(recovery): reconstruct missing native workspace obligations (#17877)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A failed native run can still own prepared files in a remote
sandbox.
> - Cleanup must preserve those files before another executor can start.
> - Older failure producers can omit the preservation record.
> - A saved message then waits for cleanup without a recovery owner.
> - This change reconstructs that record during user continuation and
orphan cleanup.
> - The existing worker saves the files, verifies stop, and returns
input to normal admission.

## Linked Issues or Issue Description

**What happened?**

A failed native run retained a prepared workspace and an active sandbox
lease. It had no file-preservation obligation. Cleanup protected the
files. The next user message waited for a stop receipt that no worker
owned.

**Expected behavior**

A new message registers the missing maintenance work at once. Existing
orphan cleanup recovers historical saved input. Ordinary Retry remains
available. Saved input can start once, after verified preservation and
containment.

**Steps to reproduce**

1. Persist a failed native run with an exact prepared remote workspace
and its active lease.
2. Leave its finalizer terminal and unowned. Omit the workspace recovery
record.
3. Send a user comment or run orphan cleanup.
4. Observe that no preservation worker starts and the comment remains
deferred.

**Paperclip version or commit**

Reproduced on `bd382eee536f4ada680072b1cc19100163c3d04a`.

**Deployment mode**

Native Runner with a remote sandbox. Local verification uses disposable
PostgreSQL 18 and real temporary workspace files.

Refs #17788 and #17800. Those fixes preserve future deadline failures
and register historical preservation on Retry. This change adds
automatic recovery for new and saved messages. Refs #17839 for the
separate restart-failure producer repair.

## What Changed

- Register missing preservation before an authenticated continuation
waits for remote cleanup.
- Reconstruct the same exact-source obligation during the existing
orphan lease sweep.
- Notify the existing preservation worker in the registration
transaction. Do not add a timer or history scan.
- Test ephemeral and reusable sources, live owners, active executors,
mismatched sources, rollback, lock contention, Retry and saved-input
delivery.
- Document the recovery contract and feature-map behavior.

## Verification

- Red: both new orphan-recovery cases fail on unchanged production code.
Four ownership and source negative controls pass.
- Red: a new comment starts no preservation work without the admission
repair.
- All 461 test cases across five affected suites pass across the local
runs. One existing 30-second timing case timed out under concurrent
verification; its isolated rerun passed with the same deadline and
assertions.
- The three final-head entry journeys pass again: HTTP Retry, HTTP
comment creation, and historical saved-input recovery.
- `pnpm -r typecheck` and `pnpm build` pass on final head
`8fab8b2ba9049f640fd884094a2007a69465e6da`. The affected server
typecheck and build also pass.
- The integration journey holds the provider stop reply, checks that no
successor starts, preserves real files and newer host edits, and checks
one saved-input successor.
- Module-boundary checks, map checks and `git diff --check` pass.
- Native macOS PostgreSQL cannot start because host semaphore capacity
is exhausted. Database verification uses separate disposable PostgreSQL
databases for each suite. An earlier shared-database attempt caused
fixture interference and is not counted as a pass.
- Greptile gives 5/5 with no findings or unresolved threads on
`8fab8b2ba9049f640fd884094a2007a69465e6da`. All final-head GitHub checks
are settled: 55 passed, two Storybook checks skipped, and one neutral
Paperclip review. Full CI test, build, typecheck, Runner and browser
lanes pass. No full local `pnpm test:run` pass is claimed. The fix is
not deployed. This PR makes no claim of a live hosted model turn or
recovery of the original incident.

## Risks

- Registration grants maintenance authority only. Existing coordinator,
descriptor, allocation, stop, file-receipt and execution gates remain
effective.
- A locked source remains held. Its one-second lock timeout does not
prevent an unrelated source from registering recovery.
- New preservation writes wake existing work. Settled sources produce no
new recurring work. Historical discovery uses the existing orphan
cleanup lifecycle and its backoff.
- Provider lifecycle is simulated in local integration. PostgreSQL, HTTP
routing, file export/merge, the worker and queue admission are real.
- No schema, credential, model, trust, retry-budget or assignment
change.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository inspection, tool
use and code execution. The runtime does not expose the exact model ID
or context-window size.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
canary/v2026.1027.0-canary.6
2026-10-27 09:50:50 -05:00
DottaandPaperclip b8f6d9b23f docs: clarify delegated connection feature and access maps (#17882)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The feature and agent access maps describe user journeys and their
permission boundaries.
> - PRs #17737 and #17739 added delegated connection setup across
several existing areas.
> - The maps include those operations, but some recipes and board-only
statements are stale.
> - This PR adds usable setup recipes and corrects the access
descriptions.
> - The benefit is a clearer verification path that preserves human and
agent authority boundaries.

## Linked Issues or Issue Description

Refs: #17737, #17739.

Related: #17862 changes connection setup advice and catalog choices.
This PR only clarifies the existing maps.

**Issue type**

Outdated and missing documentation.

**Where is the issue?**

The paired connection setup, app permissions, chat channels, model
accounts, and gateway maps under `maps/`.

**What's wrong?**

Delegated setup guidance sits under Gotchas without a matching driving
recipe. Some model-account text still groups supported account sign-in
with board-only host controls. A few callback, privacy, and native
admission descriptions omit important limits.

**Suggested fix**

Add the missing recipes. State the current route and service boundaries.
Keep existing capability IDs and distinguish source review, fixture
tests, and live provider proof.

## What Changed

- Add paired entry points and driving recipes for delegated setup,
permission changes, and managed AI sign-in.
- Describe Google consent restoration, GitHub requested credential
fields and saved rotation, and Vercel personal credential attribution.
- Correct the Vercel callback state checks and chat
conversation/test-status privacy scope. Name the separate GitHub
registration handoff endpoint.
- Replace blanket board-only AI login wording with the specific
managed-account and host/session boundaries.
- Link the five access maps to shared native admission and work-mode
limits. State the permitted self-targeted policy administration
boundary.

## Verification

- `node scripts/check-maps.mjs` passed: 35 paired areas and 192 stable
capability IDs. This checks structure and references.
- `node scripts/storybook-catalog.mjs` passed. Generated output remains
ignored.
- `git diff --check` passed.
- Eleven selected cases in
`server/src/__tests__/connection-management.integration.test.ts` passed
on source `db70c5acd9`, the unchanged base of this documentation commit.
They use a fresh PostgreSQL database, actual agent keys/run JWTs, human
sessions, and deterministic provider fixtures. The other 61 cases were
not selected.
- The selected cases cover connection/profile creation, Google saved
choices, native API entry points, Slack caller/target policy, GitHub
import and identity confirmation, durable gateway authority, AI
accounts/pools, saved human handoffs, revocation, scope, trust, and
Ask/Plan denial.
- Read-only source reviews checked all five areas. The disposable
database was removed after the run.
- Full workspace typecheck, build, and test sweep were not rerun for
this Markdown-only change. Live provider login and browser journeys were
not run. Per-operation source-only/not-run labels remain unchanged.

## Risks

Low risk. Incorrect documentation could misstate a permission boundary
or imply broader verification. The changes cite the owning routes and
services and retain fixture and live-provider limits. No runtime,
schema, lockfile, or workflow files change.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository tools, code
execution, and read-only source-review agents. The session does not
expose a more specific deployment ID or context-window size.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 09:40:41 -05:00
DottaandPaperclip db70c5acd9 fix(runner): retain startup Stop and exhaust reconnect recovery (#17812)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators must be able to stop a remote runner while it starts.
> - Remote artifact staging can finish after the transport's bounded
cleanup wait.
> - The launcher discarded an early Stop request and could dispatch
after staging.
> - A separate path missed the runner's plain-text reconnect expiry
receipt.
> - This change retains startup cancellation and ends an exhausted
reconnect incident.
> - Public package imports and a source-bound profile keep the
regressions buildable and admission checks intact.

## Linked Issues or Issue Description

Related work: Refs #16835, Refs #16843, Refs #16855.

**What happened?**

The HTTP Stop route returned 200 and recorded a cancelled run. After
bounded bootstrap cleanup, releasing artifact staging still dispatched
one launch RPC. A separate reconnect test accepted a replacement after
the runner reported that its grace period had expired.

The first PR head also failed CI. The HTTP regression imported Runner
source across the server TypeScript boundary. Its event receipt mock
read a field that is not present on both event forms. The changed
transport also invalidated the pinned Copilot source identity.

**Expected behavior**

A cancelled launch must remain cancelled after staging. An exhausted
reconnect receipt must end that incident. Regression tests must use
public package contracts. The source identity must cover the changed
transport without replacing historical profile bytes.

**Steps to reproduce**

1. Hold the remote launcher's artifact staging promise.
2. Call the board Stop route and let bootstrap cleanup reach its
preparation deadline.
3. Release staging and count launch RPCs.
4. For reconnect expiry, end an owned runner with its coded or
plain-text expiry message.
5. Run the server typecheck and the Copilot profile identity check.

## What Changed

- Retain cancellation through staging, trace publication and an
in-flight launch RPC.
- Apply a deferred signal through the existing identity checks when the
launch identity arrives.
- Preserve an earlier KILL request when a later TERM request arrives.
- Recognize both coded and plain-text reconnect expiry receipts.
- Test the real board Stop route and heartbeat cancellation with
PostgreSQL. Use public Runner exports and handle both event receipt
forms.
- Generate source-bound Copilot profile v65 after integrating current
master. Preserve exact published v62, v63 and v64 fixtures, retain the
earlier v63 reconnect candidate as historical evidence, and reject
historical profiles for a current launch.
- Document process evidence, retained uncertainty and the limits of
cleanup and continuation evidence.

## Verification

- At current integration head
`2b96ee3adcb3be642c538436113e1159d92f843d`, launcher and HTTP Stop
regressions: 21 passed. This includes the existing process-generation
and exact-exit safety cases on the integrated base. The HTTP case
includes bootstrap expiry; it is not isolated Stop-only settlement
proof.
- At current integration head, reconnect expiry regressions: 2 passed,
for coded and plain-text receipts.
- At current integration head
`2b96ee3adcb3be642c538436113e1159d92f843d`, the Copilot installation and
identity suite passed all 63 tests and the canonical generator check
passed. Before regeneration, the merged-tree admission test failed with
a transport source hash mismatch and the generator reported a stale
identity. Published v62, v63 and v64 bytes remain unchanged; the prior
reconnect candidate is preserved separately. The v62 fixture SHA-256
remains
`faa4d1b35267b54e134a8e0c84586fe1196d8fad5525cb53aa195aae53ca6e83`.
- Current integration head passed all 86 focused tests, the full
recursive repository typecheck and the root repository build. The normal
server typecheck also passed, including its full Runner release rebuild.
- Historical full recursive repository typecheck and the root repository
build passed at `187bb85b4fa1fbd2ffaec992650a1c8557df9ebe`. Server
typecheck also passed through the normal package script and a direct
full server TypeScript check. The prerequisite full Runner release build
passed.
- The historical HTTP baseline returned 200 and dispatched one late
launch RPC. The repair returned 200 and dispatched zero in the bounded
fixture.
- Earlier failures remain part of the evidence: the first head failed
TypeScript package boundaries and source identity. One local HTTP run
failed before migrations; it passed after the disposable test database
received normal migrations. Earlier sandbox-wide checks ran out of
memory or lacked Cargo. Those runs did not pass.
- Historical head `187bb85b4fa1fbd2ffaec992650a1c8557df9ebe` received
Greptile 5/5 with no actionable findings and all hosted gates passed in
run `40412261957`: 53 successful check runs, four expected skips and a
successful legacy Snyk status. Those results predate the master
integration and do not establish current-head readiness.
- Current integration head `2b96ee3adcb3be642c538436113e1159d92f843d`
includes master `bd382eee536f4ada680072b1cc19100163c3d04a`. All
current-head hosted gates passed in run `40512885858`: 53 successful
check runs, four expected skips and a successful legacy Snyk status,
with zero pending or failed checks. Build passed at 13:42:36 UTC and the
final `ci / verify` aggregate (job `122224566238`) passed at 13:43:00
UTC on 2026-10-27. Greptile reviewed exact head
`2b96ee3adcb3be642c538436113e1159d92f843d` at 5/5 (updated 2026-10-27
13:36:41 UTC), with no actionable findings and no open inline threads.
The hosted partitions passed full-suite verification, including browser
and release gates. The wider qualification limits below remain in force.
- Local setup on current master initially failed frozen install with
`ERR_PNPM_LOCKFILE_CONFIG_MISMATCH` because `patchedDependencies`
differs from the lockfile. Pinned pnpm 9.15.4 hydrated dependencies with
`--no-frozen-lockfile --ignore-scripts`; its generated lockfile change
was restored. This PR changes no lockfile or workflow.
- Reproduce the Stop tests from `server`: `pnpm exec vitest run
src/services/native-runtime/native-session-executor.test.ts -t 'remote
runner process supervision|HTTP Stop during remote runner staging'`. The
test accepts a guarded disposable `_test` database through
`PAPERCLIP_TEST_DATABASE_URL`, or starts its normal embedded database.
- Reproduce reconnect tests from `packages/paperclip-runner`: `pnpm exec
vitest run src/live/runnerd-codex-transport.test.ts -t 'does not restart
an exhausted reconnect'`. Build the normal test runner first.
- Reproduce profile checks from `packages/paperclip-runner`: `node
scripts/copilot-profile-identity.mjs --check` and `pnpm exec vitest run
src/drivers/acpx/copilot-installation.test.ts`.

## Risks

- The wider qualification is incomplete. Verified-stopped replacement
and concurrent recovery plus Stop each entered two physical provider
processes. Both fail the original requirement of at most one physical
provider entry. Passing fixture assertions do not make all incident
scenarios accepted.
- HTTP concurrent recovery, shared provider-attempt accounting,
server-admitted restored continuation, live-provider behavior and
product cleanup settlement remain unrun or unproved.
- Accepting a deferred signal is not proof that a dispatched process
stopped. The existing monitor remains authoritative.
- The HTTP test uses the real route, heartbeat cancellation and
PostgreSQL state. Execution lease and event ports, plus the launch RPC,
are controlled fixtures. Bootstrap expiry participates in this case.
- The local incident matrix uses an installed runner binary and a
scripted provider. It does not qualify a live provider. Local fixture
teardown does not certify product cleanup settlement.
- This change does not authorize historical turn replay. It does not
change ownership, pause, approval or provider-attempt gates.

## Model Used

OpenAI Codex, GPT-6 family. The runtime does not expose the exact
serving model ID or context-window size. The agent used reasoning,
repository inspection, shell execution and connected GitHub tools.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 08:45:00 -05:00
DottaandPaperclip bd382eee53 fix(recovery): explain retained source verification failures (#17773)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Workspace recovery must prove the identity of retained work before
it uses an allocation.
> - The verifier rejects several different contracts with the same
`source_binding_invalid` code.
> - A saved Retry can therefore stop before file preservation without
enough evidence for a safe repair.
> - Operators need a bounded observation of the selected source without
another attempt or weaker guards.
> - This pull request records closed failure reasons and adds an
explicit read-only inspection on the run page.
> - The benefit is a precise next diagnostic step while the saved work,
intent, and ownership fences remain intact.

## Linked Issues or Issue Description

Refs: #17741, #17053. This is a diagnostic follow-up. It does not change
recovery admission or the retained-source acceptance policy.

**What happened?**

A retained-source recovery exhausted in `verify_execution` with only
`source_binding_invalid`. Run and task diagnostics could not distinguish
an invalid reference, lease policy, source binding, or reopened
allocation. The normal recovery-actions read can also revalidate
recovery state, so it was not a safe substitute for a current read-only
inspection.

**Expected behavior**

Record an allowlisted reason at the rejected guard. Allow a reader to
inspect one exact task/run/action without changing its attempts, saved
input, receipts, or ownership. Clearly separate the recorded failure
from current identity observations.

**Steps to reproduce**

1. Prepare a terminal native run with a retained-source action and a
lease policy of `reuse_by_environment`.
2. Attempt source verification. The guard must still reject that policy.
3. Open the failed run and choose Inspect recovery, then Inspect current
source. The diagnostic should name `lease_policy_not_retained` and the
allowlisted policy.

## What Changed

- Split existing source-binding guards into closed reason codes. Keep
each acceptance predicate and physical recovery action unchanged.
- Persist validated, path-free reason/policy detail with future system
recovery failures. Keep historical generic records compatible.
- Add an explicit query on the existing task recovery-actions GET. Exact
company/task/run/action selection bypasses read revalidation. Run
selection lists at most 20 actions; inspection stops before per-lease
lookup when a record has more than eight sources.
- Add a manual inspection control on native export-failure run pages.
Keep resolved retained actions inspectable. Show recorded failure and
current recorded-source identity separately.
- Update execution semantics and feature/access recipes. The shared type
is the normalized server/UI contract; it contains no provider payload or
secret.

## Verification

- RED: the prior verifier failed both reason assertions for rejected
warm policy and foreign-run lease binding. GREEN: the final focused
source/HTTP, system-state, and UI suites pass 23 tests. The UI follow-up
passes 8 tests after the review fixes.
- HTTP tests prove exact selection, cross-company denial,
resolved-action access, reused-allocation rejection, redaction, and
unchanged actions, runs, leases, tasks, wakeup receipts, workspace
operations, and activity. A nine-source case proves bounded inspection
while the existing worker verifier still accepts all nine valid sources.
- Review follow-up: show allowlisted saved detail beside a changed
current observation, preserve all listed actions, and follow
exact-action selection in verified/limited/error tests. The prior
component fails the saved/current drift regression in an in-memory RED
control; the current component passes. UI typecheck/build pass again.
- `pnpm -r typecheck` and `pnpm build` pass. Token gates, maps, module
boundaries, and `git diff --check` pass.
- Rebased onto master `46de15ff4b` without conflicts. Range-diff proves
both diagnostic commits are unchanged. On new head
`a7e2bc87fcea75e76b6b736304d1287257f71174`, all 23 focused tests, full
typecheck/build, token gates, maps, and module checks pass again. Both
fresh reviews are 5/5 with zero unresolved threads. Final exact-head
GitHub status is CLEAN: 55 success, 2 skipped, 1 neutral, no pending
checks or failures. The [ci / verify
aggregate](https://github.com/paperclipai/paperclip/actions/runs/40387002325/job/121802415824)
passed in eight seconds. All test/build/typecheck/E2E lanes passed.
- Existing full workspace recovery suite: 56/56 pass (103.5 seconds)
before the byte-equivalent rebase. `pnpm test:run` hit repeated embedded
PostgreSQL initialization failure before database-suite assertions and
was stopped. No full local suite pass is claimed. The owned external
`_test` database provided the focused HTTP/source verification. The
prior browser attempt also stalled at Network.enable before its click;
that attempt did not establish browser acceptance.
- Current-head local browser acceptance passes through the actual run
page: Inspect recovery lists the exact action, Inspect current source
shows recorded generic failure separately from the current rejected
policy, and reload resets inspection to the explicit control. Explicit
selection works again after reload. The app used isolated data, an empty
inherited environment, a paused synthetic agent, and no provider
credentials. Final state remains failed/exhausted with zero attempts,
zero wakeup receipts, zero workspace operations, and unchanged original
generic failure. The local app and temporary tab were stopped. This
proves the diagnostic journey, not the deployed source's actual lease
policy or its recovery.
- Final merge verification at
`a7e2bc87fcea75e76b6b736304d1287257f71174`: GitHub reports CLEAN and
mergeable. Both repository-required checks, `ci / verify` and `ci /
e2e`, pass, as does the exact-head Greptile check (5/5, zero unresolved
threads). The separate [Entire
trail](https://entire.com/gh/paperclipai/paperclip/trails/7) has no
recorded reviewer approval; no such approval is claimed. It is not a
configured GitHub merge gate.
- Initial local setup failures were distinct from product assertions:
localhost sandbox EPERM required scoped local access; a browser harness
import path was corrected. A test UUID type inference error and
historical native-export UI selection gap were corrected before the
green run. The live task remains blocked; this diagnostic does not claim
it recovered.

## Risks

- This change improves evidence. It does not prove provider termination,
local descriptor integrity, or successful file preservation, and it does
not repair the original source.
- Current inspection spans multiple database reads. It is an
observation, not a historical cause or execution authority. It performs
no provider/filesystem calls and starts no retry or executor.
- The optional persisted detail is backward compatible. Unknown values
and extra fields are excluded. Oversized source lists remain uninspected
through this explicit diagnostic path; physical recovery rules do not
change.

## Model Used

OpenAI Codex based on GPT-6, with code execution and browser tools. The
exact serving model ID and context window were 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-27 08:21:28 -05:00
DottaandPaperclip 0c9d10d695 feat(connections): complete saved setup guidance and coverage maps (#17739)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Product and agent access maps describe the same capability IDs.
> - Agent connection management changes the callable operations and
permission boundaries.
> - Reviewers need an operation inventory that distinguishes API access,
human handoffs, and fixture evidence.
> - This pull request adds saved setup screens and updates the maps for
#17737.
> - The benefit is that users and contributors can inspect the coverage
and remaining boundaries.

## Linked Issues or Issue Description

**Subsystem affected**

Product and permission documentation, saved authentication setup UI and
consent metadata, and product/agent access maps.

**Problem or motivation**

The current maps describe board-only management paths. They need to
match delegated connection management and distinguish administration
from ordinary tool execution.

**Proposed solution**

Record route operations, required grants, responsible-user limits,
transport, saved authentication handoffs, provider-fixture evidence, and
explicit local-operator boundaries for every connection family.

**Roadmap alignment**

This is the documentation companion to #17737. It covers existing
Connected Apps, governed MCP gateways, chat channels, and
personal/shared AI accounts. It uses existing server setup operations
and adds their saved handoffs to the existing UI.

## What Changed

- Show saved AI sign-in and connection setup handoffs in the existing
UI.
- Open GitHub import and rotation links at the required human step.
Resume a committed rotation after reload without asking for another
secret.
- Restore saved Google service choices for the addressed user while
preserving newer consent. Successful direct/cloud consent records its
own timestamp, independently of refresh or lease writes; existing
refresh paths preserve it.
- Update product, implementation, and permission-default documentation.
- Reconcile app permissions, connection setup, chat channels,
profiles/gateways, and model account coverage.
- Use matching capability IDs in the product and agent access maps.
- Label source-reviewed operations separately from fixture-tested
operations and live provider setup.

- Preserve catalog creator attribution under the responsible user, with
the actual agent still recorded. This follow-up moved from the
implementation to keep both reviews below 100 files. This companion now
changes 38 files, including the preserved Vercel personal-credential and
OAuth callback attribution follow-ups alongside consent and catalog
ownership.

- Name the addressed-human browser-session and consent step for Slack
managed authorization and installation. Preserve source-only evidence
for those individual operations.

## Verification

- Current companion head: `04d40cdd605452d91cf4931476eb292a210825af`,
including implementation `2ecd6451d0` and master `ea727dc3c2`, 38 files.
All intended companion files survive: 35 are byte-identical to the
previous companion; two chat maps and qualification add current
evidence. Exact-head Superagent passed and Paperclip Review supplied an
actual neutral5/5 assessment with no findings and explicit
no-independent-runtime/live/current-CI verification limits. Exact-head
Greptile also completed at5/5 with no findings; both formerly failed
shards and their verification aggregate passed on attempt2; full
exact-head Linux CI workflow40446969603 completed SUCCESS. All current
checks and statuses are terminal clean, with no unresolved findings.
This PR remains dependent on #17737 and needs fresh checks after its
required post-squash replay. The preceding 605 head’s reviews are
historical after this update. Older b0ea Linux workflow success after
its single rerun also remains historical.
- Earlier preserved-stack verification passed **2,165 tests**: 70
assembled-server actual-auth scenarios, 27 OpenAPI contracts, 2,022
native API tests and 46 saved GitHub wizard cases in fresh PostgreSQL.
The parent also passed **141 existing gateway/policy tests**. Workspace
typecheck/build, maps and token gates passed. The initial typecheck IPC
denial and all material intermediate regressions remain in the
qualification record; no full local sweep or live provider setup is
claimed.
- Operation maps now cover issuer/target gateway policy intersection and
post-approval revocation, bounded activity pagination through
private-only windows, and native local-login encrypted receipts. The
companion preserves the Vercel OAuth callback ownership change with its
existing credential ownership follow-up.

- Parent CI has one retained connection-intent e2e failure from a
periodic-recovery timing race. GitHub refused the first job-rerun
attempt while its workflow was nonterminal. Once completed, the single
failed-job rerun was accepted as attempt 2 and passed, including the
full parent Linux workflow.

- Companion Linux server shard 4/12 retained a cleanup-idleness timeout
in `login-cleanup-work.test.ts` (active work was still 1 after its
five-second wait). The same unchanged cleanup suite passed all 16 cases
in the parent Linux job 121950185096 and all 16 cases locally against a
fresh disposable PostgreSQL database. Relevant cleanup/reaper/scheduling
runtime code is unchanged. The single unchanged-code failed-job rerun
was accepted as **attempt 2** on the previous `b0ea3c940f` head
(workflow 40431671705). The failed shard and complete Linux workflow
passed on attempt 2; no additional unchanged-code rerun was used for
this failure. The first local attempt failed the existing dedicated
`_test` database-name guard before running tests; the correctly named
fixture passed without weakening the guard.

- **Earlier 82 GitHub UI tests passed**. All three new replacement cases
were red before the fix. A confirmed unchanged generation preserves the
earlier copy; a changed or unreadable generation blocks copying and
reconnecting. An explicit status check can restore the copy when it is
still current. All earlier import, rotation, verification, and retry
states remain covered.
- The complete stack passed **82 API/OpenAPI tests** in fresh
PostgreSQL, including 55 scenarios with real agent keys, run JWTs, human
sessions, production defaults, persistence, and deterministic providers.
The parent also passed **60 Copilot installation tests**, preserving
published v62 and binding combined guidance as v63.
- **Earlier 10 focused Google/OAuth tests passed**: eight picker cases
and two local-HTTP protocol cases proving consent metadata survives
concurrent token refresh. The refresh and legacy UI cases were red
before the fix. These protocol fixtures use the existing injected board
actor and do not count as agent-authenticated proof.
- Workspace typecheck/build passed after the consent change; normal
migration generation reported no SQL changes for the additive JSON
metadata. The parent's latest caller-authorization correction passed
server typecheck/build. Map validation and token gates passed. Maps
cover 35 areas and 192 capability IDs. The specification names migration
`0380_broken_aqueduct.sql`.
- The test-call operation maps now record the full caller/target tool
access decision (profiles, policies, and explicit grants), credential
identity for every auth label, approval, and rate checks. The
profile-only precondition was removed after the new permitted-access
cases failed; those cases and the no-auth identity regression now pass.
Saved test status requires its originating principal and current
linked-task access; actual key/JWT privacy-denial evidence is recorded.
- Tool-connection activity is now operation-verified: 100 events over
two tasks require two capability decisions and at most two run captures
(previously 100/102), preserve private-event filtering, and deny the
next request after audit revocation. The regression was red before the
fix.
- Cloud enrollment creation and callback entries now name
instance-administrator and company/principal checks. The callback is a
browser return with HTML or a 303 redirect. Status reads retain their
separate board-actor rule.
- Operation maps identify the API, transport, authorization, evidence,
and human/operator boundaries. Structural checks do not establish live
provider behavior. Earlier failures and fixture corrections remain in
`doc/plans/2026-10-13-connection-api-parity.md`.
- An earlier companion head (`26edbe17`) received an actual Paperclip
Review assessment of 5/5 with no actionable findings, published with
neutral status. That review did not run tests or confirm CI, and it is
historical for this new head. The prior incomplete execution result is
not counted as a passed assessment. Fresh review remains pending. The
duplicate enrollment findings have explanatory replies and are resolved.
- The latest implementation correction passed all 55 actual-auth
scenarios and 27 OpenAPI contracts (separate commands). It repairs
unavailable old credential material, preserves fresh import versus
configured reconnect links, and distinguishes credential field
sets/completion modes in handoff keys. The new fresh-link and collision
cases were red before the fix. All 41 GitHub wizard tests now pass,
including filling the fresh saved handoff and asserting App import
rather than reconnect; UI typecheck, maps and token gates also pass.
Qualification retains the earlier failures and an incorrect OpenAPI
filename that selected only the integration module before the correct
separate contract run.
- Configured GitHub credential-entry links carry canonical requested
fields and all/any mode. The form displays and submits those values
through human reconnect while retaining other saved credentials. Two
webhook/client-secret cases were red before the fix; combined all/any
and legacy-link cases also pass. The current UI fixes the old bf760912
baseline referenced by the duplicate main review finding.
- After merging master baec3abd67 and regenerating migration0380, the
complete stack passed **128 tests** (55 actual-auth, 27 OpenAPI and 46
GitHub wizard cases), workspace typecheck/build, maps and token gates.
Eight current Google UI cases also passed. The two local-HTTP OAuth
cases could not initialize embedded PostgreSQL in this fresh attempt, so
that attempt is not a protocol pass; the earlier ten-case evidence is
historical and fresh Linux CI remains required.
- The reproduced Linux automatic-Slack challenge failure is fixed by
retaining verification only for the same signing fingerprint and
callback URL; all eight neighboring provider compatibility cases pass.
Qualification also retains the uncommitted GitHub field variant that
failed initial-secret compatibility before the extension was narrowed to
reconnect, and the first unmigrated external-database diagnostic.
Upstream 0379 remains intact; normal generation/pruning produced
additive/idempotent 0380, with repeated application verified.
- Both PRs must ship together. The user authorized merging when checks
pass. Merge #17737 first, replay only this companion's changes onto
master, retarget this PR, and require fresh current-head CI and review
before its squash merge.

- The updated maps distinguish authorized bot-token/API-key setup from
human verification-key entry and forbid generic channel secret-reference
replacement. Actual key/JWT security baselines were red before both
guards; all 82 API/OpenAPI cases, eight native receipt protections, and
49 policy-service tests now pass. The qualification record preserves the
initial lifecycle/fixture/union-assertion diagnostics and the preceding
Linux CI assertion failures. Maps pass on this updated stack. Fresh
exact-head CI and reviews remain required.

- The parent now preserves a per-field credential-change boundary before
retiring saved credential-entry cards. Old health, unchanged entry,
unrelated repair and retries cannot bypass it. Current authority is
checked before and inside credential persistence; a real provider-time
revocation regression was red before the fix. Final 82 API/OpenAPI
tests, four neighboring lifecycle cases, and server typecheck/build
pass. Maps pass, and qualification retains the hidden-metadata fixture
failure and the measured minute-boundary rate-test failure. Production
rate behavior is unchanged; only the relevant test Date clock is fixed.

- After the current master/parent refresh, all **341 affected UI
cases**, combined workspace typecheck/build, maps and token gates pass.
The updated parent also passed **97 actual-auth/OpenAPI cases**, **141
gateway/policy cases** and **61 profile installation cases**. Published
upstream Copilotv62/v63 remain unchanged; combined guidance is now v64
(`sha256:22e31f95ef972bcead98a4a707c9f33433558fd111e390e027a2bbb4346e112a`).
Qualification retains the initial source-identity failure and fixture
port/migration setup failures. No full local sweep, fresh local OAuth
protocol run or live acceptance is claimed; fresh Linux CI must qualify
this successor.

- Latest review follow-up: the parent fixes caller-policy bypass in
Slack tests/invitations and premature identity-handoff completion. The
maps now name caller/target Block/Ask/rate intersection, one charge for
a shared bucket, and the addressed human’s exact current identity link.
Parent verification passes all **99 actual-auth/OpenAPI tests**,
typecheck and build. The preserved combined companion passes the four
focused tests (including twelve Slack policy/recipient combinations and
the actual human OAuth confirmation path), workspace typecheck/build and
maps. The 50 embedded-PG continuation cases were skipped locally because
their database could not start; fresh Linux CI remains required. All
material red/intermediate results and corrected-fixture evidence are
retained in the qualification record.

- Current `04d40cdd60` CI workflow40446969603 retained two new failures:
server1/12 `workspace-runtime.test.ts` could not reach a fixture
subprocess’s health endpoint within its ten-second readiness window
(job122001926052), and chat2/3 `wakes Slack session sync from
publication commit and leaves settled action queues quiet` observed one
select after its idle predicate (job122001925936). Both same cases
passed in parent `2ecd6451d0` Linux jobs122001920494/122001920581. The
runtime and queue implementations are unchanged by this companion. Both
failing cases also pass unchanged locally: two real-subprocess runtime
readiness/reuse tests and the exact Slack quietness assertion against a
fresh migrated PostgreSQL database. Logs:
`/tmp/parity-04d-server1-failure.log`,
`/tmp/parity-04d-chat2-failure.log`,
`/tmp/parity-2ecd-server1-green.log`,
`/tmp/parity-2ecd-chat2-green.log`,
`/tmp/parity-04d-runtime-qualified.log`,
`/tmp/parity-04d-chat-quiet-qualified.log`. The single standard
unchanged-code failed-job rerun was **accepted as attempt2** on the
unchanged exact head at `2026-10-27T04:24:30.513973+00:00` after the
workflow completed. Both new failure retry allowances are now
**consumed**; both failed shards and the complete Linux workflow have
now passed on attempt2, including the verification aggregate. No
additional unchanged-code rerun will be requested for either failure. No
timing, assertion, idle-work guard or policy was weakened. If either
case fails again, investigate/fix rather than rerun again. The older
5b/b0ea rerun allowances remain consumed and separate.

## Risks

- Saved handoff links depend on the API changes in #17737, including the
nonsecret credential-generation metadata. Both PRs must ship together.
- A map can overstate operation coverage. Entries identify their
evidence and retain human authentication and trusted-host limits.
- Structural map checks do not prove live provider behavior.

## Model Used

- OpenAI Codex, GPT-6 family, with reasoning, repository tools, and code
execution. The runtime does not expose a more specific deployment ID or
context-window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass — focused UI/guidance and
map checks
- [x] I have added or updated tests where applicable — saved setup UI
tests and map validation apply
- [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 — exact `04d40cdd60` full Linux
CI completed SUCCESS on attempt2; all current checks/statuses are
terminal clean, with an actual neutral5/5 Paperclip assessment
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups —
exact `04d40cdd60` review completed with no findings
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-27 08:21:02 -05:00
DottaandPaperclip 13709b4c7a feat(connections): let agents configure connections within delegated authority (#17737)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Connections provide governed tools, chat channels, model accounts,
and gateways.
> - Several management routes require a human board actor even when an
agent has the required grant.
> - Default agents must configure these resources within the responsible
user's authority.
> - This pull request adds a shared authorization path to the existing
REST and native API surface.
> - Human authentication, execution approvals, and local host boundaries
remain enforced.
> - The benefit is that a standard agent can complete authorized setup
with a bounded human handoff.

## Linked Issues or Issue Description

**Subsystem affected**

Server authorization, connection services, shared contracts, native API
discovery, database schema.

**Problem or motivation**

Connection administration is available in the web app but often
unavailable to agents. Agent grants, responsible-user authority,
credential ownership, and actor attribution need separate checks.
Opening gateway-token creation also requires preserving the issuing
agent's delegated authority during token use.

**Proposed solution**

Use the existing authorization evaluator for every connection family.
Enforce current responsible-user authority, effective trust, grant
scope, resource visibility, and target-agent access. Keep existing agent
grants unchanged. Expose authorized configuration through default
`search_api` and `call_api`. Save human authentication steps and let
agents continue after completion.

**Roadmap alignment**

This change completes the API access path for the existing Connected
Apps, Personal & Shared AI Accounts, and governed MCP Gateway
capabilities in `ROADMAP.md`. It follows the maintainer's requested
design. Related work: #11899 enforces connection execution access and
creator/manager rules; this PR extends agent management across
connection families. It does not replace that execution-policy work.

## What Changed

- Add shared connection management context and mandatory
responsible-user, trust, scope, and target checks.
- Allow authorized agents to configure tool, chat/email, GitHub/Slack,
AI, profile, and gateway resources through existing routes.
- Preserve human credential ownership and record the actual agent,
responsible user, and run or key in audit data.
- Preserve saved browser authentication handoffs and callback session
checks.
- Bind agent-issued durable gateway tokens to their responsible user and
recheck delegated authority at use.
- Add explicit REST transport and management capability metadata,
built-in guidance, and sensitive-response handling.
- Intersect caller and endpoint-agent Slack test/invitation policies,
including independent and shared rates. Require the addressed human’s
current endpoint identity link before completing identity handoffs.
- Add production-authenticated integration scenarios. Companion #17739
contains the saved setup UI (including the required human
import/rotation step), product and permission documentation,
catalog/Vercel credential and callback ownership, and operation maps to
keep each review below 100 files. Both PRs must ship together; merge
this implementation first, then its companion.

## Verification

- Current implementation head:
`2ecd6451d0d0c8d82121a4e3fa4ce94c5befbbd6`, including master
`ea727dc3c2`, 99 files. The latest follow-up fixes the two valid
findings from the preceding f9 review. Exact-head Greptile has completed
at5/5 with no findings and Superagent passed. Full exact-head Linux CI
workflow40446606056 completed SUCCESS; only the queued external
Paperclip Review remains pending; earlier Linux passes do not qualify
this successor.
- Latest preserved-stack verification passed **2,165 tests** in fresh
PostgreSQL: **70 actual-auth scenarios**, 27 OpenAPI contracts, 2,022
native API tests and 46 saved GitHub wizard cases. **141 existing
gateway/policy acceptance tests** also passed. Workspace
typecheck/build, maps and token gates passed. The first workspace
typecheck was blocked by sandbox tsx IPC permissions; its authorized
rerun passed. This is focused local evidence; the full Linux CI matrix
is still required.
- Issuer/target gateway policy regressions were red before the fix. Both
decisions now intersect, including Block/Ask first/rates, same
credential identity and current token/user/agent authority after
approval. Invocations retain their token and actual requesting agent;
deleted tokens fail closed. Thirteen actual-auth cases cover revocation,
narrowed scope, approvals, shared rates and board compatibility.
Intermediate missing-token attribution, review-context and
implicit-session actor failures remain in the qualification record; all
final cases pass.
- Activity pagination now retains a bounded cursor across private-only
windows. The actual-JWT regression reaches three older readable events
after 27 private ones without exposing private details. A native
device-login regression was red for plaintext receipts; local check and
test-call results now use existing protected-response handling. The
passing actual-auth case proves encrypted persistence, exact replay,
redacted history and no response asset.

- Previous-head `5b55195593` Linux CI initially failed e2e shard 4/8 in
the existing connection-intent continuation test: periodic recovery
queued a repair at 01:21:47, the scripted journey completed the task at
01:21:48, and the extra queued run was cancelled at 01:21:49 because the
task was terminal. The assertion selected that latest cancelled run.
GitHub refused the first attempt while the workflow was nonterminal.
After completion, the single evidence-supported failed-job rerun was
accepted as **attempt 2** on the same head (workflow 40431202466). The
failed shard and final E2E aggregate passed on attempt 2, and the
complete Linux workflow for that head is successful. The original
failure remains part of qualification. No additional unchanged-code
rerun will be used for this failure.

- Companion CI also has an unchanged login-cleanup idleness timeout,
retained in its qualification record. That same 16-case suite passed in
this parent Linux run and a fresh local database. Both preceding
workflows passed after their single failed-job reruns as attempt 2; the
original failures remain recorded. No additional unchanged-code rerun
was used.

- Migration `0380_broken_aqueduct.sql` was regenerated through the
repository workflow after master took 0379 for project Git
authorization. It adds the responsible-user column with `IF NOT EXISTS`,
preserves upstream migrations, and satisfies snapshot retention and
migration safety checks.
- Upstream published Copilot v62 and v63 identities remain byte-for-byte
unchanged. The combined guidance is now v64, digest
`sha256:22e31f95ef972bcead98a4a707c9f33433558fd111e390e027a2bbb4346e112a`.
The normal shared TypeScript/Rust identity check and **61 Copilot
installation tests passed**. The old source-identity assertion first
failed against unchanged published v63; regenerating a new v64 preserves
the published release.
- Workspace `pnpm -r typecheck` and `pnpm build`, map validation, and UI
token gates passed on the final combined stack after the upstream merge.
Node 24.22.1 / pnpm 9.15.4 were used.
- **Earlier 82 API/OpenAPI tests passed** in fresh PostgreSQL 18: 55
assembled-server scenarios and 27 contracts. They use real persistent
agent keys, run JWTs, human sessions, production defaults, and
deterministic provider fixtures. No actor injection establishes access.
- The new email regression was red before per-account scope filtering.
The Arcade regression was red before human ownership and inventory-key
fixes; it also verifies actual-agent audit identity and a human-session
inventory read. The activity regression measured **200 authorization
checks before the fix and one afterward** for the same one-item page.
- **82 GitHub UI tests passed**, including all three new
failed-replacement regressions that were red before their fix. The UI
preserves an earlier copy only after confirming its committed
generation. Changed or unreadable results block copying and reconnecting
until reconciled. Human-only secret responses now include their
nonsecret committed generation.
- Private test status now rechecks the originating principal and linked
task, including mandatory agent/responsible-user privacy. Cross-agent
tests require the full caller tool access decision and the same
credential identity, regardless of auth label; caller and target
policies/approvals and rate limits apply before execution and after
human approval. A profile-only precondition was found to reject valid
allow-policy and scoped-grant access and has been removed. Both
permitted-access cases and a no-auth-label identity regression were red
before the correction; caller-specific Block still prevents provider
execution. The private-status regression was also red before its fix.
- The tool-activity regression measured 100 capability decisions and 102
run captures on a 100-event page before the fix. It now uses two task
decisions and at most two captures while retaining the same 50 visible
events, private-event filtering, and 403 after audit-grant revocation.
The regression was red before request-context reuse and task-decision
sharing; final server typecheck/build also pass.
- Generic chat configure/reconnect now hands agent-supplied verification
credentials to the addressed human before provider work or vault
changes. Actual key/JWT regressions were red for Slack signing-key
replacement and generic channel secret-reference rebinding. They now
prove unchanged refs and vaulted signing value, human setup, permitted
agent bot-token repair, and governed test messages. All 82 API/OpenAPI
cases pass in fresh PostgreSQL. Eight native receipt protections and
final server typecheck/build also pass.
- The preceding review found that old healthy state could retire a
credential-entry card before a human changed anything. The actual-JWT
regression was red. Saved field generations now change only with
protected values and commit with refs plus a verification fence; retries
retain the baseline. Old health, bot-token repair and unchanged entry
stay pending. New signing verification and current authority permit
continuation. A separate red race proved that user revocation during
provider authentication could return 403 after refs changed; authority
is now checked before persistence and inside the credential transaction.
The final 82 API/OpenAPI cases, four neighboring real-database
Slack/GitHub lifecycle cases, and final server typecheck/build pass.
- A preceding full run crossed a rate-window boundary. Database rows
prove one consumed slot at 23:35 and another at 23:36 for the same
policy/bucket. Only Date is frozen in the rate test cases; transport and
asynchronous timers remain real. Production rate behavior is unchanged,
and the final full run passes. An intermediate fixture failure that
overwrote hidden generation metadata with an API projection is retained
in the qualification record.
- Linux CI on an earlier head found two rate-admission assertions that
omitted the newly persisted bucket key. The failures were reproduced
locally; both assertions now verify the exact actor/tool bucket and all
49 policy-service tests pass. The companion qualification record retains
the initial and intermediate failures. Catalog creator attribution is
preserved in the companion to keep this implementation review at 99
files.
- The latest review reproduced three regressions before repair: complete
human replacements failed on unavailable old signing secrets, fresh
GitHub links incorrectly selected reconnect, and different
credential-entry requests shared an idempotency key. Each old protected
ref now has a narrow known-unavailable fallback; unexpected resolver
failures and company/scope violations still abort. Fresh imports keep
their credential form, configured endpoints keep reconnect, and
canonical field sets/completion modes distinguish handoffs while
matching retries retain their baseline. All 55 actual-auth scenarios and
27 separately run OpenAPI contracts pass, as do server typecheck/build.
Companion #17739 also passes all 41 wizard tests including actual
fresh-form submission, UI typecheck, maps and token gates. Earlier red
results and a mistyped OpenAPI filename are retained in qualification.
- The configured GitHub handoff regression was red in the
browser-component tests and actual-auth link/persistence tests. Saved
links now name requested fields and all/any completion mode; the
companion form submits those supplied fields through reconnect and
retains other saved credentials. Webhook/client-secret reconnect remains
human-only; first-setup secret restrictions remain. All 46 wizard tests
pass, including combined modes and legacy links. A duplicate review
referenced the older companion bf760912; the current form is already
fixed, linked in the reply, and its thread is resolved.
- Linux CI on bf760912 failed automatic Slack provisioning because vault
persistence cleared an already verified manifest-update challenge. The
same regression was reproduced locally. The provider-owned helper now
preserves proof only for the current signing fingerprint and callback
URL. Eight existing compatibility cases pass, including this case and
initial GitHub secret rejection. The first uncommitted GitHub-field
variant also failed that initial boundary and was narrowed to reconnect;
both failures remain in qualification.
- The upstream merge conflicted only in generated migration metadata.
Upstream 0379 is preserved, normal generation/pruning produced 0380, and
two repeated applications of the idempotent additive SQL passed. The
final combined stack passed **128 tests** (55 actual-auth, 27 OpenAPI
and 46 wizard cases) in fresh PostgreSQL. Eight Google UI cases also
pass; the two protocol cases could not initialize embedded PostgreSQL in
this fresh local attempt. Their earlier passing evidence remains
historical; fresh Linux CI must execute them. Vercel personal credential
attribution is preserved in companion #17739 with its other
ownership/consent follow-ups, keeping this PR at 99 files.
- The suite covers fresh defaults, restricted users/agents, scopes,
personal isolation, token revocation, human handoffs, callback replay,
execution approvals, authorized self-targeted policy changes,
self-approval denial, and current authority before commit. Reproduce API
checks with `pnpm exec vitest run
server/src/__tests__/connection-management.integration.test.ts
server/src/__tests__/openapi-routes.test.ts`, using a fresh database
through `PAPERCLIP_CONNECTION_MANAGEMENT_TEST_DATABASE_URL` or the
default embedded test database.
- Companion #17739 also records consent separately from token refresh so
saved Google choices survive refresh. Its eight service-picker and two
local-HTTP OAuth tests pass; both new UI cases were red before the fix.
Normal migration generation confirms no SQL change is needed for the
additive JSON metadata.
- The integration module shares one assembled server, authentication
factory, and provider lifecycle to preserve the
HTTP-to-vault-to-task-continuation proof. A reused external database
caused a Slack fixture identity conflict; the fresh final run passed.
Earlier fixture/transport and contract-update failures remain in the
companion qualification record.
- The broad local `pnpm test:run` attempt remains blocked by macOS
embedded PostgreSQL's host semaphore limit. It is not a clean pass. The
fresh Linux CI matrix must complete. Provider fixtures do not prove live
setup or an empirical LLM success rate.
- A current-head completed Paperclip assessment is not yet recorded. Its
earlier external recovery boundary has not been bypassed, and no
incomplete assessment is reported as passing. The user authorized both
merges when checks pass; merge this implementation first, then replay,
retarget, and requalify companion #17739 against the squash-merged
master.

- After the master refresh, local workspace typecheck/build, **97
actual-auth/OpenAPI cases** and **141 gateway/policy cases** pass. The
original fixed database port was occupied; Docker assigned a free
localhost port. The first neighbor setup command could not resolve root
`tsx`, leaving its schema uninitialized; the built database package
migrated the separate fixture and all unchanged cases passed. Both setup
failures are retained in qualification. Fresh Linux CI remains required;
no full local test-sweep pass is claimed.

- The f9 Greptile review scored **2/5** for two reproduced findings:
Slack test sends skipped the caller’s tool policy, and healthy App
credentials completed an unconfirmed identity handoff. Both are fixed in
this head and have resolved evidence threads
([Slack](https://github.com/paperclipai/paperclip/pull/17737#discussion_r4384927765),
[identity](https://github.com/paperclipai/paperclip/pull/17737#discussion_r4384927895)).
Actual key/JWT cases cover twelve Slack caller/target/recipient
combinations; the human-session GitHub OAuth callback alone remains
pending until explicit confirmation. All **99 API/OpenAPI tests** (72
actual-auth + 27 contracts), workspace typecheck and build pass. On the
combined companion, all four focused cases (including the twelve Slack
combinations), typecheck/build and maps also pass. The continuation
suite’s 50 embedded-PG cases were skipped because its database could not
start locally; fresh Linux CI must run them. Initial fixture
credential-collision and audit-projection assertion failures are
retained with the red reproductions in the companion qualification
record. No full local sweep or live consent is claimed.

## Risks

- The maintainer explicitly confirmed authorized policy administration,
including self-targeted changes that loosen controls. These writes
require management grants and the responsible user’s current authority.
Agents cannot approve their own governed action requests or edit their
own principal permission grants. The actual-auth regression exercises
both permitted administration and those denials.
- This is a broad authorization change. Incorrect scope or owner
resolution could deny permitted setup or expose another user's
resources. Integration cases cover both permitted and denied requests
with actual authentication.
- The additive nullable gateway-token column does not infer identities
for legacy clients or backfill agent grants. Legacy agent tokens without
a responsible user fail closed.
- Provider fixtures test supported setup and callback contracts. They do
not prove live provider availability.

## Model Used

- OpenAI Codex, GPT-6 family, with reasoning, repository tools, and code
execution. The runtime does not expose a more specific deployment ID or
context-window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass — focused suites above;
full local sweep remains blocked as documented
- [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 — exact `2ecd6451d0` Linux CI
completed SUCCESS; external Paperclip Review is still queued
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups —
exact `2ecd6451d0` review completed; both f9 findings have resolved
evidence replies
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
canary/v2026.1027.0-canary.5
2026-10-27 08:20:24 -05:00
Devin FoleyandPaperclip 0cb3fc7db5 fix(runner): retain ACPX transport failure details (#17837)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Paperclip Runner reports provider failures to the execution and
recovery services.
> - Those services read the terminal event's structured `error` field.
> - The ACPX transport-failure path wrote its code and message only at
the top level.
> - The session facade therefore replaced that cause with `error: null`
and the generic message "Provider turn failed".
> - This pull request uses the existing structured error contract while
retaining the old fields.
> - Recovery can retain the actual redacted cause without reopening the
failed provider or assuming a retry is safe.

## Linked Issues or Issue Description

**What happened?**

A native ACPX run can lose its failure details between runnerd and the
session facade. The transport-failure producer emits a failed turn with
top-level `code` and `message`. The facade reads `error`. The server
receives a generic provider failure and cannot use the original cause to
explain or classify recovery.

**Expected behavior**

Retain the transport code and redacted cause in the structured terminal
error. Keep failed sessions closed, provider termination unproven, and
automatic replay disabled.

**Steps to reproduce**

1. Open a native ACPX session and start a turn.
2. Send malformed activity from the sidecar after it acknowledges the
turn.
3. Poll the native provider events.
4. Observe that `turn.failed.error` is absent even though the producer
has a transport code and cause.

Refs: #16366, #17543. Those changes cover legacy process-session
diagnostics and session-ensure errors. This change repairs the native
ACPX polling terminal contract.

## What Changed

- Add the existing transport code and redacted message to the terminal
event's structured `error` field.
- Mark that error as non-recoverable. Keep the previous top-level fields
for existing readers.
- Add a real sidecar fault fixture and a native-provider regression. It
checks one failed terminal, retained error details, a closed session,
and no provider reopening.
- Document the failure contract and verification scope in execution
semantics and the recovery map.

## Verification

- Red: the new fault test failed because the structured transport error
code was null on the original code.
- Green: all 26 native-provider integration tests pass with the fix.
- Three session-facade terminal tests and three native-runtime
provider-failure classification tests pass.
- `pnpm -r typecheck` and `pnpm build` pass.
- Hosted test partitions, native Runner checks, typecheck, build,
browser shards and canary passed. Two final aggregate jobs received no
runner for several minutes. The first CI attempt was cancelled after
saving its status, and only those two cancelled jobs were resubmitted.
Both aggregate jobs passed on attempt 2. The current head has 55
successful checks, four expected skips and successful Snyk status.
- `git diff --check`, `node scripts/check-module-boundaries.mjs`, and
`node scripts/check-maps.mjs` pass.
- The full local Vitest suite is not run for this patch. The local
filesystem fell below 500 MiB free during verification. The preceding
full-suite attempt crashed embedded PostgreSQL; focused checks and CI
are used without claiming that attempt passed.
- Live incident: verified the exact predecessor sandbox was destroyed
and workspace recovery completed. An audited user retry through the
normal task-retry service started one new native run. That attempt
preserved the original question but failed before provider startup with
a missing workspace-sync descriptor binding. The startup race is fixed
separately in #17838. A later audited retry completed successfully,
saved its useful answer and restored its workspace. The agent API
handoff failed; an audited operator repair through the normal issue
service completed the required user assignment in Review without
repeating the comment or provider work. This verifies recovery admission
on the existing deployment; the new Runner change is not deployed yet.
- Evidence limit: the historical terminal had `error: null` and its raw
sidecar frame was not retained. Fault injection proves this producer
defect and the same loss of diagnostics. It does not establish which
transport fault caused the historical turn to end.

## Risks

- Recovery now receives a specific transport failure code. The error
remains non-recoverable and `providerTerminalObserved` remains false.
Stop proof, effect reconciliation, workspace preservation, retry limits,
and normal execution authority still govern any successor.
- This change preserves diagnostics. It does not make a malformed or
lost provider connection healthy.
- No schema migration, credentials, provider selection, or model
settings change.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, source inspection, tool use,
and code execution. The runtime does not expose the exact model ID or
context window size.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-26 21:34:29 -07:00
Devin FoleyandPaperclip 94819b6ded fix(recovery): preserve live heartbeat preparation ownership (#17838)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Heartbeat prepares each run before it starts the native provider.
> - Startup recovery can run while that preparation is still active.
> - The native input and coordinator exist before workspace
synchronization saves its descriptor.
> - Recovery checked the later native executor registry but missed the
earlier heartbeat execution registry.
> - It could claim the live run and dispatch a second executor against
incomplete workspace state.
> - This change gives the recovery claim the existing live heartbeat
registry and checks it before changing ownership.

## Linked Issues or Issue Description

**What happened?**

Startup recovery can claim a native run that the same server is still
preparing. The second execution reads the admitted native input before
workspace synchronization has saved its descriptor. It fails with
`native_workspace_sync_descriptor_binding_missing`. The original
preparation then finds a failed run and cannot start the provider.

**Expected behavior**

Leave live preparation with its current executor. Do not change its
native coordinator or dispatch another executor. An inactive run must
still pass the existing recovery checks.

**Steps to reproduce**

1. Start heartbeat runtime selection for a remote sandbox.
2. Hold workspace synchronization after native input and coordinator
persistence.
3. Run native startup recovery with the heartbeat execution still
active.
4. Observe an unintended `bootstrap_incomplete` claim and a second
execution.

Refs #17439 for broader remote controller recovery. This change repairs
the current heartbeat startup race. It does not add checkpoint storage
or remote adoption. A separate diagnostics correction is in #17837.

## What Changed

- Pass the existing heartbeat live execution registry to native recovery
claims.
- Check the current registry after locking the run and before any
takeover or coordinator change.
- Return the existing waiting disposition for live preparation. Keep
workspace descriptor validation unchanged.
- Add a PostgreSQL regression at the real runtime-selection boundary and
an assembled heartbeat recovery regression with the real claim service.
- Document the preparation interval and its recovery behavior.

## Verification

- Red: the new runtime-selection test observed an unintended bootstrap
claim on the original code. The heartbeat regression also failed because
recovery dispatched the live run.
- Green on current head `88bec51f10d61eb851eec849e82a80bb198555c7`: all
144 runtime-selection, heartbeat recovery, workspace-preparation and
restart-policy tests pass.
- These cases check an unchanged coordinator, no duplicate recovery
dispatch, a running source run, and normal recovery after the execution
leaves the registry.
- The full native restart integration file passes all 22 available
cases. Nine real-runner process cases are skipped because local Runner
test binaries were removed to recover disk space. The passing cases
include remote startup, old preparation fencing, ownership conflicts,
Stop, and concurrent recovery.
- Direct full server TypeScript check passes: `pnpm exec tsc --noEmit -p
server/tsconfig.json`. Hosted CI passes on this head, including full
test partitions, typecheck, build, native Runner checks, serialized
suites, browser shards, canary and both aggregates. Apex is 5/5 with no
actionable findings. The current head has 54 successful checks, two
expected skips and successful Snyk status.
- `git diff --check`, module boundary checks, and map checks pass.
- A wider local safety run stopped with PostgreSQL error 53100 and
ENOSPC after 64 tests passed. Recursive typecheck also stopped with
ENOSPC. These attempts did not pass. Generated files in this worktree
were removed to free space. The full local test suite and build have not
been run for this change; hosted CI must supply those results.
- Live evidence: a production startup recovery claimed a new run during
outward workspace staging. The second preparation failed before provider
startup. Its exact sandbox was later destroyed with successful cleanup.
The patch is not deployed. A later audited retry on the existing
deployment completed successfully and saved the requested answer once.
An audited operator repair through the normal issue service completed
the final user assignment in Review after the agent API handoff failed.
This acceptance verifies recovered work, not deployment of the new
guard.

## Risks

- A live heartbeat remains responsible for its own completion and
cleanup. Recovery must not compete with it. The registry is
process-local; existing durable ownership checks still govern other
controllers.
- An executor that is still active remains protected until its cleanup
removes its registry entry. This adds no timer, polling loop, schema or
credential change.
- Missing or corrupt workspace descriptors still fail closed. This
change does not authorize replay of unknown provider actions.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, source inspection, tool use
and code execution. The runtime does not expose the exact 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-26 21:34:15 -07:00
3edba11576 fix(github): stop counting suspended installations as repository access (#17343)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents get GitHub access through a managed GitHub App connection.
**Refresh access** reads the installations and repositories that the
user token can reach.
> - GitHub keeps listing a suspended App installation and all of its
repositories for the user token. But the token then loses write access
and cannot read private repositories.
> - `loadGitHubGrantMetadata` did not read `suspended_at`. It counted a
suspended installation as live access ("all repos") and set the
connection to healthy.
> - So the board showed "connected, all repositories" while agents got a
403 on `git push` and a 404 on private repositories. The suspend webhook
marks the connection unhealthy, but the next refresh or reconnect
reverted that.
> - This pull request skips suspended installations. When all
installations are suspended, Refresh access fails with a clear error and
a link to the installation settings page, where an owner can click
**Unsuspend**.
> - The benefit is that the board shows the real access state, and
operators get a direct path to fix it.

## Linked Issues or Issue Description

**What happened?**
A GitHub App installation on an organization was suspended. The
Paperclip GitHub connection still showed "connected" with access to all
repositories after **Refresh access**. Agents that used the managed
token got `Permission to <org>/<repo>.git denied` (403) on push,
`Resource not accessible by integration` on branch creation, and 404 on
private repositories.

**Expected behavior**
Refresh access must not count a suspended installation as repository
access. When every installation is suspended, Refresh access must fail
with a message that says the installation is suspended, and the page
must link to the installation settings page.

**Steps to reproduce**
1. Connect GitHub to Paperclip through the GitHub App, with an
installation on an organization.
2. On GitHub, suspend that installation (Organization settings → GitHub
Apps → Configure → Suspend).
3. In Paperclip, click **Refresh access** on the GitHub connection.
4. See that the connection stays healthy and reports all repositories,
while agent pushes fail with 403.

**Paperclip version or commit**
`master` at a708a1333.

**Additional context**
`GET /user/installations` returns the suspended installation with
`suspended_at` set and `repository_selection: all`, and `GET
/user/installations/{id}/repositories` still lists its repositories.
Unsuspending the installation restored push access and private reads for
the same token.

## What Changed

- `server/src/services/tool-access/github-access-discovery.ts`:
`loadGitHubGrantMetadata` skips installations that have `suspended_at`
set. It does not add their IDs, owners, repository selection, or
repositories. When there is no usable installation and at least one is
suspended, it throws `github_installation_required` with a "suspended"
message. `managementUrl` points at the suspended installation's settings
page (or the generic installations page when more than one is
suspended).
- `server/src/services/tool-access/catalog/health.ts`:
`checkConnectionHealth` (the Refresh access path) keeps
`installationUrl` and `managementUrl` when it rebuilds a
`github_installation_required` error. Before, the links were dropped.
- `server/src/services/tool-access/catalog/failures.ts`:
`github_installation_required` keeps its own code and returns 422.
Before, it was folded into a generic `paperclip_error` with HTTP 502.
- `ui/src/pages/apps/app-detail/AccessRetryBanner.tsx` (new): the
attention banner with the saved health message and **Retry access**.
After a failed Refresh access with `github_installation_required`, it
shows an **Open installation settings on GitHub** link (https
`github.com` URLs only).
- `ui/src/pages/apps/AppDetail.tsx`: uses `AccessRetryBanner`, refetches
the connection when a refresh fails so the banner shows the saved
message, and keys the page on `connectionId` so a recovery link never
carries over to another connection. The file stays under 1,000 lines.
- Tests:
- `server/src/__tests__/github-grant-metadata.test.ts`: a mix of
suspended and active installations, and an account where all
installations are suspended.
- `server/src/__tests__/github-health-recovery.test.ts`: drives
`checkConnectionHealth` with a suspended-installation GitHub response
and checks the 422, code, and recovery links.
- `ui/src/pages/apps/AppDetail.test.tsx`: a failed Retry access renders
the installation settings link, and navigating to another cached
connection does not show it.

## Verification

- Red/green: with the source changes reverted and the tests kept, the
new tests fail (suspended installation counted as `all`; recovery link
missing from the health-check error; link not rendered; link leaking to
another connection). With the changes, they pass.
- `server`: `github-grant-metadata`, `github-health-recovery`,
`tool-access-service`, `github-operation-credentials`,
`tool-access-oauth-protocol.integration`, `tool-connection-removal`,
`tool-connection-visibility`, and the other `github-*` / `tool-access-*`
suites pass locally (630 tests).
- `ui`: `AppDetail.test.tsx` passes (78 tests). `ui` `tsc --noEmit`
shows no errors in changed files.
- Not run locally: the server `tsc`. The sandbox has a 4 GB memory
limit, and the server typecheck is killed there even on clean `master`.
CI ran the full typecheck and build, and both pass.
- CI: all gates green. One unrelated `workspace-recovery.test.ts` poll
timeout in a server shard passed on re-run.
- Live check: after the suspended installation was unsuspended, a
managed-token `git push --dry-run` succeeded and a private repository
returned 200.

## Risks

- Low risk. The change only affects installations that GitHub marks as
suspended. Active installations behave as before.
- A connection that has only suspended installations now fails Refresh
access with `github_installation_required` (422) and does not report
fake access. This is the intended behavior.
- The recovery link only renders for https `github.com` URLs.
- Keying the app page on `connectionId` remounts it when moving between
connections, which resets per-page state (for example an open rename
field). That reset is intended.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, used through the
Claude Code agent SDK with tool use (shell, file edit, code execution).

## Checklist

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

🤖 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>
2026-10-26 21:16:40 -07:00
TonioandClaude Fable 5.1 3e7c3ee9a9 fix(ui): fall back to the icon face when an Artifacts V1 preview fails to load (#17260)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents produce artifacts: documents, files, pictures, video and
links. The Artifacts V1 presentation (`enableArtifactsV1`, #17228) shows
them as prints, sheets and rows on the Artifacts page, in the panel's
Artifacts tab and in the task thread
> - A picture is printed from its attachment's content path. When that
content cannot be loaded (on staging, image work products whose
attachment returns 404), the browser draws its broken-picture glyph
inside the bordered print and inside the 40px row thumbnail
> - The presentation already has a face for a file with nothing to show:
the icon sheet (the faint glyph and the type label) and the row's
document icon. A picture that cannot be loaded should take that face,
not a browser glyph
> - This pull request listens for the element's own `error` event in the
V1 primitives and falls back to the icon face on all three surfaces: the
stack front and the single tile on the Artifacts page, and the panel and
thread rows
> - The benefit is that a missing or unreadable file reads as a file of
its type everywhere, with no fetch to find out, and nothing changes for
a picture that loads

## Linked Issues or Issue Description

No public issue exists. This section follows the bug report template.

**What happened?**

With Artifacts V1 on, an image work product whose attachment content
returns 404 ("Attachment not found") shows the browser's broken-image
glyph inside the bordered print on the Artifacts page (the stack front
and the single tile) and inside the 40px thumbnail in the panel and
thread rows.

**What did you expect to happen?**

The artifact shows the icon face a file without a preview gets: the icon
sheet with the faint glyph and the type label on the Artifacts page, and
the document icon with the type label in the rows.

**Steps to reproduce**

1. Turn on Settings > Experimental > Artifacts V1.
2. Register an image work product whose attachment content returns 404,
or delete the attachment behind one.
3. Open the Artifacts page, the task's Artifacts tab and the task
thread.

**Relevant subsystem**

The web UI (`ui/src/components/artifacts/v1/`).

## What Changed

- `ArtifactPrint`: a new `usePreviewFailure(source)` hook beside
`useImageAspect`, and an `onMediaError` prop wired to the `error` event
of the print's `<img>` and `<video>`. The hook remembers the source that
failed rather than a flag, so a front that moves on to another picture
(a stack's newest artifact) gets a fresh chance.
- `ArtifactFront`: owns the failure state for its subject and renders
the icon sheet (`IconSheet`, the `icon` treatment) once the print
reports an error; it passes the error on through its own `onMediaError`.
- `ArtifactStack`: on an error it stops fitting a print and stands the
icon sheet where a sheet stands in the media stack (the portrait anchor,
where audio stands), keeping the media backing.
- `ArtifactRow`: the thumbnail listens for `error` on its `<img>` /
`<video>` and gives way to `ArtifactRowIcon`, the document icon with the
type label.
- Until the picture loads the print stays the 4:3 placeholder, and a
picture that loads behaves as before. No fetch is made to detect a
failure.
- A failed video takes the plain icon sheet with no play disc, since the
stream it would promise cannot play; a video with no URL at all keeps
the existing media-fallback treatment with the disc.
- `index.ts` exports `usePreviewFailure`; the plan doc
`doc/plans/2026-10-22-artifacts-mvp-v1.md` records the fallback.
- Tests: `ArtifactTile.test.tsx` covers the single tile (4:3
placeholder, then the icon sheet after `error`), the picture stack (the
sheet stands where the audio stack's sheet stands, on the media backing)
and a video whose stream fails (icon sheet, no play disc).
`ArtifactRow.test.tsx` covers the thumbnail and the video thumbnail
giving way to the document icon, and a new source getting its own
chance.

## Verification

- `cd ui && npx vitest run src/components/artifacts/v1` — 6 files, 33
tests pass (5 new).
- `pnpm run check:token-gates` — exits 0: all four token gates clean,
palette tokens and the ClipLab character in sync. (The bare `pnpm
check:token-gates` form reports "Command not found" under pnpm 9.15.4 in
this worktree; `pnpm run` works.)
- `cd ui && pnpm typecheck` — exits 0 with no errors.
- Browser proof on a throwaway backend-free vite page (not committed)
rendering the primitives with a 404 picture, a 404 video, a picture that
loads and audio: on the Artifacts page the 404 picture's stack front is
the PNG icon sheet at the same position as the audio stack's sheet (left
37.89px, top 13.34px) on the media backing; the single tiles show the
PNG and WEBM icon sheets with no play disc while the loading picture
keeps its fitted 240×160 print; the rows show the PNG and WEBM document
icons with no `<img>`/`<video>` left, while the loading picture's row
keeps its thumbnail. Not run: the browser suites (`pnpm test:e2e`, `pnpm
test:release-smoke`) and a live staging check.

States enumerated: loading (4:3 placeholder print, unchanged), loaded
(fitted print, unchanged), failed picture (icon sheet / document icon),
failed poster-less video (icon sheet without play disc / document icon),
source changed after a failure (print again).

## Risks

- Low risk. The change is behind `enableArtifactsV1` and only acts on
the element's `error` event, which fires for a 404, a network failure,
or content that is not decodable as a picture; nothing is fetched to
find out.
- `ArtifactFront` and `ArtifactStack` each keep the failure state for
their subject (as both already keep the loaded aspect), set from the
same event in one render; a consumer that renders `ArtifactFront` with a
print size but ignores `onMediaError` still gets the icon face, only at
the print's position.
- A picture that fails once and later becomes readable at the same URL
keeps the icon face until the surface remounts or the subject moves on
to another source; the data does not change under a mounted tile in
practice.

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

## Model Used

- Claude (Anthropic), model ID `claude-fable-5-1`, extended thinking,
running in Claude Code (desktop app) with tool use: file editing, shell,
Vitest, and the built-in browser for the proof page.

## Checklist

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

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

---------

Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
canary/v2026.1027.0-canary.4 nightly/v2026.1027.0-nightly.0
2026-10-26 21:08:49 -07:00
4da876ab0b fix(native): always advertise run-scoped readers to provider threads (#16995)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native runs use a Paperclip tool authority to declare tools to the
provider thread. Codex runs through runnerd.
> - An external-chat wake with a truncated comment batch requires the
agent to call `read_current_wake_comments`. A terminal fence fails the
run if the agent did not read every bound comment.
> - Codex `thread/resume` has no `dynamicTools` parameter. A resumed
thread keeps the tool catalog from its first `thread/start`.
> - The tool authority declared `read_current_wake_comments` and
`read_chat_attachment` only when the current run had a binding. The
tool-contract fingerprint already said that both tools are "always
advertised", so the code and the contract did not agree.
> - When a truncated chat wake resumed a thread that started on an
ordinary turn, the agent could not see the reader. The agent blocked,
and the fence failed the run with `native_current_wake_comments_unread`.
The task then stayed blocked for the board.
> - This pull request declares both readers on every turn and rotates
threads that started with the old catalog.
> - The benefit: truncated GitHub, Slack, and other chat wakes can read
their full comment batch on resumed threads. They no longer fail at the
fence.

## Linked Issues or Issue Description

No public issue exists. This is the bug description, in the fields of
`bug_report.yml`.

**What happened?**

A native Codex run got a GitHub chat wake with a truncated comment
batch. The prompt told the agent to call `read_current_wake_comments`.
The agent searched for the tool and did not find it. It reported a block
(`required_wake_reader_unavailable`). The server sent the reader in the
run's authorized tool list, but the provider thread had resumed. The
fence then failed the run with `native_current_wake_comments_unread`,
and the task became blocked. This happened 6 times on staging for the
same task.

**Expected behavior**

A resumed thread can call `read_current_wake_comments` and
`read_chat_attachment` on any turn that has a binding. The run then
completes normally.

**Steps to reproduce**

1. Start a native Codex session on a task with an ordinary wake. Do not
use a truncated chat wake.
2. Send an external-chat message that makes the wake comment batch
truncated (`fallbackFetchNeeded: true`).
3. The run resumes the same Codex thread. The model cannot see
`read_current_wake_comments`. The run fails with
`native_current_wake_comments_unread`.

**Paperclip version or commit**

Hosted staging release `fc0d390d`. The code is the same on `master` at
`3bbc210`.

**Deployment mode**

Hosted (staging). The native runner uses the `codex_app_server` backend
through runnerd in an external sandbox.

**Agent adapter(s) involved**

Codex (native `paperclip_runner`).

Related history: the existing retained-fingerprint table already lists a
"conditional current-wake reader" rotation. This change fixes a
regression of that contract. I searched open and closed PRs for
`current_wake_comments` and "wake comments" and found no duplicate fix.

## What Changed

- `PaperclipRunnerToolAuthority.definitions()` now always declares
`read_current_wake_comments` and `read_chat_attachment`. Execution stays
fail-closed: without the server-verified binding or scope, a call still
fails with `paperclip_runner_tool_not_advertised` or
`paperclip_runner_chat_attachment_read_scope_unavailable`.
- The native tool-contract fingerprint goes from v17 to v18, with the
two policy values set to `.v2`. Checkpoints from deployed v17 threads,
which may lack the readers, start a fresh provider thread one time with
full task context.
- New regression test: the catalog for an ordinary turn is equal to the
catalog for a truncated chat turn, and it contains both readers.
- New resume tests: the deployed v17 local and remote fingerprints do
not rebind. The existing test that asserted conditional advertisement
now asserts the stable catalog and the fail-closed execution.

## Verification

- Red before the fix: `npx vitest run
src/services/native-runtime/paperclip-runner-tool-authority.test.ts -t
"advertises only real bindings|keeps run-scoped reader declarations
stable"` had 2 failures. The unbound catalog had 48 tools and did not
include either reader.
- Green after the fix: `paperclip-runner-tool-authority.test.ts`,
`native-session-resume.test.ts`, `current-wake-comments.test.ts`,
`chat-attachment-read.test.ts`, `native-execution-input.test.ts`, and
`file-delivery-bridges.test.ts` all pass.
- `native-session-executor.test.ts`: 764 pass and 1 fails. The failure
(`compares export process compatibility … (changed: false)`,
`db.transaction is not a function`) also happens on unchanged `main`, so
this change did not cause it.
- Consequential assumption: Codex does not refresh dynamic tools on
resume. Evidence: `codex app-server generate-json-schema` for codex-cli
0.160.0 shows `ThreadStartParams.dynamicTools`, but `ThreadResumeParams`
has no `dynamicTools` field. The failed staging run also showed it: the
server listed `read_current_wake_comments` as authorized, but the
model's tool search returned no match after `thread/resume`.
- Not run locally: the server `tsc --noEmit` step (the sandbox has a 4
GB memory limit and the process was killed) and `dot-runner.test.ts`
(`cargo` is not installed). CI runs both.
- Not run: a live end-to-end chat wake against a real Codex provider.
That needs a staging deployment.

## Risks

- The fingerprint bump makes every native session that resumes from a
v17 checkpoint start a fresh provider thread one time. Paperclip
rebuilds the full task context, but the provider-side conversation
history is not carried over. This is the same tradeoff as earlier
fingerprint bumps.
- Two more tool declarations use a small part of the Codex dynamic-tool
budget. The assigned-MCP fit check already includes these definitions.
- Follow-up, not in this PR: the native Codex driver reports
`toolRefreshOnResume: true`, and the runnerd fake app server accepts
`dynamicTools` on `thread/resume`. Real codex-cli 0.160.0 has no such
field. Connection-tool continuations that depend on resume refresh need
a separate check.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`) from Anthropic, running as a
Paperclip agent with tool use (shell, file edit, and test execution).

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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>
2026-10-26 21:08:00 -07:00
commitperclip[bot] 2d72f2c4c0 fix: Serialize ACPX session record saves per record to stop overlapping renames (EPERM on Windows) (#16925)
> [!NOTE]
> Opened by Prospector's automated issue-fix pipeline. A maintainer
reviews and decides; nothing here merges on its own.

## Thinking Path

- Issue #15346 reports a defect in paperclipai/paperclip.
- Root cause, as the fixing agent read it: ACPX saves one session record
from several writers (live checkpoint timer, turn end, finalize, close)
with no coordination; each save renames a temp file onto the same record
file, and on Windows overlapping renames onto one file fail with EPERM.
All of these saves go through Paperclip's session-store wrapper, which
forwarded them concurrently.
- This pull request: Serialize ACPX session record saves per record to
stop overlapping renames (EPERM on Windows)

## What Changed

-
`packages/adapter-utils/src/acpx-engine/ephemeral-session-environment.ts`:
Every ACPX save in Paperclip passes through this wrapper, so it now puts
all saves of one record id in a single queue (a promise chain shared by
all wrappers in the process). Saves of one record never overlap and keep
their order; a failed save still returns its own error and does not
block the next one; saves of different records still run in parallel.

## Verification

- Reproducing test(s), added in this PR:
`packages/adapter-utils/src/acpx-engine/ephemeral-session-environment.test.ts`
- They fail on the base commit and pass with the fix, each twice in a
fresh sandbox.
- 3 independent attempts wrote 3 reproductions; this fix passes every
one of them.
- The repository's compile command passes with the fix applied.
- 3 existing test file(s) near the change pass.
- The full test suite passes except for the 246 file(s) that already
fail on the base commit.

## Risks

- Written by an automated pipeline, not a person: review it as you would
an outside contribution.
- Risk tier of the touched paths: 2.
- The scope reviewer found it also changes, beyond the report: save()
called when no earlier save of that record is pending
- The scope reviewer found it also changes, beyond the report:
persistedStore.save throwing synchronously
- The scope reviewer found it also changes, beyond the report: Records
with missing/undefined acpxRecordId
- The scope reviewer found it also changes, beyond the report: save()
for different record ids

## Model Used

- opus, sonnet (Anthropic Claude, via Claude Code), writing and checking
the change in an isolated sandbox.

## Linked Issues

Fixes #15346

Other pull requests that name #15346:
- #15370 (closed): fix(acpx): serialize session file saves and clean
failed temporary writes

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

<!-- prospector:issue-fix v1 issue=15346 base=1c07b5903b1b
report=4fe3fefc8864e00f -->

Co-authored-by: commitperclip[bot] <284812584+commitperclip[bot]@users.noreply.github.com>
2026-10-26 20:55:37 -07:00
585d37d4f2 [codex] Preserve AWS secret provider error classification (#10946)
## Thinking Path

> - Paperclip is the open source control plane operators use to manage
AI-agent companies.
> - Operators can store managed secret values in AWS Secrets Manager
provider vaults.
> - The AWS JSON gateway already classifies safe provider failures such
as access denial, expired credentials, throttling, and invalid requests.
> - The outer provider wrapper was re-normalizing those typed failures,
which collapsed an AWS access denial into a generic provider error
before the service could produce actionable guidance.
> - Missing runtime credentials also need to be recognized as an access
problem so operators receive the correct recovery path.
> - This pull request preserves typed provider errors, expands
credential-related classification, and clarifies the safe credential/IAM
remediation text.
> - The benefit is that failed AWS secret creation tells operators
whether to restore runtime credentials or grant the required capability
without exposing AWS account or principal details.

## Linked Issues or Issue Description

No public issue was found for this exact remaining server-side
classification gap.

Bug report:
- What happened: an AWS Secrets Manager access-denied response could be
classified correctly by the JSON gateway and then collapsed to
`provider_error` by the outer provider wrapper; missing runtime
credentials were also reported generically.
- Expected behavior: safe typed AWS failures should retain their
classification, and credential resolution failures should surface as
access-denied guidance.
- Steps to reproduce: configure an AWS provider vault with invalid or
missing runtime credentials, or without `secretsmanager:CreateSecret`,
then create a Paperclip-managed secret.
- Paperclip version/commit: current `master` before this PR.
- Deployment mode: Paperclip server with an AWS Secrets Manager provider
vault.

Related prior work:
- Refs #9161
- Refs #9645

## What Changed

- Preserve `SecretProviderClientError` instances returned by the AWS
JSON gateway instead of wrapping them as generic provider failures.
- Classify missing, expired, invalid, or incorrectly signed AWS runtime
credentials as access-denied failures.
- Clarify sanitized remediation guidance to cover both credential
resolution and `secretsmanager:CreateSecret` authorization.
- Add regression coverage for gateway access-denied responses, missing
runtime credentials, and the service-level actionable message.

## Verification

- `pnpm exec vitest run
server/src/__tests__/aws-secrets-manager-provider.test.ts
server/src/__tests__/secrets-service.test.ts` — changed provider tests
passed; the service file was skipped in the first no-DB focused run.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- `pnpm -r typecheck` — passed after a frozen-lockfile dependency
refresh.
- `pnpm build` — passed.
- `pnpm test:run` — 314 files / 3,443 tests passed; one unrelated
existing test failed in `plugin-orchestration-apis.test.ts` because it
observed `heartbeat.scheduling_suppressed` instead of `issue_commented`.
The same failure reproduced in isolation and none of this PR's four
changed files participate in that test.
- `git diff --check public-gh/master...HEAD` — passed.

## Risks

Low risk. The change affects only error classification and sanitized
failure guidance for AWS Secrets Manager operations. Successful secret
writes, schemas, migrations, API shapes, and stored secret material are
unchanged. The regression tests assert safe classifications while
keeping raw AWS account and principal details out of service responses.

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

## Model Used

OpenAI Codex, GPT-5 family (exact runtime build and context-window size
were not exposed), with reasoning, repository editing, shell execution,
Git, GitHub, and test execution enabled.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes (not
applicable: no documented contract changed)
- [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: commitperclip[bot] <284812584+commitperclip[bot]@users.noreply.github.com>
2026-10-26 20:54:18 -07:00
fengqing 05218571c3 fix(watchdogs): reject incomplete subtree scans (#15063)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task watchdogs verify stopped issue subtrees and restore valid work
paths.
> - A watchdog must read the full subtree before it can prove that all
paths stopped.
> - The current query stops after 100 levels and does not report omitted
children.
> - A running child beyond this limit can be absent from both review
dispatch and mutation checks.
> - This pull request reports an incomplete scan and blocks both
decisions.
> - Complete subtrees keep their current behavior and stop fingerprints.

## Linked Issues or Issue Description

Fixes: #15061

Related PRs: #11327 changes subtree loading but keeps the same depth
limit without a completeness result. #10331 changes mutation freshness
after watchdog writes. Neither patch covers this boundary. This patch
does not take code from those proposals.

## What Changed

- Keep the existing 100-level traversal limit.
- Check for an eligible child beyond the boundary in the same SQL
statement as the subtree read.
- Apply the same company, visibility, harness, and watchdog-origin
filters to this check.
- Return `incomplete` without a stop fingerprint when the scan omits
eligible children.
- Skip review dispatch and reject watchdog mutation revalidation for an
incomplete scan.
- Report incomplete scans through their own count and watched issue IDs.
Log each newly incomplete issue at startup or during periodic
reconciliation. Suppress unchanged results. Allow a new warning after
the issue recovers and becomes incomplete again.
- Add real PostgreSQL regressions for dispatch, a previously valid
mutation scope, the complete boundary leaf, and excluded children.
- Document the incomplete-scan behavior.

## Verification

- Base: `569c7203aa24b95440682983ce7940ba1d4247bd`. Head:
`06f68199656f979ef5eb4897851f0fbcbca592d3`.
- Before the fix, two real PostgreSQL regressions fail. The service
dispatches a review for a partial subtree. It also accepts a prior scope
after a running depth-100 child appears. The other 19 scheduler checks
pass.
- On the exact final head, `pnpm -r typecheck` and `pnpm build` pass on
isolated Ubuntu with Node 24.19.0, pnpm 9.15.4, and runner Rust 1.97.1.
- All 75 related tests pass. Run `pnpm --filter @paperclipai/server exec
vitest run src/__tests__/task-watchdogs-classifier.test.ts
src/__tests__/task-watchdogs-scheduler.test.ts
src/__tests__/server-startup-feedback-export.test.ts
src/__tests__/issue-watchdogs-routes.test.ts --maxWorkers=1`.
- Full `test:run` coverage passes across 29 logical groups: all official
stable-runner lanes plus supplemental service and script tests. The
selection covers all 900 server source test files without duplicate or
omitted selections. It also includes UI, CLI, and other workspace
projects.
- A separate validation tree applies both this PR and #15067 to upstream
`994d6edcdd4e15d5f9cc5cf8c135ac599104b86a` without conflicts. Type
checks, the build, and all 98 related tests across five files pass. Both
public PR heads remain unchanged.
- Four full server lanes required test-process environment corrections.
The host DNS search suffix resolved a `.invalid` fixture to an address.
Ancestor project authentication settings affected hire fixtures. The old
path-isolation tool also failed test imports with `EFAULT`. The exact
base has the same failures: 8 failed and 128 passed assertions without
isolation, then three failed imports with the old tool. All 136 checks
pass on both the base and this head with the corrected isolation. All
four original full lanes pass with the corrected tool, a resolver
without a search suffix, empty ancestor project settings, and the
standard Node path. No assertion or wait limit changed. The host
configuration remains unchanged.
- Earlier interrupted Ubuntu and Docker runs are retained and are not
counted as passes. This exact-head Ubuntu run passes the original chat,
port-conflict, and serialized lanes. The causes of the earlier host-loss
and timing failures are not fully established.
- The startup import regression in an earlier revision is fixed by a
diagnostics module with no database dependencies. All 21 startup checks
pass.
- Exact-head CI, including the added scans after marking the PR ready:
54 successful checks and 2 skipped checks. Greptile: 5/5, no outstanding
findings or follow-ups. Both review threads are resolved.
- The final audit rechecked issue authors, assignees, comments, 206 open
watchdog search results, root functions and fields, source history, and
related diffs. The latest upstream is
`994d6edcdd4e15d5f9cc5cf8c135ac599104b86a`. Its new patches do not cover
this root. No competing root repair was found within the audited scope.
The issue has no assignee or new comment.
- No model provider, production database, or user instance was used.
This verifies service behavior, not a full HTTP authorization bypass.

## Risks

- A subtree with eligible children beyond 100 levels now waits instead
of running a review. This is a deliberate safety boundary. It remains
blocked until the scan can include the full subtree.
- The boundary check adds a child-existence lookup to the existing
recursive query.
- No schema migration is required. Complete subtrees retain their
fingerprint format.
- This does not add unlimited traversal or close the separate race
between a subtree snapshot and later source mutations.
- The configuration lifecycle defect in #15060 is handled by separate PR
#15067. This branch does not include its schema or configuration
changes.

## Model Used

- OpenAI Codex, based on GPT-6, assisted with source analysis,
implementation, and test execution. The exact serving model ID and
context window are not exposed in this session. The agent used
reasoning, shell tools, GitHub CLI, and real PostgreSQL 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
- [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
2026-10-26 20:53:47 -07:00
Andrii Furmanets 61ecc93033 fix: count a recovered shutdown interrupt as recovered (#15031)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The dashboard success rate counts recovered runs as successes
> - A run is recovered when a later retry in its chain succeeds
> - The summary puts `failed` and `timed_out` into recovered
> - An `interrupted` run, including `server_shutdown_interrupted`, falls
through to other
> - The success rate stays down after the retry succeeds
> - This pull request counts a recovered interrupt in recovered, not in
other
> - The benefit is that a shutdown that was retried successfully no
longer looks like a failed day

## Linked Issues or Issue Description

No open issue covers this. I searched open and closed issues and pull
requests.

**What happened?**

`dashboardService.summary` puts only `failed` and `timed_out` into the
recovered bucket. An `interrupted` run, including
`server_shutdown_interrupted`, goes to other even when `recovered` is
set. The success rate uses `succeeded + recovered`, so the day stays low
after a successful retry.

**Expected behavior**

When the recovered flag is set, count `interrupted` in recovered. An
interrupt with no successful retry stays in other.

**Steps to reproduce**

1. Record an interrupted run with error code
`server_shutdown_interrupted`.
2. Record a succeeded retry of that run.
3. Open the dashboard summary for that day.
4. Observe that the interrupt is in other and the success rate does not
include it.

**Paperclip version or commit**

master at 215586d127

**Deployment mode**

Local dev (pnpm dev)

## What Changed

- When a run is recovered, count status `interrupted` in the recovered
bucket.
- Leave an unrecovered interrupt in other.
- Add a service test for a recovered shutdown and a stranded shutdown.

## Verification

- `pnpm exec vitest run src/__tests__/dashboard-service.test.ts --config
vitest.config.ts` in `server`
- Result: 1 file passed, 5 tests passed

## Risks

- Low risk. Unrecovered interrupts stay in other. Failed and timed-out
runs keep the same buckets.

## Model Used

- No model runtime is part of this change.

## Checklist

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

> - The Paperclip board is used from phones, and
`ui/public/site.webmanifest` is what a browser reads when someone adds
the board to a home screen.
> - That manifest asks for `display: "browser"`. Chrome installs a web
app only when `display` is `fullscreen`, `standalone` or `minimal-ui`;
with `browser` it answers `manifest-display-not-supported` and adds a
shortcut rather than an app, so every launch keeps the address bar.
> - The user interface already implements the no-browser-chrome case.
`StandaloneBrowserControls` renders Refresh, Share and Open in Browser,
and it is rendered only when `mobile && isChromelessDisplayMode()`.
`ui/src/lib/pwa-display-mode.ts` exists for no other purpose than to
detect `standalone`, `fullscreen` and `window-controls-overlay`.
> - None of those modes can occur on a mobile launch while the manifest
says `browser`, so that component and its fallback controls are
unreachable in practice.
> - That makes the manifest value look like the outlier rather than the
intent, so the change is one manifest field plus the test that pins it.

## Linked Issues or Issue Description

Refs #2139 — "enh: Officially support Install as Progressive Web App",
which asks for a supported home-screen install. This pull request is the
installability half of that request; it does not touch notifications.

**Subsystem affected**

The web app manifest and the unit test that guards it:
`ui/public/site.webmanifest` and `ui/src/lib/pwa-install-mode.test.ts`.
No server, plugin, or database code.

**Problem or motivation**

Chrome's installability criteria require `display` to be one of
`fullscreen`, `standalone` or `minimal-ui`. With `display: "browser"`
Chrome reports `manifest-display-not-supported`, offers only a shortcut,
and the launch keeps browser controls, so the board never behaves like
an installed application even though the manifest, service worker, and
icons all exist.

The same value also makes existing code unreachable: the in-app
replacement controls for a chromeless launch
(`StandaloneBrowserControls`, gated on `mobile &&
isChromelessDisplayMode()`) cannot be triggered from a mobile
home-screen launch, so the app carries a fallback that nothing can
exercise.

**Proposed solution**

- Set `display: "standalone"` in `ui/public/site.webmanifest`.
- Update `ui/src/lib/pwa-install-mode.test.ts`, which asserted `display
=== "browser"`. The assertion now pins `standalone` and a comment
records why, so the next reader does not have to reconstruct this
reasoning. The assertions that keep the legacy `mobile-web-app-capable`
and the Apple metas out of `index.html` are unchanged.

`standalone` rather than `fullscreen` for two reasons. It keeps the
Android status bar showing the
clock and battery, and it avoids a known Android behaviour of
`fullscreen` launches: opening the soft
keyboard triggers a fullscreen transition and a visible flicker ([Google
issue 392925620](https://issuetracker.google.com/issues/392925620),
[Stack Overflow
71606355](https://stackoverflow.com/questions/71606355/pwa-does-not-resize-window-when-virtual-keyboard-is-active-on-fullscreen-display)),
which `standalone` does not do.

**Alternatives considered**

- `fullscreen`. Also installable, and it additionally hides the Android
status bar. Rejected as the default for that reason; it is a one-word
change if preferred.
- Adding the legacy `mobile-web-app-capable` meta. Rejected: Chrome
reads the manifest, so the meta is redundant.
- Adding the Apple `apple-mobile-web-app-capable` metas for iOS.
Rejected for this change: it is iOS-specific, and iOS is not the
platform whose installability check this fixes.
- Changing `orientation`, which #5564 reports. Rejected as out of scope;
this pull request stays on `display` only.

**Roadmap alignment**

`ROADMAP.md` does not plan this work. Its only mention of install is the
skills store.

**Additional context**

- #7792 attempted a standalone PWA earlier and was closed during triage.
It bundled a deploy-pipeline change and a heartbeat change with the
manifest change, and its title carried an internal ticket id. This pull
request is deliberately the manifest change alone.
- #5564 reports that the manifest's `orientation: any` ignores the
Android rotation lock. Related, not addressed here.
- The keyboard flicker above was reported against a board installed with
`fullscreen`, which is one of the two
reasons this pull request asks for `standalone` instead of the more
immersive mode.

## What Changed

- `ui/public/site.webmanifest`: `display` is now `"standalone"` instead
of `"browser"`.
- `ui/src/lib/pwa-install-mode.test.ts`: the expectation that pinned
`browser` now pins `standalone`, with the reasoning in a comment; the
meta assertions are unchanged.

No runtime code, no dependency, and no data change. The icons the
installability check also requires (`android-chrome-192x192.png`,
`android-chrome-512x512.png`) are already present and unchanged.

## Verification

- `vitest run src/lib/pwa-install-mode.test.ts
src/lib/pwa-display-mode.test.ts` in `ui`: 2 files, 7 tests passed (the
same 2 + 5 the sibling plugin-event change reports).
- The manifest parses as JSON, keeps `display: "standalone"`, and keeps
192 and 512 pixel icons.
- The only place in the repository that asserts the manifest's `display`
value is the test updated here; the other references to
`site.webmanifest` are request routing and HTTP log policy and are
unaffected.
- CI on the previous head of this branch had two failures unrelated to
this change: an e2e hover-opacity assertion on a sidebar star button
(`Star Zeta`: expected `1`, received `0`) and `TypeError: Cannot read
properties of undefined (reading 'logicalEffectCount')` in the runner
verify job. Both are in subsystems this change cannot reach; CI is
re-running on this commit.

## Risks

- Low. One manifest field and one test expectation; no runtime code
changes.
- The value applies to new installations. An app already installed with
the previous value keeps its installed configuration until it is
reinstalled, so nobody loses a working install.
- iOS launches are unchanged: Safari does not take `display` from the
manifest for home-screen launches, and this change does not add the
Apple metas.
- Nobody depends on the address bar being present in an installed
launch; the in-app controls that replace it
(`StandaloneBrowserControls`) are already implemented and become
reachable.

## Model Used

- Provider: DeepSeek. Model ID: `deepseek-v4-flash`. The model ran as a
tool-using coding agent (shell, file edits, headless browser automation)
inside a coding harness. The harness did not report a context-window
size or a reasoning mode.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and issues and
linked them above (#2139, #7792, #5564)
- [x] I have either (a) linked existing issues with `Fixes:` / `Closes`
/ `Refs #` OR (b) described the issue in-PR following the relevant issue
template — both, see above
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change and contains no internal
ticket id or instance-derived details
- [x] I have run the affected tests locally and they pass (2 files, 7
tests)
- [x] I have added or updated tests where applicable — the guard test is
updated, and it is the only test that pins this value
- [x] I have updated relevant documentation to reflect my changes — no
user-facing document describes the manifest; the updated test now
records the reasoning in place
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green — CI is re-running on this commit
after the unrelated failures described under Verification
- [ ] Greptile is 5/5 — awaiting the review run on this commit
- [x] I will address all Greptile and reviewer comments before
requesting merge
2026-10-26 20:52:20 -07:00
mdc2122andmdc2122 8153061d71 fix(pi-local): strip inherited server PAPERCLIP_* env from pi_local processes (#14503)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The `pi_local` adapter spawns the Pi CLI as a child process for
agent runs, environment tests, and model discovery
> - Each of these three paths builds the child env with `{
...process.env, ...env }`, so the child inherits the full server
environment
> - The server environment can hold server-only `PAPERCLIP_*` secrets
and config paths, such as `PAPERCLIP_AGENT_JWT_SECRET`,
`PAPERCLIP_TOOL_ACTION_SIGNING_SECRET`, and
`PAPERCLIP_SECRETS_MASTER_KEY_FILE`
> - `@paperclipai/adapter-utils` already has
`sanitizeInheritedPaperclipEnv`, and the shared `runChildProcess` path
uses it for its base env. `pi_local` passes its pre-merged env as
`opts.env`, which `runChildProcess` spreads over that sanitized base, so
the inherited server values come back
> - This pull request routes the three `pi_local` merges through
`sanitizeInheritedPaperclipEnv`
> - The benefit is that `pi_local` agent processes no longer receive
server secrets. This is a security hardening change.

## Linked Issues or Issue Description

No issue exists. This is a bug report in the in-PR format.

**What happened?**

A `pi_local` agent process receives the inherited `PAPERCLIP_*`
variables of the Paperclip server process. An env dump from inside a
`pi_local` run showed `PAPERCLIP_AGENT_JWT_SECRET`,
`PAPERCLIP_TOOL_ACTION_SIGNING_SECRET`,
`PAPERCLIP_SECRETS_MASTER_KEY_FILE`, and server config path variables.
The agent process can read these values.

**Expected behavior**

The `pi_local` child env drops inherited server `PAPERCLIP_*` variables,
the same as the shared `runChildProcess` path. Only
`PAPERCLIP_RUNTIME_API_URL`, `PAPERCLIP_LISTEN_HOST`, and
`PAPERCLIP_LISTEN_PORT` stay inherited. The run-scoped variables that
the adapter sets (for example `PAPERCLIP_AGENT_ID` and
`PAPERCLIP_API_KEY`) and the adapter-configured env still reach the
child.

**Steps to reproduce**

1. Start the Paperclip server with `PAPERCLIP_AGENT_JWT_SECRET` set in
its environment.
2. Run a `pi_local` agent whose command writes `process.env` to a file.
3. Read the file. `PAPERCLIP_AGENT_JWT_SECRET` is present.

**Agent adapter(s) involved**

`pi_local` only.

**Additional context**

Other CLI adapters appear to merge `process.env` into the child env in a
similar way (for example in some `execute.ts` and `test.ts` files). This
pull request does not change them. It keeps the scope to `pi_local`.

Related: #3030 and #12870 also touch inherited agent env. This pull
request is independent of #14479.

## What Changed

- `packages/adapters/pi-local/src/server/execute.ts`: the run env and
the runtime env that the adapter logs now start from
`sanitizeInheritedPaperclipEnv(process.env)` instead of `process.env`.
- `packages/adapters/pi-local/src/server/test.ts`: the environment test
env now starts from `sanitizeInheritedPaperclipEnv(process.env)`.
- `packages/adapters/pi-local/src/server/models.ts`: the model discovery
env (`pi --list-models`) now starts from
`sanitizeInheritedPaperclipEnv(process.env)`.
- `server/src/__tests__/pi-local-execute.test.ts`: add a regression
test. The test sets three server secrets on `process.env`, runs
`execute` with a fake Pi command that dumps its env, and checks that the
secrets are absent. It also checks that the adapter-configured env and
the run-scoped `PAPERCLIP_AGENT_ID` and `PAPERCLIP_API_KEY` are present.

## Verification

- `cd server && npx vitest run src/__tests__/pi-local-execute.test.ts
src/__tests__/pi-local-adapter-environment.test.ts
src/__tests__/pi-local-skill-sync.test.ts`: 7 tests pass.
- The new test fails without the source change (the secret assertion
fails) and passes with it.
- `npx vitest run packages/adapters/pi-local`: 42 tests pass.
- `pnpm --filter @paperclipai/adapter-pi-local typecheck`: pass.
- `cd server && tsc --noEmit` (after `pnpm --filter
@paperclipai/plugin-sdk ensure-build-deps`): pass.

- Follow-up coverage observes real child environments from direct model
discovery, agent execution, and `testEnvironment`. Each path omits
inherited secrets and retains the permitted runtime/listen variables and
configured env. Removing either the model-discovery or environment-probe
sanitizer independently makes the regression fail. All 49 scoped tests
and both direct typechecks pass.

## Risks

- Low risk. A `pi_local` setup that relied on an inherited server
`PAPERCLIP_*` variable (other than the three kept keys) no longer gets
it. Such a value can still be set in the adapter `env` config. This is
the same behavior as the shared `runChildProcess` path.

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

## Model Used

- OpenAI GPT-6.1 Sol, exact runtime model ID
`gpt6sol-cliproxy/gpt-6.1-sol`, with tool use and code execution.
Context-window and thinking-level metadata are not exposed. The prior
model and human-review disclosure was not substantiated by available
runtime metadata.

## Checklist

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

---------

Co-authored-by: mdc2122 <259970464+mdc2122@users.noreply.github.com>
2026-10-26 20:49:07 -07:00
馨冉and馨冉 a02be1efc3 fix(continuation): degrade a stale interruptedRunId hint instead of failing the wake (#14807)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - A wake builds its continuation envelope from a chain of
resume-source run ids: explicit user resume, retry chain, previous run,
then the interruptedRunId hint
> - interruptedRunId is a best-effort hint written when a run is
interrupted, and it is not cleared when an issue is reassigned to
another task
> - When the hint names another task's run (or a deleted row), the
envelope build threw continuation_source_context_missing, so the new
assignee's wake could never continue the work
> - This pull request degrades a stale interruptedRunId hint to "no
resume source" when it is the only source tier and does not resolve to
this task, while explicit user resume and retry chains stay fail-closed
> - The benefit is that cross-issue reassigns no longer brick an issue's
continuation wakes

## Linked Issues or Issue Description

Fixes #14723

## What Changed

- `server/src/services/execution-continuation.ts`: when
`interruptedRunId` is the only resume-source tier and the referenced run
is missing or belongs to another issue, continue without a resume source
instead of throwing `continuation_source_context_missing`
- The degraded envelope reports `trigger.sourceRunId: null` and no
longer re-echoes the stale `interruptedRunId` into the new envelope (so
the next wake does not inherit the poisoned hint)
- Explicit user resume (`explicitUserContinuation.previousRunId`),
`retryOfRunId`, and `previousRunId` tiers keep the existing fail-closed
throw (`continuation_user_authorization_missing` for explicit user
resume)
- `server/src/services/execution-continuation.test.ts`: the "rejects
handoff history from a different task" test is rewritten to the intended
degraded behavior, plus a deleted-row case and a fail-closed control
test for explicit user resume

## Verification

- `cd server && npx vitest run
src/services/execution-continuation.test.ts` — 35/35 pass (33 prior
tests plus 2 new; 1 existing test rewritten to the new intended
behavior)
- `npx tsc --noEmit` reports no errors in the touched files
- `git diff --check` is clean

Manual reasoning check: the degrade branch only fires when
`interruptedRunId` is the sole defined tier (`explicitUserSource`,
`retryOfRunId`, `previousRunId` all null) and the run does not resolve
to this task. Every other tier keeps the previous throw, covered by the
new control test.

## Risks

- Behavioral change: a wake that previously failed with
`continuation_source_context_missing` for a stale interrupted-only hint
now continues without resume history. This is the intended fix per the
issue; the fail-closed invariant is preserved for all user-driven and
retry source tiers
- The `interruptedRunId` no longer propagates into the envelope when
stale, so downstream consumers no longer see the stale id (they would
previously only see it when the envelope build succeeded, which it never
did for this case)

## Model Used

Codex (OpenAI Codex CLI, GPT-5 based coding agent, repository-aware tool
use, ~1M context window, extended reasoning), assisted by three
read-only explorer sub-agents for issue triage and duplicate checking.

## 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: 馨冉 <xinxincui239@gmail.com>
2026-10-26 20:48:14 -07:00
Nicky Leach ea727dc3c2 Preserve plugin session continuity through warm execution and checkpoint recovery (#17820) 2026-10-26 20:25:10 -07:00
DottaandPaperclip dcfc2acfa8 Preserve complete bounded OpenCode MCP tool results (#17825)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/34/changes/paperclip/34
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip manages AI agents and their governed tools.
> - Native OpenCode receives tool results through a Runner-owned MCP
bridge.
> - The bridge cut serialized results at 64 KiB, even inside a JSON
string.
> - A tool could finish while its saved OpenCode output lost files and
receipts.
> - Native app results already use bounded results and saved-result
pages.
> - This change preserves complete JSON within a bounded output limit.
> - Agents can read the result without repeating the completed action.

## Linked Issues or Issue Description

**What happened?**

The OpenCode MCP bridge sliced serialized JSON at 65,536 characters. A
large file-list result became invalid JSON in OpenCode's saved output.
The tool execution itself had completed.

**Expected behavior**

The bridge must deliver complete bounded JSON. A delivery limit must
produce an explicit error, rather than a partial success value. A
repeated call identity must not execute the action again.

**Steps to reproduce**

1. Return a structured result with 27 file entries and duplicated
text/data representations larger than 64 KiB.
2. Call the tool through the actual HTTP MCP bridge.
3. Parse the returned text as JSON. The old source fails at position
65,536.

Related: #17740 changed assigned-tool catalog delivery. This PR repairs
result delivery and does not change the tool catalog or authority.

## What Changed

- Replace JSON slicing with complete serialization and a 1 MiB UTF-8
output limit.
- Bound serialization traversal, including escaped strings and many
small fields.
- Keep the existing native 500 KB result/page and saved-artifact
contracts.
- Report oversized delivery with a warning that the action may already
have completed. Keep call-identity replay on the original execution.
- Add real HTTP regressions and document the transport limit.

## Verification

- Red before fix: the large-result HTTP regression fails with an
unterminated JSON string at position 65,536.
- 91 focused MCP, OpenCode driver and continuation assertions pass on
Node 24.22.1. They cover escaped UTF-8 pages, oversized delivery,
authentication, cancellation and duplicate-call execution.
- Workspace typecheck and build pass.
- Actual native Rust → fixed MCP bridge → OpenCode 1.18.34 →
`openrouter/deepseek/deepseek-v4.1-flash-1017` passed with a public
synthetic 27-file result. One assigned read produced a 281,375-byte
saved JSON file. The model used Bash/Python to parse all filenames and
both unpredictable receipt fields. One finish completed the turn.
- The native probe used a synthetic gateway and a mocked completion
acknowledgment. It does not prove hosted deployment, board finalization
or a particular upstream model vendor.
- The full default test attempt is failed/incomplete. It was stopped
after repeated embedded PostgreSQL bootstrap failures and skipped
database suites. Its 15 warm-workspace cases pass unchanged on isolated
PostgreSQL 18. A controlled default-database rerun fails before the test
body when embedded PostgreSQL cannot start after five attempts. The test
source is byte-identical to the base commit. The failed attempt remains
recorded.
- The final test-only commit checks a 500 KB escaped producer page
inside the actual controller text envelope. The production bridge hash
still matches the native proof. Its 91 focused tests and package
TypeScript check pass.

- Current-head verification (`73d18ca2`): 53 CI checks passed and two
conditional Storybook checks skipped. Greptile is 5/5 on this exact head
with no review threads. The failed/incomplete full local attempt and the
controlled passing suites above remain separate evidence.

## Risks

- Larger complete results use more bounded serialization memory than the
old truncated output.
- A bridge result above 1 MiB fails delivery. Native app results and
saved-result pages are bounded at 500 KB before this bridge. The
existing artifact protocol handles larger results.
- A failed delivery cannot prove whether an upstream action took effect.
The error forbids repeating it, and identical call identities retain the
original execution.
- This change does not alter model routing, admitted tools, credentials
or trust policy. Hosted rollout and incident recovery remain separate
verification steps.

## Model Used

- OpenAI `gpt-6.1-sol`, with reasoning, tool use and code execution. The
session did not expose an authoritative context-window size. Native
acceptance used the exact DeepSeek model named above.

## 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 relevant local tests and they pass; the full
local-suite environment limitation is documented above.
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-26 22:23:05 -05:00
DottaandPaperclip db32bdf98b fix(github): recover review replay and preserve passive summaries (#17726)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/8/changes/paperclip/8
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - GitHub tools save review assessments and publish their confirmed
receipts.
> - A timeout before publication staging can leave an invocation with no
saved publication.
> - Exact replay must repeat only the authorized preflight and converge
on one publication.
> - PR requests also share an App-owned summary comment. Progress and
maintenance must preserve its final content.
> - A native turn can intentionally wait for an author without sending
another reply.
> - This pull request repairs unstaged replay and prevents queued or
passive-wait notices from replacing a published assessment.

## Linked Issues or Issue Description

Related repairs: Refs #16883, Refs #16284, Refs #16275 recover saved
publication receipts. Refs #17183 covers unavailable approved replay
output. Refs #17714 fixes a separate publication lock-order problem.

**What happened?**

Exact review retries can return null `begin_review` output or
`github_publication_unavailable` after an upstream read times out before
publication staging. Separately, response maintenance can replace a
completed PR assessment with “Review queued…” or a generic
missing-response notice after an authorized no-new-work reply. The
latter run can correctly commit a passive wait while its external
summary is still overwritten.

**Expected behavior**

Exact retries repeat only the authorized preflight. A saved or uncertain
publication keeps its existing recovery owner. Queued acknowledgements
preserve final App-owned summary content. A committed native passive
wait settles its current acknowledgement without writing a response or
treating an old summary as a new-run reply. Success before presentation
remains held until a durable decision exists; exact current-run
confirmed receipts can retire their placeholders. Genuine missing/error
responses retain fallback handling.

**Steps to reproduce**

1. Hold a GitHub metadata read until an authorized gateway call times
out before publication staging. Repeat the exact request.
2. Publish a valid PR assessment. Send an ordinary author reply that
asks for no new work and no duplicate comment.
3. Commit the native `response_wake` with a no-comment server
presentation decision. Run response maintenance twice.
4. The base returns the invalid replay result or replaces the assessment
with progress/fallback text.

**Paperclip version or commit**

Replay regression: `41c42895fa862c586ff841f8c50200d93ffc83a4`.
Maintenance regression: the previous PR head
`c7553d2325df53d98d9febff7023ade4b1e704eb`. Both use real PostgreSQL and
simulated GitHub responses. The maintenance failure was also observed in
normal hosted use.

**Deployment mode**

Source checkout on macOS arm64 with isolated PostgreSQL 18, followed by
a dedicated QA qualification. No production rollout is included.

## What Changed

- Route exact `begin_review` replay through its typed owner. Repeat
unstaged preflight only with the complete request digest and no
successful invocation result.
- Preserve current policy, immutable assessments, unique action keys,
receipt recovery, and provider leases.
- Preserve a verified App/marker-owned final shared PR comment during
acknowledgements. New canonical assessments can still replace it at the
same or a new head.
- Project only a bounded server-committed native wait decision. Settle
the current main-PR acknowledgement without provider writes or a
fabricated current-run reply receipt.
- Hold succeeded native runs while presentation is undecided, including
the committed-before-presentation window. An exact current-run confirmed
receipt can still retire its placeholder.
- Keep missing/error fallback and uncertain publication checks. Update
the bot guide, execution semantics, maps, and real-database regressions.

## Verification

- **Causal RED:** exact begin replay returned null; exact unstaged
submit replay returned unavailable. The new real-database maintenance
test then reproduced the exact generic summary overwrite. Its first
repair also exposed an earlier queued-acknowledgement overwrite. Both
publication boundaries are repaired. Four additional ordering
regressions reproduced the generic PATCH while native presentation was
undecided; the corrected tests hold until the same row settles.
- **Local GREEN:** 196 complete GitHub workflow cases pass on an empty
migrated PostgreSQL fixture. This includes queued/running preservation,
both success-before-presentation windows, null/scalar/invalid decisions,
exact-current versus predecessor receipts, durable acknowledgement
settlement, repeated no-op maintenance, eventual genuine missing/error
fallback, pending writes and same/new-head canonical replacement.
Workspace typecheck, exact-head build, maps and whitespace checks pass.
The real database respects the actual selected-column projection.
- **Command:** `PAPERCLIP_TEST_DATABASE_URL=<owned-postgres-18> pnpm
--filter @paperclipai/server exec vitest run
src/__tests__/chat-channels.integration.test.ts -t 'GitHub agent review
workflow'`.
- **CI/review:** historical heads
`c7553d2325df53d98d9febff7023ade4b1e704eb` and
`4d00d3af6f2ccbda8f38a98f81d392d82dd3c59c` had Greptile 5/5. Final
readiness head `2d267f1e4aa0b835a11aede97798fbd800d9cd96` passed
independent source review with no actionable finding and fresh Greptile
5/5 with zero unresolved threads; all 54 check runs pass with 2 intended
skips; Snyk is successful and the merge state is CLEAN. The dedicated QA
destination acceptance also passed.
- **Full-suite limitation:** the earlier `pnpm test:run` attempt
produced no final summary. Its retained log reports failures in
unchanged workspace-runtime tests and 15-second gateway-audit and
stale-question timeouts. Their cause remains unknown. Do not treat the
targeted results as a full local-suite pass.
- **Live evidence and limits:** normal native OpenCode/DeepSeek review
delivery published a useful assessment, inline finding and App check at
the exact reviewed head. A separate ordinary author reply delivered a
useful answer and settled. Those are ordinary delivery proofs, not live
injected-timeout proofs. A later no-new-work reply exposed the
maintenance overwrite. The final combined source
`070e9c4ead9341cd497b23f533a91bf194475e79` includes the exact
publication repair and passed normal review → ordinary author reply →
quiet destination acceptance. A normal current-head review restored the
legitimate assessment; the ordinary no-new-work reply then committed a
passive wait without new GitHub tools. The same 1,436-character summary
stayed identical while queued/running, after terminal settlement, and
more than 3 minutes 50 seconds after successful cleanup. Review rows and
receipts stayed unchanged, with no owner, live run or queued input.

The timeout fault fixtures prove replay and single-publication safety.
The historical first upstream timeout producer remains unknown. The live
board reads do not expose the raw acknowledgement action row: its
terminal cancellation and next-pass no-op are real-PostgreSQL fixture
proofs, while the hosted checks prove actual destination stability. The
hosted journey did not inject a timeout or pause the
terminal/presentation writer window. Private destinations, raw receipts
and internal task identifiers remain outside this PR.

## Risks

- Original and repeated metadata reads can overlap. The immutable
assessment, action uniqueness and publication lease must remain
authoritative.
- Missing complete request identity or a deleted completed receipt
remains held. The gateway does not reconstruct receipts or relax
approval.
- Passive suppression requires actual native mode/committed phase,
current source ownership and the server's no-comment decision. It
applies only to the main PR review surface. Uncertain current writes
stay held; other surfaces keep normal placeholder retirement.
- There is no migration, credential change, dependency or new retry
scheduler.

## Model Used

OpenAI GPT-6.1 Sol (`gpt-6.1-sol`) in Codex assisted with diagnosis,
implementation, tests, and source review. It used reasoning, repository
tools and code execution. The 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
- [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-26 22:22:57 -05:00
DottaandPaperclip 208a093d19 fix(chat): preserve resumed review waits and retry newer saved input (#17725)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/36/changes/paperclip/36
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - GitHub review agents publish findings and wait for an authorized
reply or head change.
> - Review tools could retain the requester after resume while
completion lost that same wait proof.
> - A yielded report could then queue its own continuation or repeat a
missing-wait error.
> - Ordinary Retry could also select an older failed turn while a newer
saved request had no admission.
> - This change shares the existing authority proofs and canonical
admission owners.
> - Current access, publication, execution and budget gates remain
required.

## Linked Issues or Issue Description

Refs #17318. Builds on the canonical saved-input retry owner in #17733.
Related #16550 covers deferred legacy comments after ownership waits.
The separate recovery repair in #17724 owns the cleanup-to-saved-input
handoff; this PR does not add a second admission owner.

A resumed review could publish an assessment, then turn its
`response_wake` into an immediate `issue_status_changed` wake. A
provider that omitted its continuation could instead repeat a completion
error. A newer linked-human message saved during a budget hold could
also remain unstarted because Retry selected the older failed source.

## What Changed

- Resolve passive review waits through the accepted requester ledger
used by GitHub tools. Require the latest reviewed head, exact company,
task, agent, repository and processed publication under current access.
- Fill omitted/null yielded continuation from that server proof. Permit
an inherited normal tree resume only through its accepted parent
identity. Permit an ordinary follow-up only after this run has a
processed useful reply linked to its exact inbound message. Reject
acknowledgements, partial assessments, pending or uncertain writes as
that reply proof.
- Recheck the same run/review/head/source proof during feedback,
finalization and final status commit. Preserve explicit malformed-input
rejection and Done policy. Add no monitor permission or publication
grant.
- Keep heartbeat and publication projections narrow. Preserve both
published Copilot v62 identities and generate source-bound v63 from the
pinned merged base. No v63 live qualification is claimed.
- Certify the structured isolated-directory binding failure only at its
pre-allocation boundary and only through canonical task Retry. Other
workspace validation reasons and unknown effects remain denied.
- Route Retry to eligible newer saved input through the existing
canonical owner. Preserve the old result and skipped receipt. Require
current principal, source, App, runtime fence, budget, native ownership
and exact cleanup proof.
- Recover absent historical App metadata only through an earlier trusted
processed response receipt and an immutable non-null endpoint claim.
Explicit malformed or conflicting metadata stays denied.
- Revalidate execution through the exact claimed wake, coalesced task
Retry and resolved user-continuation receipt after admission. A second
normal Retry can recover the exact never-started authorization failure
with unchanged saved input. Require empty execution evidence, exact
diagnostics and no allocated or active executor.
- Explain when a recorded result prevents exact replay. Ordinary board
Retry directs a new task message to continue from saved work. Inherited
requester identity alone never grants replay.
- Keep accepted board-message preparation failures with the canonical
task Retry owner. Certify the exact unchanged comment, original user
wake, accepted same-user identity and absence of provider execution. Pin
that source, recheck it at admission, and bind identity initialization
to the coalesced intent and exact claimed successor receipt.
Uncertifiable sources retain an actionable fresh-message refusal.
- Return safe named authority categories for board Retry refusals.
Update execution documentation and capability maps. Add no schema or
polling loop.

## Verification

- A fresh external review request exposed a separate isolated-directory
preparation failure before allocation. Its first normal Retry saved an
intent but admitted no successor. This follow-up certifies only that
exact structured preparation boundary through the existing canonical
Retry owner, with trusted failed settlement and no executor, lease,
result or output. A real HTTP/queue regression reproduces the missing
successor before the repair. Twenty-three current-source safety cases
and the final 98-case Retry/HTTP group pass, including a fresh click
after a denied intent, source revocation, accepted results and the older
direct API remaining denied. The warning case uses actual configuration
preparation and production event append; unknown warning shape or
provider provenance stays denied. Full workspace typecheck, build, maps
and diff checks pass. Renewed hosted normal Retry now succeeds on the
final combined source: it preserves the exact saved request and linked
requester, starts one native successor in a fresh attempt workspace,
publishes the current-head 2/5 assessment and commits In review with
passive response wake.

- A later hosted normal Retry published a low-score assessment but lost
its accepted source identity because the previous attempt failed before
identity initialization. It repeated continuation errors, then submitted
Done. This is failed acceptance, not a successful qualification. The
initializer now retains the exact source only after execution-time
failed-chat Retry authorization; generic Retry and Done policy are
unchanged. A real queue/initializer regression fails with a null source
on the previous code and passes with the newer saved source. Two real
database completion cases verify that source yields the authoritative
wait and commits In review. Renewed hosted normal Retry now retains the
accepted source and exposes the authoritative current-review wait. It
completed after two semantic feedback corrections (wrong contract
revision, then wrong criterion IDs); the correct contract was present in
emitted input. The third finish was accepted and committed In review,
with no missing-wait loop or Done escalation.
- Repeated board-message preparation failures now walk their canonical
intent, failed dispatch and accepted parent identity, rechecking each
contained predecessor. A production queue regression fails the second
ordinary HTTP Retry before the repair and passes afterward. Changed
source, contradictory execution evidence and cyclic receipts remain
denied; traversal stops after twenty links.

- New inherited omission cases failed before the repair. The final full
PostgreSQL 18 wait/status suite passes **252 tests** under Node 24.22.
It covers omitted/null input, malformed-first recovery, same-head
resume, exact-source replies, changed heads, revoked access,
pending/uncertain/mixed publications, final commit revalidation, no
duplicate review and no self-wake.
- **15 existing publication tests passed before the pinned merge** on a
separate fresh database. They retain default partial-delivery and
failure-fallback behavior. Context reads remain below the unchanged 16
KiB heartbeat and publication response limits with large saved output
present.
- Saved-input tests cover a real signed PR webhook, production
accounting-budget refusal, normal policy recovery and canonical Retry.
Historical producer-shaped tests prove the missing-App case red, then
check trusted prior receipts and malformed, late, revoked or mismatched
proof. A broader order-coupled test was reproduced on the prior source;
its global fault injector now targets its own authorization query. The
43-case sequence passes without changing production behavior or its
safety assertions.
- New signed-ingress and real heartbeat execution tests reproduced the
admitted-successor failure, then the next normal Retry failure. The
final pinned merged source passes 249 full GitHub review/publication and
Retry cases on a fresh database, including all 49 saved-input cases. A
separate 74-case Retry/HTTP run also passes. Exact dispatch negatives
cover source, company, agent, task, successor, receipt, pause,
assignment, accepted success, controller and cleanup proof. Bootstrap
contradictions remain denied. The canonical Retry suite passes 16 cases
through its supported external PostgreSQL test connection.
- The normal board HTTP route reproduced the unusable inherited-result
refusal. It now returns actionable guidance without starting a
successor. The actual UI Retry hook and banner tests pass 12 cases.
- A production-shaped accepted board comment followed by a pre-provider
qualification failure reproduced HTTP 409 before the repair. The final
real HTTP/queue test certifies one canonical successor and preserves the
accepted identity. It rejects changed source, actor, task, agent,
result, controller, process, profile, allocation and receipt evidence. A
post-acceptance source change keeps the same intent pending. Execution
revalidation rejects removed or mismatched proof. The probe stops
deliberately after production identity initialization; it does not claim
provider execution. The final source passes the 75-case Retry group, the
focused two-case board/inherited-policy group and 19 canonical Retry
cases. Full workspace typecheck, build and map checks pass.
- Full workspace `pnpm -r typecheck` and `pnpm build` pass. Map,
module-boundary and diff checks pass. Full local `pnpm test:run` remains
unverified. The 256-case explicit-native suite cannot initialize its
packaged PostgreSQL because libzstd is absent; an existing
command-scoped fallback did not resolve it. Current-head remote
full-suite checks are required. Merged-source full GitHub+Retry (249),
separate Retry/HTTP (74), full wait/status (252), profile/history and
completion (75), and visible Retry hook/banner (12) checks pass. The
fresh owned PostgreSQL 18 databases applied all 377 migrations.
- Actual Rust Runner, OpenCode 1.18.34 and exact DeepSeek used unchanged
production-generated synthetic contexts. Resume finished with an
explicit server wait. The author-reply trial omitted continuation twice:
its first report failed the existing empty-criteria check; the corrected
second report still omitted continuation and received the exact server
wait. It completed once with no missing-wait loop or Done escalation.
Controller acknowledgments were synthetic; these probes do not establish
hosted publication or board state.
- Hosted combined testing proved automatic admission of the exact saved
author reply, a useful GitHub answer and committed passive wait. It
required two malformed-continuation rejections before a nullish fallback
succeeded. A later normal Pause/Resume completed in one finish with
explicit response_wake and no new review/comment. The admitted normal
Retry then exposed the execution guard gap repaired here. Those remain
checkpoint results. On the final combined source, renewed normal Retry
completed actual native provider work and GitHub publication, committed
the passive wait, released ownership and cleaned up its environment. A
subsequent ordinary unmentioned author reply woke once, made no GitHub
tool calls or new assessment, and completed its first finish with an
explicit response wake. Both exact leases expired with successful
cleanup and the queue was empty. However, final destination inspection
found a separate automatic fallback overwrote the existing summary with
a generic no-final-response message. External summary preservation
therefore remains failed acceptance; the separate publication repair in
#17726 owns that producer. Full combined publication preservation has
not passed. These hosted runs include the pinned-base native additions;
they do not qualify Copilot v63.
- Current-head automated review is 5/5 with zero unresolved threads. All
current-head CI gates passed (54 successful checks/contexts, four
skipped, none pending). The earlier login-cleanup idle-work assertion
also reproduced locally while its controlled baseline passed; its cause
remains unknown, but the exact assertion passed in the fresh
current-head CI shard. No timeout, assertion or production workaround
was added. This PR is ready for review within its source, authority,
native finalization, state, wake and cleanup scope. The independent
automatic fallback publication regression is tracked by #17726; no
complete combined workflow pass is claimed.

## Risks

- The latest saved review defines the durable head boundary. Ingress
must record a new head before admission. A reply receipt proves delivery
only for its exact saved source; a shared GitHub comment ID alone cannot
authorize this wait.
- Historical App recovery cannot infer identity without direct proof or
an earlier valid server response receipt. It never replays that
response.
- This does not infer review-score policy from instructions or override
Done. No deployment, merge or repair of historical tasks is claimed by
the PR.

## Model Used

OpenAI `gpt-6.1-sol`, with reasoning, repository inspection, code
editing and test execution. The 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
- [x] I have run tests locally and they pass (focused database and
native suites; full local suite limitation described 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 (verified on immutable latest
head)
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(verified on the latest head)
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-26 22:22:47 -05:00
DottaandPaperclip f0877a9c7c fix: recover native work and requalify saved computers (#17724)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/32/changes/paperclip/32
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip manages AI agents and preserves their work after an
interruption.
> - Native Stop must confirm termination and save the workspace.
> - Task and run views must show the current recovery receipt.
> - A new external reply can arrive while that cleanup is pending.
> - The reply must return to its original chat owner after cleanup.
> - A successful fresh retry also becomes the task’s bound workspace for
its next ordinary turn.
> - Managed computer updates can invalidate a saved snapshot
qualification.
> - This pull request fixes those instructions, acknowledgements,
handoffs and the existing native-agent Test path.
> - The operator can continue with new input while the stopped turn
remains preserved.

## Linked Issues or Issue Description

**What happened?**

Stopped native runs could keep the initial workspace-repair instruction
after files were recovered. Native Stop could also leave the shared
cancellation state at requested after remote termination was confirmed.
A new GitHub reply saved during cleanup could then remain queued because
cleanup did not ask its original inbound owner to reconsider the
receipt. After a managed snapshot update, an existing native agent could
also need qualification while the generic environment Test required
instance-admin access.

**Expected behavior**

Show the current saved-work state. Acknowledge Stop only after exact
termination proof. Let an authorized operator use the existing
saved-agent Run test to qualify its current computer. After cleanup,
recheck the saved reply's current authority and admit it once as new
input. Preserve the stopped run and uncertain external effects.

**Steps to reproduce**

1. Stop an active native turn before it returns a semantic result.
2. Send a new authorized external reply while cleanup is pending.
3. Let workspace recovery and provider cleanup finish. Inspect the run
instruction, Stop acknowledgement and queued reply.

Related work: #17594 preserves native processes through controller
outages. #17401 addresses a separate bootstrap failure. This pull
request does not replace those fixes.

## What Changed

- Use the exact run's workspace recovery receipt for task and run
guidance, including the Activity ledger.
- Retire an obsolete exact-session notice through the existing bounded
reconciler. Preserve the separate no-replay hold.
- Write the shared native Stop acknowledgement only after the audited
Stop intent and every exact remote lease receipt match.
- Delegate saved external input to the live inbound owner. Recheck its
source, actor, endpoint, principal and conversation generation. Promote
the same receipt in the transaction that creates its new run.
- Use that owner from the cleanup callback and existing stranded-queue
catch-up after a lost callback or restart. A denied source cannot abort
other items.
- Reuse the current private task directory after a successful fresh
retry. Re-read the company/task/workspace binding and validate the
canonical stable or attempt namespace without recreating missing files;
keep a later fresh retry separate.
- Extend the existing agent-scoped qualification owner to native agents.
The clean saved-agent Test qualifies its exact computer before runtime
testing. Reuse a current proof; require a real probe and verified
cleanup for a stale proof. Preserve draft tests and metadata-only
external/pool previews.
- Update execution semantics, capability maps and the recovery
verification recipe. Add a production-page Storybook interaction for the
normal native Test.

## Verification

- **Red:** persisted recovery states returned stale instructions; an
exact terminated native lease left shared Stop requested; the real
GitHub subscribed-message fixture left zero successors after cleanup.
The controlled baseline restored the previous production source,
reproduced the failure, then restored the fixed bytes with matching
SHA-256 hashes.
- **Green:** 382 durable-chat, native-continuation, Stop and recovery
tests pass. The real GitHub/PostgreSQL handoff matrix passes 12/12. It
covers callback, application restart, callback/sweep race, valid input
behind a deleted source, and denial for unknown stop, wrong allocation,
paused endpoint, revoked principal, deleted or changed source, changed
conversation generation and foreign scope. Repeated callbacks preserve
one receipt, its actor, one successor and the unchanged stopped
predecessor.
- **Workspace continuation red/green:** the real PostgreSQL preparation
flow created a fresh attempt and saved a marker, then rejected the next
ordinary turn with the same binding mismatch observed in deployed use.
The repaired preparation and filesystem suites pass 86/86: stable and
attempt-root reuse, saved-marker preservation, a later clean sibling,
changed current bindings, foreign company/task, closed workspace,
traversal, missing path and symlink refusal. No task row reset or
old-turn replay is used.
- **Qualification red/green:** a native saved-agent qualification
returned 422, and its normal Test skipped qualification. The repaired
path passes 224 qualification/immutable-snapshot/environment tests and
176 UI/upgrade tests. It covers revoked authority, foreign or changed
computer, current-proof reuse, failed/missing proof, draft settings,
settings changed during the probe, and external/pool previews. On exact
current source, the real local Harness / Runtime UI also qualified a
newly saved disposable Computer with missing proof through actual
Daytona, then completed the actual OpenCode/OpenRouter hello probe using
`openrouter/deepseek/deepseek-v4.1-flash-1017`. Supported availability
changed from Legacy-only to Paperclip Runner after proof persistence.
Qualification and runtime leases both completed normal release/cleanup.
The five prior task run IDs were unchanged; no task wake was created.
This is macOS source-server/UI with real Linux Daytona and provider
services, not a hosted UI claim. Test probes are archived by the
existing inspection contract; release does not claim physical deletion.
- Full workspace `pnpm -r typecheck`, `pnpm build`, token, map and diff
checks pass at `f6450f4739fc8141815b8b7c35fde22a655c5f90`. The broad
local `pnpm test:run` passed 24,899 tests and reported one existing
nested-clone test timeout at 15 seconds. The complete failing file then
passed 21/21 with the normal timeout; the exact case also passed on the
pinned earlier baseline. The broad runner stopped before its later
groups. No timeout or production workaround was added. Prior head
`f6450f4` has 53 successful checks, two conditional Storybook skips and
Greptile 5/5 with no inline threads. New workspace-reuse head
`fcb13399ec70f92f71bd8250081251dca3f48962` passes workspace typecheck,
full build, 86 scoped tests, maps and diff checks; Current-head CI has
53 successful checks and two conditional Storybook skips; Greptile is
5/5 with no inline findings. Deployed ordinary-turn reuse acceptance
passed as described below.

- **Hosted Stop proof:** the real GitHub issue-mention path used native
OpenCode, `openrouter/deepseek/deepseek-v4.1-flash-1017`, Daytona and an
exact low-trust task boundary. Normal UI Stop interrupted an active Bash
call. Native and shared acknowledgements matched the exact lease.
Automatic recovery saved exactly `STARTED\n` (8 bytes), with no finish
marker. Reload showed Stopped and saved work recovered. Only the
original cancelled run remained. Normal five-minute cleanup completed
its exact provider-destruction transition.
- **Limits:** the provider buffered command output, so the marker was
read after recovery. The approved local provider credential could not
inspect the hosted sandbox; its 404 is not deletion proof. Cleanup
success comes from the exact lease's platform destruction transition. An
earlier hosted stale instruction disappeared after rollout with
unchanged raw run/action data; that historical cause remains
unexplained. Original incident holds and publication receipts were
untouched.
- **Hosted handoff proof:** after QA deployed the combined candidate
containing `46b2e99`, startup admitted the previously saved GitHub
author reply without another comment, Retry or manual action. The new
native run used the exact original wake receipt and saved comment. It
completed once, committed an external-response wait, preserved the
stopped predecessor, emptied the queue and left the task In review. The
answer appeared in the original GitHub conversation. Workspace
finalization and the exact lease's normal destruction transition
succeeded.
- **Hosted workspace proof:** on the combined candidate containing
`fcb13399`, normal Retry preserved the original authenticated GitHub
request and completed a fresh native attempt. One later ordinary
same-head author reply then completed in the exact same workspace ID and
attempt directory, with no Retry/fresh directive. Its input tree digest
matched the Retry's finalized digest; final copyback preserved that
digest. The persisted assessment row and all five prior run records were
unchanged, exactly one new reply run completed, and the task returned to
In review with an empty queue and no live run. Both exact leases
completed normal verified destruction. This used real native OpenCode,
the exact DeepSeek/OpenRouter model and Daytona, without workspace-row
repair or a new remote marker.
- **Publication limit:** the workspace audit compares the persisted
assessment and run history. The ordinary reply exposed a separate
destination-summary fallback that changed the visible GitHub summary
despite the preserved assessment row. The publication track is repairing
that behavior; this proof does not claim the visible summary was
preserved.
- **Deployment boundary:** current-head CI/review and hosted same-source
Retry/ordinary-workspace reuse acceptance passed. The separate
visible-summary publication failure remains owned by the publication
track. Combined hosted UI confirmation of the native-agent Test path is
coordinated separately. Actual missing-proof UI/provider qualification
has passed in the isolated local instance above; prior hosted Stop and
same-input handoff proof remains separate. No merge or deployment is
performed by this pull request.

## Risks

Recovery state and termination receipts must match the exact source run
and allocation. Missing or foreign proof keeps the hold. The chat owner
must retain the original receipt and actor and recheck current
authority. Cleanup cannot grant authority or replay the stopped turn.
Existing native ownership, Stop, budget, permission and uncertain-effect
guards remain in place.

## Model Used

OpenAI GPT-6.1 Sol (`gpt-6.1-sol`) through Codex, with reasoning,
repository tools, source editing and test execution. The 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
- [x] I have run tests locally and they pass (affected suites and
isolated failing file pass; broad timeout retained 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 (current fcb has 53 successful
checks/two conditional Storybook skips)
- [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-26 22:21:58 -05:00
Devin FoleyandPaperclip 832c44f26a fix: resume user follow-ups that reassign a task (#17821)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The execution control plane saves user messages while an earlier run
needs recovery.
> - A user comment can also assign a task back to its agent.
> - That update creates an assignment wake, but continuation accepted
only comment wake reasons.
> - The saved question could stay blocked after workspace recovery and
confirmed process cleanup.
> - This pull request admits assignment wakes that carry a valid,
undelivered user comment through the existing continuation checks.
> - The same rule applies to new messages and saved requests already
waiting for recovery.

## Linked Issues or Issue Description

**What happened?**

A human follow-up that also assigns a task back to its agent produces
`issue_assigned`. After a native run stops during handoff, the
continuation policy rejects that wake reason. The question remains saved
behind the execution recovery hold even after the workspace is recovered
and the old sandbox is destroyed.

**Expected behavior**

Deliver the saved human question once after the existing stop,
ownership, authority, and execution gates pass. Assignment alone must
not grant permission to continue.

**Steps to reproduce**

1. Finish task work by assigning it to a human for review. Let the
native run stop with `issue_reassigned` and retain its recovery hold.
2. Recover the workspace and confirm destruction of the old sandbox.
3. Post a new human question while assigning the task back to the agent.
4. Observe that the assignment wake stays deferred instead of delivering
the question.

Refs: #17288, #14156. These related changes cover legacy reassignment
and assignments without comments. This change covers a human comment
carried by an assignment wake behind a native or legacy continuation
hold.

## What Changed

- Share the comment-bearing wake classification between continuation
admission and saved-request recovery.
- Accept `issue_assigned` only through the existing persisted
user-comment validation and execution gates.
- Add database regressions for new and saved assignment wakes,
concurrent recovery, single delivery, and unchanged prior run results.
- Check that bare assignments, invalid authors and comments, changed
ownership, active execution, and pending approvals or questions retain
their holds. Cover native and legacy cleanup paths.
- Update execution semantics and both recovery maps.

## Verification

- Red: both new and saved assignment-with-comment cases stayed deferred
without a successor on the original code.
- Green: the final continuation suite passes all 284 tests. The four
companion recovery, retry, and queue suites pass another 208 tests.
- `pnpm -r typecheck` and `pnpm build` pass.
- `git diff --check`, `node scripts/check-maps.mjs`, and `node
scripts/check-module-boundaries.mjs` pass.
- Apex review is 5/5 on `56c9c2d90dcda55b3f760946c9aba6aee448c361`, with
no unresolved review threads. The only earlier finding concerned the
contribution rule against new source comments; those comments were
removed.
- Full `pnpm test:run` is still running locally. CI is waiting for the
`paperclip-public-pr-x64` runner fleet; the superseded workflow was
cancelled so the current head could proceed. No passing CI claim is made
yet.
- Live recovery: corrected one existing saved wake to the supported
comment reason. The normal server admitted one successor with the exact
saved question and prior-run context, then started real tool calls. This
verifies admission on the existing deployment; the new source change is
not deployed yet. The resumed turn later failed with a separate provider
terminal event whose error was null, followed by a workspace restore
failure. The task is blocked again on that failure. Its useful answer
has not been delivered, so end-to-end completion remains unverified.

## Risks

- Assignment wakes now reach continuation validation when they carry a
comment. The persisted comment must still belong to the task and
requesting human, follow the stopped run, and remain undelivered.
- The existing stop proof, cleanup, current assignment, approval, pause,
budget, and competing-execution checks remain effective. Regression
cases cover both native and legacy paths.
- No schema migration or data rewrite is required for saved requests.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, source inspection, tool use,
and code execution. The runtime does not expose the exact model ID or
context window size.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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.1027.0-canary.3
2026-10-26 20:19:16 -07:00
DottaandPaperclip 4336b9ee02 fix(db): reserve transactions before checking pipeline capacity (#17831)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Interactive requests share a PostgreSQL connection pool with
background work.
> - Prepared queries can queue behind a slow query on a busy connection.
> - Disabling pipelining lets queued work use the next free connection.
> - The driver skips its transaction reservation callback when the
pipeline limit is zero.
> - This pull request runs that callback before the capacity check.
> - Operators can test a pool without pipelining while transactions
retain their connection ownership.

## Linked Issues or Issue Description

**What happened?**

With `PGMAX_PIPELINE=0`, postgres.js 3.4.9 skips `onexecute` in its
short-circuit capacity check. A transaction then lacks its reserved
connection. Real PostgreSQL reproduces `UNSAFE_TRANSACTION` through the
app client and an undefined connection error through the raw driver.
This prevents safe use of the existing setting to reduce query queue
delays.

**Expected behavior**

A transaction must reserve its connection regardless of pipeline
capacity. Commit, rollback, savepoints, and disconnect recovery must
preserve their existing contracts.

**Steps to reproduce**

1. Use the pre-change driver patch from
`e61ee4b005f8e13f64af15f69cb62305dd869f82`.
2. Start disposable PostgreSQL and run the `no-pipeline` scenario in
`packages/db/src/__fixtures__/postgres-connection-recovery.mjs` with
`TEST_DATABASE_URL`, `DRIVER_FORMAT=esm`, and
`RECOVERY_CASE=no-pipeline`.
3. Observe the transaction failure. Apply this patch and repeat with
both `esm` and `cjs`.

Refs #13643, which introduced the existing connection recovery patch.
Searched related open and closed driver PRs. This change extends that
patch and preserves its disconnect safeguards.

## What Changed

- Move the transaction reservation callback before the pipeline capacity
check in both driver distributions.
- Add real PostgreSQL regressions for commits, concurrent transaction
reads, isolation, rollback, savepoints, reserved connections, and
progress on a freed pool connection.
- Document the existing `PGMAX_PIPELINE=0` setting and its driver
prerequisite. Keep the default pipeline limit unchanged.

## Verification

- `pnpm -r typecheck`: passed.
- `pnpm build`: passed.
- Real PostgreSQL 18.6 in an owned, disposable Docker container: the old
patch fails the new transaction scenario in both ESM and CommonJS. The
corrected patch passes all 18 scenarios, including all 12 existing
disconnect recovery cases. The container was removed afterward.
- Local service benchmark: actual `createDb` with prepared statements,
two warmed connections, two controlled advisory lock waits, and the
complete three-query `getDefaultCompanyGoal` path. Two warmups and five
alternating measured samples per setting. Pipeline 100: median 413.44
ms, range 413.33–416.10 ms. Pipeline 0 with the patch: median 12.22 ms,
range 11.86–12.57 ms. Complete SQL, bindings, and results match. Five of
five reads finish while the unrelated slow query remains blocked. These
are synthetic local queue measurements, not hosted latency claims.
- App client compatibility on real PostgreSQL: commit, concurrent
transaction reads, rollback, nested savepoint rollback, dedicated
connections, and liveness connections passed.
- `pnpm test:run`: attempted, then stopped after repeated local embedded
PostgreSQL startup failures caused by the host semaphore limit. This is
an incomplete local full-suite run, not a pass. The focused driver
checks above used disposable Docker PostgreSQL instead. The initial
sandbox attempt also stopped before test execution because tsx could not
bind its IPC socket. Final CI results will be recorded here.
- Current head `72039f71f63530300bf241475d42e857297dfb90`: Greptile and
Paperclip review are both 5/5. All three earlier findings are resolved.
[CI run
40431567516](https://github.com/paperclipai/paperclip/actions/runs/40431567516)
ran the complete validation matrix. The database lane passed all 265
tests, including all 18 real PostgreSQL driver regressions. One cleanup
timer case timed out in server shard 6/12; its isolated real-PostgreSQL
reproduction passed once and failed once, while five runs with the
pre-change driver passed. That evidence is retained and does not
establish the timer failure's cause. A single CI retry of the failed
shard passed. The final aggregate `ci / verify` gate is waiting for a
runner. Its first queued request did not start for more than 14 minutes.
A guarded queue reset confirmed that it was the only unfinished job and
that every test job had passed; attempt 3 restarts only that gate. No
test result was replaced or bypassed. The required gate must pass before
merge.

## Risks

- This changes a shared driver callback order. Both module formats and
existing disconnect recovery are covered by real database tests.
- This PR does not change pool size, enable the setting by default,
retry SQL, or change schemas. Disabling pipelining may reduce batch
throughput. Operators must compare the workload before retaining that
setting.
- The local queue benchmark proves the mechanism. Hosted request
improvement still needs acceptance after the patched image is deployed.

## Model Used

OpenAI GPT-6 in Codex, with code execution and browser developer tools.
The exact serving model identifier and context window are not exposed in
this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] 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-27 03:00:01 +00:00
Nicky Leach e61ee4b005 fix(runner): preserve Claude user settings trust in HOME (#17826) canary/v2026.1027.0-canary.2 2026-10-26 18:02:15 -07:00
Nicky Leach baec3abd67 Authorize background project Git with explicit Connector consent (#17798) canary/v2026.1027.0-canary.1 2026-10-26 17:22:49 -07:00
6874fed05c feat(adapters): refresh model catalogs and sandbox CLI pins (Oct 23 audit) (#17809)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/26/changes/paperclip/26
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents run on coding harnesses. Each adapter has a model catalog,
and sandboxes contain pinned CLIs.
> - Upstream providers released new CLI versions and new models. OpenAI
retired some Codex models for ChatGPT sign-in.
> - A stale catalog hides valid models. A misleading error makes
operators upgrade a CLI that is already current.
> - This pull request adds verified OpenCode model IDs, names Codex
ChatGPT retirements in the environment Test, and refreshes the sandbox
CLI pins.
> - The benefit is that operators can select current models and get the
correct reason when a retired model fails.

## Linked Issues or Issue Description

Refs #16708 (adds Claude Haiku 5.5 to `claude_local`; this PR does not
duplicate it).
Refs #17245 (moves the Codex runtime to 0.163.0; this PR does not change
Codex pins).

**What happened?**

OpenAI retired `gpt-5.5` (October 14, 2026) and `gpt-5.4` /
`gpt-5.4-mini` (August 31, 2026) from Codex with ChatGPT sign-in. A
saved agent with one of these models and ChatGPT sign-in now fails. The
legacy environment Test says that the plan, the rollout, or an old CLI
can be the cause. The Paperclip Runner Test gives generic account and
runtime advice. The real cause is the retirement.

**Expected behavior**

Both Tests give the retirement date and the documented replacement.
API-key users keep the models.

**Proposed change**

Record the verified retirements. Add the new verified OpenCode IDs.
Refresh the sandbox CLI pins with verified digests. Record the audit.

## What Changed

- Codex: add a table of models retired from Codex with ChatGPT sign-in
(`gpt-5.5` → `gpt-6.1-sol`, or `gpt-6-luna` on Free and Go; `gpt-5.4` →
`gpt-6-sol`; `gpt-5.4-mini` → `gpt-6-luna`). The shared
`codexChatGptRetirementHint` builds the hint. The legacy CLI Test
(`codexHelloProbeModelRejectionCheck`) and the Paperclip Runner Test
(`testNativeRunnerAuthentication`) both use it when the backend rejects
one of these models. For other failures, the Paperclip Runner Test hint
stays the same. The picker keeps the models for API-key users.
- OpenCode: add `anthropic/claude-haiku-5-5` and `google/gemini-4-argon`
to the remote fallback catalog. The Paperclip Runner OpenCode picker
also uses this catalog.
- Evaluation image sandbox layer (`docker/daytona-runner`):

  | CLI | Before | After |
  | --- | --- | --- |
  | Grok CLI | 1.0.46 | 1.0.70 |
  | Gemini CLI | 0.62.0 | 0.65.0 |
  | Kimi Code | 2.1.1 | 2.3.3 |
  | Cursor CLI | 2026.10.01-e373342 | 2026.10.22-27dcf5f |
  | GitHub CLI | 2.102.0 | 2.103.3 |

- Add `doc/adapter-model-audit-2026-10-23.md` with sources,
compatibility findings, and deferred items.

No model in this PR needs a newer Codex, Claude, or OpenCode runtime
than the current pins. Claude Haiku 5.5 needs Claude Code 2.1.293 or
later; #16708 records that floor, and this PR does not add it.

## Verification

- `vitest run src/index.test.ts src/server/test.remote.test.ts
src/server/acp.cli-version.test.ts` in `packages/adapters/codex-local`:
41 passed.
- `vitest run src/services/native-runtime/setup-readiness.test.ts` in
`server`: 98 passed. These include the two new Paperclip Runner Test
cases: a retired model shows the retirement hint, and a model that is
not retired keeps the generic hint. The retirement case fails on the
earlier code. Run with the host's empty `ANTHROPIC_BASE_URL` unset; see
the baseline note in the audit doc.
- `vitest run` in `packages/adapters/opencode-local`: 87 passed.
- `vitest run src/__tests__/adapter-models.test.ts` in `server`: 27
passed.
- `tsc --noEmit` in `packages/adapters/codex-local` and
`packages/adapters/opencode-local`: passed.
- Cursor archive SHA-256
`85296944e4600a83c4b764f578d1502bf9c416db200d33a593e8f3ecabfdc294` is
computed from the versioned Linux x64 tarball. The extracted binary
reports `2026.10.22-27dcf5f`. On October 26, 2026,
`https://cursor.com/install` still resolves this version.
- GitHub CLI SHA-256
`4c806978ffce1cec70853302a56d70ac6da4f19bae7adca4e1a8ab00633dd91d`
matches `gh_2.103.3_checksums.txt`.
- Grok CLI 1.0.70 and Kimi Code 2.3.3 installed from npm report those
versions. The flags that Paperclip uses (`-m`, `--effort`, `grok
models`; `-m`, `-p`, `--output-format`, `acp`) are present in `--help`.
- `git merge-tree` with #16708 (head `3a06740e9`) and #17245 (head
`41f9c611f`): this PR shares no files with either PR. The two PRs can
merge in any order.
- Not run in the authoring environment: the full `pnpm -r typecheck` and
`pnpm build` (no Rust toolchain), the full server `tsc` (memory limit),
and the image build. CI runs these.

## Risks

- Low risk for catalogs: the IDs are additive suggestions. No saved
selection, default, or spending setting changes.
- The Paperclip Runner Test changes only the hint text, and only for a
ChatGPT rejection of a retired model. Check codes and pass/fail results
do not change.
- Sandbox CLI pins:
- Kimi Code 2.2.0 and 2.3.0 remove the `kaos` MCP executor and require
qualified plugin skill names. Paperclip uses neither. Kimi Code 2.3.2
stops automatic swarms in `kimi -p` and ACP sessions.
- Gemini CLI 0.65.0 makes workspace settings read-only in untrusted
folders. Paperclip writes home settings and sets
`GEMINI_CLI_TRUST_WORKSPACE` for remote runs.
- xAI publishes no Grok CLI changelog. Grok 1.0.70 behavior changes are
verified only against the CLI reference and `--help`.
- The pins apply only after a new image is built and promoted. The fleet
sandbox image definition must get the same pins.
- Rollback: revert this PR.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`), Anthropic, with tool use (shell,
web research, repository search).

## Checklist

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

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

---------

Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
canary/v2026.1027.0-canary.0
2026-10-26 16:32:13 -07:00
Devin Foleyanddevinfoley cfed739f81 fix(external-objects): stop reporting GitHub rate limits as unreachable (#17792)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/11/changes/paperclip/11
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip is the open source app that people use to manage AI agents
for work.
> - Paperclip shows the status of external objects, such as GitHub pull
requests and issues.
> - A background sweep refreshes these objects and resolves a GitHub
credential.
> - The sweep read a credential only from a company secret with a
well-known name.
> - An instance without that secret made anonymous GitHub API calls.
Anonymous calls are limited to 60 per hour.
> - When the limit was reached, the sweep showed "unreachable" for
objects that it could not refresh.
> - A server environment token is instance-wide, so it must not expose a
private repository that a company cannot see.
> - This pull request reads a server token for public repositories,
reports a rate limit as its own state, and shows a warning on the pill.
> - The benefit is an accurate status, fewer misleading failures, and no
cross-company private data.

## Linked Issues or Issue Description

No public GitHub issue exists. The sections below describe the bug.

**What happened?**

The board showed "unreachable" for many GitHub links, even after a
related fix was deployed.

**Expected behavior**

The board should show "unreachable" only for real network failures. It
should show a rate limit as its own state and retry later.

**Steps to reproduce**

1. Use an instance with no GitHub token company secret and no server
token.
2. Let the external-object sweep make more than 60 anonymous GitHub
requests in one hour.
3. Open a task with GitHub links.

**Paperclip version or commit**

master (commit a67a18c at report time).

**Deployment mode**

Self-hosted server.

## What Changed

- Add `resolveGitHubApiCredential` in
`server/src/services/git-credentials.ts`. It reads a company secret with
a well-known name, then falls back to the server environment token
(`GITHUB_TOKEN`/`GH_TOKEN`), and it reports the credential `source`.
- Use the source in the GitHub external-object provider and in
`github-commit-details.ts`. A `server_env` token is limited to public
repositories. A private repository resolves only with a company
credential; otherwise the object reports `auth_required` and does not
fetch the private object.
- Add a `rate_limited` liveness state in
`packages/shared/src/constants.ts`.
- Classify a 403 with `x-ratelimit-remaining: 0`, a 429 response, and a
rate-limited repo probe as `rate_limited` instead of `unreachable`.
- Show `rate_limited` in the UI filter, icon, label, tone, overlay, and
badge maps.
- Make `ExternalObjectPill` and `ExternalObjectStatusIcon` prefer a
liveness warning icon and tone, so a rate-limited object shows a warning
even when the last status was healthy.
- Add tests for the credential source, the public-only server token, the
company-scoped private repository, and the rate-limited pill tone.

## Verification

- `vitest run server/src/__tests__/external-objects-service.test.ts
server/src/__tests__/git-credentials.test.ts
server/src/__tests__/github-commit-details.test.ts
server/src/__tests__/work-products.test.ts` — pass, with the new scoping
and credential-source tests.
- `vitest run ui/src/lib/external-objects.test.ts
ui/src/lib/issue-filters.test.ts
ui/src/components/ExternalObjectPill.test.tsx
ui/src/components/MarkdownExternalLink.test.tsx` — pass, with the new
rate-limited pill test.
- `pnpm --filter @paperclipai/shared typecheck` — pass. The full
monorepo `tsc` (server and ui) is killed by the 4GB memory cap of this
sandbox; the authoritative CI Typecheck gate runs it instead.
- One test in `git-credentials.test.ts` fails in this sandbox only. The
test "credential helper execution (real git, no network) ... env-carried
token" reads a global GitHub credential helper in the sandbox. The test
fails the same way on the base without this change.

## Risks

- Low risk. The change adds a liveness state and a credential source. It
does not change existing terminal verdicts.
- A server-environment token cannot resolve a private repository. This
is intentional. An operator who needs private repositories must add a
GitHub token as a company secret. The object then reports
`auth_required` with that instruction.
- `rate_limited` is a new value in `EXTERNAL_OBJECT_LIVENESS_STATES`. An
old client ignores an unknown value.

## Model Used

- Provider: DeepSeek (OpenRouter).
- Exact model ID: `openrouter/deepseek/deepseek-v4.1-flash`.
- Capabilities: tool use and code execution in the Paperclip runner.

## 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 similar open and closed PRs and
confirmed this is not a duplicate
- [x] I have either (a) linked existing issues with `Fixes:` / `Closes`
/ `Refs:` OR (b) described the issue in-PR following the relevant issue
template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] 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: devinfoley <139239+devinfoley@users.noreply.github.com>
2026-10-26 16:13:04 -07:00
DottaandPaperclip 32ec84db2e fix(native): restore plugin checkpoints for same-run retries (#17814)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Native sessions retain provider state across execution attempts.
> - A same-run retry can receive a replacement plugin task lease.
> - The executor loaded the old task checkpoint only for crash recovery.
> - An ordinary retry could fail its local authority check before
reading that checkpoint.
> - This change restores the checkpoint through the existing authorized
lease transfer.

## Linked Issues or Issue Description

**What happened?**

A native provider turn ended without its completion result. Paperclip
scheduled an exact-checkpoint retry on the same heartbeat run. The retry
acquired a new plugin task lease, then failed with
`runner_state_identity_mismatch: authority_indeterminate`.

The checkpoint restore branch required a `resume_dead_runner` restart
claim. Ordinary same-run retries have no such claim. A controlled
fixture with a retained remote checkpoint and no local lifecycle receipt
reproduces the error. The original controller file contents were
unavailable, so that part of the incident mechanism remains inferred.

**Expected behavior**

Load the retained plugin checkpoint after the existing policy proves the
lease transfer is safe. Keep the original run, session, and saved
control journal. Reject an unverified predecessor or a changed
checkpoint identity.

**Steps to reproduce**

1. Start a native session through a plugin task environment.
2. End its provider turn without a completion result and retain its
checkpoint.
3. Retry the same run on a replacement task lease without a restart
claim.
4. With no local lifecycle receipt, observe the authority error before
the checkpoint read.

**Paperclip version or commit**

Reproduced against source `507dc70be26c191e372f09e58dc0965e73bf8f57`.

**Deployment mode**

Native Runner with a plugin task environment. The observed provider used
ACPX Claude. The controlled executor fixture uses the shared native
boundary.

Related work: #17439 concerns recovery after controller replacement.
This fix uses the existing checkpoint contract and does not depend on
that PR.

## What Changed

- Restore prior plugin state for an ordinary same-run attempt when its
lease changes.
- Keep the existing company, agent, workspace, retained-state, and
termination checks.
- Extend executor regressions for ordinary retries, mismatched
checkpoints, and missing stop proof.
- Exercise the same-run checkpoint read through the database-backed task
API.
- Document the recovery rule and verification recipes.

## Verification

- Failing before: the ordinary retry reproduces
`authority_indeterminate`. Both new denial cases also fail because the
old branch never reaches the transfer policy.
- Passing after: all six focused checkpoint cases pass. The saved
control journal stays byte-for-byte unchanged. Rejected cases do not
construct a provider backend.
- `pnpm -r typecheck` passes.
- Map and module-boundary checks pass.
- All 858 executor, database lease-authority, plugin task, and ownership
tests pass. `pnpm build` passes.
- The broad local `pnpm test:run` attempt did not complete. It hit a
20-second timeout in the existing historical workspace-recovery HTTP
Retry case. That exact case then passed in isolation on the unchanged
head. The broad local attempt was stopped after the full CI matrix
passed; it is not counted as a local pass.
- Greptile reviewed current head
`5bc11fa978ab87d3d9c5d575fa9666185aaafc2c` at 5/5 with no actionable
findings. The full current-head CI matrix passed on attempt 2: 55 checks
passed, no pending or failed checks, and two expected Storybook jobs
skipped.
- The first broader local attempt could not bind embedded PostgreSQL
inside the sandbox (`EPERM`). It was stopped and restarted with loopback
access. This was a setup failure, separate from the observed red
regression.
- The first CI attempt failed two existing checks: the process-adapter
signoff happy path exhausted its run wait budget, and the workspace
export scheduler did not reach `complete` within 20 seconds. Both tests
pass in isolated local runs on the unchanged head. Both failed shards
passed on their single rerun without code changes. The initial failures
remain recorded. Their timing cause is not proven by the later passes.
- Live acceptance at the fixed revision has not run. The hosted
workspace still serves the earlier release. No deployment or retry of
the original task is claimed.

## Risks

The new checkpoint read runs only for same-run plugin recovery with a
replacement lease. Missing termination proof, changed scope, or a
changed Runner identity still stops execution. The lease resolver owns
these checks. The fix does not replay tools or change retry budgets. A
turn that omitted its completion result still needs the existing bounded
continuation to finish useful work.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository tools, and code
execution. The runtime does not expose a more specific backend 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-26 18:01:55 -05:00
DottaandPaperclip 27a87c0f22 perf(runner): keep routine journal flushes off the server thread (#17813)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native runners persist incoming events in the server process.
> - Routine progress and usage events can cause many full journal
writes.
> - Synchronous file flushes stop that process from serving unrelated
requests.
> - Output deltas already use asynchronous writes, but other frequent
events still block.
> - This pull request stages routine events privately and publishes them
after persistence.
> - Critical lifecycle events keep their existing ordering.

## Linked Issues or Issue Description

Refs: #17745, #17514.

**What happened?**

Page navigation, task saves, and archive requests stalled during native
runner output. A post-fix CPU profile still spent about 7 seconds of a
19-second window in synchronous event journal flushes. Progress and
usage events caused much of this traffic.

**Expected behavior**

Routine event file writes must let unrelated HTTP requests run. The
durable cursor must advance only after persistence. Completion and
semantic tool admission must retain their ordering.

**Steps to reproduce**

Run a native task that emits frequent tool progress and usage events.
Send HTTP requests while its volume flushes are slow. The controlled
tests hold a real file flush and send a real HTTP request during that
hold.

**Paperclip version or commit**

Reproduced after #17745 on master.

## What Changed

- Stage priority 1 and 2 event snapshots with asynchronous file writes
and flushes.
- Keep candidate events out of the public in-memory journal until the
durable commit finishes.
- Include a pending event in a command's existing durable write. Command
bursts do not cause repeated event writes or business callbacks.
- Reject stale candidates after an authenticated connection replaces
their owner.
- Keep priority 0 events, all run and turn lifecycle events, and
semantic tool input persistence synchronous, including terminals labeled
priority 1.
- Extend slow-volume tests for progress, usage, critical events,
concurrent commands, reconnects, write failures, drain, and replay.
- Update the durable event delivery documentation.

## Verification

- Controlled regression: the previous implementation failed the new
progress and usage slow-flush cases and exposed pending delta state. The
updated implementation passes all sixteen controlled cases.
- All 137 durable control-plane tests and three saved-continuation cases
pass. The continuation cases use the actual Rust bridge.
- Review regressions failed before the correction: a priority-1 terminal
took the asynchronous path and command writes did not absorb the pending
event. The correction passes these tests, a two-command burst with one
staged write, and a directory-flush failure after command/event rename.
- An authority-boundary regression also failed before its correction.
Explicit snapshot transactions now retire pending coalescing, so a later
command cannot publish an older event; the test verifies replay after
restart.
- An initial version made lifecycle events asynchronous too. The
saved-continuation tests caught missing final run receipts at
completion. Keeping priority 0 events synchronous fixes those failures.
This version does not claim asynchronous lifecycle support.
- Repository typecheck and build pass locally after the review fixes. At
the previous head `886f2b961f035071bd947b5116b1f7a2727ec17f`, run
`40404783081` passed the full CI suite, including 4,024 package runner
tests and 96 native-runtime tests. Final-head CI run `40409537748`,
attempt 2, passed. All 46 substantive jobs and both aggregate gates
succeeded. Runner lanes passed 4,032 package tests and 96 native-runtime
tests. The retry carried forward the successful substantive jobs from
attempt 1 and completed both aggregate gates.
- Greptile reviewed final head
`735245931450810246e3008231a29fd56d615f67` at 5/5 with no actionable
findings. The separate Paperclip review execution ended neutrally
without a new assessment. Its earlier command-churn finding is fixed and
its outdated thread is resolved; the documentation and priority-1
coverage comments are also addressed.
- The local wider runner suite passed 449 of 450 tests. The
retained-terminal replay case fails on both this candidate and unchanged
master at the same maintenance-proof check. Both local runs leave an old
terminal receipt pending behind the event backlog. These failed runs
remain recorded; they are not counted as passes.
- The broad local test command also failed one unchanged
workspace-runtime port-allocation case. That case passed in isolation.
Its cause is unresolved. The broad local run was stopped after the
complete CI test groups passed; no full local-suite pass is claimed.
- Live staging acceptance remains pending.
- The tests hold a real `FileHandle.sync`, verify an actual HTTP
response during the hold, and check disk and memory cursors. They also
verify command preservation and idempotent replay.

## Risks

- Asynchronous writes add time for concurrent commands or reconnects to
arrive. Commands absorb the event in their existing write. Revision
checks prevent stale publication. Other authority changes leave the
unacknowledged event for replay instead of retrying full snapshot
writes.
- ACKs still wait for the file flush, atomic rename, and directory
flush. Write errors still fence further writes until reload.
- Critical commits, JSON serialization, rename, and directory flushes
still run synchronously. A command's coalesced event uses that command's
existing synchronous commit. This change does not eliminate every
possible source of latency.
- No schema, API, dependency, or wire contract changes are required.

## Model Used

OpenAI GPT-6 in Codex, with reasoning, tool use, and code execution. The
session does not expose a more specific deployment 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-26 18:01:00 -05:00
Nicky LeachandPaperclip 835966dec8 Prepare agent-owned environments during hiring (#17761)
<!-- entire-trail-link-start -->
https://entire.com/gh/paperclipai/-/trails/1/changes/paperclip/1
<!-- entire-trail-link-end -->

## Thinking Path

> - Paperclip manages agents and their execution environments.
> - New agent setup saves an inactive agent before model connection
tests.
> - Some plugins need that agent to prepare its execution resource.
> - The wizard tested without the agent ID, while the setup hold
prevented preparation.
> - Environment changes also discarded the selected model account.
> - This change prepares the saved draft during setup and keeps normal
execution held.
> - Users can test the prepared resource before they finish hiring.

## Linked Issues or Issue Description

**What happened?**

An agent-owned plugin environment could not acquire a test lease during
hiring. Changing environments also returned the user to Connect and
cleared the selected account.

**Expected behavior**

Keep the selected account, invalidate its test, and prepare the saved
agent resource in the background. Test that same agent before finishing
setup.

**Steps to reproduce**

1. Start a new Claude or Codex agent and select a saved account.
2. Change to an agent-owned plugin environment.
3. Try to finish setup or reconnect the account.

Related: #17750 defers probes until after hire submission. This change
implements preparation during the wizard instead. The related PR search
and ROADMAP review found no other implementation of this setup
preparation path.

## What Changed

- Add the optional `requiresAgent` driver declaration. Require agent
lifecycle participation when it is set.
- Prepare saved drafts on creation or environment changes. Keep the
setup hold and wait in `verifying` without background verification
scans.
- Bind tests to the saved agent. Return a warning without allocating a
lease when the required agent is absent or still preparing.
- Preserve the account and configuration step when the environment
changes. Save the selection and invalidate the old test.
- Show active preparation and support authorized retries. Keep approval,
company, budget, and normal task admission controls.
- Document the contract and cover lifecycle, lease, manifest, and wizard
behavior.

## Verification

- Server and UI TypeScript checks passed.
- 112 wizard and setup helper tests passed. The shared manifest suite
passed.
- 123 lifecycle, policy, lease, and environment-test API cases passed
after the approval-gate fix. The diagnostic and driver suites and all 80
agent adapter route tests also passed.
- Token gates, module boundaries, map validation, and whitespace checks
passed.
- All 66 lifecycle/policy cases passed after the stopping-draft
permission fix. The new real-DB route case verifies creator success,
other-creator denial, and refusal to complete a hire while stopping.
- Full workspace typecheck and build were attempted. Both stop at the
unchanged Rust Runner step because Cargo is not installed on this host.
- CI on 43be3c93d passed all automated checks, including full test
shards, typecheck, build, Rust checks, browser suites, and canary
checks:
https://github.com/paperclipai/paperclip/actions/runs/40368174530.
- The full local repository test command was started. Two unchanged
workspace tests assume Git's initial branch is master; this host
defaults to main. Both fail in isolation under the host default and pass
with process-scoped init.defaultBranch=master. The broad run was stopped
after those confirmed fixture failures; CI supplies the full clean
check.
- Early new-test failures were fixture collisions and an unscoped sweep
assertion; the fixtures and assertion were corrected. One route-suite
load raced with SDK output regeneration; rebuilding first and running
the suite afterward passed.
- Initial CI exposed a route mock missing the new prerequisite lookup.
The fixture was updated and a real HTTP no-lease case was added; all 54
cases in that route suite pass. Security review identified allocation
before required approval; the new approval gate and its tests address
it.
- CI on 50b5a08af reported 32 of 33 expected calls in the unchanged
cleanup-history scheduling test. Its isolated case passed locally
without cleanup-code changes; the original failure remains recorded and
the final CI run passes on the current head.
- Standard Greptile reviewed 43be3c93d at 5/5. A subsequent deeper
review identified runner selection, stale detail reads, error retention,
and test file size issues; 70343387b addresses all four with resolved
threads. Standard review returned 5/5 on that commit and again on
rebased head fda42c5ab. The rebase preserves master's new managed
environment skill declarations alongside requiresAgent. Full CI on
fda42c5ab passes: 55 successful checks, two intentional skips, no
failures. Run:
https://github.com/paperclipai/paperclip/actions/runs/40405121641.
- After those fixes, 133 UI tests and 61 lifecycle/policy cases pass;
all eight focused UI regressions fail against the previous
implementation and pass with the fix. Token, map, and whitespace gates
pass. The stale-read fixture initially omitted the setup URL because
navigation is mocked; following the URL activates the real detail query
and reproduces the race.
- UI TypeScript passes. This host's server dependency links now point to
a separate scratch checkout lacking requiresAgent, so the ordinary
server check reports that missing property. A no-emit check pinned to
this branch's shared declarations passes; it disables declaration
generation and supplies explicit local type roots. The first temporary
configuration lacked those type roots; the corrected configuration
passes. CI remains the full workspace verification.
- After rebasing onto 8373d7867, all 133 UI, 61 lifecycle/policy, and 44
manifest cases pass again, as do UI TypeScript, token, map, and
whitespace checks. The server no-emit check now hits three mismatched
SDK/Runner declarations from the scratch dependency links; these are
outside this change. Full CI builds the complete dependency set.
- Review cycle: the first Apex review scored 3/5 with four findings. All
were fixed together, the branch was rebased to resolve a schema
conflict, full CI and standard Greptile passed, and a second Apex review
returned 5/5 on fda42c5ab with zero new comments. All six review threads
are resolved, and the PR is conflict-free. Review:
https://github.com/paperclipai/paperclip/pull/17761#issuecomment-6422635718.
- No live provider resource was created and no deployment was performed.

## Risks

Plugins must opt in and ship a compatible declaration. Companies
requiring hire approval keep drafts paused and defer allocation and
verification until approval. Other companies prepare drafts during the
wizard under their existing agent-creation authority; the setup hold
still blocks ordinary work. Preparation uses existing lifecycle
ownership, retry, and cleanup paths. The open wizard polls only while
preparation or stopping is active. No database migration is required.

## Model Used

OpenAI Codex, based on GPT-6, with reasoning, tool use, and code
execution. The exact serving model ID and context window size are not
exposed.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have described the issue in-PR using the bug report fields
- [x] I have not referenced internal or instance-local issues or links
- [x] My branch name describes the change and contains no internal
ticket id
- [x] I have run the targeted tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation
- [x] I have considered and documented the risks
- [x] All Paperclip gates are green — 55 successful checks and two
intentional skips on fda42c5ab
- [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.1026.0-canary.12
2026-10-26 15:21:47 -07:00
Nicky LeachandPaperclip c7c12dbc79 Support plugin panels in task sidebars (#17796)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task side panels keep live work beside the conversation.
> - Plugins can already provide agent detail tabs.
> - Those tabs have no generic way to appear beside the task that is
using the agent.
> - This pull request adds an optional task-side-panel declaration to
agent detail tabs.
> - The plugin owns the label, component and availability. Core supplies
the task's running agent.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Access to plugin-contributed live views from task chat.

**Subsystem affected**

Plugin manifest validation, slot discovery, and task side-panel
UI/state.

**Current behavior**

Agent detail-tab contributions appear on agent pages. A task cannot
offer the same view through a plugin-owned availability decision.

**Proposed behavior**

An agent detail tab can declare `taskSidePanel: { availabilityPath }`.
Core substitutes the running agent into the plugin-local path. A
declared board GET route scoped to that agent returns `{ available:
boolean }`. Available contributions appear in desktop and mobile task
side panels.

**Reason and benefit**

Keep the task and its live plugin views together. Preserve plugin
ownership of provider protocols and product behavior. Core contains no
concrete plugin identifiers, routes, labels or readiness rules.

**Breaking changes**

None. The field is optional. Existing contributions remain unchanged.
Missing, disabled or unavailable plugins add no panel.

**Additional context**

Related: #17111 established the agent detail-tab and scoped API
extension surfaces. Roadmap and related PR searches found no duplicate
of this extension.

## What Changed

- Extend the manifest with optional task-panel metadata. Validate safe
plugin-local paths and require a declared board GET route with
agent-scoped authorization.
- Discover and load only opted-in agent contributions. Poll their
generic availability responses and mount the existing components with
agent context.
- Add generic panel tabs, launcher entries, dismissal and inactive-view
removal in desktop/mobile task chat.
- Reuse the existing live-run polling guard so idle tasks with a healthy
live connection stop polling.
- Add provider-neutral fixtures, schema/authorization tests, task tests
and SDK/feature documentation.

## Verification

- PASS: 301 focused tests including the full task-detail file, generic
panel discovery, persistence, slot loading and manifest validation.
Regressions cover POST-before-GET route order, mobile drawer dismissal,
recovery without focus and the absence of the hidden desktop task panel
on mobile.
- PASS: UI and shared-package typechecks; production UI build; token
gates, feature-map and module-boundary checks; diff and secrets/PII
review.
- PASS on final head: all 795 UI files / 9,186 tests, and all 95
shared-package files / 1,038 tests. Commands: `node
node_modules/vitest/vitest.mjs run --project @paperclipai/ui
--maxWorkers 3` and the corresponding `--project @paperclipai/shared`
command with local socket access.
- On the final head, server shards 7/12 and 10/12 and workspace shard
a-1/2 were interrupted by runner shutdown/communication loss. GitHub
reported infrastructure failures; no assertion failure was surfaced in
the available diagnostics. The queued summary was cancelled, then `gh
run rerun --failed` successfully requested retries for only those three
jobs. They are currently queued for CI runners; the 50 successful checks
remain passing. Interrupted attempts are not counted as passing.
[Final-head CI and
retry](https://github.com/paperclipai/paperclip/actions/runs/40396870920).
- Earlier verification exposed an empty-panel rerender loop, which was
fixed. The sandboxed combined run also failed five shared port-registry
tests because it could not bind loopback listeners; the full shared
rerun with socket access passed. These earlier failures are retained
here rather than reported as passing runs.
- Full workspace typecheck/build were attempted with pinned pnpm 9 after
resolving the initial local package-manager setup. Both stop at the
unchanged Runner Rust step because `cargo` is not installed. Full local
workspace checks are not reported as passing; final CI must provide
those broader checks.
- No live provider or plugin-specific transport acceptance is claimed by
Core's synthetic fixtures.

## Risks

- Availability is polled every three seconds only while an authorized
running agent exists. Plugins own the side effects and meaning of that
read.
- A view uses the running agent's context. It is not a separate per-task
resource.
- Optional metadata adds no backend endpoint or permission bypass. The
host requires existing board and agent authorization.
- Reverting the optional contract and UI integration removes task panels
without a data migration.

## Model Used

OpenAI Codex (GPT-6), with reasoning, repository tools and code
execution. The exact backend snapshot ID and context window size are not
exposed by this session.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have described the issue in-PR following the relevant issue
template
- [x] I have not referenced internal/instance-local Paperclip issues or
links
- [x] My branch name describes the change and contains no internal
ticket id
- [x] I have run focused 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
- [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-26 14:47:15 -07:00
DottaandPaperclip 718c73dafe fix(native): require verified remote process exit receipts (#17799)
Require fresh ESRCH or an exact matching zombie generation before treating a remote Runner as stopped. Preserve uncertain ownership through adoption and transport close, retire local controller resources after failed close, and check Node before remote launch.

Bind the shared transport change in Copilot profile 62 while retaining historical profile decoding. Add red-green process, permission, launch, adoption and cleanup regressions.

Thanks to @hsm-lv for the original investigation in #17073.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
canary/v2026.1026.0-canary.11
2026-10-26 21:39:04 +00:00