## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agents often work from reviewed plans that are approved through
issue-thread interactions.
> - Accepting a plan is not just a UI decision; it must reliably resume
the assignee so approved work continues.
> - A failed continuation wake could leave an approved plan stranded in
review with no durable retry or visible recovery path.
> - This pull request makes approved plan continuations retryable,
recoverable, and visible when resume fails.
> - The benefit is that operators can trust plan approval to either
resume the agent or produce an explicit actionable failure instead of
silent limbo.
## Linked Issues or Issue Description
No public GitHub issue exists. Inline bug report follows the repository
bug 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?
When a plan-confirmation interaction was accepted, the assignee
continuation wake could fail before useful agent execution. In that case
the issue could remain in review even though the plan had been approved,
because the failed wake was fire-and-forget and there was no durable
retry or recovery path for accepted continuations.
### Expected behavior
Accepted plan continuations should either wake the assignee
successfully, retry bounded infrastructure failures, recover dropped
wakes, or surface an explicit failure state that operators can act on.
### Steps to reproduce
1. Create an issue with an assignee and a plan confirmation that wakes
the assignee on accept.
2. Accept the confirmation.
3. Simulate a pre-flight continuation failure, such as process loss
before agent start or workspace validation failure.
4. Observe that the approved issue can remain in review without an
active assignee wake or visible retry/failure state.
### Paperclip version or commit
Reproducible on `master` before this PR's retry/recovery changes.
### Deployment mode
Local dev (`pnpm dev`) and server-side recovery paths.
### Installation method
Built from source (`pnpm install`, `pnpm dev`, test runner).
### Agent adapter(s) involved
Not adapter-specific; this is a core continuation/recovery bug. The
tests cover local-agent failure shapes without relying on a
provider-specific API.
### Database mode
Embedded Postgres test database for verification. The affected logic is
database-backed and applies to normal Postgres deployments as well.
### Access context
Board accepts the interaction; agent execution resumes through the
assignee wake path.
### Relevant logs or output
No sensitive logs are needed. The regression tests simulate the failed
wake and recovery states directly.
### Relevant config
No special config is required beyond an assignee with wake-on-demand
enabled.
### Additional context
This PR also prevents a stale workspace-validation payload from
quarantining another issue's active workspace and prevents unrelated
successful runs from masking a continuation that never resumed.
### Privacy checklist
- [x] I have reviewed all pasted output for PII, usernames, file paths,
API keys, tokens, and company names, and redacted where necessary.
## What Changed
- Added bounded infrastructure retries for failed accepted-interaction
continuation wakes.
- Extended stranded issue recovery so dropped accepted-plan continuation
wakes are requeued.
- Recorded and rendered explicit resume-failure state on accepted
confirmation cards.
- Added clean-workspace fallback for workspace-validation failures while
preventing cross-issue workspace quarantine.
- Tightened recovery so unrelated successful runs do not mask an
accepted continuation that never resumed.
- Added focused server/UI coverage for retry scheduling, recovery,
visible failure state, and interaction card rendering.
## Verification
- `pnpm vitest run
server/src/__tests__/heartbeat-retry-scheduling.test.ts
server/src/__tests__/heartbeat-process-recovery.test.ts` — 106 tests
passed.
- Earlier branch verification also covered the issue-thread interaction
card tests for the visible resume-failure UI.
- GitHub CI is green on the replacement PR head, and Greptile reports
5/5 with no blocking issues.
## Risks
- Medium behavioral risk: this changes recovery behavior for accepted
continuation interactions and workspace-validation retries.
- Mitigation: retries are bounded, scoped to same-company issue context,
and workspace quarantine now requires ownership by the issue being
retried.
- Existing stored confirmation results remain compatible because the new
resume-failure field is optional.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5 coding agent, tool-enabled terminal workflow. The
runtime does not expose an exact context-window value to the agent.
## 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Local CLI adapters can run against Paperclip-managed execution
environments instead of only the host filesystem.
> - The environment picker and environment capability API derive sandbox
support from shared adapter capability lists.
> - The Grok Build adapter is implemented as a local CLI adapter, but it
was missing from those shared environment capability lists.
> - That made Grok agents look local-only even when sandbox environments
were configured.
> - This pull request registers `grok_local` in the shared adapter
constants and remote-managed environment support path.
> - The benefit is that Grok Build agents can select the same local,
SSH, and sandbox environment overrides as other local CLI adapters.
## Linked Issues or Issue Description
No public GitHub issue exists for this bug. Inline bug report follows.
### Pre-submission checklist
- [x] I have searched existing open and closed issues and this is not a
duplicate.
- [x] I am on `master`.
- [x] I have confirmed the error originates in Paperclip itself, not in
the Grok adapter provider or local configuration.
### What happened?
When configuring a Grok Build local agent in the board UI, Paperclip did
not expose configured sandbox environments as selectable environment
overrides. The shared environment capability helper treated `grok_local`
as local-only because it was missing from the remote-managed local
adapter allowlist.
### Expected behavior
Grok Build should behave like other local CLI adapters: when
environments are enabled and a runnable sandbox environment exists, the
agent configuration form should show the environment override selector
and allow the sandbox to be selected.
### Steps to reproduce
1. Enable environments in instance experimental settings.
2. Configure at least one runnable sandbox environment.
3. Open the agent configuration form for a Grok Build local agent.
4. Observe that the sandbox environment is not offered as an override
before this fix.
### Paperclip version or commit
`master` before this PR.
### Deployment mode
Local dev (`pnpm dev`).
### Installation method
Built from source (`pnpm dev` / `pnpm build`).
### Agent adapter(s) involved
- Grok Build local adapter.
- Core bug in shared environment capability logic.
### Database mode
Not database-related.
### Access context
Board human operator.
### Relevant logs or output
No runtime error is emitted; the issue is a missing UI option caused by
shared capability metadata.
### Relevant config
No secret-bearing config required. Reproduction only needs environments
enabled and a runnable sandbox environment configured.
### 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
- Added `grok_local` to the shared built-in adapter type list.
- Added `grok_local` to the remote-managed adapter set used by
environment capability helpers.
- Added shared regression coverage for Grok local sandbox provider and
driver support.
- Added a UI render regression test that confirms Grok Build agents show
the environment override when a runnable sandbox exists.
## Verification
- `git diff --check`
- Changed-file secret scan with `rg` for common token/key patterns.
- `pnpm --filter @paperclipai/shared exec vitest run
src/environment-support.test.ts`
- `pnpm --dir ui exec vitest run
src/components/AgentConfigForm.render.test.tsx`
## Risks
- Low risk. This expands environment support for an existing local
adapter to match the local CLI adapter behavior already used by Claude,
Codex, Gemini, OpenCode, Cursor, and Pi.
- Operators still need at least one configured runnable sandbox
environment before a Grok agent has a sandbox option to select.
- This PR was created from a Paperclip execution workspace branch whose
name is runtime-provided; the PR body intentionally avoids internal
issue identifiers or instance-local links.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
- OpenAI Codex, GPT-5 coding agent, tool-enabled terminal/code execution
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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board currently uses issues for execution, but longer-lived
content work needs a separate object that can survive beyond a single
task thread.
> - The Cases subsystem adds an experimental, company-scoped record for
content artifacts and their supporting metadata.
> - The backend needs durable storage, API routes, revision history,
issue linkage, and company-boundary enforcement before the UI can depend
on Cases.
> - The UI needs an opt-in navigation surface, list/detail views,
reference chips, and issue-page context so operators can inspect Cases
without making them the default workflow.
> - The agent-facing skills need a contract for creating and updating
Cases so automated content workflows can dogfood the feature.
> - This pull request ships that experimental end-to-end path behind the
`enableCases` flag.
> - The benefit is a first-class place to collect content work,
references, attachments, revisions, and related execution threads
without polluting the core issue model.
## Linked Issues or Issue Description
No public GitHub issue exists for this experimental feature.
Feature request fields:
### Problem
Content-oriented work such as release notes, announcements, docs, and
campaigns can span many execution issues, which makes the final artifact
hard to find and reason about after the execution thread moves on.
### Proposed solution
Add an experimental Cases object that is company-scoped, linked to
issues, queryable through the API, inspectable in the board UI, and
writable by agent workflows through documented conventions.
### Alternatives considered
Continue encoding content artifacts directly in issues or documents
only. That keeps the data model smaller, but it does not give operators
a stable artifact-centric view or a clean way to link related execution
history.
### Roadmap alignment
Checked `ROADMAP.md`; this PR does not duplicate an existing planned
core roadmap item.
## What Changed
- Added the `cases` data model, migration, schema exports, and
experimental `enableCases` instance setting.
- Added company-scoped Cases API routes for list/detail/update, issue
links, revisions, children, activity events, annotations, attachments,
and idempotent agent-oriented upserts.
- Scoped case and issue lookup helpers before access checks so
inaccessible cross-company identifiers resolve as not found rather than
leaking existence.
- Fixed case PATCH timestamp handling so non-status updates cannot
overwrite `completedAt` from a stale pre-transaction row snapshot.
- Moved Cases list type/status/project filters into the server request
before the server-side limit is applied, including multi-select filters
and no-project filtering.
- Added backend route coverage for creation, updates, idempotency, issue
linking, attribution, company-boundary enforcement, OpenAPI
registration, list filtering, timestamp patch behavior, and inaccessible
lookup regressions.
- Added the experimental Cases UI surface: sidebar entry, gated routes,
list filters/grouping, detail overview, activity, revisions, children,
attachments, and issue-page case rail.
- Added case reference rendering and company-prefixed case href
generation so case links resolve directly inside the active company
route.
- Added Paperclip skill documentation for agent workflows that create or
update Cases.
- Wired release-content skills to emit Cases for dogfooding.
- Rebased onto current `master` and renumbered the Cases migrations to
`0143`/`0144` after the latest upstream migration sequence.
## Verification
- Current PR head: `ecc13be0d`.
- Rebased on current `master` (`606aa4f266`) and pushed to the existing
PR branch.
- `git diff --check origin/master...HEAD` — passed before the first
update push; subsequent committed diffs were also checked with `git diff
--check` before commit.
- Guardrails checked: no `pnpm-lock.yaml` changes, no
`.github/workflows` changes, and changed-file count is below the
Greptile 100-file limit.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/cases-routes.test.ts
src/__tests__/instance-settings-service.test.ts
src/__tests__/openapi-routes.test.ts` — passed, 3 files / 26 tests
before review-fix commits.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/cases-routes.test.ts` — passed after each server-side
Greptile fix, latest 1 file / 15 tests.
- `pnpm --filter @paperclipai/server typecheck` — passed after the
timestamp and lookup fixes.
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/Cases.test.tsx src/pages/CaseDetail.test.tsx
src/pages/CompanySkills.test.tsx src/App.cases-routing.test.tsx` —
passed, 4 files / 30 tests before review-fix commits.
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/Cases.test.tsx` — passed after the list-filter fix, 1 file /
12 tests.
- `pnpm --filter @paperclipai/ui typecheck` — passed after the
list-filter fix.
- `pnpm check:token-gates` — passed after UI changes.
- Remote PR checks on head `ecc13be0d` are green: Paperclip CI, build,
typecheck, test matrix, e2e, Canary Dry Run, policy, commit review,
Superagent Security Scan, Socket, Snyk, and Greptile passed; Storybook
visual regression is skipped and security-review is neutral.
- Greptile Review: 5/5 confidence, zero unresolved Greptile threads.
## Risks
- Medium feature risk because this introduces a new experimental domain
object across database, server, shared contracts, skills, and UI.
- The feature is gated behind `enableCases`, which limits default
operator exposure while the model is exercised.
- Case links now prefer company-prefixed hrefs; the unprefixed redirect
remains for externally entered URLs.
- Cases list filtering now sends multi-select filters to the server
before limiting; the UI still applies the same local filters as a second
pass for ancestor/context rows.
- Migrations were renumbered on top of current master; the SQL uses
guarded `IF NOT EXISTS` / `ADD COLUMN IF NOT EXISTS` patterns where
relevant for safer replay.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI GPT-5 Codex in the Paperclip local coding environment was used
for this PR curation, rebase verification, review-fix implementation,
push, and PR description update. The runtime exposes tool use and shell
execution; context-window size is not exposed by this Paperclip adapter.
Several implementation commits also include AI co-author trailers
recorded in git history.
## 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 searched the GitHub PR list for similar PRs and confirmed this
is not a duplicate
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [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: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Company search is the primary way operators find issues, comments,
documents, artifacts, agents, and projects across a busy company
> - Search previously supported only a bare text query: no way to narrow
by status/assignee/project/label/date, no sort control, no typed
operators, and weak relevance/snippets meant hunting through noise
> - As companies accumulate tens of thousands of items, unfiltered
single-sort search stops scaling for day-to-day operator workflows
> - This pull request adds a full filtering model (filter bar, chips,
mobile sheet, URL state), sort modes, typed query operators (`status:`,
`assignee:`, `type:`, …) with command-palette parity,
relevance/snippet/deep-link improvements, zero-results recovery, and the
supporting shared validators, backend service work, and DB indexes
> - The benefit is that operators can go from a vague query to the exact
item in a couple of keystrokes, on desktop and mobile, with shareable
filtered-search URLs
## Linked Issues or Issue Description
No existing public GitHub issue; describing the underlying feature
request inline (per feature_request template):
- **Problem:** Company search accepted only a plain text query. Users
could not filter results by status, assignee, project, label, or
recency; could not change result ordering; and got no guidance when
filters emptied the result set.
- **Desired solution:** Structured search filters (UI controls + typed
query operators + URL parameters), selectable sort modes, better
relevance and snippets with exact deep links, and parity between the
search page and the command palette.
- **Alternatives considered:** Client-side filtering of unfiltered
results (does not scale past the fetch limit); a separate "advanced
search" page (splits the surface and duplicates state handling).
Related (not duplicate) PRs found while searching: #4848 (issue search
query planning), #8235 (search rate limiting).
## What Changed
- **Shared contract:** new search filter/sort/count/zero-results types
and validators in `packages/shared` (`validators/search.ts`, types
index).
- **Backend:** `server/src/services/company-search.ts` supports issue
filters, sort modes, per-filter option counts, snippets, artifact
visibility, and zero-results loosen suggestions; single-statement match
replaces per-scope scans and predicates are trigram-index compatible
(~3.7s → ~350ms on a live 14.8k-hit corpus).
- **DB:** migration `0142_company_search_sort_indexes.sql` adds the
supporting indexes.
- **Search page (`ui/src/pages/Search.tsx`):** filter bar, removable
chips, mobile filter sheet with result-count preview, sort menu, URL
round-tripping, zero-results recovery UI.
- **Query operators (`ui/src/lib/search-query-parser.ts`):** typed
operators parsed into filters, operator autocomplete, filter pills.
- **Command palette:** operator-aware parsing and full-search handoff.
- **Stale-operator fix (latest commit):** typed operator filters are no
longer folded into persistent URL-filter state, so deleting a token
(e.g. removing `status:blocked` from the input) actually removes the
filter from subsequent requests; filter-control edits materialize
control state and strip typed tokens so a removed chip cannot resurrect
from the input.
## Verification
- `cd ui && npx vitest run src/pages/Search.test.tsx` — 19 tests
including two new red→green regressions for the stale-operator paths
(both fail on the previous commit, pass now).
- `cd ui && npx vitest run src/components/CommandPalette.test.tsx` and
`cd server && npx vitest run
src/services/company-search-service.test.ts` — operator parity and
backend filter/sort/count coverage.
- `cd ui && npx tsc --noEmit` — clean.
- Manual: open `/search`, type `auth status:blocked`, confirm the status
filter applies; delete `status:blocked`, confirm results are unfiltered
again; drive the same filters from the filter bar/chips/mobile sheet and
confirm the URL round-trips (reload/back/forward preserves state).
- Full end-to-end QA pass (9/9 acceptance checks) against the wireframes
on desktop (1280px) and mobile (390px) with a live API and browser
automation.
## Risks
- Additive migration (indexes only, no data rewrites) — safe to roll
forward; index creation cost is paid once at migrate time.
- Search request shape gains optional parameters only; old clients keep
working.
- Behavioral shift: filter-control edits now strip typed operator tokens
from the query text (their values persist as filter state) — deliberate,
so removed filters stay removed.
- Ranking changes alter result ordering for existing queries; covered by
service tests and the QA 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
- Claude Fable 5 (`claude-fable-5`, Anthropic, extended thinking + tool
use) — stale-operator-filter fix, regression tests, PR preparation.
- GPT-5 Codex (`codex_local` adapter) and Claude Opus 4.6
(`claude-opus-4-6`) — earlier implementation phases (backend contract,
filter UI, operators, ranking) under agent orchestration.
## 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)
- [ ] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
— pre-existing branch name retained to avoid closing/reopening the PR
- [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 (no
user-facing docs affected)
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green (pending re-run on latest commit)
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
(pending re-review of the stale-filter fix)
- [x] I will address all Greptile and reviewer comments before
requesting merge
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Execution workspaces are the bridge between Paperclip's control
plane and a local agent's checked-out repository state.
> - When a workspace is restored after a failed or interrupted run, the
recorded branch can disagree with the branch currently checked out on
disk.
> - A clean branch mismatch can be reconciled safely, but a dirty
mismatch needs a lossless path that does not discard uncommitted agent
work.
> - This pull request adds a quarantine-and-restore path that saves
dirty work to a rescue branch, restores the recorded branch, and exposes
the repair from the board UI and run page.
> - The benefit is that operators can recover wedged execution
workspaces without losing work or moving another live branch
unexpectedly.
## Linked Issues or Issue Description
No public GitHub issue exists for this workspace-recovery failure, so
this PR includes the bug report inline.
**What happened**
A git worktree-backed execution workspace could become wedged when
Paperclip expected one branch but found a different checked-out branch
with dirty tracked or untracked files. The existing safe repair path
refused the restore, leaving the source task blocked with no lossless
one-click recovery path.
**Expected behavior**
Paperclip should preserve dirty work before restoring the recorded
workspace branch. If another live workspace claims the checked-out
branch, or an attached runtime service is active, the repair should
refuse with clear operator-facing evidence instead of risking work loss
or file contention.
**Steps to reproduce**
Create a git worktree execution workspace whose persisted branch name
differs from the checked-out branch, add dirty tracked or untracked
files in that worktree, then trigger workspace validation or use the
branch reconcile endpoint. Before this change, the dirty mismatch
remained blocked because Paperclip had no quarantine restore mode.
**Paperclip version or commit**
Observed on the pre-fix workspace-recovery implementation. Verified on
this PR head after rebasing onto current `master`.
**Deployment mode**
Local trusted development/worktree deployments using git worktree
execution workspaces and optional workspace runtime services.
## What Changed
- Added dirty-worktree quarantine repair that creates a rescue branch,
commits dirty tracked and untracked files there, restores the recorded
branch, writes audit comments/activity, and preserves the live foreign
branch ref.
- Added `quarantine_restore` branch reconcile API support,
recovery-action resolution, source-task wake behavior, execution-review
preservation, claimant refusal, runtime-service refusal, and coverage
for the non-transactional git ordering.
- Added board UI controls for the repair action in the recovery card
plus a compact failed-run workspace recovery surface that uses the same
reconcile handlers.
- Hardened Greptile follow-up cases by best-effort restoring the
recorded branch after a mid-sequence rescue commit failure and by
refusing quarantine restore while attached runtime services are active.
## Verification
- `pnpm exec vitest run
server/src/__tests__/execution-workspaces-service.test.ts -t
"quarantine_restore"`
- `pnpm exec vitest run server/src/__tests__/workspace-runtime.test.ts
-t "workspace dirty quarantine branch repair"`
- `pnpm exec vitest run
server/src/__tests__/heartbeat-workspace-finalize-branch.test.ts -t
"repairs clean unrecorded branch drift|adopts unrecorded forward branch
drift"`
- `pnpm exec vitest run
server/src/__tests__/workspace-runtime-routes-authz.test.ts`
- `pnpm --filter @paperclipai/server typecheck`
- Earlier PR verification covered the route, service, heartbeat, UI
component, and run-page recovery surfaces; Cutter posted public preview
screenshots for the repair popover and run-page panel at
https://github.com/paperclipai/paperclip/pull/9297#issuecomment-4926934211.
- GitHub PR checks are green on
`dcac76b05f4cf6e1ee16544c2831d83c7857e475`.
- Greptile is 5/5 with zero annotations and no unresolved review threads
on `dcac76b05f4cf6e1ee16544c2831d83c7857e475`.
## Risks
- Moderate risk because the change intentionally runs git commands
against local worktrees; the implementation refuses dirty repair when
another claimant or active runtime service is detected and records
rescue refs for auditability.
- Compatibility / release-note callout for self-hosted operators:
existing instances that left `enableWorkspaceBranchReconcileForward`
unset now get automatic forward branch reconciliation during heartbeat
workspace recovery. Operators who want the previous advisory-only
behavior can set `experimental.enableWorkspaceBranchReconcileForward` to
`false`; dirty quarantine repair can likewise be disabled with
`experimental.enableWorkspaceDirtyQuarantineRepair: false`.
- If the git rescue succeeds but a later database write fails, the
worktree may already be restored while the recovery action remains open;
this ordering is documented in code because git side effects cannot
participate in the database transaction.
- UI risk is limited to the workspace recovery surfaces and covered by
component tests plus the existing Cutter visual preview.
## Model Used
OpenAI Codex coding agent based on GPT-5, with repository tool use,
shell execution, and local test execution. Earlier preserved commits on
this branch also show Claude Code / Claude Opus 4.8 assistance in their
commit metadata.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] Branch naming exception documented: this PR preserves the existing
worktree branch requested for publication while keeping the PR title and
body public-facing
- [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: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The board UI keeps issue, agent, activity, and run state fresh
through polling across several pages and sidebar surfaces
> - When multiple components or browser tabs poll the same company data
at the same time, the API can receive bursts of duplicate issue-list
requests
> - Those duplicate requests increase database and server load without
returning meaningfully different data
> - This pull request adds server-side compression/coalescing plus
client-side visibility-aware and cross-tab shared polling
> - The benefit is lower request volume during normal board usage while
preserving fresh UI data for active users
## Linked Issues or Issue Description
No exact public GitHub issue was found for this request.
Problem:
- The board can issue redundant polling requests for the same issue-list
data from multiple UI surfaces and tabs.
- In busy operator sessions, those bursts can trigger request-storm
behavior and unnecessary issue-list load.
- Expected behavior is to reuse identical in-flight work server-side and
reduce hidden-tab or duplicate-tab polling client-side while preserving
normal refresh behavior.
Related public context found during duplicate search:
- #8206 covers a different board UI 404-storm scope.
- #5165 covers separate issue-list behavior around page-size truncation.
## What Changed
- Added API compression middleware foundation and a company/created-at
index for heartbeat run access.
- Added server-side issue-list request storm detection and identical
in-flight request coalescing.
- Added UI fetch metadata, visibility-aware polling, and request
deduplication for issue/activity/client calls.
- Added cross-tab shared polling primitives and wired them into the
sidebar, inbox, dashboard, issue, project, routine, and agent surfaces.
- Resolved the latest `master` migration collision by keeping upstream
`0140_built_in_managed_resources.sql` and renumbering this branch's
heartbeat-run index migration to
`0141_heartbeat_runs_company_created_at_index.sql`; the SQL uses `CREATE
INDEX IF NOT EXISTS` for idempotency.
- Stabilized server heartbeat cleanup tests exposed by the PR check
matrix.
- Fixed the Greptile compression follow-up by weakening strong ETags on
encoded JSON responses and bypassing compression for streamed/download
responses.
## Verification
- `pnpm exec vitest run ui/src/components/IssuesList.test.tsx
ui/src/pages/Inbox.test.tsx` — passed after resolving the latest
`master` conflict in `IssuesList.tsx` and updating the 200-result cap
expectations.
- `pnpm check:token-gates` — passed after the UI conflict resolution.
- `jq -e '.entries | length as $n | (map(.idx) | unique | length == $n)
and (map(.tag) | unique | length == $n)'
packages/db/src/migrations/meta/_journal.json` — passed after
renumbering the migration to `0141`.
- `pnpm exec vitest run server/src/__tests__/api-compression.test.ts` -
passed after the compression follow-up.
- `pnpm exec vitest run
server/src/__tests__/issue-list-assignee-filter-routes.test.ts` - passed
after the compression follow-up.
- `pnpm --filter @paperclipai/server typecheck` - passed after the
compression follow-up.
- Greptile Review for head `8dbddac41ec273fda404100b4981ddb912fad57b` -
passed after the latest conflict/migration fix; all Greptile review
threads are resolved.
- `pnpm exec vitest run server/src/__tests__/api-compression.test.ts
server/src/__tests__/issue-list-assignee-filter-routes.test.ts
ui/src/api/client.test.ts ui/src/api/issues.test.ts
ui/src/lib/polling.test.ts ui/src/lib/cross-tab-poll.test.ts
ui/src/pages/Inbox.test.tsx ui/src/components/SidebarProjects.test.tsx
ui/src/components/SidebarAccountMenu.test.tsx
ui/src/lib/issueDetailCache.test.ts` — passed.
- `pnpm exec vitest run ui/src/api/client.test.ts` — passed.
- `pnpm exec vitest run ui/src/pages/Inbox.test.tsx
ui/src/components/SidebarProjects.test.tsx
ui/src/components/SidebarAccountMenu.test.tsx
ui/src/lib/issueDetailCache.test.ts` — passed.
- `pnpm exec vitest run
server/src/__tests__/heartbeat-worktree-suppression.test.ts` — passed.
- `pnpm exec vitest run
server/src/__tests__/low-trust-red-team-routes.test.ts` — passed.
- `pnpm exec vitest run
server/src/__tests__/heartbeat-workspace-finalize-branch.test.ts` —
passed.
- `pnpm --filter @paperclipai/ui typecheck` — passed.
- `pnpm --filter @paperclipai/server typecheck` — passed.
- GitHub PR checks for head `8dbddac41ec273fda404100b4981ddb912fad57b`:
all GitHub Actions/status checks passed; Greptile, Superagent, Socket,
Snyk, build, typecheck/release registry, general tests, serialized
server suites, e2e, canary dry run, policy, and commitperclip review are
green; Storybook visual regression and security-review were
skipped/neutral by policy.
- Confirmed this branch does not include `pnpm-lock.yaml` or
`.github/workflows` changes.
## Risks
- Medium risk: issue-list coalescing changes request timing and cache
semantics for a hot API path.
- Medium risk: cross-tab polling uses browser coordination primitives,
so older or unusual browser environments need fallback behavior to stay
correct.
- Low migration risk: the new index migration is ordered after current
`master` and uses `CREATE INDEX IF NOT EXISTS`.
> 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 coding agent, GPT-5-based model, tool-enabled with
shell/git execution. Exact hosted deployment identifier and
context-window size were not surfaced in the agent runtime.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Operators need first-party agent capabilities for repeatable company
work, not just manually created one-off agents.
> - Built-in agents need to behave like normal company-scoped agents
while preserving approval gates, permissions, budgets, and audit trails.
> - Reflection and coaching work also needs bundled instructions, skill
content, and a routine so the feature can be installed and reset
predictably.
> - The API, database, UI, portability, and tests all need to agree on
the built-in lifecycle from not provisioned through setup, approval,
ready, paused, and reset.
> - This pull request adds built-in agent provisioning and the
Reflection Coach bundle end-to-end.
> - The benefit is a safer first-party path for Paperclip-managed agents
without bypassing the same governance model used for operator-created
agents.
## Linked Issues or Issue Description
No public GitHub issue was found for this exact built-in agent and
Reflection Coach bundle work.
Problem/motivation:
- Paperclip did not have a first-party built-in agent lifecycle for
product-owned agents.
- Bundled agent resources such as default instructions, skills, and
routines needed managed ownership and reset semantics.
- Approval-gated companies needed built-in setup to preserve requested
adapter, budget, manager, and permission state through board approval.
- The board UI needed clear built-in badges, setup affordances,
readiness state, and bundle status without exposing secrets.
Proposed solution:
- Add a company-scoped built-in agent registry,
provisioning/reset/reconcile/status APIs, and Reflection Coach bundled
resources.
- Track bundled managed resources in the database with idempotent
migration behavior.
- Reuse existing agent approval, authorization, budget, and activity-log
paths instead of creating a bypass.
- Add UI setup, badges, gates, bundle panels, and route coverage for
built-in agents.
Duplicate search:
- Searched GitHub PRs for `built-in agents Reflection Coach
repo:paperclipai/paperclip`; only this PR was returned.
- Searched GitHub issues for the same query; no public issues were
returned.
## What Changed
- Added built-in agent definitions, lifecycle state derivation,
provisioning, reset, reconcile, status, and routine-control routes.
- Added the `built_in_managed_resources` migration and schema exports
for bundled instructions, skill, and routine ownership.
- Added the Reflection Coach built-in bundle with default instructions,
skill catalog content, routine template, default permissions, and
managed-resource drift handling.
- Added approval-aware provisioning behavior that preserves requested
adapter config, budgets, manager assignment, and built-in permissions
through hire approval.
- Added authorization and mutation gates for built-in agent and skill
changes, including consented Reflection Coach change paths.
- Added UI surfaces for built-in agent setup, roster/detail badges,
readiness gates, bundle status, routine controls, and route filtering.
- Added company import/export and validator coverage for built-in
managed resources and low-trust/red-team presets.
- Addressed Greptile follow-ups for pending approval reconciliation,
consent-gate error propagation, config-read authorization fallback,
approval-path manager preservation, and non-model adapter provisioning.
## Verification
Local verification:
- `git diff --check public/master..HEAD` passed.
- `pnpm check:token-gates` passed with all gates clean.
- `pnpm exec vitest run
ui/src/components/ConfigureBuiltInAgentModal.test.tsx` passed: 1 file, 4
tests.
- `pnpm exec vitest run ui/src/components/EntityRow.test.tsx
ui/src/pages/Agents.test.tsx ui/src/components/BuiltInAgentGate.test.tsx
ui/src/components/ConfigureBuiltInAgentModal.test.tsx
ui/src/components/BuiltInBundlePanel.test.tsx
ui/src/pages/InstanceExperimentalSettings.test.tsx
ui/src/pages/Routines.test.tsx` passed: 7 files, 64 tests.
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/built-in-agents.test.ts
src/__tests__/authorization-service.test.ts
src/__tests__/company-skills-routes.test.ts` passed: 3 files, 91 tests.
- `pnpm --filter @paperclipai/db check:migrations` passed.
- `pnpm -r typecheck` passed after the rebase; `pnpm --filter ui
typecheck` passed after the final UI review fix.
Remote verification on latest head
`1c61f693a4ec881d739022b0e75a8ca8bf8c2cd8`:
- Merge state: `CLEAN`.
- Greptile: `5/5`, zero unresolved Greptile threads.
- PR check rollup: all checks successful, neutral, or skipped as
expected.
- Passing gates include Build, Typecheck + Release Registry, all server
shards, all workspace shards, all serialized server suites, e2e, Canary
Dry Run, policy, review, verify, Socket, Superagent, and Snyk.
## Risks
- This adds a new managed-resource table and migration; the migration
uses idempotent create/add/index guards and passed migration safety
checks.
- Built-in agent provisioning touches approval and authorization paths;
tests cover pending approval preservation, stale retry rejection,
consent gates, and config-read fallback behavior.
- Reflection Coach creates managed instructions, skill, and routine
resources; drift/reset behavior is covered by service tests and redacted
API responses.
- Non-model adapter setup now provisions a `needs_setup` built-in row
before command/endpoint fields are complete; this matches the server
lifecycle and is covered by the setup modal regression test.
## Model Used
OpenAI Codex coding agent based on GPT-5. Exact hosted model ID,
context-window size, and reasoning-mode labels are not exposed in this
runtime; tool use, shell execution, GitHub CLI/API access, and local
code editing were enabled.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The Skills Manager gives operators a reusable skill layer, but
iteration still required manual edits, ad hoc prompts, and indirect run
inspection.
> - Skill authors need a focused workflow for editing skill files,
saving representative test inputs, and running those inputs through an
agent without exposing harness tasks as normal company work.
> - The backend therefore needs durable test inputs, reusable run
templates, hidden harness issues, scoped run execution, retention
metadata, and read-containment rules around hidden work.
> - The frontend needs a three-pane Studio that keeps skill files, saved
inputs/templates, and run output/history visible together while
preserving the existing design system and token rules.
> - This pull request ships that Skill Studio surface end to end:
database migrations, shared contracts, server APIs/services, hidden
harness execution behavior, UI routes/components, and focused tests.
> - The benefit is faster and safer skill iteration, with inspectable
outputs and fewer ways for internal harness work to leak into normal
task lists, costs, or adjacent read APIs.
## Linked Issues or Issue Description
No public GitHub issue exists for this feature. Feature request summary:
- Problem: Skill authors need to edit and test company skills in one
place instead of switching between the skill detail page, task creation,
run output, and manual prompt history.
- Proposed solution: Add a Skill Studio workbench with saved inputs,
reusable templates, hidden sandboxed test runs, live run status, output
inspection, run history, rerun/delete controls, and frontmatter-aware
editing.
- Expected users: Paperclip operators and agent-company maintainers who
create, fork, import, and tune skills.
- Related public PRs: Supersedes #9205, which was replaced so the public
PR branch name follows contributor policy.
- Duplicate search: searched public GitHub issues and PRs for "Skill
Studio"; no other active public issue or PR directly covers this
feature.
## What Changed
- Added database migrations for Skill Studio test inputs, test runs,
test run retention, and reusable run templates.
- Added shared Skill Studio types, validators, route helpers,
frontmatter utilities, and status handling.
- Added server services and routes for saved inputs, test runs,
templates, reruns, terminal-run deletion, hidden harness issue
execution, and run-detail hydration.
- Strengthened hidden-issue read containment across issue-adjacent
routes and cost rollups used by skill test harness work.
- Added the Skill Studio UI with skill file editing, frontmatter
editing, saved inputs, templates, run creation/cancel/rerun/delete
flows, output rendering, history, route support, and responsive pane
behavior.
- Added focused backend, shared, and UI tests for the new APIs, routing
logic, editor/run behavior, hidden-issue containment, and migration
safety.
- Rebased onto current `master`, removed the generated lockfile diff
from the PR, and verified no workflow files are changed.
## Verification
- [x] `pnpm --filter @paperclipai/db check:migrations`
- [x] `pnpm check:token-gates`
- [x] `pnpm exec vitest run
server/src/__tests__/company-skills-service.test.ts
server/src/__tests__/company-skills-routes.test.ts
server/src/__tests__/company-skill-test-runs-service.test.ts
ui/src/lib/skill-studio.test.ts ui/src/pages/SkillStudio.test.tsx` — 5
files, 132 tests passed
- [x] Greptile review on the latest PR head
- [x] GitHub PR checks on the latest PR head
## Risks
- Medium risk because this is a broad feature touching database schema,
server orchestration, issue visibility, and a large UI surface.
- Hidden harness issue containment is security-sensitive; this PR
includes regression coverage for adjacent read paths and cost rollups.
- The new migrations are additive and use idempotent guards where
applicable, but deployed databases that previously tested draft
migration numbers should still be checked carefully.
- The UI depends on a new resizable panels package in `ui/package.json`;
the lockfile is intentionally left to repository automation.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5 coding agent with shell, git, and GitHub CLI tool
use. Earlier feature commits include assistance from other Paperclip
coding agents; this PR preparation, rebase, cleanup commit, and PR body
were completed by OpenAI Codex in a Paperclip worktree.
## 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: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip manages AI agents that each have an associated execution
environment (local, Kubernetes, etc.)
> - Instance administrators can create and delete environments;
currently the DELETE endpoint has no protection against deleting managed
or in-use environments
> - Deleting the managed local environment or the instance-default
environment would break all agents using those environments with no path
to recovery
> - The endpoint also suffered a TOCTOU race: a check-then-delete
pattern allowed the managed-local or default guard to pass if the
environment's role changed between the read and the delete
> - This pull request adds a blast-radius read endpoint so admins can
preview impact, hard-blocks the dangerous deletes atomically, cleans up
all dependent references after a valid delete, and fixes a concurrent
creation race in ensureLocalEnvironment
## Linked Issues or Issue Description
Fixes#9251
## What Changed
- **New endpoint** `GET /api/environments/:id/delete-blast-radius`
(instance-admin gated): returns reference counts (agent defaults,
workspace selections, issue selections, project selections, secret
bindings, active leases, active setup sessions) and blocking reasons —
no config, env-var values, or secret data returned.
- **Atomic delete guard** `environmentService.removeIfDeletable(id)`:
performs the DELETE with an inline `WHERE driver != 'local' AND NOT
EXISTS (instanceSettings where defaultEnvironmentId = id)` predicate,
eliminating the TOCTOU race between the app-level check and the DB
write.
- **Route hardening**: `DELETE /environments/:id` now calls
`getDeleteBlastRadius` first (app-level check + logging), then calls
`removeIfDeletable` (atomic guard). If the atomic guard returns null the
route fetches a fresh blast-radius snapshot and rejects with a 409
Conflict carrying `deleteBlockedReasons`.
- **Reference cleanup on valid delete**: after a successful delete, the
route clears environment selections on all company execution workspaces,
issues, and projects; syncs env-var secret bindings to `{}` (removing
bindings for the deleted environment); syncs config secret refs to `[]`
for the environment target; and removes the SSH private-key secret if
one was stored.
- **Race fix in `ensureLocalEnvironment`**: the insert-or-nothing path
now catches a `environments_name_idx` unique-constraint violation and
falls through to the existing SELECT, treating the name conflict as
idempotent.
- **Shared types**: `EnvironmentDeleteBlastRadius` and
`EnvironmentDeleteBlockedReason` exported from `@paperclipai/shared`.
- **OpenAPI**: registers the new blast-radius endpoint; updates the
delete-environment response schema to document 403/404/409.
- **Tests**: 56 existing environment-route and service tests continue to
pass; new service-level regression tests assert the atomic guard rejects
`local`-driver environments and instance-default environments and
succeeds for deletable ones.
## Verification
```
corepack pnpm exec vitest run \
server/src/__tests__/environment-routes.test.ts \
server/src/__tests__/environment-service.test.ts
# 56 tests, all passing
corepack pnpm --filter @paperclipai/shared typecheck
node scripts/ensure-plugin-build-deps.mjs
cd server && ../node_modules/.bin/tsc --noEmit
```
## Risks
- **Blast-radius endpoint auth**: guarded by
`assertCanAccessInstanceEnvironments`, the same gate as the existing
environment-list and delete routes. Non-admin callers receive 401/403
before any data is returned.
- **Atomic guard may reject a delete that the app-level check passed**:
this is intentional — it means the environment became protected between
the read and the write. The caller receives a fresh blast-radius
snapshot explaining why.
- **Secret cleanup ordering**: cleanup runs after the atomic DELETE
succeeds, in parallel across companies. If cleanup partially fails the
environment row is already gone; partial-cleanup state is recoverable by
re-running the sync operations. Risk: low — these are idempotent
upsert/sync operations.
- **ensureLocalEnvironment race fix**: swapping a unique-constraint
error for an idempotent SELECT adds one extra query on the conflict
path. This path is rare (only fires during concurrent boot) and is
significantly safer than the previous behavior.
- **No migration**: all changes are application-level; no schema changes
required.
## Model Used
- Provider: Anthropic
- Model: claude-sonnet-4-6 (Claude Sonnet 4.6)
- Context window: 200k tokens
- Mode: agentic tool use via Paperclip agent system (Claude Code)
## 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. `fix/...`) and contains
no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Priya Raman <priya.raman@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Harold Kim <harold.kim@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The shared telemetry package (`packages/shared/src/telemetry`)
defines the public contract for first-party telemetry events — event
names, dimension shapes, and now retention windows
> - A new telemetry event, `codex.credential_health`, carries
credential-observability fields (enums, booleans, counts, coarse
buckets) — no token material and no PII
> - The retention window for this event and its class was undocumented
at the contract level, leaving data-infra and reviewers without a
discoverable source of truth
> - This pull request adds `retention.ts` as the canonical
retention-contract surface, assigns `codex.credential_health` to the
`operational_enum_count` class (90-day window), exports the contract
from the shared package, and updates the README
> - The benefit is that the retention window is discoverable from the
event definition rather than being implicit pipeline knowledge, and the
no-token/no-PII note is locked in a contract test rather than relying on
prose
## Issue Description
No public GitHub issue exists for this change. Inline description
follows the [feature request
template](.github/ISSUE_TEMPLATE/feature_request.yml):
### Problem or motivation
`packages/shared/src/telemetry/` applies a 90-day retention window to
`codex.credential_health` events at the pipeline level, but this policy
is not stated anywhere in the shared telemetry contract. Without a
discoverable retention declaration, reviewers and data-infra must read
pipeline configuration to understand retention behaviour — there is no
contract-level source of truth. The `codex.credential_health` event
carries only enums, booleans, counts, and coarse buckets — no token
material and no PII.
### Proposed solution
Add a `retention.ts` module to the shared telemetry package that defines
the `operational_enum_count` retention class (90-day window) and maps
`codex.credential_health` to it. Export the contract from the package
index and add a focused contract test. Update the README with a
Retention section and a pointer in the Public Sources table. This is
additive documentation only — no runtime paths change.
### Alternatives considered
Keep retention implicit in pipeline configuration only. Rejected: this
leaves no discoverable, versioned contract for reviewers or data-infra,
and means every consumer must read pipeline config to understand
retention semantics. A schema-level declaration is the correct long-term
home.
### Roadmap alignment
Aligns with the telemetry contract hardening track — making implicit
operational knowledge explicit and testable at the shared-package level.
## What Changed
- **`packages/shared/src/telemetry/retention.ts`** (new): defines
`RETENTION_DAYS` (class → days) and `EVENT_RETENTION_CLASS` (event name
→ class). `operational_enum_count` is the only class: 90-day window for
enum/count/bucket events with no token material or PII.
`codex.credential_health` is the first entry. The `string` key type is
intentional to accommodate cross-system events (e.g. the Codex CLI) not
yet promoted to the first-party `PaperclipEventName` schema.
- **`packages/shared/src/telemetry/retention.test.ts`** (new): three
focused assertions — `operational_enum_count` is 90 days,
`codex.credential_health` is assigned that class, and the resolved
window is 90 days.
- **`packages/shared/src/telemetry/index.ts`**: exports
`RETENTION_DAYS`, `EVENT_RETENTION_CLASS`, and `RetentionClass` from the
shared package.
- **`packages/shared/src/telemetry/README.md`**: adds `retention.ts` to
the Public Sources table and a new Retention section with a class
reference table and guidance for future assignments.
## Verification
- `git diff --check` passes (no whitespace errors)
- `pnpm --filter @paperclipai/shared exec vitest run
src/telemetry/retention.test.ts src/telemetry/readme-contract.test.ts` —
runs the new contract tests and the existing readme-contract test
- `pnpm --filter @paperclipai/shared typecheck` — confirms the new
exports compile cleanly
- CI gates green
## Risks
Low risk. This is a documentation-only addition:
- No new telemetry event, dimension, emitter, or schema change
- No runtime code paths changed
- The new file is tree-shaken away in any consumer that doesn't import
from it
- `satisfies Record<string, number>` on `RETENTION_DAYS` ensures the
type stays correct as new classes are added
## Model Used
Claude Sonnet 4.6 (`claude-sonnet-4-6`) — Anthropic, 200k context
window, tool use enabled, no extended thinking mode. Used for code
authoring and PR composition.
## 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: Harold Kim <harold.kim@paperclip.ing>
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Harold Kim <harold-kim@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Adapter packages are the bridge between the control plane and local
agent harnesses such as Claude Code, Codex, and Gemini CLI.
> - ACP support was concentrated in a separate `acpx_local` adapter,
which made ACP feel like a separate agent choice instead of an execution
capability of the harness adapters.
> - Claude, Codex, and Gemini now have ACP-capable harnesses, so the
native adapter should own ACP selection, fallback, config, transcript
parsing, and environment diagnostics.
> - The standalone ACPX adapter still needs a compatibility path for
existing rows, but it should not be offered as an active adapter for new
agents.
> - This pull request moves the shared ACP runtime into
`@paperclipai/acpx-engine`, wires Claude/Codex/Gemini local adapters to
prefer ACP when prerequisites are available, and retires `acpx_local` to
a tombstone.
> - The benefit is one adapter per harness, richer ACP transcripts by
default where possible, and a migration path for existing Claude/Codex
ACPX agents.
## Linked Issues or Issue Description
Closes#5932 — the broken default `acpx_local` Claude path is replaced
by native `claude_local` ACP support, existing Claude/Codex ACPX rows
migrate to native adapters, and new agents no longer choose the
standalone ACPX adapter.
Refs #4893 — original merged ACPX local adapter runtime that this PR
replaces with native per-harness ACP engines.
Refs #6590 — prior ACPX-Claude seamlessness work folded into the new
native Claude ACP path.
Refs #197 — related open generic ACP/Kiro adapter work; this PR does not
close it because Kiro/custom generic ACP remains a separate adapter
decision.
Refs #7018 — related Kimi-specific `acpx_local` shell failure; this PR
retires the built-in standalone adapter but does not add a native Kimi
adapter.
Refs #8864 — related ACPX prompt/API guidance PR; this PR moves runtime
guidance into the shared/native ACP engine path instead of the old
standalone adapter.
Refs #8881 — related `acpx_local` POSIX shell failure from the old
`acpx` pin; this PR updates ACP dependencies but does not claim
custom/OMP ACP support as a first-class native adapter.
Refs #8964 — related open `acpx_local` stderr cleanup PR; this PR makes
the old runtime path obsolete for new agents but keeps it as a
non-closing reference.
Problem description:
- The standalone `acpx_local` adapter duplicates Claude/Codex agent
choices that already have first-class local adapters.
- ACP should be an execution engine capability of each harness adapter
when the underlying harness supports ACP.
- Existing `acpx_local` agents should either migrate to native harness
adapters or fail with an explicit retirement message instead of silently
falling back to the process adapter.
## What Changed
- Added `@paperclipai/acpx-engine` as the shared ACP execution,
session-codec, CLI formatter, and UI parser package.
- Wired `claude_local`, `codex_local`, and `gemini_local` to auto-select
ACP by default when prerequisites pass, with `engine=cli` opt-out and
`engine=acp` strict mode.
- Added ACP config schema/UI fields, environment checks, session-codec
preservation, transcript parsing, and adapter capability metadata for
the native adapters.
- Retired `acpx_local` to a server tombstone, removed its
UI/package/runtime image surface, and added a migration for existing
Claude/Codex ACPX agents.
- Updated package manifests, lockfile, release tooling, docs, Kubernetes
sandbox defaults, and tests.
## Verification
- `corepack pnpm --filter @paperclipai/acpx-engine typecheck`
- `corepack pnpm --filter @paperclipai/adapter-claude-local typecheck`
- `corepack pnpm --filter @paperclipai/adapter-codex-local typecheck`
- `corepack pnpm --filter @paperclipai/adapter-gemini-local typecheck`
- `corepack pnpm --filter @paperclipai/acpx-engine exec vitest run`
- `corepack pnpm --filter @paperclipai/adapter-claude-local exec vitest
run src/server/acp.test.ts src/server/execute.acp-fallback.test.ts
src/ui/build-config.test.ts`
- `corepack pnpm --filter @paperclipai/adapter-codex-local exec vitest
run src/server/acp.test.ts src/ui/build-config.test.ts`
- `corepack pnpm --filter @paperclipai/adapter-gemini-local exec vitest
run src/server/acp.test.ts src/ui/build-config.test.ts
src/ui/parse-stdout.test.ts`
- `corepack pnpm --filter @paperclipai/plugin-sdk ensure-build-deps &&
corepack pnpm --filter @paperclipai/server exec tsc --noEmit`
- `corepack pnpm --filter @paperclipai/server exec vitest run
src/__tests__/adapter-routes.test.ts
src/__tests__/adapter-session-codecs.test.ts
src/__tests__/adapter-models.test.ts`
- `corepack pnpm --filter @paperclipai/ui typecheck`
- `corepack pnpm --filter @paperclipai/ui exec vitest run
src/adapters/metadata.test.ts
src/adapters/adapter-display-registry.test.ts
src/components/AgentConfigForm.test.ts
src/components/AgentConfigForm.render.test.tsx
src/components/transcript/RunTranscriptView.test.tsx`
- `node --test scripts/bootstrap-npm-package.test.mjs
scripts/release-package-map.test.mjs
scripts/verify-release-registry-state.test.mjs`
Note: the server typecheck script calls `pnpm` internally; this dev
shell exposes pnpm through Corepack only, so I ran the two script steps
manually with `corepack pnpm`.
## Risks
- Migration changes existing `acpx_local` Claude/Codex agents to native
adapter types and clears old ACPX task sessions/runtime state.
- Custom ACP commands remain on the retired tombstone and will need a
separate future adapter/plugin path.
- ACP auto-selection depends on local Node and ACP server command
prerequisites; remote and unsupported environments fall back to CLI
unless `engine=acp` is explicit.
- `@paperclipai/acpx-engine` is a new public package and needs npm
trusted-publishing bootstrap before release automation can publish it.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI GPT-5 via Codex coding agent. Exact hosted model build and
context-window size are not exposed in this runtime. Tool use included
shell execution, repository editing, GitHub CLI operations, and local
test/typecheck 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>
Adds company skill fork precheck metadata, fork result/reassignment contracts, selected-agent reassignment during fork creation, and targeted server/shared test coverage.
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The work timeline helps operators understand when agent and user
activity actually happened across a project.
> - The timeline view needs clearer interaction context so activity is
easier to inspect and reason about.
> - The existing story coverage did not fully exercise the denser
activity states needed to review this UI safely.
> - This pull request expands the work timeline data shape, service
behavior, UI rendering, tests, and Storybook story so the activity
timeline is easier to verify.
> - The benefit is a more inspectable timeline for project activity,
backed by targeted server and UI coverage.
## Linked Issues or Issue Description
No public GitHub issue found, so this PR describes the feature inline
following the feature request template.
**Subsystem affected**
Cross-cutting: `server/`, `packages/shared`, and `ui/`.
**Problem or motivation**
Project operators need a clearer timeline view that shows when work
activity happened, how much agent time is represented inside the
selected window, and enough realistic activity states for safe visual
review. Sparse mock data and unbounded summary calculations make it
harder to trust the timeline when inspecting historical or capped
windows.
**Proposed solution**
Enrich the work timeline activity data returned by the service, render
clearer top-level timeline summary stats, clamp duration calculations to
the returned window, prorate token totals for partially visible spans,
and add Storybook/test coverage with realistic timeline activity data.
**Alternatives considered**
Keeping the existing sparse timeline story was considered, but it would
leave dense activity layouts and selected-window summary behavior
under-reviewed. Counting full span usage for partially visible spans was
also considered, but it makes historical windows report activity outside
the displayed range.
**Roadmap alignment**
Searched `ROADMAP.md` for timeline/activity references and found no
conflicting planned core work.
**Additional context**
This PR does not include migrations and does not commit generated design
screenshots or images.
## What Changed
- Extended shared work timeline activity types and server timeline
service behavior.
- Updated the timeline page and work timeline chart for richer activity
rendering.
- Clamped timeline runtime summary calculations to the returned window
and prorated summary token usage for clipped spans.
- Added and updated targeted server/UI tests for timeline activity
behavior.
- Added Storybook timeline mock coverage and Storybook preview setup
needed by the story.
## Verification
- `git rebase origin/master` completed cleanly after fetching
`paperclipai/paperclip:master`.
- `git diff --check origin/master...HEAD`
- `pnpm exec vitest run
server/src/__tests__/work-timeline-service.test.ts
ui/src/components/timeline/WorkTimelineChart.test.tsx
ui/src/pages/Timeline.test.tsx` — latest run: 3 files passed, 28 tests
passed.
- Greptile review completed at 5/5 with no unresolved Greptile threads
after fixes.
- GitHub checks completed green on the latest head SHA; Storybook visual
regression was skipped by the workflow.
- `pnpm check:token-gates` currently fails locally on existing
`origin/master` violations in `ui/src/components/ActivityCharts.tsx` and
`ui/src/components/IssueRecoveryActionCard.tsx`; this PR does not modify
those files.
## Risks
Low to moderate risk. The change affects the work timeline service
response shape and timeline UI rendering, so regressions would likely
show up as missing/incorrect timeline activity display. Targeted service
and UI tests cover the changed behavior. No migrations are included.
## Model Used
OpenAI Codex running GPT-5 as a tool-enabled coding agent with local
shell and GitHub CLI access. Exact runtime model ID/context-window size
was not exposed by the 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents run in heartbeat runs orchestrated by the server; run
lifecycle, retry scheduling, and the dashboard's run-activity metrics
are the subsystems involved
> - A spike in "failed" tasks traced to three causes: server restarts
killing in-flight runs and mislabeling them as failures, deterministic
workspace-validation loops when a worktree's branch diverged, and
provider quota/usage-limit errors being classified as generic transient
failures (putting agents into error state and polluting metrics)
> - Killed-then-recovered runs and quota waits are not product failures,
so both the runtime behavior and the reporting needed to distinguish
them
> - This pull request drains runs gracefully on shutdown with idempotent
restart retries, self-heals workspace branch mismatches, adds a
quota-aware failure class with reset-time retry, separates recovered
restart kills from true failures on the dashboard, and documents restart
hygiene for operators
> - The benefit is fewer spurious failures, automatic recovery instead
of manual repair, and dashboard metrics that reflect real failure rates
## Linked Issues or Issue Description
No public GitHub issue exists; describing the bug inline per the
bug-report template:
**What happened?**
In-flight heartbeat runs are marked `failed` when the server restarts,
even though a retry later succeeds. Worktrees whose checked-out branch
diverges from the issue branch fail workspace validation on every
subsequent run with no recovery path. Provider quota/usage-limit
responses are treated as generic transient upstream errors, putting
agents into an error state and retrying before the quota window resets.
The dashboard counts all of these as true failures, inflating failure
metrics.
**Expected behavior**
Graceful shutdown should interrupt (not fail) running runs and chain
exactly one recovery retry. Workspace validation should repair
recoverable branch mismatches automatically. Quota errors should get
their own error class with the retry scheduled at the provider reset
time and the agent left idle. The dashboard should report recovered
restart kills separately from true failures.
**Steps to reproduce**
1. Start a heartbeat run, then restart the server (SIGTERM) while it is
in flight — the run lands as `failed` with a process-loss error code
even when its retry succeeds
2. Check out an issue whose worktree branch has diverged (e.g. after a
force-moved branch) — every subsequent run fails
`workspace_validation_failed` deterministically
3. Drive an agent into a provider usage-limit window — the run fails as
a generic transient upstream error and the agent enters an error state
instead of idling until the reset time
**Paperclip version or commit**
master (base c07e650cd)
**Deployment mode**
Self-hosted dev plane (Linux, node server + embedded Postgres)
## What Changed
- Graceful shutdown (SIGTERM hook) now marks in-flight runs
`interrupted` instead of `failed` and enqueues an idempotent
process-loss retry (pre-insert existence check on `retryOfRunId`
prevents duplicates; bursts chain exactly one retry per interrupted run)
- Run-liveness classification routes `interrupted` to `needs_followup`
rather than `failed`
- Workspace validation self-heals branch mismatch / missing-branch
states instead of failing deterministically on every run
- New `provider_quota` error class: session/usage-limit responses
schedule the retry at the provider reset time and leave the agent idle
(not errored); fixes a case where a quota-terminated run with subtype
`success` was misclassified as failed; HTTP 529 remains transient
- Dashboard run-activity query separates recovered restart kills from
true failures via a recursive CTE over `retry_of_run_id` (ancestors of a
succeeded retry count as recovered), adds a per-day failed-by-error-code
breakdown, and binds the window start as a timestamptz string
- Activity charts UI: amber "Recovered" segment with legend and per-day
error-code tooltip; success-rate chart counts recovered runs as
successes
- New ops runbook: `docs/deploy/dev-plane-restart-hygiene.md`
## Verification
- Greptile follow-up fixes on `992705edd`: `pnpm exec vitest run
server/src/__tests__/server-startup-feedback-export.test.ts
server/src/__tests__/heartbeat-workspace-branch-containment.test.ts
server/src/__tests__/heartbeat-workspace-session.test.ts
server/src/__tests__/heartbeat-process-recovery.test.ts
server/src/__tests__/heartbeat-workspace-finalize-branch.test.ts` (209
tests); `pnpm --filter @paperclipai/server typecheck`; `git diff
--check`
- Post-rebase CI fixes: `pnpm exec vitest run
server/src/__tests__/heartbeat-retry-scheduling.test.ts`; `pnpm exec
vitest run server/src/__tests__/heartbeat-retry-scheduling.test.ts
server/src/__tests__/workspace-runtime.test.ts
server/src/__tests__/heartbeat-workspace-branch-containment.test.ts
server/src/__tests__/heartbeat-workspace-finalize-branch.test.ts
server/src/__tests__/heartbeat-workspace-session.test.ts` (227 tests);
`pnpm --filter @paperclipai/server typecheck`
- `npm test` server suites covering the changes:
`heartbeat-process-recovery`, `heartbeat-stop-metadata`,
`heartbeat-retry-scheduling` (95 tests), quota parse +
execute/retry-scheduling suites (100 tests), workspace self-heal suites
(200 tests), dashboard run-activity tests (3 tests) — all green,
typecheck exit 0
- Dashboard CTE cross-checked against a real development database: two
restart-burst days moved from 17 to 8 and 19 to 6 true failures once
recovered kills were separated, matching manual retry-chain inspection
- Screenshot verification of the real ActivityCharts component
(recovered segment + tooltip) during QA
## Risks
- Behavioral shift: runs killed by a restart no longer surface as
`failed`; anyone consuming raw run statuses will see `interrupted` (new
status value) — dashboards/queries in this repo were updated accordingly
- Retry chaining on repeated restarts is bounded (one chained retry per
interruption) but a pathological restart loop still delays work rather
than failing it; the runbook covers operator hygiene for that case
- Dashboard query adds a recursive CTE; cost is bounded by the
day-window row count and was verified against production-sized data
- No schema migrations; low migration risk
## Model Used
- Claude (Anthropic) — claude-fable-5 via Claude Code / Paperclip agent
harness, extended thinking with tool use; implementation commits also
produced with Codex CLI (GPT-5 class) agents under the same harness
## 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)
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board sidebar is the primary navigation surface for operators
scanning companies, projects, tasks, agents, and related control-plane
tools.
> - Goals still has a route and product surface, but keeping the
top-level sidebar link always visible makes it part of the default
navigation whether or not that surface is ready for every operator.
> - Instance experimental settings already provide a controlled place to
expose optional UI surfaces while they are being evaluated.
> - This pull request adds a dedicated experimental setting for
restoring the Goals sidebar link.
> - The benefit is a quieter default sidebar with an explicit escape
hatch for operators who still need the Goals entry point.
## Linked Issues or Issue Description
No public GitHub issue exists for this internal task, so the feature
request is described inline.
**Subsystem affected**
Cross-cutting: `ui/`, `server/`, and `packages/shared`.
**Problem or motivation**
The Goals route remains available, but the top-level Goals sidebar entry
makes that surface part of the default operator navigation. While the
goals surface is still being evaluated, operators need a quieter default
sidebar without losing an escape hatch for teams that still rely on the
link.
**Proposed solution**
Add a boolean instance experimental setting, `enableGoalsSidebarLink`,
default it to `false`, and render the Goals sidebar link only when the
setting is enabled. Expose the toggle in Instance Experimental Settings
so operators can restore the link without changing routes or rebuilding
the app.
**Alternatives considered**
- Remove the Goals route entirely: rejected because this task only asks
to hide the sidebar entry point and preserve access for teams evaluating
goals.
- Keep the sidebar link always visible: rejected because it does not
provide the requested quieter default navigation.
- Hard-code a local UI flag: rejected because instance experimental
settings already provide the expected operator-controlled pattern.
**Roadmap alignment**
Checked `ROADMAP.md`; no overlapping goals/sidebar/experimental roadmap
entry was found.
**Additional context**
The `/goals` route is preserved. This PR only gates the sidebar
navigation item.
## What Changed
- Added `enableGoalsSidebarLink` to the shared instance experimental
settings type and validator, defaulting to `false`.
- Normalized the new setting in the server instance settings service.
- Hid the Goals sidebar nav item unless the new setting is enabled.
- Added a Goals Sidebar Link toggle to the Instance Experimental
Settings page.
- Updated shared, server, sidebar, and settings page tests for the new
setting.
## Verification
- `pnpm exec vitest run packages/shared/src/validators/instance.test.ts
server/src/__tests__/instance-settings-service.test.ts
server/src/__tests__/instance-settings-routes.test.ts
ui/src/components/Sidebar.test.tsx
ui/src/pages/InstanceExperimentalSettings.test.tsx`
- `git diff --check origin/master...HEAD`
- `git merge-tree --write-tree HEAD origin/master`
- Searched for duplicate/related PRs by title and
`enableGoalsSidebarLink`; none found.
- Checked `ROADMAP.md` for overlapping goals/sidebar/experimental
entries; none found.
## Risks
Low risk. The main behavior shift is that operators who depended on the
sidebar Goals link need to enable the new experimental toggle. The
`/goals` route itself is not removed.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based Paperclip CodexCoder session with repository
tool access and command execution. Exact API model identifier and
context window were not exposed by the Paperclip harness.
## 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
## Thinking Path
> - Paperclip is an open-source app that lets teams run AI agents for
work tasks; each agent session uses an execution workspace — a git
checkout — to track the agent's active code state.
> - Every execution workspace has an expected target branch
(`PAPERCLIP_WORKSPACE_BRANCH`). The workspace git HEAD should always
point to that branch so agents commit in the right place.
> - When workspace git HEAD diverges from the expected branch — for
example after a harness branch-name fix or an accidental `checkout -b`
during a CI-retrigger — the discrepancy must be corrected before agents
can continue safely.
> - Operators (board users) need a controlled, audited path to reconcile
a workspace's live branch back to the expected target, with an override
escape-hatch for cases where the normal forward path is blocked.
> - This pull request adds a board-only `POST
/api/execution-workspaces/:id/reconcile-branch` service operation and
route that validates safety preconditions, resolves matching
recovery-action fingerprints, posts source-issue audit comments, and
records the reconciliation outcome.
> - The benefit is that operators can correct branch divergence through
the API with a full audit trail, instead of via raw database edits.
## Linked Issues or Issue Description
No public GitHub issue exists for this change. Context below follows the
feature-request template format.
**Subsystem affected**
server/ — REST API & orchestration services; packages/shared — request
validation.
**Problem or motivation**
Execution workspaces have an expected branch record that must match the
checked-out worktree branch. When the live git branch and stored branch
record drift apart, operators currently lack a first-class, audited API
to reconcile the record. The fallback is manual database repair or
workspace replacement, both of which are risky and hard to audit.
**Proposed solution**
Add a board-only execution workspace branch reconciliation operation.
`forward` mode re-inspects the server-side git state and only updates
the branch record when the stored branch is an ancestor of the
checked-out branch. `override` mode is a break-glass path that requires
board access and an operator reason. Both modes require a clean, idle
workspace, write audit details, post a source-issue audit comment, and
resolve the matching workspace-validation recovery action.
**Alternatives considered**
Manual database edit (no durable audit trail and easy to mistype),
recreating the workspace (heavier operational disruption), or trusting
client-supplied ancestry evidence (unsafe because the server must verify
the git state itself).
**Roadmap alignment**
This is incremental hardening for execution-workspace recovery and
operator controls. It does not duplicate a public roadmap item.
**Additional context**
The endpoint is intended for operator recovery, not normal agent control
flow, so the generated OpenAPI metadata and runtime route both classify
it as board-only.
## What Changed
- Added `reconcileExecutionWorkspaceBranchSchema` discriminated-union
validator (`forward` with optional reason, `override` requiring a
non-empty reason string) to
`packages/shared/src/validators/execution-workspace.ts`
- Exported `ReconcileExecutionWorkspaceBranch` type and the new schema
from the shared package index
- Added board-only reconcile-branch service operation in the execution
workspaces service: safety checks, recovery-action fingerprint
resolution, source-issue audit comment, and outcome recording
- Added clean-worktree and stopped-runtime-service preconditions before
branch-record mutation.
- Marked the reconcile route as board-only in OpenAPI generated auth
metadata.
- Added `POST /api/execution-workspaces/:id/reconcile-branch` route
wired to the new service operation with board-permission gate
- Extended `execution-workspaces-routes.test.ts` and
`execution-workspaces-service.test.ts` to cover: safety-check rejection,
override-reason validation, audit-comment posting, and recovery-action
fingerprint resolution (2 files / 19 tests)
## Verification
```sh
pnpm --filter @paperclipai/shared typecheck
pnpm --filter @paperclipai/server typecheck
pnpm exec vitest run server/src/__tests__/execution-workspaces-routes.test.ts server/src/__tests__/execution-workspaces-service.test.ts
pnpm exec vitest run server/src/__tests__/execution-workspaces-service.test.ts server/src/__tests__/openapi-routes.test.ts
```
## Risks
- **Board-only gate:** the operation is gated behind the board
permission; no agent can trigger it without operator authorization.
- **Override requires reason:** the `override` mode requires a non-empty
reason string so every bypass is audited.
- **Idempotent recovery-action resolution:** re-running with the same
fingerprint is safe; duplicate resolution is a no-op.
- **No execution-state mutation:** the route records a reconciliation
intent and updates the branch record; it does not restart the workspace
or modify running agent state.
- Overall risk: **low**.
## Model Used
- Provider: Anthropic
- Model ID: `claude-sonnet-4-6` (Claude Sonnet 4.6)
- Context window: 200 K tokens
- Capabilities: tool use, code execution, multi-turn context
Follow-up safety commit:
- Provider: OpenAI
- Model ID: `codex` / GPT-5 with tool use 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
(`feat/execution-workspace-branch-reconciliation-route`) 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The issue/task orchestration subsystem tracks parent–child and
blocker–dependent relationships, forming a directed acyclic (in
intention) subtree below each root issue
> - Agents and operators have no lightweight way to inspect the
dependency and wake state across an entire issue subtree — they must
walk the tree issue-by-issue, making multiple round-trips with full
object fetches
> - A bounded, read-only subtree diagnostic endpoint lets callers
understand the health of an entire work tree (which nodes are blocked,
which are cycling, which have pending wakes) from a single authenticated
request
> - This pull request adds `GET /api/issues/:id/diagnostics/subtree`, a
depth/node/per-node capped traversal that reuses the blocker and wake
projection helpers from the companion blocker and wake diagnostics
endpoints (see Refs #9114, #9133)
> - The benefit is that platform operators, monitoring, and coaching
tooling can surface \"why is this subtree stalled?\" across all nodes
without database access or unbounded graph walks, using only data the
caller already has read permission for
## Linked Issues or Issue Description
Refs #9114 (companion blocker diagnostics endpoint — blocker projection
helpers reused here)
Refs #9133 (companion wake diagnostics endpoint — wake projection
helpers reused here)
## What Changed
- **New route** `GET /api/issues/:id/diagnostics/subtree` in
`server/src/routes/issues.ts`: returns a bounded subtree traversal
rooted at `:id`, with depth/node/per-node caps and explicit truncation
flags
- **Cycle-safe traversal**: visited-node set prevents infinite loops on
any accidental cycle in the ancestry graph
- **Per-node authorization**: each subtree node is individually filtered
through `assertIssueReadAllowed`; unauthorized nodes are omitted from
the response and do not influence aggregate counts
- **Blocker and wake reuse**: per-node blocker rows and wake events are
projected through the same helpers as #9114 and #9133 — raw wake
payloads, raw errors, activity details, and trigger detail fields are
stripped
- **Low-trust filtering**: the `mention-scoped` low-trust path redacts
node/blocker identifiers for unauthorized actors, consistent with #9133
- **Truncation reporting**: response includes `depthTruncated`,
`nodeTruncated`, and per-node `blockersTruncated`/`wakesTruncated` flags
when caps are hit
- **Shared types** in `@paperclipai/shared`:
`IssueSubtreeDiagnosticsResponse` and supporting node/blocker/wake types
exported from the shared package
- **OpenAPI tag registration** for the new route
- **API reference docs** in
`skills/paperclip/references/api-reference.md`
- **Test coverage**
(`server/src/__tests__/issue-subtree-diagnostics-routes.test.ts`,
embedded Postgres): happy path, quiet singleton (no children/blockers),
node cap truncation, mention-scoped low-trust filtering, cross-company
denial
## Verification
```bash
# Subtree diagnostics tests only
pnpm exec vitest run server/src/__tests__/issue-subtree-diagnostics-routes.test.ts
# Full diagnostics suite (blocker + wake + subtree)
pnpm exec vitest run server/src/__tests__/issue-blocker-diagnostics-routes.test.ts server/src/__tests__/issue-wake-diagnostics-routes.test.ts server/src/__tests__/issue-subtree-diagnostics-routes.test.ts
# Type-check shared and server packages
pnpm --filter @paperclipai/shared typecheck
pnpm --filter @paperclipai/server typecheck
# Whitespace / diff check
git diff --check
```
All commands passed locally (5 subtree tests, 17 total across the three
diagnostics test files).
## Risks
- **No schema or migration changes** — read-only projection over
existing relations; no DDL risk
- **Bounded traversal** — depth, node count, and per-node blocker/wake
caps prevent unbounded graph walks; truncation is reported explicitly in
the response
- **Auth boundary** — root issue read is company-scoped and checked
before the subtree is built; each subtree node is individually
authorized; cross-company access is denied at `assertCompanyAccess`
- **No raw payloads** — raw wake payload, raw error, activity details,
and trigger detail fields are stripped from all nodes, consistent with
the companion endpoints
- Low overall risk; the endpoint is additive and read-only
## Model Used
- **Provider:** Anthropic
- **Model:** Claude Sonnet 4.6 (`claude-sonnet-4-6`)
- **Tool use:** yes (file reads, edits, bash execution, Paperclip API
calls)
- **Reasoning mode:** standard (no extended thinking)
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open-source app people use to manage AI agents for
work
> - The task/issue lifecycle subsystem tracks when agents wake up, are
suppressed, or are deferred, recording each wake request in
`agent_wakeup_requests` and each defer/suppression event in
`activity_log`
> - When an agent appears stuck or doesn't resume after a dependency
resolves, there is currently no read-only API surface to inspect its
wake history — operators must query the database directly
> - Making wake history queryable via a first-class endpoint lets
operators, support, and monitoring tools diagnose "why didn't this agent
wake up?" without database access
> - This pull request adds `GET /api/issues/:id/diagnostics/wakes`,
returning a bounded 14-day/50-row projection of wake requests and
defer/suppression activity events, with a deterministic `diagnosis`
field and a `likelyReason` inference — including a Case-B inference ("no
wake enqueued because a visible blocker is not done") that reuses the
blocker readiness data from the companion blocker diagnostics endpoint
(see Refs #9114)
> - The benefit is that platform operators can answer "why is this agent
not waking up?" from a safe, read-only HTTP endpoint rather than needing
direct database access, and CI/monitoring can assert expected wake
behavior
## Linked Issues or Issue Description
Refs #9114 (companion blocker diagnostics endpoint, already merged —
this PR extends the same diagnostic surface to wake/activity history)
## What Changed
- **New route** `GET /api/issues/:id/diagnostics/wakes` in
`server/src/routes/issues.ts`: returns a bounded (14-day window, 50-row
cap) projection of `agent_wakeup_requests` rows and wake-relevant
`activity_log` rows (defer/suppression events)
- **Sanitized projection**: raw `payload`, `details`, `error`, and
`triggerDetail` fields are stripped; unknown free-form `source`,
`reason`, and `status` values are projected to `"other"` to prevent
schema bleed
- **Deterministic `diagnosis` and `likelyReason` fields**: includes
Case-B inference ("no wake enqueued — visible blocker not done") that
calls the existing blocker-readiness helper from Slice 1 (#9114) so the
wake surface can explain missing wakes caused by outstanding blockers
- **Auth**: `assertCompanyAccess` + `assertIssueReadAllowed`;
cross-company requests are denied; Case-B blocker inference filters by
caller trust level so hidden (low-trust) blockers are mentioned but not
identified
- **Types in `@paperclipai/shared`**: `IssueWakeDiagnosticsResponse`,
`WakeEvent`, `ActivityEvent` exported from the shared package
- **OpenAPI tag registration** for the new route
- **Skill reference docs** in
`skills/paperclip/references/api-reference.md` documenting the endpoint
contract
- **Test coverage**
(`server/src/__tests__/issue-wake-diagnostics-routes.test.ts`, embedded
Postgres): happy path, empty/null diagnosis, Case-B inference, low-trust
hidden blocker, cross-company denial, raw blob minimization, cap
behaviour, combined blocker+wake test run
## Verification
```bash
# Wake diagnostics tests only
pnpm exec vitest run server/src/__tests__/issue-wake-diagnostics-routes.test.ts
# Wake + blocker diagnostics together (integration)
pnpm exec vitest run server/src/__tests__/issue-blocker-diagnostics-routes.test.ts server/src/__tests__/issue-wake-diagnostics-routes.test.ts
# Type-check shared and server packages
pnpm --filter @paperclipai/shared typecheck
pnpm --filter @paperclipai/server typecheck
# Whitespace / diff check
git diff --check
```
All commands passed locally.
## Risks
- **No schema or migration changes** — this is a read-only projection
over existing tables; no DDL risk.
- **Bounded queries** — 14-day window + 50-row cap limit per call; no
unbounded scans.
- **Auth boundary** — cross-company access is denied at
`assertCompanyAccess`; Case-B inference uses the same per-node trust
filtering as the blocker endpoint so low-trust blockers are acknowledged
but not identified.
- Low overall risk; the endpoint is additive and read-only.
## Model Used
- **Provider:** Anthropic
- **Model:** Claude Sonnet 4.6 (`claude-sonnet-4-6`)
- **Tool use:** yes (file reads, edits, bash execution, Paperclip API
calls)
- **Reasoning mode:** standard (no extended thinking)
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents block each other with `blockedByIssueIds` relationships to
express dependencies
> - Users and tooling have no lightweight way to inspect *why* an issue
is blocked or whether its blockers are themselves ready to resolve
> - A read-only diagnostic endpoint over the existing blocker graph lets
callers understand dependency chains without requiring a full issue-tree
traversal
> - This pull request adds `GET /api/issues/:id/diagnostics/blockers` —
a bounded, read-only projection over each blocker's readiness state
> - The benefit is that callers can surface blocking-chain diagnosis
(e.g. "waiting on N blockers, M of which are themselves blocked") from a
single authenticated request, using only data they already have read
permission for
## Linked Issues or Issue Description
No public GitHub issue exists for this change. Feature description:
**Subsystem affected:** `server/` — REST API & orchestration services;
`packages/shared` — types, constants, validators, API paths
**Problem or motivation**
There is no API endpoint to inspect *why* an issue is blocked or to get
a per-blocker readiness summary. Clients must walk the issue graph
manually or fetch full issue objects, which requires multiple
round-trips and is expensive.
**Proposed solution**
A single `GET /api/issues/:id/diagnostics/blockers` endpoint returns a
bounded projection: root issue summary, an ordered blocker list with
per-blocker `readiness` state, and a top-level `diagnosis` field
summarizing overall blocking status. Authorization mediation omits
blockers the caller cannot read, so `diagnosis` only reflects visible
data.
**Alternatives considered**
A general graph-walk query (too broad/expensive for a targeted
diagnostic call); enriching the existing `GET /api/issues/:id` response
(too coupled to the main response shape and adds weight for callers that
do not need blocker detail).
**Roadmap alignment**
Read-only observability surface over existing data; no database schema
changes. This aligns with tooling that helps users understand dependency
state without mutating anything.
## What Changed
- Added `GET /api/issues/:id/diagnostics/blockers` route to the server
- Returns per-blocker `readiness` state and a top-level `diagnosis`
field summarizing overall blocking status
- Enforces `issue:read` authorization per-blocker: unauthorized blockers
are omitted and do not influence `diagnosis` or `readiness` values
- Added shared TypeScript response types in `@paperclipai/shared`
- Added route-level tests using embedded Postgres
- Added API documentation in the `paperclip` skill
## Verification
```sh
./node_modules/.bin/vitest run server/src/__tests__/issue-blocker-diagnostics-routes.test.ts
pnpm --filter @paperclipai/shared typecheck
pnpm --filter @paperclipai/server typecheck
```
All three commands pass locally.
## Risks
- Read-only endpoint over existing relations — no writes, no schema or
migration changes — low risk
- Authorization mediation intentionally omits unauthorized blockers from
both the list and from `diagnosis`/`readiness`; callers with partial
access will see a narrower picture than the full blocker graph
## Model Used
- Provider: Anthropic
- Model: Claude Sonnet 4.6 (`claude-sonnet-4-6`)
- Context window: 200k tokens
- Mode: Tool use, code generation, extended reasoning
## 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents run in git worktrees tied to a workspace branch; when the
actual branch diverges from the expected one (e.g. a parent feature
branch was renamed), Paperclip currently has no structured field to
report *why* the branch is incoherent or whether it can be
auto-reconciled
> - The workspace-incoherence fingerprint already captures SHA
mismatches, but there is no evidence field distinguishing "actual branch
is a descendant of expected" (safe to fast-forward) from "branches have
diverged" (needs human review) or "SHAs are unavailable" (unknown)
> - Operators and future recovery flows need a typed verdict to make
decisions without re-running git commands themselves
> - This pull request adds `ancestryVerdict` and `plainLanguageReason`
evidence fields computed via `git merge-base --is-ancestor`, and
scaffolds the off-by-default `enableWorkspaceBranchReconcileForward`
instance setting with no runtime behavior yet
> - The benefit is that future recovery logic can branch on a typed
verdict rather than parsing prose, while the fingerprint v1 payload
stays stable
## Linked Issues or Issue Description
No public GitHub issue pre-exists for this diagnostic addition.
**Problem or motivation**
When Paperclip detects that an agent's actual workspace branch differs
from the recorded expected branch, the current fingerprint carries only
raw SHAs. There is no typed field indicating whether the actual branch
is a descendant of the expected one (safe reconcile path) vs. a true
divergence (requires human intervention) vs. an indeterminate state
(missing SHAs or git errors). Downstream recovery logic cannot branch
safely without re-running git.
**Proposed solution**
Add `ancestryVerdict` and `plainLanguageReason` to the workspace
incoherence evidence type; compute via `git merge-base --is-ancestor`;
scaffold a feature-flag for future forward-reconcile behavior
(`enableWorkspaceBranchReconcileForward`, off by default, not yet read
by any runtime path).
**Alternatives considered**
Encoding the verdict in the existing fingerprint string was rejected
because the fingerprint is a stable identity hash, not a mutable
evidence bag. Changing it would break monitors keyed on the string.
**Roadmap alignment**
Supports future workspace auto-reconcile work; ROADMAP.md has no
conflicting entry for this diagnostic layer.
## What Changed
- `packages/shared/src/types/heartbeat.ts` adds `ancestryVerdict` and
`plainLanguageReason` fields to `WorkspaceIncoherenceEvidence`
- `packages/shared/src/types/instance.ts` adds
`enableWorkspaceBranchReconcileForward` boolean (off by default)
- `packages/shared/src/validators/instance.ts` exports the new flag from
the settings validator
- `server/src/services/workspace-runtime.ts` computes `ancestryVerdict`
via `git merge-base --is-ancestor`; falls back to `unknown` on missing
SHAs or command errors; excludes verdict fields from fingerprint v1
computation
- `server/src/services/instance-settings.ts` wires the new setting
through to the settings service
- Tests updated in `workspace-runtime.test.ts`,
`instance-settings-service.test.ts`, `instance-settings-routes.test.ts`,
and `instance.test.ts` (104 tests total)
## Verification
```bash
pnpm exec vitest run \
server/src/__tests__/workspace-runtime.test.ts \
server/src/__tests__/instance-settings-service.test.ts \
server/src/__tests__/instance-settings-routes.test.ts \
packages/shared/src/validators/instance.test.ts
# 104 tests pass
pnpm --filter @paperclipai/shared typecheck
pnpm --filter @paperclipai/server typecheck
# both exit 0
```
Manual: trigger a workspace incoherence event and confirm the evidence
object carries `ancestryVerdict` and `plainLanguageReason`; confirm the
fingerprint string stays `workspace_incoherence:v1:sha256:...`.
## Risks
**Low risk.** Purely additive. Fingerprint v1 payload is unchanged. The
new flag has no runtime effect in this PR. `git merge-base
--is-ancestor` exits non-zero for both "not an ancestor" and "command
error"; both are handled and collapsed to typed values with a prose
reason.
## Model Used
Provider: Anthropic, model: Claude Sonnet 4.6 (`claude-sonnet-4-6`),
200k context, tool use enabled.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The board UI is the main daily navigation surface for agents,
projects, and their related resources.
> - Operators need a lightweight way to keep frequently used agents and
projects close without changing company-wide ordering or ownership.
> - Resource memberships already model per-user relationships to
projects and agents, so they are the right place to store user-specific
starred state.
> - This pull request extends that membership contract with a starred
timestamp and exposes star controls in list/detail views.
> - The sidebar then uses those starred memberships to show compact,
user-specific shortcuts.
> - The benefit is faster navigation without introducing a separate
favorites system or leaking preferences across users.
## Linked Issues or Issue Description
No public GitHub issue exists.
Feature request:
## Problem or motivation
Users cannot pin frequently used agents or projects into the main
sidebar. Returning to important resources requires scanning full
project/agent lists or navigating through detail pages, which adds
friction to repeated daily workflows.
## Proposed solution
Store a per-user `starred_at` timestamp on agent and project
memberships, expose API actions to set or clear that state, add star
toggle controls to list/detail pages, and render starred projects and
agents as compact sidebar shortcuts.
## Alternatives considered
A separate favorites table would work, but it would duplicate membership
scoping and require another resource relationship model. Keeping starred
state on memberships preserves existing company/user boundaries and
avoids a second source of truth.
## Roadmap alignment
Checked `ROADMAP.md`; no overlapping planned core work for starred
resource/sidebar navigation was found.
## Additional context
The affected subsystems are `packages/db`, `packages/shared`, `server/`,
and `ui/`. The migration is idempotent with `IF NOT EXISTS` guards so
environments that saw an earlier local migration name can still apply
the final ordered migration safely.
## What Changed
- Added idempotent migration `0133_resource_membership_stars` for
`starred_at` columns and lookup indexes on agent/project memberships.
- Extended shared resource membership types and validators with starred
metadata and actions.
- Updated server resource membership services/routes to read and mutate
starred resource state.
- Added reusable star toggle UI and resource membership hook support for
starred state.
- Added starred projects and agents sidebar rendering, plus star
controls on list and detail pages.
- Added focused shared, server, and UI coverage for starred membership
behavior and sidebar rendering.
## Verification
- Rebased and force-with-lease pushed current PR head
`a086fc965391c9e50a51b5b83b5b44a797b2a6f4` onto current
`paperclipai/paperclip:master`; `gh pr view` reports `MERGEABLE` with no
merge conflicts. GitHub checks are green for this fresh head.
- `pnpm exec vitest run packages/shared/src/resource-memberships.test.ts
server/src/__tests__/resource-memberships-routes.test.ts
server/src/__tests__/workspace-runtime.test.ts
ui/src/components/Sidebar.test.tsx
ui/src/components/SidebarAgents.test.tsx
ui/src/components/SidebarStarredProjects.test.tsx
ui/src/components/StarToggle.test.tsx
ui/src/pages/InstanceExperimentalSettings.test.tsx` passed after the
rebase: 8 files, 143 tests.
- Greptile re-review is 5/5; the remaining screenshot thread was
resolved as non-blocking because this task explicitly requested no
screenshots/images in the PR.
- `pnpm exec vitest run
ui/src/components/SidebarStarredProjects.test.tsx` passed after the
mobile pending-spinner fix.
- `pnpm exec vitest run packages/shared/src/resource-memberships.test.ts
server/src/__tests__/resource-memberships-routes.test.ts
ui/src/components/Sidebar.test.tsx
ui/src/components/SidebarAgents.test.tsx
ui/src/components/SidebarStarredProjects.test.tsx
ui/src/components/StarToggle.test.tsx
ui/src/pages/InstanceExperimentalSettings.test.tsx` passed: 7 files, 68
tests.
- `pnpm --filter @paperclipai/db typecheck && pnpm --filter
@paperclipai/shared typecheck && pnpm --filter @paperclipai/server
typecheck && pnpm --filter @paperclipai/ui typecheck` passed
db/shared/server, then failed in pre-existing UI code outside this PR:
`src/pages/CompanyEnvironments.tsx` missing `@xterm/*` type declarations
and `previous` possibly null.
- Checked that the PR diff does not include `pnpm-lock.yaml` or
`.github/workflows` changes.
- Checked `ROADMAP.md` and found no overlapping planned core work for
starred resource/sidebar navigation.
- Searched existing GitHub PRs for duplicate starred-resource/sidebar
work and found none.
## Risks
- Migration touches membership tables. The SQL uses `IF NOT EXISTS` for
columns and indexes so environments that saw an earlier local migration
name can still apply this safely.
- Sidebar ordering and visibility changes could affect users who rely on
the previous flat sidebar layout.
- Starred state is per-user membership metadata; code paths must
continue preserving company/user scoping around memberships.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI GPT-5 Codex, tool-enabled coding agent with shell/GitHub access.
Context window not disclosed by the 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Pipeline health reports give operators early warnings when a
workflow step cannot run cleanly.
> - Failed stage automation is surfaced as an `automation_failed` health
warning for the affected item.
> - A single item can have repeated failed automation rows for the same
stage, especially after retries or repeated failed attempts.
> - Rendering every matching row creates duplicate warnings that make
the pipeline look noisier than it is.
> - This pull request deduplicates failed automation warnings by the
item/stage pair before adding them to the health report.
> - The benefit is that repeated failures for the same item in the same
stage produce one actionable warning, while distinct items still remain
visible.
## Linked Issues or Issue Description
Refs #8866
Bug: pipeline health could emit duplicate `automation_failed` warnings
when the input contained repeated failed automation rows for the same
live item and stage. Reviewers should expect one warning per
`stageId:caseId` pair, not one warning per backing execution row.
## What Changed
- Deduplicated failed automation warnings with per-stage case tracking
in `computePipelineHealth`, avoiding collision-prone composite string
keys before pushing `automation_failed` warnings.
- Added shared Vitest coverage for a single automation failure,
duplicate same-stage same-item dedupe, separate warnings for different
item IDs in the same stage, the same item ID in different stages, and
colon-delimited ID collision cases.
- Kept pipeline route behavior unchanged; this PR only changes shared
warning rendering and direct shared tests.
## Verification
- `pnpm vitest packages/shared/src/pipeline-health.test.ts`
- 1 test file passed
- 5 tests passed
- PR #9090 remote checks on `bbbb2d4627f5be17ca210dedb9edb91edd047df8`
- All Paperclip CI/status checks passed
- Greptile Confidence Score: 5/5, 0 comments added, 0 unresolved
Greptile threads
No route test changed because this PR does not change the route's
failed-automation query or normalization behavior.
## Risks
Low risk. The change only suppresses duplicate `automation_failed`
warnings when both `stageId` and `caseId` match. Distinct items in the
same stage still produce separate warnings.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5 coding agent in the Paperclip local adapter
environment; exact model snapshot and context-window metadata were not
exposed in the runtime. Tool use and code execution were enabled.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - When you swap an agent's adapter (e.g. from one LLM provider to
another), the server merges the incoming PATCH body with stored config —
keys listed in \`ADAPTER_AGNOSTIC_KEYS\` are preserved regardless of
which adapter is active
> - That constant was defined independently in two places:
\`server/src/agents.ts\` (used by the adapter-swap route) and
\`ui/src/lib/agent-config-patch.ts\` (used by the UI patch builder)
> - PR #8975 fixed the bug where \`paperclipSkillSync.desiredSkills\`
was dropped on adapter swap by adding it to the server-side constant,
but the UI-side copy was not updated in the same PR — creating ongoing
drift risk
> - This pull request hoists \`ADAPTER_AGNOSTIC_KEYS\` into
\`packages/shared\` so both consumers import the same constant
> - The benefit is a single source of truth: any future key addition is
made in one place and both the server route and the UI patch builder
pick it up automatically, with a drift guard to catch any accidental
re-duplication
## Linked Issues or Issue Description
Refs #8975 — follow-up deduplication: #8975 fixed the runtime bug but
left the constant duplicated across server and UI. This PR closes that
gap.
## What Changed
- Added \`ADAPTER_AGNOSTIC_KEYS\` constant and \`AdapterAgnosticKey\`
type to \`packages/shared/src/adapter-agnostic-keys.ts\`
- Updated \`server/src/agents.ts\` to import the shared constant,
removing the local copy
- Updated \`ui/src/lib/agent-config-patch.ts\` to import the shared
constant, removing the local copy
- Added \`packages/shared/src/adapter-agnostic-keys.test.ts\`: drift
guard asserting the expected key set and both consumer import sites
## Verification
\`\`\`bash
pnpm exec vitest run packages/shared/src/adapter-agnostic-keys.test.ts
ui/src/lib/agent-config-patch.test.ts
server/src/__tests__/agent-instructions-routes.test.ts
pnpm --filter @paperclipai/shared typecheck
pnpm --filter @paperclipai/server typecheck
pnpm --filter @paperclipai/ui typecheck
\`\`\`
All 15 tests pass across the three files; all three packages typecheck
clean.
## Risks
Low risk — behavior-preserving refactor. The key set is unchanged; only
the import source changes. The drift guard will fail loudly if someone
accidentally re-introduces a local copy or modifies one without updating
the other.
> 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
- Model: Claude Sonnet 4.6 (\`claude-sonnet-4-6\`)
- Context: standard context window, tool use enabled
- Reasoning: standard mode (no extended thinking)
## 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)
- [ ] My branch name describes the change (e.g. \`docs/...\`,
\`fix/...\`) and contains no internal Paperclip ticket id or
instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [ ] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent runs often need provider credentials, API tokens, and other
environment-bound secrets.
> - Company-level secrets work for shared credentials, but they do not
model values that should differ by human operator.
> - Without a user-scoped model, a run can dispatch without knowing
whether the responsible human has supplied the needed value.
> - Paperclip also needs run attribution to make those user-scoped
runtime checks deterministic and auditable.
> - This pull request adds user-specific secret definitions, per-user
values, environment bindings, responsible-user attribution, and runtime
resolution gates.
> - The benefit is that teams can define the secret once, let each user
provide their own value, and block runs before dispatch when required
user secrets or active definitions are unavailable.
## Linked Issues or Issue Description
Refs #224
Refs #6057
This PR implements user-specific secret support as a core
secret-management capability rather than a one-off adapter setting. It
is related to existing public work on company secrets UI and runtime
secret refs, but is distinct because the value is owned by the
responsible user and resolved at run dispatch time.
Related PR search before opening found existing secrets work such as
#1550, #8256, #8614, #8634, and #8647; none of those add the full
user-secret definition/value/runtime gate covered here.
## What Changed
- Added user-secret definitions and per-user "My secrets" values,
keeping stored values out of access metadata.
- Added `user_secret_ref` environment bindings and UI affordances to
pick them alongside existing secret refs.
- Added responsible-user runtime resolution so user-secret refs resolve
against the human responsible for the run.
- Added pre-dispatch missing-secret gates so runs fail before adapter
dispatch when required user values are absent or definitions are
inactive.
- Added low-trust allowlist hardening for user-secret runtime access.
- Added issue, routine, run, and agent API key responsible-user
attribution and fail-closed dispatch behavior when attribution cannot be
resolved.
- Added denial-copy mapping so responsible-user authorization failures
surface as actionable run outcomes instead of opaque setup failures.
- Added OpenAPI documentation for the user-secret routes.
- Rebases cleanly on current `master`; migrations were renumbered
incrementally as `0128_user_specific_secrets`,
`0129_agent_api_key_responsible_user`, and
`0130_run_responsible_user_invariant` after upstream `0126`/`0127`
migrations.
- Removed previously committed local design screenshots so the PR
contains code/docs/tests only.
## Verification
- PASS: PR head `2527febd106bcf3ca264ca0da7fca491084192d6` is based on
`paperclipai/paperclip:master`.
- PASS: `git diff --check`
- PASS: `git diff --name-only public/master...HEAD | rg
'^(pnpm-lock\\.yaml|\\.github/workflows/|screenshots/)' || true`
produced no files.
- PASS: migration journal audit confirmed unique indexes through `130`
with tail entries `0126_issue_comment_derived_attribution`,
`0127_environment_custom_images_instance_scoped`,
`0128_user_specific_secrets`, `0129_agent_api_key_responsible_user`, and
`0130_run_responsible_user_invariant`.
- PASS: `pnpm --filter @paperclipai/ui typecheck`
- PASS: `pnpm --filter @paperclipai/server typecheck`
- PASS: `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-responsible-user-invariant.test.ts`
- PASS: `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-active-run-output-watchdog.test.ts
src/__tests__/heartbeat-stale-queue-invalidation.test.ts
src/__tests__/heartbeat-workspace-finalize-branch.test.ts
src/__tests__/issue-monitor-scheduler.test.ts`
- PASS: `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-comment-wake-batching.test.ts
src/__tests__/heartbeat-retry-scheduling.test.ts
src/__tests__/heartbeat-accepted-plan-workspace-refresh.test.ts
src/__tests__/heartbeat-plugin-environment.test.ts`
- PASS: `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/low-trust-red-team-routes.test.ts`
- PASS: `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/secrets-service.test.ts` (55 tests)
- PASS: `pnpm vitest run server/src/__tests__/secrets-routes.test.ts
server/src/__tests__/secrets-service.test.ts` (89 tests after final
Greptile cleanup fixes)
- PASS: `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/heartbeat-issue-liveness-escalation.test.ts` (17 tests
after the final rebase CI fix)
- PASS: focused server Vitest batches covering heartbeat recovery,
project env, plugin env, routines, low-trust, pipelines, monitors,
watchdog, and stale queue paths.
- PASS: GitHub checks are green on
`2527febd106bcf3ca264ca0da7fca491084192d6`, including Typecheck +
Release Registry, Build, General tests, serialized server suites, e2e,
Canary Dry Run, verify, security checks, and Greptile Review.
- PASS: Greptile Review completed successfully on
`2527febd106bcf3ca264ca0da7fca491084192d6` with Confidence Score 5/5,
and GraphQL review-thread audit returned zero unresolved non-outdated
threads.
## Risks
- Runtime behavior now depends on a run having a correct responsible
user; missing or incorrect responsibility assignment can block runs
before adapter dispatch.
- `user_secret_ref` bindings intentionally expose metadata without
values, but UI/API callers may need to handle the new binding kind
explicitly.
- External secret providers and IAM policies are not automatically
provisioned by this PR; operators still need to configure provider-side
access for non-local vaults.
- The PR is broad across db/shared/server/UI/runtime paths, so release
validation should include both API and UI secret workflows before merge.
- The migration renumbering is intentionally incremental after upstream
migrations; the branch migrations use guarded
column/table/index/constraint creation so users who tested the older
draft numbering should not hit duplicate DDL for the existing objects.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based coding agent (`gpt-5`), Codex local adapter
with shell/tool use and code execution. Context window and internal
reasoning mode are not exposed by the 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>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Environment sandboxes already support custom image creation and
refresh through a temporary SSH setup session.
> - The existing workflow makes operators copy an SSH command into an
external terminal before they can install packages or make image
changes.
> - That extra context switch is slower, easier to get wrong, and less
integrated with the setup session Paperclip already tracks.
> - This pull request adds an embedded browser SSH terminal for custom
image setup, so operators can start working in the target sandbox
directly from the environment configuration flow.
> - The implementation uses short-lived websocket attachment tokens,
session-lifetime SSH host-key pinning, and server-managed terminal
cleanup so the feature fits the existing setup-session boundary.
> - The benefit is a smoother custom image creation and refresh
experience without asking users to leave Paperclip for routine sandbox
setup work.
## Linked Issues or Issue Description
No public GitHub issue exists.
### Subsystem affected
Cross-cutting: `server/` custom image setup APIs and websocket handling,
`ui/` environment configuration UI, and shared custom image contracts.
### Problem or motivation
Custom image creation and refresh require an operator to open a separate
SSH client, paste the command shown by Paperclip, perform setup work,
then return to the browser to finish the image flow. This is functional
but awkward for a setup process that already starts and tracks a
temporary sandbox session.
### Proposed solution
Embed an SSH terminal in the custom image setup UI. When a setup session
exposes an SSH payload, Paperclip should open a browser terminal backed
by a server-side websocket session, let the operator run setup commands
in-place, and then close the terminal when setup is finished, cancelled,
expired, or disconnected.
### Alternatives considered
- Keep the existing copy/paste SSH command workflow. This remains a
fallback, but it does not streamline the common path.
- Put SSH credentials directly into websocket URLs. This was avoided so
terminal authentication can happen in an explicit first websocket auth
frame rather than in logged URLs.
- Trust the SSH host blindly for every reconnect. This PR instead pins
the observed host-key fingerprint for the setup-session lifetime.
### Roadmap alignment
This fits the roadmap theme of making agent workspaces usable in more
remote and sandboxed environments while preserving Paperclip's
control-plane model.
### Additional context
Public GitHub search did not find a duplicate issue or PR for `custom
image terminal ssh` in `paperclipai/paperclip`.
## What Changed
- Added server-side terminal session tracking for custom image setup
sessions, including connect-token issuance, websocket attachment,
expiry, resize, input, and shutdown handling.
- Added an embedded browser terminal to the custom image creation and
refresh flow when a setup session provides SSH connection details.
- Moved terminal token authentication out of the websocket URL and into
the first websocket JSON auth frame.
- Added SSH host-key SHA-256 pinning for each terminal session and
documented the provider convention for username-embedded SSH
credentials.
- Updated the custom image environment API and UI so the setup terminal
can open, reconnect, show status, authenticate, resize, and remain
active for the setup-session lifetime once attached.
- Kept custom image setup routes company-scoped and closed active
terminal sessions on setup finish/cancel.
- Added focused unit/integration/UI coverage for token expiry,
setup-session expiry, websocket close paths, host-key pinning, and
terminal session lifecycle behavior.
- Removed the generated lockfile delta from the PR; CI owns temporary
lockfile regeneration for manifest-changing PRs.
## Verification
- `pnpm exec vitest run
server/src/__tests__/server-startup-feedback-export.test.ts
server/src/__tests__/environment-custom-image-terminal-ws.test.ts
server/src/services/environment-custom-image-terminal-sessions.test.ts
server/src/__tests__/environment-custom-image-routes.test.ts
packages/shared/src/environment-custom-images.test.ts
ui/src/pages/CompanyEnvironments.test.tsx`
- 6 test files passed
- 58 tests passed
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm --filter @paperclipai/ui typecheck`
- `pnpm --filter @paperclipai/server build`
- `pnpm --filter @paperclipai/ui build`
- `pnpm run typecheck:build-gaps`
- `git diff --check`
- Local sensitive-content scan over the PR diff using patterns for API
keys, private keys, private hostnames, local paths, token fields, and
credential-like strings.
- Findings were limited to removed URL-token code and synthetic test
placeholders such as `ssh-token-secret` and
`terminal-token-terminal-token-123456`.
- No real credentials, private hostnames, local filesystem paths, or
instance-local links were found.
- Remote PR checks were green after the implementation commit, including
Build, Typecheck + Release Registry, General tests, serialized server
suites, e2e, verify, Socket, Snyk, Superagent, and Greptile 5/5.
- Post-merge PR hardening on July 3, 2026: merged `origin/master` at
`47448721e` into the branch, resolved the `CompanyEnvironments.tsx`
import conflict, reran focused tests, server/UI typechecks, server/UI
builds, `pnpm run typecheck:build-gaps`, and `git diff --check`, scanned
the final diff for sensitive content, pushed `4b43558cc`, and confirmed
all remote checks plus Greptile 5/5 were green.
- PR metadata correction on July 3, 2026: changed the title/body framing
from bug-fix language to feature-request language. No source files
changed for this metadata-only update.
## Risks
- Moderate surface area because this adds websocket routing,
setup-session runtime state, package dependencies, and a new custom
image UI path.
- New websocket attachments still require valid short-lived tokens;
established terminal sessions remain bounded by setup-session expiry,
explicit finish/cancel, client close, or server shutdown.
- The terminal-session store is in-memory, so active terminal websocket
tokens and host-key pins do not survive server restarts.
- SSH host-key verification uses session-lifetime TOFU pinning because
the current provider payload does not expose a trusted host-key
fingerprint.
- The external SSH command remains important as a fallback if a browser,
proxy, or network environment cannot sustain the websocket terminal.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5 coding agent with shell/tool execution. Context
window size was not exposed in 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - A core part of that experience is watching active agent runs without
dropping into raw logs first
> - Local and sandbox-backed adapters already record useful run output,
progress, and tool activity
> - But active issue threads could sit visually stale while the agent
was syncing workspaces, tailing sandbox output, or emitting incremental
tool-call updates
> - Operators need timely, human-readable progress while preserving the
raw transcript underneath
> - This pull request streams sandbox run-log progress into runtime
status, keeps visible issue threads refreshed, and folds repeated ACPX
tool updates into stable transcript cards
> - The benefit is that long-running agent work becomes easier to
supervise without changing the task/comment control-plane model
## Linked Issues or Issue Description
No public GitHub issue exists for this exact change.
Problem/motivation:
- During long-running sandboxed agent work, the issue UI can appear idle
even though the agent is actively syncing, running tools, or producing
incremental output.
- Operators need realtime feedback at the issue-thread layer, not only
after opening raw logs or waiting for the final heartbeat result.
- Related public context: #1808 previously added live-run status dots to
Projects; #4362 touches heartbeat wakeup behavior but is not a duplicate
of this runtime/UI feedback change.
## What Changed
- Added sandbox run-log streaming support and defaulted sandbox-capable
local adapters into the richer live-feedback path.
- Surfaced environment/sandbox sync progress through heartbeat runtime
status with bounded, redacted snippets.
- Added live issue-thread cache patching so visible active runs update
as progress events arrive.
- Folded repeated ACPX `tool_call` updates into one transcript card
instead of stacking duplicate cards.
- Updated adapter docs and added focused regression coverage for sandbox
log streaming, runtime status, ACPX parsing, live updates, transcript
rendering, and issue chat messages.
## Verification
- `pnpm install --frozen-lockfile`
- `pnpm exec vitest run ui/src/context/LiveUpdatesProvider.test.ts`
- `pnpm exec vitest run
server/src/services/heartbeat-run-runtime-status.test.ts
server/src/__tests__/heartbeat-runtime-state.test.ts
ui/src/context/LiveUpdatesProvider.test.ts`
- `pnpm exec vitest run
packages/adapter-utils/src/execution-target-sandbox.test.ts
packages/adapter-utils/src/sandbox-managed-runtime.test.ts
server/src/services/heartbeat-run-runtime-status.test.ts
server/src/__tests__/agent-live-run-routes.test.ts
server/src/__tests__/heartbeat-runtime-state.test.ts
packages/adapters/acpx-local/src/ui/parse-stdout.test.ts
ui/src/context/LiveUpdatesProvider.test.ts
ui/src/components/transcript/RunTranscriptView.test.tsx
ui/src/lib/issue-chat-messages.test.ts
ui/src/components/IssueChatThread.test.tsx`
- GitHub PR workflow on head `8397953e7b41ccd42e5d9457ee7e4dfb996e4ec5`:
`verify`, build, typecheck/release-registry, e2e, general shards,
serialized server shards, and canary dry run passed.
- Greptile Review on head `8397953e7b41ccd42e5d9457ee7e4dfb996e4ec5`:
Confidence Score 5/5, no unresolved review threads.
## Risks
- Live issue-thread cache patching could miss an edge case for a route
shape not covered by tests.
- Surfacing active-run snippets needs continued care around redaction;
this PR keeps snippets bounded and adds redaction-focused coverage.
- More frequent active-run UI refreshes could expose performance issues
on very large issue threads, though updates are scoped to visible
run/query caches.
## Model Used
OpenAI GPT-5 via Codex, operating as a tool-enabled coding agent with
shell, git, and repository-editing capabilities. Context window size is
not exposed in 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>
Adds the public telemetry data contract README, links it from contributor docs, and adds a focused README contract test for generated helper names.
Verification:
- git diff --check origin/master..HEAD
- pnpm exec vitest run packages/shared/src/telemetry/readme-contract.test.ts
- PR CI green
- Greptile 5/5
Co-Authored-By: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Operators need to *see* how work actually flowed across their agents
over time — who was invoked, what they worked on, and how work was
delegated between them
> - The dashboard shows point-in-time state but nothing reconstructs the
temporal, cross-actor picture of heartbeat runs and delegations
> - A read-only company work-timeline endpoint was landed first (server
aggregation over runs/issues/activity); it had no frontend
> - This pull request adds the Gantt-style **Work Timeline** page that
renders that endpoint, plus the small additive server contract change it
needs (shared DTOs + a task title on each span)
> - The benefit is a single dense view — actor rows, concurrency lanes,
delegation connectors, zoom and a mini-map — that makes agent activity
legible without an N+1 fetch storm from the client
## Linked Issues or Issue Description
No public GitHub issue. Problem, in-PR:
- **Gap:** the company work-timeline aggregation endpoint has no UI.
There is no way to visually inspect how heartbeat runs unfolded over
time or how work was delegated between agents.
- **Solution:** a dashboard-adjacent Gantt-style page at
`/:companyPrefix/timeline`, linked from the sidebar's "Work" section,
rendering runs as bars on per-actor rows with delegation connectors,
kickoff chips, zoom, a lens filter, and a mini-map.
- Built with React + custom inline SVG (no chart dependency; consistent
with the existing Tailwind/Radix stack).
## What Changed
- **Frontend Gantt page** (`ui/src/pages/Timeline.tsx`,
`ui/src/components/timeline/WorkTimelineChart.tsx`): actor rows
(agents/system only — humans never get a row), overlapping runs packed
into concurrency sub-lanes, bars = heartbeat runs with a left colour tab
for issue identity, truncated task title + timing/status on hover,
click-through to the task.
- **Human activity markers & human rows** for kickoff/delegation
involving people, without giving humans their own run lane.
- **Kickoff avatar chips** at each bar's leading edge; straight
agent→agent delegation connectors (dashed for
retries/changes-requested); in-progress runs extend to a dashed "now"
line and fade out.
- **Zoom** (hour/day/week, auto-fit), full-window **mini-map** with a
draggable brush, **lens filter** (Everyone / per-user, server-side), and
colour **by task / by status**.
- **Pure layout/transform module** (`ui/src/lib/timeline/layout.ts`) —
packing, kickoff derivation, connector resolution, scales — unit-tested
in isolation.
- **Server contract (additive):** moved the `WorkTimeline*` DTOs into
`@paperclipai/shared` so the aggregation service and the UI consume one
contract; added `issueTitle` to each span so the tooltip shows the task
title with no N+1 client fetch.
- Sidebar link, query keys, API client (`ui/src/api/workTimeline.ts`),
and a Storybook story with fixtures.
## Verification
- `pnpm --filter @paperclipai/shared build` ✅
- `pnpm --filter @paperclipai/server typecheck` ✅ · `pnpm --filter
@paperclipai/ui typecheck` ✅
- `pnpm --filter @paperclipai/ui exec vitest run
src/lib/timeline/layout.test.ts
src/components/timeline/WorkTimelineChart.test.tsx` ✅ (15/15)
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/work-timeline-service.test.ts` ✅ (5/5) — the DTO move +
`issueTitle` are additive; existing service tests use `objectContaining`
and still pass.
- Rendered `WorkTimelineChart` headless against a real slice of company
activity via a Storybook story; manual browser QA of the live page
passed on the feature branch.
## Risks
- **Low risk.** The change is UI-only plus an additive server DTO
refactor (types relocated to `@paperclipai/shared`, one new optional
field). No schema/migration changes, no change to endpoint behaviour
beyond the extra `issueTitle` field. The page is behind its own route
and does not alter existing views.
## Model Used
- Claude, Opus 4.8 (`claude-opus-4-8`), via Claude Code with extended
thinking and tool use.
## 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 (only the merged endpoint PR #8875 is related; no duplicates)
- [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
ticket id
- [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: Paperclip <noreply@paperclip.ing>
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The inbox is where operators quickly scan which issues are active,
blocked, or waiting for attention
> - A blocked parent can still have active descendant work, but the
inbox previously depended on only loaded rows to infer that state
> - That made collapsed or partially loaded issue trees look more stuck
than they really were
> - This pull request carries live descendant summary data through the
issue list API and inbox UI
> - The benefit is a more accurate blocked-inbox signal, so operators
can distinguish truly stalled work from blocked parents that still have
live child activity
## Linked Issues or Issue Description
No public GitHub issue was found for this exact inbox descendant-status
polish.
Feature request fields:
**Subsystem affected**
Cross-cutting: `server/`, `packages/shared`, plugin/MCP API surfaces,
and `ui/` inbox rendering.
**Problem or motivation**
Inbox rows need to show when blocked or collapsed parents still have
live descendant work, even when the live child row is not loaded in the
current client tree. Without a server-provided descendant summary, a
parent can look stalled even though active work continues below it.
**Proposed solution**
Expose an optional live descendant count on issue list results, request
it from inbox views, and use it to render covered blocked status and
live-below indicators. Keep the field opt-in so other issue list callers
keep their existing payload shape and query cost.
**Alternatives considered**
Relying only on client-loaded subtree state was ruled out because it
misses collapsed or unloaded descendants. Always returning the count was
also avoided because most list callers do not need this extra summary.
**Roadmap alignment**
This is scoped operator-visibility polish for the existing inbox. It
does not duplicate a named `ROADMAP.md` milestone.
**Additional context**
The recursive summary query is guarded against parent cycles, and the UI
still falls back to loaded subtree live counts when server summary data
is absent or stale.
## What Changed
- Added optional `includeLiveDescendantSummary` support to issue list
contracts, SDK surfaces, MCP tools, routes, services, and tests.
- Added `liveDescendantCount` to issue list results when requested.
- Updated inbox and blocked-inbox queries to request live descendant
summaries.
- Updated inbox row status rendering so blocked parents with live
descendants show covered blocker treatment without duplicating the
live-below chip.
- Hardened live descendant summary traversal against parent cycles and
preserved the loaded-subtree fallback path for blocked inbox rows.
- Added focused tests for the API parameter, service behavior, helper
logic, cycle handling, and inbox UI query/rendering behavior.
## Verification
- `pnpm exec vitest run
server/src/__tests__/issue-list-assignee-filter-routes.test.ts
ui/src/lib/inbox-live-descendants.test.ts
ui/src/components/IssueColumns.test.tsx
ui/src/components/BlockedInboxView.test.tsx ui/src/pages/Inbox.test.tsx`
- `pnpm --filter @paperclipai/ui typecheck`
- Rebased cleanly onto current upstream `master` before pushing.
- Confirmed the branch diff does not include `pnpm-lock.yaml` or
`.github/workflows/*` changes.
## Risks
Low to moderate risk. The new descendant count is opt-in on list
requests, but it adds query work when the inbox asks for it. The
recursive traversal now tracks visited ancestors to avoid cycle
failures. The UI uses the server count as a supplement to existing
loaded-tree state, so stale or absent counts fall back to the prior
behavior.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5 coding agent, tool-enabled with local shell and git
access. Reasoning mode and context window are managed by the
Paperclip/Codex 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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Environments are now managed as instance-level runtime resources
rather than per-company rows
> - The custom environment image setup tables were introduced with their
own `company_id` columns and route query parameters
> - That split made one saved environment image state depend on an extra
company context even though the environment itself is the durable owner
> - It also made saved-environment probes harder because applying the
active custom image template could require a company context when no
secret-backed config needed one
> - This pull request scopes custom image templates and setup sessions
directly to the saved environment
> - The benefit is that reusable environment images follow the same
instance-scoped model as environments while secret resolution still uses
company context only when secrets require it
## Linked Issues or Issue Description
No matching public GitHub issue was found.
Bug report:
### What happened?
saved environment custom-image routes and persistence required a
`companyId` even though environments are instance-scoped, and saved
sandbox probes did not opt into active custom-image template application
unless a company context was present.
### Expected behavior
custom-image templates and setup sessions should be owned by the saved
environment, and saved sandbox probes should apply the active template
while still requiring a company context only for secret-backed runtime
config.
### Steps to reproduce
1. Configure an instance-scoped sandbox environment with custom-image
setup support.
2. Start or inspect a custom-image session or template for that saved
environment.
3. Probe the saved environment without a custom-image-specific
`companyId` query parameter.
### Paperclip version or commit
current `master` after the environment custom-image template migration.
### Deployment mode
Local dev (pnpm dev) or authenticated local Paperclip instance.
### Installation method
Built from source (pnpm dev / pnpm build).
### Agent adapter(s) involved
Not adapter-specific (core bug).
### Database mode
Embedded PGlite/Postgres dev database.
### Access context
Board human operator.
### Privacy checklist
No logs, secrets, tokens, private URLs, or local machine paths are
included.
Duplicate search performed:
- `gh search prs "environment custom image companyId
repo:paperclipai/paperclip" --state open --limit 20`
- `gh search prs "custom image environment scoped
repo:paperclipai/paperclip" --state open --limit 20`
- `gh search issues "environment custom image
repo:paperclipai/paperclip" --state open --limit 20`
The returned results were unrelated adapter, Docker, auth, or
stale-workspace items.
## What Changed
- Removed redundant `company_id` columns from environment custom-image
templates and setup sessions.
- Added migration `0127_environment_custom_images_instance_scoped` to
collapse duplicate active rows per environment before dropping the old
company-scoped indexes/columns.
- Updated custom-image services, route handlers, shared validators, and
UI API/query keys to use environment-scoped custom-image state.
- Kept runtime secret resolution company-aware only when secret refs or
bindings require a company context.
- Made saved sandbox environment probes opt into active custom-image
template application.
- Updated DB, shared, server, and UI tests for the new
environment-scoped contract.
## Verification
- `pnpm --filter @paperclipai/db run check:migrations`
- `pnpm exec vitest run
packages/db/src/environment-custom-images-schema.test.ts
packages/shared/src/environment-custom-images.test.ts
server/src/__tests__/environment-custom-image-routes.test.ts
server/src/__tests__/environment-custom-images-service.test.ts
server/src/__tests__/environment-routes.test.ts
ui/src/pages/CompanyEnvironments.test.tsx`
- `pnpm -r typecheck`
- `pnpm test:run` before rebasing onto latest `master`; after the rebase
only the migration number changed, and the migration check plus focused
suite, typecheck, and build were rerun.
- `pnpm build`
## Risks
- Migration safety: the migration supersedes duplicate active templates
per environment and fails duplicate active setup sessions before adding
environment-only unique indexes. Operators with duplicate historical
active rows should review which active template is kept.
- Behavior shift: plugin custom-image setup calls now receive
`companyId: "instance"` when no secret binding determines a concrete
company context.
- Secret-backed configs still require an explicit or uniquely inferable
company context; environments with secret bindings spread across
multiple companies continue to fail fast.
> 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 via the `codex_local` adapter, GPT-5-based coding model
with tool-enabled repository inspection, editing, testing, git, and
GitHub CLI access. Exact context-window metadata was not exposed by the
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
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Task/issue threads render each comment as a chat bubble; the author
determines whether it shows as a left-aligned agent bubble (name + icon)
or a right-aligned "Board" bubble
> - Comments posted by an agent from a local execution environment are
written with a non-human author id (`local-board`/system), so they were
mis-rendered as blue "Board" bubbles instead of being attributed to the
authoring agent
> - This misattribution is confusing (it looks like the human board said
something an agent actually said) and it can drive false
wake/reconciliation behavior on the affected threads
> - This pull request adds server-side attribution derivation (lossless
run-id join first, then an explicit run-log post marker), persists the
derived agent so the read path stops re-scanning run logs, and stops the
client from labeling agent-derived comments "Board"
> - The benefit is agent comments render as the correct agent, genuine
human board comments are never reattributed, and reads get cheaper after
a one-time persistence
## Linked Issues or Issue Description
<!-- No public GitHub issue — describing the problem in-PR (bug report
shape). -->
**What happened?**
In a task/issue comment thread, comments authored by an agent from a
local execution environment are stored with a non-human author id
(`local-board`/system). The UI renders these as right-aligned blue
"Board" bubbles, implying a human board member authored them. The
mislabeling is also a wake/reconciliation hazard: an agent comment that
reads as "Board" can look like human board input.
**Expected behavior**
Such comments should render as the authoring agent (left-aligned bubble
with agent name + icon). Genuine human/board comments must continue to
render as "Board" and must never be reattributed to an agent.
**Steps to reproduce**
1. Have an agent post a comment on an issue from a local execution
environment (author id `local-board`).
2. Open the issue comment thread in the UI.
3. Observe the agent's comment rendered as a right-aligned blue "Board"
bubble instead of the authoring agent.
**Root cause**
The read path did not resolve the authoring agent for these comments,
and the client fell back to a "Board" label for the `local-board`
author.
## What Changed
- **Server derivation (`server/src/services/issues.ts`):**
- Resolve the authoring agent from the comment's run id first
(`createdByRunId`/`derivedCreatedByRunId` → `heartbeatRuns.agentId`) —
lossless when present.
- Second tier `run_log_comment_post`: read the run log lazily (only for
still-unresolved comments) to match the explicit `comment id:` post
marker.
- **Guard:** never reattribute a comment whose author maps to a genuine
user profile. Only the non-human sentinel (`local-board`, which is
itself a `user` row) and authors absent from the `user` table are
eligible.
- Pure timing-overlap tiers are intentionally **not** used (Option A) —
an agent comment and a human board comment posted during the same run
are indistinguishable rows, so any timing guess risks mislabeling a real
human comment.
- **Persistence
(`packages/db/src/migrations/0126_issue_comment_derived_attribution.sql`,
`packages/db/src/schema/issue_comments.ts`):** add stored `derived_*`
attribution columns and write the resolved agent back with a single bulk
`UPDATE ... FROM (VALUES ...)`, so reads stop recomputing from run logs.
Migration is additive (new nullable columns) with a batched, idempotent
backfill of the lossless run-id tier over historical rows.
- **Types (`packages/shared/src/types/issue.ts`):** expose the persisted
attribution fields and the `IssueCommentDerivedAuthorSource` union.
- **Client (`ui/src/lib/issue-chat-messages.test.ts`):** the message
builder already prefers a resolved agent id (`authorAgentId ??
runAgentId ?? derivedAuthorAgentId`), so once the server persists the
derived agent the bubble renders as the agent automatically — no client
code change needed. Adds a regression guard confirming a genuine board
comment with no derived agent is still rendered as "Board".
- **Tests:** derivation + message-building tests, including assertions
that genuine board/user comments are **not** reattributed.
## Verification
- `cd server && npx vitest run issues-service` — 94 tests pass: run-id
resolution, no-attribution on timing overlap alone (Option A), multi-run
ambiguity, same-agent multi-run, and the genuine-user guard. Exercises
the real persistence path (bulk UPDATE) against the test DB.
- `cd ui && npx vitest run issue-chat-messages` — 27 tests pass; client
no longer labels agent-derived comments "Board", and a genuine board
comment with no derived agent is not re-labeled.
- `cd server && npm run typecheck` — passes (exit 0).
- Manual: on a thread containing old agent-authored comments, the blue
"Board" bubbles render as the authoring agent; a genuine board comment
on the same thread still renders as "Board".
## Risks
- **Mis-reattributing a genuine board comment made during an agent run**
→ mitigated by the human-profile guard (only `local-board`/system
authors are eligible) and by dropping pure timing tiers (Option A): only
the lossless run-id join and the explicit run-log post marker attribute
history.
- **Backfill volume / run-log reads** → the migration backfill is
batched (5000 rows/loop) and results are persisted so reads stop
recomputing; the read-path persistence is a single bulk UPDATE rather
than per-comment round-trips. Migration adds only nullable columns (no
destructive change).
- The persistence/backfill has **not** been run against any production
database as part of opening this PR.
## Model Used
Claude Opus 4.8 (`claude-opus-4-8`), extended thinking, via Claude Code
with tool use.
## 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 — related open
PRs (#6006 narrow attribution run scan, #4729 attribution roll-up, #7014
reaped-run attribution) address different attribution paths; none fix
the `local-board` "Board" bubble rendering this PR targets. Supersedes
#8832 (same change; branch renamed to drop an internal ticket id per
CONTRIBUTING → Branch Naming)
- [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 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 4.8 <noreply@anthropic.com>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Issue-thread interactions are how agents ask users or the board for
decisions and structured input
> - Product telemetry needs to understand when those interactions
resolve without exposing private interaction content
> - Resolution currently happens through several service paths, so
telemetry needs to be emitted consistently from the terminal transitions
> - The interaction service should describe the resolved interaction,
while the telemetry backend owns unknown-value normalization for
dimensions
> - This pull request emits `interaction.resolved` after successful
database writes and removes redundant client-side normalization from the
service
> - The benefit is aggregate-safe telemetry for interaction completion
behavior without leaking raw IDs, answer text, rejection reasons, or
document content
## Linked Issues or Issue Description
No public GitHub issue exists for this internal telemetry follow-up.
Feature context:
- Problem/motivation: Paperclip needs aggregate product telemetry for
issue-thread interaction resolution outcomes while preserving privacy
boundaries around user answers and internal identifiers.
- Proposed solution: Emit `interaction.resolved` once from terminal
interaction resolution paths, passing runtime dimensions through the
shared telemetry helper while preserving aggregate-safe counts and
ID/free-text omission.
- Alternatives considered: Normalizing interaction dimensions in the
interaction service duplicated telemetry backend responsibility and made
unknown-value handling inconsistent across telemetry clients.
- Roadmap alignment: This is a focused telemetry instrumentation
follow-up that builds on the generated telemetry event types from #8818.
## What Changed
- Wires `interaction.resolved` telemetry into terminal issue-thread
interaction resolution paths after successful database writes.
- Passes raw interaction kind, status, continuation policy, resolution
reason, target type, and creator agent role values to the shared
telemetry helper instead of maintaining service-local allowlists.
- Preserves resolver classification, target `none` derivation for
non-confirmation interactions, non-negative aggregate counts, raw ID
omission, and free-text omission.
- Logs telemetry failures without blocking interaction resolution.
- Adds service-level tests for accepted, rejected, answered,
stale-target expiry, superseded-comment expiry, and raw creator-role
pass-through payloads.
## Verification
- `pnpm run preflight:workspace-links && pnpm exec vitest run
server/src/__tests__/issue-thread-interactions-telemetry.test.ts
server/src/__tests__/shared-telemetry-events.test.ts`
- `pnpm typecheck`
- GitHub PR checks on the latest head commit are green, including
`verify`, build, e2e, general tests, serialized server suites, security
scans, and Greptile Review.
- Security code review completed before this branch update.
## Risks
- Low operational risk: telemetry is emitted after successful
persistence and telemetry failures are logged without blocking the
user-visible interaction flow.
- Main behavioral risk is duplicate or missing telemetry from a
resolution path; the focused tests cover the terminal resolution
variants.
- Telemetry dimension normalization now depends on the shared telemetry
backend path instead of the interaction service, so backend
normalization must remain the source of truth for unknown or empty
dimension values.
- The existing PR branch name contains an internal task id because this
update continues an already-open PR branch instead of opening a
replacement PR.
## Model Used
OpenAI GPT-5 Codex coding agent, API-based coding environment with
shell, repository, and GitHub CLI tool use. Context window size was not
reported by the 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)
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Telemetry is part of the control plane's operational visibility and
needs stable event contracts.
> - The shared telemetry client accepted first-party event names through
a broad string surface, which weakened compile-time guarantees.
> - Plugin telemetry still needs a dynamic path because plugin-defined
events cannot be enumerated in the core generated type module.
> - This pull request vendors generated Paperclip telemetry event and
dimension types, closes the first-party event-name union, and keeps
plugin telemetry on an explicit dynamic method.
> - Review feedback clarified that backend normalization should remain
the source of truth, so telemetry helpers now preserve raw categorical
values while keeping generated per-event type hints.
> - The benefit is stricter first-party telemetry typing without hiding
backend normalization signals or changing batching, flushing, schema
versioning, sinks, or endpoints.
## Linked Issues or Issue Description
No public GitHub issue exists for this internal type-contract
maintenance change.
### Problem or motivation
The shared telemetry client should reject unregistered first-party event
names at compile time, while the plugin telemetry bridge must continue
to emit plugin-defined events through the existing batching and envelope
path. Helper wrappers should also avoid client-side enum coercion so the
backend can detect and record normalization when clients send unexpected
categorical values.
### Proposed solution
Generate and vendor the accepted Paperclip telemetry event and dimension
types, use those types for the first-party `track()` API, keep
plugin-defined telemetry on an explicit dynamic method, and let helper
wrappers pass raw categorical dimensions through to backend validation.
### Alternatives considered
Keeping `track()` open to arbitrary strings would preserve flexibility,
but it would not give first-party callers the type safety this change is
meant to provide. Enumerating plugin events in core was also ruled out
because plugin-defined events are not known to the core package.
Client-side enum normalization was removed after review because it
duplicates backend validation and can hide misbehaving-client signals.
### Roadmap alignment
This is a tightly scoped telemetry contract maintenance change and does
not overlap with a roadmap-level core feature.
## What Changed
- Vendored the generated Paperclip telemetry event and dimension type
module under shared telemetry code.
- Closed the first-party telemetry event-name union to generated
backend-accepted names plus an explicit `RegisteredPluginEventName =
never` extension point.
- Added `TelemetryClient.trackDynamic()` for plugin telemetry bridge
emission while keeping `track()` closed and typed.
- Added JSDoc explaining when to use `track()` versus `trackDynamic()`.
- Updated telemetry helper wrappers to type dimensions from each event's
generated schema entry while passing raw categorical values through for
backend normalization.
- Added `trackInteractionResolved()` and updated focused shared/server
tests for telemetry event typing, raw pass-through behavior, and plugin
telemetry bridging.
## Verification
Local verification passed before the latest push:
- `pnpm --filter @paperclipai/shared typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm exec vitest run
packages/shared/src/telemetry/client-types.test.ts
server/src/__tests__/shared-telemetry-events.test.ts
server/src/__tests__/plugin-telemetry-bridge.test.ts
server/src/__tests__/project-goal-telemetry-routes.test.ts
server/src/__tests__/routine-run-telemetry.test.ts
server/src/__tests__/issue-telemetry-routes.test.ts`
- `git diff --check`
Post-push verification completed on head
`3d973ffbea6154b19ad208dcffd1374d1b25b654`:
- GitHub PR checks passed, including `verify`, build, typecheck/release
registry, general test shards, serialized server shards, canary dry run,
e2e, and security checks.
- Greptile Review passed with 5/5 confidence.
- All PR review threads are resolved.
## Risks
Low runtime risk. The change is intended to affect TypeScript contracts
and helper typing while preserving the existing telemetry enqueue,
batching, and backend ingest path. The main intentional behavior shift
is that helper wrappers no longer coerce unexpected categorical values
on the client; those values reach the backend so backend normalization
can record the signal. Private company import source refs still use
`hashPrivateRef` when `isPrivate` is true.
## Model Used
OpenAI GPT-5 Codex, tool-enabled coding agent. Exact context window was
not exposed by the runtime; the agent used repository file access, shell
commands, and GitHub CLI operations.
## 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Issue-thread interactions are how agents ask board users for typed
decisions and structured answers inside an issue thread
> - Confirmation interactions already become stale when a later
board/user comment supersedes the pending decision
> - Question interactions had the same workflow risk, because a
board/user could answer in a comment while the old question card stayed
pending
> - This pull request extends the supersede-by-comment lifecycle to
ask-user-question interactions and makes that status visible in the UI
> - The benefit is agents get a clear continuation signal and users do
not see stale question forms after the discussion has moved on
## Linked Issues or Issue Description
No exact public GitHub issue was found.
Bug report:
**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
an agent adapter, API provider, or local configuration.
**What happened?**
Pending `ask_user_questions` interactions could remain open after a
later board/user comment changed or answered the request in-thread. That
left a stale form visible and kept the interaction in a pending state
even though the discussion had moved on.
**Expected behavior**
Question interactions should follow the same default
supersede-on-comment behavior as confirmation interactions, with an
explicit expired result that points to the superseding comment.
**Steps to reproduce**
1. Create an `ask_user_questions` interaction on an issue.
2. Add a board/user comment created at or after that interaction.
3. Observe that before this change, the question interaction stayed
pending instead of expiring as superseded by the comment.
**Paperclip version or commit**
Current `master` before this PR.
**Deployment mode**
Self-hosted server or local dev. The bug is in shared issue-thread
interaction lifecycle handling.
**Installation method**
Built from source.
**Agent adapter(s) involved**
Not adapter-specific. This is a core issue-thread interaction bug.
**Database mode**
Applies to the normal Paperclip database-backed interaction lifecycle.
**Access context**
Board user comments supersede agent-created questions.
**Relevant logs or output**
No crash output. The stale pending interaction was visible in the issue
thread state.
**Relevant config (if applicable)**
None.
**Additional context**
Confirmation-style interactions already supported this stale-by-comment
behavior. This PR brings question interactions into the same lifecycle
model.
**Privacy checklist**
- [x] I have reviewed all pasted output for PII and included no private
instance links, local ticket ids, secrets, logs, or screenshots.
## What Changed
- Added `supersedeOnUserComment` support to `ask_user_questions`
payloads, defaulting it to `true` during interaction creation.
- Expire pending question interactions when a later board/user comment
supersedes them, including a result with `expirationReason:
"superseded_by_comment"` and the superseding `commentId`.
- Updated interaction summaries and cards so expired question requests
show a clear amber state with a jump link to the comment and correct
singular/plural copy.
- Updated agent onboarding guidance to describe the new default and how
to opt out.
- Added shared, server, and UI test coverage for the new lifecycle
behavior.
## Verification
- `pnpm exec vitest run
packages/shared/src/issue-thread-interactions.test.ts
server/src/__tests__/issue-thread-interaction-routes.test.ts
server/src/__tests__/issue-thread-interactions-service.test.ts
ui/src/components/IssueThreadInteractionCard.test.tsx
ui/src/lib/issue-thread-interactions.test.ts --reporter=dot` passed: 5
files, 73 tests.
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/server typecheck && pnpm --filter @paperclipai/ui
typecheck` passed.
- `pnpm exec vitest run
server/src/__tests__/issue-thread-interactions-service.test.ts
--reporter=dot && pnpm --filter @paperclipai/server typecheck` passed
after the final type-safety cleanup.
- Confirmed the branch is rebased on current `origin/master`.
- Confirmed the diff does not touch `pnpm-lock.yaml`,
`.github/workflows`, or database migrations.
## Risks
- Low-to-medium risk: `ask_user_questions` now defaults to expiring
after later board/user comments. Existing callers that need questions to
stay open through discussion can set `supersedeOnUserComment: false`.
- Expired question interactions store an empty `answers` array, so
downstream consumers should treat the explicit `expirationReason` as the
meaningful outcome.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based coding agent in Paperclip CodexCoder runtime,
with terminal and repository tool use. Exact context window is not
exposed in 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent runs are assembled by the heartbeat service from agent config,
project workspaces, environment config, secret bindings, skills, and
runtime session state.
> - The heartbeat service intentionally reuses adapter sessions,
execution workspaces, and sandbox leases when that preserves useful
state.
> - Reuse becomes incorrect when the effective next-run config changes
after a saved session, workspace, or lease was created.
> - Stale reuse can make a later run appear pinned to old agent,
environment, secret, instruction, or workspace settings.
> - This pull request records non-sensitive fingerprints for the
effective session, workspace, and lease config at run boundaries.
> - When those fingerprints drift, Paperclip refreshes persisted runtime
config or starts fresh execution instead of reusing stale state.
> - The benefit is predictable next-run config freshness without storing
raw secret values, full env maps, provider credentials, or private path
details.
## Linked Issues or Issue Description
- Refs #8058
- Related PRs checked during dedup search: #4968, #4155, #84, #8480.
These cover nearby workspace/session routing or model-config freshness
areas, but do not duplicate this effective run config fingerprinting
path.
## What Changed
- Added effective run config fingerprinting for session, workspace, and
lease reuse decisions, with canonicalization that ignores generated
runtime noise and redacts sensitive values.
- Updated heartbeat reuse logic to compare stored and next-run
fingerprints, reset stale saved sessions, refresh persisted workspace
config snapshots, replace stale reused workspaces when required, and
avoid stale sandbox lease reuse.
- Included plain environment value drift via value hashes, without
storing the raw env values.
- Root-bound instruction content hashing so legacy direct absolute
instruction paths are represented but not read for config fingerprints.
- Batched secret/version metadata lookups for environment lease
fingerprinting.
- Added workspace operation/run result freshness metadata so operators
can inspect non-sensitive decision categories.
- Surfaced config freshness labels and next-run copy in the UI and docs.
- Added focused coverage for fingerprint redaction, session reset
decisions, workspace refresh/replace behavior, environment lease drift,
and persisted workspace restoration.
## Verification
- `git diff --check`
- Sensitive-data scan before push:
- `git diff --unified=0 origin/master...HEAD | rg -n --pcre2
"(AWS_ACCESS_KEY_ID|AWS_SECRET_ACCESS_KEY|ghp_[A-Za-z0-9_]{20,}|github_pat_[A-Za-z0-9_]{20,}|sk-[A-Za-z0-9]{20,}|-----BEGIN
(RSA |OPENSSH |EC |DSA )?PRIVATE KEY-----|AKIA[0-9A-Z]{16})"`
- `git diff --unified=0 origin/master...HEAD | rg -n --pcre2
"[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\\.[A-Za-z]{2,}"`
- `pnpm exec vitest run
server/src/__tests__/effective-run-config-fingerprints.test.ts
server/src/__tests__/heartbeat-workspace-session.test.ts
server/src/__tests__/environment-runtime.test.ts`
- `pnpm --filter @paperclipai/server typecheck`
- `pnpm -r typecheck`
- `pnpm --filter @paperclipai/db clean`
- `pnpm test:run`
- `pnpm build`
- UI screenshots from Cutter:
-
https://artifacts.cutter.sh/8797/run-2f4827c-2026-06-30T18-57-25/preview/change-01.png
-
https://artifacts.cutter.sh/8797/run-2f4827c-2026-06-30T18-57-25/preview/change-02.png
-
https://artifacts.cutter.sh/8797/run-2f4827c-2026-06-30T18-57-25/preview/change-03.png
## Risks
- Medium: overly broad fingerprints could start fresh sessions,
workspaces, or sandbox leases more often than necessary.
- Medium: missing a config category would allow stale reuse to persist
for that category.
- Medium: legacy direct absolute instruction paths are no longer
content-hashed unless they are paired with an absolute managed
instructions root.
- Low data risk: fingerprint metadata stores hashes and category names,
not raw secrets, raw env values, provider credentials, or private path
details.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI GPT-5 via Codex CLI / Codex coding agent, tool-enabled with
shell, Git, GitHub CLI, local test execution, and code editing. The
exact deployed model variant and context window are 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>
Co-authored-by: Cody <cody@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - A growing part of that work runs in sandboxed environments rather
than on the operator's local machine.
> - Today sandbox providers can start fresh workspaces and run probes,
but they do not have a shared contract for capturing and reusing
prepared sandbox state.
> - Operators need a way to set up tools, credentials, and project
dependencies once, then reuse that prepared image for later agent runs.
> - This pull request adds reusable sandbox custom images across the
provider contract, server runtime, and board UI.
> - It also keeps probes and sandbox copy flows aligned with
pre-authenticated/custom-image environments.
> - The benefit is faster, more reliable sandbox runs without repeatedly
rebuilding the same environment setup.
## Linked Issues or Issue Description
No public GitHub issue was found for this change. Inline feature request
follows.
### Problem or motivation
Sandboxed agents need reusable prepared runtime state so repeated runs
do not require manual setup every time. Operators often need system
packages, CLIs, SDKs, dependency caches, credentials, and project
tooling available before an agent can work productively.
### Proposed solution
Add a provider-level custom-image capability, server-side setup/capture
lifecycle, Daytona/fake provider support, and board UI controls for
creating, testing, selecting, and deleting custom images.
### Alternatives considered
Leaving this as provider-specific setup outside Paperclip would keep the
control plane blind to image state and would not give agents consistent
environment metadata. Re-running setup commands for every lease is
simpler, but slower and less reliable for interactive or credentialed
setup.
### Roadmap alignment
Checked `ROADMAP.md`; this aligns with the Cloud / Sandbox agents
roadmap area and does not duplicate any related public issue or PR found
by search.
Additional context:
- Subsystem affected: cross-cutting (`packages/db`, `packages/shared`,
`packages/plugins`, `server`, `ui`).
- Duplicate search: searched GitHub for `sandbox custom image` and
`sandbox template environment`; no related public issues or PRs were
found.
## What Changed
- Added custom-image shared types, validators, constants, API paths, and
database schema/migration.
- Added server services/routes for custom-image templates and setup
sessions, including runtime cleanup and provider metadata handling.
- Extended plugin/sandbox provider capabilities for interactive setup,
template capture, and template deletion.
- Implemented custom-image support in the fake sandbox provider and
Daytona provider.
- Updated environment runtime/config handling so active custom images
flow into leases, probes, and agent execution.
- Added board UI controls and API client support for custom-image setup,
capture, selection, status, and error states.
- Hardened sandbox copy/probe behavior for insecure clipboard contexts
and pre-authenticated sandbox images.
- Added targeted coverage across shared validators, DB schema, server
routes/services, provider plugins, adapter probes, and UI flows.
## Verification
- `pnpm install --frozen-lockfile --ignore-scripts`
- `pnpm vitest run
packages/adapters/claude-local/src/server/test.probe.test.ts
packages/adapters/claude-local/src/server/test.ts
packages/adapters/codex-local/src/server/test.remote.test.ts
packages/adapters/codex-local/src/server/test.ts`
- `pnpm --filter @paperclipai/adapter-claude-local typecheck`
- `pnpm --filter @paperclipai/adapter-codex-local typecheck`
- `pnpm vitest run
packages/db/src/environment-custom-images-schema.test.ts
packages/shared/src/environment-custom-images.test.ts
packages/shared/src/validators/plugin.test.ts
server/src/__tests__/environment-custom-images-service.test.ts
server/src/__tests__/workspace-runtime.test.ts
packages/plugins/sandbox-providers/daytona/src/plugin.test.ts
ui/src/pages/CompanyEnvironments.test.tsx
ui/src/pages/CompanySettings.test.tsx`
- `pnpm -r typecheck`
- `pnpm build`
- `rm -rf packages/db/dist && pnpm test:run`
- Public-safety scan of the final diff found no internal Paperclip issue
links, private instance URLs, or real secret patterns.
## Risks
- Adds a database migration and new environment runtime tables, so
migration ordering and rollback need care.
- Provider implementations may differ in how reliably they can
capture/delete images; unsupported providers surface capability-gated UI
states.
- Custom-image state can contain operator-prepared tooling and
credentials inside the provider image, so providers must enforce their
own access controls and cleanup semantics.
- Broad surface area across shared contracts, server runtime, plugins,
adapters, and UI means CI and Greptile review should be watched closely.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI GPT-5 (`gpt-5`) via Codex CLI with tool use and code execution.
Assisted with branch cleanup, conflict resolution, local verification,
and PR preparation. Earlier branch implementation work was assisted by
Paperclip-managed Claude/Codex agents.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Pipeline automations let operators standardize repeated issue and
workflow actions.
> - Pipeline-created issues currently need a way to derive useful titles
from routine variables.
> - Without a configurable title template, automated pipeline output is
harder to scan and distinguish.
> - This pull request adds a title-template field through shared
contracts, server persistence, API routes, and the pipeline settings UI.
> - The benefit is clearer issue titles for pipeline-created work while
preserving the existing pipeline behavior when no template is
configured.
## Linked Issues or Issue Description
Refs #8790
This PR adds configurable generated-issue title templates for pipeline
automations.
## What Changed
- Added `issueTitleTemplate` to the shared pipeline automation contract
and field constants.
- Persisted and returned the title template through pipeline service and
route code.
- Applied title-template rendering when pipeline automations create
issue work.
- Added pipeline settings UI controls for editing the title template and
reusing routine variables.
- Moved title-token cursor restoration out of the React state updater
and into a layout effect.
- Added server and UI coverage for storing, returning, and rendering
pipeline title templates.
## Verification
- `NODE_ENV=test pnpm run preflight:workspace-links && NODE_ENV=test
pnpm exec vitest run server/src/__tests__/pipelines-service.test.ts
server/src/__tests__/pipelines-routes.test.ts
ui/src/pages/PipelineSettings.test.ts`
- Result before review follow-up: 3 files passed, 58 tests passed.
- `pnpm --filter @paperclipai/ui exec vitest run
src/pages/PipelineSettings.test.ts`
- Result after review follow-up: 1 file passed, 7 tests passed.
- Branch was merged with current `paperclipai:master` at `f019f54bb3`
before opening this PR.
- Searched existing PRs for the same head branch and for pipeline
title-template duplicates; no matching existing PR was found.
- Note: GitHub could not open a PR directly from `cryppadotta/paperclip`
because that repository is not a fork of `paperclipai/paperclip`. The
same updated branch SHA was pushed to `paperclipai/paperclip` so this PR
can compare normally against `master`.
## Risks
Low to moderate risk. The change touches pipeline automation persistence
and generated issue creation, so regressions would most likely appear as
missing or incorrectly rendered generated issue titles. Existing
behavior should remain unchanged when `issueTitleTemplate` is unset.
## Model Used
OpenAI Codex, GPT-5-based coding agent, tool-enabled execution in a
Paperclip heartbeat, with repository inspection, Git, GitHub CLI, and
local test execution.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The experimental server info debug view helps local operators
inspect what code a running dev instance is actually serving
> - The view was moved into the account-menu drawer, which only mounts
while that drawer is open
> - That made stale health-query data easier to see after restarts, and
the server was also caching the running commit at process boot
> - A clean commit label alone is incomplete when the checkout has
uncommitted local changes
> - This pull request keeps the drawer health data fresh, refreshes git
metadata on demand, and adds a path-free checkout-state summary
> - The benefit is that the debug view reports restart time, running
commit, and dirty-checkout state without exposing local paths, secrets,
logs, or environment details
## Linked Issues or Issue Description
Fixes: #8752
## What Changed
- `SidebarServerInfo.tsx`: refetch the health query whenever the drawer
opens and poll every 2s while the dev server is active.
- `server-info.ts`: keep `processStartedAt` stable while refreshing git
HEAD through a short TTL cache instead of freezing commit metadata at
module boot.
- Shared health contract/OpenAPI: add `serverInfo.git.localChanges` with
only staged, unstaged, and untracked counts plus safe unavailable
fallbacks.
- `SidebarServerInfo.tsx`: add a `Checkout state` row that renders
clean/dirty/unavailable copy without file paths.
- Tests: cover stale drawer refresh, interval polling, TTL commit
refresh, health response shape, checkout-state count parsing, and
path-free UI rendering.
## Verification
- `npx vitest run server/src/__tests__/server-info.test.ts
server/src/__tests__/health.test.ts
ui/src/components/SidebarServerInfo.test.tsx` -> 3 files / 19 tests
passing.
- `pnpm install --frozen-lockfile --ignore-scripts` -> refreshed stale
workspace links without lockfile/source churn.
- `pnpm --filter @paperclipai/shared --filter @paperclipai/server
--filter @paperclipai/ui typecheck` -> passing.
- `pnpm --filter @paperclipai/ui typecheck` -> passing after the
Greptile test-coverage fix.
- `pnpm check:tokens` -> no forbidden tokens found.
- Local diff scans for obvious secrets, credentials, private URLs, local
paths, and PII patterns -> no matches.
- GitHub PR checks on head `56defd446` -> all green, including `verify`,
canary dry run, e2e, security scans, and Greptile Review.
- Greptile latest summary -> Confidence Score 5/5, 0 new comments; the
prior P2 polling-coverage thread is resolved.
## Risks
Low risk. The UI remains behind the experimental
`enableServerInfoDebugView` flag. The extra git status call is throttled
by the existing server-info TTL and reports only counts, not paths or
file names. If git status is unavailable, the commit row still works and
the checkout-state row shows clear fallback copy.
## Model Used
Claude Opus (claude-opus-4-8), extended thinking, with tool use / code
execution assisted the original stale-metadata fix. OpenAI GPT-5 via
Codex local, with tool use and code execution, added the checkout-state
follow-up, Greptile fix-up, and PR 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Planning work relies on issue documents, request-confirmation
interactions, and inline plan annotations
> - Agents can be woken after a plan comment, annotation, or
confirmation decision
> - The wake payload needs enough plan-review context for the agent to
act on the specific feedback instead of losing the thread and falling
back to broad refetches
> - This pull request adds bounded plan-review context to wake payloads
and heartbeat context
> - It also teaches the adapter wake prompt renderer to surface those
open plan annotations and interaction results directly
> - The benefit is that agents can continue plan review and plan
acceptance flows with the relevant comments in hand while keeping wake
payloads bounded and company-scoped
## Linked Issues or Issue Description
No matching public GitHub issue was found.
### Subsystem affected
Cross-cutting: `server/`, `packages/shared`, and
`packages/adapter-utils`.
### Problem or motivation
Plan-review continuations can wake an agent after a plan comment, inline
annotation, or request-confirmation decision without enough inline
context about the open plan annotations or accepted/rejected
confirmation target. That makes scoped wakes less reliable because the
agent may need to refetch broad issue history before it can tell what
feedback should be incorporated.
### Proposed solution
Include bounded, company-scoped plan review context in wake payloads and
heartbeat context. The context includes open `plan` annotation threads,
recent annotation comments, truncation metadata, and plan-confirmation
interaction target/result details. Render that information in the
adapter wake prompt so agents see the relevant plan-review feedback
immediately.
### Alternatives considered
Relying on agents to fetch the full issue thread after every plan-review
wake was rejected because it is slower, harder to audit, and easier to
mishandle when the wake is meant to be scoped to a specific comment,
annotation, or interaction result.
### Roadmap alignment
This supports the roadmap areas for Agent Reviews and Approvals, Deep
Planning, and Enforced Outcomes by making plan approval continuations
explicit and actionable.
### Additional context
The implementation keeps payload size bounded with per-thread,
per-comment, and total-body limits. Resolved annotation threads are
intentionally omitted so the wake focuses on feedback still needing
action.
## What Changed
- Added shared `PlanReviewContext` types for plan annotation threads,
comments, interaction targets, and continuation results.
- Added server-side plan review context assembly for open `plan`
annotation threads with bounded thread/comment/body limits.
- Included plan review context in heartbeat context and scoped wake
payloads for planning, annotation, comment, and plan-confirmation
interaction wakes.
- Rendered plan annotation deltas, open plan comments, interaction
results, and accepted target revisions in adapter wake prompts.
- Added focused regression coverage for scoped plan review context, wake
prompt rendering, annotation filtering, and safe standard-mode
annotation wakes.
- Addressed Greptile feedback by bounding the plan-comment DB fetch and
removing unused plan review context input fields.
## Verification
- `pnpm run preflight:workspace-links`
- `pnpm exec vitest run --project @paperclipai/adapter-utils
packages/adapter-utils/src/server-utils.test.ts`
- `pnpm exec vitest run --project @paperclipai/server
--no-file-parallelism --maxWorkers=1
server/src/__tests__/document-annotations-service.test.ts
server/src/__tests__/issue-thread-interaction-routes.test.ts
server/src/__tests__/issues-goal-context-routes.test.ts`
- `pnpm --filter @paperclipai/shared typecheck`
- `pnpm --filter @paperclipai/adapter-utils typecheck`
- `pnpm --filter @paperclipai/server typecheck`
- `git diff --check public-gh/master...HEAD`
- GitHub PR checks are green on
`36d0ac6a5dce27b9d62e201bf6d9829170c5974e`n- Rebased onto current
`paperclipai/paperclip:master` and confirmed GitHub reports the PR as
mergeable
- Greptile Review completed successfully after 2 comments were addressed
and resolved; 0 unresolved review threads remain
## Risks
- Medium: wake payloads now include additional plan-review data, so
limits and truncation behavior need to stay conservative as annotation
volume grows.
- Low migration risk: no database schema or migration changes.
- Low repository hygiene risk: this PR does not touch `pnpm-lock.yaml`,
`.github/workflows`, or media assets.
> 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 using `gpt-5` as a coding agent with shell/tool execution.
Reasoning mode and exact context window were not exposed by the 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - The dev/server UI exposes a `/api/health` endpoint and a lower-left
account drawer, but nothing surfaces *which* build the running instance
is on or when it last restarted
> - When iterating on a local dev instance it is hard to tell whether
the server you're looking at has actually restarted onto your latest
commit, or how stale the running process is
> - Developers need a lightweight, opt-in way to confirm the running
instance's identity without digging through logs or shelling into the
host
> - This pull request adds an experimental "Server Info Debug View"
setting that surfaces the running instance's last-restart time and
current commit as read-only rows in the account drawer
> - The benefit is a quick, in-UI sanity check of what the live server
is actually running, behind an experimental flag so it ships zero cost
to users who don't opt in
## Linked Issues or Issue Description
No public GitHub issue exists. Describing the underlying request inline
following the feature request template:
**Problem or motivation:**
When working against a local Paperclip dev instance there is no in-UI
way to confirm what the running server is — its current commit or when
it last restarted. You have to check logs or the host shell to know
whether the process picked up your latest build.
**Proposed solution:**
An opt-in experimental setting ("Server Info Debug View") that, once
enabled, renders a small read-only "Server" section at the bottom of the
lower-left account drawer showing **Last restarted** (the server process
start time) and **Running commit** (the current git HEAD short SHA +
subject).
**Alternatives considered:**
A separate top-right pill/overlay (like the work-life-balance plugin).
The account drawer was chosen to reuse existing menu-row styling and
avoid adding new always-present chrome.
**Roadmap alignment:**
Small, self-contained developer-experience aid gated behind an
experimental flag; does not overlap planned core roadmap work.
## What Changed
- Added `server/src/server-info.ts`: captures a `serverInfo` snapshot
once at boot — process start time and current git commit (SHA +
subject). Git is read via `execFileSync` with SHA validation and a
timeout.
- `/api/health` exposes the `serverInfo` snapshot, but only on
full-details health responses (board/agent in authenticated mode, or
local-trusted dev).
- Gated the UI surface behind a new `enableServerInfoDebugView`
experimental setting, wired through the shared instance type, validator,
settings normalizer, and OpenAPI schema.
- UI: added `SidebarServerInfo` rendering the read-only rows in the
account drawer (`BreadcrumbBar` / `SidebarAccountMenu`), plus the
experimental settings toggle and a typed `health` API client.
- Moved `ServerGitInfo` / `ServerInfoSnapshot` into
`@paperclipai/shared` so the server and UI share one definition instead
of duplicating it.
- Added unit tests for the server-info snapshot, health route exposure,
validator/normalizer, settings routes, the experimental settings page,
and the sidebar component.
## Verification
- `pnpm --filter @paperclipai/server exec vitest run
src/__tests__/health.test.ts src/__tests__/server-info.test.ts
src/__tests__/instance-settings-service.test.ts
src/__tests__/instance-settings-routes.test.ts` — 32 passed
- `pnpm --filter @paperclipai/ui exec vitest run
src/components/SidebarServerInfo.test.tsx
src/pages/InstanceExperimentalSettings.test.tsx` — 9 passed
- `tsc --noEmit` on both `@paperclipai/server` and `@paperclipai/ui` —
clean
- Manual: enable **Settings → Experimental → Server Info Debug View**,
refresh the UI, open the lower-left account drawer — a "Server" section
shows Last restarted and Running commit.
## Risks
- Low risk. The UI surface is fully opt-in via an experimental flag and
defaults off.
- The `serverInfo` field on `/api/health` is access-controlled to
full-details responses only (board/agent in authenticated mode, or
local-trusted dev) — never anonymous authenticated callers — so the git
SHA is not broadly exposed.
- The only new server work is a one-time git read at boot, guarded with
SHA validation and a timeout; failures degrade gracefully (the git block
reports `available: false` rather than throwing).
## Model Used
Claude — `claude-opus-4` (Anthropic), extended thinking with tool use,
via Claude Code.
## 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
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
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Agent adapters are the boundary between the control plane and the
runtimes that actually do work.
> - Hermes support needs to be available as first-class local and
gateway adapters while still preserving the adapter-manager override
path for external packages.
> - The adapter work touches runtime execution, UI adapter metadata,
onboarding prompts, scoped credentials, release packaging, and smoke
coverage, so the handoff needs concrete verification rather than only
unit tests.
> - This pull request adds built-in Hermes local and Hermes gateway
support, keeps external adapter overrides compatible, and
documents/tests the gateway flow end to end.
> - The benefit is that operators can hire Hermes-backed agents without
a manual plugin install, while self-hosted installs can still
override/shadow the built-ins through Adapter manager packages.
## Linked Issues or Issue Description
No public GitHub issue exists for this exact Hermes built-in adapter,
gateway onboarding, and release-source work.
Problem description:
- Hermes local and gateway adapters need a public, reviewable source
path in the monorepo so package artifacts and built-in adapter behavior
match the application source.
- Operators need built-in `hermes_local` and `hermes_gateway` adapter
choices without losing the ability to install external Hermes packages
as overrides.
- Gateway onboarding needs secure defaults for API server URLs, API
keys, and generated agent setup text.
- Hermes-originated task bridge credentials need narrower API-key scope
configuration.
- Related public PRs found during duplicate search include #3027, #2363,
#7544, #7950, #8095, and #8543.
## What Changed
- Added the unified Hermes adapter package with local and gateway
server/UI/CLI exports, config schemas, transcript parsing, model
detection, and package metadata.
- Registered `hermes_local` and `hermes_gateway` as built-in adapters
across shared constants, server registries, CLI packaging, and UI
adapter registries.
- Kept the external adapter override path compatible so installed Hermes
packages can shadow built-ins and restore the built-in parser when
disabled.
- Added Hermes gateway onboarding docs, board-operator docs, Docker
smoke assets, and shell smoke harnesses for join/e2e validation.
- Added scoped task-bridge API-key support, authorization checks,
issue-origin handling, and tests for Hermes-created Paperclip tasks.
- Hardened gateway transport and redaction behavior for API keys,
headers, session data, and smoke diagnostics.
- Updated release packaging/bootstrap checks for the Hermes packages
while leaving `pnpm-lock.yaml` out of the PR per repository policy.
## Verification
Targeted local verification recorded before PR handoff:
- `pnpm --filter @paperclipai/hermes-paperclip-adapter exec vitest run
src/gateway/server/execute.test.ts` — 14/14 passed.
- `pnpm test:hermes-gateway-smoke` — 6/6 passed.
- Hermes package typecheck/build checks passed.
- Focused server/UI adapter tests passed — 31/31.
- Release helper Node tests passed — 18/18.
- `git diff --check origin/master..HEAD` passed.
Fresh Docker E2E smoke evidence:
- Ran `pnpm smoke:hermes-gateway-e2e` on 2026-06-26 with a fresh state
directory and fresh Docker container against a live Paperclip dev
server.
- Hermes direct execution reached `completed`.
- Hermes stop/cancel path reached `cancelled`.
- Hermes gateway created a Paperclip task, Paperclip ran the Hermes
agent, and the task reached `done` with the expected marker response.
- Temporary board auth keys, token files, smoke state, and Docker
containers were cleaned up after the run.
PR checks on head `b5eae40ce`:
- GitHub Actions passed: `policy`, `review`, `Typecheck + Release
Registry`, all general test shards, all serialized server shards,
`Build`, `Canary Dry Run`, `e2e`, and aggregate `verify`.
- External checks passed: Snyk and Socket Project Report.
- External Socket Pull Request Alerts remained pending after the
first-party CI matrix completed.
## Risks
- Medium risk: this spans adapter registration, package publishing,
gateway execution, onboarding docs, API-key scoping, and UI adapter
metadata.
- Migration risk is low: the scope-config migration adds a nullable
column and does not rewrite existing keys.
- Gateway execution depends on operator-provided Hermes API
configuration; the smoke covers the Docker gateway path but real
deployments may differ by network/auth setup.
- Direct Greptile review on the latest expanded diff is file-count
limited, although the commitperclip review gate passed.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5 coding agent, tool use enabled in a local repository
workspace. Context window size is not exposed in 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] Commitperclip review gate is green; direct Greptile review is
file-count limited on the latest expanded diff
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The pipeline subsystem models repeatable work as items moving
through stages, with agent automation, review gates, blockers, drift
notices, and linked work.
> - Operators need this to be usable as one coherent workflow surface,
not just as backend primitives or disconnected route experiments.
> - The branch now carries the pipeline data model, service/routes,
CLI/tutorial path, aggregation feeds, operator UI, stage automation
controls, liveness/retry handling, and follow-up polish that make the
primitive reviewable end to end.
> - This pull request is the single review target for that pipeline
workflow primitive stack.
> - The benefit is that reviewers can evaluate the full operator
experience and server contract together against `master`.
## Linked Issues or Issue Description
No public GitHub issue exists for this work. The underlying feature
request is described inline.
### Problem or motivation
Paperclip needs a first-class way to model multi-stage agent/company
workflows where upstream items can spawn downstream work, request
review, carry fields across pipelines, surface drift, retry automation,
and show operators where work is blocked or active. Without a unified
pipeline primitive, these workflows spread across ad hoc issues,
routines, and comments, making the state hard to inspect or operate.
### Proposed solution
Add the pipeline workflow primitive stack: database schema and
migrations, shared validators/types, server services and REST routes,
aggregation and liveness helpers, CLI/tutorial smoke support, and the
React operator UI for pipeline lists, boards, item detail,
review/learnings views, settings, stage automation, secrets, carry-over
fields, and retry/recovery flows.
### Alternatives considered
- Keep workflows as loosely linked issues and routines: rejected because
operators need a single board/detail/settings surface for repeated
workflow patterns.
- Ship backend primitives first and defer UI: rejected for this branch
because the operator experience is the main way to validate the
primitive.
- Add a narrower one-off content workflow: rejected because the same
primitives are useful across future company processes.
## What Changed
- Added and evolved pipeline schema, migrations, shared contracts,
server services, REST routes, route tests, and CLI/tutorial smoke
support.
- Added pipeline aggregation, health/liveness, drift acknowledgment,
blocker/carry-over, automation retry, stage automation environment, and
permission recovery behavior.
- Added the operator UI for pipeline index/board/item
detail/settings/review/learnings flows, including stage secrets,
automation controls, markdown/item descriptions, linked issue assets,
liveness banners, and source automation metadata.
- Refactored issue document frame rendering through the shared
`DocumentFrameHeader` component to keep document controls consistent
with the pipeline document surfaces.
- Kept this PR as the single base-branch review target for the current
pipeline branch.
## Verification
Current branch refresh:
- `pnpm vitest run server/src/__tests__/pipelines-service.test.ts` — 31
passed
- `pnpm vitest run server/src/__tests__/pipelines-routes.test.ts` — 19
passed
- `pnpm --filter ./server typecheck` — passed
- `pnpm --filter ./ui typecheck` — passed
- Verified Pipelines remains gated by `enablePipelines === true`:
sidebar item is hidden unless the flag is enabled, direct pipeline
routes redirect to `/dashboard` when disabled, and the Experimental
settings UI still has no Pipelines toggle.
- GitHub status checks on `df071c710646de625131064c3fb6588b5e97964a` —
all complete with no failing conclusions, including Actions, Socket,
Superagent/Security, and Greptile Review
- Greptile summary on `df071c710646de625131064c3fb6588b5e97964a` —
Confidence Score 5/5
- GitHub review-thread sweep — 0 unresolved Greptile threads
Previously recorded during branch development:
- Server pipeline service/route and aggregation tests
- Shared validator tests
- UI pipeline page/settings/item-detail/learnings/liveness tests
- Pipeline tutorial smoke path
## Risks
- High review surface: this is a large feature branch spanning database,
shared contracts, server behavior, CLI/docs, and UI.
- Migration ordering and schema compatibility need reviewer attention
because this branch has been kept current across multiple `master`
syncs.
- GitHub still reports merge state `BLOCKED` because the PR is awaiting
normal human review/branch-protection completion; all current status
checks are green.
- Branch-name checklist exception: this PR uses the pre-existing
requested branch name, which predates the current public-branch naming
rule. The PR title/body avoid internal issue references.
> 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 coding agent based on GPT-5, with repository tool use,
shell execution, git/GitHub CLI operations, and local verification
commands. Earlier commits in this branch were assisted by Paperclip
agents and other AI coding agents as recorded in commit authorship.
## 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)
- [ ] 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Scheduled routines can prompt agents with variables that are filled
in at dispatch time.
> - Existing routine variable handling supported plain text-like values,
but date inputs need a structured contract so routines can pass
consistent date values.
> - Operators also need date variables to be easy to configure and
override from the routine UI.
> - This pull request adds a date variable type across shared
validation, server dispatch, and UI editing/run dialogs.
> - The benefit is that routine authors can define date inputs once and
agents receive validated ISO-style date values when routines run.
## Linked Issues or Issue Description
Refs #219
Feature request:
- Problem/motivation: Scheduled routines need first-class, typed date
variables so operators can configure dates without relying on free-form
text conventions.
- Proposed solution: Add an `x-date` routine variable type with shared
parsing/validation, server dispatch support, and UI date-picker controls
in routine variable editors and run dialogs.
- Alternatives considered: Continue treating dates as plain text, but
that leaves validation and formatting to individual operators and
agents.
- Roadmap alignment: This is a focused improvement to the completed
Scheduled Routines milestone and does not duplicate an active roadmap
item.
Related PR search:
- Searched existing PRs/issues for `routine date picker`, `date
variables`, and `scheduled routine date variable`; no direct duplicate
PR was found.
## What Changed
- Added the shared `x-date` routine variable contract, parsing,
defaults, and validation coverage.
- Extended routine dispatch to validate and pass date variable values.
- Added date input controls to the routine variable editor and routine
run variables dialog.
- Added focused tests for shared validation, server dispatch, and the UI
date controls.
## Verification
- `git diff --check public/master...HEAD`
- `pnpm run preflight:workspace-links && pnpm exec vitest run
packages/shared/src/routine-variables.test.ts
packages/shared/src/validators/routine.test.ts
server/src/__tests__/routines-service.test.ts
ui/src/components/RoutineRunVariablesDialog.test.tsx
ui/src/components/RoutineVariablesEditor.test.tsx`
- 5 test files passed
- 68 tests passed
## Risks
Low to medium risk. This adds a new routine variable type across
shared/server/UI paths, so the main risk is compatibility with existing
routine variable payloads. The change keeps existing variable types
intact and adds targeted validation tests for the new date behavior.
> 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 coding agent based on GPT-5, with terminal, git, GitHub
CLI, and local test execution capabilities.
## 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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - The execution workspace subsystem powers project and workspace views
by listing runtime state, branch metadata, issue links, and status
summaries.
> - The existing workspace views relied on broad list data that can grow
expensive as a company accumulates many workspaces and linked issues.
> - That makes the Workspaces page and project workspace cards slower
than necessary because the UI does not always need the full workspace
detail payload up front.
> - This pull request adds a bounded overview contract for workspace
listings and moves the relevant UI surfaces to that cheaper path.
> - The benefit is faster workspace loading while preserving detail
fetches for pages that actually need full workspace data.
## Linked Issues or Issue Description
No public GitHub issue exists for this change. Inline bug report follows
the repository bug template.
### What happened?
Workspace index-style screens can load too much execution workspace
detail before the user asks for it. Several UI surfaces used fuller
workspace data paths for summary displays, which can make workspace
loading slower as workspace history grows.
### Expected behavior
Overview screens should request a bounded summary payload, while detail
screens should keep using the full workspace detail endpoint.
### Steps to reproduce
1. Run Paperclip from source with enough execution workspace history to
make workspace lists non-trivial.
2. Open the Workspaces page or a project workspace summary card.
3. Observe that summary UI needs only bounded workspace metadata but can
depend on broader workspace payloads.
### Paperclip version or commit
`master` at the time this branch was prepared.
### Deployment mode
Local dev (`pnpm dev`)
## What Changed
- Added shared types, validators, and path constants for bounded
execution workspace overviews.
- Added server service and route support for overview queries with
bounded linked issue/runtime metadata.
- Updated workspace overview UI API calls, query keys, breadcrumbs,
quicklooks, close dialogs, project summaries, and detail links to
consume the cheaper overview shape where appropriate.
- Added regression coverage for the new server route/service behavior
and the UI overview consumers.
- Registered the new workspace overview route in the generated OpenAPI
spec.
- Kept overview totals aligned with the project join and preserved
project slug links in workspace headers.
## Verification
- `pnpm exec vitest run
server/src/__tests__/execution-workspaces-service.test.ts
server/src/__tests__/execution-workspaces-routes.test.ts
ui/src/api/execution-workspaces.test.ts
ui/src/components/ProjectWorkspaceSummaryCard.test.tsx
ui/src/pages/Workspaces.test.tsx`
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/server typecheck && pnpm --filter @paperclipai/ui
typecheck`
- `pnpm exec vitest run server/src/__tests__/openapi-routes.test.ts
server/src/__tests__/execution-workspaces-routes.test.ts
server/src/__tests__/execution-workspaces-service.test.ts`
- `pnpm test:run:serialized -- --shard-index 1 --shard-count 4`
## Risks
Low to medium risk. The change introduces a new overview contract across
shared/server/ui layers, so the main risk is a mismatch between summary
and detail payload expectations. The added route/service/UI tests cover
the intended split, and full detail pages continue using the detail
path.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5-based coding agent with repository tool use and
local command execution. Exact served model identifier and context
window were not exposed in this session.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [x] All Paperclip CI gates are green
- [x] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [x] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - Agents and board users operate inside a company-scoped control plane
where permissions decide which mutating actions they can perform
> - Company skills are part of the reusable agent-company setup surface,
but skill mutation had been coupled to broader agent-creation authority
> - That coupling meant importing or managing skills required a
permission that also implies hiring power, which is broader than the
operation needs
> - Paperclip already has a grant-based permission vocabulary, so skill
mutation should be authorized through a dedicated `skills:create`
capability while preserving existing default behavior for trusted agents
> - This pull request adds the skill creation permission contract,
enforces it on company skill mutations, exposes it in agent permission
management, and documents the changed CLI/API expectations
> - The benefit is a narrower, auditable permission path for skill
import/create/update/delete flows without forcing agents to receive
broader agent-creation authority
## Linked Issues or Issue Description
No public issue is linked.
Problem: company skill mutation APIs were effectively tied to broader
agent creation authority. This PR splits skill mutation authorization
onto the public `skills:create` permission while keeping existing
default skill creation behavior for agents unless explicitly disabled.
Related public PR found during duplicate search: #5330. That PR uses an
older `canManageSkills` shape; this PR implements the `skills:create`
grant path instead.
## What Changed
- Added `skills:create` to shared permission constants and agent
permission types/validators as `canCreateSkills`.
- Backfilled default human/member role grants for `skills:create`.
- Updated company skill mutation routes to require board/user or agent
access to `skills:create`, while preserving legacy/default agent
behavior through `canCreateSkills` unless explicitly disabled.
- Updated agent permission update handling, UI permission controls,
duplicate-agent payloads, plugin SDK fixtures, and agent detail API
surfaces for `canCreateSkills`.
- Added regression coverage for skill route authorization, permission
schema/default behavior, invite grants, omitted permission updates, and
duplicate-agent payloads.
- Updated CLI and Paperclip skill documentation for the new skill
creation permission.
## Verification
- `pnpm exec vitest run
server/src/__tests__/agent-permissions-service.test.ts
server/src/__tests__/agent-permissions-routes.test.ts
server/src/__tests__/company-skills-routes.test.ts
server/src/__tests__/invite-join-grants.test.ts
ui/src/lib/duplicate-agent-payload.test.ts` — 5 files, 90 tests passed.
- `pnpm --filter @paperclipai/shared typecheck && pnpm --filter
@paperclipai/server typecheck && pnpm --filter @paperclipai/ui
typecheck` — passed.
- `pnpm test:run ...changed files...` was attempted first, but the
stable wrapper rejects explicit file arguments; direct Vitest was used
for the same targeted files.
## Risks
- Moderate authorization risk: this changes the gate for company skill
mutations, so the tests cover board grant checks, agent explicit grant
checks, legacy default allowance, and explicit denial.
- Migration/backfill risk is low: the migration only grants
`skills:create` to existing human roles that already need broad
management capability.
- UI/API compatibility risk is low: `canCreateSkills` remains default-on
for full agent permissions, and the update validator preserves omitted
values so unrelated permission edits do not re-enable disabled skill
creation.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI Codex, GPT-5 coding agent with terminal/tool use enabled.
## Checklist
- [x] I have included a thinking path that traces from project context
to this change
- [x] I have specified the model used (with version and capability
details)
- [x] I have checked ROADMAP.md and confirmed this PR does not duplicate
planned core work
- [x] I have searched GitHub for duplicate or related PRs and linked
them above
- [x] I have either (a) linked existing issues with `Fixes: #` / `Closes
#` / `Refs #` OR (b) described the issue in-PR following the relevant
issue template
- [x] I have not referenced internal/instance-local Paperclip issues or
links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip`
URLs)
- [x] My branch name describes the change (e.g. `docs/...`, `fix/...`)
and contains no internal Paperclip ticket id or instance-derived details
- [x] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [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>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work.
> - Sandboxed agent runs can spend meaningful time preparing a remote
workspace before the agent transcript shows useful output.
> - Operators need short, current progress text for those setup phases,
but that text should not become durable run history.
> - The existing live-run websocket path already carries run updates to
the UI, so the backend can reuse that channel instead of adding polling.
> - This pull request adds an ephemeral runtime-progress contract, a
process-local status store, and heartbeat integration for
sandbox-managed runs.
> - The benefit is a clearer active-run experience without database
migrations or persistent progress rows.
## Linked Issues or Issue Description
Refs #248
No exact public GitHub issue was found for this status-message plumbing.
The underlying problem is that active sandboxed runs currently have
setup phases, such as workspace sync and restore, where the operator
cannot see concise current progress through the live run state. This PR
addresses that gap for the backend/runtime layer while keeping progress
messages ephemeral.
GitHub search performed for related or duplicate work: `sandbox runtime
status`, `sandbox restore index`, and `runtime progress`. No direct
duplicate PR was found.
## What Changed
- Added shared runtime-progress types and the `heartbeat.run.progress`
live event type.
- Added a process-local heartbeat run runtime-status store with TTL,
bounded/redacted messages, and terminal cleanup.
- Threaded runtime progress callbacks through heartbeat execution and
active/live run serialization.
- Emitted sandbox-managed runtime phase updates for sync, adapter
startup, restore/export, and finalization paths.
- Added backend and adapter-utils tests for ephemeral status behavior,
terminal cleanup, live serialization, and sandbox progress callbacks.
## Verification
- `pnpm install --frozen-lockfile`
- Local PII scan before push: high-confidence secret patterns, internal
issue links, local user paths, and private URL patterns checked across
all three split diffs; no real secrets or internal links found. The only
secret-like text is an intentional fake test fixture (`sk-test-secret`).
- `git diff --check origin/master..feat/sandbox-runtime-status`
- `pnpm exec vitest run
server/src/services/heartbeat-run-runtime-status.test.ts
server/src/__tests__/heartbeat-runtime-state.test.ts
server/src/__tests__/agent-live-run-routes.test.ts
packages/adapter-utils/src/sandbox-managed-runtime.test.ts` — 4 files,
23 tests passed.
- `pnpm run typecheck` passed on both top stacks that include this
branch: `feat/sandbox-status-ui` and `fix/sandbox-restore-index-sync`.
- `pnpm run build` passed on both top stacks that include this branch;
Vite reported existing CSS `::highlight` and chunk-size warnings.
- `pnpm run test:run` was attempted on `fix/sandbox-restore-index-sync`;
it failed in two unrelated broad-suite tests. One depends on this host's
Git default branch behavior, and one depends on local Claude
model-discovery environment. The changed focused suites above pass.
## Risks
- Runtime progress is process-local by design, so status disappears
after TTL, terminal cleanup, or server restart.
- Clients that do not consume `heartbeat.run.progress` simply keep
existing behavior.
- Message redaction is intentionally generic; overly specific phase
details should stay out of runtime-progress payloads.
> For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and
discuss it in `#dev` before opening the PR. Feature PRs that overlap
with planned core work may need to be redirected — check the roadmap
first. See `CONTRIBUTING.md`.
## Model Used
OpenAI GPT-5 via Codex coding agent, with shell/tool execution in a
local worktree. Exact context-window metadata is not exposed by the
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 CTO <cto@paperclip.local>
Co-authored-by: Paperclip CTO <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source app people use to manage AI agents for
work
> - This change touches several already-shipped subsystems —
document-comment annotations, the selected-agent Conference Room chat
surface, routine description annotations, workspace-operation tracking,
and the board polling/inbox UI
> - A batch of incremental fixes and two small backend additions had
accumulated on a local mainline and were deployed to a live instance,
but never landed upstream — so each `origin/master` sync kept
re-diverging
> - Leaving them un-upstreamed means the same delta has to be re-merged
on every sync and risks being lost or silently reverted
> - This pull request rebases that delta cleanly on top of current
`origin/master` (preserving recent upstream work such as reusable
sandbox leases and the relation-list collapse controls) and brings it up
for review
> - The benefit is that mainline and the deployed instance converge, and
these fixes/additions get normal review + CI + Greptile coverage
## Linked Issues or Issue Description
No single GitHub issue tracks this; it is a bundle of bug fixes and two
small feature additions. Following the issue-template fields:
**Bug fixes (what was wrong → what this does):**
- Document comments rendered out of document order, didn't live-update
across clients, swallowed save failures, and lost the markdown text
selection on re-render. Now: doc-order sort, live updates, surfaced
save-failure state, stable selection across re-renders.
- The board polling hot path returned oversized payloads on every poll.
Now: an opt-in `summary` projection trims the heartbeat-run list
payload.
- Assorted UI fixes: sidebar nav peek/streamlining edge cases, markdown
file-viewer re-mount/line-height issues on iOS Safari, inbox badge/skill
deep-link tab selection, and `⌘.` work-mode cycling on iOS.
**Feature additions:**
- `workspace_operations.issue_id` — associate a workspace operation with
the issue that triggered it (new migration `0106`, schema, service,
shared type).
- Routine **description annotations** — comment threads on a routine's
description document, mirroring issue document annotations (new
migration `0107`, `routine_documents` schema, routes/service,
editable-sections UI).
- Selected-agent **Conference Room chat** surface wiring and live
issue-thread updates.
**Related PR:** #8229 (`feat(control-plane): add annotation and
workspace controls`, draft) covers overlapping
annotation/workspace-control territory — flagging it so a reviewer can
reconcile the two rather than double-merging.
## What Changed
- `feat(workspace)`: `workspace_operations.issue_id` migration +
schema/service/type; issue workspace property controls reconciled with
upstream's evolved "Service" row.
- `feat(routines)`: routine description annotations —
`routine_documents` schema, migration `0107`, routes/service,
`editable-sections` UI.
- `fix(document-comments)`: doc-order sort, live updates, save-failure
surfacing, stable markdown selection (+ storybook story, rerender test).
- `feat(chat)`: selected-agent Conference Room chat surface and live
issue-thread updates (`LiveUpdatesProvider`, `issue-chat-messages`,
interactions service).
- `perf(board)`: opt-in `summary` projection for the board
polling/heartbeat-run list payload, plus assorted sidebar / file-viewer
/ inbox / IssueProperties UI fixes.
Organized into 5 logical commits. Migrations are numbered incrementally
after upstream's latest (`0105`) — `0106` then `0107`, no journal
collision.
## Verification
- Built by 3-way merging the deployed delta onto current
`origin/master`; the only merge conflict (`IssueProperties.test.tsx`,
two adjacent test blocks) was resolved in favor of upstream's evolved
"green service link above the workspace row" layout, which matches the
merged component's rendered output.
- Confirmed recent upstream work is preserved post-merge: reusable
sandbox lease teardown (`#8513`), the IssueProperties relation-list
collapse controls, and the sidebar streamlined-nav default.
- Confirmed the net diff vs `origin/master` is exactly the intended
feature delta (65 files) and that overlapping server files (`issues.ts`,
`agents.ts`, `heartbeat.ts`) only add feature code without disturbing
upstream logic.
- This delta is already running on a live deployed instance.
- Full typecheck/test suite + Greptile to run in CI (see checklist).
## Risks
- Two new migrations (`0106`, `0107`). Both are additive (new table /
new nullable column) and ordered after upstream's `0105`; no data
backfill, low risk. If another migration-bearing PR merges first,
renumber before merge.
- Largest blast radius is in the merged overlapping UI/service files;
covered by the existing test suites for those files plus CI.
- Overlaps thematically with draft PR #8229 — reviewers should reconcile
rather than merge both blindly.
## Model Used
Claude Opus 4.8 (`claude-opus-4-8`), extended thinking, with tool use
(git, shell). Agentic coding workflow.
## 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)
- [ ] I have run tests locally and they pass
- [x] I have added or updated tests where applicable
- [x] I have updated relevant documentation to reflect my changes
- [x] I have considered and documented any risks above
- [ ] All Paperclip CI gates are green
- [ ] Greptile is 5/5 with no open P2s, recommendations, or follow-ups
- [ ] I will address all Greptile and reviewer comments before
requesting merge
---------
Co-authored-by: Paperclip <noreply@paperclip.ing>
## Thinking Path
> - Paperclip is the open source control plane people use to coordinate
AI agents, issues, approvals, comments, and work products.
> - The involved subsystem is issue context: markdown links, issue
properties, related work, lists, filters, inbox/sidebar status, and
plugin-provided external context.
> - The gap is that URLs to external systems currently remain mostly
plain links, so humans and agents must manually open them to understand
status, identity, and liveness.
> - This matters because external work objects such as GitHub issues and
pull requests are part of the operational state of a Paperclip company.
> - The implementation keeps core provider-neutral: shared contracts,
storage, sync, routes, and UI surfaces live in core while providers can
contribute detection and status resolution.
> - This pull request adds the external object reference foundation,
GitHub provider support, issue-surface rendering, filters,
sidebar/list/inbox signals, and test/story coverage.
> - The benefit is that linked external work becomes inspectable
Paperclip context without hardcoding every provider directly into the
UI.
## Linked Issues or Issue Description
No public GitHub issue exists for this work.
Feature request:
- Problem: URLs in Paperclip issues, comments, documents, and related
surfaces do not expose provider status or object identity inline.
- Proposed behavior: detect supported external object URLs, persist
normalized references, refresh provider status, and render concise
status-aware links across issue surfaces.
- Users affected: board users, agents, and maintainers who triage issues
containing external work links.
- Acceptance: external object references are company-scoped,
provider-extensible, visible in key issue surfaces, filterable where
relevant, and covered by focused shared/server/UI tests.
Related PR search:
- No open duplicate PRs found for `external object references`.
- Closed related prior attempt: #4556.
## What Changed
- Added shared external-object contracts, validators, status/liveness
helpers, and plugin protocol declarations.
- Added database schema and additive migrations for external objects,
source mentions, and display metadata.
- Added server services/routes for detecting, syncing, summarizing,
refreshing, and resolving external objects across issues, documents,
comments, projects, and plugins.
- Added a GitHub external-object provider plus plugin SDK authoring
docs.
- Wired UI presentation across markdown links, comments, issue chat,
documents, properties, related work, issue rows, filters, inbox/sidebar
badges, and Storybook stories.
- Rebasing cleanup: moved the branch onto current `master`, repaired
stale worktree provision config, hardened environment-sensitive
tests/mocks, and removed committed screenshot artifacts from the PR
branch to keep the reviewable file set below tool limits.
## Verification
- `pnpm exec vitest run packages/shared/src/external-objects.test.ts
server/src/__tests__/external-object-routes.test.ts
server/src/__tests__/external-objects-service.test.ts
ui/src/components/ExternalObjectPill.test.tsx
ui/src/lib/external-objects.test.ts` passed after rebasing: 5 files, 56
tests.
- Historical branch verification before this PR creation included `pnpm
test:run`, `pnpm -r typecheck`, and `pnpm build`; this PR body does not
claim those were rerun after the final rebase.
## Risks
- Medium: this adds a new cross-surface sync path on
issue/document/comment writes. The implementation uses safe sync
wrappers so external-object failures warn instead of blocking core
mutations.
- Medium: the migrations introduce new tables and indexes. They are
additive and company-scoped.
- Medium: provider-specific URL parsing can miss or misclassify edge
cases. Shared canonicalization tests and provider tests cover current
GitHub shapes.
- Low: UI badge/filter behavior could add visual noise for object-heavy
issues; component tests and Storybook stories cover the intended
surfaces.
> Roadmap checked: `ROADMAP.md` references the plugin system as the
current extension path and does not list a duplicate core feature.
Related long-range docs discuss external references, work products,
preview URLs, and plugin extension points; this PR implements the scoped
external-object reference foundation.
## Model Used
OpenAI Codex, GPT-5 coding-agent runtime, with shell and GitHub CLI tool
use. Reasoning mode: medium. Exact deployed runtime model ID and context
window were not exposed in the 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>