diff --git a/ui/src/pages/InstanceExperimentalSettings.test.tsx b/ui/src/pages/InstanceExperimentalSettings.test.tsx index 1cfa6da65e..3e6ae06160 100644 --- a/ui/src/pages/InstanceExperimentalSettings.test.tsx +++ b/ui/src/pages/InstanceExperimentalSettings.test.tsx @@ -862,3 +862,67 @@ describe("InstanceExperimentalSettings — cloud-managed keys", () => { expect(mockInstanceSettingsApi.updateExperimental).toHaveBeenCalledWith({ enableApps: true }); }); }); + +describe("InstanceExperimentalSettings — card ordering and headings (PAP-393)", () => { + let container: HTMLDivElement; + let root: Root | null = null; + + async function renderPage(settings: InstanceExperimentalSettingsWithManaged) { + mockInstanceSettingsApi.getExperimental.mockResolvedValue({ ...settings }); + root = createRoot(container); + const queryClient = new QueryClient({ + defaultOptions: { queries: { retry: false } }, + }); + flushSync(() => { + root!.render( + + + , + ); + }); + await flushReact(); + } + + beforeEach(() => { + container = document.createElement("div"); + document.body.appendChild(container); + mockInstanceSettingsApi.updateExperimental.mockImplementation(async (patch) => ({ + ...defaultExperimentalSettings(), + ...patch, + })); + }); + + afterEach(() => { + flushSync(() => { + root?.unmount(); + }); + root = null; + container.remove(); + vi.clearAllMocks(); + }); + + it("renders every card heading in alphabetical order", async () => { + await renderPage(defaultExperimentalSettings()); + + const headings = [...container.querySelectorAll("h2")].map( + (heading) => heading.textContent ?? "", + ); + + // Sanity: the page rendered a meaningful set of cards, not an empty list. + expect(headings.length).toBeGreaterThan(10); + + const alphabetical = [...headings].sort((a, b) => + a.localeCompare(b, undefined, { sensitivity: "base" }), + ); + expect(headings).toEqual(alphabetical); + }); + + it("no longer renders an 'Experimental' secondary badge on any card", async () => { + await renderPage(defaultExperimentalSettings()); + + const badges = [...container.querySelectorAll('[data-slot="badge"]')].map( + (badge) => badge.textContent?.trim(), + ); + expect(badges).not.toContain("Experimental"); + }); +}); diff --git a/ui/src/pages/InstanceExperimentalSettings.tsx b/ui/src/pages/InstanceExperimentalSettings.tsx index d450863600..40a2a40359 100644 --- a/ui/src/pages/InstanceExperimentalSettings.tsx +++ b/ui/src/pages/InstanceExperimentalSettings.tsx @@ -87,7 +87,6 @@ function ManagedByCloudBadge() { function ExperimentalToggleCard({ title, - experimental = false, description, footnote, checked, @@ -97,7 +96,6 @@ function ExperimentalToggleCard({ ariaLabel, }: { title: string; - experimental?: boolean; description: string; footnote?: string; checked: boolean; @@ -113,7 +111,6 @@ function ExperimentalToggleCard({

{title}

- {experimental ? Experimental : null} {isManaged ? : null}

{description}

@@ -460,66 +457,8 @@ export function InstanceExperimentalSettings() {
)} - {inWorktree ? ( - -
-
-
-
-

Run tasks in this worktree

- {worktreeRunExecutionManaged ? : null} -
-

- This is an isolated git-worktree preview instance. Turn this on to let the scheduler execute runs - here. Only tasks created after enabling will run automatically — copied/pre-existing tasks stay - parked. Toggling off and on resets the cutoff. -

-
- { - if (worktreeRunExecutionManaged) return; - toggleMutation.mutate({ enableWorktreeRunExecution: checked }); - }} - disabled={toggleMutation.isPending || worktreeRunExecutionManaged} - aria-label="Toggle worktree run execution setting" - /> -
- - {worktreeRunExecutionState.kind === "armed" ? ( -
- - - Running tasks created after{" "} - - {formatActivationTimestamp(worktreeRunExecutionState.activatedAt)} - - . - -
- ) : null} - - {worktreeRunExecutionState.kind === "fail_closed" ? ( -
- -
-

Execution is suppressed — effectively off.

-

- {worktreeRunExecutionState.reason === "instance_mismatch" - ? "This setting was armed in a different instance and copied here, so no tasks run automatically." - : "This setting is missing its activation cutoff, so no tasks run automatically."}{" "} - Toggle it off and back on to arm execution for tasks created here. -

-
-
- ) : null} -
-
- ) : null} - toggleMutation.mutate({ enableApps: checked })} @@ -528,206 +467,6 @@ export function InstanceExperimentalSettings() { ariaLabel="Toggle apps experimental setting" /> - toggleMutation.mutate({ enableCases: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableCases} - ariaLabel="Toggle cases experimental setting" - /> - - toggleMutation.mutate({ enableEnvironments: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableEnvironments} - ariaLabel="Toggle environments experimental setting" - /> - - toggleMutation.mutate({ enableBuiltInAgents: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableBuiltInAgents} - ariaLabel="Toggle built-in agents experimental setting" - /> - - toggleMutation.mutate({ enableBetaSkills: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableBetaSkills} - ariaLabel="Toggle beta skills experimental setting" - /> - - - toggleMutation.mutate( - checked || !enableStatusCards - ? { enableSummaries: checked } - : { enableSummaries: false, enableStatusCards: false }, - ) - } - disabled={toggleMutation.isPending || summariesRequiredByManagedStatusCards} - managed={managedKeys.enableSummaries} - ariaLabel="Toggle summaries experimental setting" - /> - - toggleMutation.mutate({ enableExperimentalFileViewer: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableExperimentalFileViewer} - ariaLabel="Toggle experimental file viewer setting" - /> - - - toggleMutation.mutate( - checked - ? { enableSummaries: true, enableStatusCards: true } - : { enableStatusCards: false }, - ) - } - disabled={toggleMutation.isPending || statusCardsBlockedByManagedSummaries} - managed={managedKeys.enableStatusCards} - ariaLabel="Toggle status cards experimental setting" - /> - - toggleMutation.mutate({ enableExternalObjects: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableExternalObjects} - ariaLabel="Toggle external objects experimental setting" - /> - - toggleMutation.mutate({ enableDecisions: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableDecisions} - ariaLabel="Toggle decisions experimental setting" - /> - - toggleMutation.mutate({ enableGoalsSidebarLink: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableGoalsSidebarLink} - ariaLabel="Toggle goals sidebar link experimental setting" - /> - - toggleMutation.mutate({ enableIsolatedWorkspaces: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableIsolatedWorkspaces} - ariaLabel="Toggle isolated workspaces experimental setting" - /> - - {SHOW_CONFERENCE_ROOM_EXPERIMENTAL_SETTING ? ( - toggleMutation.mutate({ enableConferenceRoomChat: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableConferenceRoomChat} - ariaLabel="Toggle conference room chat experimental setting" - /> - ) : null} - - toggleMutation.mutate({ enableIssuePlanDecompositions: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableIssuePlanDecompositions} - ariaLabel="Toggle task plan decomposition panel experimental setting" - /> - - toggleMutation.mutate({ enableTaskChatRedesign: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableTaskChatRedesign} - ariaLabel="Toggle chat-style tasks experimental setting" - /> - - toggleMutation.mutate({ enableTaskWatchdogs: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableTaskWatchdogs} - ariaLabel="Toggle task watchdogs experimental setting" - /> - - toggleMutation.mutate({ enableServerInfoDebugView: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableServerInfoDebugView} - ariaLabel="Toggle server info debug view experimental setting" - /> - - toggleMutation.mutate({ enableSmokeLab: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.enableSmokeLab} - ariaLabel="Toggle smoke lab experimental setting" - /> - - toggleMutation.mutate({ autoRestartDevServerWhenIdle: checked })} - disabled={toggleMutation.isPending} - managed={managedKeys.autoRestartDevServerWhenIdle} - ariaLabel="Toggle guarded dev-server auto-restart" - /> -
@@ -818,6 +557,261 @@ export function InstanceExperimentalSettings() {
+ toggleMutation.mutate({ autoRestartDevServerWhenIdle: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.autoRestartDevServerWhenIdle} + ariaLabel="Toggle guarded dev-server auto-restart" + /> + + toggleMutation.mutate({ enableBetaSkills: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableBetaSkills} + ariaLabel="Toggle beta skills experimental setting" + /> + + toggleMutation.mutate({ enableBuiltInAgents: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableBuiltInAgents} + ariaLabel="Toggle built-in agents experimental setting" + /> + + toggleMutation.mutate({ enableCases: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableCases} + ariaLabel="Toggle cases experimental setting" + /> + + toggleMutation.mutate({ enableTaskChatRedesign: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableTaskChatRedesign} + ariaLabel="Toggle chat-style tasks experimental setting" + /> + + {SHOW_CONFERENCE_ROOM_EXPERIMENTAL_SETTING ? ( + toggleMutation.mutate({ enableConferenceRoomChat: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableConferenceRoomChat} + ariaLabel="Toggle conference room chat experimental setting" + /> + ) : null} + + toggleMutation.mutate({ enableDecisions: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableDecisions} + ariaLabel="Toggle decisions experimental setting" + /> + + toggleMutation.mutate({ enableEnvironments: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableEnvironments} + ariaLabel="Toggle environments experimental setting" + /> + + toggleMutation.mutate({ enableExternalObjects: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableExternalObjects} + ariaLabel="Toggle external objects experimental setting" + /> + + toggleMutation.mutate({ enableIsolatedWorkspaces: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableIsolatedWorkspaces} + ariaLabel="Toggle isolated workspaces experimental setting" + /> + + toggleMutation.mutate({ enableExperimentalFileViewer: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableExperimentalFileViewer} + ariaLabel="Toggle experimental file viewer setting" + /> + + toggleMutation.mutate({ enableGoalsSidebarLink: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableGoalsSidebarLink} + ariaLabel="Toggle goals sidebar link experimental setting" + /> + + {inWorktree ? ( + +
+
+
+
+

Run tasks in this worktree

+ {worktreeRunExecutionManaged ? : null} +
+

+ This is an isolated git-worktree preview instance. Turn this on to let the scheduler execute runs + here. Only tasks created after enabling will run automatically — copied/pre-existing tasks stay + parked. Toggling off and on resets the cutoff. +

+
+ { + if (worktreeRunExecutionManaged) return; + toggleMutation.mutate({ enableWorktreeRunExecution: checked }); + }} + disabled={toggleMutation.isPending || worktreeRunExecutionManaged} + aria-label="Toggle worktree run execution setting" + /> +
+ + {worktreeRunExecutionState.kind === "armed" ? ( +
+ + + Running tasks created after{" "} + + {formatActivationTimestamp(worktreeRunExecutionState.activatedAt)} + + . + +
+ ) : null} + + {worktreeRunExecutionState.kind === "fail_closed" ? ( +
+ +
+

Execution is suppressed — effectively off.

+

+ {worktreeRunExecutionState.reason === "instance_mismatch" + ? "This setting was armed in a different instance and copied here, so no tasks run automatically." + : "This setting is missing its activation cutoff, so no tasks run automatically."}{" "} + Toggle it off and back on to arm execution for tasks created here. +

+
+
+ ) : null} +
+
+ ) : null} + + toggleMutation.mutate({ enableServerInfoDebugView: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableServerInfoDebugView} + ariaLabel="Toggle server info debug view experimental setting" + /> + + toggleMutation.mutate({ enableSmokeLab: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableSmokeLab} + ariaLabel="Toggle smoke lab experimental setting" + /> + + + toggleMutation.mutate( + checked + ? { enableSummaries: true, enableStatusCards: true } + : { enableStatusCards: false }, + ) + } + disabled={toggleMutation.isPending || statusCardsBlockedByManagedSummaries} + managed={managedKeys.enableStatusCards} + ariaLabel="Toggle status cards experimental setting" + /> + + + toggleMutation.mutate( + checked || !enableStatusCards + ? { enableSummaries: checked } + : { enableSummaries: false, enableStatusCards: false }, + ) + } + disabled={toggleMutation.isPending || summariesRequiredByManagedStatusCards} + managed={managedKeys.enableSummaries} + ariaLabel="Toggle summaries experimental setting" + /> + + toggleMutation.mutate({ enableIssuePlanDecompositions: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableIssuePlanDecompositions} + ariaLabel="Toggle task plan decomposition panel experimental setting" + /> + + toggleMutation.mutate({ enableTaskWatchdogs: checked })} + disabled={toggleMutation.isPending} + managed={managedKeys.enableTaskWatchdogs} + ariaLabel="Toggle task watchdogs experimental setting" + /> + {previewDialogOpen && !autoRecoveryManaged ? (