mirror of
https://github.com/paperclipai/paperclip.git
synced 2026-10-13 23:39:21 +02:00
## Thinking Path > - Paperclip is the open source app people use to manage AI agents for work. > - The task properties pane lets an operator change the settings for one task. > - The pane did not let an operator select the execution workspace for the next run. > - The existing workspace card had selection rules that another control could copy by mistake. > - This pull request adds one shared selection module and one compact property picker. > - The picker keeps the current workspace active until the next run. > - The benefit is a clear workspace choice in the task properties pane without an API change. ## Linked Issues or Issue Description **Subsystem affected** ui/ — React + Vite board UI **Problem or motivation** An operator cannot set the execution workspace from the new task properties pane. The existing task workspace card also owns selection rules that a second control could copy and change over time. **Proposed solution** Add a compact workspace property picker. Put the shared selection and update rules in one UI module. Show the picker only when isolated workspaces and the project workspace policy are enabled. **Alternatives considered** The existing workspace card could remain the only control. This would leave the new task interface incomplete. The picker could also copy the card logic, but that would create two sources of truth. **Roadmap alignment** This is a small UI improvement for existing workspace controls. It does not duplicate a planned item in `ROADMAP.md`. Related workspace work: Refs #12682. That pull request changes runner recovery and other workspace controls. It does not add this task property picker. ## What Changed - Added shared helpers for the current workspace selection and its issue update payload. - Updated the existing workspace card to use the shared helpers without changing its project-default behavior. - Added a gated workspace property picker with mode and workspace search steps. - Added unit and component tests for visibility, selection payloads, search, and workspace reuse. - Rebased onto the upstream native-run teardown fix so CI drains background heartbeat writes before PostgreSQL cleanup. ## Verification - `NODE_ENV=test pnpm --filter @paperclipai/ui exec vitest run src/lib/issue-workspace-selection.test.ts src/components/IssueProperties.test.tsx src/components/RoutineRunVariablesDialog.test.tsx` — 74 tests passed. - `pnpm --filter @paperclipai/ui typecheck` — passed. - `pnpm check:token-gates` — passed. - `pnpm -r typecheck` — passed. - `pnpm build` — passed. - `NODE_ENV=test pnpm --filter @paperclipai/server exec vitest run src/services/native-runtime/native-question-bridge.test.ts` — 8 tests passed on the rebased head. The upstream teardown drain prevents the prior PostgreSQL cleanup deadlock. ## Risks - Low risk. This is a gated UI-only change. - A wrong selection payload could change the next workspace mode. Exact payload tests cover every mode. - The old card must keep its existing project-default payload. A shared-helper test covers that payload. > 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 exact serving revision and context window are not exposed. The agent used reasoning, 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 (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>
83 lines
2.6 KiB
TypeScript
83 lines
2.6 KiB
TypeScript
import type { ExecutionWorkspaceMode, Issue } from "@paperclipai/shared";
|
|
import {
|
|
defaultExecutionWorkspaceModeForProject,
|
|
issueExecutionWorkspaceModeForExistingWorkspace,
|
|
} from "./project-workspace-defaults";
|
|
|
|
export type IssueWorkspaceSelection = ExecutionWorkspaceMode | "reuse_existing" | null;
|
|
|
|
type IssueWorkspaceSelectionSource = Pick<
|
|
Issue,
|
|
| "executionWorkspaceId"
|
|
| "executionWorkspacePreference"
|
|
| "executionWorkspaceSettings"
|
|
| "currentExecutionWorkspace"
|
|
>;
|
|
|
|
type ProjectWorkspaceSelectionSource = Parameters<typeof defaultExecutionWorkspaceModeForProject>[0];
|
|
|
|
export interface WorkspaceSelectionUpdate extends Record<string, unknown> {
|
|
executionWorkspacePreference: IssueWorkspaceSelection;
|
|
executionWorkspaceId: string | null;
|
|
executionWorkspaceSettings: {
|
|
mode: ExecutionWorkspaceMode;
|
|
environmentId: null;
|
|
} | null;
|
|
}
|
|
|
|
/**
|
|
* Resolves the issue's effective workspace choice. A bound isolated or operator
|
|
* workspace is always presented as reuse-existing, even if its persisted
|
|
* preference still describes how it was originally created.
|
|
*/
|
|
export function currentWorkspaceSelection(
|
|
issue: IssueWorkspaceSelectionSource,
|
|
project: ProjectWorkspaceSelectionSource,
|
|
): IssueWorkspaceSelection {
|
|
const persistedMode =
|
|
issue.currentExecutionWorkspace?.mode
|
|
?? issue.executionWorkspaceSettings?.mode
|
|
?? issue.executionWorkspacePreference;
|
|
|
|
if (
|
|
issue.executionWorkspaceId
|
|
&& (persistedMode === "isolated_workspace" || persistedMode === "operator_branch")
|
|
) {
|
|
return "reuse_existing";
|
|
}
|
|
|
|
return (
|
|
issue.executionWorkspacePreference
|
|
?? issue.executionWorkspaceSettings?.mode
|
|
?? defaultExecutionWorkspaceModeForProject(project)
|
|
) as IssueWorkspaceSelection;
|
|
}
|
|
|
|
/** Returns null when the selection is incomplete and cannot be saved. */
|
|
export function buildWorkspaceSelectionUpdate(
|
|
selection: IssueWorkspaceSelection,
|
|
workspaceId: string | null | undefined,
|
|
reusedWorkspaceMode: string | null | undefined,
|
|
): WorkspaceSelectionUpdate | null {
|
|
if (selection === "reuse_existing" && !workspaceId) return null;
|
|
|
|
if (selection === null) {
|
|
return {
|
|
executionWorkspacePreference: null,
|
|
executionWorkspaceId: null,
|
|
executionWorkspaceSettings: null,
|
|
};
|
|
}
|
|
|
|
return {
|
|
executionWorkspacePreference: selection,
|
|
executionWorkspaceId: selection === "reuse_existing" ? workspaceId! : null,
|
|
executionWorkspaceSettings: {
|
|
mode: selection === "reuse_existing"
|
|
? issueExecutionWorkspaceModeForExistingWorkspace(reusedWorkspaceMode)
|
|
: selection,
|
|
environmentId: null,
|
|
},
|
|
};
|
|
}
|