FN-6863: raise footer concurrency slider limits
Raise the footer engine control sliders to support higher concurrency without hiding persisted values. - Increase max tasks, max triage, and max worktree slider baselines to 50. - Preserve value-aware slider maxima for persisted settings above the new baseline. - Add coverage for in-range 50 limits, above-range persisted values, and saving a value of 50. Files changed: .../dashboard/app/components/EngineControlMenu.tsx | 9 ++-- .../__tests__/EngineControlMenu.test.tsx | 50 +++++++++++++++++++--- 2 files changed, 49 insertions(+), 10 deletions(-) Fusion-Task-Id: FN-6863 Fusion-Task-Lineage: f42373fc-322b-4d20-9b8b-2d0c63254e79
This commit is contained in:
@@ -35,9 +35,9 @@ const DEFAULT_CONCURRENCY_VALUES: ConcurrencyValues = {
|
||||
};
|
||||
|
||||
const CONCURRENCY_SLIDER_LIMITS: Record<keyof ConcurrencyValues, { min: number; max: number }> = {
|
||||
maxConcurrent: { min: 1, max: 10 },
|
||||
maxTriageConcurrent: { min: 1, max: 10 },
|
||||
maxWorktrees: { min: 1, max: 20 },
|
||||
maxConcurrent: { min: 1, max: 50 },
|
||||
maxTriageConcurrent: { min: 1, max: 50 },
|
||||
maxWorktrees: { min: 1, max: 50 },
|
||||
};
|
||||
|
||||
function clamp(value: number, min: number, max: number) {
|
||||
@@ -58,6 +58,9 @@ Engine stop/start, triage pause/resume, and live scheduler concurrency/worktree
|
||||
|
||||
FNXC:EngineControls 2026-06-21-00:00:
|
||||
FN-6862 requires the footer popover chrome to stay opaque across themes. Its CSS must use a defined solid surface token (`var(--card)`) because `--surface-elevated` is not in the dashboard token vocabulary and makes the menu transparent when unresolved.
|
||||
|
||||
FNXC:EngineControls 2026-06-21-00:00:
|
||||
FN-6863 raises the footer concurrency sliders' base drag ceiling to 50 for max tasks, triage, and worktrees. Keep getConcurrencySliderMax value-aware so already-persisted settings above 50 expand the slider instead of hiding or clamping the truthful readout.
|
||||
*/
|
||||
export const EngineControlMenu = forwardRef<EngineControlMenuHandle, EngineControlMenuProps>(function EngineControlMenu({ projectId }, ref) {
|
||||
const { t } = useTranslation("app");
|
||||
|
||||
@@ -104,9 +104,9 @@ describe("EngineControlMenu", () => {
|
||||
it("persists debounced concurrency and worktree slider changes and refreshes settings", async () => {
|
||||
legacyMocks.fetchSettings.mockResolvedValue({
|
||||
...defaultSettings,
|
||||
maxConcurrent: 12,
|
||||
maxTriageConcurrent: 3,
|
||||
maxWorktrees: 25,
|
||||
maxConcurrent: 60,
|
||||
maxTriageConcurrent: 70,
|
||||
maxWorktrees: 80,
|
||||
});
|
||||
await openMenu();
|
||||
|
||||
@@ -116,10 +116,12 @@ describe("EngineControlMenu", () => {
|
||||
|
||||
vi.useFakeTimers();
|
||||
|
||||
expect(maxConcurrent).toHaveAttribute("max", "12");
|
||||
expect(maxConcurrent).toHaveValue("12");
|
||||
expect(maxWorktrees).toHaveAttribute("max", "25");
|
||||
expect(maxWorktrees).toHaveValue("25");
|
||||
expect(maxConcurrent).toHaveAttribute("max", "60");
|
||||
expect(maxConcurrent).toHaveValue("60");
|
||||
expect(maxTriage).toHaveAttribute("max", "70");
|
||||
expect(maxTriage).toHaveValue("70");
|
||||
expect(maxWorktrees).toHaveAttribute("max", "80");
|
||||
expect(maxWorktrees).toHaveValue("80");
|
||||
|
||||
fireEvent.change(maxConcurrent, { target: { value: "9" } });
|
||||
fireEvent.change(maxTriage, { target: { value: "4" } });
|
||||
@@ -136,6 +138,40 @@ describe("EngineControlMenu", () => {
|
||||
expect(apiMocks.fetchSettings).toHaveBeenCalledTimes(2);
|
||||
});
|
||||
|
||||
it("uses a 50 max for all in-range concurrency sliders", async () => {
|
||||
legacyMocks.fetchSettings.mockResolvedValue({
|
||||
...defaultSettings,
|
||||
maxConcurrent: 12,
|
||||
maxTriageConcurrent: 3,
|
||||
maxWorktrees: 25,
|
||||
});
|
||||
await openMenu();
|
||||
|
||||
expect(await screen.findByLabelText(/max concurrent tasks/i)).toHaveAttribute("max", "50");
|
||||
expect(screen.getByLabelText(/max triage concurrent/i)).toHaveAttribute("max", "50");
|
||||
expect(screen.getByLabelText(/max worktrees/i)).toHaveAttribute("max", "50");
|
||||
});
|
||||
|
||||
it("persists a slider value of 50 through the debounced settings save", async () => {
|
||||
await openMenu();
|
||||
|
||||
const maxConcurrent = await screen.findByLabelText(/max concurrent tasks/i);
|
||||
vi.useFakeTimers();
|
||||
|
||||
expect(maxConcurrent).toHaveAttribute("max", "50");
|
||||
|
||||
fireEvent.change(maxConcurrent, { target: { value: "50" } });
|
||||
|
||||
await act(async () => {
|
||||
await vi.advanceTimersByTimeAsync(500);
|
||||
});
|
||||
|
||||
expect(legacyMocks.updateSettings).toHaveBeenCalledWith(
|
||||
{ maxConcurrent: 50, maxTriageConcurrent: 1, maxWorktrees: 4 },
|
||||
"proj_123",
|
||||
);
|
||||
});
|
||||
|
||||
it("renders a load error state without crashing", async () => {
|
||||
legacyMocks.fetchSettings.mockRejectedValue(new Error("settings unavailable"));
|
||||
await openMenu();
|
||||
|
||||
Reference in New Issue
Block a user