Commit Graph
2233 Commits
Author SHA1 Message Date
DottaandPaperclip 7eea9d8214 fix(github): use bot names in review summaries (#16212)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - A GitHub bot can send a pull request to an agent and publish its
review.
> - Each bot has its own GitHub App identity, but every summary
currently starts with "Paperclip Review".
> - A generic heading makes it harder to identify reviewers when several
bots review the same pull request.
> - This pull request uses the bot display name in review summary
headings.
> - Users can identify each reviewer while required checks keep their
existing name.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

The heading of a GitHub bot's published pull request review summary.

**Current behavior**

Every summary uses "Paperclip Review", regardless of the bot's name. The
check also uses "Paperclip Review" and must keep that name for existing
branch rules.

**Proposed behavior**

Complete and incomplete review summary headings use the GitHub bot
display name. The heading falls back to the bot login, then the assigned
agent name, when old connections have no display name. Separate GitHub
Apps keep independent checks with the same name.

**Reason and benefit**

Readers can identify each bot's review. A regression test confirms that
publishing a review does not update a check owned by another App or
endpoint.

**Breaking changes**

The summary heading changes. Consumers that parse the old heading must
accept bot names. No API, prompt configuration, database schema, or
check name changes are included.

No duplicate PR was found in the GitHub bot title search. ROADMAP.md
does not contain a duplicate of this change.

## What Changed

- Use the bot display name in complete and incomplete review headings.
- Escape Markdown in names and fall back to the bot login or agent name.
- Verify the heading through the publication workflow and keep check
updates scoped to the App and endpoint.
- Document bot-named summaries and independent checks. Update both
access maps.

## Verification

- Focused policy, heading, and existing UI tests: 67 passed.
- Database-backed regression cases: 2 passed, including bot-name
publication and independent checks.
- Map checks, token gates, and git diff --check passed.
- Typecheck and build passed on the final rebased branch. The final
focused run passed 44 tests; the two database-backed regression cases
also passed in a separate run. All final-head CI checks passed,
including full test suites, build, typecheck, E2E, and security scans.
Greptile reviewed the final commit at 5/5 with no actionable findings.
No review threads remain unresolved.

## Risks

- Consumers that parse the old summary heading must accept bot names.
- The GitHub check remains Paperclip Review. Multiple-bot check
isolation was tested with provider fixtures; a live two-bot GitHub test
was not run for this change.

## Model Used

OpenAI GPT-6 through Codex. The exact backend model ID and context
window are not exposed in this session. Used reasoning, repository
tools, and code execution.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (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-13 15:33:05 -05:00
Devin FoleyandPaperclip c7f4b74111 fix: isolate fresh task sources from restricted project workspaces (#16095)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Each task must have an isolated workspace that its agent can read.
> - Older project checkouts can contain worktrees for restricted tasks.
> - The source access check correctly rejects these shared checkouts.
> - A fresh task then fails even when its agent can clone the project
remote.
> - This pull request gives fresh default tasks a separate clone of the
same remote.
> - The task can start while the restricted task files remain protected.

## Linked Issues or Issue Description

**What happened?**

Fresh tasks fail with `Task workspace access is no longer available`
when their default project checkout contains a restricted task worktree.
The caller has access to the new task and its remote repository. The
shared local Git source is unsafe to copy.

**Expected behavior**

Use a separate clone of the same authorized remote for a fresh default
task. Keep source access checks and responsible-user Git credentials in
force.

**Steps to reproduce**

1. Put a restricted task worktree inside a project checkout.
2. Assign a new default task to an agent that cannot read the restricted
task.
3. Start the new task. The current source check fails before workspace
creation.
4. With this fix, the task uses a separate remote source. The restricted
files are absent. Later runs reuse the same owned source.

Related work: Refs #15749, Refs #15528. This change keeps their source
retention and workspace ownership checks.

## What Changed

- Add a remote source path scoped to the company and task.
- Clone through the run’s Git connection after the shared source fails
its access check.
- Reject reused workspaces, explicit selections, pinned branches, local
sources, credential-bearing URLs, and symlink paths.
- Check source access again before publishing the workspace.
- Resolve later runs from the verified recorded source. Ignore leftover
clones that the saved workspace does not own.
- Record provenance for new implicit Git defaults. Read the current task
binding before fallback. Preserve explicit, inherited and ambiguous
legacy checkout choices.
- Keep the effective task strategy during startup validation of matching
implicit defaults. Preserve the existing explicit-selection write check.
- Add regression coverage and document the fallback.

## Verification

- Merged master `c8efdbfba43` and resolved both rounds of catalog/index
conflicts. All upstream suites, classifications, source references and
story IDs are preserved. Both fix branches combine cleanly in either
merge order, with no catalog fingerprint drift.
- Passed locally: 313 focused source-isolation, preparation,
task-creation, binding and trust tests before the final startup fix. The
final fix passes all 100 source-trust, task-binding and
environment-selection tests. Earlier workspace-resolution and
persisted-source suites also passed.
- Real-Git regressions cover all three review findings. Reconstruction
ignores an unrecorded clone and preserves unpublished commits/files.
Explicit and parent-inherited checkout IDs without typed selections
retain their denial and branch tips without cloning or acquiring an
environment. A protected-project heartbeat completes and resumes using
the task’s Git strategy with read access. Explicit selection still
requires writes, and a mismatched default is rejected.
- Passed: the complete Storybook catalog check with no drift errors,
feature and agent map structure checks, and all 17 catalog/index script
tests. The generated index counts match the merged catalog. The current
upstream verification-route inventory remains unchanged. The catalog
contains all 262 authored suites and 2,481 story IDs.
- Passed: full workspace typecheck and build after the final guard
changes, on latest head `79d2adadcb6d3d7a04b9943a91bdb8b0b6abea6d`.
- Latest head `79d2adadcb6d3d7a04b9943a91bdb8b0b6abea6d`: complete CI
passed (54 successful checks, two intentional skips), including the full
general/serialized suites and all eight browser shards. Fresh Greptile
Apex review is 5/5 on this same commit, with no unresolved findings or
recommendations. The full local test command was attempted earlier
without a complete aggregate result; the complete suite result is from
CI.
- Manual check: configure a shared project checkout with a restricted
task worktree. Admit a fresh default task using the authorized remote.
Confirm separate Git storage and no restricted local branch or file.
Reuse, close and reopen the task workspace. Confirm that explicit,
literal branch and low-trust selections retain the denied source.

## Risks

- A clean remote clone excludes unpublished local work. The fallback
applies only to fresh default tasks and rejects existing workspace or
branch selections.
- A private remote can fail to clone when the responsible user lacks Git
credentials. The run then reports the failure; access checks are not
bypassed.
- Older untyped checkout bindings cannot prove default provenance. They
keep the source denial instead of silently dropping unpublished work.
- New implicit Git defaults use the existing workspace-intent contract.
The source directory uses existing instance storage. No schema migration
is required.

## Model Used

OpenAI GPT-6, model ID `gpt-6`, through Codex. Used reasoning,
repository inspection, code editing, terminal tools, and test execution.
The API session does not expose an exact 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-13 12:31:40 -07:00
Nicky LeachandPaperclip db65a0863e Deliver durable resource lifecycle events to plugins without polling (#16203)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Plugins keep external resources aligned with Paperclip projects and
agents.
> - The durable resource journal stores changes, but its plugin
interface requires polling.
> - Project deletion has no journal entry, so consumers must scan for
missing projects.
> - This PR delivers committed journal events through a native plugin
callback and records deletion.
> - Plugins can recover unfinished work without periodic change scans.

## Linked Issues or Issue Description

Related: #15343 introduced the durable inbox. #15631 added required
agent lifecycle participation. This change keeps both contracts and adds
automatic delivery for the resource inbox.

**What happened?**

Plugins must schedule reads of the durable inbox. A deleted project has
no durable deletion event. Consumers must scan saved bindings to detect
its removal.

**Expected behavior**

Core should deliver committed resource events and replay unfinished
work. Empty queues should have no polling timer. Project removal should
leave a durable event.

**Steps to reproduce**

1. Install a plugin that consumes project lifecycle events.
2. Create or update a project. The plugin must call `listLifecycle` to
discover it.
3. Delete the project. The journal has no deletion entry.

## What Changed

- Add `resourceLifecycle` to the manifest and `onResourceLifecycle` to
the SDK. Require `events.subscribe` and a configured, enabled company
scope.
- Reuse transaction-aware work signals and the delivery coordinator.
Wake after commit, on startup and plugin activation, and after
configuration changes.
- Acknowledge only a matching successful result. Retry unfinished or
interrupted work. Retain deferred events without an idle retry timer.
- Wake again after successful worker configuration application, and
retain failed scans through idle drain (both Apex findings fixed).
- Keep ordering per resource and deliver past failed or deferred
resources. Keep the manual inbox methods for existing consumers.
- Record project deletion in the removal transaction. Extend the journal
constraint with a generated migration.

## Verification

- Apex follow-up: 129 focused worker, delivery, coordinator, and
application tests passed; server typecheck passed. Regression coverage
verifies configuration-application races and failed scans across idle
drain.

- Focused lifecycle, transaction coordinator, manifest, and SDK dispatch
suites: 105 tests passed, including single-connection saves, redacted
failure diagnostics, wakeups retained during idle drain, and per-event
retry deadlines.
- Complete SDK suite plus delivery database tests: 128 tests passed.
- Shared, database, and SDK typechecks passed. Direct server `tsc
--noEmit` passed against rebuilt SDK declarations.
- Full local `pnpm test:run` finished with 19,491 passed, 148 skipped,
and 6 failed tests across 5 files (plus two suite setup errors). This
run overlapped branch updates and is not final-head evidence: failures
included missing Cargo, local Git default-branch assumptions, and stale
in-process lifecycle modules. Final Apex-fix commit `75d028788` passed
all CI gates, including the full test shards, and received Apex 5/5 with
no unresolved comments.
- `pnpm -r typecheck` and `pnpm build` were attempted. Both stopped
because Cargo is unavailable locally. CI supplies the Rust toolchain.
- Local installation used the repository's documented lockfile fallback
because master has dependencies missing from its lockfile. No dependency
changes are included here.

## Risks

- Delivery is at least once. Plugins must re-read current state and make
external effects idempotent.
- Signals are process-local, like the other delivery queues. Startup
recovers committed rows. Independent database writers or multiple active
server processes require an explicit ownership and wake protocol.
- Apply migration `0340_open_warlock.sql` before running code that
records project deletion. Earlier deletions cannot be reconstructed.
- Automatic delivery selects plugins with company configuration rows.
`deferred` waits for a later startup, configuration change, or other
work notification.
- This does not replace required agent lifecycle participation or
establish that external mounts are unused.

## Model Used

OpenAI Codex (GPT-6), with tool-assisted code editing, code inspection,
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
- [ ] 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-13 12:00:30 -07:00
Devin FoleyandPaperclip 5a6d0d08a7 fix: record verified workspace file recovery from tasks (#16104)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Recovery must preserve files and show the next operator action.
> - A stopped run can leave a file repair hold after its recovery action
is resolved.
> - The task notice shows that hold but has no form to record verified
file recovery.
> - A chat connection can also replace the file repair instruction with
a chat retry instruction.
> - This pull request adds a file recovery form and preserves the file
repair instruction.
> - The form records evidence for the original run and keeps the current
task status.

## Linked Issues or Issue Description

**What happened?**

A retained workspace file hold can block a task after its recovery
action is resolved. The task page offers no file recovery form. A chat
binding can hide the required file repair instruction.

**Expected behavior**

An authorized board member can record verified recovery of the original
files. The server checks the original stop proof. The task keeps its
current status and does not start another run. Chat instructions must
not replace file repair instructions.

**Steps to reproduce**

1. Create a stopped legacy run with a retained workspace recovery marker
and a resolved recovery action.
2. Open its task page, including a task with a chat connection.
3. Observe that the file hold has no supported form to record verified
recovery.

Refs: #15857, #15824, #16095. Those PRs address shutdown export, native
finalization and workspace source isolation. This PR uses the existing
file repair API.

## What Changed

- Add a board-only file recovery form with evidence and an explicit file
verification confirmation.
- Preserve the current task status when recording file recovery.
- Project whether the original recovery marker supports file recording.
- Keep file repair instructions when a task has a chat binding.
- Add UI, route and Storybook examples for success, denial and
chat-connected file holds.
- Document the recovery flow and update the affected Storybook catalog
entries.

## Verification

- Merged master `c8efdbfba43` and resolved both rounds of catalog/index
conflicts. Upstream review notes and classifications are preserved,
together with all four retained-file recovery examples and their stable
story IDs. Both fix branches also combine without merge conflicts or
catalog fingerprint drift.
- Passed locally after conflict resolution: 47 tests covering the file
recovery UI, execution blocker notice and retained workspace repair
route. The earlier ownership-route pass also covered 196 tests.
- Passed: UI token gates, the complete Storybook catalog check with no
drift errors, feature and agent map structure checks, and all 17
catalog/index script tests. Verification inventory counts match all 262
authored suites and 2,485 story IDs. The upstream production
verification obligations remain intact.
- Passed: full workspace typecheck, build and Storybook build on latest
head `10ac22b35d0a4fe469cf620914845c509046da48`. The built index retains
all four file-recovery story IDs.
- Latest head `10ac22b35d0a4fe469cf620914845c509046da48` has 54
successful checks and two intentionally skipped Storybook checks. Apex
is 5/5 on that exact commit, with no unresolved findings. The full
general/serialized suites and all eight browser shards passed in CI.
Local tests use the focused suites above. Storybook browser interactions
have not been executed locally.
- Manual check: open a task with a retained file hold. Verify that
evidence and confirmation are required. Record verified file recovery.
Confirm that its status stays unchanged and no run starts. Repeat
without board runtime access and with an unverified stop receipt.

## Risks

- The form records an operator attestation. It does not recover files
itself or prove external action outcomes.
- A stale source or missing stop proof must remain rejected by the
existing server route.
- No database migration or automatic task retry is added.

## Model Used

OpenAI GPT-6 (`gpt-6`), used through Codex with reasoning, repository
tools, code editing and test execution. The exact context window size is
not exposed by this environment.

## 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-13 13:47:10 -05:00
DottaandPaperclip ccdc6754b2 fix: retain background preparation traces from unsampled requests (#16205)
Start preparation phase root traces independently of request sampling and link each phase to its trigger. Preserve operator root sampling, concurrent child context, and silent unconfigured behavior.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 12:23:48 -05:00
Nicky LeachandPaperclip da9af67415 Persist agent-owned instance secrets through lifecycle results (#16191)
## Thinking Path

> - Paperclip coordinates agents and controls when they can run.
> - Lifecycle plugins prepare and remove resources for each agent.
> - Those resources can require private connection credentials.
> - The current SDK can resolve configured secrets but cannot save these
credentials securely.
> - Ordinary company secrets can be bound to agents and do not provide
the required server-only boundary.
> - This change saves agent-owned instance secrets through the existing
local lifecycle result and native secret service.
> - The tenant can retain runtime credentials through restarts without a
separate encryption system or remote API.

## Linked Issues or Issue Description

**Subsystem affected**

Agent lifecycle, plugin contract, and native secret storage.

**Problem or motivation**

Lifecycle plugins need to save private runtime connection details for
the owning agent. Worker memory loses these details on restart. Plugin
state and company secrets do not provide a private runtime-only
boundary.

**Proposed solution**

Add optional instance-secret changes to lifecycle results. Save them
through the existing secret service and local encrypted provider before
accepting the result. Require company, agent, plugin, phase, operation,
revision, and current unexpired attempt ownership. Supply saved values
through the existing local preparation/resume request.

**Alternatives considered**

A separate encrypted store duplicates the native secret system. Generic
plugin company-secret writes proposed by #5007 do not supply agent
ownership or a server-only secret scope. This change does not add that
general write API.

**Roadmap alignment**

Extends the existing lifecycle contract from #15631. It does not add a
resource provider or select an execution backend.

## What Changed

- Add an instance scope to native secrets with mandatory agent/plugin
ownership, a company-qualified agent foreign key, and key uniqueness per
owner. Existing company and user scopes retain their constraints.
- Reuse native creation, rotation, deletion, secret versions, access
audit, and the local encrypted provider. Keep values out of company
catalogs, normal bindings, agent access, and public agent records.
- Accept local secret patches only after locking and checking the
current lifecycle operation and required plugin selection. Storage
failure or a stale reply cannot complete setup. Expired or replaced
attempt owners cannot overwrite a successor's credentials.
- Keep vault-import conflicts company-scoped. Protect instance-owned
account-home paths during failed device-login cleanup with a private,
fail-closed scan.
- Skip decryption on stop and cleanup so a lost key cannot block
removal. Cascade secret records when their agent or plugin is deleted.
- Log fixed provider diagnostics without raw errors that can contain
credentials. Document the local contract, runtime resolver, and
access-map exclusions. Retain the five latest migration snapshots
through the normal generator pruning.

## Verification

- 239 focused tests passed across agent-instance-secrets, lifecycle,
secrets-service, secrets-routes, plugin-secrets-handler, and
agent-device-login-routes. Used disposable PostgreSQL 18 clusters
through the existing test constructor seam because the bundled binary
cannot start on this macOS host. The temporary harness is outside git.
- Regression tests reproduced the vault-import conflict and missed
cleanup claimant before the fixes; both pass after the fixes.
- New coverage includes encrypted native versions, rotation, restart
recovery, company/agent/plugin isolation, ordinary-read and binding
denial, stale replies, storage failure, safe logging, cleanup without
the master key, and same-name secret isolation, native account-home
mutation guards, lease takeover/expiry, and live agent/plugin deletion
cascades.
- Direct server TypeScript check, DB/shared typechecks, migration
numbering/safety checks, module boundaries, and git diff check passed.
- Full workspace typecheck and build were attempted; both stop at the
missing local cargo toolchain. The full test command was started and
stopped after failures in unchanged native-runtime/workspace suites plus
bundled-PostgreSQL skips; it did not complete. CI must verify the full
toolchain paths.
- Current head `c0f8bfd12477ad509252ecd40e168a6c84dbeb36`: all 55 CI
checks passed, including the previously canceled e2e shard. Greptile
Apex reviewed this exact head at 5/5 with no unresolved threads. GitHub
reports the branch mergeable and clean.
- No live provider probe, production data access, or deployment.
Reviewed the diff for credentials, private references, and run
artifacts.

## Risks

Apply migration 0339_luxuriant_iron_monger.sql before deploying tenant
code. It adds nullable ownership columns, a partial unique index,
foreign keys, and a stricter scope constraint. Existing rows remain
valid. Keep the schema on application rollback. Preserve the existing
secrets master key through deployments and backup restoration.

The lifecycle request/result now has a sensitive, server-only field.
Plugins must never log it or return it to an agent or browser. Values
stay in the tenant database through the existing encryption provider.
Trusted runtime consumers must apply normal execution admission before
resolving them. Older hosts omit instanceSecrets; plugins that require
durable storage must reject those hosts.

## Model Used

OpenAI GPT-6 via Codex, with reasoning, repository inspection, code
editing, and local test execution. 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
- [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-13 10:13:42 -07:00
DottaandPaperclip e43af9770a refactor(server): isolate publication recovery and Board sends (#16192)
## Thinking Path

> - Paperclip publishes task messages through several chat providers.
> - Operator recovery must keep durable receipts and task authority
consistent.
> - The service combines shared publication transactions with provider
recovery details.
> - Provider-specific recovery belongs beside its transport
implementation.
> - This pull request extracts publication requests and resolution
workflows.
> - Shared workflows keep transaction ownership and delegate provider
operations.

## Linked Issues or Issue Description

Depends on #16187. Refs #16185, #16183, and #16180. Review against the
parent branch.

**What existing behavior does this improve?**
The structure of publication requests, Board sends, and operator
recovery.

**Current behavior**
Six public publication methods live in the main service and include
provider-specific transfer and receipt operations.

**Proposed behavior**
Move shared requests and resolution to publications modules. Move Teams
file resolution and Board preparation, Slack file receipts and Board
staging, and Photon retry records to their provider folders.

**Reason and benefit**
Provider recovery is easier to locate without moving transaction and
idempotency ownership into providers.

## What Changed

- Extract six public publication methods into two shared workflow
factories (629 and 467 lines).
- Reduce chat-channels.ts from 5,337 to 4,078 lines. The full stack
reduces it from 7,919 lines.
- Extract Teams transfer resolution and cold-runtime Board preparation.
- Extract Slack upload receipt resolution and Board send staging.
- Extract Photon operator retry records.
- Preserve the same public service API and keep provider operations in
the original transactions.

## Verification

- Merge preparation: replayed this stack onto current master after
#16180 landed. Preserve master's new Telegram scheduling methods and
connection-scoped Photon notifications. Server typecheck, callback
syntax audit, and module-boundary check passed. Fresh local integration
and wiring checks passed (1,318 tests). The dedicated Photon suite
passed 18 before and 18 after the conflict resolution. This final slice
was replayed onto merged #16187 with all chat-channel source unchanged.
Current-head CI and review are running. Earlier verification below
describes previous stack commits.

- Publication, attachment, Board send, and operator recovery baseline:
261 passed before and 261 passed after; 1,034 other cases excluded.
- Server typecheck, module-boundary check, and git diff --check passed.
- Syntax audit reconstructs all six public method bodies from the
provider helpers and matches the original AST. Unrelated service
functions and the public API match too.
- Final repository typecheck passed. Build passed. The broad local test
run encountered heartbeat fixture setup failures under the macOS
PostgreSQL shared-memory limit and was interrupted after 868 seconds
(exit 130). Four confirmed abandoned guard slots were reclaimed without
stopping active databases. The complete chat integration suite and
source-wiring checks passed separately (1,309 tests).
- Greptile rates current head 55417debfe 5/5 with zero inline findings.
All 55 CI checks pass on that head. Tests use local PostgreSQL and
fixture transports, not live provider execution.

- Previous stack validation on #16192 (55417debfe): repository typecheck
and build passed; all 1,295 chat integration cases plus 14 source-wiring
checks passed locally (1,309 total). All five PRs have current-head 5/5
reviews and 55 passing CI checks. The broad local pnpm test:run hit
PostgreSQL shared-memory fixture failures and was interrupted after 868
seconds (exit 130); it is not a local full-suite pass.

## Risks

Operator recovery can duplicate a send if receipt updates move out of
their transaction. The extracted helpers use the caller transaction.
Teams preparation remains outside Board locks, and repeated send
identities retain their existing receipt. Public method names and return
values stay the same.

## Model Used

OpenAI GPT-6 via Codex, with code execution and repository tools. The
precise serving variant and context-window size are not exposed in this
session.

## Checklist

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

> - Paperclip accepts chat messages as durable task input.
> - Wakeups must preserve current company, identity, and task
permissions.
> - The main service mixes those shared checks with provider message
rules.
> - Shared wakeup workflows should own persistence and queue ordering.
> - This pull request extracts wakeup authorization, processing, and
notices.
> - Slack and GitHub rules move into their provider folders.

## Linked Issues or Issue Description

Depends on #16185. Refs #16183 and #16180. Review against the parent
branch. Related open #13620 covers durable chat wakeup feature work;
this PR only moves existing behavior.

**What existing behavior does this improve?**
The structure of durable inbound wakeup processing.

**Current behavior**
The service contains authorization, durable wake receipts, queue
notices, and provider authority details.

**Proposed behavior**
Use three cohesive inbound modules and provider-owned authority
functions.

**Reason and benefit**
The shared transaction and company checks stay together while provider
rules have a clear home.

## What Changed

- Move wakeup authorization, processing, and notices into inbound
modules (245, 418, and 377 lines).
- Move Slack mention checks and GitHub automatic-event admission and
acknowledgements into provider folders.
- Preserve lock order, durable receipt ordering, notice cursor lifetime,
and the live heartbeat dependency.
- Reduce chat-channels.ts from 6,340 to 5,337 lines, including blank-gap
cleanup.

## Verification

- Merge preparation: replayed this stack onto current master after
#16180 landed. Preserve master's new Telegram scheduling methods and
connection-scoped Photon notifications. Server typecheck, callback
syntax audit, and module-boundary check passed. Fresh local integration
and wiring checks passed (1,318 tests). The dedicated Photon suite
passed 18 before and 18 after the conflict resolution. This slice was
replayed onto merged #16185 with its chat-channel source unchanged.
Current-head CI and review are running. Earlier verification below
describes previous stack commits.

- Existing wakeup, queue, retry, and authorization integration cases:
212 passed before and 212 passed after; 1,083 other cases excluded.
- Server typecheck, module-boundary check, and git diff --check passed.
- Syntax-tree audit compares all 12 moved functions, cursor state,
retained service functions, and public API after explicit provider
delegations.
- The first three stack PRs have 55 passing CI checks and current-head
5/5 reviews.
- Final stack checks are recorded below. The first broad local run
encountered a PostgreSQL shared-memory fixture limit and was interrupted
after that failure.
- Greptile rates current head 1462819f53 5/5 with zero inline findings.
All 55 CI checks now pass. Initial CI failures: the first workspace job
hit EADDRINUSE in sandbox-callback-bridge.test.ts, and
cloud-auth.spec.ts hit a disposed Playwright response. Both jobs passed
on retry without source changes. Integration uses local PostgreSQL and
fixture provider transports, not live providers.

- Previous stack validation on #16192 (55417debfe): repository typecheck
and build passed; all 1,295 chat integration cases plus 14 source-wiring
checks passed locally (1,309 total). All five PRs have current-head 5/5
reviews and 55 passing CI checks. The broad local pnpm test:run hit
PostgreSQL shared-memory fixture failures and was interrupted after 868
seconds (exit 130); it is not a local full-suite pass.

## Risks

Authorization and wakeups must retain the same locks and transaction
scope. Factories receive narrow dependencies and initialize before their
consumers. The notice cursor stays per service. Shutdown and heartbeat
reads remain live. Provider helpers do not own transactions.

## Model Used

OpenAI GPT-6 via Codex, with code execution and repository tools. The
precise serving variant and context-window size are not exposed in this
session.

## Checklist

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

> - Paperclip accepts provider callbacks as durable task input.
> - Runtime ownership prevents two servers from accepting the same
receiver stream.
> - Discord and Photon share the lease machinery but have different
callback details.
> - Moving that shared machinery into Discord would create the wrong
platform dependency.
> - This pull request extracts common receiver ownership and puts
provider callbacks in their own folders.
> - The service keeps startup and shutdown coordination.

## Linked Issues or Issue Description

Depends on #16183; review against the parent branch. Refs #16180 and
#16080. Related open work: #15532 changes Discord command reconciliation
scheduling. This extraction preserves existing scheduling.

**What existing behavior does this improve?**
The structure of shared receiver ownership and Discord/Photon runtime
callbacks.

**Current behavior**
The shared entry point mixes lease acquisition, renewal, provider
capability checks, and Photon checkpoint transactions.

**Proposed behavior**
Put the common lease operations in runtime/ownership.ts and the protocol
details in the provider folders.

**Reason and benefit**
The ownership lifetime stays explicit without making Photon depend on
Discord implementation code.

## What Changed

- Extract 11 receiver-ownership helpers into runtime/ownership.ts (397
lines). Expose only the operations used by the service.
- Extract Photon ownership checks and checkpoint callbacks (44 lines).
Retain the same runtime context object.
- Extract Discord command and capability reconciliation (112 lines).
- Keep the original service-owned lease map, runtime versions, stop
tasks, and shutdown sequence. Read shutdown and test overrides through
live accessors.
- Reduce chat-channels.ts from 6,880 to 6,340 lines, including removal
of blank gaps left by extractions.

## Verification

- Merge preparation: replayed this stack onto current master after
#16180 landed. Preserve master's new Telegram scheduling methods and
connection-scoped Photon notifications. Server typecheck, callback
syntax audit, and module-boundary check passed. Fresh local integration
and wiring checks passed (1,318 tests). The dedicated Photon suite
passed 18 before and 18 after the conflict resolution. Current head is
6d52aba35f; CI and review are running. Earlier verification below
describes the previous stack commits.

- Existing Discord, Photon, and runtime integration cases: 110 passed
before and 110 passed after; 1,185 other cases excluded.
- Server typecheck, module-boundary check, and git diff --check passed.
- Syntax-tree comparison: 11 ownership helpers, two Photon callback
bodies, Discord reconciliation, unrelated service functions, and the
full public API/shutdown return expression preserve structure.
Intentional binding changes are audited.
- Both preceding PRs have 55 passing CI checks and current-head 5/5
reviews.
- Repository typecheck and build passed earlier in this stack. Final
stack checks are recorded below. The first broad local run hit a macOS
PostgreSQL shared-memory fixture limit and was interrupted after that
failure.
- All 55 CI checks pass on df15f9dd9c. Greptile rates that head 5/5 with
no inline findings. Tests use real local persistence with fixture
runtimes; they do not prove live Discord or Photon execution.

- Previous stack validation on #16192 (55417debfe): repository typecheck
and build passed; all 1,295 chat integration cases plus 14 source-wiring
checks passed locally (1,309 total). All five PRs have current-head 5/5
reviews and 55 passing CI checks. The broad local pnpm test:run hit
PostgreSQL shared-memory fixture failures and was interrupted after 868
seconds (exit 130); it is not a local full-suite pass.

## Risks

Lease loss and partial startup can create overlapping receivers if
cleanup order changes. The service retains the same map objects and
shutdown ordering. The ownership module reads current shutdown state,
and Photon callbacks retain the exact mutable runtime context reference.
No new worker or timer starts during module initialization.

## Model Used

OpenAI GPT-6 via Codex, with code execution and repository tools. The
precise serving variant and context-window size are not exposed in this
session.

## Checklist

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

> - Paperclip connects provider messages to governed agent work.
> - Webhooks must authenticate and persist messages before acknowledging
them.
> - The shared service still contains GitHub and Slack protocol details.
> - The platform-boundary rule places those details in provider folders.
> - This pull request extracts provider webhook handling behind typed
methods.
> - The service retains dispatch, runtime selection, and durable
lifecycle recording.

## Linked Issues or Issue Description

Depends on #16180; review this PR against its parent branch. Refs
#16080. Related open work: #16163 changes GitHub automatic-author
policy. This extraction preserves that policy implementation.

**What existing behavior does this improve?**
The ownership and readability of GitHub and Slack webhook handling.

**Current behavior**
The shared service implements GitHub signatures, replay fences,
automatic event messages, and Slack callback verification.

**Proposed behavior**
Place those details in the respective provider folders and keep the
common dispatcher small.

**Reason and benefit**
Provider code can be reviewed without reading the shared task service.

## What Changed

- Extract GitHub request-local deadline and replay scope (60 lines).
- Extract GitHub authentication and durable-ingress coordination (280
lines).
- Extract GitHub event admission, message construction, and policy-skip
receipts (140 lines).
- Extract Slack workspace validation and authenticated callback
observation (131 lines).
- Keep the same service-owned background task set and GitHub provenance
maps. Forward late-initialized callbacks without reading them during
construction.
- Reduce chat-channels.ts from 7,383 to 6,880 lines: 503 net lines
removed.

## Verification

- Merge preparation: replayed this stack onto current master after
#16180 landed. Preserve master's new Telegram scheduling methods and
connection-scoped Photon notifications. Server typecheck, callback
syntax audit, and module-boundary check passed. Fresh integration, CI,
and current-head review are running. Earlier verification below
describes the previous stack commits.

- Parent baseline: 544 selected GitHub, Slack, and webhook integration
cases passed; 751 other cases excluded.
- Initial extracted run: 543 passed and one idle-queue scan assertion
failed. The idle-queue case passed separately on both the parent and
extracted code. Its publication implementation is unchanged. The full
selected-suite retry passed all 544 cases; the initial failure remains
documented and its cause is not established.
- Server typecheck, module-boundary check, and git diff --check passed.
- Syntax-tree comparison verifies unchanged request-scope logic, durable
ingress, leased authentication, event handling, workspace validation,
and callback recording. All unrelated service functions remain
unchanged.
- Repository typecheck and production build passed for the parent
extraction. Full-stack verification will run at the final handoff. The
first broad local run hit a macOS PostgreSQL shared-memory fixture limit
and was interrupted after that failure.
- All 55 current-head CI checks passed. Greptile: 5/5 on f74ecb5094,
with zero inline findings. No live third-party execution is claimed.

- Previous stack validation on #16192 (55417debfe): repository typecheck
and build passed; all 1,295 chat integration cases plus 14 source-wiring
checks passed locally (1,309 total). All five PRs have current-head 5/5
reviews and 55 passing CI checks. The broad local pnpm test:run hit
PostgreSQL shared-memory fixture failures and was interrupted after 868
seconds (exit 130); it is not a local full-suite pass.

## Risks

Moving callback code can change initialization timing or lose
request-scoped state. The new factories retain live option getters, the
exact runtime context and provenance maps, and typed forwarding for late
initialization. Authentication stays inside the existing credential
lease. The common lifecycle write still completes before returning
provider success.

## Model Used

OpenAI GPT-6 via Codex, with code execution and repository tools. The
precise serving variant and context-window size are not exposed in this
session.

## Checklist

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

> - Paperclip manages AI agents and their work.
> - Chat channels connect provider messages to governed tasks.
> - Slack task recovery still lives in the shared channel service.
> - Its provider sends and recovery state belong in the Slack folder.
> - This pull request moves Slack task-start resolution and
Board-message recovery into two modules.
> - The service keeps the public dispatcher and the existing shared
authorization owners.

## Linked Issues or Issue Description

Refs #16080. Related open work: #15421 changes linked-user task trust;
#15532 changes Discord reconciliation. This extraction changes neither
behavior.

**What existing behavior does this improve?**
The ownership and readability of Slack task-start and Board-message
recovery.

**Current behavior**
The 7,919-line shared service implements Slack retry and cancellation
details.

**Proposed behavior**
Keep those implementations in the Slack folder. Preserve public methods
and all state transitions.

**Reason and benefit**
The channel boundary follows rules/chat-channel-platform-boundaries.mdc
and makes Slack recovery easier to review.

## What Changed

- Move Slack task-start operator resolution into
slack/task-start-resolution.ts (488 lines).
- Move the durable Slack Board-message worker into
slack/board-messages.ts (104 lines).
- Keep initial endpoint/action lookup and provider-effect dispatch in
the service. Pass the original endpoint record to avoid a new read or
race.
- Preserve live reads of heartbeat and deferred-processing options.
- Reduce chat-channels.ts from 7,919 to 7,383 lines: 536 net lines
removed.

## Verification

- Existing Slack integration cases: 282 passed before and 282 passed
after; 1,013 non-Slack cases excluded by the filter.
- pnpm -r typecheck: passed. The first sandboxed attempt could not
create a local tsx IPC socket; the permitted rerun passed.
- pnpm build: passed.
- Module-boundary check and git diff --check: passed.
- Normalized TypeScript AST comparison: moved Board worker and Slack
resolution tail unchanged; all other service function declarations
unchanged.
- All 55 current-head CI checks passed; two optional Storybook checks
skipped. Greptile: 5/5 on 1c1bf5d019, with zero inline findings.
- The broad local pnpm test:run encountered PostgreSQL fixture startup
failure because the macOS shared-memory ID limit was exhausted (the chat
integration file did not execute in that run). The focused 282-case
before/after runs passed. Two verified abandoned, unattached 56-byte
guard segments were reclaimed; no active database was stopped. The broad
run remains unverified and its output is retained.
- Existing public-service integration tests use real local database
fixtures and injected provider transports; no live Slack execution is
claimed.

- Final stack validation on #16192 (55417debfe): repository typecheck
and build passed; all 1,295 chat integration cases plus 14 source-wiring
checks passed locally (1,309 total). All five PRs have current-head 5/5
reviews and 55 passing CI checks. The broad local pnpm test:run hit
PostgreSQL shared-memory fixture failures and was interrupted after 868
seconds (exit 130); it is not a local full-suite pass.

## Risks

Credential lease loss, ambiguous sends, and scheduler failure can cause
duplicate work if ordering changes. The extraction keeps the lease,
transaction, provider-confirmed receipt, and resume marker sequence
intact. No schema, API, dependency, or workflow change.

## Model Used

OpenAI GPT-6 via Codex, with code execution and repository tools. The
precise serving variant and context-window size are not exposed in this
session.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-13 10:50:23 -05:00
DottaandPaperclip e3029c632b feat: trace controller preparation before agent dispatch (#16178)
Trace eleven controller preparation operations with the existing hashed run correlation and preserve errors without exporting exception text. Verify asynchronous child-span parenting with a real OpenTelemetry SDK and context manager.

Full exact-head CI and E2E passed after one documented fixture-startup retry; Greptile Apex 5/5.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 10:16:10 -05:00
0b0e53029f fix: recover saved workspace results automatically (#15824)
## Thinking Path

- Paperclip preserves accepted agent results before workspace copyback
and result commitment.
- A replaced controller can leave a physical copyback owner receipt
behind. A lost database lock or expired lease cannot prove that its
tar/Git children stopped.
- Infrastructure recovery should belong to the runtime and deployment
harness; task users should not investigate processes or attest shutdown.
- New receipts record the exact deployment. The harness independently
verifies that deployment fully stopped and signs a short-lived proof
bound to the stack, run, owner and deployment.
- The existing guarded reconciler then finishes the same accepted result
without a new agent turn. Legacy receipts retain the documented
administrator API after independent platform verification.

## Linked Issues or Issue Description

**What happened?**

An accepted native result can remain at `workspace_finalizing` with an
abandoned physical copyback owner. Repeated recovery sweeps could
replace specific instructions with advice to start new agent work. The
first version of this PR exposed a shutdown-evidence form in the task;
that asks end users to resolve infrastructure details.

**Expected behavior**

Automatically verify and recover the exact stopped deployment, preserve
the original accepted result and existing approval/monitor waits, and
show only a quiet task status. Unknown or live workers must remain
fenced. Older receipts without platform identity need independently
verified operator assistance through the administrator API.

**Steps to reproduce**

1. Persist an accepted native result and a copyback receipt from a prior
controller.
2. Replace that deployment before workspace finalization commits.
3. Let the normal finalization reconciler revisit the saved result.
4. With a platform-bound receipt and a fully stopped deployment,
recovery should finish automatically. With missing, live, unverifiable
or changed ownership, it must not write files or create another provider
turn.

Related public work: #14402 introduced copyback ownership; #9687 handles
dead-run finalize barriers; #15795 handles terminal stale-run cleanup.
This change addresses an accepted result whose physical copyback owner
prevents commitment.

## What Changed

- Record Railway project, environment, service and deployment identity
on new physical copyback owners.
- Request read-only platform verification from the managed cloud
harness. Require an Ed25519 stop receipt with a disjoint purpose,
configured signing key, exact stack/run/owner/deployment binding and
valid expiry.
- Keep live, unknown, malformed, unavailable and legacy owners fenced.
Compare the whole prior receipt during ownership takeover, under the
existing dedicated advisory lock.
- Persist platform proof and audit automatic takeover. Resume normal
finalization of the same saved result, preserving task ownership,
approved boundaries, monitors and pending confirmations.
- Remove process metadata, shutdown evidence, confirmation checkboxes
and infrastructure-repair forms from task views. Suppress the duplicate
technical owner-recovery card. Show “Restoring saved work…” only from
server-confirmed recovery or queued operator recovery; platform identity
alone shows the operator-assistance fallback. Remove the status when
recovery resolves.
- Share current-task, current-running-execution, accepted-result,
authoritative-reviewer and newer-execution checks between automatic and
operator recovery. Validate after the provider lookup, atomically with
the owner claim, and again before file publication; closed or superseded
tasks cannot start copyback.
- Keep the separately authorized exact-owner API for operator recovery
of legacy receipts, with active-lock, live-controller, stale-result and
newer-execution rejection. Generic dismissal cannot bypass the copyback
fence.
- Replace obsolete form stories with production-backed automatic,
queued, legacy, completed and mobile examples; retain stable suite IDs.
Update the operator runbook and scoped Storybook catalog.

## Verification

- Red/green: new platform-proof tests failed before implementation; the
database takeover regression failed against the original ownership
guard; reduced-UI assertions failed against the original form. All pass
after the changes.
- Passed: 59 physical-owner/finalization database tests using isolated
PostgreSQL, including real reconciler commitment from all three
saved-result phases through both automatic and operator recovery. They
preserve result identity, exact saved file bytes, pending approval and
overdue monitor waits, and create no new run or wake request. Unknown
evidence and changed ownership remain fenced.
- Passed: 162 focused proof, cloud identity, recovery route,
recovery-card and reduced-UI tests.
- Passed: full workspace typecheck and build, final server/UI
typechecks, token gates, and Storybook build.
- Browser inspection: desktop task composition shows only the quiet
status; completed example removes it and retains the approval-wait
message. Mobile shows the existing bottom navigation with no horizontal
overflow. These are fixture UI observations, not live provider
acceptance.
- A companion harness change implements the actual provider check,
signed receipts, server-token authentication and bounded/coalesced
provider reads. Its full typecheck and test suite passed: 3,240 passed,
92 skipped, zero failures. This application alone cannot enable platform
verification.
- The updated full application `pnpm test:run` was stopped after
repeated local embedded-Postgres startup failures; it is not claimed
green. The same affected recovery suites pass against isolated
PostgreSQL. CI passed on final head `9ad5c3529e`: 54 successful checks,
two intentional skips, and successful legacy Snyk status. Greptile is
5/5 with no unresolved findings.
- Global Storybook catalog validation is green: 225 files, 2,224
stories. Its 17 catalog/index tests pass. Master was merged and the
inherited index metadata was reconciled after source review.
- Review correction red/green: two UI and two database assertions
reproduced misleading progress / missing server recovery state; all pass
after server-confirmed state was added. All 153 focused server tests and
68 UI tests pass on the final recovery code, including repeated-sweep
idempotence. Full workspace typecheck/build, token gates and Storybook
build also pass.
- Safety regression red/green: stale-task copyback and publication tests
failed against the previous guard. The final suite covers changes during
proof lookup, closed/cancelled tasks, replaced execution/checkout, newer
and queued legacy runs, reassignment and rejected/unbound results, plus
the real reconciler selecting closed or superseded tasks. Files remain
untouched and no workspace operation starts.
- No staging mutations, deployment, live provider probes or live
production recovery were performed. The three historical receipts lack
deployment identity and still require independent operator proof.

## Risks

Automatic takeover requires the companion harness release and configured
runtime-identity keys. Deploying only this application leaves
unavailable evidence fenced. Legacy receipts are not retrospectively
assigned a deployment identity. An authorized operator can still make an
incorrect attestation through the fallback API; its administrator gate
and exact-owner safeguards remain. Provider history that is missing or
deeper than the bounded lookup defers automatic recovery. Copyback can
still fail under its existing retained-workspace policy.

No migration, telemetry change or cloud provider credential delivery is
required. The companion harness PR also has passing CI and a 5/5 review;
neither branch is deployed.

## Model Used

OpenAI Codex, GPT-6, with repository inspection, browser verification,
tool use and code execution. The exact serving model ID and
context-window size 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 and contains no internal
Paperclip ticket id or instance-derived details
- [x] I have run the relevant tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Paperclip <paperclip@paperclip.ing>
Co-authored-by: Devin Foley <devin@paperclip.ing>
2026-10-13 10:14:45 -05:00
Devin FoleyandPaperclip 833769ab55 perf(chat): schedule Telegram maintenance from commits and deadlines (#16094)
Replace periodic Telegram discovery and maintenance scans with bounded startup recovery, connection-specific commit notifications, and durable action deadlines. Preserve ownership, inactive subscription parking, retry and cleanup recovery, and atomic credential binding updates.

Verified full typecheck/build, 30 focused scheduler tests, 12 database-backed recovery cases, all current-head CI checks, and Greptile 5/5 with no unresolved threads.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 07:55:27 -07:00
Devin FoleyandPaperclip a0651c96f8 Add database-free process liveness probes (#16101)
Expose process liveness without database or credential queries while preserving hostname and runtime identity guards. Deep health and sleep/wake readiness checks retain their existing behavior.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 07:43:46 -07:00
DottaandPaperclip be380ba155 fix(ui): hide agent-created tasks by default (#16157)
Hide agent-created, system-created, and chat-channel tasks from independent collections by default. Preserve task hierarchy context and existing inbox participation. Add a saved opt-in checkbox without helper text.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 09:26:37 -05:00
DottaandPaperclip 3f7b00a8d0 fix: preserve public identifiers in feedback redaction (#16168)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - People rate agent comments and documents to capture feedback.
> - Feedback bundles remove credentials and personal information before
export.
> - The feedback scanner treats ordinary dotted text as JWTs and
authentication terms as bearer tokens.
> - This pull request checks encoded JWT headers and preserves known
authentication terms.
> - The benefit is readable feedback context with credential masking
kept in place.

## Linked Issues or Issue Description

Refs #12309 for the related dotted-identifier false-positive report.
This PR fixes the separate feedback scanner. It does not close every
case in that report.

Related PR: #14318 adds fine-grained GitHub token coverage. It addresses
a separate credential form and is not duplicated here.

**What happened?**

Feedback exports replace `api.openai.com`, dotted filenames, and public
schema IDs with a JWT placeholder. They also replace the words after
`bearer` in phrases such as “bearer tokens.” Five-part encrypted JWTs
are only partly masked.

**Expected behavior**

Keep public identifiers and authentication terms readable. Mask complete
credential tokens. Count only text replacements in the redaction
summary.

**Steps to reproduce**

1. Pass `api.openai.com` or `Use bearer tokens.` to
`sanitizeFeedbackText`.
2. Check that the original text is returned and the redaction counts
stay empty.
3. Pass a signed JWT or an encrypted JWT to the same function.
4. Check that the full token is replaced and one JWT replacement is
counted.

**Paperclip version or commit**

Reproduced in the feedback scanner on master at
`5e8e992df2b3d71f82091b7bbf29fd482854d9f5`.

**Deployment mode**

Built from source. The defect is in the shared feedback export service
and does not depend on deployment mode.

## What Changed

- Use the existing JWT header predicate in the feedback scanner.
- Match complete three-part and five-part tokens, including
direct-encryption tokens with an empty key segment. Accept short token
segments in the predicate.
- Preserve the closed set of authentication terms already used by the
general scanner, including terms inside closing prose delimiters.
- Count only replacements that change the text.
- Add tests for readable context, credential masking, nested values, and
redaction counts.
- Document feedback text redaction.

## Verification

- Focused redaction and feedback service tests: 89 tests pass across
five suites.
- `pnpm --filter @paperclipai/adapter-utils typecheck` passes. Full
local typecheck, tests, and build were attempted. The kernel killed
compiler and test processes at the sandbox memory limit of 4 GB. Serial
retries and a capped Node heap still could not complete the server
compiler. CI runs the full repository checks.
- `git diff --check` passes.
- Latest commit `0eb8d3fb4257bb79e67fab615a8299ef69230ad8`: **55 checks
passed**, including full typecheck, tests, build, and E2E; two optional
Storybook checks were skipped. [CI
evidence](https://github.com/paperclipai/paperclip/actions/runs/38407932875).
Both Greptile findings have regression tests and fixes. [Greptile gives
this commit
5/5](https://github.com/paperclipai/paperclip/pull/16168#issuecomment-6146917423),
with both review threads resolved.

## Risks

- This changes the feedback export matching rules. Public dotted text no
longer triggers credential masking by shape alone.
- Explicit credential assignments, provider keys, structured secret
fields, email addresses, and phone numbers remain masked. Tests cover
these cases.
- Existing feedback snapshots are not rewritten. No schema change or
migration is needed.
- The encoded-header check remains a heuristic. It is not a full JWT
validator.

## Model Used

OpenAI Codex, GPT-6, with reasoning, tool use, and code execution. The
runtime identifies the model as GPT-6. It does not expose the exact
provider deployment ID or context window size. The earlier patch and
this PR preparation were AI-assisted.

## 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-13 09:10:37 -05:00
DottaandPaperclip aded4197e5 fix: make multiplayer task toasts personal (#16153)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for work.
> - A company can have several human users and shared agents.
> - Live task events currently produce toasts for any user who can read the task.
> - This makes unrelated work interrupt other users.
> - This pull request checks each user's task participation before it permits a toast.
> - Users receive relevant task notifications while company views continue to refresh.

## Linked Issues or Issue Description

**What happened?**

Company task activity and agent failures produce noisy toasts for users who have no involvement in the task.

**Expected behavior**

A task qualifies when the viewer created it, commented on it, is its assigned user, deliberately acted on it under the Mine rules, or has an explicit user mention in its description or a non-deleted comment. Viewing, agent ownership, responsible-user attribution, and parent participation alone do not qualify.

**Steps to reproduce**

1. Sign in as two users in the same company.
2. Have the second user create and update a task that does not involve the first user.
3. Observe a task toast in the first user's company view before this fix.

The duplicate PR search found no existing change for personal task-toast eligibility.

## What Changed

- Extract the existing Mine participation predicate without changing Mine behavior.
- Compute task eligibility from stored tasks, comments, and run associations after WebSocket access checks. Use an exact indexed comment-mention lookup with `LIMIT 1`.
- Add optional `toastEligible` metadata to each viewer's event copy. Keep it off the shared event bus.
- Require eligibility for live task activity and task-linked run errors. Suppress generic agent errors.
- Continue cache updates for quiet events. Refetch visible comments when authenticated events omit hydration details. Ignore stale socket callbacks after account or company changes.
- Document toast semantics and add feature-map and agent-access-map coverage.
- Add persisted-data, socket, UI, and authenticated browser regression coverage.

## Verification

- Final commit: `1dc4f9402c5883744738cfe236ad00fa0bb5ca4c`.
- All 55 current-head checks passed. Two Storybook checks were skipped because this change does not affect that coverage.
- Greptile approved this exact commit with 5/5. The security scan passed. Both review threads are resolved.
- Local `pnpm build`, `pnpm -r typecheck`, `pnpm check:token-gates`, and `node scripts/check-maps.mjs` passed.
- The focused socket/UI suite passed 123 tests. The persisted eligibility suite passed 9 tests, including long history, exact viewer matching, and regex escaping. The existing Mine service suite previously passed 140 tests.
- The named browser case `task toasts follow personal involvement in multiplayer` passed against the final commit in an isolated authenticated instance. It used two real sessions, real WebSocket delivery, and persisted tasks, edits, comments, and mentions. It verified quiet unrelated activity, viewing alone, first and historical mentions, own-action suppression, visible-task suppression, and live cache refresh.
- A prior-head server shard passed its single rerun after a fixture-cleanup deadlock. The final-head CI run passed without a rerun.
- The broad local run remained in progress when CI completed the full test suite. Local focused checks and CI provide the completed test evidence.

## Risks

- This intentionally reduces live notifications. A new client stays quiet against an older server that omits eligibility.
- Eligibility queries add work to live event delivery. A lookup failure suppresses the toast and still delivers the authorized event.
- Deleted mentions cease to qualify unless another participation rule applies. Historical comment authorship retains the existing Mine behavior.
- No database migration or notification-settings change is required.

## Model Used

OpenAI Codex with GPT-6 assisted with implementation, tests, documentation, and PR preparation. It used repository tools, shell commands, and browser automation. The runtime did not expose an exact backend revision 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-13 08:09:19 -05:00
DottaandPaperclip 2ad36f4ff2 Fix skill source removal with a keep-or-delete choice (#16150)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - GitHub sources import skills and keep their updates together.
> - Disconnect only disabled a source and kept its read-only imports.
> - A disabled source could not be removed or imported again.
> - Removing a source needs an explicit decision about its imported
skills.
> - This pull request removes the source after the user keeps editable
copies or deletes the imports.
> - Existing disabled sources use the same removal flow.

## Linked Issues or Issue Description

Refs #15731, #14713.

**What happened?**

Disconnect left the repository on Sources. Its Disconnect action was
then disabled. The saved repository still blocked a new import.

**Expected behavior**

Remove the repository from Sources. Require a choice to keep editable
copies or delete its imports. Allow removal of sources that are already
disconnected.

**Steps to reproduce**

1. Import a repository in Skills → Sources.
2. Choose Disconnect source.
3. Observe the disconnected row and disabled removal action.
4. Try to import the repository again.

**Paperclip version or commit**

d67e3507b2.

## What Changed

- Add one confirmation with a required keep-or-delete choice.
- Remove the source and entries, including legacy disabled sources.
- Keep complete editable copies in My Skills, including shared
repository files and executable modes. Preserve skill IDs, keys,
versions, and agent assignments.
- Delete imported skills and source in one transaction with its activity
record. Check policy for every linked skill, including deselected
entries. Reject deletion of skills still used by agents.
- Fence removal with the source revision. Reload stale confirmations and
require a new choice. Prevent an in-flight refresh from restoring
removed imports.
- Reject incomplete legacy copies with a recovery message. Lock copied
skill rows against concurrent deletion and check publication succeeded.
- Add regression tests and production-page Storybook examples. Make an
initially unselected radio-card group keyboard accessible.

## Verification

- Workspace typecheck and build passed. Final affected server/UI
typechecks passed.
- Focused server, UI, and API suites passed: 114 tests initially. After
review fixes, all 21 source persistence and 58 route authorization tests
passed, including the complete repository copy regression; affected
server/UI typechecks and token gates passed again.
- Design token gates passed.
- Real isolated app: imported a public repository, assigned bro to the
CEO, verified that deletion reports the assignment, kept an editable
copy, observed the source disappear, opened the copy in My Skills, saved
an edit, and refreshed. The assignment remained. Re-imported the same
repository, deleted its new unused import, and verified the original
editable copy and assignment remained. No model tasks ran.
- Full local Vitest was attempted but terminated with exit 137 before
completion; it is not counted as a pass. Current-head sharded CI is
pending. Greptile reviewed the current head at 5/5 with no new
actionable comments. The final workspace build passed again.
- Storybook catalog aligned. The legacy removal example was inspected in
the browser; the Delete choice enables removal.
- Storybook: Skills / GitHub-synced skills / Sources · Disconnect source
and Sources · Remove disconnected source.

## Risks

- DELETE of a skill source now requires the saved revision and an
explicit copy/delete choice. Old callers must update their request body.
- Deletion removes imported skills and their records. Assigned skills
must be detached first.
- Incomplete legacy versions must be refreshed before keeping copies;
removal does not silently discard supporting files.
- Copies intentionally stop receiving upstream updates. Their IDs and
keys remain stable for assigned agents.
- No database migration. Copy writes use new managed directories and
clean up on failure. Delete restores quarantined files before releasing
publication locks on failure.

## Model Used

OpenAI GPT-6 through Codex, configured model `gpt-6-astra`. The runtime
does not expose the context-window size. Used shell tools, code editing,
tests, and browser interaction.

## 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-13 07:53:13 -05:00
DottaandPaperclip df06df2306 fix(slack): allow direct messages without at-mentions (#16151)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Slack connections let people start and continue agent tasks.
> - The Require at-mention setting currently applies to channels and
direct messages.
> - A person who sends a direct message has already selected the agent.
> - This pull request exempts direct messages from mention checks,
including queued work.
> - People can talk normally in DMs while channel messages keep the
configured mention rule.

## Linked Issues or Issue Description

Refs #15612, which introduced the Require at-mention setting. Searches
found no duplicate PR for the DM exemption.

**What happened?**

With Require at-mention enabled, a plain Slack DM does not start or
continue agent work. New Slack connections enable the setting by
default.

**Expected behavior**

Direct messages do not need an at-mention. Top-level DM replies remain
top-level. Channel messages and channel thread replies still follow the
saved mention setting.

**Steps to reproduce**

1. Enable Allow direct messages and Require at-mention on a Slack
connection.
2. Send a plain DM from an authorized sender.
3. Confirm the agent responds without an at-mention. Send a plain
follow-up and confirm it also works.
4. Confirm unmentioned channel messages remain filtered when Require
at-mention is enabled.
5. Disable Allow direct messages and confirm new DMs remain blocked.

**Paperclip version or commit**

Source baseline: `eb06c9fedd`.

**Deployment mode**

Built from source. Automated tests use disposable local Postgres and
provider fixtures. This change has not been deployed or retested in live
Slack.

## What Changed

- Exempt DMs at inbound admission, task mutation, work authorization,
and heartbeat queue claim.
- Keep channel mention checks and all existing sender, endpoint, and
destination checks.
- Update regressions for new connections, follow-ups, deferred delivery,
and coalesced queued messages.
- Clarify the Slack settings copy, connection documentation, and
verification maps.
- Refresh the affected Storybook review references after source
inspection.

## Verification

- Passed 55 Slack and durable scheduler tests with Vitest on the current
rebased commit, including admission through the extracted inbound
module.
- Passed `pnpm -r typecheck` and `pnpm build` on the rebased PR commit.
- Passed token gates and map checks.
- Passed Storybook catalog validation after rebase: 223 files and 2,210
stories.
- Full local `pnpm test:run` was stopped before completion after three
failures in the unchanged tool-access suite. All 418 tests in that suite
passed in a standalone rerun. The focused regressions passed; full
GitHub CI remains the merge gate.
- Greptile reviewed commit `b0293fa6571403c5e01c30bb7b5fcc5e91b88c97`:
5/5, with no findings or unresolved review threads. All 55 GitHub checks
passed on this commit; two optional Storybook jobs were skipped.
- No live Slack messages were sent during implementation verification.

## Risks

Existing Slack connections now accept authorized plain DMs even when
Require at-mention is enabled. Allow direct messages and sender
authorization still apply. Queued DM work follows the same exemption.
Channel behavior and DM reply routing are unchanged. No schema or
migration change is required.

## Model Used

OpenAI GPT-6, running as Codex, with reasoning, tool use, and code
execution. The runtime does not expose a more specific 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-13 07:40:46 -05:00
DottaandPaperclip fc3b15537b fix(runner): match call_api to existing agent REST permissions (#16040)
Route native call_api through existing agent REST permissions. Preserve admitted credentials across restarts, protect sensitive replies and private snapshots, and compare saved completion with provider history through the same redaction projection. Add permission, privacy and recovery regressions and update the access map and run-log guide.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 07:34:15 -05:00
DottaandPaperclip eb06c9fedd refactor(server): split chat channel workflows into provider and shared modules (#16080)
Move provider operations into channel folders and shared chat work into named interaction, setup, admission, delivery and publication workflows. Consolidate reviewed PRs #16085, #16089, #16096 and #16105. Preserve upstream execution-default and recovery behavior. The main service is 7,917 lines.

All 55 current-head CI checks pass, with two optional Storybook skips. Greptile is 5/5 with no findings. All 1,294 current chat integration cases, repository-wide typecheck and build pass locally. Broad local tool-route timing/socket failures are recorded in the PR.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 07:26:32 -05:00
DottaandPaperclip d67e3507b2 feat(runner): qualify Codex platform packages and cache hosted builds (#16028)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Native execution needs a compatible installed daemon and provider
dependency.
> - The Codex-default work found missing platform packages and
clean-install startup failures.
> - Release builds also repeat dependency compilation and run
preparation serially.
> - This prerequisite packages four daemon targets and verifies
installed native startup.
> - It also caches hosted Cargo dependencies and overlaps JavaScript
preparation with compilation.
> - Agent creation defaults and experimental gates stay at the current
master behavior.

## Linked Issues or Issue Description

Refs #15683. Refs #15555. Refs #15422. Refs #15926. Refs #15932.

Extract runtime and installation support from #15683. This prerequisite
has 65 changed files. Creation, onboarding, imports, UI runner selection
and graduation remain in the dependent PR. Feature/access maps and the
Storybook source catalog stay synchronized. Existing agents keep their
execution choices.

## What Changed

- Correct the existing runner test fixtures. Reserve owned credential
quorum listeners through the existing bounded fixture helper. Keep
foreign listeners intact, run actual credential staging once, and
preserve its rejection and unchanged-auth assertions. Reuse the
durable-resume outer-limit correction. Wait for actual warm-completion
and held-authorization callback entry under the existing recovery
deadline and surface early request errors. Preserve every semantic
assertion.
- Retain bounded installation failure diagnostics through the existing
qualification receipt upload. Keep the original error, 10-minute
deadline and all success checks.
- Keep startup ownership until credential preparation and registration
drain. Reject duplicate and post-close startup/resume work. Observe
shutdown during normal and adopted authentication readiness. Preserve
the primary failure and retain private state when cleanup is
unconfirmed.
- Package Linux x64/ARM64 and macOS Apple Silicon/Intel daemons. Verify
target headers, source identities, manifests, and hashes before assembly
and installation.
- Reuse the existing release producer/consumer workflows. Cache Cargo
dependencies by target, OS, compiler, lockfile, and contract inputs.
Rebuild workspace binaries. Permit trusted cache writes only for
canonical exact-source master pushes.
- Overlap JavaScript preparation with native compilation. Join both
commands and failure/cancellation cleanup before artifact verification.
- Preserve shell positional arguments in the shared SSH runner and apply
environment assignments correctly. Reject environment option keys. Six
before/after controls and two real SSH subprocess paths pass with zero
provider calls; this fix lands before both Codex and Claude defaults.
- Admit verified suspended SSH backups during cold controller
continuation, using the same full-scope, integrity and lifecycle checks
as sandbox targets. Two positive SSH cases fail before; all 14
positive/negative matrix cases pass afterward. Active ownership and
corrupt/malformed/nonempty state remain rejected.
- Record measured sandbox platform facts through the existing explicit
environment probe. Bind facts to the company, effective provider
configuration, immutable OCI image and actual captured image binding.
Check the returned cleanup receipt. Reject client-authored proof and
stale or named boot identities. Creation and discovery allocate nothing.
Preserve connection-only behavior.
- Resolve the actual remote Codex target and preserve same-host
development overrides. Preserve runtime artifact verification. Use
actual protocol/model compatibility rather than a numerical CLI version
fence.
- Resolve the installed Codex login dependency on 64-bit Linux and Mac
independently of the daemon libc baseline. Preserve account homes and
safe errors.
- Fix actual clean-install TOML table comparison. Preserve strict
semantic checks, comments, private writes, and malformed-input
rejection. Preserve primary startup failures and bounded cleanup.
- Document GNU libc 2.39+ and stock Ubuntu sandbox prerequisites.
Qualify ARM startup with a scoped distribution profile and unchanged
global restrictions. Preserve Windows and Alpine deferral.
- Preserve selected and saved native Codex reasoning effort in the
existing UI builder, while excluding it from other providers. This small
shared correction prevents import edits higher in the stack from
dropping effort.
- Reuse existing installed-package checks and assertions. Paperclip
tarballs contain no Codex native payloads. Official npm dependencies
supply the consumer's Codex binaries. All Docker and native builds stay
remote.

## Verification

Candidate: `271d434c170e74bdedde65d0b13059a55419c943`. It includes
current master `e168512f7bdbb6af6ea01954c6d7ed80b91e903e` and preserves
prior candidates through ordinary commits.

Current [full
CI](https://github.com/paperclipai/paperclip/actions/runs/38346601686)
is successful on attempt 2. GitHub reports 54 successful checks and four
conditional skips, including required verify and browser aggregates,
workspace typecheck/tests/build, Linux DB controls, both runner shards
and [isolated native
compilation](https://github.com/paperclipai/paperclip/actions/runs/38346601609).
Fresh current-head Greptile is 5/5 with all four threads resolved. The
passing build's clean Grok/Codex report records CI merge source
`7a82a37438e6e448abc7cc5caa60dc2519a97be0`, 18 installed packages,
omitted Codex payloads, official host-only npm dependencies and zero
provider calls. Pi setup and its no-prompt lease verification also pass.
The first attempt's single 60-second Node archive download timeout
remains retained; its DNS/TLS/download cause remains unknown. One
unchanged-source retry was requested through the normal Build-job dialog
with debug logging disabled. GitHub's attempt 2 graph reports the other
CI jobs as well; no second retry or source/deadline relaxation was made.

The [final exact-source package
campaign](https://github.com/paperclipai/paperclip/actions/runs/38347027535)
is successful on literal `271d434c170e74bdedde65d0b13059a55419c943`. It
passes all four native producers, public Linux package assembly and
clean installed consumers on Apple Silicon, Intel Mac and Linux ARM64.
The nine qualification-mode skips are intentional; full CI above covers
their ordinary gates. Native builds and Docker assembly ran remotely.
The predecessor [`dae808` package
campaign](https://github.com/paperclipai/paperclip/actions/runs/38342104102)
remains literal historical evidence. Only tests and the dated record
differ from that source; runtime, package and workflow code is
unchanged. The reused credential fixture correction passes all 42
canonical Node 24 controls with synthetic data and independent
matching-source review, and the current hosted runner shard passes.
Production credential behavior is unchanged.

**Merge hold:** current-source CI, fresh 5/5 review and exact-source
package qualification pass. Actual code-owner approval and normal
protected merge remain pending. No slice has merged or deployed. GitHub
CLI authentication needs refresh; the normal signed-in embedded GitHub
UI provided this status without credential workarounds.

- The master merge preserves both Storybook source-review appendices and
the guarded test-home cleanup. All six existing cleanup controls pass
without skips. Master's isolated-home helper/test/setup are
byte-identical. The two existing whitespace lines in master's unrelated
privacy test remain outside this PR's diff.
- Canonical Node 24 passes all 24 existing public-install helper tests.
Three narrow added cases prove bounded/redacted failure metadata,
symlink exclusions, original exception identity, failed diagnostic
writes and no false failure on success. The Mac installer now uses
`--timing`; at most two 16 KiB tails and 32 structured entries per tail
enter the existing receipt upload. Raw argv, URLs, environment, error
text and stacks are omitted. A failed receipt explicitly remains
FAILED/unqualified/runtime-not-attempted. The 600,000 ms install
deadline and every real package/hash/payload/lock/lifecycle/runtime
oracle are unchanged.
- Canonical Node 24 reproduces the durable-resume outer timeout under
controlled fake latency. Only its outer test limit changes to 15
seconds; internal 500 ms/5 second deadlines and usage/effect assertions
remain. Controlled warm-recovery and before-bootstrap authorization
scheduling delays reproduce their callback polling failures, while the
explicit entry signals pass. Early recovery errors surface unchanged.
Independent review is clear. Historical mock binaries support the
fixture controls; fresh full hosted CI remains decisive. The original
hosted callback failure cause is inconclusive.
- The composed runner/surfaces/server/E2E TypeScript checks pass. Map
checks pass 35 areas/171 capability IDs. Token gates pass. The existing
Storybook catalog passes 223 files/2,210 stories; story IDs and
classifications remain intact. The current master had 25 inherited stale
fingerprints from three changed map recipes. Only those guarded
fingerprints and a source-only review note change; all 92 non-map
story/component/fixture inputs remain unchanged. Existing
classifications are retained. This proves source structure, not visual
or live acceptance.
- The 65-file contribution audit has no code file crossing from below
1,000 lines to above the limit. Earlier comment cleanup preserves
executable emissions, parsed workflows and action pins. Independent
cleanup/catalog and three-file reporting reviews are clear. No new
workflow, suite, grader or local native/Docker build was added.

**Prior `dae808` gate evidence:** fresh Greptile is 5/5 with four
resolved threads, isolated native compilation and the browser aggregate
pass. Its first runner shard passes the previous two corrections but
fails a different held-authorization cell at the implicit one-second
polling deadline. It records 1,672 passes/one failure/three skips. This
candidate applies the verified entry-signal correction without changing
production deadlines or any semantic assertions. Its original hosted
cause remains inconclusive until fresh CI.

**Previous-source evidence, kept literal:** the [exact `16a532` package
campaign](https://github.com/paperclipai/paperclip/actions/runs/38338806076)
passes all four native producers, public Linux package assembly, Apple
Silicon, Intel Mac and Linux ARM64 installed consumers. Its fresh review
is 5/5 with all four threads resolved. Full CI records 52 successful
checks, four conditional skips and the two fixture failures corrected
here. This is not a green full-CI candidate or proof for the new source.

`64c074fb8da1b31f8d6042c4fef48dc4164e759b` passed [full
CI](https://github.com/paperclipai/paperclip/actions/runs/38332726938),
54 checks/four conditional skips, [isolated native
compilation](https://github.com/paperclipai/paperclip/actions/runs/38332726787/job/115085895328),
and fresh Greptile 5/5/all four resolved threads. The hosted transport
suite passed 246 tests and startup trust passed 22, closing that
source's local fake-binary limitation. These are historical after
reconciliation.

The [previous exact-source package
campaign](https://github.com/paperclipai/paperclip/actions/runs/38332921463)
built all four platform daemons and passed public Linux package assembly
plus Apple Silicon and Linux ARM64 installed consumers. **Intel Mac
failed:** its initial scripts-disabled npm install timed out before
lifecycle/runtime assertions. The underlying download/resolution/host
cause is unknown because the old helper deleted the npm logs. This is
the concrete baseline for the diagnostic change. The failed campaign
remains failed; no all-platform signoff or automatic retry is claimed.

The earlier `a200495` [four-target
campaign](https://github.com/paperclipai/paperclip/actions/runs/38283695248)
and [scoped ARM
qualification](https://github.com/paperclipai/paperclip/actions/runs/38291535914)
remain component evidence. Cargo restores were cold misses; no
warm-cache speedup is claimed. Paperclip tarballs contain no Codex
payloads. Official npm dependencies supply Codex on the user's platform.

Current-source full workspace typecheck/tests/build, Linux DB controls,
static/Rust/native tests, all browser shards and exact-source package
consumers must pass. New-agent defaults and final managed onboarding
belong to #15683. Actual private Cloud composition, warm/cold signup,
task/follow-up and rollback compatibility remain later release gates.
Source audits found that ordinary managed onboarding does not run the
saved platform probe and Cloud distributes named snapshots without a
verified final boot-image binding. These dependent release blockers
remain open; prequalified E2E fixtures do not close them. Master adds
task-workspace migrations; the final image/migrator and previous-image
checks must include them. ARM paid task/follow-up and ordinary OS APT
installation are not proven.

## Risks

- Multi-target packages require source and header agreement. Missing or
mismatched artifacts fail explicitly. Existing execution choices remain
unchanged.
- Linux requires GNU libc 2.39+ and compatible sandbox prerequisites.
The scoped hosted ARM preparation does not qualify arbitrary containers
or kernels. Windows, 32-bit ARM, and Alpine remain deferred.
- Only dependency crates are cached. Preview jobs cannot write trusted
caches. Concurrent command cleanup must preserve the primary failure.
- Native-free Paperclip packaging retains official Codex npm
dependencies. No Codex payload bundling, schema migration, automatic
agent migration, or production deployment is included.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, repository tools, code
execution, and parallel agents. The exact serving model snapshot 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 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-13 06:24:07 -05:00
Dotta 4a641d0980 fix(ui): default human task replies to the latest assigning agent (#15594)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Tasks let agents ask a person for input.
> - The reply composer needs a useful default recipient.
> - A recent comment does not identify the agent that assigned the task.
> - The old fallback can select the person who is writing the reply.
> - This change uses the latest agent assignment and keeps manual
choices.

## Linked Issues or Issue Description

**What happened?**
When an agent assigns a task to a person, the reply composer can select
that person or a different commenter. A data refresh can also replace a
manual selection.

**Expected behavior**
For a task assigned to the current person, select the latest agent that
assigned it to them. If no such assignment exists, use the latest other
commenter. If neither exists, leave the recipient empty. Keep explicit
choices during refreshes.

**Steps to reproduce**
1. Have agent Alpha assign a task to a person.
2. Assign it to agent Beta, then have Beta assign it to the same person.
3. Open the task as that person.
4. Check that the reply composer selects Beta.
5. Select another recipient. Refresh task data. Check that the selection
stays.

Related work: #15091 shares task composer controls. This change fixes
recipient defaults.

## What Changed

- Read real assignment changes from REST and native runner task
activity, in time order.
- Record human assignee fields in native create and reassign activity.
- Ignore unchanged assignment fields and assignments to other people.
- Use the assigning agent before comment hints for human-owned tasks.
- Connect the suggestion to current and classic task composers and
pipeline replies.
- Keep manual selections when suggestions change.
- Add regression tests and document the fallback.
- Use tasks with no assignee in the attachment receipt tests. This keeps
the tests on the comment route. Dismiss announcements through the real
UI.
- Record human and agent assignees in REST creation receipts. Include
child creation in the assignment history resolver.

## Verification

- Focused tests cover assignment order, creation, other recipients,
unchanged fields, fallback, and manual selections.
- Browser fixture uses the real composer and assignment resolver. It
selects Beta and keeps a manual human selection after history refresh.
- Passed: 223 focused tests across four suites.
- Passed: `pnpm --filter @paperclipai/ui typecheck`.
- Passed: `pnpm --filter @paperclipai/ui build`.
- Passed: `pnpm check:token-gates`.
- The initial CI run passed full build, typecheck, Rust, unit tests and
seven E2E shards. The attachment shard exposed tests that relied on the
old self-selection default. The tests now select the recipient
explicitly.
- Native assignment regression tests and resolver tests pass: 40 tests.
- Attachment E2E suite: 10 passed; one page-load timing failure passed
on an isolated rerun. All 11 cases passed across the suite and rerun.
- Previous full CI passed at cc7ed05. New CI must pass on the current
head.
- Latest local checks: 16 resolver tests and 7 workspace policy tests
pass. REST creation audit tests cover both creation routes.
- The latest browser check could not start. The UI build exceeded the
local memory limit (exit 137). Full typecheck, tests, build, and browser
checks will run in CI.

## Risks

- Old activity rows without previous assignment values cannot prove a
reassignment. They use the comment fallback.
- An empty fallback requires the person to choose a recipient or use the
existing no-assignee flow.
- No database or API schema changes.

## Model Used

OpenAI Codex, GPT-6. The session does not expose an exact model build or
context window. Used code editing, shell tools, tests, and browser
verification.

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


Final validation at f83f2f6: 55 checks passed. Two optional Storybook
checks were skipped. All browser shards passed. Both review threads are
resolved. Classic attachment tests use the real no-assignee
confirmation.
2026-10-13 00:56:15 -05:00
DottaandPaperclip d837b65be8 refactor(server): move provider setup, inventory and delivery into channel folders (#16058)
Consolidate the ten reviewed provider-folder extractions. Preserve the verified source tree and current master changes.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 00:26:33 -05:00
DottaandPaperclip 7d7f55e4be fix(apps): show user avatars and center access rows (#15577)
## Thinking Path

> - Paperclip helps people manage AI agents and their access to apps.
> - The app access page shows people with linked chat accounts.
> - The identity response omitted the user's saved image.
> - The row placed the avatar beside only the first name line.
> - This change returns the image and centers the avatar beside both
name lines.
> - People can recognize linked accounts and still see initials when no
image is available.

## Linked Issues or Issue Description

**What happened?**

The app access page showed initials even when the linked Paperclip user
had a profile image. The avatar was above the vertical center of the
Disconnect button.

**Expected behavior**

Show the linked user's saved image. Center it beside the Paperclip and
chat names. Keep initials when no image is available.

**Steps to reproduce**

1. Link a Slack account to a Paperclip user with a profile image.
2. Open the app's Access tab.
3. Inspect the person's avatar and the two name lines.

Searched public issues and pull requests. No duplicate fix was found.
Related avatar work in #15095 concerns agent uploads, not linked user
profiles.

## What Changed

- Return `paperclipUserImage` from the chat principal service and
declare it in the API and client types.
- Use the shared avatar primitives and center the avatar beside the full
name block. Keep full names available in hover text.
- Add image and initials examples to the existing Storybook access
story, with image and alignment checks.
- Extend the service test to check saved images and missing images.

## Verification

- Token gates and `git diff --check` pass.
- The 33 existing chat setup and identity-link UI tests pass.
- The OAuth installer service regression test passes.
- Chromium rendered the Storybook Access story at desktop and mobile
sizes. The avatar, name block, and Disconnect button centers all
measured 307 CSS pixels on desktop.
- UI typecheck passes. Full repository typecheck and build stop at the
Rust runner because this workspace has no `cargo`. The initial parallel
typecheck also exceeded the 4 GiB workspace limit. The full local test
attempt was stopped as memory approached the 4 GiB limit; it has no
complete verdict. CI passed the full build, typecheck, test, and E2E
lanes on the initial commit. The previously reviewed commit passed 54
checks with two intentionally skipped Storybook checks. Master was
merged on October 10 to resolve an import conflict; fresh CI is running
for `24badb742d331c1b6ee3e90cbd06defefe5cd634`. Token gates and
whitespace checks pass after conflict resolution. The single retry after
CI runner shutdowns passed. Greptile gives this commit 5/5, with both
review threads resolved.

### Screenshots

The Storybook fixture shows an image, fallback initials, and a pending
account.

![Desktop access
rows](https://raw.githubusercontent.com/paperclipai/paperclip/fix/app-access-user-avatar/doc/screenshots/app-access-avatar/desktop.png)

![Mobile access
rows](https://raw.githubusercontent.com/paperclipai/paperclip/fix/app-access-user-avatar/doc/screenshots/app-access-avatar/mobile.png)

## Risks

Low risk. The API field is additive and needs no migration. The image
uses the same avatar primitive as other user identities. Missing or
failed images retain the initials fallback. Other chat providers share
this access row.

## Model Used

OpenAI Codex, GPT-6. Tool use and code execution. The runtime does not
expose a more specific 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>
2026-10-12 23:54:01 -05:00
Devin FoleyandPaperclip 6c198d616e fix(issues): preserve human handoffs from the active agent run (#16082)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Tasks have one owner, and the server records assignment changes.
> - An agent can finish its work by handing the task to a human for
review.
> - The assignment route stops the active run before it saves that
handoff.
> - When the caller is that run, a slow workspace restore can exceed the
stop timeout and prevent the assignment from saving.
> - Recovery can then mark the task Blocked while its comment says the
handoff succeeded.
> - This pull request lets the authenticated legacy run save a human
handoff and finish its turn normally.
> - Other actors and transfers to executable agents keep the verified
stop requirement.

## Linked Issues or Issue Description

Refs #13730. Related: #13782, #14700, and #15094.

**What happened?**

An agent posted its review handoff, then sent `PATCH /api/issues/{id}`
to assign the task to its human creator with status `in_review`. The
route cancelled that same run before saving the assignment. Workspace
restore took 66 seconds. The stop limit is 60 seconds. The assignment
never committed. Recovery marked the task Blocked, and it stayed
assigned to the agent.

**Expected behavior**

The authenticated active legacy run can hand its own task to a human and
finish normally. The human assignment and requested review status must
save without waiting for workspace restore. Native runs, board
reassignment, agent API keys, and transfers to another agent still
require a verified stop.

**Steps to reproduce**

1. Run a legacy agent on its assigned task with a signed run token.
2. Send a task PATCH from that run with a human `assigneeUserId`, a null
`assigneeAgentId`, and status `in_review`.
3. Make stopping the sandbox take more than 60 seconds. Before this fix,
the handoff fails before the task update. With this fix, the update
saves and the run finishes normally.

**Paperclip version or commit**

Reproduced in hosted execution. The patch is based on master at
`56a0bf0d492`.

**Deployment mode**

Hosted authenticated server with a legacy agent in a remote sandbox. The
same route serves local deployments.

PR #13782 proposes a broader exemption for all self-handoffs. This
focused change limits the exemption to human recipients and legacy runs.
It checks the exact active run, agent, company, and task, and it retains
the stop requirement before an executable successor can take ownership.
PRs #14700 and #15094 address later wake and recovery behavior after a
reassignment stop.

## What Changed

- Skip the reassignment cancellation only for an authenticated legacy
run handing its own task to a human.
- Reject later mutations from the outgoing run against its human-owned
task, both in route authorization and under the update transaction
locks.
- Keep assignment permission checks, goal control, and queued human
response protection.
- Add route regressions for slow stops, active-run lookup, actor and
runtime boundaries, recipient types, permission denials, and follow-up
edits, completion, and reassignment.
- Update the real HTTP low-trust handoff test to prove the run stays
alive, its late edit fails, and the human wake receipt remains
invalidated.
- Add Postgres tests for saved assignment, former-owner rejection, and
review state after the outgoing run succeeds or fails.
- Document the handoff rule and update the task feature and agent access
maps.

## Verification

- 328 relevant tests passed across the route, issue service, low-trust
HTTP, and legacy recovery suites. The route suite passed all 160 tests,
and all 13 low-trust HTTP tests passed after updating the obsolete
cancellation expectation.
- Removing the production guard makes both slow-shutdown handoff
regressions fail with HTTP 500.
- `pnpm -r typecheck` passed.
- `pnpm build` passed.
- `node scripts/check-maps.mjs` passed.
- `git diff --check` passed.
- All 54 current-head checks passed; 2 optional Storybook checks were
skipped. The CI lanes cover the full Vitest suite, typecheck/build,
Runner checks, and product E2E checks.
- Apex reviewed `c68fbfb356c3d80eb7136babb49940709a3c3459` at 5/5 with
no findings; both earlier review threads are resolved.
- The complete test lanes passed in CI. The duplicate local full `pnpm
test:run` was stopped after CI completed; it did not finish locally. All
328 focused local tests and full local typecheck/build passed.

## Risks

- The outgoing legacy turn stays alive after the human takes ownership.
Route and transactional ownership checks reject its later mutations of
the human-owned task. Read and comment access follows normal policy.
Goal control still runs before the handoff.
- This exemption does not apply to native runs or another agent as
recipient. Those paths still stop the old execution before assignment.
- This fix prevents future failed handoffs. It does not infer or replay
old assignment requests.
- There is no schema change or API shape change.

## Model Used

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

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-12 20:34:28 -07:00
DottaandPaperclip e168512f7b perf(chat): stop polling failed run retries (#15871)
Stop one-second scans of failed chat-run retries. Wake the existing retry queue after transaction commits and at saved retry or stale-claim deadlines. Preserve claim fences, authorization, recovery, and idempotent agent admission.

Resolve conflicts with the current chat-service extraction by applying the retry signals and shared deadline predicate in the failed-run retry module.

Verified with pnpm typecheck, pnpm build, all 1,328 chat and coordinator regression tests, and current-head CI. Greptile reviewed the final head at 5/5 with no unresolved threads.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 19:49:47 -07:00
56a0bf0d49 Support test Cloud connector identities (#16033)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Its Cloud connector uses an environment-bound identity to call a
hosted broker.
> - The identity parser and enrollment flow accept development, staging,
and production.
> - A separate test broker needs its own environment identity.
> - This pull request accepts test identities and maps the test broker
to that environment.
> - Known hosted brokers still reject identities from other
environments.

## Linked Issues or Issue Description

**What existing behavior does this improve?**

Cloud connector configuration and self-host enrollment.

**Current behavior**

The app rejects an identity with environment `test`. Self-host
enrollment infers development for the test broker.

**Proposed behavior**

Accept `test` identities at `https://test.paperclip.app`. Infer test
during self-host enrollment. Reject development, staging, and production
identities at that broker. Continue to reject test identities at the
production and staging brokers.

**Reason and benefit**

Branch testing needs a separate broker so test identities and requests
stay in their selected environment. The existing identity, enrollment,
and signature flow can provide that isolation with a new allowed
environment value.

**Breaking changes**

Existing environment values and broker checks keep their current
behavior. Older app versions cannot use test connector identities.

## What Changed

- Add `test` to managed and self-hosted connector environment types and
validation, and to the board API enrollment status type.
- Infer test from its hosted broker and enforce the matching
environment.
- Test enrollment persistence, signed request environment/audience
claims, and broker mismatch rejection.
- Document test broker configuration in the development guide.

## Verification

- Connector, enrollment, and board API-client suites passed: 63 tests
across three files. Board UI typecheck and token gates passed after
synchronizing its enrollment response type.
- `pnpm -r typecheck` and `pnpm build` passed, including after rebasing
onto current master.
- `pnpm test:run` completed its general server stage: 17,867 passed, 105
skipped, and 9 failed across 5 files. The runner stops at that failing
stage, so it did not run subsequent workspace/serialized stages locally.
All nine failures also reproduce on the unchanged base commit in a
separate checkout. Five skill-related cases resolve an unrelated
ancestor skills directory; two workspace cases accept a remote main
branch that their fixture expects to be absent; two permission-cleanup
cases return macOS `ENOTEMPTY` instead of the expected permission error.
- Current-head GitHub CI passed, including full general and serialized
test suites, typecheck, build, runner checks, and browser e2e. Greptile
is 5/5 with no unresolved comments.
- `git diff --check` and Gitleaks passed.

## Risks

The new environment value is additive. The test broker must support test
identities before use. This change preserves existing signature
handling, managed identity overrides, and rejection of mismatched
identities at known hosted brokers. A local full-suite pass is not
claimed; the nine failures above also occur without this change.

## Model Used

OpenAI Codex, GPT-6, with reasoning and tool execution. The exact
serving model ID 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
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Dotta <34892728+cryppadotta@users.noreply.github.com>
2026-10-12 19:32:33 -07:00
83e13db8fa fix(inbox): keep automatic recovery cleanup out of personal participation (#16063)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The personal inbox uses issue activity to identify a person's
participation.
> - Issue reads can cancel stale recovery actions and record that
cleanup as the reader's action.
> - This adds unrelated tasks to the inbox and refreshes their activity
timestamps.
> - A workspace owner hold can also alternate between creation and
cancellation after a task finishes.
> - This pull request corrects cleanup attribution, excludes historical
read cleanup, and retains one exact owner hold.
> - Agents can read authorized issue activity to diagnose the same
problem themselves.

## Linked Issues or Issue Description

Refs #15742 excludes logs by the execution-recovery actor from activity
timestamps. This PR covers reader-attributed stale recovery cleanup,
personal inbox participation, exact workspace owner holds, and sandbox
issue activity access. I searched open issues and PRs for recovery inbox
and activity bridge fixes. I found no duplicate for this defect.

**What happened?**

Loading issue lists cancels stale recovery actions under the reader's
identity. The personal inbox treats those rows as user participation.
One completed issue had hundreds of such rows and no edits or comments
by the affected reader. A retained native workspace owner repeatedly
creates a verification action that read projections then cancel. Sandbox
agents cannot inspect the issue activity because the reverse bridge
rejects the route.

**Expected behavior**

Automatic cleanup belongs to the system and does not add an issue to a
reader's inbox. Historical cleanup rows remain auditable but do not
count as participation or recent activity. An unresolved physical
workspace owner keeps one stable verification action. Agents can inspect
activity only for issues their identity may read.

**Steps to reproduce**

1. Finish a task whose native run retains an unverifiable workspace
owner receipt.
2. Repeat workspace recovery checks and GET requests for issue detail,
compact lists, and recovery actions.
3. Observe new recovery action IDs and reader-attributed cancellations
in the activity history.
4. Query the personal inbox for that reader and observe the unrelated
task.
5. From a sandbox agent, request GET /api/issues/:id/activity and
observe Route not allowed.

**Paperclip version or commit**

Observed at f5346c05b9. Regression tests
use current master behavior.

**Deployment mode**

Authenticated hosted instance with native and legacy sandbox agents. The
inbox defect is independent of the adapter.

## What Changed

- Attribute automatic stale recovery cleanup to the recovery system
actor.
- Exclude exact historical read cleanup rows from personal inbox
membership, unread counts, and both activity timestamp queries. Keep all
audit rows and explicit user recovery decisions.
- Retain owner verification holds only while the exact company, run,
issue, and owner fingerprint still match. Apply the same rule to reads
and background recovery.
- Make repeated checks for the same owner hold leave its identity,
attempts, and timestamps unchanged. Keep physical takeover blocked.
- Allow GET issue activity through the sandbox bridge. Keep the existing
company and issue-read authorization and reject broader audit routes.
- Add regressions and update the runtime API and recovery documentation.
Refresh generated source references for the documentation edits. Update
the paired activity, inbox, and recovery maps.

## Verification

- Focused Vitest verification: 315 tests pass across five files. The
updated terminal owner hold regression also passes. The files cover
issue service, recovery routes, activity authorization, sandbox callback
bridge, and native workspace finalization.
- `pnpm -r typecheck` and `pnpm build` pass locally. The full general
and serialized unit test matrix passes in CI on the latest commit. The
duplicate local `pnpm test:run` was stopped after about 30 minutes once
that full CI coverage passed; it did not report a local full-suite pass.
- `node scripts/check-maps.mjs` passes for all paired areas and
capability IDs.
- Apex is 5/5 on commit 44c3787888. Its
access map finding is fixed and resolved. All CI checks are green on
this commit.
- The new regression repeats reads and background sweeps for done and
cancelled tasks. It checks one unchanged hold, zero physical copyback,
zero personal inbox participation, and system cleanup after the exact
receipt is cleared.
- Historical cleanup tests preserve explicit recovery decisions,
nullable metadata, unread counts, and the original audit rows.
- Authorization tests permit an authorized agent and reject
cross-company or issue-boundary reads. Bridge tests reject write
methods, extra path segments, and company-wide audit routes.

## Risks

Existing inbox items can disappear if their only participation evidence
was automatic read cleanup. No data migration or audit deletion is
needed. An unresolved workspace owner remains visible after task
completion; this preserves the physical writer fence. A stale or cleared
owner does not retain the hold. Issue activity access expands only the
sandbox transport allowlist and still uses existing controller
authorization.

## Model Used

OpenAI GPT-6 through Codex, with reasoning, repository inspection, code
editing, shell execution, and test tools. The session identifies the
model family as GPT-6. A more specific runtime 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 either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Amy (Opus) <noreply@anthropic.com>
2026-10-12 19:25:15 -07:00
DottaandPaperclip f329b24ee1 feat: give tasks durable project-optional workspaces (#15749)
Give tasks durable company-scoped workspace bindings independent of project assignment, channel defaults and authorized repository preparation. Reuse verified input seeds and sparse output recovery while preserving trust, privacy and controller provisioning boundaries.

Full CI and E2E, Superagent, legacy checks and Greptile Apex 5/5 passed on reviewed head 753680b61c. All review findings are resolved; the PR records focused verification and historical acceptance limits.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 20:45:18 -05:00
4b958ecbd8 fix(server): keep classified provider quota runs out of Sentry (#16029)
A run that stops because the user's provider account hit its usage
limit is an expected wait, not a Paperclip defect. The adapter already
classifies it (errorCode and resultJson.errorFamily are both
provider_quota) and recovery schedules a retry at the reset time.

Skip the Sentry report only when both adapter-owned fields agree, the
run status is failed, no signal stopped it, and the failure did not
happen during setup. Timeouts, cancellations, signals, other codes and
missing or mismatched families still report.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Dotta <cryppadotta@users.noreply.github.com>
2026-10-12 15:59:27 -07:00
DottaandPaperclip 64bd6e9e07 refactor(chat): extract delivery, reaction, and setup boundaries (#16024)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Chat channels connect that work to provider conversations.
> - One shared service contains callback, delivery, reaction, and setup
logic.
> - These responsibilities have distinct contracts and existing
public-path tests.
> - This PR moves ten cohesive responsibilities into focused modules.
> - The service retains shared runtime ownership and scheduling.
> - Provider behavior is easier to locate and review.

## Linked Issues or Issue Description

Refs #15995, #16026, #16027, #16030, #16031, #16035, #16037, #16038,
#16041, #16043.

This PR consolidates the reviewed extraction stack. The plan is
`doc/plans/2026-10-12-chat-channel-delivery-boundaries-refactor.md`.

## What Changed

- Move Slack callback verification into the Slack folder.
- Move Discord Gateway events and root-thread admission/recovery into
the Discord folder.
- Extract durable receipt reactions and inbound reaction
admission/replay.
- Extract inbound attachment ingestion and outbound integrity-checked
hydration. Preserve the public hydration export.
- Extract durable provider effects. Keep Telegram callback notices in
the Telegram folder.
- Extract conversation/activity reads and setup qualification. Keep
Slack and Teams projections in their channel folders.
- Preserve the merged managed Slack setup behavior, shared workers,
credential leases, and transaction boundaries.
- Reduce `chat-channels.ts` from 26,493 lines / 1,048,769 bytes to
21,460 lines / 862,284 bytes. This removes 5,033 lines from the service.

## Verification

- Each extraction passed its selected existing tests before and after
through public service paths.
- On updated master, all 55 selected Slack cases passed before
extraction. All 194 selected chat/managed-Slack cases passed on the
final stack tip.
- `pnpm -r typecheck`, `pnpm build`, module-boundary checks, and
whitespace checks passed on that tip.
- The normalized syntax audit verifies 69 moved declarations and all
retained statements, including their relative order, against updated
master.
- The eight earlier stack merges retain the exact validated repository
tree. The combined root equals the validated tip merged with current
master. All chat-channel files remain byte-identical to the validated
tip. Current combined-root CI and review are pending.
- Full local verification remains unverified. The unchanged
`workspace-git-snapshot-streaming.test.ts:56` stress test timed out at
300,000 ms in a clean run and again in isolation. The full run was
stopped after the reproduced failure. No timeout or assertion was
weakened.
- These checks use assembled services, real local databases, and
provider fixtures. They do not prove live third-party execution.

Reviewers can run the public-path suites:

```sh
pnpm exec vitest run server/src/__tests__/chat-channels.integration.test.ts server/src/services/chat-outbound-attachment-hydration.test.ts server/src/__tests__/slack-development-manager.test.ts
```

## Risks

- Factory wiring must preserve live values, shared state, and
initialization order. Identity-link construction retains deferred access
to the provider-effects callback.
- Credential fences, authorization transactions, and delivery settlement
must keep their original ordering. Syntax parity and public-path
integration cases cover these boundaries.
- Full local verification has the recorded Git-streaming timeout. The
combined PR must pass master’s required CI before merging into master.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning and repository tools. The
exact runtime model ID and context-window size are not exposed in this
session. No separate agent was used.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-12 22:46:25 +00:00
Devin FoleyandPaperclip 9b1b905e01 fix(heartbeat): answer an unsettled adapter Stop with a 409 instead of a 500 (#16032)
An owned adapter Stop that is still settling when the 60s request wait
expires now returns 409 adapter_stop_pending instead of escaping as an
HTTP 500. Stop proof, lease and restore ownership are unchanged.

Sentry is notified only when the same execution is still unsettled ten
minutes after the Stop request (measured from stopRequestedAt), so real
Stop hangs stay visible and slow workspace sync-back no longer pages.

Co-Authored-By: Paperclip <Paperclip-Paperclip@users.noreply.github.com>
2026-10-12 15:44:47 -07:00
DottaandPaperclip dabb8c5daf test: remove repeated setup and strengthen regression proof (#16022)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Its tests protect company boundaries, approvals, durable tasks,
credentials and the board UI.
> - Several owner suites repeat large database fixtures and render
frames.
> - Some tests repeat stronger proof or pass when the behavior they
describe is broken.
> - This pull request shares exact setup, removes proven duplicate cases
and repairs weak observations.
> - It preserves distinct security, lifecycle, concurrency and
user-facing contracts.
> - The benefit is 5,027 fewer test lines and stronger regression
checks.

## Linked Issues or Issue Description

Refs #15978. This continues the merged test-audit cleanup in [PR
#15978](https://github.com/paperclipai/paperclip/pull/15978), which
removed 11,976 test/support lines. The savings in this pull request are
additional.

Related open work: [PR
#13460](https://github.com/paperclipai/paperclip/pull/13460) changes
legacy OAuth parent/version lock ordering. This pull request retains
those production paths and the existing lock-order test. Its secrets
changes compact fixtures and repair the two AWS wrapper exclusion
observations.

Scoped title searches found no open duplicate audit or
fixture-consolidation PR and no matching audit issue. The problem is
described here using the enhancement template.

**What existing behavior does this improve?**

The maintenance and regression value of the existing server, UI, CLI,
shared and wiki-plugin test suites.

**Subsystem affected**

Cross-cutting test code. Two UI modules also lose unused test inspection
exports.

**Current behavior**

Owner tests repeat database rows, service inputs, HTTP setup and render
frames. Some cases repeat stronger assertions. Two AWS lock tests record
only cleanup events, so an AWS provider write can happen inside the held
lock without failing those observations. Several other negative tests
accept or do not observe the wrong result. Worktree fixtures also use a
temporary path alias that conflicts with the existing canonical-path
checks on macOS.

**Proposed behavior**

Use small local helpers that preserve exact inputs, fresh allocations,
callbacks, query order and await positions. Remove only cases with named
stronger keepers. Tighten negative assertions and observe actual AWS
provider entry while a confirmed lock holder remains parked. Resolve
temporary fixture roots to their canonical paths.

**Reason and benefit**

Remove 5,027 net test lines and 15 redundant registered executions.
Remove 11 lines of unused UI inspection exports. Keep every distinct
company, actor, approval, credential, persistence, stale-write and
concurrency contract. No wall-time speedup is claimed for setup
compaction.

**Breaking changes**

None for application behavior, APIs or stored data. The removed exports
expose private UI test state and have no remaining callers.

## What Changed

- Consolidate repeated runtime, workspace, portability, route,
tool-access, policy, gateway and state-service fixtures.
- Share repeated UI render frames and CLI/wiki-plugin setup while
retaining the original actions and expected results.
- Remove 15 repeated case executions only where the audit records an
exact stronger keeper.
- Repair weak HTTP/UI negative assertions and two AWS account-home lock
observations. Keep the existing durable results and cleanup behavior.
- Remove the unused `__inflightGetCount` and `__secondTickInternals`
inspection exports. Keep observable request coalescing and ticker
lifecycle checks.
- Canonicalize temporary worktree fixture paths without weakening
production path checks.
- Record the owner decisions, savings, independent preservation reviews
and validation in [the dated audit
report](https://github.com/paperclipai/paperclip/blob/58c5c4aa241c85f4665a3159c192888bbb2a5cfa/doc/plans/2026-10-12-test-audit-rounds.md)
and [the compact control
evidence](https://github.com/paperclipai/paperclip/blob/58c5c4aa241c85f4665a3159c192888bbb2a5cfa/doc/plans/2026-10-12-test-audit-controls.json).

## Verification

- Complete owner and retained-sibling baselines passed before each audit
cutover. Independent reviewers checked the full original tests, actual
helper bodies, exact inputs, receipt provenance, async order, fresh
allocations and case registrations.
- All 14 UI/route/wiki mutations failed at their intended assertions:
ten UI/API/ticker controls, one route control and three wiki
persistence/receipt controls. Each mutated production source was
restored to its exact original bytes.
- Both old AWS lock tests passed with their respective AWS-only lock
bypass. Both repaired tests failed at the intended assertions with the
same bypasses. These give 16 intended failures and two passing weak
controls across 18 executions. Each source was restored exactly.
- Final complete state-owner/sibling run: 329/329 passed at code
revision `64370831ebe191e5ce092f85f79b6fb661df7e16`.
- Final `pnpm -r typecheck`, `pnpm check:token-gates` and `pnpm build`:
passed at the same code revision.
- Delivery head `58c5c4aa241c85f4665a3159c192888bbb2a5cfa` includes the
evidence commit and master `4b766b872993045a2e787404f0475503683e5ded`.
All 27 audited test files and both application files match the locally
verified code revision byte-for-byte. Current-head CI validates the
integrated branch, including the incoming master changes.
- All eight production-owner SHA guards for the 16 deliberate failing
mutation controls still match the integrated delivery checkout. The
mutation receipts therefore apply to those current owners.
- Full default `pnpm test:run`: **failed** with exit 1 at frozen code
revision `64370831ebe191e5ce092f85f79b6fb661df7e16`; HEAD and tracked
source bytes remained unchanged. The first general-server group reported
878 passing files, four failing files and six skipped files; 17,846
passing cases, 24 failing cases and 105 skipped cases. Later groups did
not run. Failures were `http-log-redaction` (socket hangup),
`public-mcp` (initial 15-second timeout followed by hosted token
“Refresh before subscribing” failures), `remove-test-home` (macOS
`ENOTEMPTY` where permission errors were expected, plus cleanup
failure), and `workspace-git-snapshot-streaming` (300-second timeout).
All four failing test files and the relevant cleanup/MCP production
owners are unchanged by this audit. Full local success is not claimed.
- Focused complete-owner diagnosis at the same frozen revision: 149/152
passed, three failed, zero skipped. `public-mcp` passed 82/82 and
`http-log-redaction` passed 66/66 unchanged. The two `remove-test-home`
cases reproduced macOS `ENOTEMPTY` against an expected `EACCES`/`EPERM`
contract; the 32 MiB workspace snapshot case reproduced its existing
300,000 ms timeout. HEAD and tracked files stayed clean. Exact blob
comparison confirmed all four test files and the relevant cleanup/MCP
owners match both inspected master
`4b766b872993045a2e787404f0475503683e5ded` and the frozen local
revision. The three remaining local failures are unresolved; no timeout,
retry or assertion was weakened.
- Current-head CI: [workflow
38299262149](https://github.com/paperclipai/paperclip/actions/runs/38299262149)
has passed runner selection and created all downstream
test/typecheck/build gates; those gates are queued, with no failures
reported. Greptile completed successfully at
`58c5c4aa241c85f4665a3159c192888bbb2a5cfa` with [5/5 and zero
comments](https://github.com/paperclipai/paperclip/pull/16022#issuecomment-6131457592).
All review threads are resolved or absent. This pull request will remain
open after verification.

Reviewers can reproduce the required workspace checks:

```sh
pnpm -r typecheck
pnpm check:token-gates
pnpm test:run
pnpm build
```

Browser suites were not run. Their sources and user journeys are
unchanged by this audit.

## Risks

- A fixture helper can hide a specialized input or change object
freshness. Full-program expansion checks and independent reviews
preserve the exact old inputs and execution order.
- Removing duplicate proof can remove a distinct contract by mistake.
The audit records the keeper for each deletion; all distinct security
and lifecycle cases remain.
- Concurrency checks can miss progress during a fixed delay. The AWS
repairs record actual provider entry, confirm holder entry, use measured
entry windows, require exact final event order and drain both operations
in `finally`.
- The full local default run failed. Focused diagnosis reproduced two
macOS cleanup failures and one existing snapshot timeout in unchanged
owners; these remain unresolved. Current-head CI is pending. No test
deadline, retry policy, skip or CI shard was weakened to obtain a pass.

> 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 with GPT-6, reasoning, repository inspection, code
execution and delegated audit/review. The runtime does not expose the
exact served model identifier or context-window size; those values are
unavailable and are not inferred from the model catalog.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [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-12 22:23:11 +00:00
DottaandPaperclip f0e12c8519 Add managed Slack setup with an independent own-app path (#15679)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Slack connects a person to an agent through an installed app and a
verified identity.
> - Independent installations must remain usable without Paperclip Cloud
or manager credentials.
> - Approved Cloud deployments can use Slack manager grants to create
and install each agent's app.
> - Both paths should share credential storage, identity checks,
recovery, and the existing runtime.
> - This pull request adds gated managed setup and keeps independent app
setup under Advanced.
> - It also makes ongoing connection settings and personal test messages
easier to use.

## Linked Issues or Issue Description

**Subsystem affected**

Slack setup, connection settings, tenant credential storage, and the
Cloud connector handoff.

**Problem or motivation**

Approved deployments need an installation flow without pasted
configuration tokens. Open-source users must retain their independent
Slack app flow. A connection manager also needs a simple way to check a
bot from Settings.

**Proposed solution**

Use the managed path when the instance is configured to support it.
Choose agent and Connect are the two visible stops. Continue starts
authorization or installation. Use your own app remains under Advanced
before creation. Save personal manager grants by company, Paperclip
user, Slack user, workspace, and manager app. Reuse only verified
identities for test messages. Unknown identities must complete a private
Slack connection challenge before linking and receiving the test DM.

**Alternatives considered**

A shared bot would change each agent's Slack identity. Requiring Slack
CLI or a hosted service for self-hosted users would remove independent
installation.

**Roadmap alignment**

This maintainer-requested change improves Connected Apps and
experimental Agent Chat. It adds provisioning and management controls to
the existing Slack runtime.

**Additional context**

The companion Cloud broker profile is a separate deployment
prerequisite. Managed setup remains gated. Existing-app adoption is
deferred.

## What Changed

- Extract Slack registration, provider requests, completion,
customer-owned setup, managed setup, and personal manager grants into
focused services. Preserve the compatibility facade and independent
removal cleanup.
- Add company-scoped manager grants and a registration reference through
an additive, idempotent migration after current master. Keep runtime
credentials separate from app-registration secrets.
- Add authenticated manager authorization, sealed handoff, serialized
refresh and revocation, app creation, managed installation, and
installer-bound OAuth fallback.
- Preserve creation intent, block automatic uncertain recreation, save
credentials before downstream checks, and validate app, workspace, bot,
scopes, and installer identity.
- Add the two-stop production wizard and ordered stories. Keep the
own-app, manual, existing-app, and legacy-draft paths.
- Simplify Settings. Open Slack configuration targets the saved app. The
test-message action has its own row. Remove the old manual-avatar
section, channel-message section, and broken permissions link.
- Add paginated account discovery and a private connection challenge for
unknown accounts. Expired invitations stop waiting and offer a new
private invitation. Save send receipts before provider dispatch and
preserve confirmed delivery when activity logging fails.
- Reinstall the saved managed app on explicit reconnect, replace revoked
credentials, and resume completion. Persist reconnect intent and
duplicate-request receipts without creating another app.
- Update provider browser tests, recovery tests, secret redaction, setup
documentation, and the agent prompt. Preserve current master's durable
verification scheduling.

## Verification

- Full workspace typecheck and repository build passed on `56d389d47a`.
The subsequent reconnect retry fix in `288364eada` also passed UI
typecheck and UI build.
- 114 focused Slack registration cases and 13 production managed-wizard
cases passed, including fresh-token reconnect, retry after installation
failure, and duplicate reconnect requests.
- All nine affected Slack browser cases passed against the production UI
and a disposable fixture server. They cover managed setup, automatic
setup, own-app selection, refresh, recovery, mobile behavior, and
connection settings.
- Eight focused real-database test-message cases passed. They cover a
private ownership challenge, saved grant reuse, account pagination,
revoked and conflicting links, workspace mismatch, disabled DMs, revoked
membership, audit failure, ambiguous dispatch, and secret canaries.
- UI token gates and the production Storybook build passed.
- Greptile reviewed exact head
`288364eada5d9c114fad991ff634612639d06672` at 5/5 with no actionable
findings or unresolved review threads.
- The full repository test suite and current-head CI are running. Their
final results will replace this line before merge.
- Earlier live feature previews created two bots in the authorized
Paperclip Slack workspace and reused one personal manager grant. Local
managed Continue created and installed a bot without a configuration
token or a separate Add to Slack action. Avatars, welcome DMs, signed
callbacks, and direct replies were observed. A Settings test DM also
arrived in the signed-in user's Slack account.
- Earlier staging thread qualification required manual publication
recovery for one bot. Current-head live qualification remains required
before enabling the production default. Fixture results do not establish
Slack approval behavior in other workspaces.

## Risks

- Personal manager tokens can create apps. They stay in scoped vault
entries. The manager client secret stays at the broker. Company,
actor/session, origin, and app identity checks apply throughout setup.
- The additive migration must also work on qualification instances that
applied earlier branch migrations. Table, column, index, and constraint
creation is idempotent.
- Creation and message timeouts can leave uncertain outcomes. Saved
request IDs prevent automatic duplicate dispatch.
- Unknown Slack identities must receive and complete their private
challenge while signed in. Account selection alone does not grant
Paperclip permissions. Existing revoked or conflicting links are not
overwritten.
- A development-only manager transport uses an explicitly configured
private credential file for local qualification. Production ignores this
transport; no manager credentials are bundled with open source.
- Managed rollout requires Slack approval, the companion broker
deployment, configured secrets, and final live qualification. Removing a
connection does not delete its Slack app.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning, code execution, and browser
tools. The session 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
- [ ] 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-12 15:34:02 -05:00
4b766b8729 fix(acpx): record turn activity at the moment a stop fires (#15962)
Record stop-time ACP turn diagnostics (last-event age, oldest pending tool age, pending tool kinds) so wall-clock timeout events show whether the turn was stalled. Diagnostics only.

Co-Authored-By: Paperclip <Paperclip-Paperclip@users.noreply.github.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-12 12:33:59 -07:00
DottaandPaperclip 0d0e138dad refactor(chat): extract channel status, receipts, drafts and authority workflows (#15995)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Chat channels connect that work to provider conversations.
> - Provider workflows still fill the shared service.
> - Their authority and recovery rules need clear homes.
> - This PR lands five reviewed extractions into channel folders.
> - Shared authorization, state and worker lifetime stay explicit.

## Linked Issues or Issue Description

Refs #15976, #15996, #15999, #16001 and #16002. This root PR now
contains all five extractions from batch three. The four upper PRs were
merged into their parents. The combined tree matches reviewed stack head
`789e918b5af49ec1958076ce4d22d31006205302` exactly.

## What Changed

- Extract Slack session status and accepted-file receipt recovery.
- Extract Telegram publication drafts and Stop handling.
- Extract Photon events and native responses.
- Extract Teams file-transfer authority.
- Reduce `chat-channels.ts` from 29,897 to 26,461 lines (3,436 lines,
11.5%).
- Preserve function bodies, runtime-map identity, leases and transaction
boundaries.

## Verification

- All 117 selected public-service cases passed before and after their
respective extractions.
- All 30 moved declarations and remaining service statements preserve
normalized syntax across the five slices.
- Server typecheck, module boundaries and whitespace checks passed.
- Cumulative `pnpm -r typecheck` and `pnpm build` passed. Combined-head
CI and review are pending on `5de1b0b99d71049a72440e8c131bf4bb30bdf3f3`.
- Fixtures exercise assembled services and disposable databases, not
live third-party providers.
- Cumulative local `pnpm test:run` on
`789e918b5af49ec1958076ce4d22d31006205302` exited 1: 32,414 passed, 125
skipped, one failure. `company-skills-routes.test.ts:1691` (`stars,
forks, and comments on skills through company-scoped endpoints`) failed
with `socket hang up`; that test and its route are unchanged by this
stack. The same 58-case suite then passed in isolation without code
changes. The original command did not reach 111 later serialized suites;
its failure is retained, so the full-local-test checklist remains
unchecked.

```sh
pnpm exec vitest run server/src/__tests__/chat-channels.integration.test.ts -t 'Slack session|Slack turns to Idle|signed Slack session stop|denies guest Slack stops'
```

- Combined-head CI attempt 1: five runners received a shutdown signal at
19:13:08 UTC (server shard 10/12, serialized shards 2/9 and 7/9, E2E
shards 3/8 and 7/8). These jobs stopped during execution without a
failing test assertion. [Original
workflow](https://github.com/paperclipai/paperclip/actions/runs/38293566028).
The affected jobs will receive one unchanged retry after the other jobs
finish.

## Risks

- Changes cross durable provider recovery and authority boundaries. The
moved bodies preserve syntax, and 117 focused public-service cases
passed before and after their extractions.
- Shared workers, timers, runtime maps and generic authorization remain
in the service.
- The full local test run failed in an unchanged company-skills route
test; its result and the unrun suites are recorded above.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning and repository tools. The
exact runtime model ID and context-window size are not exposed in this
session. No separate agent was used.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-12 19:26:54 +00:00
7df517d18f perf(build): reuse a current Runner build in the server's prepare:runner-vendor step (#15926)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Its CI runs a `Build` job on every pull request. That job runs `pnpm
build` and then verifies the public npm install. Since the server test
shards got faster, `Build` is the slowest check in most runs (419-497s
on the six fully green `master` runs created 2026-10-11 19:46Z to
20:17Z).
> - Inside `pnpm build`, the server package is the last package to
build. Its first step, `prepare:runner-vendor`, runs the full build of
`@paperclipai/paperclip-runner` so the server can vendor
`packages/paperclip-runner/dist`. In `pnpm -r build` that package was
built seconds earlier by the same command. The second build took 12-16s
on those six runs and sits on the critical path of the job.
> - The step cannot just be removed. A standalone `pnpm --filter
@paperclipai/server build` (the Dockerfile's path) must produce a
current vendored Runner on its own.
> - This pull request keeps that guarantee and skips only the build that
is provably redundant. The Runner's `build` records a digest of its
inputs, captured before its first step and confirmed after its last,
together with a manifest of every file it left under `dist`. The
server's `prepare:runner-vendor` reuses the build when the digest
matches, the stamp is for this platform, and every recorded output is
still there at its recorded size. Any other state runs the same full
Runner build as before.
> - The benefit is 12-16s off the `Build` job and off every other `pnpm
build` (the Canary Dry Run's `release.sh` build, local builds), with no
change to what a standalone server build produces. The PR's own first
run measured it: the server step reused the build in 56ms where `master`
runs rebuilt for 11-15s.

## Linked Issues or Issue Description

No GitHub issue exists for this. The description below follows the
enhancement template.

**What existing behavior does this improve?**

The server build's `prepare:runner-vendor` step, which builds
`@paperclipai/paperclip-runner` so the server can vendor its `dist`.

**Subsystem affected**

Build and CI (`server/package.json`,
`packages/paperclip-runner/package.json`, the `Build` job).

**Current behavior**

`prepare:runner-vendor` runs the Runner's full `build` every time.
Inside `pnpm -r build`, the Runner was already built by the same command
20 to 25 seconds earlier, with every check passing. On six fully green
`master` runs (2026-10-11, 19:46Z to 20:17Z) the repeated build took
11.8s to 15.9s, measured from the `server build$ pnpm run
prepare:runner-vendor` log line to the Runner's `Finished release
profile` line. The server package builds last, so that time is on the
critical path of `Build`, which is the slowest check in most pull
request runs.

**Proposed behavior**

- The Runner's `build` starts with `node scripts/record-build-inputs.mjs
--begin` and ends with `node scripts/record-build-inputs.mjs --finish`.
`--begin` removes the previous stamp and records a digest of every build
input before the first step reads anything. `--finish`, after every step
and check has passed, recomputes the digest and writes
`packages/paperclip-runner/node_modules/.cache/paperclip-runner-build-inputs.json`
only if the inputs are still the ones the build began with, together
with the path and size of every file under `dist`. A stamp therefore
exists only for a build that completed every step on inputs that did not
move while it ran.
- The server's `prepare:runner-vendor` becomes `node
scripts/prepare-runner-vendor.mjs`. It reuses the build if the stamp is
for this platform and architecture, every recorded output is still there
at its recorded size, and the recomputed digest matches. In every other
case it removes the stamp and runs `pnpm --filter
@paperclipai/paperclip-runner build`, the exact command the step ran
before.
- `PAPERCLIP_RUNNER_VENDOR_REBUILD=1` forces the rebuild.

**Reason and benefit**

12-16s off the `Build` job and off every other `pnpm build` (the Canary
Dry Run's `release.sh` build, local builds), with no change to what a
standalone `pnpm --filter @paperclipai/server build` produces.

**Breaking changes**

None. The rebuild path runs the same command as before. A fresh
checkout, a changed input, a missing or truncated output, an edit that
landed during the build, or a stamp from another platform always
rebuilds.

## What Changed

- `packages/paperclip-runner/scripts/build-inputs.mjs` (new): defines
the input set, the digest, the output manifest and the reuse decision.
Inputs are every file under `packages/paperclip-runner` except `dist`,
`dist-browser`, `runner/target`, `node_modules` and dot-directories;
every file under `packages/paperclip-eval-kernel` except `dist` and
`node_modules` (the Runner's `build:typescript` builds it first); every
file under the root `patches` directory (`check:acpx-profiles` hashes
`acpx@0.13.1.patch`, and `build-verified-provider-entrypoints.mjs`
copies the `pi-acp` and `brace-expansion` patches into
`dist/cli/pi-provision-inputs`); and the root `package.json`,
`pnpm-lock.yaml`, `pnpm-workspace.yaml` and `tsconfig.base.json`. The
toolchain pins are inside the package (`rust-toolchain.toml`,
`runner/Cargo.lock`) and the TypeScript version is in the lockfile, so
both are covered. A symlink is hashed as its target path and is not
followed. Outputs are every file under `dist`, the tree the server
copies wholesale, recorded with their sizes. Exports `beginRunnerBuild`,
`recordRunnerBuild` and `assessRunnerBuild` (returns `current` plus a
stable reason code: `no_stamp`, `schema_mismatch`, `platform_mismatch`,
`missing_output`, `outputs_changed`, `inputs_changed`,
`inputs_unchanged`).
- `packages/paperclip-runner/scripts/record-build-inputs.mjs` (new): the
two phases, `--begin` and `--finish`, added as the first and last step
of the Runner's `build` in `packages/paperclip-runner/package.json`.
Each build chain has its own pending record under
`node_modules/.cache/paperclip-runner-build-inputs.pending/`, named
after the shell pnpm runs the chain in (the parent of both phases;
`--finish` also accepts a record named after its own pid, for a shell
that execs the last command of the chain), so two Runner builds running
at once in one checkout do not consume each other's record. `--finish`
without a pending record for its chain is an error, so the chain cannot
silently lose its first step; `--finish` after an edit that landed
mid-build prints `not recorded: inputs changed while the build ran` and
leaves no stamp. Records of builds that never finished are pruned by the
next `--begin` after a day.
- `server/scripts/prepare-runner-vendor.mjs` (new): the reuse-or-rebuild
step. It resolves the pnpm that is running it through `npm_execpath`
with the same rule as the CLI's `resolvePnpmInstallInvocation` (a
JavaScript entry runs through this Node, a native executable runs
directly, otherwise `pnpm` on PATH). It prints one
`paperclip.runner-vendor.timing.v1` line with `action`, `reason`,
`check_ms` and `build_ms`, so the saving is measured on this PR's own
`Build` job.
- `server/package.json`: `prepare:runner-vendor` now runs that script.
The `build` and `typecheck` scripts are unchanged.
- `server/src/__tests__/server-package-build-script.test.ts`: pins the
new indirection and the command behind it (imported from the script, so
the Dockerfile's standalone path stays pinned), and a new test asserts
the Runner's `build` starts with `--begin`, ends with `--finish`, and
has exactly those two record steps.
- Tests: `packages/paperclip-runner/scripts/build-inputs.test.mjs`
(node:test, added to `test:typescript:prep`) and
`server/scripts/prepare-runner-vendor.test.mjs` (vitest, in the server
lane).
- `doc/DEVELOPING.md`: a "Reused Runner build in the server build"
section under Test Commands.

## Review findings on the first head, all fixed in
[2d57542](https://github.com/paperclipai/paperclip/pull/15926/commits/2d575425977f87b4612222d0a1961bc9cc5ba569)

Greptile scored the first head 2/5 with three P1 findings. All three
were valid.

| Finding | Fix |
| --- | --- |
| **Patch edits leave stale output.** The root patch files the build
reads were not in the digest. | Every file under the root `patches`
directory is an input. Tests assert the patches are in the input list
and that editing the pi-acp patch, the acpx patch, or adding a patch
changes the digest. |
| **Missing helpers pass the build.** Only four outputs were checked, so
a `dist` that lost `dist/cli/acpx-runtime-sidecar.cjs` still counted as
current. | `--finish` records the path and size of every file under
`dist`; reuse requires all of them intact and reports `outputs_changed`
naming the first missing or resized file. Schema bumped to
`paperclip.runner-build-inputs.v2` so an older stamp is rejected. Tests
cover a deleted helper, a truncated helper, and the server step
rebuilding on `outputs_changed`. |
| **New edits count as compiled.** The digest was hashed after the
build, so a source edited while `build:binary` ran was recorded as
compiled. | The build is bracketed: `--begin` captures the digest before
the first step, `--finish` compares after the last and writes nothing if
it moved. Tests cover the mid-build edit, the undisturbed build, and the
CLI in both orders. |

## Review finding on the second head, fixed in the third commit

Greptile scored the second head 4/5 with one P1, valid: two Runner
builds running at once in one checkout (server `build` beside
`typecheck`) shared one pending record, so the second `--begin`
overwrote it, the first `--finish` consumed it, and the second
`--finish` threw `no pending build inputs` after every step of its build
had passed. Reproduced on the second head's module: two
`beginRunnerBuild`, then the first `recordRunnerBuild` records and the
second throws.

| Finding | Fix |
| --- | --- |
| **Overlapping builds fail each other.** One pending file for every
build chain. | One record per chain, named after the shell pnpm runs the
chain in: `--begin` sees it as its parent, and so does `--finish`,
unless the shell execs the last command of the chain (bash can), in
which case `--finish` is that shell and finds the record under its own
pid. Pending records of builds that never finished are pruned after a
day. An edit between two overlapping starts leaves no stamp whichever
build finishes last: the build that began before the edit reports
`inputs_changed_during_build` and removes the stamp the other wrote, so
a stamp always means every build that wrote `dist` read the inputs it
names. Tests: both finishing orders with and without an edit, the
pruning, a `--finish` that cannot use another chain's record, and two
real `sh -c` chains (plus one under `bash`) held open at the same time,
both recording. On the real repo, two `pnpm --filter
@paperclipai/paperclip-runner build` chains started 8s apart with a
cargo stand-in both recorded (`files=1223 outputs=1434`) and left the
pending directory empty. |

## Measured on this PR's runs

The `Build` job ran `pnpm build` from the branch, so both builds of the
Runner in that job used the new chain.

First head (c23faab, run 38175281058), against the four fully green
`master` runs created 20:41Z to 21:37Z the same evening:

| | PR run 38175281058 | `master`, four runs |
| --- | --- | --- |
| Runner build inside `pnpm -r build` | 55.3s | 47.3-62.0s |
| Server step `prepare:runner-vendor` |
`action:reuse,reason:inputs_unchanged,check_ms:56,build_ms:0` | full
Runner rebuild, 11.0-14.8s |
| Server package build | 29.7s | 32.3-43.5s |
| `pnpm build` step | 114s | 102-135s |
| `Build` job | 411s | 409-474s |

Second head (2d57542, run 38178686999), against the two fully green
`master` runs created in the same half hour (38178951607 and
38180574768):

| | PR run 38178686999 | `master`, two runs |
| --- | --- | --- |
| Runner build inside `pnpm -r build` | 53.9s (`begin:` 22:03:04,
`recorded:` 22:03:57, digest 460e02616081 over 1231 files and 1434
outputs) | 62.2s, 64.6s |
| Server step `prepare:runner-vendor` |
`action:reuse,reason:inputs_unchanged,check_ms:109,build_ms:0` | full
Runner rebuild, 14.5s, 15.2s |
| Server package build | 27.4s | 43.5s, 44.8s |
| `pnpm build` step | 106s | 91s, 95s to the Runner's end plus the rest;
127s, 135s in all |
| `Build` job | 405s | 461s, 479s |

The rebuild is gone from the server build on both heads, and the check
costs 56-109ms where `master` rebuilds for 11-15s; the rest of the
spread between runs is runner speed on the `runs-on` fleet. The third
commit changes nothing on the reuse path: it only changes where the
pending record lives between `--begin` and `--finish`.

## Verification

- `node --test packages/paperclip-runner/scripts/build-inputs.test.mjs`:
11 tests, three consecutive runs clean. Two builds at once in one
checkout each keep their own pending record and both record, in either
finishing order; with an edit between their starts the build that began
before it leaves no stamp whichever finishes last. A build's start
prunes records older than a day and no others. A `--finish` cannot use
another chain's record. Two chains run by separate `sh -c` shells at the
same time (and one under `bash`) both record and leave the pending
directory empty. The input set is exactly the sources, the eval kernel,
the root patches and the root config, and skips outputs and caches. The
output manifest lists every file under `dist` with its size and nothing
outside it. The digest is stable across reads, does not change when
`dist`, `dist-browser`, `node_modules`, `.vite` or `runner/target`
change, and does change for a Runner source, a Rust source, an
eval-kernel source, the pi-acp patch, the acpx patch, a new patch, the
lockfile, the base tsconfig, a new file and a rename. A symlink changes
the digest but its target's files are not read. A build is current only
with a matching v2 stamp, the same platform and architecture, the four
named outputs present, and every recorded output intact (a deleted or
truncated helper is `outputs_changed`; a file the build did not produce
may appear). An input edited between `--begin` and `--finish` leaves no
stamp and removes the pending record; the next build records normally.
`--finish` without `--begin`, or after a build that left a required
output missing, throws and leaves no stamp. The CLI brackets the build
for the package it is pointed at, reports a mid-build edit, and rejects
`--finish` first, both flags, neither flag, and unknown arguments.
- `vitest run scripts/prepare-runner-vendor.test.mjs
src/__tests__/server-package-build-script.test.ts` in `server/`: 21
tests. The decision reuses a current build, rebuilds a stale one, and
rebuilds a current one only when `PAPERCLIP_RUNNER_VENDOR_REBUILD=1`.
The pnpm resolver covers a JavaScript entry, a native executable, a
Windows executable, no `npm_execpath`, and npm's `npm_execpath`. With an
injected build command: a current stamp runs nothing and prints
`action:reuse`; changed inputs run the exact command from `server/` with
the stale stamp already removed; a lost
`dist/cli/acpx-runtime-sidecar.cjs` rebuilds with
`reason:outputs_changed`; a failing build returns its status and leaves
no stamp. End to end, the CLI with a stand-in `pnpm` on PATH that
brackets its build the way the real one does rebuilds on the first call,
reuses on the second, rebuilds after a source change, rebuilds after a
lost output, and reuses again. The guard test asserts
`prepare:runner-vendor` is the new script, the rebuild command is still
`pnpm --filter @paperclipai/paperclip-runner build`, the server build
still starts with the step, and the Runner's `build` starts with
`--begin` and ends with `--finish` with exactly two record steps; it
fails against the previous build script on its first assertion.
- Real repo, this checkout, with `cargo` and `pnpm` stand-ins on PATH
(no Rust toolchain here; the stand-in writes the one file
`stage-runner-binary.mjs` copies): `pnpm --filter
@paperclipai/paperclip-runner build` ran the complete chain through the
real nested pnpm (every TypeScript step and check, the stand-in cargo,
the binary staging, the two binary-reading checks) and recorded
`files=1223 bytes=18735059 outputs=1434`; the stamp is 128 KB. `pnpm run
prepare:runner-vendor` in `server/` then printed
`action:reuse,reason:inputs_unchanged,check_ms:222,build_ms:0`.
Appending a newline to `patches/pi-acp@0.0.33.patch` made the assessment
`inputs_changed`; truncating or deleting
`dist/cli/acpx-runtime-sidecar.cjs` or
`dist/cli/opencode-app-server-proxy.cjs` made it `outputs_changed` in
1-5ms; restoring each made it current again. `--begin`, an edit to
`src/index.ts`, then `--finish` printed `not recorded: inputs changed
while the build ran` and left no stamp.
- The whole check on the real set (1223 inputs, 18 MB; 1434 outputs)
takes 165-227ms on this host and took 56ms on the CI runner; the output
manifest part is 1-5ms.
- Not run here: the real `cargo build`, and `pnpm build` end to end.
This PR's `Build` job runs `pnpm build` from the branch, so its log
carries the Runner's `begin:` and `recorded:` lines and the server
build's `paperclip.runner-vendor.timing.v1` line with the measured
result.

## Risks

- A build input outside the digest would let a stale Runner be vendored.
The digest covers the whole Runner package except its outputs, the eval
kernel the Runner builds first, the root patches, and the root files the
Runner's tsconfig and dependency graph read. The lockfile covers
dependency versions, and the pins inside the package cover the Rust
toolchain. An input missed here is a bug to fix by adding it to the set;
the fallback direction is always a rebuild, never a skip.
- The whole `patches` directory is an input, although the Runner build
reads three of its fourteen files. An edit to an unrelated patch
therefore costs one extra Runner rebuild, which is the safe direction
and avoids a hand-kept list of file names that could drift from the
scripts that read them.
- The stamp lives in `node_modules/.cache`, which pnpm does not manage
and `.gitignore` and `.dockerignore` already exclude. It is about 128 KB
with the output manifest. The Docker build has no stamp and no prior
Runner build, so it rebuilds exactly as before.
- A stamp is written only after the Runner's `build` passed every step
on inputs that did not change while it ran. The Runner's first step and
the prepare step both remove any existing stamp before a rebuild starts,
so a build that stops partway, or a build command that fails before its
first step, leaves no stamp.
- `node scripts/record-build-inputs.mjs --finish` on its own is an
error; the script is only meant to run as the two ends of the `build`
chain, and the guard test pins that.
- The pending record is keyed by the pid of the shell that runs the
chain. pnpm runs every script through a shell (`sh -c` on POSIX, `cmd`
on Windows), and both phases are children of that shell; a shell that
execs the last command of the chain is covered by matching the finishing
process's own pid too. A chain run some other way, with the two phases
under different parents, fails loudly at `--finish` rather than
recording. Two builds running at once still write the same `dist`, which
no stamp can make safe; what the record guarantees is that a stamp is
written only by a build whose own inputs did not move.
- The platform and architecture are part of the comparison, so a stamp
from another machine is never trusted; an older v1 stamp is rejected by
schema.
- The Typecheck job builds the Runner once, through this step, with no
prior `pnpm -r build`, so it gains nothing and loses nothing: there is
no stamp and it rebuilds as before.
- The step now spawns pnpm through `npm_execpath` instead of relying on
`pnpm` being on PATH, because under corepack the script shell has no
`pnpm`. Outside a pnpm script it still falls back to `pnpm` on PATH.

## Model Used

- Claude (Anthropic), model id `claude-fable-5-1` (Claude Fable 5.1),
extended thinking, with tool use (shell, file editing, GitHub CLI) in a
Paperclip agent 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

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

---------

Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 11:25:24 -07:00
0ea8ebd040 fix(test): make the vitest home teardown remove read-only runtime-context bundles (#15931)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Its CI runs the server test suite as twelve `General tests (server
(n/12))` shards on every pull request. Each shard worker gets its own
throwaway `PAPERCLIP_HOME` from
`server/src/__tests__/setup-supertest.ts`, which removes it with a
recursive `rmSync` at worker exit.
> - That removal has been failing now and then with `EACCES, Permission
denied: /tmp/.../paperclip-vitest-home-...` after every test file in the
shard passed (four shard jobs on 2026-10-11 between 14:10Z and 22:10Z,
on four different branches, each attributed to
`native-session-resumption.test.ts` because that file was running in the
worker at exit). Vitest reports it as an unhandled error and fails the
shard, so the job has to be re-run, which costs a full shard (four to
six minutes) on the run's critical path.
> - The directory it cannot remove is one a native session leaves
behind: runtime-context bundles are published under the instance root
(`$PAPERCLIP_HOME/instances/<id>/runtime-context-assets/bundles/<digest>`)
and every directory in them is made read-only by design
(`makeDirectoriesReadOnly` in
`services/native-runtime/runtime-context.ts`). A file inside a read-only
directory cannot be unlinked, so the recursive removal stops with
`EACCES`. Whether such a bundle is under the worker's home at exit
depends on timing: suites set and restore `PAPERCLIP_HOME` for their own
fixtures, and a heartbeat that is still finishing when the worker exits
can publish its bundle under whichever home is current.
> - The teardown is the right place to fix this: the bundle is read-only
on purpose, and the test home is throwaway. On a permission error the
teardown now makes every directory in the tree writable and removes it
again.

## Linked Issues or Issue Description

No GitHub issue exists for this. The description below follows the bug
report template.

**What happened?**

A `General tests (server (n/12))` shard fails after all its test files
passed:

```
Vitest caught 1 unhandled error during the test run.
Error: EACCES, Permission denied: /tmp/pv-k2snZS/t/paperclip-vitest-home-qANo1F
 ❯ Object.rmSync node:fs:1282:18
 ❯ process.<anonymous> server/src/__tests__/setup-supertest.ts:32:31
This error originated in "server/src/__tests__/native-session-resumption.test.ts" test file.
 Test Files  74 passed (74)
      Tests  3226 passed | 1 skipped (3227)
     Errors  1 error
```

Seen on 2026-10-11 in the first attempt of run 38146108750 (server
(11/12)), run 38150787810 (server (11/12)), run 38178832013 (server
(12/12), branch `fix/windows-opencode-utf8`) and run 38179110043 (server
(11/12), branch `fix/sentry-timeout-assertion-event-id`). The re-run of
the same job passes every time.

**Expected behavior**

A shard whose tests all passed is green. The per-worker test home is
removed at exit whatever a finishing run left in it.

**Steps to reproduce**

1. Create a directory tree with a read-only directory inside it, the way
`makeDirectoriesReadOnly` publishes a runtime-context bundle: `mkdir -p
home/bundle && touch home/bundle/SKILL.md && chmod 555 home/bundle`.
2. Remove it the way the setup file did: `node -e
"require('fs').rmSync('home', { recursive: true, force: true })"`.
3. The removal throws `EACCES` and leaves the tree behind. The new
`removeTestHome` removes it.

**Paperclip version or commit**

`master` at bfe7c6357.

**Deployment mode**

CI (`pr-trusted.yml`, the general server shards on the `runs-on` fleet);
the same removal runs on every local `pnpm test` in `server/`.

## What Changed

- `server/src/__tests__/remove-test-home.ts` (new):
`removeTestHome(home)` runs the same recursive `rmSync` as before
(`force`, eight retries); on `EACCES` or `EPERM` it walks the tree, adds
owner read, write and search to every directory (`lstat`, so a symlink
is not followed and nothing outside the home is touched), and removes
the tree again. Any other error, and a second failure, still throw.
- `server/src/__tests__/setup-supertest.ts`: the exit handler calls
`removeTestHome` instead of `rmSync` directly.
- `server/src/__tests__/remove-test-home.test.ts` (new): a home with a
runtime-context bundle whose directories and files are read-only fails
the plain `rmSync` with `EACCES` (asserted, so the test documents the
failure it fixes) and is removed by `removeTestHome`; an ordinary home
is removed and a missing one is tolerated; a symlink out of the home is
not followed, so a read-only directory it points at keeps its mode and
its file.

### Review fix (second commit)

- Greptile (P1): the symlink test ran on Windows without a guard, where
`chmod` only toggles the read-only attribute (so the `0o555` assertion
cannot hold) and a directory symlink can need elevated privileges. The
test is now `it.skipIf(process.platform === "win32")`, the guard the
suite's other permission tests use; the first test already guarded its
`rmSync` assertion the same way.

## Verification

- `vitest run src/__tests__/remove-test-home.test.ts` in `server/`: 3
tests pass (three consecutive runs on the second commit). The first one
fails against a `removeTestHome` that only calls `rmSync` (it throws
`EACCES` on the read-only bundle). There is no Windows job in this
repo's CI, so the skip is verified by reading `os.chmod` semantics on
Windows plus the guard pattern already in the suite.
- `vitest run src/__tests__/env-file-policy.test.ts` in `server/`, which
loads the changed setup file: passes, and the worker exits clean.
`native-session-resumption.test.ts` itself could not be loaded in the
workspace this was written in (the AI SDK's default OpenAI provider
rejects that sandbox's empty base URL at import time, before any test
runs), so the server shards on this PR's own run are the check for that
file.
- The three files typecheck under the server's `tsconfig.json` options
(`tsc --noEmit` on a config that includes only them, since the server
project excludes `src/__tests__`).
- The CI failure itself is timing-dependent and was not reproduced on
demand; the fix is to the teardown, which now removes the tree the
failing runs left behind, as the new test shows.
- First commit's CI run (38183149920): every check green on the first
attempt, including all twelve server shards.

## Risks

- The teardown makes directories writable only under the throwaway test
home, only after a permission error, and only through `lstat` (symlinks
are not followed), so it cannot reach into a developer's real
`~/.paperclip` or any directory the home links to.
- A home the teardown still cannot remove (an entry owned by another
user, for instance) throws as before, so a real permission problem stays
visible.
- The underlying timing, a run still publishing a bundle while the
worker exits, is unchanged; this PR makes the teardown correct for what
such a run leaves behind rather than changing how runs are drained,
which is a larger change to the test harness.

## Model Used

- Claude (Anthropic), model id `claude-fable-5-1` (Claude Fable 5.1),
extended thinking, with tool use (shell, file editing, GitHub CLI) in a
Paperclip agent 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
- [ ] 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 Fable 5.1 <noreply@anthropic.com>
2026-10-12 11:25:16 -07:00
a96329dfa2 fix(test): stop the Sentry timeout assertion matching a random event id (#15929)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - Every pull request runs the `Sentry SDK contract` workflow, whose
one job installs the real `@sentry/node` outside the workspace and runs
`server/src/__tests__/run-failure-sentry-real-sdk.test.ts` to prove that
run-failure reports never carry provider output or credentials.
> - One of its assertions checks that the captured events do not contain
the provider's stderr canary `<html>504 Gateway Time-out
private-timeout-credential-canary</html>`, by matching the serialized
events against `/private-timeout-|<html>|504|Timed out after/`.
> - The bare `504` alternative also matches a Sentry event id. On pull
request #15926 (a build-script change that does not touch this code) the
job failed because that run's event id was
`f353d184e5504ee39db611f2e60f49ff`; the re-run passed. About 0.7% of
random 32-character hex ids contain `504`, and the test serializes two
events, so roughly one job in seventy fails for no reason.
> - This pull request narrows that alternative to `504 Gateway`, which
still catches the canary with or without its HTML wrapper and cannot
match a hex id.

## Linked Issues or Issue Description

No GitHub issue exists for this. The description below follows the bug
report template.

**Pre-submission checklist**

- [x] I have searched existing open and closed issues and this is not a
duplicate.
- [x] I am on the latest released version of Paperclip (or can reproduce
on `master`).
- [x] I have confirmed the error originates in Paperclip itself — not in
my agent adapter, API provider, or local configuration.

**What happened?**

The `Sentry SDK contract` job (`Real Sentry SDK isolation`) failed on
pull request #15926 at head 2d57542, run 38178686828 attempt 1, in
`run-failure-sentry-real-sdk.test.ts > captures an early OpenCode
timeout without inventing a deadline or exporting provider output`:

```
AssertionError: expected '[{"exception":{"values":[{"type":"Err…' not to match /private-timeout-|<html>|504|Timed out after/
```

The serialized events contained no provider output. The match was the
event id `f353d184e5504ee39db611f2e60f49ff`, which contains `504`. The
pull request changes build scripts only, and the same job passed on its
previous head and on the re-run of this one.

**Expected behavior**

The assertion fails only when a captured event carries the stderr canary
or a `Timed out after` message, never because of the random event id
Sentry assigns.

**Steps to reproduce**

1. On `master`, run `node -e
'console.log(/private-timeout-|<html>|504|Timed out
after/.test(JSON.stringify([{event_id:"f353d184e5504ee39db611f2e60f49ff"}])))'`.
It prints `true`.
2. Run the `Sentry SDK contract` job repeatedly; about one run in
seventy draws an event id containing `504` and fails on that assertion.

**Paperclip version or commit**

`master` at bfe7c63.

**Deployment mode**

Other (CI, GitHub Actions)

**Installation method**

Built from source (pnpm dev / pnpm build)

**Agent adapter(s) involved**

- [x] Not adapter-specific (core bug)

**Database mode**

Not database-related

**Access context**

Unclear / not applicable

**Node.js version**

24 (the workflow's `actions/setup-node` version)

**Operating system**

ubuntu-latest (GitHub-hosted runner)

**Relevant logs or output**

```
 ❯ src/__tests__/run-failure-sentry-real-sdk.test.ts (11 tests | 1 failed) 2571ms
   × captures an early OpenCode timeout without inventing a deadline or exporting provider output 510ms
AssertionError: expected '[{"exception":{"values":[{"type":"Err…' not to match /private-timeout-|<html>|504|Timed out after/
"[{\"exception\":{\"values\":[{\"type\":\"Error\",\"value\":\"OpenCode execution timed out\",\"mechanism\":{\"type\":\"generic\",\"handled\":true}}]},\"event_id\":\"f353d184e5504ee39db611f2e60f49ff\", ...
```

**Relevant config (if applicable)**

None.

**Additional context**

The assertion was added in #15789. The fixture's stderr is `<html>504
Gateway Time-out private-timeout-credential-canary</html>`, so `<html>`
and `private-timeout-` already cover it; `504 Gateway` keeps a third,
independent check on the status text itself.

**Privacy checklist**

- [x] I have reviewed all pasted output for PII (usernames, file paths,
API keys, tokens, company names) and redacted where necessary.

## What Changed

- `server/src/__tests__/run-failure-sentry-real-sdk.test.ts`: the
forbidden-substring assertion matches `504 Gateway` instead of `504`.
One line.

## Verification

- With the real SDK installed outside the workspace the way the workflow
does it (`npm install --prefix <tmp> @sentry/node@10.71.0`, `NODE_PATH`
pointing at it, `PAPERCLIP_REQUIRE_SENTRY_TEST_SDK=1`), `pnpm --filter
@paperclipai/server exec vitest run
src/__tests__/run-failure-sentry-real-sdk.test.ts` passes 11/11 on three
consecutive runs.
- The failing run's payload against both patterns: the old regex matches
it (`true`), the new one does not (`false`). The stderr canary with its
HTML wrapper and the bare `504 Gateway Time-out` both still match the
new regex.
- 100,000 random 32-character hex ids: 0.73% contain `504`; none can
contain `504 Gateway`.

## Risks

- The assertion no longer catches a leaked `504` that appears without
`Gateway` after it. The fixture's canary always carries `Gateway`, and
the `<html>` and `private-timeout-` alternatives cover the same leak
independently, so the test's purpose is unchanged.

## Model Used

- Claude (Anthropic), model id `claude-fable-5-1` (Claude Fable 5.1),
extended thinking, with tool use (shell, file editing, GitHub CLI) in a
Paperclip agent 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

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

Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Fable 5.1 <noreply@anthropic.com>
2026-10-12 11:25:09 -07:00
Devin Foley 4f399cec9f fix(docker): skip the full-tree ownership walk on routine boots (#15992)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - The Docker image keeps all instance state under `PAPERCLIP_HOME`,
and deployments mount a volume there
> - Before it drops privileges, the entrypoint makes sure that the
`node` user owns the home (#10530)
> - The ownership probe is a first-mismatch `find` over the whole tree.
When ownership is correct, `-quit` never fires, so every boot stats
every file on the volume
> - On homes with many agent worktrees and `node_modules` trees, this
walk takes minutes before Node starts. That is close to common 300s
healthcheck windows
> - This pull request keeps a cheap shallow probe on every boot. It runs
the full-tree probe only once per volume and node UID/GID, and records
the result in a marker file
> - The benefit is that routine boots no longer scale with the file
count, and the original root-owned-volume fix stays in place

## Linked Issues or Issue Description

Refs #10431 (the open follow-up about the remaining cost of the
ownership walk). Related: #10530 (the change that added the full-tree
probe).

**What happened?**
On a hosted multi-instance deployment, we measured the time from
container start to the first app log line. Instances with many files on
the volume (agent worktrees with `node_modules`) took about 190-235s.
Small instances took 5-22s. One 30 GB volume with few files took 24s, so
the cost follows the file count, not the volume size. Almost all of this
time is the `find "$home_dir" \( ! -user node -o ! -group node \) -print
-quit` walk in `scripts/docker-entrypoint.sh`. When ownership is already
correct, `-quit` never fires, so the walk stats every inode on every
boot.

**Expected behavior**
When ownership is already correct, the entrypoint pre-start phase is
short and does not depend on the number of files in `PAPERCLIP_HOME`. A
fresh root-owned volume, a UID/GID remap, and an upgrade from an older
image are still repaired before the privilege drop.

**Steps to reproduce**
1. Run the image with a volume on `/paperclip` that holds a few hundred
thousand files, all owned by `node:node`.
2. Restart the container.
3. Compare the container start time with the first server log line. The
gap grows with the file count, even though no `chown` runs.

**Paperclip version or commit**
`master` at a9830414c7

**Deployment mode**
Docker production image (any platform that mounts a volume on
`PAPERCLIP_HOME`)

## What Changed

- `scripts/docker-entrypoint.sh`: the ownership probe is now tiered.
- Every boot runs a shallow probe (`find -maxdepth 3`) over the home and
its first three levels. A fresh root-owned mount and a UID/GID remap
both change the owner of the home directory, so this probe catches them.
- The full-tree probe runs only when
`PAPERCLIP_HOME/.paperclip-ownership-verified` is missing or records a
different node UID/GID (format `v1 <uid>:<gid>`). This catches deep
mismatches, including the first boot after an upgrade from an older
image.
- Any mismatch repairs the whole tree with `chown -R node:node`, as
before. After a clean probe or a repair, the entrypoint writes the
marker as the `node` user (through `gosu`), so the marker never trips
the shallow probe. A failed marker write is ignored: the only cost is a
full probe on the next boot. If `find` exits non-zero without a mismatch
(for example, an I/O error), the walk is incomplete, so the entrypoint
does not write the marker and the next boot probes again.
- `scripts/docker-entrypoint.sh`: timestamped log lines at entrypoint
start, around the full probe and the repair (with durations), and right
before the `exec` of the server. You can now measure the pre-start phase
from the container log.
- `server/src/__tests__/docker-entrypoint.test.ts`: the `find` stub now
tells the shallow probe from the full probe. New tests cover each path
(see Verification).
- `doc/DOCKER.md`: documents the marker, how to force a full check, and
the new log lines.

## Verification

- `cd server && npx vitest run src/__tests__/docker-entrypoint.test.ts
src/__tests__/container-init-reaping.test.ts
src/__tests__/docker-build-stamp.test.ts`: 40 passed. New cases:
- correct ownership + current marker → shallow probe only, no full walk,
no `chown`
- current marker + deep-only mismatch → full walk skipped (this is the
documented trade-off)
- no marker (upgrade) → one full walk, no `chown`, marker written as `v1
1000:1000`
- no marker + deep mismatch → full walk finds it, `chown -R`, marker
written
- root-owned top level with a current marker → `chown -R` without a full
walk
- full walk fails without a mismatch → no `chown`, no marker, retry
logged
- marker for another UID/GID → full walk runs again, marker rewritten as
`v1 1001:1001`
- timestamped `starting`, `ownership repair finished in Ns`, and
`starting app as node` lines, in order, before the command runs
- The new tests fail against the old script. The 11 existing tests pass
unchanged.
- `cd server && npx tsc --noEmit`: passes.
- `shellcheck -s sh scripts/docker-entrypoint.sh`: clean. `dash -n`:
syntax OK.
- Manual run with the real `find` (BSD find, script run under `dash`)
against a 200k-file tree. Only `id`/`usermod`/`groupmod`/`chown`/`gosu`
were stubbed, and `node` was mapped to the local user and group:
- boot 1, no marker, one deep group mismatch → full probe, `chown -R`,
marker written
  - boot 2, marker present → no full probe, no `chown`
- boot 3, no marker, clean tree → full probe, no `chown`, marker written
  - boot 4, marker present, clean → no full probe, no `chown`
  - boot 5, top-level group mismatch with marker → `chown -R`
- I did not build the image or measure boot time in a container. Docker
was not available on the machine I used. On a warm local disk the full
walk is sub-second, so the local timings say nothing about cold network
block storage.

## Risks

- Once the marker is current, a mismatch deeper than three levels is not
detected automatically. Examples: files written as root by `docker exec`
or a restore run as root, after the first verified boot. Before this
change, those files were repaired on the next boot. The workaround is in
`doc/DOCKER.md`: delete the marker and restart. A remap, a fresh volume,
and a root-owned top level are still detected on every boot.
- On the first boot after an upgrade, each existing volume pays one full
walk (the same cost as today). Later boots skip it.
- The marker is a new dotfile in `PAPERCLIP_HOME`. Backups and copies
that include the home carry it along. This is safe, because the marker
records the UID/GID that it verified, and a different UID/GID forces a
new full probe.
- The non-root path (restricted PodSecurity, OpenShift) does not change,
apart from two new stdout log lines.

## Model Used

- Claude Opus 5.5 (`claude-opus-5-5`, 1M context) via Claude Code, with
tool use (shell, file editing). Default 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 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
2026-10-12 10:22:43 -07:00
DottaandPaperclip a2ac5c2715 refactor(chat): isolate Telegram, Slack, GitHub and Discord workflows (#15976)
## Thinking Path

> - Paperclip manages AI agents and their work.
> - Chat channels connect that work to external conversations.
> - The shared chat service still contains provider-specific workflows.
> - Those workflows are hard to inspect in a 34,000-line file.
> - This stack extracts Telegram, Slack, GitHub and Discord workflows
into their channel folders.
> - Shared authorization and service lifetime remain explicit.

## Linked Issues or Issue Description

Refs #15823, #15834, #15836, #15844 and #15845. Root of the second
behavior-preserving channel-folder refactor stack. The dependent PRs
#15979, #15981, #15985 and #15987 have merged into this branch; this PR
lands all five extractions on master.

## What Changed

- Move Telegram maintenance and subscription repair into
`chat-channels/telegram/maintenance.ts`.
- Move Slack session stopping and task starts into
`chat-channels/slack/session-stop.ts` and `task-start.ts`.
- Move GitHub webhook intake and recovery into
`chat-channels/github/webhook-ingress.ts`.
- Move Discord command registration and handling into
`chat-channels/discord/commands.ts`.
- Share type-only runtime records while preserving shared authorization,
transactions, live lease checks and worker ownership.
- Reduce the original service from 34,603 to 29,897 lines, removing
4,706 lines (13.6%).

## Verification

- All 205 selected integration cases passed before and after their
respective extractions: Telegram 41, Slack session stop 2 multi-step
scenarios, GitHub 84, Slack task start 25 and Discord 53. Each dependent
PR records its focused reproduction command.
- Server typecheck, module boundaries and whitespace checks passed.
- Per-slice syntax comparisons passed for moved declarations and
remaining service statements, allowing only the documented live callback
substitutions.
- Tests exercise the public service with disposable databases and
fixture provider transports. They do not claim live third-party
execution.
- Cumulative workspace typecheck and build passed on the final stack.
- Current-head CI passed on the combined stack commit
`d5b37dbe512e3e199932b6608cdc17639eda555a`; Greptile is 5/5 with no
unresolved threads.
- The [original serialized
shard](https://github.com/paperclipai/paperclip/actions/runs/38266518817/job/114870022249)
timed out in unchanged `adapter-auth-signal-routes.test.ts`. The exact
case passed locally without changes, and the [single job
retry](https://github.com/paperclipai/paperclip/actions/runs/38266518817/job/114874019933)
passed. The original timeout remains recorded.

Reproduce the original Telegram slice’s focused suite:

```sh
pnpm exec vitest run server/src/__tests__/chat-channels.integration.test.ts -t 'Telegram durable private draft Stop|Telegram maintenance|Telegram webhook|Telegram.*(pause|remove)|Telegram.*commands'
```

- Cumulative local `pnpm test:run` exited 1: the first server group had
**18,770 passed, 105 skipped, and one timeout** in unchanged
`server/src/services/workspace-git-snapshot-streaming.test.ts:56`
(300,000 ms on macOS). The fail-fast runner did not reach its later
workspace and serialized groups. An unchanged isolated rerun passed
using the existing timeout; this does not turn the original full run
green. The test and its Git utility code are outside this refactor; all
current-head CI jobs passed.
- Reproduce that diagnostic alone: `pnpm exec vitest run
server/src/services/workspace-git-snapshot-streaming.test.ts`.

## Risks

- Credential lease ownership and provider-confirmation ordering must
remain unchanged. Existing rotation, lease-loss, recovery and durable
Stop cases pass.
- The channel factories create no workers or timers and preserve live
runtime, shutdown and gateway-ownership reads.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning and repository tools. The
exact runtime model ID and context-window size are not exposed in this
session. No separate agent was used.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-12 12:03:23 -05:00
Dotta a9830414c7 Preserve repository dependencies when importing GitHub skills (#15731)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Its Skills library imports reusable instructions from GitHub.
> - Some skills depend on shared files and sibling skills outside their
folders.
> - Directory-only imports lose those dependencies.
> - This pull request keeps repository files together and lets users
select which skills appear in the library.
> - Agents receive the original files and paths through the existing
skill delivery system.

## Linked Issues or Issue Description

Refs #14713. Related: #13656.

**Current behavior**

GitHub imports copy independent skill directories. Shared scripts,
policies, helper skills, and credit files can be omitted. One
shared-file error can block every skill.

**Proposed behavior**

New imports keep repository files together at the scanned commit. Show
all skills with their folders and select every skill by default.
Collapse individual package files. Omit unsupported or oversized files
with a warning. Import available content without repair choices or an
extra confirmation. Keep warnings on the saved source.

**Reason and benefit**

Skills can read their shared repository files after Paperclip delivers
them to an agent. Original instructions stay unchanged. Users can
inspect included files and understand omissions before and after import.

## What Changed

- Store immutable company-scoped repository snapshots and associate them
with skill versions. Regenerate the migration after merging master.
- Preserve original bytes, binary encoding, and executable modes.
Shared-file changes version each usable selected skill. Pinned versions
retain their original files.
- Deliver a discovery wrapper with the original tree under
`.paperclip-repository/`. Preserve invocation policy when present. Fork
and create-from-existing retain repository files.
- Keep repository files together for new UI imports. Show every skill,
select all by default, and collapse package files. Remove the
packaging-mode menu. Keep the mobile import footer visible.
- Warn about symlinks, submodules, oversized files, missing references,
and invalid optional manifests. A malformed skill remains selected with
a Needs setup warning. Usable skills still publish, and its original
file remains repository support content. Source warnings persist through
refresh. Unsafe execution and secret-disclosure findings omit the
affected files before snapshot publication.
- Expose saved repository files in the installed skill viewer, including
binary encoding and executable flags. Reads use the immutable
company-scoped snapshot; relative instruction links resolve from their
original repository directory.
- Audit delivered inventories and derive trust from all repository
files. Accept evaluation prose while retaining dynamic-execution
findings. Read blobs in one bounded Git batch and check budgets before
reading support files. Imports never execute setup commands.
- Add scanner, database, delivery, UI, and browser regressions. Update
Storybooks, screenshots, and documentation.

## Verification

- 69 focused scanner, Git snapshot, delivery, and PostgreSQL lifecycle
tests passed. They cover partial imports, warning persistence, missing
references, binary files, executable modes, copies, pinned versions,
cancellation, company isolation, and batched Git reads.
- 110 import, skill-detail, selection-tree, saved-file link, and
Markdown renderer tests passed. A file-switch regression failed before
the resolver fix and passed afterward. Browser walkthroughs cover the
all-selected open tree, repository preview, non-blocking warnings,
missing references, setup problems, warning persistence, and mobile
layout.
- Full workspace typecheck and build passed. Runner E2E TypeScript
checks and design-token gates passed. The full serial local suite was
attempted and stopped after over an hour. The existing 40,000-file Git
streaming stress test failed after 303 seconds on macOS; the broad local
suite is not counted as passed. All 55 applicable PR checks passed on
the final commit, including the full sharded CI suite. Two optional
Storybook workflows were skipped; the five local Storybook walkthroughs
passed.
- On October 12, live campaigns imported the private Poteto repository
through the production GitHub Sources UI, selected architect and bro,
and invoked them with native Codex on local and Daytona. The source
connection was disabled before invocation. Independent filesystem
inspections matched every supported Git blob, size, and executable mode.
Both tasks completed and cleanup passed. Campaigns:
`local-2026-10-12T15-07-39-504Z` (both environments),
`local-2026-10-12T16-08-39-682Z` (local), and
`local-2026-10-12T16-03-33-938Z` (Daytona). Each of the two selected
skills received all 234 supported repository files. Later changes only
affect the installed-file viewer and documentation. This qualifies skill
delivery and instruction reads, not every Poteto workflow or adapter.
- A manual source-checkout test drive imported a synthetic private
GitHub repository with a symlink, an oversized file, a missing
reference, and malformed skill metadata. Available skills published with
their shared checklist. The walkthrough found duplicate warning text
after import; the final UI fix removes that duplicate. The file viewer
exposes saved repository dependencies and resolves authored relative
links without changing the instructions. A follow-up live refresh
omitted unsafe helper files with warnings, preserved every safe file
byte, and survived a server restart.

Screenshots below use the production UI with Storybook fixtures. They do
not replace the live GitHub and runtime checks.

<details>
<summary>All skills visible and selected by default</summary>

![Default repository
import](https://github.com/paperclipai/paperclip/blob/codex/skill-repository-packages/docs/pr-screenshots/pr-15731/repository-import.png?raw=true)

</details>

<details>
<summary>Import stays available with warnings</summary>

![Import
warnings](https://github.com/paperclipai/paperclip/blob/codex/skill-repository-packages/docs/pr-screenshots/pr-15731/repository-warning-details.png?raw=true)

</details>

<details>
<summary>Repository file inspector</summary>

![Repository
files](https://github.com/paperclipai/paperclip/blob/codex/skill-repository-packages/docs/pr-screenshots/pr-15731/repository-files.png?raw=true)

</details>

<details>
<summary>Mobile import</summary>

![Mobile
import](https://github.com/paperclipai/paperclip/blob/codex/skill-repository-packages/docs/pr-screenshots/pr-15731/repository-mobile.png?raw=true)

</details>

## Risks

- Each selected skill carries a repository copy in the runtime. The
database stores each snapshot once. Runtime transfer and storage
increase with the number of assigned skills.
- Runtime budgets remain: 9,990 repository entries, 1 MiB per file, and
61 MiB total. File omissions are recorded as warnings. Source-access or
download failures retain installed versions.
- Import success does not prove that every skill can run. Environment
dependencies, credentials, delegation tools, and schedules still need
configuration. Imported setup scripts are not executed.
- Live runtime coverage is native Codex on local and Daytona. Other
adapters use the shared delivery path but were not exercised in these
campaigns.

The roadmap marks Skills Manager as shipped. This extends the existing
importer. Related pull requests were checked.

## Model Used

OpenAI Codex, based on GPT-6, with repository inspection, code
execution, automated tests, and browser tools. The exact deployment
identifier and context-window size 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
- [ ] 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-12 11:30:20 -05:00
DottaandPaperclip ce08872dfb refactor(heartbeat): extract instruction working-copy lifecycle (#15984)
## Thinking Path

> - Paperclip runs agent tasks and preserves their managed agent files.
> - Heartbeat must prepare a private copy, collect stopped-provider
edits, and retain ownership during warm-session handoff.
> - That lifecycle is spread across preparation, adapter callbacks, and
cleanup.
> - A per-run controller can own the copy and receipt while the executor
retains teardown ordering.
> - This PR moves that coordination into a dedicated module.
> - Live getters preserve updated state and partially acquired
resources.

## Linked Issues or Issue Description

**What existing behavior does this improve?**
Maintenance of heartbeat instruction preparation. This is the second and
final PR in this batch, originally stacked on #15980. That dependency is
now merged; this PR is rebased onto its squash merge with an identical
patch and identical touched-file contents. Related-PR search found no
duplicate extraction.

**Current behavior**
After #15980, `heartbeat.ts` has 4,215 lines and 173,657 bytes. It
coordinates instruction-copy preparation, save receipts, warm handoffs,
and deferred cleanup inline.

**Proposed behavior**
`heartbeat/instruction-working-copy.ts` owns the per-run lifecycle in
233 lines. `heartbeat.ts` becomes 4,055 lines and 162,520 bytes. The
executor retains the outer release order, unavailable-copy reporting,
and environment/service cleanup policy.

**Reason and benefit**
The copy, save receipt, and warm ownership are readable together. The
remaining executor names preparation and dispatch phases without
obscuring teardown decisions.

**Breaking changes**
None intended. All 140 named public heartbeat exports remain unchanged.

## What Changed

- Extract copy preparation, save/checkpoint callbacks, warm
reservation/adoption, prompt guidance, and the deferred-release closure.
- Read the executor's current run row through a callback; expose current
copies, receipts, and acquired cleanup handles through live getters.
- Keep cleanup handles available after preparation failure, and preserve
release-before-retry on invalidated warm reuse.
- Add four public-path characterization tests with real disposable
PostgreSQL and files, plus four direct boundary tests.
- Add isolated browser acceptance with a real process that edits
instructions, completes its task, and reads the saved bytes on its next
task.
- Document the boundary and opt-in acceptance command.

## Verification

- **Before extraction:** four new public-path tests pass on the original
implementation, as do 108 existing
instruction-copy/directory/native-cleanup tests. The two-task browser
journey also passes on the baseline server.
- **After extraction:** all 112 baseline tests pass; all four new
boundary tests pass; 12 selected native-session ownership/retirement
cases pass (the other 670 cases were outside this focused selection).
- **Browser acceptance:** the fresh extracted-code server passes the
full two-task journey (25.9 seconds): real child-process edits,
saved/unchanged receipts, canonical bytes through the agent-file API,
next-task restore, completed task UI, and copy cleanup.
- `pnpm -r typecheck`, final server typecheck, and `pnpm build` pass.
Module-boundary and public-export checks pass.
- Normalized syntax comparison confirms all moved bodies and the
reconstructed executor match the base, accounting explicitly for live
run getters and delegation.
- Final rebased head: `a08872c97e587a21ffc0fc6b78f34b81e484e7b0`.
Combined entry-point and boundary regressions pass again (12/12); fresh
Greptile review is 5/5 with no findings. All 55 current-head checks pass
(two configured skips), including the hosted full-suite tests, build,
typecheck, and all eight browser shards. There are no open review
threads.

## Risks

- Acquisition can succeed before preparation throws. Boundary tests
verify that both the reservation and a transferred copy remain visible
to the cleanup owner.
- Callbacks can replace copy/save state during execution. Public tests
verify saved receipts; direct tests verify the current run row, warm
checkpoint receipt, and permission-root preservation.
- Missing edit authorization and cleanup failure must remain nonfatal.
Characterization tests revoke actual membership after dispatch admission
and inject a cleanup error while preserving the saved result.
- The browser uses a test-launcher-only instruction-capable wrapper
around the real process adapter. Production adapter capabilities are
unchanged. No live provider calls are made; native warm ownership is
covered by focused module and native-session tests rather than a live
provider browser run.

## Model Used

OpenAI Codex, based on GPT-6, assisted with reasoning, code edits, tool
execution, and verification. The exact deployment model ID and
context-window size are not exposed in this session.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-12 10:52:32 -05:00
DottaandPaperclip 1e0cfcb4fc test: retire redundant suites and consolidate owner coverage (#15978)
Retire duplicate ACPX and Codex proof after migrating unique assertions to their behavioral owners. Consolidate repeated fixtures and exact case matrices while preserving lifecycle, protocol, company, credential, approval, and ordering contracts. Remove the unused runtime-controls compatibility path and document test ownership and preservation evidence.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-12 10:44:02 -05:00
DottaandPaperclip 94e4218310 refactor(heartbeat): extract runtime service coordination (#15980)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Heartbeat runs start workspace services and receive service reports
from adapters.
> - Service context updates and readiness notices are spread through the
executor.
> - These steps share a clear responsibility, while process and lease
ownership already live elsewhere.
> - This pull request puts service coordination in one module.
> - The executor retains the same dispatch and cleanup order.

## Linked Issues or Issue Description

**What existing behavior does this improve?**
Maintenance of heartbeat execution. This is the first of two final
focused extractions after #15973. Related #15653 changes runtime
callback origins; this PR preserves that policy.

**Current behavior**
`heartbeat.ts` has 4,372 lines and 178,459 bytes. It validates service
intents, starts services, persists adapter reports and primary URLs, and
posts readiness notices inline.

**Proposed behavior**
`heartbeat/runtime-services.ts` owns those steps in 212 lines.
`heartbeat.ts` becomes 4,215 lines and 173,657 bytes. Process registries
and leases stay in `workspace-runtime.ts`. The executor still releases
services in its original cleanup path.

**Reason and benefit**
Service coordination has named preparation and result phases. The
execution path is easier to read without changing service lifetime or
moving cleanup policy.

**Breaking changes**
None intended. All 140 named public heartbeat exports remain available,
including `postWorkspaceReadyComment`.

## What Changed

- Move service-intent validation, startup, adapter-result persistence,
and readiness notices into one module.
- Preserve mutable run context, primary URL selection, notice error
handling, and release ordering.
- Add four public-path tests using disposable PostgreSQL, real HTTP
service processes, and a fixture adapter.
- Extend browser recovery acceptance to fetch the actual service URL,
check the readiness notice, and verify listener cleanup.
- Update the module map and acceptance instructions.

## Verification

- Before extraction: all five tests pass, including the four new
characterization cases and existing notice formatting test.
- After extraction: all 17 tests pass across service coordination,
notice formatting, output recording, and runtime authorization.
- Structural comparison confirms that all moved bodies and the
reconstructed executor match the baseline. Module boundary checks pass.
- `pnpm -r typecheck` and `pnpm build` pass.
- Extended isolated browser acceptance: **2/2 pass** (3.7 minutes),
covering actual service readiness, recovery, bounded failure, and
cleanup. At `8e33e2032de681082f128470eb8ab0711b382afe`, all 55
current-head checks pass (two configured skips), including hosted
full-suite tests. Greptile is 5/5 with no findings or review threads.

## Risks

- Service context must be persisted before adapter dispatch and before
run completion. The tests inspect both phases and preserve the first
service URL when adapter reports arrive.
- Notice failures must remain nonfatal. The public-path test checks both
startup and result notices.
- Shared services must survive run cleanup; ephemeral services must stop
after success or failure. Tests use actual processes and HTTP requests.
- The fixture supplies UUID service report IDs, as required by the
database. The existing generated-ID fallback for reports without IDs is
outside this extraction.
- Test adapters and services are local fixtures; these tests make no
live provider calls.

## Model Used

OpenAI Codex, based on GPT-6, assisted with reasoning, code edits, tool
execution, and verification. The exact deployment model ID and
context-window size are not exposed in this session.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-12 10:40:42 -05:00
DottaandPaperclip 914b70572c refactor(heartbeat): extract execution configuration (#15973)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Heartbeat turns task, agent, environment, and account settings into
an executable run.
> - The executor still assembles scoped credentials and skill
configuration inline between environment selection and session
preparation.
> - That phase has a clear input/output boundary, but credential
acquisition must remain visible to cleanup even when later setup fails.
> - This pull request extracts configuration assembly and immediately
notifies the executor of acquired resources and redaction state.
> - The executor retains admission, dispatch, and cleanup, with the same
statement ordering and explicit busy-account deferral.

## Linked Issues or Issue Description

**What existing behavior does this improve?**
Maintenance of heartbeat execution. This PR follows #15969 and #15966,
both now merged. The branch is rebased onto `master`; `git range-diff`
confirms that the extraction patch is unchanged.

**Current behavior**
`heartbeat.ts` has 4,561 lines and 188,950 bytes. Configuration assembly
mixes workspace overrides, secrets, managed AI preparation, and skill
delivery into the executor.

**Proposed behavior**
`execution-configuration.ts` owns this preparation phase in 269 lines
and 16,204 bytes. The executor calls it between environment and session
preparation and retains immediate cleanup ownership. `heartbeat.ts`
becomes 4,372 lines and 178,459 bytes.

**Reason and benefit**
The execution path exposes another named phase, with tests that exercise
its effective configuration, resource lifetime, failure diagnostics, and
busy-account result through public heartbeat dispatch. Related work:
#11827 changes pre-dispatch secret refresh and #15355 changes GitHub
delegation; this extraction preserves current policies.

**Breaking changes**
None intended. Public exports remain available, and the original helpers
keep their modules.

## What Changed

- Move configuration merging, scoped secret resolution, managed AI
acquisition, runtime skills, connector instructions, and managed
identity preparation into one typed module.
- Notify the executor immediately when failure-redaction values, managed
credentials, and identity redaction become available. Returning
resources only after all preparation succeeds would lose cleanup on
partial failure.
- Preserve the live failure-secret getter for later runtime environment
changes. Return an explicit deferred result for busy accounts.
- Add four real-database characterization tests through
`heartbeat.wakeup`, using actual credential files and cleanup with a
fixture adapter.
- Update the developer module map.

## Verification

- Before extraction: all 45 tests pass across the new public-path tests
and existing project-environment tests.
- After extraction: 47 tests pass across execution configuration,
project environment, and runtime skills. No skips.
- Two additional real-process managed identity tests pass; nine
unrelated cases are deliberately filtered out.
- `pnpm -r typecheck` and `pnpm build` pass.
- Normalized syntax comparison confirms the moved body and reconstructed
executor match after accounting for immediate ownership callbacks and
the explicit prepared/deferred result.
- Isolated browser acceptance: 2/2 pass (3.9 minutes). A real process
worker completes after Git recovery, with persisted stdout and matching
final output byte counts. Persistent failures stop after three attempts.
Both outcomes survive reload. The disposable server was shut down after
verification.
- All eleven new public-path characterization cases across the three-PR
stack pass together on the final commit.
- All 140 named public exports from the batch baseline remain available.
- CI initially failed the legacy-continuation message browser case:
periodic recovery escalated the issue, then queue admission cancelled
the run with `execution_reconciliation_required` before configuration
preparation. The unchanged predecessor shard passes, and the exact case
passes locally (5 seconds in the browser). The original failing case
passed on its single rerun (8.2 seconds), but GitHub then shut down the
runner during a different test (`The runner has received a shutdown
signal`). The separate infrastructure restart completed: 24 browser
cases passed, with two existing optional cases skipped. Assertions and
timeouts are unchanged.
- Greptile: 5/5 on `27ab7e3df64c157ba1f00c5757db812ff7a4e5aa`, with no
actionable findings. Hosted full-suite CI passes on this exact commit:
all 54 check runs and the additional commit status are successful.

- Merge preparation: all eleven characterization tests pass after
rebasing onto `master`. Fresh full CI passes for
`1758673f87eaa76a8b2c520eac4b631ada5d9ba1`: 55 successful checks and two
optional Storybook skips, including all eight browser shards, typecheck,
tests, and build. Greptile is 5/5 on this exact commit with zero review
threads. All heartbeat source, tests, documentation, and browser
assertions are identical to the previously reviewed final stack.

## Risks

- A failure after AI acquisition must still remove the disposable
credential home. The new public-path failure test proves actual
filesystem cleanup and diagnostic redaction before the external
reporting sink.
- The baseline exposed an existing gap: arbitrary exception text in the
local `run.error` field can contain a credential even though the failure
reporter redacts it. This behavior is unchanged and merits a separate
fix; the test does not claim the local error field is sanitized.
- Account-busy deferral must return without dispatch while retaining its
scheduled retry. The new test checks the durable run states.
- Browser acceptance uses a real process fixture and disposable data,
without live provider calls.

## Model Used

OpenAI Codex, based on GPT-6, assisted with reasoning, code edits, tool
execution, and verification. The exact deployment model ID and
context-window size are not exposed in this session.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-12 10:01:00 -05:00
DottaandPaperclip 37913e4907 refactor(heartbeat): extract run output recording (#15969)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Heartbeat runs stream adapter output and persist execution results.
> - Output recording currently sits inside the executor beside admission
and cleanup.
> - Log handles, sequence numbers, progress writes, and excerpt buffers
share one attempt lifetime.
> - This pull request moves that state and its callbacks into a
per-attempt recorder.
> - The executor keeps dispatch and finalization visible, while
finalization reads live output values.

## Linked Issues or Issue Description

**What existing behavior does this improve?**
Maintenance of heartbeat execution. This PR follows #15966, which is now
merged. The branch is rebased onto `master`; `git range-diff` confirms
that its extraction patch is unchanged.

**Current behavior**
`heartbeat.ts` has 4,740 lines and 195,258 bytes. It directly owns log
handles, excerpt buffers, output sequence numbers, progress flushes, and
adapter event callbacks.

**Proposed behavior**
`run-output.ts` owns the per-attempt recorder. The main file has 4,561
lines and 188,950 bytes; the recorder has 209 lines and 7,757 bytes.

**Reason and benefit**
Output state has one explicit lifetime and interface. The shared log
store and finalization policy retain their original owners. Related open
output work includes #12539 and #15524; this PR does not change
redaction policy.

**Breaking changes**
None intended. Public flush constants and log formatting exports remain
available from `heartbeat.ts`.

## What Changed

- Move log initialization, redaction, excerpts, progress persistence,
live delivery, and adapter callbacks into `createHeartbeatRunOutput`.
- Preserve the acquired log handle if later setup fails. Bind callbacks
to the execution-start run row.
- Pass live excerpt getters to success and failure completion so output
arriving during finalization remains visible.
- Add three real-database public-path tests. Extend browser acceptance
to inspect persisted worker stdout and output byte counts.
- Update the module map.

## Verification

- Before extraction: 23 tests pass, including all three new output
characterization cases.
- After extraction: 45 tests pass across output, log formatting, event
sequencing, and run completion. No skips.
- `pnpm -r typecheck` and `pnpm build` pass.
- Normalized syntax comparison confirms the moved bodies and
reconstructed executor match. Reviewed the factory ownership, callback
binding, getter wiring, and constant re-exports separately.
- Isolated browser acceptance: 2/2 pass, including persisted worker
stdout and matching final byte counts. The process worker completes
after recovery; persistent timeouts stop after three attempts. Both
terminal states survive reload.
- Hosted full-suite CI passes on
`d32c8fc40e72b9766bdd9e191d777e8dc554e3f2`; all 55 reported checks are
successful. Greptile is 5/5 with zero review threads.
- The initial chat shard timed out waiting for GitHub mention admission.
Its fixture mocks heartbeat wakeup and never calls the output recorder.
Both parameterized cases passed locally; the unchanged shard passed on
one rerun. No test or timeout changes.

- Merge preparation: all seven session-preparation and run-output
regression tests pass after rebasing onto `master`. Fresh full CI passes
for `60e2a49ce2879645a9f618a801e0ba0903ede161`: 55 successful checks and
two optional Storybook skips, including all eight browser shards,
typecheck, tests, and build. Greptile is 5/5 on this exact commit with
zero review threads.

## Risks

- A stale excerpt snapshot could lose output that arrives while
finalization awaits I/O. Passing-before/passing-after tests exercise
this for both successful and failed runs.
- Per-attempt state must not become service-wide. Each executor
constructs its own recorder and keeps the existing shared store.
- Browser acceptance uses a real process fixture and disposable data; it
makes no live provider calls.

## Model Used

OpenAI Codex, based on GPT-6, assisted with reasoning, code edits, tool
execution, and verification. The exact deployment model ID and
context-window size are not exposed in this session.

## Checklist

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

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-12 09:51:17 -05:00
DottaandPaperclip d497d1ccec refactor(chat): extract failed-run retry coordination (#15845)
## Thinking Path

> - Paperclip manages agent work and sends results back to chat.
> - A failed chat run can be retried only while its original source
remains authorized.
> - That decision includes persisted source evidence and native runtime
state.
> - Retry claims also share in-flight promises and depend on service
shutdown.
> - This PR groups retry authorization, execution and notices in one
module.
> - The service retains the live admission, runtime and shutdown
boundaries.

## Linked Issues or Issue Description

Refs #15844, #15836, #15834 and #15823. Stack slice 5 of 5, based on
`codex/chat-publication-replacement`.
Merge each predecessor first, then retarget this PR. Do not merge the
stack out of order.

Stack order: #15823 (provider messages) → #15834 (resource
reconciliation) → #15836 (identity links) → #15844 (publication
replacement) → #15845 (failed-run retries). GitHub native stack merging
preserves this order and automatically restacks the remaining branches.

## What Changed

- Extract failed-run retry admission and coordination while keeping the
in-flight map, cursor, error class and authority registrations scoped to
one chat service.
- Move the exact Teams uncertain-file continuation proof into
`microsoft-teams/file-transfer-source.ts`.
- Pass the existing transaction, explicit source identifiers and denial
factory. Return only the proven publication ID.
- Keep committed-result, complete source-batch, ancestor and
other-uncertain-effect checks in shared `failed-run-retry.ts`.
- Keep native execution recovery together; its provider references
describe runner backends, not chat channels.
- The retry coordinator is 1,825 lines and the Teams proof is 149 lines.
Restack on revised #15844 and record the reassessment.

## Verification

- Baseline: 59 retry cases and six negative Teams authority cases
passed. The combined 65 cases passed after the move.
- Cases cover retry ownership, service restart/lifetime, ambiguous Teams
receipts, sibling unknown effects, changed source authority and active
attempts.
- Teams guard, joins, locks, attempt checks and selected ID match after
explicit input substitutions. The shared coordinator retains its state
and cleanup ownership.
- Server typecheck, module-boundary and whitespace checks passed.
- Integration uses disposable databases and fixture provider transports;
no live third-party execution is claimed.

- Fresh Greptile review is 5/5 on
`4f7151409717458717633aa270441a8faf33664e`, with a successful check and
no open review threads.
- Final-stack `pnpm -r typecheck` and `pnpm build` passed at
`57ec5ba94aecd860471f9b14db46f99120c7c790`.
- All CI checks passed on `57ec5ba94aecd860471f9b14db46f99120c7c790`.
- Full local `pnpm test:run` stopped in the general-server group: 18,763
passed, 105 skipped, and one test timed out after 20 seconds in
unchanged `workspace-runtime.test.ts` ("does not accept an occupied
allocated port when listener ownership is unavailable"). The same case
passed in an isolated rerun without code, assertion or timeout changes
(1 passed, 175 excluded). The original timeout is retained as a failed
full run; later workspace and serialized groups were not reached. The
full local gate remains unverified.

- Merge verification on the restacked tree: 118 focused identity,
publication, Photon and retry integration tests passed; server typecheck
and module-boundary checks passed. GitHub’s rebased trees exactly match
the previously reviewed file contents.

- All current-head required checks passed on
`4f7151409717458717633aa270441a8faf33664e`: full CI; Greptile 5/5 with
no unresolved threads.

## Risks

- Authorization must remain fail-closed across committed output and
retry admission. The helper proves only the Teams transfer; the caller
still checks the complete source and result in the same transaction.
- In-flight promises, error-class identity, registration cleanup, cursor
state and the live shutdown getter must remain per-service. Their code
is unchanged by this channel-folder move.
- This PR depends on #15844. Merge and retarget in stack order.

## Model Used

OpenAI Codex, GPT-6 family, with reasoning and repository tools. The
exact runtime model ID and context-window size are not exposed in this
session. No separate agent was used.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-12 09:49:34 -05:00