mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-07 16:11:46 +02:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work > - Agents execute on isolated workspaces (git worktrees), each pinned to a specific branch and commit at checkout time > - When an agent's live checkout diverges from the recorded workspace branch — either through a branch rename, a stale worktree, or a concurrent git operation — Paperclip detects the mismatch and surfaces a recovery card to the operator > - But the existing recovery card showed a generic error with no diagnostic context: it didn't display *which* branch was expected vs. which was checked out, the commit SHAs involved, or whether the branches share ancestry > - Without that information operators cannot diagnose the root cause, and the only recovery path was fully manual re-issue > - This pull request extends `IssueRecoveryActionCard` for `workspace_validation` / `git_worktree_branch_incoherence` recovery kinds to render a divergence-diagnosis panel (expected branch, live branch, short SHAs, ancestry-verdict badge, plain-language reason) and adds a confirm-gated "Re-issue on isolated workspace" action that creates a new task with `executionWorkspacePreference: isolated_workspace` so the re-issued run cannot trip the same branch-mismatch gate > - The benefit is that operators can immediately see *why* a workspace was declined and recover with a single click instead of having to manually reconstruct the task ## Linked Issues or Issue Description Refs #4757 (heartbeat re-wake doesn't reconcile working-tree HEAD against ticket's expected branch — this PR surfaces the resulting divergence to the operator and provides a one-click isolated re-issue path) Refs #8460 (workspace_validation_failed local-only project workspaces — this PR extends the recovery card UI for this case) **Subsystem affected:** ui/ — React + Vite board UI **Problem or motivation:** When Paperclip records a workspace branch for an agent run and the live checkout disagrees (diverged HEAD, renamed branch, stale worktree), the issue recovery card surfaces a generic `workspace_validation` error. The operator sees "run declined" but has no visibility into the expected vs. actual branch, the relevant commit SHAs, or whether the branches even share ancestry. There is no one-click path to re-issue the task on a clean isolated workspace — the operator must manually reconstruct the task from scratch. **Proposed solution:** Extend `IssueRecoveryActionCard` to: 1. Render a divergence-diagnosis panel from the `recoveryEvidence` field: expected branch, live branch, short SHAs for both, an ancestry-verdict badge (`forward-only` / `diverged` / `ancestry unknown`), and the server's `plainLanguageReason`. 2. Add Action 3 "Re-issue on isolated workspace" — a confirm-gated button that calls `issuesApi.create` with `executionWorkspacePreference: isolated_workspace` and `workspaceStrategy.baseRef` set to the live branch (SHA fallback when detached). The current workspace is never mutated. 3. Wire `onReissueIsolated` / `reissuePending` through `IssueChatThread` → `IssueDetail` so the operator sees an immediate success toast and is navigated to the new task. **Alternatives considered:** Showing divergence details only in a tooltip (rejected — too easy to miss). Providing a "force-reset the workspace" action (rejected — destructive, no audit trail, doesn't fix stale-branch root cause). Isolated re-issue via isolated workspace was the clearest safe path. ## What Changed - `IssueRecoveryActionCard.tsx` — Added `DiagnosisPanel` sub-component rendered for `workspace_validation` / `git_worktree_branch_incoherence` recovery kinds: displays expected vs. live branch, short SHAs, ancestry-verdict badge, and plain-language reason. Added Action 3 confirm-popover with `onReissueIsolated` callback and `reissuePending` loading state. Kept existing Action 1 and Action 2 unchanged. - `IssueChatThread.tsx` — Threaded `onReissueIsolated` and `reissuePending` props down to `IssueRecoveryActionCard`. - `IssueDetail.tsx` — Implemented `handleReissueIsolated`: calls `issuesApi.create` with `executionWorkspacePreference: isolated_workspace` + `workspaceStrategy.baseRef` derived from live branch / SHA; shows a success toast and navigates to the new task on completion. - `IssueRecoveryActionCard.test.tsx` — Added 19 unit tests covering diagnosis-panel rendering, verdict label rendering, base-ref derivation (branch-first then detached-HEAD SHA fallback), and action gating. ## Verification ```bash # Unit tests — 19/19 pass pnpm vitest run ui/src/components/IssueRecoveryActionCard.test.tsx # Typecheck — 0 new errors pnpm typecheck ``` Manual browser validation deferred to QA — see Risks. ## Risks - **Re-issue creates a new task** — the original task remains unchanged. This is intentional (safe default), but operators should be aware both tasks exist after re-issue. - **Base-ref derivation falls back to the live HEAD SHA when detached.** SHA-based worktrees are valid for isolated re-issue but may surprise operators expecting a branch name. - **Browser-level end-to-end validation not included here.** Toast, navigation, and full create-flow are covered by integration QA in a follow-up pass. - **Low overall risk** — no new endpoints, no data mutations on existing records, no PII or telemetry changes. Composes the existing `issuesApi.create` endpoint; all new behavior is additive. ## Model Used Claude Sonnet 4.6 (`claude-sonnet-4-6`) — Anthropic. 200k context window, tool use, code execution. Extended thinking not used. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [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: Claude Opus 4.8 <noreply@anthropic.com> Co-authored-by: Paperclip <noreply@paperclip.ing>
@paperclipai/ui
Published static assets for the Paperclip board UI.
What gets published
The npm package contains the production build under dist/. It does not ship the UI source tree or workspace-only dependencies.
Storybook
Storybook config, stories, and fixtures live under ui/storybook/.
pnpm --filter @paperclipai/ui storybook
pnpm --filter @paperclipai/ui build-storybook
Typical use
Install the package, then serve or copy the built files from node_modules/@paperclipai/ui/dist.