Commit Graph
1707 Commits
Author SHA1 Message Date
DottaandPaperclip df4ee06e2f feat(skills): select and validate tracked GitHub branches (#16288)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Skills can track a branch in an authorized GitHub repository.
> - Sources previously offered only a default-branch repair action.
> - People need to select a listed branch or enter a branch that GitHub
has not listed.
> - The server must validate the selected ref with the caller's
authorized connection.
> - This pull request connects the approved branch picker to production
APIs.
> - Installed skills remain usable while the source changes its tracked
ref.

## Linked Issues or Issue Description

**Subsystem affected**

Skills UI, shared API contracts, GitHub source services, and agent API
discovery.

**Problem or motivation**

The default-branch repair action does not let people choose another
branch. The settings service also validates the old ref when a caller
supplies a new ref other than HEAD. A missing tracked branch can prevent
connection repair even when the selected account has repository access.

**Proposed solution**

Load a bounded branch list with the authorized connection. Mark the
default branch. Search listed branches or accept a typed ref. Validate
the selected ref before saving. Keep the chosen value after a load
error, invalid ref, or concurrent update. Save the selected connection
and branch together during connection repair.

**Alternatives considered**

A default-branch button does not support a named branch. A text field
alone does not help people find the repository default or available
branches.

**Roadmap alignment**

This improves the existing Skills source manager. It does not add a new
skill import system.

**Additional context**

Refs #16066 and #16256. This is the production implementation of the
approved branch-selection Storybook journeys.

## What Changed

- Add an authorized branch-list endpoint and shared response/query
types. Register it in agent API discovery.
- Validate the requested ref, including the returned commit SHA.
Preserve source leases, revision checks, project ownership, skill
identities, selections, and installed versions.
- Add the searchable branch picker to Sources and connection repair.
Allow typed refs during branch discovery and after discovery fails.
- Keep save errors in the dialog. Retain the selected branch through a
concurrent source update.
- Use the production components and API client in Storybook. Add
keyboard, touch, loading, retry, invalid-ref, connection-repair, and
concurrent-update browser journeys.
- Update product documentation and the feature/access maps.

## Verification

- Red/green: two new database regressions fail against the previous
service and pass against the implementation.
- Linux database/service suite: 85 tests passed. This includes source
visibility, human/agent authorization, selected connections, immutable
versions, and invalid refs.
- Rebased branch and review fixes: 94 focused UI, route,
branch-validation, and fixture tests passed. Two conflict-message
regressions and the stale-retry fixture regression fail before the fix
and pass after it.
- Storybook build passed. All 26 focused browser journeys passed.
- Repository typecheck and build passed. Final UI typecheck passed after
the review fixes.
- The full local Vitest run was interrupted when the checkout and its
dependencies disappeared. It reported missing working-directory/Postgres
files and worker startup errors. Its two timed-out tests pass after the
checkout was restored. The complete CI suite passed on the final head,
`cf5d4c8f0a`: [CI
run](https://github.com/paperclipai/paperclip/actions/runs/38571068332).
One unchanged workspace timestamp test required a single rerun. Its
release-minus-one-millisecond value can equal the original confirmation
time. The rerun passed.
- Live acceptance passed on implementation head `5dd935bf14` in an
isolated macOS arm64 instance with Node 24.21.0 and an authorized
private GitHub repository. The UI loaded the actual default branch,
saved branch and selected connection together, and preserved the
installed skill/version IDs after reload. An invalid typed ref retained
the draft and showed the provider error. A valid typed HEAD ref then
saved successfully. The owned runtime and database were stopped after
the test. Focused unit, Linux database, and browser suites passed again
after the error-handling review fixes.
- Greptile reviewed final head `cf5d4c8f0a` at 5/5. Both review threads
are resolved; the fresh review has no remaining findings.
- Token gates and map/catalog checks passed.
- Review entry points: `Skills / Project repository skills / 03 Branch
selection components` and `04 Branch selection journeys`. Run `pnpm
storybook` to inspect them.
- Browser command: `pnpm exec playwright test --config
tests/storybook-visual/playwright.config.ts
tests/storybook-visual/skill-source-branch-selection.spec.ts
tests/storybook-visual/project-repository-skills.spec.ts`.
- Manual check: open an independent GitHub source, choose a new branch,
select the default or type a ref, save, and reload. Try an invalid ref
and confirm the selection remains in the dialog. For a project-bound
source, change its ref in project settings.

## Risks

- GitHub discovery is bounded to 100 listed branches. The default branch
is always included. Typed refs remain available.
- A source can change while the dialog is open. The API rejects a stale
revision and the UI keeps the draft for retry.
- A ref change schedules the existing refresh pipeline. Installed
versions remain available until a successful refresh imports new
content.
- No database migration or credential storage change is required.
Existing HEAD requests remain supported.

## Model Used

OpenAI Codex, GPT-6 family. The serving model's exact identifier and
context-window size are not exposed in this session. Used reasoning,
repository tools, code execution, and browser tools.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 09:55:43 -05:00
DottaandPaperclip f69c6a8a35 fix(github): retry and recover review publication (#16284)
## Thinking Path

> - Paperclip manages agent work and its published results.
> - GitHub review bots publish task-bound assessments through governed
tools.
> - A review can survive a caller timeout because its publication intent
is durable.
> - A transient provider failure still returned a failed receipt before
recovery.
> - Gateway replay could return an empty cached result, and publication
retries had no dedicated scheduler deadline.
> - This pull request adds bounded retries, receipt recovery, and an
independent durable worker.
> - A temporary GitHub failure can recover without another review or
duplicate publication.

## Linked Issues or Issue Description

Refs #16275. That change increased the GitHub tool deadline. This change
repairs retry and recovery behavior.

**What happened?**

A one-off GitHub HTTP 503 or 429 left the caller with a failed
publication receipt. The saved retry did not have its own scheduler
worker. An identical gateway call after a timeout could return an empty
cached result instead of the saved publication receipt.

**Expected behavior**

Temporary failures should retry under current authority. Pending work
should resume at its saved deadline after restart. Exact retries should
return the current receipt. Lost responses must not create duplicate
comments, findings, or checks.

**Steps to reproduce**

1. Admit an advisory PR review request and begin its assessment.
2. Return HTTP 503 or 429 for the first summary-comment PATCH, then let
GitHub succeed.
3. Submit the assessment through the run-scoped tool gateway.
4. On master, the result says publication failed. With this change, the
same call returns the processed receipt and neutral check.
5. For receipt recovery, use a shorter caller deadline and repeat the
exact call after publication finishes.

**Paperclip version or commit**

Reproduced on master at `6cb6ac3b8e40cb90d734ba115a2f22f850ff9477`.

## What Changed

- Classify transient transport, server, and rate-limit failures. Honor
Retry-After and primary reset deadlines. Stop retries for permanent
permission and validation errors.
- Retry safe reads and installation-token issuance within a bounded call
window. Retry publication writes through the existing outbox and
provider receipt checks.
- Wait for up to three publication attempts within a 45-second retry
window. Keep the durable eight-failure cap and enforce saved deadlines
on explicit retries.
- Register an independent GitHub publication worker with
transaction-owned wake notifications, startup recovery, and per-PR lease
deadlines.
- Recover exact comment, progress, and assessment gateway calls from
their stored publication. Require the same company, agent, task, run,
connection, tool, arguments, and current policy. Clear stale timeout
errors after recovery. Approved exact retries read the current receipt
through the existing signed replay path; the worker retains the verified
approval for deferred writes and still rejects revocation.
- Add regression coverage and update the connector guide and
feature/access maps.

## Verification

- Red: both one-off HTTP 503 and 429 regressions failed on master with a
failed publication receipt.
- Green: the same tests passed after the change.
- GitHub review workflows: 133 covered against isolated PostgreSQL 18.
The final combined run passed 132 and hit one fifteen-second timeout
under concurrent build load. All three variants of that workflow passed
in isolation. The earlier combined run passed all 131 workflows before
the two approval regressions were added.
- Retry policy and delivery coordinator tests: 35 passed.
- Additional focused checks passed for the actual eight-failure cap,
exact-argument conflicts, policy revocation, and timeout receipt
recovery. The approved deferred-publication and revocation regressions
failed before the review repair and passed after it, with one approval
request preserved.
- `pnpm -r typecheck`: passed. `pnpm build`: passed. `node
scripts/check-maps.mjs`: passed.
- The broad local `pnpm test:run` path remains blocked by the host's
exhausted PostgreSQL shared-memory slots. The changed database-backed
workflows use an isolated Docker database instead. All Paperclip CI jobs
passed at a6d81b67cb, including the required verify and E2E gates.
Greptile is 5/5 at the same head, with the approval-recovery finding
resolved. The external Superagent Security Scan also passed.
- Live acceptance passed on a dedicated single-repository local test App
and native Paperclip Runner. A test-only preload returned one HTTP 503
before the final summary PATCH. One submit_review call completed in
17.305 seconds; the bot published its 3/5 advisory review and neutral
check, then automatically finished the task. [Published
review](https://github.com/paperclipai/paperclip-permissions-smoke-20260926-pap57-fee7428e/pull/9#issuecomment-6168809007),
[neutral
check](https://github.com/paperclipai/paperclip-permissions-smoke-20260926-pap57-fee7428e/runs/115863564014).
This live test ran at de6862072c; approval-specific recovery is covered
by the later regressions. The preload is excluded from the repository
and removed from the running local instance.

## Risks

Retries make additional bounded provider requests. A provider write with
a lost response is recovered through its existing App-owned identity
before another write is permitted. The worker rechecks authority and the
PR head; revocation and superseded work stop publication. Calls can
still exceed their deadline, and repeated failures eventually exhaust
the retry cap. A pending publication is not a completed task. The agent
still owns task completion after receiving a confirmed receipt. No
schema migration is needed.

## Model Used

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 08:40:32 -05:00
DottaandPaperclip 7c016c049b perf(workspaces): wake terminal cleanup from committed changes (#16282)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents use execution workspaces to retain their files and Git
branches.
> - A reaper closes eligible workspaces after their task tree is
terminal.
> - The reaper scanned every heartbeat tick and read Git before cheap
eligibility checks.
> - Permanent scans also query idle databases when no cleanup work
exists.
> - This pull request wakes cleanup after transaction settlement and
restores bounded work at startup.
> - The benefit is fewer scans while fresh cleanup guards continue to
protect retained work.

## Linked Issues or Issue Description

Related work: Refs #13619, Refs #11753. Those PRs bound scans or back
off unchanged candidates. This PR uses the transaction work coordinator
to remove the permanent reaper tick. It overlaps their early
terminal-state filtering but does not change their runtime restart
behavior or introduce a second Git scheduler cache.

**What existing behavior does this improve?**

Terminal workspace cleanup and Git inspection for workspace detail
reads.

**Subsystem affected**

Server orchestration, execution workspaces, and idle sleep admission.

**Current behavior**

The heartbeat tick scans workspace candidates every thirty seconds. It
runs Git before rejecting nonterminal trees, cooldowns, and active runs.
Display inspections expire after five seconds.

**Proposed behavior**

Recover workspace rows in bounded startup pages. Wake after relevant
issue, workspace, and pull-request work-product transactions settle.
Schedule the terminal tree cooldown or a five-minute eligibility retry.
Back off unchanged candidates across unrelated wakes, and invalidate
their backoff after relevant committed changes. Park empty and
nonterminal-only lanes. Reuse display inspections for thirty seconds.
Read fresh Git before destructive cleanup.

**Reason and benefit**

Avoid recurring database and Git work when no cleanup is possible.
Retain transaction admission holds, restart recovery, and fresh deletion
checks. Retention eligibility can wait for the next host wake; active
and failed recovery work still blocks sleep.

**Breaking changes**

No schema or API shape changes. Display Git state can lag by thirty
seconds. Cleanup eligibility retries can take five minutes.

## What Changed

- Add a terminal-workspace worker to the existing transaction work
coordinator and scheduler.
- Signal relevant writes before mutation on the owning transaction.
Outer transaction settlement wakes cleanup.
- Derive cooldown deadlines from the full company-scoped terminal task
tree. Continue bounded recovery pages without a permanent sweep.
- Bound unchanged candidate inspections to five minutes, retain at most
256 entries, and invalidate affected entries with scoped transaction
hints. Repeated wakes preserve the earliest inspection deadline.
- Collect deadlines from task trees already read by bounded pages.
Restore the cursor after a failed page and rescan commits received
during a rotation.
- Check terminal state, cooldown, and active runs before Git. Preserve
lifecycle locks, generation fences, and fresh final checks.
- Keep failed or incomplete recovery visible to idle admission. Preserve
the passive retention exception.
- Extend the bounded display cache to thirty seconds and document the
scheduling and wake limits.

## Verification

- Focused workspace, coordinator, cache, and local sleep suites: 111
tests passed with real local PostgreSQL and Git worktrees where
applicable.
- Final worker, display cache, and local sleep suites: 28 tests passed.
- The regressions prove three ineligible workspaces invoke zero Git
scans, cached clean display state cannot remove new untracked files, and
real producer notifications wait for outer transaction settlement.
- `pnpm -r typecheck` and `pnpm build` passed locally. Map and
module-boundary checks passed.
- Work-product and sleep-safety suites: 221 tests passed. Reopen and
issue-tree control suites: 46 tests passed. Final backoff, workspace,
worker, and work-product suites: 87 tests passed.
- Final workspace and worker suites: 81 tests passed. Deadline selection
performs zero database reads. A failed full page retries its unread
terminal workspace before recovery can park.
- Full CI coverage passed on head `2c397970f7`: general tests,
serialized server suites, runner verification, E2E shards, typecheck,
build, and the final verification gate. The duplicate serial local `pnpm
test:run` was stopped after full CI coverage passed; it is not reported
as a completed local pass.
- Greptile: 5/5 with both findings resolved. The security scan also
passed. No failing or pending PR checks remain.

## Risks

- Display state can be stale for thirty seconds. Destructive guards
bypass the display cache.
- Undelivered or otherwise ineligible terminal work retries after five
minutes instead of each heartbeat tick.
- Transaction signals cover the owning `createDb` scope. Direct SQL and
independent server processes require startup recovery.
- Local cooldown timers are maintenance hints. They do not establish an
external wake alarm or whole-instance sleep safety. Existing blockers
remain in force; failed bounded recovery keeps the host awake.

## Model Used

OpenAI GPT-6, model ID `gpt-6`, through Codex. Reasoning, repository
inspection, shell execution, and browser tools assisted the change. The
runtime does not expose the context-window size.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 08:28:45 -05:00
DottaandPaperclip 1e731520bb fix(mcp): deduplicate discovered tool descriptors (#16281)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents receive governed app tools through the MCP gateway.
> - GitHub review tasks can contain a PR discussion and inline finding
threads.
> - Discovery repeated each tool for every conversation linked to the
task.
> - Native tool registration treated these repeated descriptors as a
fatal collision.
> - This pull request lists GitHub tools once and deduplicates identical
descriptors across providers.
> - Agents can continue work while conflicting definitions and normal
tool permissions remain enforced.

## Linked Issues or Issue Description

**What happened?**

A follow-up run on a GitHub review task failed with
`assigned_mcp_tool_name_collision` before the provider started. The task
had a main conversation and an inline finding thread. Discovery returned
the same seven tools twice. Repeated descriptors from other providers
could cause the same failure.

**Expected behavior**

Discovery exposes each identical tool once. Separate connection names
remain separate tools. Conflicting definitions remain an error. Every
call still passes the gateway's policy, approval, revocation, and audit
checks.

**Steps to reproduce**

1. Run a GitHub review and publish an inline finding.
2. Send a follow-up request on the same PR task.
3. Inspect the task-bound bot catalog and the next native startup.
4. Separately, return two identical descriptors from an assigned app or
plugin catalog.

Related: #13717 added the GitHub review workflow.

## What Changed

- Use a distinct GitHub discovery query across conversations that share
an endpoint and task. Keep both thread mappings.
- Deduplicate identical gateway descriptors by their full gateway name
across providers. Use this rule for MCP listings, plugin listings,
assigned-tool startup, and fresh assigned-tool discovery.
- Reject conflicting descriptors with `tool_name_collision`. Keep the
projected-name collision guard.
- Cover GitHub inline-thread discovery, repeated plugin/app tools,
direct and on-demand catalogs, revocation, conflicting bindings, and
separate connection names.
- Update the execution contract, GitHub documentation, and agent access
map.

## Verification

- Regression baseline: six new assertions failed on the original
implementation. GitHub returned fourteen entries, plugin discovery
repeated a tool, and native startup failed on repeated app tools.
- Focused verification passed: 272 GitHub cases, all 29 assigned-tool
cases, and all 88 gateway acceptance cases.
- `pnpm -r typecheck` passed.
- `pnpm build` passed.
- `node scripts/check-maps.mjs` passed.
- Current-head CI passed: 55 checks passed, two inapplicable checks
skipped. Greptile scored the current head 5/5 with no unresolved
threads.
- Full local `pnpm test:run` encountered a 15-second timeout in the
existing bearer-auth throttling test, before tool discovery. That test
passed in the separate 88-case gateway run and on one isolated rerun.
The duplicate broad local run was then stopped; the complete
current-head CI matrix passed.

## Risks

- Deduplication keeps the first identical descriptor and preserves
listing order. Same-name descriptors with different definitions fail
with HTTP 409 rather than selecting an ambiguous binding.
- The GitHub query changes result cardinality. It retains the existing
task/company/agent filters and leaves conversation rows intact.
- No schema migration or credential change. Live staging recovery
requires deployment and a new authorized run.

## Model Used

OpenAI Codex, based on GPT-6, with code inspection, editing, browser
investigation, and test 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
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 07:39:54 -05:00
DottaandPaperclip e9b6c4d44c perf: remove history scans from task and chat queries (#16278)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - Task reads and external chat notices must remain responsive as run
history grows.
> - The chat notice sweep casts indexed UUID columns to text.
> - Run redaction and task provenance filter large JSON contexts with
expressions that lack matching indexes.
> - These queries scan history and read large stored contexts on common
requests.
> - This pull request adapts two existing fixes to current master and
adds the missing provenance index.
> - The benefit is less database work with the same query results and
access rules.

## Linked Issues or Issue Description

Refs #15927. This change adapts that chat sweep fix to the extracted
inbound notice module and retains the contributor credit.

Refs #12499. This change adds that missing nested-context index through
a current, concurrent migration. It retains the contributor credit.

This combined change replaces those older layouts for the measured query
paths. It also covers task creation provenance, which those pull
requests do not address.

**What happened?**

The chat notice sweep scans wakeup history. Issue redaction scans run
contexts because only one branch of its JSON condition has an index. The
task creation-source filter also scans contexts because its native and
legacy ID expression lacks an index.

**Expected behavior**

The queries use indexed lookups and return the same rows. Company
access, task origin precedence, secret redaction, and chat notice
ordering remain the same.

**Steps to reproduce**

Run `pnpm --filter @paperclipai/server exec tsx
../scripts/benchmark-slow-queries.ts --baseline-ref=f8af817f8d`. The
benchmark creates disposable PostgreSQL data and compares the old query
with the current query on the same fixture.

## What Changed

- Compare chat UUID columns directly with safely parsed payload IDs.
Reject malformed and noncanonical IDs without a cast failure.
- Add the nested `paperclipIssue.id` run-context index and the exact
native/legacy task-provenance expression index.
- Build both indexes concurrently. Register the migration for top-level
execution and invalid-index retry repair.
- Extend chat payload and migration regression coverage.
- Add a repeatable synthetic SQL benchmark and document its scope and
command.
- Keep the five newest schema snapshots.

## Verification

- Focused local checks: 76 selected tests passed in five files. These
cover FIFO notices, malformed IDs, conversation state, redaction,
provenance, and company boundaries.
- Disposable PostgreSQL 18.1 fixture: 335 tasks, 1,400 runs, 13,000
wakeups, 377 actions, and 192 KiB synthetic contexts.
- Five warm samples per query, after two warmups. Every returned SQL
column matched before and after, compared as positional tuples so
duplicate unnamed projections are retained.

| Query | Before, median | After, median | Improvement |
| --- | ---: | ---: | ---: |
| Chat notice candidates | 26.21 ms | 6.43 ms | 4.1× |
| Task creation-source list | 159.69 ms | 6.51 ms | 24.5× |
| Issue redaction contexts | 131.88 ms | 0.34 ms | 389.1× |

The run-history context projection returned the same 200 rows and kept
the same index plan and buffer counts. Its timings were noisy (37.30 ms
before and 56.78 ms after in the last run; 34.25 ms and 34.14 ms in the
previous run). No improvement is claimed for this control. Its repeated
JSON projection remains a separate optimization opportunity.

The populated plans use the wakeup primary key, the provenance
expression index, and a BitmapOr over both redaction indexes. These are
local SQL measurements. They do not prove an equivalent change in hosted
API latency.

- `pnpm -r typecheck` and `pnpm build` passed locally.
- The full local `pnpm test:run` was started and then interrupted (exit
130) when the Mac reached its PostgreSQL shared-memory limit. It did not
complete and is not reported as passed. The final SQL benchmark passed
after its disposable database resources were released. The full general
and serialized test suites passed in CI.
- All CI checks for final head
`193acca6abaa992acad12f4b631bd31dd3234641` passed, including full Vitest
coverage, E2E checks, typecheck, build, migration order, and lifecycle
boundaries.
- Greptile reviewed that exact head at 5/5 with no actionable comments.


## Risks

- Both indexes add storage and write cost. Initial builds read stored
run contexts and can take time on large histories.
- Concurrent index builds avoid holding a write-blocking table lock for
the whole build. The existing migration retry path removes invalid
partial indexes before retrying.
- The chat parser accepts only canonical lowercase UUID text. This
preserves the prior text comparison, including rejection of uppercase
and malformed values.
- Staging has not been migrated or deployed. Local PostgreSQL and
synthetic data do not reproduce hosted load or network latency.

## Model Used

- OpenAI Codex, based on GPT-6. The exact serving model ID and context
window were not exposed in this session. Used reasoning, repository
tools, browser inspection, and local code execution. The adapted fixes
retain their original contributor credits.

## Checklist

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 07:37:51 -05:00
DottaandPaperclip c80d93e14e fix(observability): time execution configuration steps (#16236)
Record optional execution-configuration timings before provider dispatch, with success, failure, sampling, cleanup, and silent-unconfigured controls.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-14 07:21:56 -05:00
DottaandPaperclip 6cb6ac3b8e fix(github): allow review publication to finish before tool timeout (#16275)
## Thinking Path

> - Paperclip manages agent work and its published results.
> - GitHub review bots publish task-scoped assessments through the tool
gateway.
> - A submission saves its assessment and publication intent before it
waits for GitHub.
> - The gateway's ten-second default can expire while those provider
requests continue.
> - GitHub can then show a completed review while the agent receives a
timeout and blocks its task.
> - This pull request gives GitHub bot tools the existing sixty-second
maximum by default.
> - The caller can receive the confirmed publication result without
manual task repair for ordinary slower responses.

## Linked Issues or Issue Description

**What happened?**

A GitHub bot's `submit_review` call returned `504 tool_timeout`. Its
saved assessment continued to publish a summary and completed check
after the caller stopped waiting. The agent treated publication as
unconfirmed and blocked the task.

**Expected behavior**

A valid GitHub response that takes longer than ten seconds should return
its confirmed publication receipt within the gateway's supported
deadline.

**Steps to reproduce**

1. Configure a report-only GitHub review bot and admit a PR review
request.
2. Delay the summary-comment PATCH response by eleven seconds.
3. Submit a complete assessment through a real run-scoped tool gateway.
4. Before this fix, the caller gets a timeout while the durable action
publishes one summary and a neutral check.
5. With this fix, the caller gets `completed`, `processed`, and the
assessment score. Retrying does not add another comment.

**Paperclip version or commit**

Reproduced from master at `2c17d3ca52a7840f280f1dd87975a2ad77de3b35`.

Related changes: Refs #16259 and #16263. Those changes repaired
connection recovery. This change addresses the subsequent submission
deadline.

## What Changed

- Use a sixty-second default for `paperclip_github_chat` tools in run
execution and board tool tests.
- Preserve explicit shorter deadlines and the existing maximum.
- Add PostgreSQL-backed regressions through the real gateway for delayed
publication, explicit deadlines, neutral checks, and duplicate
prevention.
- Document the deadline and the durable publication behavior.

## Verification

- Red: the eleven-second publication regression failed with
`ToolGatewayHttpError(504, tool_timeout)` after the summary and neutral
check were published.
- Green: the same regression passed after the repair.
- GitHub review workflow suite: 122 tests passed.
- `pnpm -r typecheck`: passed.
- `pnpm build`: passed.
- `pnpm test:run`: running; results will be recorded before handoff.
- Live local verification is running against a dedicated test App on
[the disposable test
PR](https://github.com/paperclipai/paperclip-permissions-smoke-20260926-pap57-fee7428e/pull/9).
A test-only preload delays one real GitHub PATCH response by eleven
seconds. It is not part of this change.

## Risks

GitHub bot calls can occupy their execution slot for up to sixty seconds
by default. Calls that exceed that limit can still time out. Durable
publication may continue after any caller timeout, so a timeout alone is
not proof of a failed write. This change does not add automatic task
reconciliation for arbitrarily slow or unavailable providers. No schema
or credential changes are required.

## Model Used

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 06:52:33 -05:00
DottaandPaperclip 2c17d3ca52 fix(github): recover webhook verification in attention state (#16263)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - GitHub review bots require signed webhook delivery before
activation.
> - Cloud can deduplicate an old ping after a connection repair.
> - A fresh event could verify active connections, but an
attention-state connection discarded it before authentication.
> - This pull request allows signed delivery verification while the
connection needs attention.
> - It discards ordinary work until the manager completes the existing
reconnect and activation checks.

## Linked Issues or Issue Description

Refs #16259. Related: #16138 addresses the separate classification of
local authorization failures.

**What happened?**

Live verification of #16259 found that a GitHub App already marked as
needing attention remained stuck waiting for a signed ping. The Cloud
relay had already delivered that ping. New signed events were discarded
by the attention-state ingress gate before they could verify delivery.

**Expected behavior**

A fresh valid event from the bound installation verifies delivery. It
does not activate the connection, change repository access, or start
work. The manager can then complete the normal reconnect flow.

**Steps to reproduce**

1. Put an existing App connection into attention state with webhook
verification cleared after repair.
2. Deliver a fresh signed comment event for its bound installation.
3. Continue setup. Before this fix, webhook verification remains missing
and reconnect cannot proceed.

## What Changed

- Use a content-free verification path for signed ordinary GitHub events
while the connection needs attention.
- Allow a fresh signed event from the same installation to verify
webhook delivery.
- Verify delivery even from disabled or unavailable repositories.
Discard the event before content storage, resource reconciliation, or
runtime creation.
- Keep newly verified connections in the provider-setup step. Later
installation events cannot activate them before reconnect. Preserve
normal installation recovery for existing qualified connections.
- Extend recovery regressions to cover attention state, foreign
installations, invalid signatures, missing installation IDs, unchanged
resources, no wakeups, and successful reconnect after verification.
- Document the recovery behavior.

## Verification

- The targeted regression reproduced the attention-state failure before
the change.
- All ten recovery cases pass after the change. The tests cover disabled
and unavailable repositories, content-free verification, and
installation events after verification.
- Server typecheck passed.
- All 260 GitHub integration tests passed after the review fixes. Final
server typecheck also passed.
- `pnpm -r typecheck` and `pnpm build` passed.
- The full local suite is running. One unrelated workspace-runtime
conflict-reference test failed and then passed on its isolated rerun.
Final totals and CI/review outcomes will be updated before merge.
- Live verification will use the existing advisory review bot after
deployment.

## Risks

- Attention-state ordinary events authenticate under the current
credential lease without creating a durable content record. They return
before resource reconciliation or runtime creation.
- Verification is still bound to the current credential lease, runtime
generation, valid signature, and installation ID. It does not activate
the bot or replace normal setup checks.
- Paused, revoked, archived, and unconfigured connections retain their
existing ingress rules. No schema changes or permission expansion.

## Model Used

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

---------

Co-authored-by: Paperclip <noreply@paperclip.ing>
2026-10-14 05:23:42 +00:00
DottaandPaperclip c36281d639 fix(skills): repair missing branches and show grouped import progress (#16256)
## Thinking Path

> - Paperclip lets people manage AI agents and their reusable skills.
> - GitHub skill sources must keep their installed content usable during updates.
> - A source can track a branch that no longer exists.
> - The UI treats that GitHub error as a connection problem, so saving a valid account does not resolve it.
> - Project workspaces also show the same repository several times, and long imports give little feedback.
> - This pull request adds explicit branch repair, groups repository imports, and shows import progress.
> - People can repair the actual problem and see when their import is working.

## Linked Issues or Issue Description

Refs #16066 and #14713. This fixes the Sources experience introduced by those changes. Related adapter-source PRs #14340 and #14341 cover runtime mounting, not this source-management flow. No overlapping source-management fix was found.

**What happened?**

An installed source tracked `main` while its GitHub repository used `master`. GitHub returned HTTP 422. The UI showed a general read error and asked for a new connection after a valid account was saved. The project repository list repeated the same repository/ref for each project. Import had no spinner or explanation of queued work.

**Expected behavior**

Identify an unavailable branch and offer an explicit repair. Retain the selected connection and installed versions. Show one repository/ref row with its projects. Give immediate feedback and show the queued and active import states.

**Steps to reproduce**

1. Install a source with an immutable commit and a saved tracked branch that does not exist.
2. Refresh it, or try to save a valid GitHub connection.
3. Add the same repository/ref to two legacy project workspaces and open Sources.
4. Import that repository and observe the row until installation completes.

## What Changed

- Distinguish a missing GitHub revision from access denial and temporary read errors.
- Validate both repository identity and the tracked revision when saving a connection.
- Offer Use default branch on independent sources and in connection repair. Keep `HEAD` as the default-branch alias after refresh. Preserve skill IDs, versions and selections.
- Keep active project refs under project configuration. Reject source-level changes that would alter their tracking. Recheck the ref under the source lock when project attachment races with branch repair.
- Group matching repository/ref workspaces. Adopt eligible workspaces and skip pending workspaces. Keep remaining project links inside the existing source card. Allow remaining imports while another workspace is pending.
- Show a spinner, busy state and queued/importing text. Keep the saved authorized account selected in connection repair.
- Update shared contracts, feature/access maps, database notes and production-component Storybook journeys.

## Verification

- Red/green: three local regressions failed on the original code: missing-ref classification, duplicate repository rows, and branch repair instead of another connection prompt. The changed UI/reader tests pass after the fixes.
- Live local app with a real authorized GitHub credential: reproduced HTTP 422 for the unavailable branch; used the UI to restore default-branch tracking; confirmed the same connection, skill ID and single immutable version remained.
- Live local grouped Paperclip import: one source, two project associations and 52 installed skills. Sources survived reload. No staging data was changed.
- All 16 project repository Storybook browser journeys pass on the final head, including keyboard focus recovery after branch repair. These include branch repair, grouped imports, connection selection, recovery, removal and responsive layouts.
- Repository typecheck, production build, Storybook build, token gates and 28 map/catalog tests passed. Final UI typecheck and build also passed after grouping remaining project links.
- Linux qualification: all 94 focused source, project import, GitHub reader and UI tests pass with supported Node 24. No database tests were skipped. Both review findings failed before their fixes and pass after them.
- The full local `pnpm test:run` attempt was stopped after macOS database suites exhausted the host shared-memory limit. The complete Linux CI matrix is the full-suite gate. The complete Linux CI matrix passes for head `f886e3ac073ec1c6f051fd6429496234f5143694`.
- Greptile rates head `f886e3ac073ec1c6f051fd6429496234f5143694` 5/5. Both findings are fixed and all review threads are resolved.
- Storybook: `skills-project-repository-skills-02-journeys--missing-branch` and `--duplicate-projects`.

## Risks

- Default-branch repair changes the tracked branch only after an explicit user action. Existing execution version pins stay intact.
- A selected connection now needs access to the tracked revision, not only repository metadata.
- Grouping is by repository and ref. Project access and provenance remain separate and are checked by the existing services.
- Legacy source errors contain safe text. The repair-action classifier maps known errors and treats other failures as retryable. No credentials or provider response bodies are returned.
- No schema migration, workflow change, lockfile change or staging deployment.

## Model Used

OpenAI Codex, GPT-6 family. The exact serving model identifier and context-window size are not exposed in this session. Tool-assisted implementation, code execution 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

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 22:27:11 -05:00
DottaandPaperclip a8b652b085 fix(github): preserve review delivery through reconnect (#16259)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - GitHub review bots turn authorized repository events into agent
tasks and publish the result on the pull request.
> - Cloud Apps send callbacks through a relay, but the direct callback
recovery worker reported that URL as broken.
> - Reconnecting the same App then prevented a new authorized
publication from updating its existing working comment.
> - Report-only runs could also produce an action-required check when no
complete assessment was available.
> - This pull request preserves comment identity across reconnects,
verifies fresh signed delivery after repair, and keeps report-only
results neutral.
> - Users can receive the review without changing repository access or
making an advisory bot block their pull request.

## Linked Issues or Issue Description

**What happened?**

A review could be saved in Paperclip while GitHub kept showing “Working
on this…”. A same-App reconnect changed the runtime generation and the
existing response receipt then failed with “GitHub response binding
changed”. Cloud callback recovery also compared the relay URL with a
direct tenant URL. An incomplete or failed report-only run could produce
an action-required check.

**Expected behavior**

A fresh authorized attempt updates the same App-owned comment after
reconnect. Stale attempts and revoked people remain denied. Cloud relay
delivery does not produce a direct-callback mismatch. Report-only checks
remain neutral and explain incomplete work honestly.

**Steps to reproduce**

1. Accept a GitHub request and publish its working comment.
2. Reconnect the same App before the agent publishes its result.
3. Submit the result and inspect the working comment and check.
4. Separately, let a report-only run end without an assessment and
inspect its check conclusion.

Related: #16138 addresses the separate classification of local
authorization errors as provider credential failures. This change fixes
the response binding and report-only paths themselves.

## What Changed

- Bind durable response comments to the App and agent. Keep per-attempt
runtime generation checks and provider comment ownership checks,
including legacy receipts.
- Exclude Cloud-managed Apps from the direct webhook recovery scan and
its stale callback-mismatch warning. The Cloud inbox owns relay retries.
- Let a fresh authenticated event from the bound installation verify
webhook delivery after repair. Keep signature, runtime epoch,
installation, and credential lease checks.
- Keep report-only assessment and terminal-run checks neutral. Preserve
incomplete/error text and gated-review behavior.
- Add integration regressions for reconnect races, legacy comments,
failed runs, Cloud recovery, and foreign or forged webhook traffic.
Update connector documentation.
- Correct the assigned-tool search guidance to name the actual input
fields (`offset` and `schemaOffset`) and show valid arguments. The
failed automatic review had an input-schema rejection before tool
execution; this clarifies recovery without expanding tool access.

## Verification

- `pnpm -r typecheck` passed.
- GitHub policy and integration suite: 291 tests passed (`vitest run
server/src/services/chat-github-review-policy.test.ts
server/src/__tests__/chat-channels.integration.test.ts -t
'GitHub|github'`).
- The report-only policy regression failed before the fix and passed
after it.
- `pnpm build` passed.
- Assigned tool discovery: 24 tests passed. Its guidance now maps
returned pagination offsets to the correct input fields and tells the
model to omit unused optional fields.
- Full `pnpm test:run` is running. It encountered a 15-second timeout in
the unrelated authenticated Workspace calendar-write test. That test
passed on one isolated rerun.
- After the review-requested module extraction, server typecheck and
both direct/Cloud recovery activity integration checks passed.
- All 55 non-skipped CI checks passed for
`32e690279d847e44a6946b6c7cfb110fb11aff79`. Greptile scored that commit
5/5 with no unresolved findings. Live staging publication verification
follows deployment.

## Risks

- A durable response can now survive a same-App reconnect. Fresh
authorization, runtime generation checks for each publication attempt,
App ownership, and the publication lease still fence writes.
- Report-only failures intentionally become neutral. The check still
reports the failure; it does not claim a complete assessment.
- Cloud-managed callbacks rely on their existing durable relay inbox.
The direct callback recovery worker remains unchanged for direct
installations.
- No schema migration or permission expansion.

## Model Used

OpenAI Codex (GPT-6), with code inspection, editing, test execution, and
browser verification. The exact deployment 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
- [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 22:24:41 -05:00
DottaandPaperclip 40ecacff4c fix(voice): accept authenticated Cloud callers (#16254)
## Thinking Path

> - Paperclip manages agent work through company-scoped tasks.
> - Speko voice connections bind a caller to that work.
> - Private voice routes must recognize an authenticated human caller.
> - Cloud sign-in uses a trusted Cloud actor, rather than a local
session actor.
> - The voice caller check rejected that existing authentication type.
> - This pull request accepts authenticated Cloud callers and retains
the current access checks.
> - Cloud users can load call history and start private voice sessions.

## Linked Issues or Issue Description

Refs #15727, #15728, #15729.

**What happened?**

A signed-in Cloud user sees “Sign in to start a private voice
conversation” when the Speko connection loads call activity. The same
check prevents the user from starting a private voice session.

**Expected behavior**

The trusted Cloud user identity should have the same voice caller access
as a local authenticated session. The current company and user identity
checks must still apply.

**Steps to reproduce**

1. Sign in to a Cloud workspace with experimental chat connectors
enabled.
2. Connect a Speko persona to an existing agent.
3. Open its call activity, or start a browser voice session.
4. Observe the sign-in rejection despite the authenticated workspace
session.

**Paperclip version or commit**

Reproduced on the voice route in master at b8ffeb973a.

**Deployment mode**

Paperclip Cloud with trusted tenant authentication.

## What Changed

- Accept `cloud_tenant` and `session` actors with a user ID as private
voice callers.
- Use current company membership for Cloud owners, including owners with
temporary administrator access. Retain local administrator authority,
company isolation, and credential-source restrictions.
- Add route regressions for call history, session creation,
administrator authority, missing identities, company boundaries, and
rejected caller types.
- Add Cloud authentication qualification steps to the Speko runbook.

## Verification

- Before the fix: 2 Cloud regressions failed; 9 boundary and local-user
tests passed.
- After the fix: all 100 voice caller and real-database persistence
regressions passed. The Cloud-owner route test covers history, session
creation, persisted caller authority, and membership revocation.
- Repository typecheck and build passed with the repository’s pinned
dependencies.
- All current-head CI gates passed. Greptile reviewed f84fee2605 at 5/5
with no remaining findings. The repaired local full repository test run
is still in progress. The first local full-suite attempt failed on
missing native database library aliases; those aliases were repaired
only in the isolated test dependency. No skipped run is counted as
database proof.
- On Cloud, verify that signed-in call activity loads and private
session creation reaches media setup. Real conversation and spoken
playback acceptance remain separate.

## Risks

The change accepts an existing trusted Cloud actor type. Authentication
still runs in the current middleware. Raw Cloud headers, board API keys,
MCP OAuth, Cloud control credentials, and agent callers cannot establish
private voice caller authority. No migration, UI change, credential,
transcript, or lockfile is included.

## Model Used

OpenAI Codex, GPT-6 family, with tool use and code execution. The exact
serving variant 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 21:39:56 -05:00
DottaandPaperclip b8ffeb973a feat(runner): add experimental Grok Bot agent participation (#16219)
## Thinking Path

> - Paperclip manages AI agents that perform work for a company.
> - The shared Runner owns task admission, authority, receipts and
finalization.
> - An existing Grok Bot needs to receive assignments and return results
through that Runner.
> - The Bot also needs governed access when it starts work from its own
conversation.
> - This PR adds agent-purpose MCP access and routine webhook dispatch.
> - It reuses the external-agent Rust Runner and keeps Grok capabilities
explicit.
> - The provider stays experimental until the real-client managed
journeys pass.

## Linked Issues or Issue Description

**Agent or provider**
Grok Bot, distinct from the Grok Build coding runtime in #13882.

**Why this adapter is useful**
Users can assign work to an existing Bot, receive progress, questions,
documents and files in Paperclip, and let the Bot initiate work under
its agent permissions.

**How the agent is invoked**
A webhook-only routine delivers bounded references. Dedicated
named-agent OAuth and MCP provide inbound calls. Setup follows [Grok
routines](https://cursor.com/help/grok-bot/routines) and [custom
plugins](https://cursor.com/help/grok-bot/connect-plugins). The current
desktop app needs its Bot-generated Webhook URL navigation link when
routine-row clicks fail.

The GitHub search found related Grok Build runtime work, but no existing
Grok Bot agent provider. This extends the roadmap’s existing agent,
Runner, governance and artifact foundations.

## What Changed

- Add default-off Grok Bot participation through the shared real Rust
external backend, preserving native provider identity guarantees and
distinguishing session closure from remote termination.
- Bind agent OAuth to company, agent, client, gateway and authorizer;
recheck permissions and task-bound execution authority without
manufacturing a native identity.
- Persist routine configuration, dispatch intents, authenticated
acknowledgement and recovery holds. Store URL/key together in encrypted
secret storage, use guarded public egress, and never automatically
replay ambiguous HTTP delivery.
- Reuse native tools, durable waiting, completion assessment and review.
Add task-bound file grants through existing artifact storage; suppress
proactive echo dispatch and synchronous result wake loops.
- Enforce the current native deliverable work-mode policy at file-ticket
issuance and use. Add operator retries for confirmed rejected wakes,
with retained receipts, stable retry intent and current-authority
checks. Recovery locks bindings before routine rows. Thread Stop
resolves the selected run and its agent.
- Cancel queued and preparing Stop targets before assignment creation,
restore saved setup after a failed request and successful retry, and
publish the Grok provider descriptor schema. Advance the versioned
Copilot source declaration for the shared schema change; its
qualification remains pending.
- Filter task summaries with the shared privacy policy, preserve
feedback admission when wake delivery is blocked, reconcile settled
delivery receipts without restoring work authority, and bind every Stop
control to its displayed run. Recovery records distinguish dispatch
uncertainty from a revoked session.
- Add resumable guided setup, credential replacement, consent,
management, observed work status and stop/recovery controls, plus an
interactive Storybook journey using the production task thread, question
form and report card. Grok’s own apps remain available; Paperclip
workspace and third-party tools are deferred.

## Verification

- Five focused protocol, authorization, person MCP and Runner files: 192
tests pass. The Grok assigned-work case executes the actual Rust Runner,
saves a document, uploads/deduplicates a file and reaches ordinary
finalization.
- Proactive intake/deduplication and renewed-session recovery pass.
These use scripted clients and synthetic storage; they are not
real-provider acceptance.
- Repository typecheck, production build, token gates, map checks and
Storybook build/catalog checks pass. The local full-suite campaign was
stopped after macOS shared-memory exhaustion repeatedly prevented
embedded PostgreSQL startup. No local aggregate pass is claimed. The
first clean-worker CI run identified avatar fixture/configuration, flag
defaults, feature-card ordering and OpenAPI route-coverage gaps; all
four focused regressions pass after correction. Those regressions pass.
The latest clean-worker CI run also found a Copilot source-hash
declaration mismatch after the shared descriptor update; the corrected
declaration passes all 28 integrity tests. The next review corrections
pass 144 rendered UI tests, 21 OpenAPI tests and 82 Runner
contract/integrity/history tests. The latest database fixtures were
blocked at startup by host shared-memory exhaustion; clean-worker CI
must execute their new regressions. Full current-head CI now passes: 54
successful checks and four intentional skips. The database regression
uses the PostgreSQL driver result shape; file-policy, rejected-feedback
and lock-order cases pass on clean workers. Current-head Greptile is 5/5
with no unresolved findings.
- Rendered setup steps and outbound-only verification were walked
through in Storybook. Simulation cannot enable Done without an
authenticated-reply state.
- Actual client OAuth, continued access, reconnect and one routine wake
with an exact chat acknowledgement were observed separately. This
candidate’s live authenticated challenge, assigned/proactive workflows,
Linux managed artifact, restart and cleanup are not yet qualified. The
provider stays disabled by default.
- The separate Cloud ingress change forwards only exact task-bound
upload/download paths and preserves host identity and header stripping.
- Current Cloud policy reserves Test for pre-merge app/harness
experiments; Staging accepts only master-derived revisions. Real-client
qualification needs a supported, authorized deployment target and
remains outstanding.

## Risks

One append-only migration adds routine/dispatch state and extends shared
bindings and file tickets. The unshipped migrations were regenerated
after upstream consumed their original numbers. Rolling back code
retains the durable rows; revoke grants and inspect unknown remote
activity before resuming dispatch. Grok cannot confirm remote
termination, stream its complete transcript or report spending. Plugins
are account-wide and do not isolate credentials between Bots.
Real-client managed acceptance remains required before enabling the
experimental provider.

## Model Used

OpenAI Codex based on GPT-6, with reasoning, code execution and
browser/native UI tools. The exact deployment model ID and
context-window size are not exposed by this runtime.

## Checklist

- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [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 20:57:42 -05:00
DottaandPaperclip 450d93d49d fix(auth): deduplicate run history permission checks (#16242)
Share matching run-history permission checks within one list operation while preserving current authorization, redaction, and fresh checks on subsequent requests.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 20:37:48 -05:00
Devin FoleyandPaperclip 5ea3f905cf fix: preserve workspace finalization during watchdog recovery (#16247)
Keep task completion separate from runtime and workspace settlement. Permit verified board file-only repair of successful legacy retained-source runs without authorizing replay.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 18:35:27 -07:00
9cc979116e fix(server): stop reporting queries that outlive the database shutdown (#16243)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work.
> - The server reports unexpected route errors to Sentry through the
shared error handler.
> - An orderly shutdown drains the HTTP listener, and then it ends the
database pool.
> - `server.close()` completes when the sockets close. A route handler
whose client disconnected, or whose socket the drain timeout closed, can
still run.
> - The next query of that handler fails with postgres.js
`CONNECTION_ENDED`, because this process ended the pool. The error
handler reports this as a 500 crash.
> - This pull request marks the pool as closing just before it ends. The
error handler then skips the error sinks for that one error code during
shutdown.
> - Public MCP calls run in their own internal Express app, so the flag
is also shared with that app.
> - The benefit is that a normal restart does not create false Sentry
crash reports. Real errors are still reported.

## Linked Issues or Issue Description

No public GitHub issue exists. This is the bug description.

**What happened?**

A staging server restarted. During the restart, `GET
/api/companies/:companyId/heartbeat-runs` failed in the per-row run
privacy check (`canActorReadHeartbeatRun`). The error was `Failed query:
select "id" from "issues" ...`. Its cause was `write CONNECTION_ENDED
<host>:5432`. postgres.js raises this code only for a query that starts
after the process called `sql.end()`. The error handler sent the error
to Sentry as a crash.

**Expected behavior**

A query that runs after the shutdown ends the pool is an expected part
of the shutdown. The server must not report it as a crash. If the
response is still open, the server answers `503`. The server must
continue to report all other errors.

**Steps to reproduce**

1. Start the server, and send a slow request (for example, a large
heartbeat run list).
2. Close the client connection, or let the 5 second drain timeout close
it, while the handler still runs.
3. Send SIGTERM. The shutdown drains HTTP and ends the pool.
4. The handler's next query fails with `CONNECTION_ENDED`. Before this
change, the error goes to Sentry.

## What Changed

- `server/src/database-retry.ts`: Add `isEndedDbPoolError`. It finds the
postgres.js `CONNECTION_ENDED` code anywhere in the error `cause` chain.
`isTransientDbConnectionError` now uses the same chain walk. Its
behavior does not change.
- `server/src/index.ts`: The orderly shutdown sets
`app.locals.paperclipDatabaseClosing = true` immediately before it ends
the database pools.
- `server/src/middleware/error-handler.ts`: When the flag is set and the
error is `CONNECTION_ENDED`, the handler answers `503 { error: "Server
is shutting down" }`, or it ends the response if the headers are already
sent. It does not call Sentry or crash telemetry. This follows the
existing client-abort (`ECONNRESET`) case.
- `server/src/services/public-mcp/capabilities.ts` and
`server/src/app.ts`: `createMcpApiDispatch` takes the main app's locals.
Each per-call dispatch app reads `paperclipDatabaseClosing` through a
live getter, so a public MCP call that outlives the pool is handled the
same way (Greptile review finding).
- `server/src/__tests__/error-handler.test.ts`: Add tests for the 503
path, the headers-sent path, `CONNECTION_ENDED` outside shutdown (still
reported), and `CONNECTION_CLOSED` during shutdown (still reported).
- `server/src/__tests__/public-mcp-dispatch-shutdown.test.ts`: Send an
ended-pool error through the real MCP dispatch, both during shutdown
(503, not reported) and outside shutdown (reported).

## Verification

- `cd server && vitest run
src/__tests__/public-mcp-dispatch-shutdown.test.ts
src/__tests__/error-handler.test.ts
src/__tests__/cloud-tenant-transient-db-retry.test.ts`: 25 tests pass.
- With only the error-handler change reverted, the two new error-handler
shutdown tests fail. With only the dispatch change reverted, the MCP
shutdown test fails. The negative-control tests pass in both cases.
- I could not run the full `pnpm -r typecheck`, `pnpm test:run`, and
`pnpm build` locally, or `public-mcp.test.ts`, which needs built
workspace packages. The sandbox has a 4 GB memory limit. CI runs the
full gates.

## Risks

- Low risk. The skip applies only to `CONNECTION_ENDED`, and only after
the shutdown starts to end the pools. postgres.js raises that code only
for a pool that this process ended. Network and database failures
(`CONNECTION_CLOSED`, `CONNECTION_DESTROYED`, and others) are still
reported.
- `CONNECTION_ENDED` outside shutdown (for example, a dedicated client
used after it ended) is still reported as a crash.
- The MCP dispatch app gets only the shutdown flag from the main app's
locals. It does not get any other locals.
- No schema, API contract, or UI change.

## Model Used

Anthropic Claude Opus 5.5 (`claude-opus-5-5`) in Claude Code, with tool
use (repository inspection, shell, test execution) and the Sentry and
GitHub connectors.

## Checklist

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

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

---------

Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-13 18:26:03 -07:00
ba4fac585d feat(agents): show who created an agent in shared companies (#16055)
## Thinking Path

> - Paperclip is the open source app people use to manage AI agents for
work
> - A company in Paperclip can have more than one human member, and each
member can create agents
> - The app does not record which person created an agent
> - When two or more people share a company, members cannot see who
added an agent or who to ask about it
> - This pull request records the creator of each agent and shows the
creator on the agent Overview tab
> - The benefit is clear ownership of agents in shared companies, with
no change for single-user companies

## Linked Issues or Issue Description

No public issue exists. This description follows `feature_request.yml`.

**Subsystem affected**

Agents: the agent data model, the agent create path on the server, and
the agent Overview tab in the board UI.

**Problem or motivation**

Paperclip does not record which human user created an agent. When more
than one human user works in a company, the agent page does not tell
members who created the agent.

**Proposed solution**

Record the user ID of the creator when a human creates an agent. Show
"Created by <name>" on the Identity card of the agent Overview tab. Show
this row only when the company has two or more human members. Do not
count the synthetic local Board principal as a human member. Do not show
the local Board as a creator.

**Alternatives considered**

Count human users across the full instance. We did not do this. An
instance-wide count tells a member of one company that other companies
have users. The creator is always a member of the agent's company, so
the company count is sufficient.

**Roadmap alignment**

`ROADMAP.md` has no item for agent ownership. This change is small and
additive.

## What Changed

- Add the nullable column `agents.created_by_user_id`
(`packages/db/src/schema/agents.ts`, migration `0343_new_mentor.sql`).
- Set the column in the single agent insert path
(`server/src/modules/agent-lifecycle/adapters/records.ts`). The value
comes from the authenticated user actor. The server ignores a creator
value in the request body. Agents that agents or the system create keep
`null`.
- Backfill existing agents in the migration. The backfill uses the
earliest `agent.created` or `agent.hire_created` activity row that a
user actor wrote in the same company.
- Add `createdByUserId` to the shared `Agent` type
(`packages/shared/src/types/agent.ts`).
- Add `ui/src/lib/agent-creator.ts`. It decides if the UI shows the
creator row and it resolves the creator name. If the creator left the
company, the row shows "Former member".
- Add the "Created by" row to the Identity card in
`ui/src/pages/AgentDetail.tsx`. If the company user directory request
fails, the row shows "Could not load creator". The row does not
disappear silently.
- Add the Storybook story `Pages/Agent Detail → Shared Company Shows
Creator`.
- Document the column in `doc/SPEC-implementation.md` §7.2.

## Verification

- `pnpm --filter @paperclipai/db typecheck` passes. This includes the
migration numbering and migration safety checks.
- `pnpm --filter @paperclipai/shared typecheck` passes.
- `ui`: `tsc --noEmit` passes.
- `server`: `vitest run
src/modules/agent-lifecycle/__tests__/lifecycle.test.ts` passes (49/49).
Two new tests:
- The server records the creator from the actor and ignores a creator
value in the request body.
- The backfill takes the earliest user activity row. It skips agent
actors and rows from other companies.
- `server`: `vitest run src/__tests__/built-in-agents.test.ts` passes
(40/40). A new test checks that a built-in agent enabled by a human
records that human, and that automatic provisioning stays unattributed.
The gated (pending-approval) provisioning test also asserts the creator.
`src/__tests__/built-in-agent-routes.test.ts` also passes.
- `ui`: `vitest run src/lib/agent-creator.test.ts
src/pages/AgentOverview.test.tsx` passes (10/10). The tests cover these
cases: the row shows with two or more humans, the row stays hidden with
one human plus the Board, the UI never shows the Board as the creator,
the row shows an error when the directory request fails, and the row
stays hidden (no error) for agents with no creator or the Board as
creator. The last case fails without the fix.
- The current Storybook catalog uses source annotations and generated
indexes. `node scripts/storybook-catalog.mjs --check` passes (264 story
files, 2,514 stories). The merge keeps the authored catalog deletion
from master.
- `pnpm check:token-gates` is clean.
- Screenshots from the Storybook story (fake data: two human members,
Alice Admin and Bob Builder, plus the local Board). The red "Could not
load public key" text is a Storybook artifact: the story has no fixture
for the public key request. The existing `Dashboard` story shows the
same text.

  **Two or more human members: the Identity card shows "Created by".**

![Identity card with the Created by
row](https://raw.githubusercontent.com/paperclipai/paperclip/9b62bd2ea6cf984ce6745f08cc45f44ddf00fb51/pr-16055/identity-created-by.png)

  **One human member plus the local Board: the row stays hidden.**

![Identity card without the Created by
row](https://raw.githubusercontent.com/paperclipai/paperclip/9b62bd2ea6cf984ce6745f08cc45f44ddf00fb51/pr-16055/identity-single-human.png)

Full Overview tab:
[overview-created-by.png](https://raw.githubusercontent.com/paperclipai/paperclip/9b62bd2ea6cf984ce6745f08cc45f44ddf00fb51/pr-16055/overview-created-by.png).
The images are on the separate branch `pr-assets/agent-created-by`. They
are not in this PR and do not go to `master`.
- Manual check: open Storybook at `Pages/Agent Detail → Shared Company
Shows Creator`. The Identity card shows "Created by Alice Admin". The
`Dashboard` story has one human, so it does not show the row.
- Final focused verification on
`5a02350cf16050a3cf3cb3f8dc061fe61758c60d`: all 99 tests pass across the
two UI suites, built-in-agent suite, and lifecycle suite. The backfill
test applies the complete migration twice. The migration uses `ADD
COLUMN IF NOT EXISTS` and preserves the company-scoped backfill.
Migration numbering and token checks pass.
- Full repository typecheck, build, and Storybook build pass. The
generated creator story ID is present. A Chromium check renders the
production agent page fixture and verifies the creator row and Alice
Admin name. Map checks and their 21 tests pass. A fresh Apex review is
5/5 on the current head with no unresolved findings. All 54 successful
CI checks pass on this head, with two intentional skips, including all
general and serialized test shards, E2E, typecheck, and build. The
workspaces-b job passed after rerunning a runner shutdown; no source
change was needed. The separate, unsharded local `pnpm test:run`
invocation has not completed; complete suite verification comes from CI.

## Risks

- Migration: the migration adds one nullable column and runs one
`UPDATE` on `agents` with a correlated subquery on `activity_log`. On an
instance with a very large activity log, the backfill can take some
time. The migration does not lock other tables and does not delete data.
- Backfill accuracy: agents with no user-authored create activity row
keep `null`. The UI then does not show a creator. This is the same as
the current behavior.
- Privacy: the row shows only names from the company user directory, and
only to users who can already see that directory.
- Low risk for single-user companies: the UI does not change for them.

> 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

- Provider: Anthropic, Claude Opus 5.5 (model ID `claude-opus-5-5`), run
as a coding agent through Claude Code with tool use (shell, file edit,
and test execution), standard context window.

OpenAI GPT-6 (`gpt-6`) through Codex assisted conflict resolution,
regression tests, and repository verification. This 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

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

---------

Co-authored-by: Amy (Opus) <noreply@anthropic.com>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: devinfoley <139239+devinfoley@users.noreply.github.com>
2026-10-13 14:57:41 -07:00
Harjoth KharaandClaude Opus 5 be3e614d61 fix(workspace-runtime): keep the real readiness failure when the deadline-clamped probe aborts (#11575)
## Thinking Path

> - Paperclip manages AI agents and the services they use for work.
> - An agent can wait for a workspace service to become ready.
> - Each readiness probe has a time limit.
> - A final probe can expire and hide an earlier HTTP failure.
> - This pull request keeps that observed failure in the reported error.
> - Operators can then see why the service was not ready.

## Linked Issues or Issue Description

Refs #11525. Related: #11494.

**What happened?**

A deadline-limited final readiness probe replaced an observed HTTP 503
with an abort message.

**Expected behavior**

Keep an earlier service failure when only the readiness deadline aborts
the last probe. Report normal connection failures and probe timeouts as
before.

**Steps to reproduce**

Return HTTP 503 from the first probe. Leave one millisecond in the
readiness budget. Abort the last probe. The reported error must still
contain HTTP 503.

## What Changed

- Track whether a readiness probe observed a service failure.
- Preserve that observation when the final probe expires at the
readiness deadline.
- Add a deterministic regression with an injected clock.
- Keep probe scheduling, request count and timeout limits unchanged.

## Verification

- Author verification: the readiness suite passed all nine tests; server
typecheck passed. The regression failed before the fix and passed after
it.
- The later task review recorded 172 passing workspace tests and seven
additional readiness checks. Historical author baseline failures are not
claimed as passing local runs.
- Latest head `936a374a08d0e74d87d588ba034f674388e8d508`: CI passed with
54 successful checks and two intentional skips. Fresh Apex review is 5/5
with no unresolved findings or recommendations.
- A test merge with current master is clean.

## Risks

- Error text changes when a service first fails and then hangs. The
earlier observed failure is retained.
- A hang without a prior service failure still reports the abort.
- No data model, API or permission change.

## Model Used

Anthropic Claude Opus 5 (`claude-opus-5`) authored the fix through
Claude Code with reasoning, file editing and test tools. OpenAI GPT-5.6
assisted the original diagnosis. OpenAI GPT-6 (`gpt-6`) through Codex
assisted this final review and PR preparation with repository and
terminal tools. This 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: Claude Opus 5 <noreply@anthropic.com>
2026-10-13 14:25:09 -07:00
DottaandPaperclip 473c80d13f Automatically import and update project repository skills (#16066)
Automatically import skills from new GitHub project repositories and update enabled sources in the background. Preserve project-derived access, saved selections, immutable execution versions and explicit pins; add legacy adoption, refresh and connection repair in Sources.

Include durable scheduling and bounded work credits, production Storybook journeys, real GitHub-to-routine acceptance receipts and final-head CI/security/Greptile qualification.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 16:11:43 -05:00
Devin FoleyandPaperclip de335b42d4 Expose signed routine wake deadlines during idle sleep checks
Expose signed routine wake deadlines during idle sleep checks

Report the earliest eligible routine deadline to an authenticated host scheduler, while preserving local execution, owned sleep leases and conservative blocking for unsupported work.

Verified with focused tests, typecheck, build, all required CI checks and Apex 5/5.

Co-Authored-By: Paperclip <noreply@paperclip.ing>
2026-10-13 14:09:16 -07:00
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
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
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 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
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
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
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
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