From 7e4aec9379eafbb1cba0d3df98fde7f69821ace1 Mon Sep 17 00:00:00 2001 From: Dotta Date: Mon, 16 Mar 2026 18:37:59 -0500 Subject: [PATCH] Remove the experimental workspace toggle Co-Authored-By: Paperclip --- ui/src/components/IssueProperties.tsx | 38 ++++-------------------- ui/src/components/NewIssueDialog.tsx | 38 ++++-------------------- ui/src/components/ProjectProperties.tsx | 7 ----- ui/src/lib/experimentalSettings.ts | 39 ------------------------- ui/src/pages/InstanceSettings.tsx | 30 ------------------- ui/src/pages/IssueDetail.tsx | 7 +---- 6 files changed, 12 insertions(+), 147 deletions(-) delete mode 100644 ui/src/lib/experimentalSettings.ts diff --git a/ui/src/components/IssueProperties.tsx b/ui/src/components/IssueProperties.tsx index 4f3a673b..d864b3ad 100644 --- a/ui/src/components/IssueProperties.tsx +++ b/ui/src/components/IssueProperties.tsx @@ -17,7 +17,6 @@ import { PriorityIcon } from "./PriorityIcon"; import { Identity } from "./Identity"; import { formatDate, cn, projectUrl } from "../lib/utils"; import { timeAgo } from "../lib/timeAgo"; -import { useExperimentalWorkspacesEnabled } from "../lib/experimentalSettings"; import { Separator } from "@/components/ui/separator"; import { Popover, PopoverContent, PopoverTrigger } from "@/components/ui/popover"; import { User, Hexagon, ArrowUpRight, Tag, Plus, Trash2 } from "lucide-react"; @@ -134,7 +133,6 @@ function PropertyPicker({ export function IssueProperties({ issue, onUpdate, inline }: IssuePropertiesProps) { const { selectedCompanyId } = useCompany(); - const { enabled: showExperimentalWorkspaceUi } = useExperimentalWorkspacesEnabled(); const queryClient = useQueryClient(); const companyId = issue.companyId ?? selectedCompanyId; const [assigneeOpen, setAssigneeOpen] = useState(false); @@ -219,11 +217,8 @@ export function IssueProperties({ issue, onUpdate, inline }: IssuePropertiesProp const currentProject = issue.projectId ? orderedProjects.find((project) => project.id === issue.projectId) ?? null : null; - const currentProjectExecutionWorkspacePolicy = showExperimentalWorkspaceUi - ? currentProject?.executionWorkspacePolicy ?? null - : null; + const currentProjectExecutionWorkspacePolicy = currentProject?.executionWorkspacePolicy ?? null; const currentProjectSupportsExecutionWorkspace = Boolean(currentProjectExecutionWorkspacePolicy?.enabled); - const currentProjectWorkspaces = currentProject?.workspaces ?? []; const currentExecutionWorkspaceSelection = issue.executionWorkspacePreference ?? issue.executionWorkspaceSettings?.mode @@ -240,7 +235,7 @@ export function IssueProperties({ issue, onUpdate, inline }: IssuePropertiesProp projectWorkspaceId: issue.projectWorkspaceId ?? undefined, reuseEligible: true, }), - enabled: Boolean(companyId) && showExperimentalWorkspaceUi && Boolean(issue.projectId), + enabled: Boolean(companyId) && Boolean(issue.projectId), }); const selectedReusableExecutionWorkspace = (reusableExecutionWorkspaces ?? []).find( (workspace) => workspace.id === issue.executionWorkspaceId, @@ -509,10 +504,10 @@ export function IssueProperties({ issue, onUpdate, inline }: IssuePropertiesProp const defaultMode = defaultExecutionWorkspaceModeForProject(p); onUpdate({ projectId: p.id, - projectWorkspaceId: showExperimentalWorkspaceUi ? defaultProjectWorkspaceIdForProject(p) : null, + projectWorkspaceId: defaultProjectWorkspaceIdForProject(p), executionWorkspaceId: null, - executionWorkspacePreference: showExperimentalWorkspaceUi ? defaultMode : null, - executionWorkspaceSettings: showExperimentalWorkspaceUi && p.executionWorkspacePolicy?.enabled + executionWorkspacePreference: defaultMode, + executionWorkspaceSettings: p.executionWorkspacePolicy?.enabled ? { mode: defaultMode } : null, }); @@ -602,28 +597,7 @@ export function IssueProperties({ issue, onUpdate, inline }: IssuePropertiesProp {projectContent} - {showExperimentalWorkspaceUi && currentProjectWorkspaces.length > 0 && ( - - - - )} - - {showExperimentalWorkspaceUi && currentProjectSupportsExecutionWorkspace && ( + {currentProjectSupportsExecutionWorkspace && (
setProjectWorkspaceId(e.target.value)} - > - {currentProjectWorkspaces.map((workspace) => ( - - ))} - -
- )} - {currentProjectSupportsExecutionWorkspace && (
Execution workspace
diff --git a/ui/src/components/ProjectProperties.tsx b/ui/src/components/ProjectProperties.tsx index 016498d5..c3788b32 100644 --- a/ui/src/components/ProjectProperties.tsx +++ b/ui/src/components/ProjectProperties.tsx @@ -17,7 +17,6 @@ import { AlertCircle, Archive, ArchiveRestore, Check, ExternalLink, Github, Load import { ChoosePathButton } from "./PathInstructionsModal"; import { DraftInput } from "./agent-config-primitives"; import { InlineEditor } from "./InlineEditor"; -import { useExperimentalWorkspacesEnabled } from "../lib/experimentalSettings"; const PROJECT_STATUSES = [ { value: "backlog", label: "Backlog" }, @@ -152,7 +151,6 @@ function ProjectStatusPicker({ status, onChange }: { status: string; onChange: ( export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSaveState, onArchive, archivePending }: ProjectPropertiesProps) { const { selectedCompanyId } = useCompany(); - const { enabled: showExperimentalWorkspaceUi } = useExperimentalWorkspacesEnabled(); const queryClient = useQueryClient(); const [goalOpen, setGoalOpen] = useState(false); const [executionWorkspaceAdvancedOpen, setExecutionWorkspaceAdvancedOpen] = useState(false); @@ -749,9 +747,6 @@ export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSa )}
- {/* PAP-525: workspace UI gated by useExperimentalWorkspacesEnabled() */} - {showExperimentalWorkspaceUi && ( - <>
@@ -990,8 +985,6 @@ export function ProjectProperties({ project, onUpdate, onFieldUpdate, getFieldSa )}
- - )} diff --git a/ui/src/lib/experimentalSettings.ts b/ui/src/lib/experimentalSettings.ts deleted file mode 100644 index a48d7c06..00000000 --- a/ui/src/lib/experimentalSettings.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { useEffect, useState } from "react"; - -const WORKSPACES_KEY = "paperclip:experimental:workspaces"; - -export function loadExperimentalWorkspacesEnabled(): boolean { - if (typeof window === "undefined") return false; - return window.localStorage.getItem(WORKSPACES_KEY) === "true"; -} - -export function saveExperimentalWorkspacesEnabled(enabled: boolean) { - if (typeof window === "undefined") return; - window.localStorage.setItem(WORKSPACES_KEY, enabled ? "true" : "false"); - window.dispatchEvent(new CustomEvent("paperclip:experimental:workspaces", { detail: enabled })); -} - -export function useExperimentalWorkspacesEnabled() { - const [enabled, setEnabled] = useState(loadExperimentalWorkspacesEnabled); - - useEffect(() => { - const handleStorage = (event: StorageEvent) => { - if (event.key && event.key !== WORKSPACES_KEY) return; - setEnabled(loadExperimentalWorkspacesEnabled()); - }; - const handleCustom = () => setEnabled(loadExperimentalWorkspacesEnabled()); - window.addEventListener("storage", handleStorage); - window.addEventListener("paperclip:experimental:workspaces", handleCustom as EventListener); - return () => { - window.removeEventListener("storage", handleStorage); - window.removeEventListener("paperclip:experimental:workspaces", handleCustom as EventListener); - }; - }, []); - - const update = (next: boolean) => { - saveExperimentalWorkspacesEnabled(next); - setEnabled(next); - }; - - return { enabled, setEnabled: update }; -} diff --git a/ui/src/pages/InstanceSettings.tsx b/ui/src/pages/InstanceSettings.tsx index ab77a177..a4781e1f 100644 --- a/ui/src/pages/InstanceSettings.tsx +++ b/ui/src/pages/InstanceSettings.tsx @@ -12,7 +12,6 @@ import { Button } from "@/components/ui/button"; import { Card, CardContent } from "@/components/ui/card"; import { queryKeys } from "../lib/queryKeys"; import { formatDateTime, relativeTime } from "../lib/utils"; -import { useExperimentalWorkspacesEnabled } from "../lib/experimentalSettings"; function asRecord(value: unknown): Record | null { if (typeof value !== "object" || value === null || Array.isArray(value)) return null; @@ -31,7 +30,6 @@ export function InstanceSettings() { const { setBreadcrumbs } = useBreadcrumbs(); const queryClient = useQueryClient(); const [actionError, setActionError] = useState(null); - const { enabled: workspacesEnabled, setEnabled: setWorkspacesEnabled } = useExperimentalWorkspacesEnabled(); useEffect(() => { setBreadcrumbs([ @@ -112,34 +110,6 @@ export function InstanceSettings() { return (
-
-
-
- -

Experimental

-
-

- UI-only feature flags for in-progress product surfaces. -

-
-
-
-
Workspaces
-
- Show workspace, execution workspace, and work product controls in project and issue UI. -
-
- -
-
-
diff --git a/ui/src/pages/IssueDetail.tsx b/ui/src/pages/IssueDetail.tsx index dbc4fadb..0b9c1df9 100644 --- a/ui/src/pages/IssueDetail.tsx +++ b/ui/src/pages/IssueDetail.tsx @@ -11,7 +11,6 @@ import { useCompany } from "../context/CompanyContext"; import { usePanel } from "../context/PanelContext"; import { useBreadcrumbs } from "../context/BreadcrumbContext"; import { queryKeys } from "../lib/queryKeys"; -import { useExperimentalWorkspacesEnabled } from "../lib/experimentalSettings"; import { readIssueDetailBreadcrumb } from "../lib/issueDetailBreadcrumb"; import { useProjectOrder } from "../hooks/useProjectOrder"; import { relativeTime, cn, formatTokens, visibleRunCostUsd } from "../lib/utils"; @@ -216,7 +215,6 @@ function ActorIdentity({ evt, agentMap }: { evt: ActivityEvent; agentMap: Map(); const { selectedCompanyId } = useCompany(); - const { enabled: experimentalWorkspacesEnabled } = useExperimentalWorkspacesEnabled(); const { openPanel, closePanel, panelVisible, setPanelVisible } = usePanel(); const { setBreadcrumbs } = useBreadcrumbs(); const queryClient = useQueryClient(); @@ -662,10 +660,7 @@ export function IssueDetail() { // Ancestors are returned oldest-first from the server (root at end, immediate parent at start) const ancestors = issue.ancestors ?? []; const workProducts = issue.workProducts ?? []; - const showOutputsTab = - experimentalWorkspacesEnabled || - Boolean(issue.currentExecutionWorkspace) || - workProducts.length > 0; + const showOutputsTab = Boolean(issue.currentExecutionWorkspace) || workProducts.length > 0; const handleFilePicked = async (evt: ChangeEvent) => { const files = evt.target.files;