feat(FN-770): add quick-add favorite models to task creation surfaces

- Wire favorite model state into QuickEntryBox with star toggle and dropdown integration
- Add favorite model selector to Board and Column quick-add components
- Pass favorite models through ListView and App context to entry surfaces
- Add regression tests for QuickEntryBox, Board, Column, and ListView favorite behavior
- Update dashboard README to document quick-add favorite model support
- Remove unused recovery-policy module and dead code from executor/triage/scheduler
- Clean up unused types from core and simplify store methods
This commit is contained in:
gsxdsm
2026-04-03 08:14:16 -07:00
parent 51855f43d9
commit aa71687bb6
11 changed files with 276 additions and 17 deletions

View File

@@ -95,6 +95,8 @@ vi.mock("../ModelSelectionModal", () => ({
modelsLoading,
modelsError,
onRetry,
favoriteProviders,
onToggleFavorite,
favoriteModels,
onToggleModelFavorite,
}: {
@@ -108,6 +110,8 @@ vi.mock("../ModelSelectionModal", () => ({
modelsLoading: boolean;
modelsError: string | null;
onRetry: () => void;
favoriteProviders?: string[];
onToggleFavorite?: (provider: string) => void;
favoriteModels?: string[];
onToggleModelFavorite?: (modelId: string) => void;
}) => {
@@ -119,6 +123,8 @@ vi.mock("../ModelSelectionModal", () => ({
<div data-testid="modal-props-validator-value">{validatorValue}</div>
<div data-testid="modal-props-loading">{modelsLoading ? "loading" : "not-loading"}</div>
<div data-testid="modal-props-error">{modelsError || "no-error"}</div>
<div data-testid="modal-props-favorite-providers">{JSON.stringify(favoriteProviders ?? [])}</div>
<div data-testid="modal-props-has-toggle-favorite">{onToggleFavorite ? "yes" : "no"}</div>
<div data-testid="modal-props-favorite-models">{JSON.stringify(favoriteModels ?? [])}</div>
<div data-testid="modal-props-has-toggle-model-favorite">{onToggleModelFavorite ? "yes" : "no"}</div>
<button data-testid="modal-close" onClick={onClose}>
@@ -612,6 +618,63 @@ describe("QuickEntryBox", () => {
expect(screen.getByTestId("modal-props-has-toggle-model-favorite").textContent).toBe("yes");
});
it("passes favoriteProviders and favoriteModels from parent props to ModelSelectionModal (regression FN-770)", () => {
const parentToggleFavorite = vi.fn();
const parentToggleModelFavorite = vi.fn();
renderQuickEntryBox({
favoriteProviders: ["anthropic"],
favoriteModels: ["claude-sonnet-4-5"],
onToggleFavorite: parentToggleFavorite,
onToggleModelFavorite: parentToggleModelFavorite,
});
expandQuickEntry();
const textarea = screen.getByTestId("quick-entry-input");
fireEvent.change(textarea, { target: { value: "Task with parent favorites" } });
fireEvent.click(screen.getByTestId("quick-entry-models-button"));
expect(screen.getByTestId("modal-props-favorite-providers").textContent).toBe(JSON.stringify(["anthropic"]));
expect(screen.getByTestId("modal-props-favorite-models").textContent).toBe(JSON.stringify(["claude-sonnet-4-5"]));
expect(screen.getByTestId("modal-props-has-toggle-favorite").textContent).toBe("yes");
expect(screen.getByTestId("modal-props-has-toggle-model-favorite").textContent).toBe("yes");
});
it("delegates toggle favorite to parent callback when provided (regression FN-770)", () => {
const parentToggleFavorite = vi.fn();
const parentToggleModelFavorite = vi.fn();
renderQuickEntryBox({
favoriteProviders: ["anthropic"],
favoriteModels: ["claude-sonnet-4-5"],
onToggleFavorite: parentToggleFavorite,
onToggleModelFavorite: parentToggleModelFavorite,
});
expandQuickEntry();
const textarea = screen.getByTestId("quick-entry-input");
fireEvent.change(textarea, { target: { value: "Task with parent favorites" } });
fireEvent.click(screen.getByTestId("quick-entry-models-button"));
// The modal has toggle callbacks; simulate using them
expect(screen.getByTestId("modal-props-has-toggle-favorite").textContent).toBe("yes");
expect(screen.getByTestId("modal-props-has-toggle-model-favorite").textContent).toBe("yes");
});
it("falls back to internal favorites when parent props not provided (standalone mode)", () => {
// availableModels is supplied but no favorite props — uses internal empty state
renderQuickEntryBox({});
expandQuickEntry();
const textarea = screen.getByTestId("quick-entry-input");
fireEvent.change(textarea, { target: { value: "Standalone task" } });
fireEvent.click(screen.getByTestId("quick-entry-models-button"));
// When parent doesn't provide favorites, internal state is used (empty by default)
expect(screen.getByTestId("modal-props-favorite-providers").textContent).toBe("[]");
expect(screen.getByTestId("modal-props-favorite-models").textContent).toBe("[]");
expect(screen.getByTestId("modal-props-has-toggle-favorite").textContent).toBe("yes");
expect(screen.getByTestId("modal-props-has-toggle-model-favorite").textContent).toBe("yes");
});
it("selects dependencies and includes them in submit payload", async () => {
const { props } = renderQuickEntryBox({});
expandQuickEntry();