mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-11-14 17:22:00 +01:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The recovery service restores execution when a task loses its live path. > - The service retries the original agent for a limited number of attempts. > - The old fallback could select a manager or an executive and wake that agent. > - That fallback changed the effective recovery owner without a board decision. > - This pull request keeps the source owner and gives the exhausted recovery decision to the board. > - The benefit is a clear ownership rule with no automatic task takeover. ## Linked Issues or Issue Description Refs: #11807 Refs: #11817 **What existing behavior does this improve?** This improves stranded-task recovery in the server and the recovery action card in the board UI. **Subsystem affected** Cross-cutting: server recovery orchestration, recovery observability, board UI, and execution documentation. **Current behavior** Paperclip retries the original agent for a limited number of attempts. After the retry limit, it can select a manager, task creator, CTO, or CEO as a recovery owner. It can then wake that substitute agent. The source task keeps its assignee, but the automatic substitute wake creates an implicit takeover path. **Proposed behavior** Paperclip keeps the limited retry path for the original agent. If recovery is exhausted or unsafe, Paperclip creates one board-owned source recovery action. It keeps both source assignee fields. It does not wake a substitute agent. The board can repair, retry the original owner, explicitly reassign, or resolve the task. **Reason and benefit** Source task ownership must remain stable until a person or an approved policy changes it. The new rule removes implicit manager and executive takeover. It also gives operators clear evidence through the `board_escalation_no_takeover_v1` routing marker. **Breaking changes** Automatic recovery no longer wakes a manager or executive after the original-agent retry limit. Existing active agent-owned recovery actions remain visible and can resolve. Paperclip does not schedule a new takeover wake for those legacy actions. ## What Changed - Route exhausted and unsafe stranded recovery to a board-owned source action. - Preserve agent and user assignee fields during automatic escalation. - Keep limited same-agent continuity repair and provider quota monitoring. - Stop new manager, creator, CTO, and CEO recovery wakes. - Keep legacy agent-owned recovery actions readable and resolvable. - Add the routing marker to new board escalation evidence and observability. - Update recovery notices, the board UI card, tests, and execution documentation. ## Verification - Run `pnpm -r typecheck`. - Run `pnpm build`. - Run `pnpm check:token-gates`. - Run `pnpm --filter @paperclipai/server exec vitest run src/__tests__/heartbeat-process-recovery.test.ts`. - Run `pnpm --filter @paperclipai/server exec vitest run src/__tests__/heartbeat-workspace-branch-containment.test.ts`. - Run the focused recovery and UI Vitest files changed by this pull request. - Confirm that a paused or over-budget source owner creates one board action, keeps the source assignee, and creates no substitute wake. ## Risks - Operators must now make the final recovery decision after the original-agent limit. - Legacy agent-owned actions use their stored contract. This avoids a rollout-time ownership rewrite. - No database migration or API response shape changes are included. - The tests cover concurrent escalation, paused and over-budget owners, legacy actions, provider quota monitoring, and UI presentation. > For core feature work, check [`ROADMAP.md`](ROADMAP.md) first and discuss it in `#dev` before opening the PR. Feature PRs that overlap with planned core work may need to be redirected — check the roadmap first. See `CONTRIBUTING.md`. ## Model Used OpenAI Codex with GPT-5. The hosted exact model revision and context window are not exposed. Reasoning, 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>
205 lines
5.4 KiB
TypeScript
205 lines
5.4 KiB
TypeScript
import type {
|
|
SystemNoticeMetadataSection,
|
|
SystemNoticeProps,
|
|
} from "../components/SystemNotice";
|
|
|
|
export type SystemNoticeFixture = {
|
|
id: string;
|
|
caption: string;
|
|
} & SystemNoticeProps;
|
|
|
|
const HANDOFF_METADATA: SystemNoticeMetadataSection[] = [
|
|
{
|
|
title: "Recovery owner",
|
|
rows: [
|
|
{
|
|
kind: "issue",
|
|
label: "Recovery issue",
|
|
identifier: "PAP-3440",
|
|
href: "/PAP/issues/PAP-3440",
|
|
title: "Successful run handoff missing disposition",
|
|
},
|
|
{
|
|
kind: "agent",
|
|
label: "Owner",
|
|
name: "CTO",
|
|
href: "/PAP/agents/cto",
|
|
},
|
|
{
|
|
kind: "text",
|
|
label: "Suggested action",
|
|
value: "Reassign to a recovery agent and pick a disposition.",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: "Run evidence",
|
|
rows: [
|
|
{
|
|
kind: "run",
|
|
label: "Source run",
|
|
runId: "9cdba892-c7ca-4d93-8604-4843873b127c",
|
|
href: "/PAP/agents/codexcoder/runs/9cdba892-c7ca-4d93-8604-4843873b127c",
|
|
status: "succeeded",
|
|
},
|
|
{
|
|
kind: "run",
|
|
label: "Recovery run",
|
|
runId: "61fdb79b-8012-4676-ac71-2971830e126a",
|
|
href: "/PAP/agents/codexcoder/runs/61fdb79b-8012-4676-ac71-2971830e126a",
|
|
status: "failed",
|
|
},
|
|
{
|
|
kind: "text",
|
|
label: "Normalized cause",
|
|
value: "Run completed without issuing a disposition for an in_progress task.",
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
const REQUIRED_METADATA: SystemNoticeMetadataSection[] = [
|
|
{
|
|
title: "Required action",
|
|
rows: [
|
|
{
|
|
kind: "issue",
|
|
label: "Source issue",
|
|
identifier: "PAP-3440",
|
|
href: "/PAP/issues/PAP-3440",
|
|
title: "Successful run handoff missing disposition",
|
|
},
|
|
{
|
|
kind: "agent",
|
|
label: "Responsible",
|
|
name: "CodexCoder",
|
|
href: "/PAP/agents/codexcoder",
|
|
},
|
|
{
|
|
kind: "text",
|
|
label: "Next step",
|
|
value: "Pick done, blocked, or in_review and post a one-line rationale.",
|
|
},
|
|
],
|
|
},
|
|
{
|
|
title: "Run context",
|
|
rows: [
|
|
{
|
|
kind: "run",
|
|
label: "Successful run",
|
|
runId: "9cdba892-c7ca-4d93-8604-4843873b127c",
|
|
href: "/PAP/agents/codexcoder/runs/9cdba892-c7ca-4d93-8604-4843873b127c",
|
|
status: "succeeded",
|
|
},
|
|
{
|
|
kind: "code",
|
|
label: "Status before",
|
|
value: "in_progress",
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
const NEUTRAL_METADATA: SystemNoticeMetadataSection[] = [
|
|
{
|
|
rows: [
|
|
{
|
|
kind: "agent",
|
|
label: "Reassigned to",
|
|
name: "ClaudeFixer",
|
|
href: "/PAP/agents/claudefixer",
|
|
},
|
|
{
|
|
kind: "agent",
|
|
label: "From",
|
|
name: "CodexCoder",
|
|
href: "/PAP/agents/codexcoder",
|
|
},
|
|
{
|
|
kind: "text",
|
|
label: "Reason",
|
|
value: "Manual reassignment requested by Board.",
|
|
},
|
|
],
|
|
},
|
|
];
|
|
|
|
export const systemNoticeFixtures: readonly SystemNoticeFixture[] = [
|
|
{
|
|
id: "warning-collapsed",
|
|
caption: "Warning · collapsed (default)",
|
|
tone: "warning",
|
|
label: "System warning",
|
|
source: { label: "Paperclip", href: "/PAP/agents" },
|
|
timestamp: "2026-05-04T16:32:00.000Z",
|
|
body: "Paperclip needs a disposition before this issue can continue.",
|
|
metadata: REQUIRED_METADATA,
|
|
detailsDefaultOpen: false,
|
|
},
|
|
{
|
|
id: "warning-expanded",
|
|
caption: "Warning · expanded",
|
|
tone: "warning",
|
|
label: "System warning",
|
|
source: { label: "Paperclip", href: "/PAP/agents" },
|
|
timestamp: "2026-05-04T16:32:00.000Z",
|
|
body: "Paperclip needs a disposition before this issue can continue.",
|
|
metadata: REQUIRED_METADATA,
|
|
detailsDefaultOpen: true,
|
|
},
|
|
{
|
|
id: "danger-collapsed",
|
|
caption: "Danger · collapsed (default)",
|
|
tone: "danger",
|
|
label: "System alert",
|
|
source: { label: "Paperclip", href: "/PAP/agents" },
|
|
timestamp: "2026-05-04T16:48:00.000Z",
|
|
body: "Paperclip could not resolve this issue's missing disposition automatically. The source assignment is unchanged and a board decision is required.",
|
|
metadata: HANDOFF_METADATA,
|
|
detailsDefaultOpen: false,
|
|
},
|
|
{
|
|
id: "danger-expanded",
|
|
caption: "Danger · expanded",
|
|
tone: "danger",
|
|
label: "System alert",
|
|
source: { label: "Paperclip", href: "/PAP/agents" },
|
|
timestamp: "2026-05-04T16:48:00.000Z",
|
|
body: "Paperclip could not resolve this issue's missing disposition automatically. The source assignment is unchanged and a board decision is required.",
|
|
metadata: HANDOFF_METADATA,
|
|
detailsDefaultOpen: true,
|
|
},
|
|
{
|
|
id: "neutral-collapsed",
|
|
caption: "Neutral · collapsed (default)",
|
|
tone: "neutral",
|
|
label: "System notice",
|
|
source: { label: "Paperclip" },
|
|
timestamp: "2026-05-04T15:10:00.000Z",
|
|
body: "Reassigned to ClaudeFixer.",
|
|
metadata: NEUTRAL_METADATA,
|
|
detailsDefaultOpen: false,
|
|
},
|
|
{
|
|
id: "neutral-expanded",
|
|
caption: "Neutral · expanded",
|
|
tone: "neutral",
|
|
label: "System notice",
|
|
source: { label: "Paperclip" },
|
|
timestamp: "2026-05-04T15:10:00.000Z",
|
|
body: "Reassigned to ClaudeFixer.",
|
|
metadata: NEUTRAL_METADATA,
|
|
detailsDefaultOpen: true,
|
|
},
|
|
{
|
|
id: "warning-no-details",
|
|
caption: "Warning · no metadata (Details affordance hidden)",
|
|
tone: "warning",
|
|
label: "System warning",
|
|
source: { label: "Paperclip" },
|
|
timestamp: "2026-05-04T17:02:00.000Z",
|
|
body: "This run paused while waiting on board approval.",
|
|
},
|
|
];
|