## Thinking Path > - Paperclip manages AI agents and their work. > - Cases keep facts, documents, and one primary task across agent handoffs. > - Each case currently needs its own procedure text. > - Agents need to reuse procedures and move cases between them without replacing the task. > - This change adds company workflow definitions with a name and Markdown prompt. > - Existing handoff and document transactions preserve authority, revisions, and recovery. > - Cases remain behind `experimental.enableCases`. ## Linked Issues or Issue Description Refs #16959. Related proposal: #3951 uses task graphs and a visual editor. This change extends existing Cases with prompt definitions and keeps its current handoff model. **Problem or motivation** Agents cannot share a reusable procedure across cases or inspect a case's movement between procedures. **Proposed solution** Add name-and-prompt workflow definitions. Adopt a prompt into the primary task's revisioned workflow document through a handoff. Keep phases as free text. **Alternatives considered** Defer graphs, workflow versions, run entities, role schemas, authoring screens, and Spec Kit integration. **Roadmap alignment** This extends the existing Cases experiment and same-task review flow. It adds no work queue or new execution engine. The maintainer approved this scope in the implementation plan. ## What Changed - Add company-scoped definition storage, shared request schemas, REST routes, and four optional Cases tools. - Accept a workflow ID at kickoff. Agent handoffs can adopt, refresh, preserve, or detach a workflow. - Capture prompt text and the document base revision at acceptance. Apply workflow, phase, and document revision together after outgoing execution settles. - Hold conflicting document edits for recovery. Preserve cancellation, replay, and restart behavior. - Show current workflow names and captured transition names/phases in Work, history, and Audit. - Update instructions, API/tool contracts, capability maps, and the isolated workflow journey. - Pin bounded workflow prompt reads to a content fingerprint; continuation conflicts on edits and can restart safely. - Remove experimental Cases/workflow/Mermaid guidance from the always-on native prompt. Extract runtime-context prompt strings into a CODEOWNERS-protected constants file while preserving the public imports. - Refresh Copilot’s pinned source identity to v57, including the extracted prompt file and retaining historical identities. ## Verification Local checks: repository `pnpm -r typecheck` and `pnpm build` pass on the prompt-extraction tree (`88222d45d8`). Full repository coverage previously completed through the supported isolated shard groups (1,082 general server suites, 161 serialized suites, and all three workspace groups). The earlier default serial run was interrupted and remains incomplete. The first sharded run retained the failures described below; targeted corrections and separate limited-concurrency reruns pass. All CI jobs, security checks, and the 5/5 Greptile review passed on `fc1bd79c28`. Fresh CI and review are pending for the prompt extraction in `88222d45d8`. Focused verification passes: real-database handoff recovery and reuse; 2,002 native-tool/API contract checks; 144 Runner catalog/discovery checks; 27 Runner context/coverage checks; 37 case/Audit UI checks; and Product E2E oracle calibration. Token gates, migration safety/numbering, module boundaries, capability maps, and generated tool/Storybook catalogs pass. The final route/context corrections pass 49 API/native-tool tests, 90 real-database handoff tests, and server typecheck; 142 Copilot installation/sidecar tests pass with the refreshed identity. Greptile’s first review found that successive prompt pages could mix definitions after an edit. The fix returns a content fingerprint, requires it for continuation, and rejects edits with 409. Strict live/catalog schemas pass (171 Runner checks). The 2,005 API/OpenAPI tests pass; the initial native regression caught compression’s weak ETag prefix, and all 23 native integration tests pass after handling it. The regression covers same-length edits and a four-byte restart window. No stored version fields or entities were added. The prompt extraction passes all 462 checks across six focused suites, including persisted-context recovery and provider transport. The first local transport attempt lacked the required debug Runner binaries (102 failures, retained); after building the normal prerequisites, all 283 tests in that suite pass without source or assertion changes. Existing saved snapshots retain their recorded prompt text, and new executions use core-only prompt v7. The extracted prompt source is now covered by both CODEOWNERS and Copilot’s source fingerprint. Live-provider evidence is separate from those simulated checks: the isolated API-key campaign `reusable-workflows-api-final-2026-10-21` passed with local code matching `a1d73b4652`. Four actual Codex API runs read the adopted task documents, moved one case A → B → A on the same task, completed normally, and passed all nine durable checks. Browser assertions verified the current workflow/phase, historical names/phases, five handoff records including terminal completion, and the completed original task. Cleanup passed. The local report remains under `tests/runner-e2e/results/reusable-workflows-api-final-2026-10-21/`; no credentials or screenshots are committed. CI initially caught an omitted route-inventory entry and a stale pinned instruction-source hash; both are corrected and separately verified. Local serialized coverage also recorded an unrelated environment-route socket disconnect and activity-route timeout; their separate 11-test and 17-test suites passed without code or timeout changes. An unchanged chunked-webhook check also passed its separate four-test rerun. The initial adapter/database workspace groups hit time and resource limits under default worker counts; complete reruns with `VITEST_MAX_WORKERS=2` pass, including all 55 database files and 211 tests. The UI/CLI groups passed 837 files and 9,442 tests in the original run. Original failures remain retained and are not regraded. Earlier failed live campaigns remain unchanged: subscription HTTP 429 before agent execution; an obsolete exact-four-row oracle that rejected the valid terminal fifth row; and a chat-marker expectation that did not account for the existing explicit-comment presentation policy. The final fixture requires terminal completion and places the marker in its authoritative comment. Positive/negative oracle tests and the fresh live campaign pass. ## Risks - The additive migration adds a table and nullable case/handoff fields. Existing rows keep null workflow metadata; no assignments are inferred. - Adoption can wait on an intervening task-document edit. Recovery must cancel the old intent and accept a new one after readback. - Shared definition edits affect future adoptions. Existing case copies keep their revisions. - The feature remains experimental. This PR does not merge or deploy the change. - Live acceptance covers native Codex before the prompt extraction; this cleanup has contract/transport verification rather than a new live-provider campaign. The refreshed Copilot identity has static compatibility evidence, not new live-provider qualification. ## Model Used OpenAI Codex, configured model ID `gpt-6-astra`, xhigh reasoning. Used terminal execution, file editing, and tool calls. The active context-window size is not exposed. ## Checklist - [x] I have included a thinking path that traces from project context to this change - [x] I have specified the model used (with version and capability details) - [x] I have checked ROADMAP.md and confirmed this PR does not duplicate planned core work - [x] I have searched GitHub for duplicate or related PRs and linked them above - [x] I have either (a) linked existing issues with `Fixes: #` / `Closes #` / `Refs #` OR (b) described the issue in-PR following the relevant issue template - [x] I have not referenced internal/instance-local Paperclip issues or links (only public GitHub `#NNN` / `github.com/paperclipai/paperclip` URLs) - [x] My branch name describes the change (e.g. `docs/...`, `fix/...`) and contains no internal Paperclip ticket id or instance-derived details - [x] I have run tests locally and they pass - [x] I have added or updated tests where applicable - [x] I have updated relevant documentation to reflect my changes - [x] I have considered and documented any risks above - [ ] 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>
16 KiB
navigation
| navigation | ||||||
|---|---|---|---|---|---|---|
|
Cases
First-task kickoff may supply initialDocuments; these are ordinary task
documents, saved before the assignee wakes. The read-only case page surfaces
authorized primary/linked task documents and their revisions with their task
origin. Historical case-owned documents remain readable; no document copies or
editing controls are introduced. Native create_case requires kickoff and
creates the first task before dispatch; missing kickoff creates nothing.
Either workflowId or workflow_document_text becomes the task's ordinary Markdown workflow
document. Agents read it on demand; its reference is prioritized in bounded
context. Authoring guidance recommends both a Mermaid stateDiagram-v2 map and
prose explaining each transition's conditions, evidence, agent and wait/retry
rules, without server transition parsing.
The cases experiment exposes structured records with types, fields, parent/child relationships, revisions, and related task work. Work appears above Details in one column. Details use a compact monospace key/value table: nested objects become dotted leaf paths, singleton object arrays omit the redundant index (items.quantity), and multiple object-array entries retain their indices (items[0].quantity). Primitive arrays remain inline JSON; scalar task/case/URL references remain links. Case changes live in Audit, accessible through a case-scoped link and the Cases action/Case entity filters.
Feature ID: cases.
Implementation: case list, case detail.
Agent access: companion.
Sub-features
browse:cases/browse— search, choose visible columns, filter by status/type/project, sort/group, and navigate read-only case records.structure:cases/structure— read saved fields, documents, attachments, and parent/child relationships.history:cases/history— inspect document revisions and case changes in the shared Audit view, with actor avatars and case/action filters.task-links:cases/task-links— follow work associated with a case in both directions.handoffs:cases/handoffs— move the same primary task between agents and inspect its phase and handoff history.
How to get to it (user POV)
case-list
Open Cases in the company sidebar (/:companyPrefix/cases). The task-style header has Search and four icon controls: Columns, Filter (with an active-filter count), Sort and Group. Filter opens status/type/project choices, including No project. Sort offers updated/created/title/status/type and toggles direction; Group offers none/status/type/project. Open a row; the breadcrumb returns to the same filters and view. Cases have no Kanban or New case control.
case-record
Open /cases/:caseIdentifier or a task’s linked case.
Driving it with Playwright and the Paperclip CLI
Preconditions: follow the baseline. Enable cases and prepare related records with different types/statuses plus an associated task.
case-list
Automated: case list covers the four header controls, read-only column visibility, URL filters/view state, server sort/group queries before pagination, group headers, detail/back navigation and loading/failure with mocked data.
Provider-free browser regression: cases read-only journey seeds passive cases, nested objects, singleton/multiple object arrays, a related unassigned task and two document revisions through production APIs, then checks column visibility, server-backed title sorting/type grouping, Filter popover/count, semantic key/value table rows and dotted paths, Work-before-Details layout, document revision previews, case-scoped Audit history/action filtering and return navigation in Chromium. It creates no agents. Run env -u DATABASE_URL -u DATABASE_MIGRATION_URL pnpm exec playwright test --config tests/e2e/playwright.config.ts tests/e2e/cases-read-only.spec.ts --project chromium on an unused PAPERCLIP_E2E_PORT. The existing harness starts an isolated instance; this journey does not qualify agent handoffs or live agent avatars.
Manual: Hide/show a field through Columns, choose a sort/direction, group by type/status/project, then use Filter for status/type/project including No project. Verify the badge counts active selectors. Open a row and return through the breadcrumb; reload to verify URL filters, columns, sort and grouping remain. Use keyboard and touch controls; include empty results, terminal cases, loading, and retry after a list failure. If there are more results, use Next/Previous without losing the view.
case-record
Automated: case routes covers API behavior; case fields covers field UI separately.
Manual: Inspect Work above Details in one column. Read nested values as compact dotted key/value rows; a singleton item has items.quantity, while multiple items use items[0].quantity and items[1].quantity. Verify keys/values remain legible on mobile without horizontal page overflow and scalar references still link to their destinations. Preview a document, inspect its revisions, and close with Escape; focus returns to the document link. Follow View in Audit to the case's retained history; filter by Cases actions or Case entities in Audit and verify agents use their configured avatars. Read attachments, follow parent/child/task links, and inspect a passive case. Case pages have no editing controls, including status, labels, documents, or handoff retry/cancel. Verify inaccessible cases fail clearly and remain distinct from pipeline-owned items.
case-handoff
Reusable company workflows have only a name and Markdown prompt plus ordinary identity/attribution. Agent/API authoring shares the Cases flag. Work shows the current workflow; pending intent shows the next name/phase; expanded handoffs and Audit retain names captured before renames. Switch A → B → A, refresh the current ID, or detach with null while retaining one primary task and document history. Shared edits affect future adoptions, and intervening case-document edits hold a pending adoption for recovery instead of being overwritten. Workflow authoring screens, graph execution and deletion lifecycle are deferred.
Evidence for this extension: 2026-10-21 real-database and authenticated native-tool integration checks pass. The extended arithmetic Product E2E oracle requires adopted prompt markers and persisted transitions; registration alone is not live provider evidence.
Start a standalone case through the agent's handoff_case operation or the case-handoff REST API. Open its existing case record and follow its primary task. Check that the phase and current assignee are distinct from a pending recipient. Each handoff includes a link to the same primary task; expand its history to inspect instructions. A blocked handoff shows its known reason without implying that the recipient is running. Recovery remains available through authorized APIs and existing task controls.
The final agent may hand off to done. While ordinary task completion is pending, the case remains open and shows that it will close when the task completes. Accepted completion closes the case and records a completed terminal handoff. Review and user waits do not close it. Case pages remain read-only.
Automated: shared schema tests, case route privacy tests, case handoff service/delivery tests, native lifecycle tests, and CaseDetail.test.tsx exercise their individual boundaries. The registered explicit-only case-handoffs Product E2E suite covers the full native journey; registration is not a live pass.
Manual: hand the task through implementation, verification, and correction. Its ID, workspace, thread, and review gates must remain stable. Repeat a request and restart delivery; neither may duplicate work. Try private cases with an unauthorized actor and verify documents, assets, activity, and handoff history remain inaccessible.
Force local scratch preparation to fail, then let the source adapter exit while a child with closed output streams continues writing. The recipient must remain pending until the owned process group stops; a fresh delivery worker must then recover the same intent without repeating the source run. Create terminal intent from the board and stop the current execution, including a pending native retry: later task completion must leave the case open. Repeating Stop for an older run must preserve a newer execution's terminal intent.
With generic API tools disabled, use native read_case to open a historical
case document and a linked task document using section: "document",
documentKey, and the linked task's documentIssueId where applicable. Recover
the full body through bounded JSON pages. Edit the document between pages and
verify a snapshot conflict; unlink the task or revoke source access and verify
further reads fail. System and quarantined documents must remain inaccessible.
While a transfer awaits source release, change its recipient's assignment policy or revoke the caller's assignment grant. The worker must retain the pending handoff without changing the task owner or phase or waking the recipient. Restore the permission and verify that the same receipt can proceed without duplicate comments or tasks. Internal authorization context must not appear in public history, retry, or cancellation responses.
In Cloud, verify that a stack owner with owner elevation enabled can complete a permitted delayed transfer. Disable that setting or change the current stack role before release, then verify that the transfer observes the new authority. An unavailable portfolio lookup must leave the transfer pending for recovery.
Small live smoke: select only
case-handoffs.runner-codex-subscription.local.receipt-smoke in Product E2E.
Three agents use four bounded runs to save, review, and correct a tiny case receipt
on the same task. Two API-seeded passive cases exercise read-only list filters;
the browser reads the saved document, its revisions and handoff history. The
independent oracle checks actual native document reads and revision attribution.
See fixture and limits.
Gotchas
- A case is not a task execution; inspect linked tasks to prove work occurred.
- Feature gating and offered controls determine available actions. The API accepts a string case type and arbitrary JSON fields; do not infer typed case-field validation or a field-editing UI from those values.
- Cases can exist without a primary task. A phase name does not approve a review or complete either the task or case. Work begins only after normal execution admission; a queued wake is not a running agent.
Storybook verification
browse
Capability ID: cases/browse.
Scope: ui Storybook: Mount the production Cases page in its real shell: Columns/Filter/Sort/Group header controls, URL view state, search/status/type/project filters, No project, result paging, list-to-detail return, keyboard navigation and empty/loading/error recovery. The ListControls story changes columns, title sorting and type grouping using fixture query responses. Production: GET /companies/{id}/cases and /cases/types scope/filter behavior and ordering before pagination require production evidence; rendered fixtures establish presentation only. Mutation/permission scenarios need real API checks separately. Sources: Source: Cases.tsx, cases.ts. Tests: Cases.test.tsx, cases-routes.test.ts
structure
Capability ID: cases/structure.
Scope: ui Storybook: Mount read-only CaseDetail and CaseFieldsTable with nested objects, singleton/multiple object arrays and scalar reference links in a compact monospace key/value table, alongside documents, attachments, parent/children and unavailable references. Dotted leaf paths omit the index for a singleton object array and retain it for multiple entries. CaseDetail shows Work above Details in one column. The older CaseFieldsPanel arbitrary/empty examples retain their generic component scope. Do not invent a general field editor or typed field validation. Production: GET /cases/{id} and the parent-filtered case list must return permitted company records and correct relationships; changes/persistence require route tests separately. Sources: Source: CaseDetail.tsx, CaseFieldsPanel.tsx, Cases.tsx, cases.ts. Tests: CaseFieldsPanel.test.tsx, Cases.test.tsx, CaseDetail.test.tsx, cases-routes.test.ts
history
Capability ID: cases/history.
Scope: ui Storybook: Mount CaseDetail's View in Audit link with the case UUID query scope, and document revisions. Preserve the historical ActivityHistory export/ID for its Audit-navigation example. The destination Audit feed is a separate production surface, not reproduced in the case fixture shell. Production: The case-scoped Audit query must include retained historical case events, enforce the same case access boundary, and deduplicate events also recorded in the activity log. Case/Case action filters and configured agent avatars need real Audit/browser checks. GET /cases/{id}/events and /cases/{id}/documents/{key}/revisions still require company/access filtering; fixtures do not establish durable creation, privacy or restoration. Sources: Source: CaseDetail.tsx, cases.ts. Tests: CaseDetail.test.tsx, cases-routes.test.ts
task-links
Capability ID: cases/task-links.
Scope: ui Storybook: Mount CaseDetail and IssueCasesPanel related-task/case links in both directions, missing links and unavailable targets. Production: GET /issues/{id}/cases and /cases/{id}/issue-links must enforce permitted task/case relationships; production browser cold links verify destination and company context. Sources: Source: CaseDetail.tsx, IssueCasesPanel.tsx, cases.ts. Tests: IssueCasesPanel.test.tsx, CaseDetail.test.tsx, cases-routes.test.ts
handoffs
Capability ID: cases/handoffs.
Scope: ui Storybook: Read-only pages and CaseWorkSummary mount production task/phase/history with waiting and paused-recipient API fixtures. No handoff mutation controls are exposed on these pages. Production: POST /companies/{id}/case-handoffs and GET /cases/{id}/handoffs require company/task authorization, retained-source privacy, observed revisions, and durable admission. Retry/cancel retain task-control authority. Native and legacy provider/workspace settlement require runtime evidence. Sources: Source: CaseWorkSummary.tsx, case-handoffs.ts, case-handoff-delivery.ts. Tests: CaseDetail.test.tsx